[
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]\npatreon: # Replace with a single Patreon username\nopen_collective: # Replace with a single Open Collective username\nko_fi: # Replace with a single Ko-fi username\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\notechie: # Replace with a single Otechie username\ncustom:  ['https://paypal.me/wendux91','https://camo.githubusercontent.com/3442fb56bdedc11300c23d958a2f62e813adae4903bd714fe852232623bb97c6/68747470733a2f2f63646e2e6a7364656c6976722e6e65742f67682f666c75747465726368696e612f666c75747465722d696e2d616374696f6e40312e302e332f646f63732f696d67732f7061792e6a706567']\n\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules/\n*.DS_Store\nbook/\n.packages\n.dart_tool/\n.idea/\n"
  },
  {
    "path": "README.MD",
    "content": "# 注意\n\n本书第二版草稿已上传，**本 repo 将不再维护**，请大家移步：https://github.com/flutterchina/flutter_in_action_2nd\n\n---\n\n# 第一版简介\n\n本项目为Flutter中文网《Flutter实战》开源电子书项目，官网地址为：https://book.flutterchina.club 。\n\n本书随书源码：https://github.com/wendux/flutter_in_action_source_code\n\n\n## 实体书\n<div style=\"text-align:center; padding-bottom:30px\"><a href=\"https://item.jd.com/12816296.html\" title='点击购买'><img height=\"250\" style=\"box-shadow: #aaa 5px 5px 10px;\" src=\"https://pcdn.flutterchina.club/imgs/book.png\"/></a>  <br/> <a class=\"buy-btn\" href=\"https://item.jd.com/12816296.html\" title='点击购买'> 购买实体书 </a></div>\n\n\n## 贡献须知\n\n本书目前仍在创作中，如果您想参与到本书创作，欢迎提PR。本书目录结构如下：\n\n| 目录及文件      | 说明                                                         |\n| --------------- | ------------------------------------------------------------ |\n| src             | 文档目录，您应该在此目录下对应的章节文件夹下修改/创建Markdown文档 |\n| docs            | 打包后的网站代码目录，您不应该手动修改此目录下的文档         |\n| src/SUMMARY.md | 本书目录，要修改目录请参考本文档中前面章节。                 |\n| src/imgs       | 本书所引的图片、截图目录                                     |\n\n### 图片引入说明\n\n为了后续图片能够容易上CDN，如果您需要在文档中引入新的图片，请按如下步骤操作：\n\n1. 将您的图片大小进行调整，有一个要求是图片高度最大不能超过500像素，原则上单张图片最大不能超过300K.\n\n2. 将图片拷贝到docs/imgs目录，注意不要重名\n\n3. 在你的文章中用**相对路径**引用，如：\n\n   ```\n   ![](../imgs/xx.jpg)\n   ```\n\n当您提交内容被合入后，我们会来上传CDN，自动替换图片链接，然后回提到仓库中，在您下次提交之前，您需要先pull一下变更。\n\n### 构建环境搭建\n\n本书是采用gitbook编写，要想在本地运行网站，需要安装一下环境：\n\n1. 安装node；如果已经安装过node，可以省略这一步。node安装方法自行百度。\n\n2. 开始构建并启动测试服务器。\n\n   ```shell\n    yarn run dev\n   ```\n\n3. 构建发布包：\n\n   ```shell\n    yarn run build\n   ```\n\n\n### 第二版\n\n本书第一版已出版，由于flutter和dart更新非常快，本书第二版正在创作中，敬请期待。\n\n### 提交更新\n\n为了保证书籍质量，提交后的内容都需要Review，所以您PR提交之后离正式合入可能会需要多次修改，为了节省时间，请在提交PR后的第一时间通知我Review。\n\n## 勘误\n\n如果您发现本书的错误，欢迎PR。\n\n## 联系方式\n\n微信号：Demons-du\n\n## 免费？\n\n知识是应该付费的，创作不易，开源不等于免费，如果您是本书读者并手头宽裕，可以微信扫描下面二维码打赏，也不用太多，够买一杯咖啡就行。当然，如果您囊中羞涩，您也可以阅读本书，但我对您有个小小的要求，希望您在阅读的过程中能积极参与到本书的纠错以及未完成内容的创作上来，也算是有所付出。\n\n![](https://cdn.jsdelivr.net/gh/flutterchina/flutter-in-action@1.0.3/docs/imgs/pay.jpeg)\n\n"
  },
  {
    "path": "docs/404.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/129.88b4391d.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/2.f4fe4405.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/39.c3d0c942.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><div class=\"theme-default-content\"><h1>404</h1> <blockquote>How did we get here?</blockquote> <a href=\"/\" class=\"router-link-active\">\n      Take me home.\n    </a></div></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/129.88b4391d.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/assets/css/0.styles.9ca8d33d.css",
    "content": "code[class*=language-],pre[class*=language-]{color:#ccc;background:none;font-family:Consolas,Monaco,Andale Mono,Ubuntu Mono,monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#2d2d2d}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.block-comment,.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#999}.token.punctuation{color:#ccc}.token.attr-name,.token.deleted,.token.namespace,.token.tag{color:#e2777a}.token.function-name{color:#6196cc}.token.boolean,.token.function,.token.number{color:#f08d49}.token.class-name,.token.constant,.token.property,.token.symbol{color:#f8c555}.token.atrule,.token.builtin,.token.important,.token.keyword,.token.selector{color:#cc99cd}.token.attr-value,.token.char,.token.regex,.token.string,.token.variable{color:#7ec699}.token.entity,.token.operator,.token.url{color:#67cdcc}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:green}.theme-default-content code{color:#476582;padding:.25rem .5rem;margin:0;font-size:.85em;background-color:rgba(27,31,35,.05);border-radius:3px}.theme-default-content code .token.deleted{color:#ec5975}.theme-default-content code .token.inserted{color:#1389fd}.theme-default-content pre,.theme-default-content pre[class*=language-]{line-height:1.4;padding:1.25rem 1.5rem;margin:.85rem 0;background-color:#282c34;border-radius:6px;overflow:auto}.theme-default-content pre[class*=language-] code,.theme-default-content pre code{color:#fff;padding:0;background-color:transparent;border-radius:0}div[class*=language-]{position:relative;background-color:#282c34;border-radius:6px}div[class*=language-] .highlight-lines{-webkit-user-select:none;user-select:none;padding-top:1.3rem;position:absolute;top:0;left:0;width:100%;line-height:1.4}div[class*=language-] .highlight-lines .highlighted{background-color:rgba(0,0,0,.66)}div[class*=language-] pre,div[class*=language-] pre[class*=language-]{background:transparent;position:relative;z-index:1}div[class*=language-]:before{position:absolute;z-index:3;top:.8em;right:1em;font-size:.75rem;color:hsla(0,0%,100%,.4)}div[class*=language-]:not(.line-numbers-mode) .line-numbers-wrapper{display:none}div[class*=language-].line-numbers-mode .highlight-lines .highlighted{position:relative}div[class*=language-].line-numbers-mode .highlight-lines .highlighted:before{content:\" \";position:absolute;z-index:3;left:0;top:0;display:block;width:3.5rem;height:100%;background-color:rgba(0,0,0,.66)}div[class*=language-].line-numbers-mode pre{padding-left:4.5rem;vertical-align:middle}div[class*=language-].line-numbers-mode .line-numbers-wrapper{position:absolute;top:0;width:3.5rem;text-align:center;color:hsla(0,0%,100%,.3);padding:1.25rem 0;line-height:1.4}div[class*=language-].line-numbers-mode .line-numbers-wrapper br{-webkit-user-select:none;user-select:none}div[class*=language-].line-numbers-mode .line-numbers-wrapper .line-number{position:relative;z-index:4;-webkit-user-select:none;user-select:none;font-size:.85em}div[class*=language-].line-numbers-mode:after{content:\"\";position:absolute;z-index:2;top:0;left:0;width:3.5rem;height:100%;border-radius:6px 0 0 6px;border-right:1px solid rgba(0,0,0,.66);background-color:#282c34}div[class~=language-js]:before{content:\"js\"}div[class~=language-ts]:before{content:\"ts\"}div[class~=language-html]:before{content:\"html\"}div[class~=language-md]:before{content:\"md\"}div[class~=language-vue]:before{content:\"vue\"}div[class~=language-css]:before{content:\"css\"}div[class~=language-sass]:before{content:\"sass\"}div[class~=language-scss]:before{content:\"scss\"}div[class~=language-less]:before{content:\"less\"}div[class~=language-stylus]:before{content:\"stylus\"}div[class~=language-go]:before{content:\"go\"}div[class~=language-java]:before{content:\"java\"}div[class~=language-c]:before{content:\"c\"}div[class~=language-sh]:before{content:\"sh\"}div[class~=language-yaml]:before{content:\"yaml\"}div[class~=language-py]:before{content:\"py\"}div[class~=language-docker]:before{content:\"docker\"}div[class~=language-dockerfile]:before{content:\"dockerfile\"}div[class~=language-makefile]:before{content:\"makefile\"}div[class~=language-javascript]:before{content:\"js\"}div[class~=language-typescript]:before{content:\"ts\"}div[class~=language-markup]:before{content:\"html\"}div[class~=language-markdown]:before{content:\"md\"}div[class~=language-json]:before{content:\"json\"}div[class~=language-ruby]:before{content:\"rb\"}div[class~=language-python]:before{content:\"py\"}div[class~=language-bash]:before{content:\"sh\"}div[class~=language-php]:before{content:\"php\"}.custom-block .custom-block-title{font-weight:600;margin-bottom:-.4rem}.custom-block.danger,.custom-block.tip,.custom-block.warning{padding:.1rem 1.5rem;border-left-width:.5rem;border-left-style:solid;margin:1rem 0}.custom-block.tip{background-color:#f3f5f7;border-color:#42b983}.custom-block.warning{background-color:rgba(255,229,100,.3);border-color:#e7c000;color:#6b5900}.custom-block.warning .custom-block-title{color:#b29400}.custom-block.warning a{color:#2c3e50}.custom-block.danger{background-color:#ffe6e6;border-color:#c00;color:#4d0000}.custom-block.danger .custom-block-title{color:#900}.custom-block.danger a{color:#2c3e50}.custom-block.details{display:block;position:relative;border-radius:2px;margin:1.6em 0;padding:1.6em;background-color:#eee}.custom-block.details h4{margin-top:0}.custom-block.details figure:last-child,.custom-block.details p:last-child{margin-bottom:0;padding-bottom:0}.custom-block.details summary{outline:none;cursor:pointer}.arrow{display:inline-block;width:0;height:0}.arrow.up{border-bottom:6px solid #ccc}.arrow.down,.arrow.up{border-left:4px solid transparent;border-right:4px solid transparent}.arrow.down{border-top:6px solid #ccc}.arrow.right{border-left:6px solid #ccc}.arrow.left,.arrow.right{border-top:4px solid transparent;border-bottom:4px solid transparent}.arrow.left{border-right:6px solid #ccc}.theme-default-content:not(.custom){max-width:740px;margin:0 auto;padding:2rem 2.5rem}@media (max-width:959px){.theme-default-content:not(.custom){padding:2rem}}@media (max-width:419px){.theme-default-content:not(.custom){padding:1.5rem}}.table-of-contents .badge{vertical-align:middle}body,html{padding:0;margin:0;background-color:#fff}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-size:16px;color:#2c3e50}.page{padding-left:20rem}.navbar{z-index:20;right:0;height:3.6rem;background-color:#fff;box-sizing:border-box;border-bottom:1px solid #eaecef}.navbar,.sidebar-mask{position:fixed;top:0;left:0}.sidebar-mask{z-index:9;width:100vw;height:100vh;display:none}.sidebar{font-size:16px;background-color:#fff;width:20rem;position:fixed;z-index:10;margin:0;top:3.6rem;left:0;bottom:0;box-sizing:border-box;border-right:1px solid #eaecef;overflow-y:auto}.theme-default-content:not(.custom)>:first-child{margin-top:3.6rem}.theme-default-content:not(.custom) a:hover{text-decoration:underline}.theme-default-content:not(.custom) p.demo{padding:1rem 1.5rem;border:1px solid #ddd;border-radius:4px}.theme-default-content:not(.custom) img{max-width:100%}.theme-default-content.custom{padding:0;margin:0}.theme-default-content.custom img{max-width:100%}a{font-weight:500;text-decoration:none}a,p a code{color:#1389fd}p a code{font-weight:400}kbd{background:#eee;border:.15rem solid #ddd;border-bottom:.25rem solid #ddd;border-radius:.15rem;padding:0 .15em}blockquote{font-size:1rem;color:#999;border-left:.2rem solid #dfe2e5;margin:1rem 0;padding:.25rem 0 .25rem 1rem}blockquote>p{margin:0}ol,ul{padding-left:1.2em}strong{font-weight:600}h1,h2,h3,h4,h5,h6{font-weight:600;line-height:1.25}.theme-default-content:not(.custom)>h1,.theme-default-content:not(.custom)>h2,.theme-default-content:not(.custom)>h3,.theme-default-content:not(.custom)>h4,.theme-default-content:not(.custom)>h5,.theme-default-content:not(.custom)>h6{margin-top:-3.1rem;padding-top:4.6rem;margin-bottom:0}.theme-default-content:not(.custom)>h1:first-child,.theme-default-content:not(.custom)>h2:first-child,.theme-default-content:not(.custom)>h3:first-child,.theme-default-content:not(.custom)>h4:first-child,.theme-default-content:not(.custom)>h5:first-child,.theme-default-content:not(.custom)>h6:first-child{margin-top:-1.5rem;margin-bottom:1rem}.theme-default-content:not(.custom)>h1:first-child+.custom-block,.theme-default-content:not(.custom)>h1:first-child+p,.theme-default-content:not(.custom)>h1:first-child+pre,.theme-default-content:not(.custom)>h2:first-child+.custom-block,.theme-default-content:not(.custom)>h2:first-child+p,.theme-default-content:not(.custom)>h2:first-child+pre,.theme-default-content:not(.custom)>h3:first-child+.custom-block,.theme-default-content:not(.custom)>h3:first-child+p,.theme-default-content:not(.custom)>h3:first-child+pre,.theme-default-content:not(.custom)>h4:first-child+.custom-block,.theme-default-content:not(.custom)>h4:first-child+p,.theme-default-content:not(.custom)>h4:first-child+pre,.theme-default-content:not(.custom)>h5:first-child+.custom-block,.theme-default-content:not(.custom)>h5:first-child+p,.theme-default-content:not(.custom)>h5:first-child+pre,.theme-default-content:not(.custom)>h6:first-child+.custom-block,.theme-default-content:not(.custom)>h6:first-child+p,.theme-default-content:not(.custom)>h6:first-child+pre{margin-top:2rem}h1:focus .header-anchor,h1:hover .header-anchor,h2:focus .header-anchor,h2:hover .header-anchor,h3:focus .header-anchor,h3:hover .header-anchor,h4:focus .header-anchor,h4:hover .header-anchor,h5:focus .header-anchor,h5:hover .header-anchor,h6:focus .header-anchor,h6:hover .header-anchor{opacity:1}h1{font-size:2.2rem}h2{font-size:1.65rem;padding-bottom:.3rem;border-bottom:1px solid #eaecef}h3{font-size:1.35rem}a.header-anchor{font-size:.85em;float:left;margin-left:-.87em;padding-right:.23em;margin-top:.125em;opacity:0}a.header-anchor:focus,a.header-anchor:hover{text-decoration:none}.line-number,code,kbd{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}ol,p,ul{line-height:1.7}hr{border:0;border-top:1px solid #eaecef}table{border-collapse:collapse;margin:1rem 0;display:block;overflow-x:auto}tr{border-top:1px solid #dfe2e5}tr:nth-child(2n){background-color:#f6f8fa}td,th{border:1px solid #dfe2e5;padding:.6em 1em}.theme-container.sidebar-open .sidebar-mask{display:block}.theme-container.no-navbar .theme-default-content:not(.custom)>h1,.theme-container.no-navbar h2,.theme-container.no-navbar h3,.theme-container.no-navbar h4,.theme-container.no-navbar h5,.theme-container.no-navbar h6{margin-top:1.5rem;padding-top:0}.theme-container.no-navbar .sidebar{top:0}@media (min-width:720px){.theme-container.no-sidebar .sidebar{display:none}.theme-container.no-sidebar .page{padding-left:0}}@media (max-width:959px){.sidebar{font-size:15px;width:16.4rem}.page{padding-left:16.4rem}}@media (max-width:719px){.sidebar{top:0;padding-top:3.6rem;transform:translateX(-100%);transition:transform .2s ease}.page{padding-left:0}.theme-container.sidebar-open .sidebar{transform:translateX(0)}.theme-container.no-navbar .sidebar{padding-top:0}}@media (max-width:419px){h1{font-size:1.9rem}.theme-default-content div[class*=language-]{margin:.85rem -1.5rem;border-radius:0}}#nprogress{pointer-events:none}#nprogress .bar{background:#1389fd;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #1389fd,0 0 5px #1389fd;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border-color:#1389fd transparent transparent #1389fd;border-style:solid;border-width:2px;border-radius:50%;-webkit-animation:nprogress-spinner .4s linear infinite;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@-webkit-keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.icon.outbound{color:#aaa;display:inline-block;vertical-align:middle;position:relative;top:-1px}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.home{padding:3.6rem 2rem 0;max-width:960px;margin:0 auto;display:block}.home .hero{text-align:center}.home .hero img{max-width:100%;max-height:280px;display:block;margin:3rem auto 1.5rem}.home .hero h1{font-size:3rem}.home .hero .action,.home .hero .description,.home .hero h1{margin:1.8rem auto}.home .hero .description{max-width:35rem;font-size:1.6rem;line-height:1.3;color:#6a8bad}.home .hero .action-button{display:inline-block;font-size:1.2rem;color:#fff;background-color:#1389fd;padding:.8rem 1.6rem;border-radius:4px;transition:background-color .1s ease;box-sizing:border-box;border-bottom:1px solid #027bf3}.home .hero .action-button:hover{background-color:#2b95fd}.home .features{border-top:1px solid #eaecef;padding:1.2rem 0;margin-top:2.5rem;display:flex;flex-wrap:wrap;align-items:flex-start;align-content:stretch;justify-content:space-between}.home .feature{flex-grow:1;flex-basis:30%;max-width:30%}.home .feature h2{font-size:1.4rem;font-weight:500;border-bottom:none;padding-bottom:0;color:#3a5169}.home .feature p{color:#4e6e8e}.home .footer{padding:2.5rem;border-top:1px solid #eaecef;text-align:center;color:#4e6e8e}@media (max-width:719px){.home .features{flex-direction:column}.home .feature{max-width:100%;padding:0 2.5rem}}@media (max-width:419px){.home{padding-left:1.5rem;padding-right:1.5rem}.home .hero img{max-height:210px;margin:2rem auto 1.2rem}.home .hero h1{font-size:2rem}.home .hero .action,.home .hero .description,.home .hero h1{margin:1.2rem auto}.home .hero .description{font-size:1.2rem}.home .hero .action-button{font-size:1rem;padding:.6rem 1.2rem}.home .feature h2{font-size:1.25rem}}.search-box{display:inline-block;position:relative;margin-right:1rem}.search-box input{cursor:text;width:10rem;height:2rem;color:#4e6e8e;display:inline-block;border:1px solid #cfd4db;border-radius:2rem;font-size:.9rem;line-height:2rem;padding:0 .5rem 0 2rem;outline:none;transition:all .2s ease;background:#fff url(/assets/img/search.83621669.svg) .6rem .5rem no-repeat;background-size:1rem}.search-box input:focus{cursor:auto;border-color:#1389fd}.search-box .suggestions{background:#fff;width:20rem;position:absolute;top:2rem;border:1px solid #cfd4db;border-radius:6px;padding:.4rem;list-style-type:none}.search-box .suggestions.align-right{right:0}.search-box .suggestion{line-height:1.4;padding:.4rem .6rem;border-radius:4px;cursor:pointer}.search-box .suggestion a{white-space:normal;color:#5d82a6}.search-box .suggestion a .page-title{font-weight:600}.search-box .suggestion a .header{font-size:.9em;margin-left:.25em}.search-box .suggestion.focused{background-color:#f3f4f5}.search-box .suggestion.focused a{color:#1389fd}@media (max-width:959px){.search-box input{cursor:pointer;width:0;border-color:transparent;position:relative}.search-box input:focus{cursor:text;left:0;width:10rem}}@media (-ms-high-contrast:none){.search-box input{height:2rem}}@media (max-width:959px) and (min-width:719px){.search-box .suggestions{left:0}}@media (max-width:719px){.search-box{margin-right:0}.search-box input{left:1rem}.search-box .suggestions{right:0}}@media (max-width:419px){.search-box .suggestions{width:calc(100vw - 4rem)}.search-box input:focus{width:8rem}}.sidebar-button{cursor:pointer;display:none;width:1.25rem;height:1.25rem;position:absolute;padding:.6rem;top:.6rem;left:1rem}.sidebar-button .icon{display:block;width:1.25rem;height:1.25rem}@media (max-width:719px){.sidebar-button{display:block}}.dropdown-enter,.dropdown-leave-to{height:0!important}.dropdown-wrapper{cursor:pointer}.dropdown-wrapper .dropdown-title,.dropdown-wrapper .mobile-dropdown-title{display:block;font-size:.9rem;font-family:inherit;cursor:inherit;padding:inherit;line-height:1.4rem;background:transparent;border:none;font-weight:500;color:#2c3e50}.dropdown-wrapper .dropdown-title:hover,.dropdown-wrapper .mobile-dropdown-title:hover{border-color:transparent}.dropdown-wrapper .dropdown-title .arrow,.dropdown-wrapper .mobile-dropdown-title .arrow{vertical-align:middle;margin-top:-1px;margin-left:.4rem}.dropdown-wrapper .mobile-dropdown-title{display:none;font-weight:600}.dropdown-wrapper .mobile-dropdown-title font-size inherit:hover{color:#1389fd}.dropdown-wrapper .nav-dropdown .dropdown-item{color:inherit;line-height:1.7rem}.dropdown-wrapper .nav-dropdown .dropdown-item h4{margin:.45rem 0 0;border-top:1px solid #eee;padding:1rem 1.5rem .45rem 1.25rem}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subitem-wrapper{padding:0;list-style:none}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subitem-wrapper .dropdown-subitem{font-size:.9em}.dropdown-wrapper .nav-dropdown .dropdown-item a{display:block;line-height:1.7rem;position:relative;border-bottom:none;font-weight:400;margin-bottom:0;padding:0 1.5rem 0 1.25rem}.dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active,.dropdown-wrapper .nav-dropdown .dropdown-item a:hover{color:#1389fd}.dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active:after{content:\"\";width:0;height:0;border-left:5px solid #1389fd;border-top:3px solid transparent;border-bottom:3px solid transparent;position:absolute;top:calc(50% - 2px);left:9px}.dropdown-wrapper .nav-dropdown .dropdown-item:first-child h4{margin-top:0;padding-top:0;border-top:0}@media (max-width:719px){.dropdown-wrapper.open .dropdown-title{margin-bottom:.5rem}.dropdown-wrapper .dropdown-title{display:none}.dropdown-wrapper .mobile-dropdown-title{display:block}.dropdown-wrapper .nav-dropdown{transition:height .1s ease-out;overflow:hidden}.dropdown-wrapper .nav-dropdown .dropdown-item h4{border-top:0;margin-top:0;padding-top:0}.dropdown-wrapper .nav-dropdown .dropdown-item>a,.dropdown-wrapper .nav-dropdown .dropdown-item h4{font-size:15px;line-height:2rem}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subitem{font-size:14px;padding-left:1rem}}@media (min-width:719px){.dropdown-wrapper{height:1.8rem}.dropdown-wrapper.open .nav-dropdown,.dropdown-wrapper:hover .nav-dropdown{display:block!important}.dropdown-wrapper.open:blur{display:none}.dropdown-wrapper .nav-dropdown{display:none;height:auto!important;box-sizing:border-box;max-height:calc(100vh - 2.7rem);overflow-y:auto;position:absolute;top:100%;right:0;background-color:#fff;padding:.6rem 0;border:1px solid;border-color:#ddd #ddd #ccc;text-align:left;border-radius:.25rem;white-space:nowrap;margin:0}}.nav-links{display:inline-block}.nav-links a{line-height:1.4rem;color:inherit}.nav-links a.router-link-active,.nav-links a:hover{color:#1389fd}.nav-links .nav-item{position:relative;display:inline-block;margin-left:1.5rem;line-height:2rem}.nav-links .nav-item:first-child{margin-left:0}.nav-links .repo-link{margin-left:1.5rem}@media (max-width:719px){.nav-links .nav-item,.nav-links .repo-link{margin-left:0}}@media (min-width:719px){.nav-links a.router-link-active,.nav-links a:hover{color:#2c3e50}.nav-item>a:not(.external).router-link-active,.nav-item>a:not(.external):hover{margin-bottom:-2px;border-bottom:2px solid #2692fd}}.navbar{padding:.7rem 1.5rem;line-height:2.2rem}.navbar a,.navbar img,.navbar span{display:inline-block}.navbar .logo{height:2.2rem;min-width:2.2rem;margin-right:.8rem;vertical-align:top}.navbar .site-name{font-size:1.3rem;font-weight:600;color:#2c3e50;position:relative}.navbar .links{padding-left:1.5rem;box-sizing:border-box;background-color:#fff;white-space:nowrap;font-size:.9rem;position:absolute;right:1.5rem;top:.7rem;display:flex}.navbar .links .search-box{flex:0 0 auto;vertical-align:top}@media (max-width:719px){.navbar{padding-left:4rem}.navbar .can-hide{display:none}.navbar .links{padding-left:1.5rem}.navbar .site-name{width:calc(100vw - 9.4rem);overflow:hidden;white-space:nowrap;text-overflow:ellipsis}}.page-edit{max-width:740px;margin:0 auto;padding:2rem 2.5rem}@media (max-width:959px){.page-edit{padding:2rem}}@media (max-width:419px){.page-edit{padding:1.5rem}}.page-edit{padding-top:1rem;padding-bottom:1rem;overflow:auto}.page-edit .edit-link{display:inline-block}.page-edit .edit-link a{color:#4e6e8e;margin-right:.25rem}.page-edit .last-updated{float:right;font-size:.9em}.page-edit .last-updated .prefix{font-weight:500;color:#4e6e8e}.page-edit .last-updated .time{font-weight:400;color:#767676}@media (max-width:719px){.page-edit .edit-link{margin-bottom:.5rem}.page-edit .last-updated{font-size:.8em;float:none;text-align:left}}.page-nav{max-width:740px;margin:0 auto;padding:2rem 2.5rem}@media (max-width:959px){.page-nav{padding:2rem}}@media (max-width:419px){.page-nav{padding:1.5rem}}.page-nav{padding-top:1rem;padding-bottom:0}.page-nav .inner{min-height:2rem;margin-top:0;border-top:1px solid #eaecef;padding-top:1rem;overflow:auto}.page-nav .next{float:right}.page{padding-bottom:2rem;display:block}.sidebar-group .sidebar-group{padding-left:.5em}.sidebar-group:not(.collapsable) .sidebar-heading:not(.clickable){cursor:auto;color:inherit}.sidebar-group.is-sub-group{padding-left:0}.sidebar-group.is-sub-group>.sidebar-heading{font-size:.95em;line-height:1.4;font-weight:400;padding-left:2rem}.sidebar-group.is-sub-group>.sidebar-heading:not(.clickable){opacity:.5}.sidebar-group.is-sub-group>.sidebar-group-items{padding-left:1rem}.sidebar-group.is-sub-group>.sidebar-group-items>li>.sidebar-link{font-size:.95em;border-left:none}.sidebar-group.depth-2>.sidebar-heading{border-left:none}.sidebar-heading{color:#2c3e50;transition:color .15s ease;cursor:pointer;font-size:1.1em;font-weight:700;padding:.35rem 1.5rem .35rem 1.25rem;width:100%;box-sizing:border-box;margin:0;border-left:.25rem solid transparent}.sidebar-heading.open,.sidebar-heading:hover{color:inherit}.sidebar-heading .arrow{position:relative;top:-.12em;left:.5em}.sidebar-heading.clickable.active{font-weight:600;color:#1389fd;border-left-color:#1389fd}.sidebar-heading.clickable:hover{color:#1389fd}.sidebar-group-items{transition:height .1s ease-out;font-size:.95em;overflow:hidden}.sidebar .sidebar-sub-headers{padding-left:1rem;font-size:.95em}a.sidebar-link{font-size:1em;font-weight:400;display:inline-block;color:#2c3e50;border-left:.25rem solid transparent;padding:.35rem 1rem .35rem 1.25rem;line-height:1.4;width:100%;box-sizing:border-box}a.sidebar-link:hover{color:#1389fd}a.sidebar-link.active{font-weight:600;color:#1389fd;border-left-color:#1389fd}.sidebar-group a.sidebar-link{padding-left:2rem}.sidebar-sub-headers a.sidebar-link{padding-top:.25rem;padding-bottom:.25rem;border-left:none}.sidebar-sub-headers a.sidebar-link.active{font-weight:500}.sidebar ul{padding:0;margin:0;list-style-type:none}.sidebar a{display:inline-block}.sidebar .nav-links{display:none;border-bottom:1px solid #eaecef;padding:.5rem 0 .75rem}.sidebar .nav-links a{font-weight:600}.sidebar .nav-links .nav-item,.sidebar .nav-links .repo-link{display:block;line-height:1.25rem;font-size:1.1em;padding:.5rem 0 .5rem 1.5rem}.sidebar>.sidebar-links{padding:1.5rem 0}.sidebar>.sidebar-links>li>a.sidebar-link{font-size:1.1em;line-height:1.7;font-weight:700}.sidebar>.sidebar-links>li:not(:first-child){margin-top:.75rem}@media (max-width:719px){.sidebar .nav-links{display:block}.sidebar .nav-links .dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active:after{top:calc(1rem - 2px)}.sidebar>.sidebar-links{padding:1rem 0}}.copyright{text-align:center;margin:50px 16px 8px;color:grey;font-size:.9em}.f-links a{font-weight:400;text-decoration:underline;font-size:.9em;color:#1e90ff!important}.f-links a:hover{opacity:.8!important}.book{transition:box-shadow .2s;max-width:180px;box-shadow:2px 2px 5px #aaa;cursor:pointer}.book:hover{box-shadow:5px 5px 8px #888}.theme-code-block[data-v-759a7d02]{display:none}.theme-code-block__active[data-v-759a7d02]{display:block}.theme-code-block>pre[data-v-759a7d02]{background-color:orange}.badge[data-v-15b7b770]{display:inline-block;font-size:14px;height:18px;line-height:18px;border-radius:3px;padding:0 6px;color:#fff}.badge.green[data-v-15b7b770],.badge.tip[data-v-15b7b770],.badge[data-v-15b7b770]{background-color:#42b983}.badge.error[data-v-15b7b770]{background-color:#da5961}.badge.warn[data-v-15b7b770],.badge.warning[data-v-15b7b770],.badge.yellow[data-v-15b7b770]{background-color:#e7c000}.badge+.badge[data-v-15b7b770]{margin-left:5px}.theme-code-group__nav[data-v-deefee04]{margin-bottom:-35px;background-color:#282c34;padding-bottom:22px;border-top-left-radius:6px;border-top-right-radius:6px;padding-left:10px;padding-top:10px}.theme-code-group__ul[data-v-deefee04]{margin:auto 0;padding-left:0;display:inline-flex;list-style:none}.theme-code-group__nav-tab[data-v-deefee04]{border:0;padding:5px;cursor:pointer;background-color:transparent;font-size:.85em;line-height:1.4;color:hsla(0,0%,100%,.9);font-weight:600}.theme-code-group__nav-tab-active[data-v-deefee04]{border-bottom:1px solid #42b983}.pre-blank[data-v-deefee04]{color:#42b983}"
  },
  {
    "path": "docs/assets/js/10.5f481ee1.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[10],{583:function(t,s){t.exports=\"data:image/png;base64,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J598cvF5NIITjs6BoyOvvPKKGYzao+zJJ59MK6+8svVj5plrAy1da5R88cUX6eCDD07ff/990e8XXnghXX311QnnlBNiIwRynuISAj5833jjjfZxU/HM+a+//rrgGsfgHF9ORhZffHHLk+acfir77rvvpg8//NBsT5zC82mnnVZ8PPXtt99Od999dzrkkEMsENBGU6R2awB8yBJScfgbbrjBgMcQbr/9dsOUSHz44Ycb6eutt545JXW33Xbb9O233yaidVlwfp4KZ5xxRlphhRWGnN58882tjdtuuy0tsMAC9kWkY4891gIM7d58883m9BgbxEtOPPHEhPOfffbZ6b777rM25p133oQxvPzyy6pWKy2jrlWnKjpz/PHHm/PvvPPO6d5777UZ2hprrGFP8vPOO8+uwEl5YuP8zOjgigC96aabps8//zwdddRRabnllrMZxDLLLGPXnHrqqfYwqbilBQLugTz00ENFlcsuu8zS48ePt6DALOOUU04x57/ooovM8QlMBAcWFf3DpGikxonaBYClllrKouiCCy6Yll56aZuyg99TTz1lMAIwK+pzzDFHOv/889NCCy2UqIvTIhiEj/qUERxOOumktMUWW1iblEkgljaY/p9++ukqtkBCuzwZttlmGyt/8cUXi/O8XjCd3GSTTdJcc81lbfCqgNQ1ABSdr3ECx4YrsD3iiCPs89a8ox944IHW6zfeeMM0dsDskM+eM2ODK3gkMCPYCAGP1zYFPpy0/Bpnlf/3R68AOLaEmQei14pLL73UHhI77rhjWmedddJ8882Xll9++UQe4ZWlSVK7eaqmZAIRYhGe7ghTMqT8vXvIxcEfe+wxCxa777671eMPBHUjCy+8cFHN90Pvdrx6SFZccUXrE1NNHP7LL7+0g/M8JUKmDwGcFWx57+fpz5Se93N9rZoAgfzwww+mCb6amlPA6x/rAXJ6q9TlH2YMCG1zH3hkLYJXDq1B8TqK8O6voESemSuCLeyyyy6WrsMf4SBd7lPtAkC5g8qLeL1zewdVHQUL3vV6IeoDbTMDYNrJKwmGQ/CQUfbi3oPUJq9a7M/j9HBKANeakHBQQKiyA63TqG63GidZd911EzO99957z5ycazX9Jw3fiBYWLeP++HULV1zbZGMCgBDU05jIXBYt9LGT0EvhP9CQ89966622a8D9LrjgAivv5b1z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\"},584:function(t,s){t.exports=\"data:image/png;base64,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\"},585:function(t,s){t.exports=\"data:image/png;base64,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\"},586:function(t,s){t.exports=\"data:image/png;base64,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\"},587:function(t,s,a){t.exports=a.p+\"assets/img/3-14.e8ad30dc.png\"},588:function(t,s){t.exports=\"data:image/png;base64,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\"},589:function(t,s){t.exports=\"data:image/png;base64,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\"},884:function(t,s,a){\"use strict\";a.r(s);var n=a(45),r=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_3-4-按钮\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-4-按钮\"}},[t._v(\"#\")]),t._v(\" 3.4 按钮\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_3-4-1-material组件库中的按钮\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-4-1-material组件库中的按钮\"}},[t._v(\"#\")]),t._v(\" 3.4.1 Material组件库中的按钮\")]),t._v(\" \"),n(\"p\",[t._v(\"Material 组件库中提供了多种按钮组件如\"),n(\"code\",[t._v(\"RaisedButton\")]),t._v(\"、\"),n(\"code\",[t._v(\"FlatButton\")]),t._v(\"、\"),n(\"code\",[t._v(\"OutlineButton\")]),t._v(\"等，它们都是直接或间接对\"),n(\"code\",[t._v(\"RawMaterialButton\")]),t._v(\"组件的包装定制，所以他们大多数属性都和\"),n(\"code\",[t._v(\"RawMaterialButton\")]),t._v(\"一样。在介绍各个按钮时我们先介绍其默认外观，而按钮的外观大都可以通过属性来自定义，我们在后面统一介绍这些属性。另外，所有Material 库中的按钮都有如下相同点：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"按下时都会有“水波动画”（又称“涟漪动画”，就是点击时按钮上会出现水波荡漾的动画）。\")]),t._v(\" \"),n(\"li\",[t._v(\"有一个\"),n(\"code\",[t._v(\"onPressed\")]),t._v(\"属性来设置点击回调，当按钮按下时会执行该回调，如果不提供该回调则按钮会处于禁用状态，禁用状态不响应用户点击。\")])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"raisedbutton\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#raisedbutton\"}},[t._v(\"#\")]),t._v(\" RaisedButton\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"RaisedButton\")]),t._v(' 即\"漂浮\"按钮，它默认带有阴影和灰色背景。按下后，阴影会变大，如图3-10所示：')]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(583),alt:\"图3-10\"}})]),t._v(\" \"),n(\"p\",[t._v(\"使用\"),n(\"code\",[t._v(\"RaisedButton\")]),t._v(\"非常简单，如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"normal\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"h3\",{attrs:{id:\"flatbutton\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#flatbutton\"}},[t._v(\"#\")]),t._v(\" FlatButton\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"FlatButton\")]),t._v(\"即扁平按钮，默认背景透明并不带阴影。按下后，会有背景色，如图3-11所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(584),alt:\"图3-11\"}})]),t._v(\" \"),n(\"p\",[t._v(\"使用FlatButton也很简单，代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"normal\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"h3\",{attrs:{id:\"outlinebutton\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#outlinebutton\"}},[t._v(\"#\")]),t._v(\" OutlineButton\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"OutlineButton\")]),t._v(\"默认有一个边框，不带阴影且背景透明。按下后，边框颜色会变亮、同时出现背景和阴影(较弱)，如图3-12所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(585),alt:\"图3-12\"}})]),t._v(\" \"),n(\"p\",[t._v(\"使用\"),n(\"code\",[t._v(\"OutlineButton\")]),t._v(\"也很简单，代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"OutlineButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"normal\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"h3\",{attrs:{id:\"iconbutton\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#iconbutton\"}},[t._v(\"#\")]),t._v(\" IconButton\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"IconButton\")]),t._v(\"是一个可点击的Icon，不包括文字，默认没有背景，点击后会出现背景，如图3-13所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(586),alt:\"图3-13\"}})]),t._v(\" \"),n(\"p\",[t._v(\"代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"IconButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  icon\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"thumb_up\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"h3\",{attrs:{id:\"带图标的按钮\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#带图标的按钮\"}},[t._v(\"#\")]),t._v(\" 带图标的按钮\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"RaisedButton\")]),t._v(\"、\"),n(\"code\",[t._v(\"FlatButton\")]),t._v(\"、\"),n(\"code\",[t._v(\"OutlineButton\")]),t._v(\"都有一个\"),n(\"code\",[t._v(\"icon\")]),t._v(\" 构造函数，通过它可以轻松创建带图标的按钮，如图3-14所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(587),alt:\"图3-14\"}})]),t._v(\" \"),n(\"p\",[t._v(\"代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"RaisedButton\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  icon\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"send\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  label\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"发送\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\nOutlineButton\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  icon\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"add\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  label\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"添加\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\nFlatButton\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  icon\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"info\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  label\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"详情\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),n(\"h2\",{attrs:{id:\"_3-4-2-自定义按钮外观\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-4-2-自定义按钮外观\"}},[t._v(\"#\")]),t._v(\" 3.4.2 自定义按钮外观\")]),t._v(\" \"),n(\"p\",[t._v(\"按钮外观可以通过其属性来定义，不同按钮属性大同小异，我们以FlatButton为例，介绍一下常见的按钮属性，详细的信息可以查看API文档。\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"  \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//按钮点击回调\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"textColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//按钮文字颜色\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"disabledTextColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//按钮禁用时的文字颜色\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//按钮背景颜色\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"disabledColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//按钮禁用时的背景颜色\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"highlightColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//按钮按下时的背景颜色\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"splashColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//点击时，水波动画中水波的颜色\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"colorBrightness\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//按钮主题，默认是浅色主题 \")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//按钮的填充\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"shape\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//外形\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//按钮的内容\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"其中大多数属性名都是自解释的，我们不赘述。下面我们通过一个示例来看看如何自定义按钮。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"示例\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),n(\"p\",[t._v(\"定义一个背景蓝色，两边圆角的按钮。效果如图3-15所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(588),alt:\"图3-15\"}})]),t._v(\" \"),n(\"p\",[t._v(\"代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  highlightColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"700\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  colorBrightness\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Brightness\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dark\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  splashColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Submit\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  shape\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RoundedRectangleBorder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"borderRadius\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BorderRadius\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"circular\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"很简单吧，在上面的代码中，我们主要通过\"),n(\"code\",[t._v(\"shape\")]),t._v(\"来指定其外形为一个圆角矩形。因为按钮背景是蓝色(深色)，我们需要指定按钮主题\"),n(\"code\",[t._v(\"colorBrightness\")]),t._v(\"为\"),n(\"code\",[t._v(\"Brightness.dark\")]),t._v(\"，这是为了保证按钮文字颜色为浅色。\")]),t._v(\" \"),n(\"p\",[t._v(\"Flutter 中没有提供去除背景的设置，假若我们需要去除背景，则可以通过将背景颜色设置为全透明来实现。对应上面的代码，便是将 \"),n(\"code\",[t._v(\"color: Colors.blue\")]),t._v(\" 替换为 \"),n(\"code\",[t._v(\"color: Color(0x000000)\")]),t._v(\"。\")]),t._v(\" \"),n(\"p\",[t._v(\"细心的读者可能会发现这个按钮没有阴影(点击之后也没有)，这样会显得没有质感。其实这也很容易，将上面的\"),n(\"code\",[t._v(\"FlatButton\")]),t._v(\"换成\"),n(\"code\",[t._v(\"RaisedButton\")]),t._v(\"就行，其它代码不用改（这里 color 也不做更改），换了之后的效果如图3-16所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(589),alt:\"图3-16\"}})]),t._v(\" \"),n(\"p\",[t._v(\"是不是有质感了！之所以会这样，是因为\"),n(\"code\",[t._v(\"RaisedButton\")]),t._v(\"默认有配置阴影：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"elevation \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//正常状态下的阴影\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"highlightElevation \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"8.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//按下时的阴影\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"disabledElevation \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 禁用时的阴影\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"值得注意的是，在Material 组件库中，我们会在很多组件中见到elevation相关的属性，它们都是用来控制阴影的，这是因为阴影在Material设计风格中是一种很重要的表现形式，以后在介绍其它组件时，便不再赘述。\")]),t._v(\" \"),n(\"p\",[t._v('如果我们想实现一个背景渐变的圆角按钮，按钮有没有相应的属性呢？答案是否定的，但是，我们可以通过其它方式来实现，我们将在后面\"自定义组件\"一章中实现。')])])}),[],!1,null,null,null);s.default=r.exports}}]);"
  },
  {
    "path": "docs/assets/js/100.ce32ed9e.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[100],{472:function(t,s,n){t.exports=n.p+\"assets/img/5-1.239dadc0.png\"},795:function(t,s,n){\"use strict\";n.r(s);var a=n(45),e=Object(a.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h2\",{attrs:{id:\"_5-1-填充-padding\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_5-1-填充-padding\"}},[t._v(\"#\")]),t._v(\" 5.1 填充（Padding）\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"Padding\")]),t._v(\"可以给其子节点添加填充（留白），和边距效果类似。我们在前面很多示例中都已经使用过它了，现在来看看它的定义：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  EdgeInsetsGeometry padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  Widget child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[a(\"code\",[t._v(\"EdgeInsetsGeometry\")]),t._v(\"是一个抽象类，开发中，我们一般都使用\"),a(\"code\",[t._v(\"EdgeInsets\")]),t._v(\"类，它是\"),a(\"code\",[t._v(\"EdgeInsetsGeometry\")]),t._v(\"的一个子类，定义了一些设置填充的便捷方法。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"edgeinsets\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#edgeinsets\"}},[t._v(\"#\")]),t._v(\" EdgeInsets\")]),t._v(\" \"),a(\"p\",[t._v(\"我们看看\"),a(\"code\",[t._v(\"EdgeInsets\")]),t._v(\"提供的便捷方法：\")]),t._v(\" \"),a(\"ul\",[a(\"li\",[a(\"code\",[t._v(\"fromLTRB(double left, double top, double right, double bottom)\")]),t._v(\"：分别指定四个方向的填充。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"all(double value)\")]),t._v(\" : 所有方向均使用相同数值的填充。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"only({left, top, right ,bottom })\")]),t._v(\"：可以设置具体某个方向的填充(可以同时指定多个方向)。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"symmetric({ vertical, horizontal })\")]),t._v(\"：用于设置对称方向的填充，\"),a(\"code\",[t._v(\"vertical\")]),t._v(\"指\"),a(\"code\",[t._v(\"top\")]),t._v(\"和\"),a(\"code\",[t._v(\"bottom\")]),t._v(\"，\"),a(\"code\",[t._v(\"horizontal\")]),t._v(\"指\"),a(\"code\",[t._v(\"left\")]),t._v(\"和\"),a(\"code\",[t._v(\"right\")]),t._v(\"。\")])]),t._v(\" \"),a(\"h3\",{attrs:{id:\"示例\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),a(\"p\",[t._v(\"下面的示例主要展示了\"),a(\"code\",[t._v(\"EdgeInsets\")]),t._v(\"的不同用法，比较简单，源码如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"PaddingTestRoute\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//上下左右各添加16像素补白\")]),t._v(\"\\n      padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//显式指定对齐方式为左对齐，排除对齐干扰\")]),t._v(\"\\n        crossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" CrossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//左边添加8像素补白\")]),t._v(\"\\n            padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"only\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"left\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"8.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hello world\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//上下各添加8像素补白\")]),t._v(\"\\n            padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"symmetric\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"vertical\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"8.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"I am Jack\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 分别指定四个方向的补白\")]),t._v(\"\\n            padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fromLTRB\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Your friend\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"运行效果如图5-1所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(472),alt:\"图5-1\"}})])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/101.60ad385d.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[101],{494:function(t,s,a){t.exports=a.p+\"assets/img/6-1.a5c8558b.png\"},803:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_6-2-singlechildscrollview\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_6-2-singlechildscrollview\"}},[t._v(\"#\")]),t._v(\" 6.2 SingleChildScrollView\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"SingleChildScrollView\")]),t._v(\"类似于Android中的\"),n(\"code\",[t._v(\"ScrollView\")]),t._v(\"，它只能接收一个子组件。定义如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SingleChildScrollView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"scrollDirection \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Axis\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"vertical\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//滚动方向，默认是垂直方向\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"reverse \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n  bool primary\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"physics\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"除了上一节我们介绍过的可滚动组件的通用属性外，我们重点看一下\"),n(\"code\",[t._v(\"reverse\")]),t._v(\"和\"),n(\"code\",[t._v(\"primary\")]),t._v(\"两个属性：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"reverse\")]),t._v(\"：该属性API文档解释是：是否按照阅读方向相反的方向滑动，如：\"),n(\"code\",[t._v(\"scrollDirection\")]),t._v(\"值为\"),n(\"code\",[t._v(\"Axis.horizontal\")]),t._v(\"，如果阅读方向是从左到右(取决于语言环境，阿拉伯语就是从右到左)。\"),n(\"code\",[t._v(\"reverse\")]),t._v(\"为\"),n(\"code\",[t._v(\"true\")]),t._v(\"时，那么滑动方向就是从右往左。其实此属性本质上是决定可滚动组件的初始滚动位置是在“头”还是“尾”，取\"),n(\"code\",[t._v(\"false\")]),t._v(\"时，初始滚动位置在“头”，反之则在“尾”，读者可以自己试验。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"primary\")]),t._v(\"：指是否使用widget树中默认的\"),n(\"code\",[t._v(\"PrimaryScrollController\")]),t._v(\"；当滑动方向为垂直方向（\"),n(\"code\",[t._v(\"scrollDirection\")]),t._v(\"值为\"),n(\"code\",[t._v(\"Axis.vertical\")]),t._v(\"）并且没有指定\"),n(\"code\",[t._v(\"controller\")]),t._v(\"时，\"),n(\"code\",[t._v(\"primary\")]),t._v(\"默认为\"),n(\"code\",[t._v(\"true\")]),t._v(\".\")])]),t._v(\" \"),n(\"p\",[t._v(\"需要注意的是，通常\"),n(\"code\",[t._v(\"SingleChildScrollView\")]),t._v(\"只应在期望的内容不会超过屏幕太多时使用，这是因为\"),n(\"code\",[t._v(\"SingleChildScrollView\")]),t._v(\"不支持基于Sliver的延迟实例化模型，所以如果预计视口可能包含超出屏幕尺寸太多的内容时，那么使用\"),n(\"code\",[t._v(\"SingleChildScrollView\")]),t._v(\"将会非常昂贵（性能差），此时应该使用一些支持Sliver延迟加载的可滚动组件，如\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"示例\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),n(\"p\",[t._v(\"下面是一个将大写字母A-Z沿垂直方向显示的例子，由于垂直方向空间会超过屏幕视口高度，所以我们使用\"),n(\"code\",[t._v(\"SingleChildScrollView\")]),t._v(\"：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"SingleChildScrollViewTestRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    String str \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scrollbar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 显示进度条\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SingleChildScrollView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//动态创建一个List<Widget>  \")]),t._v(\"\\n            children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" str\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"split\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//每一个字母都用一个Text显示,字体为原来的两倍\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"map\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"c\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"c\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" textScaleFactor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toList\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图6-1所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(494),alt:\"图6-1\"}})])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/102.53edb23b.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[102],{509:function(t,a,s){t.exports=s.p+\"assets/img/7-1.fc1ee2fb.png\"},807:function(t,a,s){\"use strict\";s.r(a);var e=s(45),n=Object(e.a)({},(function(){var t=this,a=t.$createElement,e=t._self._c||a;return e(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[e(\"h1\",{attrs:{id:\"_7-2-数据共享-inheritedwidget\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_7-2-数据共享-inheritedwidget\"}},[t._v(\"#\")]),t._v(\" 7.2 数据共享（InheritedWidget）\")]),t._v(\" \"),e(\"p\",[e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"是Flutter中非常重要的一个功能型组件，它提供了一种数据在widget树中从上到下传递、共享的方式，比如我们在应用的根widget中通过\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"共享了一个数据，那么我们便可以在任意子widget中来获取该共享的数据！这个特性在一些需要在widget树中共享数据的场景中非常方便！如Flutter SDK中正是通过InheritedWidget来共享应用主题（\"),e(\"code\",[t._v(\"Theme\")]),t._v(\"）和Locale (当前语言环境)信息的。\")]),t._v(\" \"),e(\"blockquote\",[e(\"p\",[e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"和React中的context功能类似，和逐级传递数据相比，它们能实现组件跨级传递数据。\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"的在widget树中数据传递方向是从上到下的，这和通知\"),e(\"code\",[t._v(\"Notification\")]),t._v(\"（将在下一章中介绍）的传递方向正好相反。\")])]),t._v(\" \"),e(\"h3\",{attrs:{id:\"didchangedependencies\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#didchangedependencies\"}},[t._v(\"#\")]),t._v(\" didChangeDependencies\")]),t._v(\" \"),e(\"p\",[t._v(\"在之前介绍\"),e(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"时，我们提到\"),e(\"code\",[t._v(\"State\")]),t._v(\"对象有一个\"),e(\"code\",[t._v(\"didChangeDependencies\")]),t._v(\"回调，它会在“依赖”发生变化时被Flutter Framework调用。而这个“依赖”指的就是子widget是否使用了父widget中\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"的数据！如果使用了，则代表子widget依赖有依赖\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"；如果没有使用则代表没有依赖。这种机制可以使子组件在所依赖的\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"变化时来更新自身！比如当主题、locale(语言)等发生变化时，依赖其的子widget的\"),e(\"code\",[t._v(\"didChangeDependencies\")]),t._v(\"方法将会被调用。\")]),t._v(\" \"),e(\"p\",[t._v(\"下面我们看一下之前“计数器”示例应用程序的\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"版本。需要说明的是，本示例主要是为了演示\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"的功能特性，并不是计数器的推荐实现方式。\")]),t._v(\" \"),e(\"p\",[t._v(\"首先，我们通过继承\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"，将当前计数器点击次数保存在\"),e(\"code\",[t._v(\"ShareDataWidget\")]),t._v(\"的\"),e(\"code\",[t._v(\"data\")]),t._v(\"属性中：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ShareDataWidget\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"InheritedWidget\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ShareDataWidget\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    Widget child\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" child\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" int data\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//需要在子树中共享的数据，保存点击次数\")]),t._v(\"\\n    \\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//定义一个便捷方法，方便子树中的widget获取共享数据  \")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" ShareDataWidget \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dependOnInheritedWidgetOfExactType\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ShareDataWidget\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//该回调决定当data发生变化时，是否通知子树中依赖data的Widget  \")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  bool \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"updateShouldNotify\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ShareDataWidget old\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//如果返回true，则子树中依赖(build函数中有调用)本widget\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//的子widget的`state.didChangeDependencies`会被调用\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" old\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" data\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"然后我们实现一个子组件\"),e(\"code\",[t._v(\"_TestWidget\")]),t._v(\"，在其\"),e(\"code\",[t._v(\"build\")]),t._v(\"方法中引用\"),e(\"code\",[t._v(\"ShareDataWidget\")]),t._v(\"中的数据。同时，在其\"),e(\"code\",[t._v(\"didChangeDependencies()\")]),t._v(\" 回调中打印日志：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_TestWidget\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  __TestWidgetState \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"__TestWidgetState\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"__TestWidgetState\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"_TestWidget\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//使用InheritedWidget中的共享数据\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ShareDataWidget\\n        \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n        \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\\n        \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didChangeDependencies\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didChangeDependencies\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//父或祖先widget中的InheritedWidget改变(updateShouldNotify返回true)时会被调用。\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//如果build中没有依赖InheritedWidget，则此回调不会被调用。\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Dependencies change\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"最后，我们创建一个按钮，每点击一次，就将\"),e(\"code\",[t._v(\"ShareDataWidget\")]),t._v(\"的值自增：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"InheritedWidgetTestRoute\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _InheritedWidgetTestRouteState \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_InheritedWidgetTestRouteState\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_InheritedWidgetTestRouteState\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"InheritedWidgetTestRoute\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  int count \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\"  \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ShareDataWidget\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//使用ShareDataWidget\")]),t._v(\"\\n        data\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" count\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          mainAxisAlignment\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          children\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              padding\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"only\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"bottom\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              child\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_TestWidget\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//子widget中依赖ShareDataWidget\")]),t._v(\"\\n            \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              child\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Increment\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//每点击一次，将count自增，然后重新build,ShareDataWidget的data将被更新  \")]),t._v(\"\\n              onPressed\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"++\")]),t._v(\"count\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n          \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"运行后界面如图7-1所示：\")]),t._v(\" \"),e(\"p\",[e(\"img\",{attrs:{src:s(509),alt:\"图7-1\"}})]),t._v(\" \"),e(\"p\",[t._v(\"每点击一次按钮，计数器就会自增，控制台就会打印一句日志：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language- extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[e(\"code\",[t._v(\"I/flutter ( 8513): Dependencies change\\n\")])])]),e(\"p\",[t._v(\"可见依赖发生变化后，其\"),e(\"code\",[t._v(\"didChangeDependencies()\")]),t._v(\"会被调用。但是读者要注意，\"),e(\"strong\",[t._v(\"如果_TestWidget的build方法中没有使用ShareDataWidget的数据，那么它的\"),e(\"code\",[t._v(\"didChangeDependencies()\")]),t._v(\"将不会被调用，因为它并没有依赖ShareDataWidget\")]),t._v(\"。例如，我们将\"),e(\"code\",[t._v(\"__TestWidgetState\")]),t._v(\"代码改为下面这样，\"),e(\"code\",[t._v(\"didChangeDependencies()\")]),t._v(\"将不会被调用:\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"__TestWidgetState\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"_TestWidget\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 使用InheritedWidget中的共享数据\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//    return Text(ShareDataWidget\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//        .of(context)\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//        .data\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//        .toString());\")]),t._v(\"\\n     \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"text\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didChangeDependencies\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didChangeDependencies\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// build方法中没有依赖InheritedWidget，此回调不会被调用。\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Dependencies change\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"上面的代码中，我们将\"),e(\"code\",[t._v(\"build()\")]),t._v(\"方法中依赖\"),e(\"code\",[t._v(\"ShareDataWidget\")]),t._v(\"的代码注释掉了，然后返回一个固定\"),e(\"code\",[t._v(\"Text\")]),t._v(\"，这样一来，当点击Increment按钮后，\"),e(\"code\",[t._v(\"ShareDataWidget\")]),t._v(\"的\"),e(\"code\",[t._v(\"data\")]),t._v(\"虽然发生变化，但由于\"),e(\"code\",[t._v(\"__TestWidgetState\")]),t._v(\"并未依赖\"),e(\"code\",[t._v(\"ShareDataWidget\")]),t._v(\"，所以\"),e(\"code\",[t._v(\"__TestWidgetState\")]),t._v(\"的\"),e(\"code\",[t._v(\"didChangeDependencies\")]),t._v(\"方法不会被调用。其实，这个机制很好理解，因为在数据发生变化时只对使用该数据的Widget更新是合理并且性能友好的。\")]),t._v(\" \"),e(\"blockquote\",[e(\"p\",[t._v(\"思考题：Flutter framework是怎么知道子widget有没有依赖InheritedWidget的？\")])]),t._v(\" \"),e(\"h4\",{attrs:{id:\"应该在didchangedependencies-中做什么\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#应该在didchangedependencies-中做什么\"}},[t._v(\"#\")]),t._v(\" 应该在didChangeDependencies()中做什么？\")]),t._v(\" \"),e(\"p\",[t._v(\"一般来说，子widget很少会重写此方法，因为在依赖改变后framework也都会调用\"),e(\"code\",[t._v(\"build()\")]),t._v(\"方法。但是，如果你需要在依赖改变后执行一些昂贵的操作，比如网络请求，这时最好的方式就是在此方法中执行，这样可以避免每次\"),e(\"code\",[t._v(\"build()\")]),t._v(\"都执行这些昂贵操作。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"深入了解inheritedwidget\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#深入了解inheritedwidget\"}},[t._v(\"#\")]),t._v(\" 深入了解InheritedWidget\")]),t._v(\" \"),e(\"p\",[t._v(\"现在来思考一下，如果我们只想在\"),e(\"code\",[t._v(\"__TestWidgetState\")]),t._v(\"中引用\"),e(\"code\",[t._v(\"ShareDataWidget\")]),t._v(\"数据，但却不希望在\"),e(\"code\",[t._v(\"ShareDataWidget\")]),t._v(\"发生变化时调用\"),e(\"code\",[t._v(\"__TestWidgetState\")]),t._v(\"的\"),e(\"code\",[t._v(\"didChangeDependencies()\")]),t._v(\"方法应该怎么办？其实答案很简单，我们只需要将\"),e(\"code\",[t._v(\"ShareDataWidget.of()\")]),t._v(\"的实现改一下即可：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//定义一个便捷方法，方便子树中的widget获取共享数据\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" ShareDataWidget \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//return context.dependOnInheritedWidgetOfExactType<ShareDataWidget>();\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"getElementForInheritedWidgetOfExactType\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ShareDataWidget\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"widget\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"唯一的改动就是获取\"),e(\"code\",[t._v(\"ShareDataWidget\")]),t._v(\"对象的方式，把\"),e(\"code\",[t._v(\"dependOnInheritedWidgetOfExactType()\")]),t._v(\"方法换成了\"),e(\"code\",[t._v(\"context.getElementForInheritedWidgetOfExactType<ShareDataWidget>().widget\")]),t._v(\"，那么他们到底有什么区别呢，我们看一下这两个方法的源码（实现代码在\"),e(\"code\",[t._v(\"Element\")]),t._v(\"类中，\"),e(\"code\",[t._v(\"Context\")]),t._v(\"和\"),e(\"code\",[t._v(\"Element\")]),t._v(\"的关系我们将在后面专门介绍）：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\nInheritedElement getElementForInheritedWidgetOfExactType\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"InheritedWidget\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_debugCheckStateIsActiveForAncestorLookup\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" InheritedElement ancestor \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _inheritedWidgets \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _inheritedWidgets\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"T\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" ancestor\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\nInheritedWidget \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dependOnInheritedWidgetOfExactType\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" Object aspect \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_debugCheckStateIsActiveForAncestorLookup\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" InheritedElement ancestor \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _inheritedWidgets \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _inheritedWidgets\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"T\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//多出的部分\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ancestor \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ancestor \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"is\")]),t._v(\" InheritedElement\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dependOnInheritedElement\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ancestor\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" aspect\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" aspect\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"as\")]),t._v(\" T\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  _hadUnsatisfiedDependencies \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"我们可以看到，\"),e(\"code\",[t._v(\"dependOnInheritedWidgetOfExactType()\")]),t._v(\" 比 \"),e(\"code\",[t._v(\"getElementForInheritedWidgetOfExactType()\")]),t._v(\"多调了\"),e(\"code\",[t._v(\"dependOnInheritedElement\")]),t._v(\"方法，\"),e(\"code\",[t._v(\"dependOnInheritedElement\")]),t._v(\"源码如下：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[t._v(\"  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  InheritedWidget \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dependOnInheritedElement\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"InheritedElement ancestor\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" Object aspect \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ancestor \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    _dependencies \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" HashSet\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"InheritedElement\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    _dependencies\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ancestor\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    ancestor\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"updateDependencies\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" aspect\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" ancestor\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"widget\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"可以看到\"),e(\"code\",[t._v(\"dependOnInheritedElement\")]),t._v(\"方法中主要是注册了依赖关系！看到这里也就清晰了，\"),e(\"strong\",[t._v(\"调用\"),e(\"code\",[t._v(\"dependOnInheritedWidgetOfExactType()\")]),t._v(\" 和 \"),e(\"code\",[t._v(\"getElementForInheritedWidgetOfExactType()\")]),t._v(\"的区别就是前者会注册依赖关系，而后者不会\")]),t._v(\"，所以在调用\"),e(\"code\",[t._v(\"dependOnInheritedWidgetOfExactType()\")]),t._v(\"时，\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"和依赖它的子孙组件关系便完成了注册，之后当\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"发生变化时，就会更新依赖它的子孙组件，也就是会调这些子孙组件的\"),e(\"code\",[t._v(\"didChangeDependencies()\")]),t._v(\"方法和\"),e(\"code\",[t._v(\"build()\")]),t._v(\"方法。而当调用的是 \"),e(\"code\",[t._v(\"getElementForInheritedWidgetOfExactType()\")]),t._v(\"时，由于没有注册依赖关系，所以之后当\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"发生变化时，就不会更新相应的子孙Widget。\")]),t._v(\" \"),e(\"p\",[t._v(\"注意，如果将上面示例中\"),e(\"code\",[t._v(\"ShareDataWidget.of()\")]),t._v(\"方法实现改成调用\"),e(\"code\",[t._v(\"getElementForInheritedWidgetOfExactType()\")]),t._v('，运行示例后，点击\"Increment\"按钮，会发现'),e(\"code\",[t._v(\"__TestWidgetState\")]),t._v(\"的\"),e(\"code\",[t._v(\"didChangeDependencies()\")]),t._v(\"方法确实不会再被调用，但是其\"),e(\"code\",[t._v(\"build()\")]),t._v('仍然会被调用！造成这个的原因其实是，点击\"Increment\"按钮后，会调用'),e(\"code\",[t._v(\"_InheritedWidgetTestRouteState\")]),t._v(\"的\"),e(\"code\",[t._v(\"setState()\")]),t._v(\"方法，此时会重新构建整个页面，由于示例中，\"),e(\"code\",[t._v(\"__TestWidget\")]),t._v(\" 并没有任何缓存，所以它也都会被重新构建，所以也会调用\"),e(\"code\",[t._v(\"build()\")]),t._v(\"方法。\")]),t._v(\" \"),e(\"p\",[t._v(\"那么，现在就带来了一个问题：实际上，我们只想更新子树中依赖了\"),e(\"code\",[t._v(\"ShareDataWidget\")]),t._v(\"的组件，而现在只要调用\"),e(\"code\",[t._v(\"_InheritedWidgetTestRouteState\")]),t._v(\"的\"),e(\"code\",[t._v(\"setState()\")]),t._v(\"方法，所有子节点都会被重新build，这很没必要，那么有什么办法可以避免呢？答案是缓存！一个简单的做法就是通过封装一个\"),e(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"，将子Widget树缓存起来，具体做法下一节我们将通过实现一个\"),e(\"code\",[t._v(\"Provider\")]),t._v(\" Widget 来演示如何缓存，以及如何利用\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\" 来实现Flutter全局状态共享。\")])])}),[],!1,null,null,null);a.default=n.exports}}]);"
  },
  {
    "path": "docs/assets/js/103.21193c45.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[103],{520:function(t,n,s){t.exports=s.p+\"assets/img/8-1.fd0e65f1.png\"},814:function(t,n,s){\"use strict\";s.r(n);var a=s(45),e=Object(a.a)({},(function(){var t=this,n=t.$createElement,a=t._self._c||n;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_8-1-原始指针事件处理\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_8-1-原始指针事件处理\"}},[t._v(\"#\")]),t._v(\" 8.1 原始指针事件处理\")]),t._v(\" \"),a(\"p\",[t._v(\"本节先来介绍一下原始指针事件(Pointer Event，在移动设备上通常为触摸事件)，下一节再介绍手势处理。\")]),t._v(\" \"),a(\"p\",[t._v(\"在移动端，各个平台或UI系统的原始指针事件模型基本都是一致，即：一次完整的事件分为三个阶段：手指按下、手指移动、和手指抬起，而更高级别的手势（如点击、双击、拖动等）都是基于这些原始事件的。\")]),t._v(\" \"),a(\"p\",[t._v(\"当指针按下时，Flutter会对应用程序执行\"),a(\"strong\",[t._v(\"命中测试(Hit Test)\")]),t._v(\"，以确定指针与屏幕接触的位置存在哪些组件（widget）， 指针按下事件（以及该指针的后续事件）然后被分发到由命中测试发现的最内部的组件，然后从那里开始，事件会在组件树中向上冒泡，这些事件会从最内部的组件被分发到组件树根的路径上的所有组件，这和Web开发中浏览器的\"),a(\"strong\",[t._v(\"事件冒泡\")]),t._v(\"机制相似， 但是Flutter中没有机制取消或停止“冒泡”过程，而浏览器的冒泡是可以停止的。注意，只有通过命中测试的组件才能触发事件。\")]),t._v(\" \"),a(\"p\",[t._v(\"Flutter中可以使用\"),a(\"code\",[t._v(\"Listener\")]),t._v(\"来监听原始触摸事件，按照本书对组件的分类，则\"),a(\"code\",[t._v(\"Listener\")]),t._v(\"也是一个功能性组件。下面是\"),a(\"code\",[t._v(\"Listener\")]),t._v(\"的构造函数定义：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Listener\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  Key key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onPointerDown\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//手指按下回调\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onPointerMove\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//手指移动回调\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onPointerUp\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//手指抬起回调\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onPointerCancel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//触摸事件取消回调\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"behavior \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" HitTestBehavior\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"deferToChild\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//在命中测试期间如何表现\")]),t._v(\"\\n  Widget child\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"我们先看一个示例，后面再单独讨论一下\"),a(\"code\",[t._v(\"behavior\")]),t._v(\"属性。\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//定义一个状态，保存当前指针位置\")]),t._v(\"\\nPointerEvent _event\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Listener\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"150.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_event\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"style\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  onPointerDown\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"PointerDownEvent event\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"_event\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"event\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  onPointerMove\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"PointerMoveEvent event\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"_event\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"event\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  onPointerUp\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"PointerUpEvent event\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"_event\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"event\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"运行后效果如图8-1所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:s(520),alt:\"图8-1\"}})]),t._v(\" \"),a(\"p\",[t._v(\"手指在蓝色矩形区域内移动即可看到当前指针偏移，当触发指针事件时，参数\"),a(\"code\",[t._v(\"PointerDownEvent\")]),t._v(\"、\"),a(\"code\",[t._v(\"PointerMoveEvent\")]),t._v(\"、\"),a(\"code\",[t._v(\"PointerUpEvent\")]),t._v(\"都是\"),a(\"code\",[t._v(\"PointerEvent\")]),t._v(\"的一个子类，\"),a(\"code\",[t._v(\"PointerEvent\")]),t._v(\"类中包括当前指针的一些信息，如：\")]),t._v(\" \"),a(\"ul\",[a(\"li\",[a(\"code\",[t._v(\"position\")]),t._v(\"：它是鼠标相对于当对于全局坐标的偏移。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"delta\")]),t._v(\"：两次指针移动事件（\"),a(\"code\",[t._v(\"PointerMoveEvent\")]),t._v(\"）的距离。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"pressure\")]),t._v(\"：按压力度，如果手机屏幕支持压力传感器(如iPhone的3D Touch)，此属性会更有意义，如果手机不支持，则始终为1。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"orientation\")]),t._v(\"：指针移动方向，是一个角度值。\")])]),t._v(\" \"),a(\"p\",[t._v(\"上面只是\"),a(\"code\",[t._v(\"PointerEvent\")]),t._v(\"一些常用属性，除了这些它还有很多属性，读者可以查看API文档。\")]),t._v(\" \"),a(\"p\",[t._v(\"现在，我们重点来介绍一下\"),a(\"code\",[t._v(\"behavior\")]),t._v(\"属性，它决定子组件如何响应命中测试，它的值类型为\"),a(\"code\",[t._v(\"HitTestBehavior\")]),t._v(\"，这是一个枚举类，有三个枚举值：\")]),t._v(\" \"),a(\"ul\",[a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"deferToChild\")]),t._v(\"：子组件会一个接一个的进行命中测试，如果子组件中有测试通过的，则当前组件通过，这就意味着，如果指针事件作用于子组件上时，其父级组件也肯定可以收到该事件。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"opaque\")]),t._v(\"：在命中测试时，将当前组件当成不透明处理(即使本身是透明的)，最终的效果相当于当前Widget的整个区域都是点击区域。举个例子：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Listener\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ConstrainedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        constraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BoxConstraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"tight\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Size\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"150.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Box A\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//behavior: HitTestBehavior.opaque,\")]),t._v(\"\\n    onPointerDown\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"event\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"down A\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"上例中，只有点击文本内容区域才会触发点击事件，因为 \"),a(\"code\",[t._v(\"deferToChild\")]),t._v(\" 会去子组件判断是否命中测试，而该例中子组件就是 \"),a(\"code\",[t._v('Text(\"Box A\")')]),t._v(\" 。\\n如果我们想让整个300×150的矩形区域都能点击我们可以将\"),a(\"code\",[t._v(\"behavior\")]),t._v(\"设为\"),a(\"code\",[t._v(\"HitTestBehavior.opaque\")]),t._v(\"。注意，该属性并不能用于在组件树中拦截（忽略）事件，它只是决定命中测试时的组件大小。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"translucent\")]),t._v(\"：当点击组件透明区域时，可以对自身边界内及底部可视区域都进行命中测试，这意味着点击顶部组件透明区域时，顶部组件和底部组件都可以接收到事件，例如：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Stack\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Listener\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ConstrainedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        constraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BoxConstraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"tight\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Size\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecoratedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      onPointerDown\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"event\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"down0\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Listener\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ConstrainedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        constraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BoxConstraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"tight\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Size\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"左上角200*100范围内非文本区域点击\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      onPointerDown\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"event\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"down1\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v('//behavior: HitTestBehavior.translucent, //放开此行注释后可以\"点透\"')]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"上例中，当注释掉最后一行代码后，在左上角200*100范围内非文本区域点击时（顶部组件透明区域），控制台只会打印“down0”，也就是说顶部组件没有接收到事件，而只有底部接收到了。当放开注释后，再点击时顶部和底部都会接收到事件，此时会打印：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language- extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[a(\"code\",[t._v(\"I/flutter ( 3039): down1\\nI/flutter ( 3039): down0\\n\")])])]),a(\"p\",[t._v(\"如果\"),a(\"code\",[t._v(\"behavior\")]),t._v(\"值改为\"),a(\"code\",[t._v(\"HitTestBehavior.opaque\")]),t._v('，则只会打印\"down1\"。')])])]),t._v(\" \"),a(\"h3\",{attrs:{id:\"忽略pointerevent\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#忽略pointerevent\"}},[t._v(\"#\")]),t._v(\" 忽略PointerEvent\")]),t._v(\" \"),a(\"p\",[t._v(\"假如我们不想让某个子树响应\"),a(\"code\",[t._v(\"PointerEvent\")]),t._v(\"的话，我们可以使用\"),a(\"code\",[t._v(\"IgnorePointer\")]),t._v(\"和\"),a(\"code\",[t._v(\"AbsorbPointer\")]),t._v(\"，这两个组件都能阻止子树接收指针事件，不同之处在于\"),a(\"code\",[t._v(\"AbsorbPointer\")]),t._v(\"本身会参与命中测试，而\"),a(\"code\",[t._v(\"IgnorePointer\")]),t._v(\"本身不会参与，这就意味着\"),a(\"code\",[t._v(\"AbsorbPointer\")]),t._v(\"本身是可以接收指针事件的(但其子树不行)，而\"),a(\"code\",[t._v(\"IgnorePointer\")]),t._v(\"不可以。一个简单的例子如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Listener\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AbsorbPointer\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Listener\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      onPointerDown\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"event\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"in\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  onPointerDown\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"event\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"up\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"点击\"),a(\"code\",[t._v(\"Container\")]),t._v(\"时，由于它在\"),a(\"code\",[t._v(\"AbsorbPointer\")]),t._v('的子树上，所以不会响应指针事件，所以日志不会输出\"in\"，但'),a(\"code\",[t._v(\"AbsorbPointer\")]),t._v('本身是可以接收指针事件的，所以会输出\"up\"。如果将'),a(\"code\",[t._v(\"AbsorbPointer\")]),t._v(\"换成\"),a(\"code\",[t._v(\"IgnorePointer\")]),t._v(\"，那么两个都不会输出。\")])])}),[],!1,null,null,null);n.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/104.02d5761d.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[104],{521:function(t,s,a){t.exports=a.p+\"assets/img/8-6.ce4b57e6.png\"},815:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_8-4-notification\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_8-4-notification\"}},[t._v(\"#\")]),t._v(\" 8.4 Notification\")]),t._v(\" \"),n(\"p\",[t._v(\"通知（Notification）是Flutter中一个重要的机制，在widget树中，每一个节点都可以分发通知，通知会沿着当前节点向上传递，所有父节点都可以通过\"),n(\"code\",[t._v(\"NotificationListener\")]),t._v(\"来监听通知。Flutter中将这种由子向父的传递通知的机制称为\"),n(\"strong\",[t._v(\"通知冒泡\")]),t._v(\"（Notification Bubbling）。通知冒泡和用户触摸事件冒泡是相似的，但有一点不同：通知冒泡可以中止，但用户触摸事件不行。\")]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[t._v(\"通知冒泡和Web开发中浏览器事件冒泡原理是相似的，都是事件从出发源逐层向上传递，我们可以在上层节点任意位置来监听通知/事件，也可以终止冒泡过程，终止冒泡后，通知将不会再向上传递。\")])]),t._v(\" \"),n(\"p\",[t._v(\"Flutter中很多地方使用了通知，如可滚动组件（Scrollable Widget）滑动时就会分发\"),n(\"strong\",[t._v(\"滚动通知\")]),t._v(\"（ScrollNotification），而Scrollbar正是通过监听ScrollNotification来确定滚动条位置的。\")]),t._v(\" \"),n(\"p\",[t._v(\"下面是一个监听可滚动组件滚动通知的例子：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"NotificationListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  onNotification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"notification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"switch\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"notification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"runtimeType\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" ScrollStartNotification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"开始滚动\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"break\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" ScrollUpdateNotification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"正在滚动\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"break\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" ScrollEndNotification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"滚动停止\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"break\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" OverscrollNotification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"滚动到边界\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"break\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ListView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      itemCount\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      itemBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$index\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上例中的滚动通知如\"),n(\"code\",[t._v(\"ScrollStartNotification\")]),t._v(\"、\"),n(\"code\",[t._v(\"ScrollUpdateNotification\")]),t._v(\"等都是继承自\"),n(\"code\",[t._v(\"ScrollNotification\")]),t._v(\"类，不同类型的通知子类会包含不同的信息，比如\"),n(\"code\",[t._v(\"ScrollUpdateNotification\")]),t._v(\"有一个\"),n(\"code\",[t._v(\"scrollDelta\")]),t._v(\"属性，它记录了移动的位移，其它通知属性读者可以自己查看SDK文档。\")]),t._v(\" \"),n(\"p\",[t._v(\"上例中，我们通过\"),n(\"code\",[t._v(\"NotificationListener\")]),t._v(\"来监听子\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"的滚动通知的，\"),n(\"code\",[t._v(\"NotificationListener\")]),t._v(\"定义如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"NotificationListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Notification\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"NotificationListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onNotification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码 \")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"  \\n\")])])]),n(\"p\",[t._v(\"我们可以看到：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"NotificationListener\")]),t._v(\" 继承自\"),n(\"code\",[t._v(\"StatelessWidget\")]),t._v(\"类，所以它可以直接嵌套到Widget树中。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"NotificationListener\")]),t._v(\" 可以指定一个模板参数，该模板参数类型必须是继承自\"),n(\"code\",[t._v(\"Notification\")]),t._v(\"；当显式指定模板参数时，\"),n(\"code\",[t._v(\"NotificationListener\")]),t._v(\" 便只会接收该参数类型的通知。举个例子，如果我们将上例子代码改为：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//指定监听通知的类型为滚动结束通知(ScrollEndNotification)\")]),t._v(\"\\nNotificationListener\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ScrollEndNotification\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  onNotification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"notification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//只会在滚动结束时才会触发此回调\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"notification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ListView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      itemCount\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      itemBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$index\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上面代码运行后便只会在滚动结束时在控制台打印出通知的信息。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"onNotification\")]),t._v(\"回调为通知处理回调，其函数签名如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"typedef\")]),t._v(\" NotificationListenerCallback\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Notification\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" bool \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"Function\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"T notification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"它的返回值类型为布尔值，当返回值为\"),n(\"code\",[t._v(\"true\")]),t._v(\"时，阻止冒泡，其父级Widget将再也收不到该通知；当返回值为\"),n(\"code\",[t._v(\"false\")]),t._v(\" 时继续向上冒泡通知。\")])])]),t._v(\" \"),n(\"p\",[t._v(\"Flutter的UI框架实现中，除了在可滚动组件在滚动过程中会发出\"),n(\"code\",[t._v(\"ScrollNotification\")]),t._v(\"之外，还有一些其它的通知，如\"),n(\"code\",[t._v(\"SizeChangedLayoutNotification\")]),t._v(\"、\"),n(\"code\",[t._v(\"KeepAliveNotification\")]),t._v(\" 、\"),n(\"code\",[t._v(\"LayoutChangedNotification\")]),t._v(\"等，Flutter正是通过这种通知机制来使父元素可以在一些特定时机来做一些事情。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"自定义通知\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#自定义通知\"}},[t._v(\"#\")]),t._v(\" 自定义通知\")]),t._v(\" \"),n(\"p\",[t._v(\"除了Flutter内部通知，我们也可以自定义通知，下面我们看看如何实现自定义通知：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"p\",[t._v(\"定义一个通知类，要继承自Notification类；\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyNotification\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Notification\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyNotification\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"msg\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String msg\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"分发通知。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"Notification\")]),t._v(\"有一个\"),n(\"code\",[t._v(\"dispatch(context)\")]),t._v(\"方法，它是用于分发通知的，我们说过\"),n(\"code\",[t._v(\"context\")]),t._v(\"实际上就是操作\"),n(\"code\",[t._v(\"Element\")]),t._v(\"的一个接口，它与\"),n(\"code\",[t._v(\"Element\")]),t._v(\"树上的节点是对应的，通知会从\"),n(\"code\",[t._v(\"context\")]),t._v(\"对应的\"),n(\"code\",[t._v(\"Element\")]),t._v(\"节点向上冒泡。\")])])]),t._v(\" \"),n(\"p\",[t._v(\"下面我们看一个完整的例子：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"NotificationRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  NotificationRouteState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"NotificationRouteState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"NotificationRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"NotificationRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  String _msg\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//监听通知  \")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" NotificationListener\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"MyNotification\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      onNotification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"notification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          _msg\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+=\")]),t._v(\"notification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"msg\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"  \"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n       \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          mainAxisSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"min\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//          RaisedButton(\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v('//           onPressed: () => MyNotification(\"Hi\").dispatch(context),')]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v('//           child: Text(\"Send Notification\"),')]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//          ),  \")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//按钮点击时分发通知  \")]),t._v(\"\\n                  onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyNotification\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hi\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispatch\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Send Notification\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_msg\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyNotification\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Notification\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyNotification\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"msg\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String msg\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上面代码中，我们每点一次按钮就会分发一个\"),n(\"code\",[t._v(\"MyNotification\")]),t._v(\"类型的通知，我们在Widget根上监听通知，收到通知后我们将通知通过Text显示在屏幕上。\")]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[t._v(\"注意：代码中注释的部分是不能正常工作的，因为这个\"),n(\"code\",[t._v(\"context\")]),t._v(\"是根Context，而NotificationListener是监听的子树，所以我们通过\"),n(\"code\",[t._v(\"Builder\")]),t._v(\"来构建RaisedButton，来获得按钮位置的context。\")])]),t._v(\" \"),n(\"p\",[t._v(\"运行效果如图8-6所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(521),alt:\"图8-6\"}})]),t._v(\" \"),n(\"h3\",{attrs:{id:\"阻止冒泡\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#阻止冒泡\"}},[t._v(\"#\")]),t._v(\" 阻止冒泡\")]),t._v(\" \"),n(\"p\",[t._v(\"我们将上面的例子改为：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"NotificationRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"NotificationRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  String _msg\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//监听通知\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" NotificationListener\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"MyNotification\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      onNotification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"notification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"notification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"msg\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//打印通知\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" NotificationListener\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"MyNotification\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        onNotification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"notification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            _msg\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+=\")]),t._v(\"notification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"msg\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"  \"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略重复代码\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上列中两个\"),n(\"code\",[t._v(\"NotificationListener\")]),t._v(\"进行了嵌套，子\"),n(\"code\",[t._v(\"NotificationListener\")]),t._v(\"的\"),n(\"code\",[t._v(\"onNotification\")]),t._v(\"回调返回了\"),n(\"code\",[t._v(\"false\")]),t._v(\"，表示不阻止冒泡，所以父\"),n(\"code\",[t._v(\"NotificationListener\")]),t._v(\"仍然会受到通知，所以控制台会打印出通知信息；如果将子\"),n(\"code\",[t._v(\"NotificationListener\")]),t._v(\"的\"),n(\"code\",[t._v(\"onNotification\")]),t._v(\"回调的返回值改为\"),n(\"code\",[t._v(\"true\")]),t._v(\"，则父\"),n(\"code\",[t._v(\"NotificationListener\")]),t._v(\"便不会再打印通知了，因为子\"),n(\"code\",[t._v(\"NotificationListener\")]),t._v(\"已经终止通知冒泡了。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"通知冒泡原理\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#通知冒泡原理\"}},[t._v(\"#\")]),t._v(\" 通知冒泡原理\")]),t._v(\" \"),n(\"p\",[t._v(\"我们在上面介绍了通知冒泡的现象及使用，现在我们更深入一些，介绍一下Flutter框架中是如何实现通知冒泡的。为了搞清楚这个问题，就必须看一下源码，我们从通知分发的的源头出发，然后再顺藤摸瓜。由于通知是通过\"),n(\"code\",[t._v(\"Notification\")]),t._v(\"的\"),n(\"code\",[t._v(\"dispatch(context)\")]),t._v(\"方法发出的，那我们先看看\"),n(\"code\",[t._v(\"dispatch(context)\")]),t._v(\"方法中做了什么，下面是相关源码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispatch\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext target\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  target\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"visitAncestorElements\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"visitAncestor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"dispatch(context)\")]),t._v(\"中调用了当前context的\"),n(\"code\",[t._v(\"visitAncestorElements\")]),t._v(\"方法，该方法会从当前Element开始向上遍历父级元素；\"),n(\"code\",[t._v(\"visitAncestorElements\")]),t._v(\"有一个遍历回调参数，在遍历过程中对遍历到的父级元素都会执行该回调。遍历的终止条件是：已经遍历到根Element或某个遍历回调返回\"),n(\"code\",[t._v(\"false\")]),t._v(\"。源码中传给\"),n(\"code\",[t._v(\"visitAncestorElements\")]),t._v(\"方法的遍历回调为\"),n(\"code\",[t._v(\"visitAncestor\")]),t._v(\"方法，我们看看\"),n(\"code\",[t._v(\"visitAncestor\")]),t._v(\"方法的实现：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//遍历回调，会对每一个父级Element执行此回调\")]),t._v(\"\\nbool \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"visitAncestor\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Element element\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//判断当前element对应的Widget是否是NotificationListener。\")]),t._v(\"\\n  \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//由于NotificationListener是继承自StatelessWidget，\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//故先判断是否是StatelessElement\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"element \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"is\")]),t._v(\" StatelessElement\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//是StatelessElement，则获取element对应的Widget，判断\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//是否是NotificationListener 。\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" StatelessWidget widget \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" element\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"is\")]),t._v(\" NotificationListener\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Notification\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//是NotificationListener，则调用该NotificationListener的_dispatch方法\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_dispatch\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" element\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"visitAncestor\")]),t._v(\"会判断每一个遍历到的父级Widget是否是\"),n(\"code\",[t._v(\"NotificationListener\")]),t._v(\"，如果不是，则返回\"),n(\"code\",[t._v(\"true\")]),t._v(\"继续向上遍历，如果是，则调用\"),n(\"code\",[t._v(\"NotificationListener\")]),t._v(\"的\"),n(\"code\",[t._v(\"_dispatch\")]),t._v(\"方法，我们看看\"),n(\"code\",[t._v(\"_dispatch\")]),t._v(\"方法的源码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"  bool \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_dispatch\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Notification notification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Element element\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 如果通知监听器不为空，并且当前通知类型是该NotificationListener\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 监听的通知类型，则调用当前NotificationListener的onNotification\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"onNotification \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\" notification \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"is\")]),t._v(\" T\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" bool result \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onNotification\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"notification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 返回值决定是否继续向上遍历\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" result \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们可以看到\"),n(\"code\",[t._v(\"NotificationListener\")]),t._v(\"的\"),n(\"code\",[t._v(\"onNotification\")]),t._v(\"回调最终是在\"),n(\"code\",[t._v(\"_dispatch\")]),t._v(\"方法中执行的，然后会根据返回值来确定是否继续向上冒泡。上面的源码实现其实并不复杂，通过阅读这些源码，一些额外的点读者可以注意一下：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"code\",[t._v(\"Context\")]),t._v(\"上也提供了遍历Element树的方法。\")]),t._v(\" \"),n(\"li\",[t._v(\"我们可以通过\"),n(\"code\",[t._v(\"Element.widget\")]),t._v(\"得到\"),n(\"code\",[t._v(\"element\")]),t._v(\"节点对应的widget；我们已经反复讲过Widget和Element的对应关系，读者通过这些源码来加深理解。\")])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"总结\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),n(\"p\",[t._v(\"Flutter中通过通知冒泡实现了一套自低向上的消息传递机制，这个和Web开发中浏览器的事件冒泡原理类似，Web开发者可以类比学习。另外我们通过源码了解了Flutter 通知冒泡的流程和原理，便于读者加深理解和学习Flutter的框架设计思想，在此，再次建议读者在平时学习中能多看看源码，定会受益匪浅。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/105.b6044eeb.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[105],{532:function(t,r,a){t.exports=a.p+\"assets/img/1-1.41c572c4.png\"},832:function(t,r,a){\"use strict\";a.r(r);var e=a(45),v=Object(e.a)({},(function(){var t=this,r=t.$createElement,e=t._self._c||r;return e(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[e(\"h1\",{attrs:{id:\"_1-2-初识flutter\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-2-初识flutter\"}},[t._v(\"#\")]),t._v(\" 1.2 初识Flutter\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"_1-2-1-flutter简介\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-2-1-flutter简介\"}},[t._v(\"#\")]),t._v(\" 1.2.1 Flutter简介\")]),t._v(\" \"),e(\"p\",[t._v(\"Flutter 是 Google推出并开源的移动应用开发框架，主打跨平台、高保真、高性能。开发者可以通过 Dart语言开发 App，一套代码同时运行在 iOS 和 Android平台。 Flutter提供了丰富的组件、接口，开发者可以很快地为 Flutter添加 native扩展。同时 Flutter还使用 Native引擎渲染视图，这无疑能为用户提供良好的体验。\")]),t._v(\" \"),e(\"h4\",{attrs:{id:\"跨平台自绘引擎\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#跨平台自绘引擎\"}},[t._v(\"#\")]),t._v(\" 跨平台自绘引擎\")]),t._v(\" \"),e(\"p\",[t._v(\"Flutter与用于构建移动应用程序的其它大多数框架不同，因为Flutter既不使用WebView，也不使用操作系统的原生控件。 相反，Flutter使用自己的高性能渲染引擎来绘制widget。这样不仅可以保证在Android和iOS上UI的一致性，而且也可以避免对原生控件依赖而带来的限制及高昂的维护成本。\")]),t._v(\" \"),e(\"p\",[t._v(\"Flutter使用Skia作为其2D渲染引擎，Skia是Google的一个2D图形处理函数库，包含字型、坐标转换，以及点阵图都有高效能且简洁的表现，Skia是跨平台的，并提供了非常友好的API，目前Google Chrome浏览器和Android均采用Skia作为其绘图引擎。\")]),t._v(\" \"),e(\"p\",[t._v(\"目前Flutter默认支持iOS、Android、Fuchsia（Google新的自研操作系统）三个移动平台。但Flutter亦可支持Web开发（Flutter for web）和PC开发，本书的示例和介绍主要是基于iOS和Android平台的，其它平台读者可以自行了解。\")]),t._v(\" \"),e(\"h4\",{attrs:{id:\"高性能\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#高性能\"}},[t._v(\"#\")]),t._v(\" 高性能\")]),t._v(\" \"),e(\"p\",[t._v(\"Flutter高性能主要靠两点来保证，首先，Flutter APP采用Dart语言开发。Dart在 JIT（即时编译）模式下，速度与 JavaScript基本持平。但是 Dart支持 AOT，当以 AOT模式运行时，JavaScript便远远追不上了。速度的提升对高帧率下的视图数据计算很有帮助。其次，Flutter使用自己的渲染引擎来绘制UI，布局数据等由Dart语言直接控制，所以在布局过程中不需要像RN那样要在JavaScript和Native之间通信，这在一些滑动和拖动的场景下具有明显优势，因为在滑动和拖动过程往往都会引起布局发生变化，所以JavaScript需要和Native之间不停的同步布局信息，这和在浏览器中要JavaScript频繁操作DOM所带来的问题是相同的，都会带来比较可观的性能开销。\")]),t._v(\" \"),e(\"h4\",{attrs:{id:\"采用dart语言开发\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#采用dart语言开发\"}},[t._v(\"#\")]),t._v(\" 采用Dart语言开发\")]),t._v(\" \"),e(\"p\",[t._v(\"这是一个很有意思，但也很有争议的问题，在了解Flutter为什么选择了 Dart而不是 JavaScript之前我们先来介绍两个概念：JIT和AOT。\")]),t._v(\" \"),e(\"p\",[t._v(\"目前，程序主要有两种运行方式：静态编译与动态解释。静态编译的程序在执行前全部被翻译为机器码，通常将这种类型称为\"),e(\"strong\",[t._v(\"AOT\")]),t._v(\" （Ahead of time）即 “提前编译”；而解释执行的则是一句一句边翻译边运行，通常将这种类型称为\"),e(\"strong\",[t._v(\"JIT\")]),t._v(\"（Just-in-time）即“即时编译”。AOT程序的典型代表是用C/C++开发的应用，它们必须在执行前编译成机器码，而JIT的代表则非常多，如JavaScript、python等，事实上，所有脚本语言都支持JIT模式。但需要注意的是JIT和AOT指的是程序运行方式，和编程语言并非强关联的，有些语言既可以以JIT方式运行也可以以AOT方式运行，如Java、Python，它们可以在第一次执行时编译成中间字节码、然后在之后执行时可以直接执行字节码，也许有人会说，中间字节码并非机器码，在程序执行时仍然需要动态将字节码转为机器码，是的，这没有错，不过通常我们区分是否为AOT的标准就是看代码在执行之前是否需要编译，只要需要编译，无论其编译产物是字节码还是机器码，都属于AOT。在此，读者不必纠结于概念，概念就是为了传达精神而发明的，只要读者能够理解其原理即可，得其神忘其形。\")]),t._v(\" \"),e(\"p\",[t._v(\"现在我们看看Flutter为什么选择Dart语言？笔者根据官方解释以及自己对Flutter的理解总结了以下几条（由于其它跨平台框架都将JavaScript作为其开发语言，所以主要将Dart和JavaScript做一个对比）：\")]),t._v(\" \"),e(\"ol\",[e(\"li\",[e(\"p\",[e(\"strong\",[t._v(\"开发效率高\")])]),t._v(\" \"),e(\"p\",[t._v(\"Dart运行时和编译器支持Flutter的两个关键特性的组合：\")]),t._v(\" \"),e(\"p\",[e(\"strong\",[t._v(\"基于JIT的快速开发周期\")]),t._v(\"：Flutter在开发阶段采用，采用JIT模式，这样就避免了每次改动都要进行编译，极大的节省了开发时间；\")]),t._v(\" \"),e(\"p\",[e(\"strong\",[t._v(\"基于AOT的发布包\")]),t._v(\":  Flutter在发布时可以通过AOT生成高效的ARM代码以保证应用性能。而JavaScript则不具有这个能力。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"strong\",[t._v(\"高性能\")])]),t._v(\" \"),e(\"p\",[t._v(\"Flutter旨在提供流畅、高保真的的UI体验。为了实现这一点，Flutter中需要能够在每个动画帧中运行大量的代码。这意味着需要一种既能提供高性能的语言，而不会出现会丢帧的周期性暂停，而Dart支持AOT，在这一点上可以做的比JavaScript更好。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"strong\",[t._v(\"快速内存分配\")])]),t._v(\" \"),e(\"p\",[t._v(\"Flutter框架使用函数式流，这使得它在很大程度上依赖于底层的内存分配器。因此，拥有一个能够有效地处理琐碎任务的内存分配器将显得十分重要，在缺乏此功能的语言中，Flutter将无法有效地工作。当然Chrome V8的JavaScript引擎在内存分配上也已经做的很好，事实上Dart开发团队的很多成员都是来自Chrome团队的，所以在内存分配上Dart并不能作为超越JavaScript的优势，而对于Flutter来说，它需要这样的特性，而Dart也正好满足而已。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"strong\",[t._v(\"类型安全\")])]),t._v(\" \"),e(\"p\",[t._v(\"由于Dart是类型安全的语言，支持静态类型检测，所以可以在编译前发现一些类型的错误，并排除潜在问题，这一点对于前端开发者来说可能会更具有吸引力。与之不同的，JavaScript是一个弱类型语言，也因此前端社区出现了很多给JavaScript代码添加静态类型检测的扩展语言和工具，如：微软的TypeScript以及Facebook的Flow。相比之下，Dart本身就支持静态类型，这是它的一个重要优势。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"strong\",[t._v(\"Dart团队就在你身边\")])]),t._v(\" \"),e(\"p\",[t._v(\"看似不起眼，实则举足轻重。由于有Dart团队的积极投入，Flutter团队可以获得更多、更方便的支持，正如Flutter官网所述“我们正与Dart社区进行密切合作，以改进Dart在Flutter中的使用。例如，当我们最初采用Dart时，该语言并没有提供生成原生二进制文件的工具链（这对于实现可预测的高性能具有很大的帮助），但是现在它实现了，因为Dart团队专门为Flutter构建了它。同样，Dart VM之前已经针对吞吐量进行了优化，但团队现在正在优化VM的延迟时间，这对于Flutter的工作负载更为重要。”\")])])]),t._v(\" \"),e(\"h4\",{attrs:{id:\"总结\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),e(\"p\",[t._v(\"本节主要介绍了一下Flutter的特点，如果你感到有些点还不是很好理解，不用着急，随着日后对Flutter细节的了解，再回过头来看，相信你会有更深的体会。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"_1-2-2-flutter框架结构\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-2-2-flutter框架结构\"}},[t._v(\"#\")]),t._v(\" 1.2.2 Flutter框架结构\")]),t._v(\" \"),e(\"p\",[t._v(\"本节我们先对Flutter的框架做一个整体介绍，旨在让读者心中有一个整体的印象，这对初学者来说非常重要。如果一下子便深入到Flutter中，就会像是一个在沙漠中没有地图的人，即使可以找到一个绿洲，但是他也不会知道下一个绿洲在哪。因此，无论学什么技术，都要先有一张清晰的“地图”，而我们的学习过程就是“按图索骥”，这样我们才不会陷于细节而“目无全牛”。言归正传，我们看一下Flutter官方提供的Flutter框架图，如图1-1所示：\")]),t._v(\" \"),e(\"p\",[e(\"img\",{attrs:{src:a(532),alt:\"图1-1\"}})]),t._v(\" \"),e(\"h3\",{attrs:{id:\"flutter-framework\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#flutter-framework\"}},[t._v(\"#\")]),t._v(\" Flutter Framework\")]),t._v(\" \"),e(\"p\",[t._v(\"这是一个纯 Dart实现的 SDK，它实现了一套基础库，自底向上，我们来简单介绍一下：\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"p\",[t._v(\"底下两层（Foundation和Animation、Painting、Gestures）在Google的一些视频中被合并为一个dart UI层，对应的是Flutter中的\"),e(\"code\",[t._v(\"dart:ui\")]),t._v(\"包，它是Flutter引擎暴露的底层UI库，提供动画、手势及绘制能力。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[t._v(\"Rendering层，这一层是一个抽象的布局层，它依赖于dart UI层，Rendering层会构建一个UI树，当UI树有变化时，会计算出有变化的部分，然后更新UI树，最终将UI树绘制到屏幕上，这个过程类似于React中的虚拟DOM。Rendering层可以说是Flutter UI框架最核心的部分，它除了确定每个UI元素的位置、大小之外还要进行坐标变换、绘制(调用底层dart:ui)。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[t._v(\"Widgets层是Flutter提供的的一套基础组件库，在基础组件库之上，Flutter还提供了 Material 和Cupertino两种视觉风格的组件库。而\"),e(\"strong\",[t._v(\"我们Flutter开发的大多数场景，只是和这两层打交道\")]),t._v(\"。\")])])]),t._v(\" \"),e(\"h3\",{attrs:{id:\"flutter-engine\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#flutter-engine\"}},[t._v(\"#\")]),t._v(\" Flutter Engine\")]),t._v(\" \"),e(\"p\",[t._v(\"这是一个纯 C++实现的 SDK，其中包括了 Skia引擎、Dart运行时、文字排版引擎等。在代码调用 \"),e(\"code\",[t._v(\"dart:ui\")]),t._v(\"库时，调用最终会走到Engine层，然后实现真正的绘制逻辑。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"总结-2\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结-2\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),e(\"p\",[t._v(\"Flutter框架本身有着良好的分层设计，本节旨在让读者对Flutter整体框架有个大概的印象，相信到现在为止，读者已经对Flutter有一个初始印象，在我们正式动手之前，我们还需要了解一下Flutter的开发语言Dart。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"_1-2-3-如何学习flutter\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-2-3-如何学习flutter\"}},[t._v(\"#\")]),t._v(\" 1.2.3 如何学习Flutter\")]),t._v(\" \"),e(\"p\",[t._v(\"本节给大家一些学习建议，分享一下笔者在学习Flutter中的一些心得，希望可以帮助你提高学习效率，避免不必要的坑。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"资源\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#资源\"}},[t._v(\"#\")]),t._v(\" 资源\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"p\",[e(\"strong\",[t._v(\"官网\")]),t._v(\"：阅读Flutter官网的资源是快速入门的最佳方式，同时官网也是了解最新Flutter发展动态的地方，由于目前Flutter仍然处于快速发展阶段，所以建议读者还是时不时的去官网看看有没有新的动态。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"strong\",[t._v(\"源码及注释\")]),t._v(\"：源码注释应作为学习Flutter的第一文档，Flutter SDK的源码是开源的，并且注释非常详细，也有很多示例，实际上，Flutter官方的SDK文档就是通过注释生成的。源码结合注释可以帮你解决大多数问题。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"strong\",[t._v(\"Github\")]),t._v(\"：如果遇到的问题在StackOverflow上也没有找到答案，可以去github flutter 项目下提issue。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"strong\",[t._v(\"Gallery源码\")]),t._v(\"：Gallery是Flutter官方示例APP，里面有丰富的示例，读者可以在网上下载安装。Gallery的源码在Flutter源码“examples”目录下。\")])])]),t._v(\" \"),e(\"h3\",{attrs:{id:\"社区\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#社区\"}},[t._v(\"#\")]),t._v(\" 社区\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"strong\",[t._v(\"StackOverflow\")]),t._v(\"：如果你还没听过StackOverflow，这是目前全球最大的程序员问答社区，现在也是活跃度最高的Flutter问答社区。StackOverflow上面除了世界各地的Flutter使用者会在上面交流之外，Flutter开发团队的成员也经常会在上面回答问题。\")]),t._v(\" \"),e(\"li\",[e(\"strong\",[t._v(\"Flutter中文网社区\")]),t._v(\"：Flutter中文网(https://flutterchina.club)是笔者维护中文网站，目前也是最大的中文资源社区，上面提供了Flutter官网的文档翻译、开源项目、及案例，还有申请加入组织的入口哦。\")]),t._v(\" \"),e(\"li\",[e(\"strong\",[t._v(\"博客\")]),t._v(\"：随着Flutter技术的推广，相信很快网上将会有很多Flutter相关的文章、博客，读者可以多去浏览、阅读。\")])]),t._v(\" \"),e(\"h3\",{attrs:{id:\"总结-3\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结-3\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),e(\"p\",[t._v(\"有了资料和社区后，对于我们学习者自身来说，最重要的还是要多动手、多实践，在本书后面的章节中，希望读者能够亲自动手写一下示例。准备好了吗，下一章中，我们将正式进入Flutter的世界！\")])])}),[],!1,null,null,null);r.default=v.exports}}]);"
  },
  {
    "path": "docs/assets/js/106.9b857fc8.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[106],{542:function(t,s,n){t.exports=n.p+\"assets/img/10-1.f2135ea6.png\"},836:function(t,s,n){\"use strict\";n.r(s);var a=n(45),e=Object(a.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_10-2-组合现有组件\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_10-2-组合现有组件\"}},[t._v(\"#\")]),t._v(\" 10.2 组合现有组件\")]),t._v(\" \"),a(\"p\",[t._v(\"在Flutter中页面UI通常都是由一些低阶别的组件组合而成，当我们需要封装一些通用组件时，应该首先考虑是否可以通过组合其它组件来实现，如果可以，则应优先使用组合，因为直接通过现有组件拼装会非常简单、灵活、高效。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"示例-自定义渐变按钮\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例-自定义渐变按钮\"}},[t._v(\"#\")]),t._v(\" 示例：自定义渐变按钮\")]),t._v(\" \"),a(\"p\",[t._v(\"Flutter Material组件库中的按钮默认不支持渐变背景，为了实现渐变背景按钮，我们自定义一个\"),a(\"code\",[t._v(\"GradientButton\")]),t._v(\"组件，它需要支持一下功能：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"背景支持渐变色\")]),t._v(\" \"),a(\"li\",[t._v(\"手指按下时有涟漪效果\")]),t._v(\" \"),a(\"li\",[t._v(\"可以支持圆角\")])]),t._v(\" \"),a(\"p\",[t._v(\"我们先来看看最终要实现的效果（图10-1）：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(542),alt:\"gradient-button\"}})]),t._v(\" \"),a(\"p\",[t._v(\"我们\"),a(\"code\",[t._v(\"DecoratedBox\")]),t._v(\"可以支持背景色渐变和圆角，\"),a(\"code\",[t._v(\"InkWell\")]),t._v(\"在手指按下有涟漪效果，所以我们可以通过组合\"),a(\"code\",[t._v(\"DecoratedBox\")]),t._v(\"和\"),a(\"code\",[t._v(\"InkWell\")]),t._v(\"来实现\"),a(\"code\",[t._v(\"GradientButton\")]),t._v(\"，代码如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"GradientButton\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GradientButton\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"borderRadius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 渐变色数组\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" List\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Color\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 按钮宽高\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" double width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" double height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Widget child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" BorderRadius borderRadius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//点击回调\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" GestureTapCallback onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    ThemeData theme \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Theme\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//确保colors数组不空\")]),t._v(\"\\n    List\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Color\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _colors \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" colors \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"theme\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"primaryColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" theme\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"primaryColorDark \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" theme\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"primaryColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecoratedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        gradient\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"LinearGradient\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        borderRadius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" borderRadius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Material\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        type\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MaterialType\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"transparency\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"InkWell\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          splashColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"last\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          highlightColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"transparent\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          borderRadius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" borderRadius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          onTap\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ConstrainedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            constraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BoxConstraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"tightFor\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"8.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DefaultTextStyle\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  style\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"fontWeight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" FontWeight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"bold\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"可以看到\"),a(\"code\",[t._v(\"GradientButton\")]),t._v(\"是由\"),a(\"code\",[t._v(\"DecoratedBox\")]),t._v(\"、\"),a(\"code\",[t._v(\"Padding\")]),t._v(\"、\"),a(\"code\",[t._v(\"Center\")]),t._v(\"、\"),a(\"code\",[t._v(\"InkWell\")]),t._v(\"等组件组合而成。当然上面的代码只是一个示例，作为一个按钮它还并不完整，比如没有禁用状态，读者可以根据实际需要来完善。\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"使用gradientbutton\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#使用gradientbutton\"}},[t._v(\"#\")]),t._v(\" 使用GradientButton\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'../widgets/index.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"GradientButtonRoute\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _GradientButtonRouteState \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_GradientButtonRouteState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_GradientButtonRouteState\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"GradientButtonRoute\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GradientButton\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"orange\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Submit\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" onTap\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GradientButton\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"lightGreen\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"green\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"700\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Submit\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" onTap\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GradientButton\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"lightBlue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blueAccent\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Submit\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" onTap\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onTap\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"button click\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"h3\",{attrs:{id:\"总结\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),a(\"p\",[t._v(\"通过组合的方式定义组件和我们之前写界面并无差异，不过在抽离出单独的组件时我们要考虑代码规范性，如必要参数要用\"),a(\"code\",[t._v(\"@required\")]),t._v(\" 标注，对于可选参数在特定场景需要判空或设置默认值等。这是由于使用者大多时候可能不了解组件的内部细节，所以为了保证代码健壮性，我们需要在用户错误地使用组件时能够兼容或报错提示（使用\"),a(\"code\",[t._v(\"assert\")]),t._v(\"断言函数）。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/107.188f42e4.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[107],{543:function(t,s,a){t.exports=a.p+\"assets/img/10-3.3989fea9.png\"},837:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_10-4-自绘组件-custompaint与canvas\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_10-4-自绘组件-custompaint与canvas\"}},[t._v(\"#\")]),t._v(\" 10.4 自绘组件 （CustomPaint与Canvas）\")]),t._v(\" \"),n(\"p\",[t._v(\"对于一些复杂或不规则的UI，我们可能无法通过组合其它组件的方式来实现，比如我们需要一个正六边形、一个渐变的圆形进度条、一个棋盘等。当然，有时候我们可以使用图片来实现，但在一些需要动态交互的场景静态图片也是实现不了的，比如要实现一个手写输入面板，这时，我们就需要来自己绘制UI外观。\")]),t._v(\" \"),n(\"p\",[t._v(\"几乎所有的UI系统都会提供一个自绘UI的接口，这个接口通常会提供一块2D画布\"),n(\"code\",[t._v(\"Canvas\")]),t._v(\"，\"),n(\"code\",[t._v(\"Canvas\")]),t._v(\"内部封装了一些基本绘制的API，开发者可以通过\"),n(\"code\",[t._v(\"Canvas\")]),t._v(\"绘制各种自定义图形。在Flutter中，提供了一个\"),n(\"code\",[t._v(\"CustomPaint\")]),t._v(\" 组件，它可以结合画笔\"),n(\"code\",[t._v(\"CustomPainter\")]),t._v(\"来实现自定义图形绘制。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"custompaint\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#custompaint\"}},[t._v(\"#\")]),t._v(\" CustomPaint\")]),t._v(\" \"),n(\"p\",[t._v(\"我们看看\"),n(\"code\",[t._v(\"CustomPaint\")]),t._v(\"构造函数：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CustomPaint\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"painter\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"foregroundPainter\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"size \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"zero\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isComplex \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"willChange \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n  Widget child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//子节点，可以为空\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"painter\")]),t._v(\": 背景画笔，会显示在子节点后面;\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"foregroundPainter\")]),t._v(\": 前景画笔，会显示在子节点前面\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"size\")]),t._v(\"：当child为null时，代表默认绘制区域大小，如果有child则忽略此参数，画布尺寸则为child尺寸。如果有child但是想指定画布为特定大小，可以使用SizeBox包裹CustomPaint实现。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"isComplex\")]),t._v(\"：是否复杂的绘制，如果是，Flutter会应用一些缓存策略来减少重复渲染的开销。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"willChange\")]),t._v(\"：和\"),n(\"code\",[t._v(\"isComplex\")]),t._v(\"配合使用，当启用缓存时，该属性代表在下一帧中绘制是否会改变。\")])]),t._v(\" \"),n(\"p\",[t._v(\"可以看到，绘制时我们需要提供前景或背景画笔，两者也可以同时提供。我们的画笔需要继承\"),n(\"code\",[t._v(\"CustomPainter\")]),t._v(\"类，我们在画笔类中实现真正的绘制逻辑。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"注意\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#注意\"}},[t._v(\"#\")]),t._v(\" 注意\")]),t._v(\" \"),n(\"p\",[t._v(\"如果\"),n(\"code\",[t._v(\"CustomPaint\")]),t._v(\"有子节点，为了避免子节点不必要的重绘并提高性能，通常情况下都会将子节点包裹在\"),n(\"code\",[t._v(\"RepaintBoundary\")]),t._v(\"组件中，这样会在绘制时就会创建一个新的绘制层（Layer），其子组件将在新的Layer上绘制，而父组件将在原来Layer上绘制，也就是说\"),n(\"code\",[t._v(\"RepaintBoundary\")]),t._v(\" 子组件的绘制将独立于父组件的绘制，\"),n(\"code\",[t._v(\"RepaintBoundary\")]),t._v(\"会隔离其子节点和\"),n(\"code\",[t._v(\"CustomPaint\")]),t._v(\"本身的绘制边界。示例如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CustomPaint\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Size\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//指定画布大小\")]),t._v(\"\\n  painter\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyPainter\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RepaintBoundary\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"h3\",{attrs:{id:\"custompainter\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#custompainter\"}},[t._v(\"#\")]),t._v(\" CustomPainter\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"CustomPainter\")]),t._v(\"中提定义了一个虚函数\"),n(\"code\",[t._v(\"paint\")]),t._v(\"：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language- extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[n(\"code\",[t._v(\"void paint(Canvas canvas, Size size);\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"paint\")]),t._v(\"有两个参数:\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"Canvas\")]),t._v(\"：一个画布，包括各种绘制方法，我们列出一下常用的方法：\")]),t._v(\" \"),n(\"table\",[n(\"thead\",[n(\"tr\",[n(\"th\",[t._v(\"API名称\")]),t._v(\" \"),n(\"th\",[t._v(\"功能\")])])]),t._v(\" \"),n(\"tbody\",[n(\"tr\",[n(\"td\",[t._v(\"drawLine\")]),t._v(\" \"),n(\"td\",[t._v(\"画线\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"drawPoint\")]),t._v(\" \"),n(\"td\",[t._v(\"画点\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"drawPath\")]),t._v(\" \"),n(\"td\",[t._v(\"画路径\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"drawImage\")]),t._v(\" \"),n(\"td\",[t._v(\"画图像\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"drawRect\")]),t._v(\" \"),n(\"td\",[t._v(\"画矩形\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"drawCircle\")]),t._v(\" \"),n(\"td\",[t._v(\"画圆\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"drawOval\")]),t._v(\" \"),n(\"td\",[t._v(\"画椭圆\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"drawArc\")]),t._v(\" \"),n(\"td\",[t._v(\"画圆弧\")])])])])]),t._v(\" \"),n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"Size\")]),t._v(\"：当前绘制区域大小。\")])])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"画笔paint\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#画笔paint\"}},[t._v(\"#\")]),t._v(\" 画笔Paint\")]),t._v(\" \"),n(\"p\",[t._v(\"现在画布有了，我们最后还缺一个画笔，Flutter提供了\"),n(\"code\",[t._v(\"Paint\")]),t._v(\"类来实现画笔。在\"),n(\"code\",[t._v(\"Paint\")]),t._v(\"中，我们可以配置画笔的各种属性如粗细、颜色、样式等。如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" paint \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Paint\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//创建一个画笔并配置其属性\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isAntiAlias \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//是否抗锯齿\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"style \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" PaintingStyle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"fill \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//画笔样式：填充\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Color\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0x77cdb175\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//画笔颜色\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"更多的配置属性读者可以参考Paint类定义。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"示例-五子棋-盘\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例-五子棋-盘\"}},[t._v(\"#\")]),t._v(\" 示例：五子棋/盘\")]),t._v(\" \"),n(\"p\",[t._v(\"下面我们通过一个五子棋游戏中棋盘和棋子的绘制来演示自绘UI的过程，首先我们看一下我们的目标效果，如图10-3所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(543),alt:\"图10-3\"}})]),t._v(\" \"),n(\"p\",[t._v(\"代码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'dart:math'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CustomPaintRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CustomPaint\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Size\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//指定画布大小\")]),t._v(\"\\n        painter\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyPainter\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyPainter\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CustomPainter\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"paint\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Canvas canvas\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Size size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    double eWidth \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"width \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"15\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    double eHeight \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"height \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"15\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//画棋盘背景\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" paint \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Paint\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isAntiAlias \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"style \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" PaintingStyle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"fill \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//填充\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"color \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Color\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0x77cdb175\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//背景为纸黄色\")]),t._v(\"\\n    canvas\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"drawRect\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"zero \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&\")]),t._v(\" size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" paint\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//画棋盘网格\")]),t._v(\"\\n    paint\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"style \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" PaintingStyle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"stroke \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//线\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"color \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"black87\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"strokeWidth \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"for\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int i \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" i \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"15\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"++\")]),t._v(\"i\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      double dy \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" eHeight \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" i\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      canvas\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"drawLine\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" dy\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" dy\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" paint\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"for\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int i \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" i \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"15\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"++\")]),t._v(\"i\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      double dx \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" eWidth \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" i\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      canvas\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"drawLine\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"dx\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"dx\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" paint\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//画一个黑子\")]),t._v(\"\\n    paint\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"style \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" PaintingStyle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"fill\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"color \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"black\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    canvas\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"drawCircle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"width \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" eWidth \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"height \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" eHeight \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"min\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"eWidth \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" eHeight \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      paint\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//画一个白子\")]),t._v(\"\\n    paint\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"color \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    canvas\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"drawCircle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"width \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" eWidth \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"height \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" eHeight \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"min\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"eWidth \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" eHeight \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      paint\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//在实际场景中正确利用此回调可以避免重绘开销，本示例我们简单的返回true\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  bool \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"shouldRepaint\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"CustomPainter oldDelegate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"h3\",{attrs:{id:\"性能\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#性能\"}},[t._v(\"#\")]),t._v(\" 性能\")]),t._v(\" \"),n(\"p\",[t._v(\"绘制是比较昂贵的操作，所以我们在实现自绘控件时应该考虑到性能开销，下面是两条关于性能优化的建议：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"p\",[t._v(\"尽可能的利用好\"),n(\"code\",[t._v(\"shouldRepaint\")]),t._v(\"返回值；在UI树重新build时，控件在绘制前都会先调用该方法以确定是否有必要重绘；假如我们绘制的UI不依赖外部状态，那么就应该始终返回\"),n(\"code\",[t._v(\"false\")]),t._v(\"，因为外部状态改变导致重新build时不会影响我们的UI外观；如果绘制依赖外部状态，那么我们就应该在\"),n(\"code\",[t._v(\"shouldRepaint\")]),t._v(\"中判断依赖的状态是否改变，如果已改变则应返回\"),n(\"code\",[t._v(\"true\")]),t._v(\"来重绘，反之则应返回\"),n(\"code\",[t._v(\"false\")]),t._v(\"不需要重绘。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"绘制尽可能多的分层；在上面五子棋的示例中，我们将棋盘和棋子的绘制放在了一起，这样会有一个问题：由于棋盘始终是不变的，用户每次落子时变的只是棋子，但是如果按照上面的代码来实现，每次绘制棋子时都要重新绘制一次棋盘，这是没必要的。优化的方法就是将棋盘单独抽为一个组件，并设置其\"),n(\"code\",[t._v(\"shouldRepaint\")]),t._v(\"回调值为\"),n(\"code\",[t._v(\"false\")]),t._v(\"，然后将棋盘组件作为背景。然后将棋子的绘制放到另一个组件中，这样每次落子时只需要绘制棋子。\")])])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"总结\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),n(\"p\",[t._v(\"自绘控件非常强大，理论上可以实现任何2D图形外观，实际上Flutter提供的所有组件最终都是通过调用Canvas绘制出来的，只不过绘制的逻辑被封装起来了，读者有兴趣可以查看具有外观样式的组件源码，找到其对应的\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"对象，如\"),n(\"code\",[t._v(\"Text\")]),t._v(\"对应的\"),n(\"code\",[t._v(\"RenderParagraph\")]),t._v(\"对象最终会通过\"),n(\"code\",[t._v(\"Canvas\")]),t._v(\"实现文本绘制逻辑。下一节我们会再通过一个自绘的圆形背景渐变进度条的实例来帮助读者加深印象。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/108.47fd6022.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[108],{544:function(t,s,n){t.exports=n.p+\"assets/img/gradient_circular_progress.4cea87cb.png\"},838:function(t,s,n){\"use strict\";n.r(s);var a=n(45),p=Object(a.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_10-5-自绘实例-圆形背景渐变进度条\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_10-5-自绘实例-圆形背景渐变进度条\"}},[t._v(\"#\")]),t._v(\" 10.5 自绘实例：圆形背景渐变进度条\")]),t._v(\" \"),a(\"p\",[t._v(\"本节我们实现一个圆形背景渐变进度条，它支持：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"支持多种背景渐变色。\")]),t._v(\" \"),a(\"li\",[t._v(\"任意弧度；进度条可以不是整圆。\")]),t._v(\" \"),a(\"li\",[t._v(\"可以自定义粗细、两端是否圆角等样式。\")])]),t._v(\" \"),a(\"p\",[t._v(\"可以发现要实现这样的一个进度条是无法通过现有组件组合而成的，所以我们通过自绘方式实现，代码如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'dart:math'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"GradientCircularProgressIndicator\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GradientCircularProgressIndicator\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"strokeWidth \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"stops\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"strokeCapRound \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"backgroundColor \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Color\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0xFFEEEEEE\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"totalAngle \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" pi\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"///粗细\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" double strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// 圆的半径\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" double radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"///两端是否为圆角\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" bool strokeCapRound\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// 当前进度，取值范围 [0.0-1.0]\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" double value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// 进度条背景色\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Color backgroundColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// 进度条的总弧度，2*PI为整圆，小于2*PI则不是整圆\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" double totalAngle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// 渐变色数组\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" List\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Color\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// 渐变色的终止点，对应colors属性\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" List\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" stops\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    double _offset \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 如果两端为圆角，则需要对起始位置进行调整，否则圆角部分会偏离起始位置\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 下面调整的角度的计算公式是通过数学几何知识得出，读者有兴趣可以研究一下为什么是这样  \")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"strokeCapRound\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _offset \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"asin\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"strokeWidth \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"radius \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" _colors \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_colors \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      Color color \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Theme\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"accentColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      _colors \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" Transform\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"rotate\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      angle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\"pi \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2.0\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" _offset\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CustomPaint\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          size\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Size\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fromRadius\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          painter\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_GradientCircularProgressPainter\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            strokeCapRound\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" strokeCapRound\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            backgroundColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" backgroundColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            total\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" totalAngle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//实现画笔\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_GradientCircularProgressPainter\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CustomPainter\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_GradientCircularProgressPainter\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"10.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"strokeCapRound\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"backgroundColor \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Color\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0xFFEEEEEE\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"total \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" pi\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"stops\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" double strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" bool strokeCapRound\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" double value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Color backgroundColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" List\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Color\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" double total\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" double radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" List\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" stops\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"paint\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Canvas canvas\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Size size\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"radius \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      size \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Size\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fromRadius\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    double _offset \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" strokeWidth \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    double _value \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"value \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    _value \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"clamp\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" total\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    double _start \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"strokeCapRound\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _start \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"asin\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"size\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"width \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n    Rect rect \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_offset\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" _offset\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Size\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        size\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"width \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        size\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"height \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" strokeWidth\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" paint \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Paint\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"strokeCap \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" strokeCapRound \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" StrokeCap\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"round \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" StrokeCap\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"butt\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"style \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" PaintingStyle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"stroke\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isAntiAlias \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"strokeWidth \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 先画背景\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"backgroundColor \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"transparent\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      paint\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"color \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" backgroundColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      canvas\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"drawArc\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          rect\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          _start\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          total\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          paint\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 再画前景，应用渐变\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_value \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      paint\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"shader \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SweepGradient\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        startAngle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        endAngle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        stops\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" stops\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createShader\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"rect\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n      canvas\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"drawArc\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          rect\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          _start\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          _value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          paint\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  bool \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"shouldRepaint\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"CustomPainter oldDelegate\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"下面我们来测试一下，为了尽可能多的展示\"),a(\"code\",[t._v(\"GradientCircularProgressIndicator\")]),t._v(\"的不同外观和用途，这个示例代码会比较长，并且添加了动画，建议读者将此示例运行起来观看实际效果，我们先看看其中的一帧动画的截图：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(544),alt:\"gradient_circular_progress\"}})]),t._v(\" \"),a(\"p\",[t._v(\"示例代码：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'dart:math'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'../widgets/index.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"GradientCircularProgressRoute\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  GradientCircularProgressRouteState \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"GradientCircularProgressRouteState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"GradientCircularProgressRouteState\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"GradientCircularProgressRoute\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"with\")]),t._v(\" TickerProviderStateMixin \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  AnimationController _animationController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    _animationController \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimationController\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"vsync\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" duration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    bool isForward \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    _animationController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addStatusListener\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"status\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"status \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" AnimationStatus\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"forward\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        isForward \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"status \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" AnimationStatus\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"completed \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"||\")]),t._v(\"\\n          status \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" AnimationStatus\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dismissed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"isForward\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          _animationController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"reverse\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          _animationController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"forward\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"status \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" AnimationStatus\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"reverse\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        isForward \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    _animationController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"forward\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    _animationController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SingleChildScrollView\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          crossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" CrossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedBuilder\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              animation\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _animationController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              builder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Widget child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"symmetric\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"vertical\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Wrap\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                        spacing\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"10.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        runSpacing\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GradientCircularProgressIndicator\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                            \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// No gradient\")]),t._v(\"\\n                            colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _animationController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GradientCircularProgressIndicator\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                            colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"orange\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _animationController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GradientCircularProgressIndicator\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                            colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"orange\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"5.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _animationController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GradientCircularProgressIndicator\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                            colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"teal\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cyan\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"5.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            strokeCapRound\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CurvedAnimation\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                                    parent\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _animationController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                    curve\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Curves\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"decelerate\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TurnBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                            turns\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"8\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GradientCircularProgressIndicator\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                                colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"orange\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"5.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                strokeCapRound\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                backgroundColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                totalAngle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.5\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" pi\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CurvedAnimation\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                                        parent\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _animationController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                        curve\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Curves\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"ease\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                                    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RotatedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                            quarterTurns\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GradientCircularProgressIndicator\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                                colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"700\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                strokeCapRound\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                backgroundColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"transparent\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _animationController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GradientCircularProgressIndicator\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                            colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                              Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                              Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"amber\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                              Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cyan\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                              Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"green\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                              Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                              Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\\n                            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"5.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            strokeCapRound\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _animationController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GradientCircularProgressIndicator\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                        colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"700\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _animationController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                        padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"symmetric\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"vertical\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GradientCircularProgressIndicator\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                          colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"700\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _animationController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          strokeCapRound\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//剪裁半圆\")]),t._v(\"\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ClipRect\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Align\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                          alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"topCenter\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          heightFactor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".5\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                            padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"only\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"bottom\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"8.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                              \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//width: 100.0,\")]),t._v(\"\\n                              child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TurnBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                                turns\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".75\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GradientCircularProgressIndicator\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                                  colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"teal\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cyan\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"500\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                  radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                  strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"8.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                  value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _animationController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                  totalAngle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" pi\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                  strokeCapRound\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                        height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"104.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Stack\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                          alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                            \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Positioned\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                              height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                              top\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                              child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TurnBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                                turns\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".75\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GradientCircularProgressIndicator\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                                  colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"teal\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cyan\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"500\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                  radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                  strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"8.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                  value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _animationController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                  totalAngle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" pi\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                  strokeCapRound\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                              padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"only\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"top\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"10.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                              child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                                \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"${(_animationController.value * 100).toInt()}%\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                style\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                                  fontSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"25.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                  color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blueGrey\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"怎么样，很炫酷吧！\"),a(\"code\",[t._v(\"GradientCircularProgressIndicator\")]),t._v(\"已经被添加进了笔者维护的flukit组件库中了，读者如果有需要，可以直接依赖flukit包。\")])])}),[],!1,null,null,null);s.default=p.exports}}]);"
  },
  {
    "path": "docs/assets/js/109.9119c4a8.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[109],{545:function(t,s,a){t.exports=a.p+\"assets/img/10-2.2a9ed156.png\"},841:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_10-3-组合实例-turnbox\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_10-3-组合实例-turnbox\"}},[t._v(\"#\")]),t._v(\" 10.3 组合实例：TurnBox\")]),t._v(\" \"),n(\"p\",[t._v(\"我们之前已经介绍过\"),n(\"code\",[t._v(\"RotatedBox\")]),t._v(\"，它可以旋转子组件，但是它有两个缺点：一是只能将其子节点以90度的倍数旋转；二是当旋转的角度发生变化时，旋转角度更新过程没有动画。\")]),t._v(\" \"),n(\"p\",[t._v(\"本节我们将实现一个\"),n(\"code\",[t._v(\"TurnBox\")]),t._v(\"组件，它不仅可以以任意角度来旋转其子节点，而且可以在角度发生变化时执行一个动画以过渡到新状态，同时，我们可以手动指定动画速度。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"TurnBox\")]),t._v(\"的完整代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/widgets.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TurnBox\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TurnBox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"turns \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//旋转的“圈”数,一圈为360度，如0.25圈即90度\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"speed \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//过渡动画执行的总时长\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" double turns\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" int speed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Widget child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _TurnBoxState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_TurnBoxState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_TurnBoxState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"TurnBox\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"with\")]),t._v(\" SingleTickerProviderStateMixin \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  AnimationController _controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    _controller \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimationController\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        vsync\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        lowerBound\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"infinity\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        upperBound\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" double\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"infinity\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    _controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"turns\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    _controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RotationTransition\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      turns\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didUpdateWidget\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"TurnBox oldWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didUpdateWidget\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"oldWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//旋转角度发生变化时执行过渡动画  \")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"oldWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"turns \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"turns\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"animateTo\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"turns\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        duration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"milliseconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"speed\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        curve\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Curves\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"easeOut\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上面代码中：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"我们是通过组合\"),n(\"code\",[t._v(\"RotationTransition\")]),t._v(\"和child来实现的旋转效果。\")]),t._v(\" \"),n(\"li\",[t._v(\"在\"),n(\"code\",[t._v(\"didUpdateWidget\")]),t._v(\"中，我们判断要旋转的角度是否发生了变化，如果变了，则执行一个过渡动画。\")])]),t._v(\" \"),n(\"p\",[t._v(\"下面我们测试一下\"),n(\"code\",[t._v(\"TurnBox\")]),t._v(\"的功能，测试代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'../widgets/index.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TurnBoxRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _TurnBoxRouteState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_TurnBoxRouteState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_TurnBoxRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"TurnBoxRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  double _turns \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TurnBox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            turns\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _turns\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            speed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"500\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"refresh\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TurnBox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            turns\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _turns\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            speed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1000\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"refresh\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"150.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"顺时针旋转1/5圈\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                _turns \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"逆时针旋转1/5圈\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                _turns \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"测试代码运行后效果如图10-2所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(545),alt:\"图10-2\"}})]),t._v(\" \"),n(\"p\",[t._v(\"当我们点击旋转按钮时，两个图标的旋转都会旋转1/5圈，但旋转的速度是不同的，读者可以自己运行一下示例看看效果。\")]),t._v(\" \"),n(\"p\",[t._v(\"实际上本示例只组合了\"),n(\"code\",[t._v(\"RotationTransition\")]),t._v(\"一个组件，它是一个最简的组合类组件示例。另外，如果我们封装的是\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"，那么一定要注意在组件更新时是否需要同步状态。比如我们要封装一个富文本展示组件\"),n(\"code\",[t._v(\"MyRichText\")]),t._v(\" ，它可以自动处理url链接，定义如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyRichText\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyRichText\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 文本字符串\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"linkStyle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// url链接样式\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" TextStyle linkStyle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _MyRichTextState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_MyRichTextState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"接下来我们在\"),n(\"code\",[t._v(\"_MyRichTextState\")]),t._v(\"中要实现的功能有两个：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"解析文本字符串“text”，生成\"),n(\"code\",[t._v(\"TextSpan\")]),t._v(\"缓存起来；\")]),t._v(\" \"),n(\"li\",[t._v(\"在\"),n(\"code\",[t._v(\"build\")]),t._v(\"中返回最终的富文本样式；\")])]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"_MyRichTextState\")]),t._v(\" 实现的代码大致如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_MyRichTextState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"MyRichText\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\n  TextSpan _textSpan\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RichText\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _textSpan\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  TextSpan \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"parseText\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 耗时操作：解析文本字符串，构建出TextSpan。\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 省略具体实现。\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    _textSpan \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"parseText\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"由于解析文本字符串，构建出\"),n(\"code\",[t._v(\"TextSpan\")]),t._v(\"是一个耗时操作，为了不在每次build的时候都解析一次，所以我们在\"),n(\"code\",[t._v(\"initState\")]),t._v(\"中对解析的结果进行了缓存，然后再\"),n(\"code\",[t._v(\"build\")]),t._v(\"中直接使用解析的结果\"),n(\"code\",[t._v(\"_textSpan\")]),t._v(\"。这看起来很不错，但是上面的代码有一个严重的问题，就是父组件传入的\"),n(\"code\",[t._v(\"text\")]),t._v(\"发生变化时（组件树结构不变），那么\"),n(\"code\",[t._v(\"MyRichText\")]),t._v(\"显示的内容不会更新，原因就是\"),n(\"code\",[t._v(\"initState\")]),t._v(\"只会在State创建时被调用，所以在\"),n(\"code\",[t._v(\"text\")]),t._v(\"发生变化时，\"),n(\"code\",[t._v(\"parseText\")]),t._v(\"没有重新执行，导致\"),n(\"code\",[t._v(\"_textSpan\")]),t._v(\"任然是旧的解析值。要解决这个问题也很简单，我们只需添加一个\"),n(\"code\",[t._v(\"didUpdateWidget\")]),t._v(\"回调，然后再里面重新调用\"),n(\"code\",[t._v(\"parseText\")]),t._v(\"即可：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didUpdateWidget\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"MyRichText oldWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"text \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" oldWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    _textSpan \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"parseText\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didUpdateWidget\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"oldWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"有些读者可能会觉得这个点也很简单，是的，的确很简单，之所以要在这里反复强调是因为这个点在实际开发中很容易被忽略，它虽然简单，但却很重要。总之，当我们在State中会缓存某些依赖Widget参数的数据时，一定要注意在组件更新时是否需要同步状态。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/11.2026a71f.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[11],{634:function(t,s,n){t.exports=n.p+\"assets/img/5-2.40b01667.png\"},635:function(t,s){t.exports=\"data:image/png;base64,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\"},636:function(t,s){t.exports=\"data:image/png;base64,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\"},637:function(t,s){t.exports=\"data:image/png;base64,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\"},638:function(t,s){t.exports=\"data:image/png;base64,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\"},639:function(t,s,n){t.exports=n.p+\"assets/img/5-7.27479289.png\"},640:function(t,s,n){t.exports=n.p+\"assets/img/5-8.5b913c51.png\"},901:function(t,s,n){\"use strict\";n.r(s);var a=n(45),r=Object(a.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_5-2-尺寸限制类容器\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_5-2-尺寸限制类容器\"}},[t._v(\"#\")]),t._v(\" 5.2 尺寸限制类容器\")]),t._v(\" \"),a(\"p\",[t._v(\"尺寸限制类容器用于限制容器大小，Flutter中提供了多种这样的容器，如\"),a(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"、\"),a(\"code\",[t._v(\"SizedBox\")]),t._v(\"、\"),a(\"code\",[t._v(\"UnconstrainedBox\")]),t._v(\"、\"),a(\"code\",[t._v(\"AspectRatio\")]),t._v(\"等，本节将介绍一些常用的。\")]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_5-2-1-constrainedbox\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_5-2-1-constrainedbox\"}},[t._v(\"#\")]),t._v(\" 5.2.1 ConstrainedBox\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"用于对子组件添加额外的约束。例如，如果你想让子组件的最小高度是80像素，你可以使用\"),a(\"code\",[t._v(\"const BoxConstraints(minHeight: 80.0)\")]),t._v(\"作为子组件的约束。\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"示例\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),a(\"p\",[t._v(\"我们先定义一个\"),a(\"code\",[t._v(\"redBox\")]),t._v(\"，它是一个背景颜色为红色的盒子，不指定它的宽度和高度：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[t._v(\"Widget redBox\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecoratedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"我们实现一个最小高度为50，宽度尽可能大的红色容器。\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ConstrainedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  constraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxConstraints\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    minWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" double\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"infinity\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//宽度尽可能大\")]),t._v(\"\\n    minHeight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//最小高度为50像素\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"5.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" redBox \\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"运行效果如图5-2所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(634),alt:\"图5-2\"}})]),t._v(\" \"),a(\"p\",[t._v(\"可以看到，我们虽然将Container的高度设置为5像素，但是最终却是50像素，这正是ConstrainedBox的最小高度限制生效了。如果将Container的高度设置为80像素，那么最终红色区域的高度也会是80像素，因为在此示例中，ConstrainedBox只限制了最小高度，并未限制最大高度。\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"boxconstraints\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#boxconstraints\"}},[t._v(\"#\")]),t._v(\" BoxConstraints\")]),t._v(\" \"),a(\"p\",[t._v(\"BoxConstraints用于设置限制条件，它的定义如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxConstraints\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"minWidth \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//最小宽度\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"maxWidth \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" double\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"infinity\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//最大宽度\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"minHeight \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//最小高度\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"maxHeight \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" double\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"infinity \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//最大高度\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"BoxConstraints还定义了一些便捷的构造函数，用于快速生成特定限制规则的BoxConstraints，如\"),a(\"code\",[t._v(\"BoxConstraints.tight(Size size)\")]),t._v(\"，它可以生成给定大小的限制；\"),a(\"code\",[t._v(\"const BoxConstraints.expand()\")]),t._v(\"可以生成一个尽可能大的用以填充另一个容器的BoxConstraints。除此之外还有一些其它的便捷函数，读者可以查看\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/rendering/BoxConstraints-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"API文档\"),a(\"OutboundLink\")],1),t._v(\"。\")]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_5-2-2-sizedbox\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_5-2-2-sizedbox\"}},[t._v(\"#\")]),t._v(\" 5.2.2 SizedBox\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"SizedBox\")]),t._v(\"用于给子元素指定固定的宽高，如：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" redBox\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"运行效果如图5-3所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(635),alt:\"图5-3\"}})]),t._v(\" \"),a(\"p\",[t._v(\"实际上\"),a(\"code\",[t._v(\"SizedBox\")]),t._v(\"只是\"),a(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"的一个定制，上面代码等价于：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ConstrainedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  constraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BoxConstraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"tightFor\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" redBox\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"而\"),a(\"code\",[t._v(\"BoxConstraints.tightFor(width: 80.0,height: 80.0)\")]),t._v(\"等价于：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxConstraints\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"minHeight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"maxHeight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"minWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"maxWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"而实际上\"),a(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"和\"),a(\"code\",[t._v(\"SizedBox\")]),t._v(\"都是通过\"),a(\"code\",[t._v(\"RenderConstrainedBox\")]),t._v(\"来渲染的，我们可以看到\"),a(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"和\"),a(\"code\",[t._v(\"SizedBox\")]),t._v(\"的\"),a(\"code\",[t._v(\"createRenderObject()\")]),t._v(\"方法都返回的是一个\"),a(\"code\",[t._v(\"RenderConstrainedBox\")]),t._v(\"对象：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\nRenderConstrainedBox \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createRenderObject\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"RenderConstrainedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    additionalConstraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"h2\",{attrs:{id:\"_5-2-3-多重限制\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_5-2-3-多重限制\"}},[t._v(\"#\")]),t._v(\" 5.2.3 多重限制\")]),t._v(\" \"),a(\"p\",[t._v(\"如果某一个组件有多个父级\"),a(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"限制，那么最终会是哪个生效？我们看一个例子：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ConstrainedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    constraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxConstraints\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"minWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"60.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" minHeight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"60.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//父\")]),t._v(\"\\n    child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ConstrainedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      constraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxConstraints\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"minWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"90.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" minHeight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//子\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" redBox\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"上面我们有父子两个\"),a(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"，他们的限制条件不同，运行后效果如图5-4所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(636),alt:\"图5-4\"}})]),t._v(\" \"),a(\"p\",[t._v(\"最终显示效果是宽90，高60，也就是说是子\"),a(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"的\"),a(\"code\",[t._v(\"minWidth\")]),t._v(\"生效，而\"),a(\"code\",[t._v(\"minHeight\")]),t._v(\"是父\"),a(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"生效。单凭这个例子，我们还总结不出什么规律，我们将上例中父子限制条件换一下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ConstrainedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    constraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxConstraints\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"minWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"90.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" minHeight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ConstrainedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      constraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxConstraints\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"minWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"60.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" minHeight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"60.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" redBox\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"运行效果如图5-5所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(637),alt:\"图5-5\"}})]),t._v(\" \"),a(\"p\",[t._v(\"最终的显示效果仍然是90，高60，效果相同，但意义不同，因为此时\"),a(\"code\",[t._v(\"minWidth\")]),t._v(\"生效的是父\"),a(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"，而\"),a(\"code\",[t._v(\"minHeight\")]),t._v(\"是子\"),a(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"生效。\")]),t._v(\" \"),a(\"p\",[t._v(\"通过上面示例，我们发现有多重限制时，对于\"),a(\"code\",[t._v(\"minWidth\")]),t._v(\"和\"),a(\"code\",[t._v(\"minHeight\")]),t._v(\"来说，是取父子中相应数值较大的。实际上，只有这样才能保证父限制与子限制不冲突。\")]),t._v(\" \"),a(\"blockquote\",[a(\"p\",[t._v(\"思考题：对于\"),a(\"code\",[t._v(\"maxWidth\")]),t._v(\"和\"),a(\"code\",[t._v(\"maxHeight\")]),t._v(\"，多重限制的策略是什么样的呢？\")])]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_5-2-4-unconstrainedbox\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_5-2-4-unconstrainedbox\"}},[t._v(\"#\")]),t._v(\" 5.2.4 UnconstrainedBox\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"UnconstrainedBox\")]),t._v('不会对子组件产生任何限制，它允许其子组件按照其本身大小绘制。一般情况下，我们会很少直接使用此组件，但在\"去除\"多重限制的时候也许会有帮助，我们看下下面的代码：')]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ConstrainedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    constraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxConstraints\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"minWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"60.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" minHeight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//父\")]),t._v(\"\\n    child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"UnconstrainedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//“去除”父级限制\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ConstrainedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        constraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxConstraints\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"minWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"90.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" minHeight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//子\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" redBox\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"上面代码中，如果没有中间的\"),a(\"code\",[t._v(\"UnconstrainedBox\")]),t._v(\"，那么根据上面所述的多重限制规则，那么最终将显示一个90×100的红色框。但是由于\"),a(\"code\",[t._v(\"UnconstrainedBox\")]),t._v(\" “去除”了父\"),a(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"的限制，则最终会按照子\"),a(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"的限制来绘制\"),a(\"code\",[t._v(\"redBox\")]),t._v(\"，即90×20：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(638),alt:\"图5-6\"}})]),t._v(\" \"),a(\"p\",[t._v(\"但是，读者请注意，\"),a(\"code\",[t._v(\"UnconstrainedBox\")]),t._v(\"对父组件限制的“去除”并非是真正的去除：上面例子中虽然红色区域大小是90×20，但上方仍然有80的空白空间。也就是说父限制的\"),a(\"code\",[t._v(\"minHeight\")]),t._v(\"(100.0)仍然是生效的，只不过它不影响最终子元素\"),a(\"code\",[t._v(\"redBox\")]),t._v(\"的大小，但仍然还是占有相应的空间，可以认为此时的父\"),a(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"是作用于子\"),a(\"code\",[t._v(\"UnconstrainedBox\")]),t._v(\"上，而\"),a(\"code\",[t._v(\"redBox\")]),t._v(\"只受子\"),a(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"限制，这一点请读者务必注意。\")]),t._v(\" \"),a(\"p\",[t._v(\"那么有什么方法可以彻底去除父\"),a(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"的限制吗？答案是否定的！所以在此提示读者，在定义一个通用的组件时，如果要对子组件指定限制，那么一定要注意，因为一旦指定限制条件，子组件如果要进行相关自定义大小时将可能非常困难，因为子组件在不更改父组件的代码的情况下无法彻底去除其限制条件。\")]),t._v(\" \"),a(\"p\",[t._v(\"在实际开发中，当我们发现已经使用\"),a(\"code\",[t._v(\"SizedBox\")]),t._v(\"或\"),a(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"给子元素指定了宽高，但是仍然没有效果时，几乎可以断定：已经有父元素已经设置了限制！举个例子，如Material组件库中的\"),a(\"code\",[t._v(\"AppBar\")]),t._v(\"（导航栏）的右侧菜单中，我们使用\"),a(\"code\",[t._v(\"SizedBox\")]),t._v(\"指定了loading按钮的大小，代码如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n   title\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n   actions\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n         \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n             width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n             height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n             child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircularProgressIndicator\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                 strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                 valueColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlwaysStoppedAnimation\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white70\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n             \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n         \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n   \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"上面代码运行后，效果如图5-7所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(639),alt:\"图5-6\"}})]),t._v(\" \"),a(\"p\",[t._v(\"我们会发现右侧loading按钮大小并没有发生变化！这正是因为\"),a(\"code\",[t._v(\"AppBar\")]),t._v(\"中已经指定了\"),a(\"code\",[t._v(\"actions\")]),t._v(\"按钮的限制条件，所以我们要自定义loading按钮大小，就必须通过\"),a(\"code\",[t._v(\"UnconstrainedBox\")]),t._v(\"来“去除”父元素的限制，代码如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  title\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  actions\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"UnconstrainedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircularProgressIndicator\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                valueColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlwaysStoppedAnimation\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white70\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"运行后效果如图5-8所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(640),alt:\"图5-8\"}})]),t._v(\" \"),a(\"p\",[t._v(\"生效了！\")]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_5-2-4-其它尺寸限制类容器\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_5-2-4-其它尺寸限制类容器\"}},[t._v(\"#\")]),t._v(\" 5.2.4 其它尺寸限制类容器\")]),t._v(\" \"),a(\"p\",[t._v(\"除了上面介绍的这些常用的尺寸限制类容器外，还有一些其他的尺寸限制类容器，比如\"),a(\"code\",[t._v(\"AspectRatio\")]),t._v(\"，它可以指定子组件的长宽比、\"),a(\"code\",[t._v(\"LimitedBox\")]),t._v(\" 用于指定最大宽高、\"),a(\"code\",[t._v(\"FractionallySizedBox\")]),t._v(\" 可以根据父容器宽高的百分比来设置子组件宽高等，由于这些容器使用起来都比较简单，我们便不再赘述，读者可以自行了解。\")])])}),[],!1,null,null,null);s.default=r.exports}}]);"
  },
  {
    "path": "docs/assets/js/110.fec0c84f.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[110],{546:function(t,s,a){t.exports=a.p+\"assets/img/11-1.dd10418c.png\"},845:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_11-2-通过httpclient发起http请求\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_11-2-通过httpclient发起http请求\"}},[t._v(\"#\")]),t._v(\" 11.2 通过HttpClient发起HTTP请求\")]),t._v(\" \"),n(\"p\",[t._v(\"Dart IO库中提供了用于发起Http请求的一些类，我们可以直接使用\"),n(\"code\",[t._v(\"HttpClient\")]),t._v(\"来发起请求。使用\"),n(\"code\",[t._v(\"HttpClient\")]),t._v(\"发起请求分为五步：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"p\",[t._v(\"创建一个\"),n(\"code\",[t._v(\"HttpClient\")]),t._v(\"：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\" HttpClient httpClient \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"HttpClient\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"打开Http连接，设置请求头：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"HttpClientRequest request \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" httpClient\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getUrl\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"uri\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"这一步可以使用任意Http Method，如\"),n(\"code\",[t._v(\"httpClient.post(...)\")]),t._v(\"、\"),n(\"code\",[t._v(\"httpClient.delete(...)\")]),t._v(\"等。如果包含Query参数，可以在构建uri时添加，如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Uri uri\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Uri\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"scheme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"https\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" host\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"flutterchina.club\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" queryParameters\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"xx\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"xx\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"yy\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"dd\"')]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"通过\"),n(\"code\",[t._v(\"HttpClientRequest\")]),t._v(\"可以设置请求header，如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"request\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"headers\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"user-agent\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"test\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"如果是post或put等可以携带请求体方法，可以通过HttpClientRequest对象发送request body，如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"String payload\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"...\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\nrequest\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"utf8\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"encode\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"payload\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//request.addStream(_inputStream); //可以直接添加输入流\")]),t._v(\"\\n\")])])])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"等待连接服务器：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"HttpClientResponse response \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" request\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"close\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"这一步完成后，请求信息就已经发送给服务器了，返回一个\"),n(\"code\",[t._v(\"HttpClientResponse\")]),t._v(\"对象，它包含响应头（header）和响应流(响应体的Stream)，接下来就可以通过读取响应流来获取响应内容。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"读取响应内容：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"String responseBody \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" response\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"transform\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"utf8\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"decoder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"join\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们通过读取响应流来获取服务器返回的数据，在读取时我们可以设置编码格式，这里是utf8。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"请求结束，关闭\"),n(\"code\",[t._v(\"HttpClient\")]),t._v(\"：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"httpClient\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"close\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"关闭client后，通过该client发起的所有请求都会中止。\")])])]),t._v(\" \"),n(\"h4\",{attrs:{id:\"示例\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),n(\"p\",[t._v(\"我们实现一个获取百度首页html的例子，示例效果如图11-1所示：\")]),t._v(\" \"),n(\"p\",[t._v(\"​    \"),n(\"img\",{attrs:{src:a(546),alt:\"图11-1\"}})]),t._v(\" \"),n(\"p\",[t._v(\"点击“获取百度首页”按钮后，会请求百度首页，请求成功后，我们将返回内容显示出来并在控制台打印响应header，代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'dart:convert'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'dart:io'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"HttpTestRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _HttpTestRouteState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_HttpTestRouteState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_HttpTestRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"HttpTestRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  bool _loading \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  String _text \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ConstrainedBox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      constraints\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BoxConstraints\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"expand\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SingleChildScrollView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"获取百度首页\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _loading \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                    _loading \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                    _text \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"正在请求...\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//创建一个HttpClient\")]),t._v(\"\\n                    HttpClient httpClient \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"HttpClient\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//打开Http连接\")]),t._v(\"\\n                    HttpClientRequest request \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" httpClient\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getUrl\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                        Uri\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"parse\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"https://www.baidu.com\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//使用iPhone的UA\")]),t._v(\"\\n                    request\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"headers\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"user-agent\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//等待连接服务器（会将请求信息发送给服务器）\")]),t._v(\"\\n                    HttpClientResponse response \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" request\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"close\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//读取响应内容\")]),t._v(\"\\n                    _text \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" response\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"transform\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"utf8\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"decoder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"join\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//输出响应头\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"response\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"headers\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//关闭client后，通过该client发起的所有请求都会中止。\")]),t._v(\"\\n                    httpClient\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"close\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"catch\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                    _text \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"请求失败：$e\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"finally\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                      _loading \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MediaQuery\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"width\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"replaceAll\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"RegExp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('r\"\\\\s\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"控制台输出：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language- extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[n(\"code\",[t._v(\"I/flutter (18545): connection: Keep-Alive\\nI/flutter (18545): cache-control: no-cache\\nI/flutter (18545): set-cookie: ....  //有多个，省略...\\nI/flutter (18545): transfer-encoding: chunked\\nI/flutter (18545): date: Tue, 30 Oct 2018 10:00:52 GMT\\nI/flutter (18545): content-encoding: gzip\\nI/flutter (18545): vary: Accept-Encoding\\nI/flutter (18545): strict-transport-security: max-age=172800\\nI/flutter (18545): content-type: text/html;charset=utf-8\\nI/flutter (18545): tracecode: 00525262401065761290103018, 00522983\\n\")])])]),n(\"h4\",{attrs:{id:\"httpclient配置\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#httpclient配置\"}},[t._v(\"#\")]),t._v(\" HttpClient配置\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"HttpClient\")]),t._v(\"有很多属性可以配置，常用的属性列表如下：\")]),t._v(\" \"),n(\"table\",[n(\"thead\",[n(\"tr\",[n(\"th\",[t._v(\"属性\")]),t._v(\" \"),n(\"th\",[t._v(\"含义\")])])]),t._v(\" \"),n(\"tbody\",[n(\"tr\",[n(\"td\",[t._v(\"idleTimeout\")]),t._v(\" \"),n(\"td\",[t._v(\"对应请求头中的keep-alive字段值，为了避免频繁建立连接，httpClient在请求结束后会保持连接一段时间，超过这个阈值后才会关闭连接。\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"connectionTimeout\")]),t._v(\" \"),n(\"td\",[t._v(\"和服务器建立连接的超时，如果超过这个值则会抛出SocketException异常。\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"maxConnectionsPerHost\")]),t._v(\" \"),n(\"td\",[t._v(\"同一个host，同时允许建立连接的最大数量。\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"autoUncompress\")]),t._v(\" \"),n(\"td\",[t._v('对应请求头中的Content-Encoding，如果设置为true，则请求头中Content-Encoding的值为当前HttpClient支持的压缩算法列表，目前只有\"gzip\"')])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"userAgent\")]),t._v(\" \"),n(\"td\",[t._v(\"对应请求头中的User-Agent字段。\")])])])]),t._v(\" \"),n(\"p\",[t._v(\"可以发现，有些属性只是为了更方便的设置请求头，对于这些属性，你完全可以通过\"),n(\"code\",[t._v(\"HttpClientRequest\")]),t._v(\"直接设置header，不同的是通过\"),n(\"code\",[t._v(\"HttpClient\")]),t._v(\"设置的对整个\"),n(\"code\",[t._v(\"httpClient\")]),t._v(\"都生效，而通过\"),n(\"code\",[t._v(\"HttpClientRequest\")]),t._v(\"设置的只对当前请求生效。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"http请求认证\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#http请求认证\"}},[t._v(\"#\")]),t._v(\" HTTP请求认证\")]),t._v(\" \"),n(\"p\",[t._v(\"Http协议的认证（Authentication）机制可以用于保护非公开资源。如果Http服务器开启了认证，那么用户在发起请求时就需要携带用户凭据，如果你在浏览器中访问了启用Basic认证的资源时，浏览就会弹出一个登录框，如：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:\"https://cdn.jsdelivr.net/gh/flutterchina/flutter-in-action@1.0/docs/imgs/image-20181031114207514.png\",alt:\"image-20181031114207514\"}})]),t._v(\" \"),n(\"p\",[t._v(\"我们先看看Basic认证的基本过程：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"p\",[t._v(\"客户端发送http请求给服务器，服务器验证该用户是否已经登录验证过了，如果没有的话，  服务器会返回一个401 Unauthozied给客户端，并且在响应header中添加一个 “WWW-Authenticate” 字段，例如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language- extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[n(\"code\",[t._v('WWW-Authenticate: Basic realm=\"admin\"\\n')])])]),n(\"p\",[t._v('其中\"Basic\"为认证方式，realm为用户角色的分组，可以在后台添加分组。')])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"客户端得到响应码后，将用户名和密码进行base64编码（格式为用户名:密码），设置请求头Authorization，继续访问 :\")]),t._v(\" \"),n(\"div\",{staticClass:\"language- extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[n(\"code\",[t._v(\"Authorization: Basic YXXFISDJFISJFGIJIJG\\n\")])])]),n(\"p\",[t._v(\"服务器验证用户凭据，如果通过就返回资源内容。\")])])]),t._v(\" \"),n(\"p\",[t._v(\"注意，Http的方式除了Basic认证之外还有：Digest认证、Client认证、Form Based认证等，目前Flutter的HttpClient只支持Basic和Digest两种认证方式，这两种认证方式最大的区别是发送用户凭据时，对于用户凭据的内容，前者只是简单的通过Base64编码（可逆），而后者会进行哈希运算，相对来说安全一点点，但是为了安全起见，\"),n(\"strong\",[t._v(\"无论是采用Basic认证还是Digest认证，都应该在Https协议下\")]),t._v(\"，这样可以防止抓包和中间人攻击。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"HttpClient\")]),t._v(\"关于Http认证的方法和属性：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"addCredentials(Uri url, String realm, HttpClientCredentials credentials)\")])]),t._v(\" \"),n(\"p\",[t._v(\"该方法用于添加用户凭据,如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"httpClient\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addCredentials\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_uri\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"admin\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"HttpClientBasicCredentials\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"username\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"password\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//Basic认证凭据\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"如果是Digest认证，可以创建Digest认证凭据：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"HttpClientDigestCredentials\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"username\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"password\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])])]),t._v(\" \"),n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"authenticate(Future<bool> f(Uri url, String scheme, String realm))\")])]),t._v(\" \"),n(\"p\",[t._v(\"这是一个setter，类型是一个回调，当服务器需要用户凭据且该用户凭据未被添加时，httpClient会调用此回调，在这个回调当中，一般会调用\"),n(\"code\",[t._v(\"addCredential()\")]),t._v(\"来动态添加用户凭证，例如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"httpClient\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"authenticate\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Uri url\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" String scheme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" String realm\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"url\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"host\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"xx.com\"')]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\" realm\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"admin\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    httpClient\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addCredentials\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"url\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"admin\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"HttpClientBasicCredentials\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"username\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"pwd\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"一个建议是，如果所有请求都需要认证，那么应该在HttpClient初始化时就调用\"),n(\"code\",[t._v(\"addCredentials()\")]),t._v(\"来添加全局凭证，而不是去动态添加。\")])])]),t._v(\" \"),n(\"h4\",{attrs:{id:\"代理\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#代理\"}},[t._v(\"#\")]),t._v(\" 代理\")]),t._v(\" \"),n(\"p\",[t._v(\"可以通过\"),n(\"code\",[t._v(\"findProxy\")]),t._v(\"来设置代理策略，例如，我们要将所有请求通过代理服务器（192.168.1.2:8888）发送出去：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"  client\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"findProxy \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"uri\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 如果需要过滤uri，可以手动判断\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"PROXY 192.168.1.2:8888\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"findProxy\")]),t._v(' 回调返回值是一个遵循浏览器PAC脚本格式的字符串，详情可以查看API文档，如果不需要代理，返回\"DIRECT\"即可。')]),t._v(\" \"),n(\"p\",[t._v(\"在APP开发中，很多时候我们需要抓包来调试，而抓包软件(如charles)就是一个代理，这时我们就可以将请求发送到我们的抓包软件，我们就可以在抓包软件中看到请求的数据了。\")]),t._v(\" \"),n(\"p\",[t._v(\"有时代理服务器也启用了身份验证，这和http协议的认证是相似的，HttpClient提供了对应的Proxy认证方法和属性：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"set\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"authenticateProxy\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"bool\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"f\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String host\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int port\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" String scheme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" String realm\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addProxyCredentials\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    String host\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int port\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" String realm\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" HttpClientCredentials credentials\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"他们的使用方法和上面“HTTP请求认证”一节中介绍的\"),n(\"code\",[t._v(\"addCredentials\")]),t._v(\"和\"),n(\"code\",[t._v(\"authenticate\")]),t._v(\" 相同，故不再赘述。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"证书校验\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#证书校验\"}},[t._v(\"#\")]),t._v(\" 证书校验\")]),t._v(\" \"),n(\"p\",[t._v(\"Https中为了防止通过伪造证书而发起的中间人攻击，客户端应该对自签名或非CA颁发的证书进行校验。\"),n(\"code\",[t._v(\"HttpClient\")]),t._v(\"对证书校验的逻辑如下：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"如果请求的Https证书是可信CA颁发的，并且访问host包含在证书的domain列表中(或者符合通配规则)并且证书未过期，则验证通过。\")]),t._v(\" \"),n(\"li\",[t._v(\"如果第一步验证失败，但在创建HttpClient时，已经通过SecurityContext将证书添加到证书信任链中，那么当服务器返回的证书在信任链中的话，则验证通过。\")]),t._v(\" \"),n(\"li\",[t._v(\"如果1、2验证都失败了，如果用户提供了\"),n(\"code\",[t._v(\"badCertificateCallback\")]),t._v(\"回调，则会调用它，如果回调返回\"),n(\"code\",[t._v(\"true\")]),t._v(\"，则允许继续链接，如果返回\"),n(\"code\",[t._v(\"false\")]),t._v(\"，则终止链接。\")])]),t._v(\" \"),n(\"p\",[t._v(\"综上所述，我们的证书校验其实就是提供一个\"),n(\"code\",[t._v(\"badCertificateCallback\")]),t._v(\"回调，下面通过一个示例来说明。\")]),t._v(\" \"),n(\"h5\",{attrs:{id:\"示例-2\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例-2\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),n(\"p\",[t._v(\"假设我们的后台服务使用的是自签名证书，证书格式是PEM格式，我们将证书的内容保存在本地字符串中，那么我们的校验逻辑如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"String PEM\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"XXXXX\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//可以从文件读取\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\nhttpClient\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"badCertificateCallback\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"X509Certificate cert\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" String host\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int port\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"cert\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"pem\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\"PEM\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//证书一致，则允许发送数据\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"X509Certificate\")]),t._v(\"是证书的标准格式，包含了证书除私钥外所有信息，读者可以自行查阅文档。另外，上面的示例没有校验host，是因为只要服务器返回的证书内容和本地的保存一致就已经能证明是我们的服务器了（而不是中间人），host验证通常是为了防止证书和域名不匹配。\")]),t._v(\" \"),n(\"p\",[t._v(\"对于自签名的证书，我们也可以将其添加到本地证书信任链中，这样证书验证时就会自动通过，而不会再走到\"),n(\"code\",[t._v(\"badCertificateCallback\")]),t._v(\"回调中：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"SecurityContext sc\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"SecurityContext\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//file为证书路径\")]),t._v(\"\\nsc\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setTrustedCertificates\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"file\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//创建一个HttpClient\")]),t._v(\"\\nHttpClient httpClient \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"HttpClient\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" sc\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"注意，通过\"),n(\"code\",[t._v(\"setTrustedCertificates()\")]),t._v(\"设置的证书格式必须为PEM或PKCS12，如果证书格式为PKCS12，则需将证书密码传入，这样则会在代码中暴露证书密码，所以客户端证书校验不建议使用PKCS12格式的证书。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"总结\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),n(\"p\",[t._v(\"值得注意的是，\"),n(\"code\",[t._v(\"HttpClient\")]),t._v(\"提供的这些属性和方法最终都会作用在请求header里，我们完全可以通过手动去设置header来实现，之所以提供这些方法，只是为了方便开发者而已。另外，Http协议是一个非常重要的、使用最多的网络协议，每一个开发者都应该对http协议非常熟悉。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/111.95623244.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[111],{547:function(t,s,a){t.exports=a.p+\"assets/img/11-4.b1cfbd03.png\"},847:function(t,s,a){\"use strict\";a.r(s);var n=a(45),r=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_11-7-json转dart-model类\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_11-7-json转dart-model类\"}},[t._v(\"#\")]),t._v(\" 11.7 Json转Dart Model类\")]),t._v(\" \"),n(\"p\",[t._v(\"在实战中，后台接口往往会返回一些结构化数据，如JSON、XML等，如之前我们请求Github API的示例，它返回的数据就是JSON格式的字符串，为了方便我们在代码中操作JSON，我们先将JSON格式的字符串转为Dart对象，这个可以通过\"),n(\"code\",[t._v(\"dart:convert\")]),t._v(\"中内置的JSON解码器json.decode() 来实现，该方法可以根据JSON字符串具体内容将其转为List或Map，这样我们就可以通过他们来查找所需的值，如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//一个JSON格式的用户列表字符串\")]),t._v(\"\\nString jsonStr\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\\'[{\"name\":\"Jack\"},{\"name\":\"Rose\"}]\\'')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//将JSON字符串转为Dart对象(此处是List)\")]),t._v(\"\\nList items\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"json\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"decode\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"jsonStr\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//输出第一个用户的姓名\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"items\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"name\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"通过json.decode() 将JSON字符串转为List/Map的方法比较简单，它没有外部依赖或其它的设置，对于小项目很方便。但当项目变大时，这种手动编写序列化逻辑可能变得难以管理且容易出错，例如有如下JSON:\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-json extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-json\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"name\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"John Smith\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"email\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"john@example.com\"')]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们可以通过调用\"),n(\"code\",[t._v(\"json.decode\")]),t._v(\"方法来解码JSON ，使用JSON字符串作为参数:\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Map\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" user \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" json\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"decode\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"json\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Howdy, ${user['\")]),t._v(\"name\"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"']}!'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'We sent the verification link to ${user['\")]),t._v(\"email\"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"']}.'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"由于\"),n(\"code\",[t._v(\"json.decode()\")]),t._v(\"仅返回一个\"),n(\"code\",[t._v(\"Map<String, dynamic>\")]),t._v(\"，这意味着直到运行时我们才知道值的类型。 通过这种方法，我们失去了大部分静态类型语言特性：类型安全、自动补全和最重要的编译时异常。这样一来，我们的代码可能会变得非常容易出错。例如，当我们访问\"),n(\"code\",[t._v(\"name\")]),t._v(\"或\"),n(\"code\",[t._v(\"email\")]),t._v(\"字段时，我们输入的很快，导致字段名打错了。但由于这个JSON在map结构中，所以编译器不知道这个错误的字段名，所以编译时不会报错。\")]),t._v(\" \"),n(\"p\",[t._v(\"其实，这个问题在很多平台上都会遇到，而也早就有了好的解决方法即“Json Model化”，具体做法就是，通过预定义一些与Json结构对应的Model类，然后在请求到数据后再动态根据数据创建出Model类的实例。这样一来，在开发阶段我们使用的是Model类的实例，而不再是Map/List，这样访问内部属性时就不会发生拼写错误。例如，我们可以通过引入一个简单的模型类(Model class)来解决前面提到的问题，我们称之为\"),n(\"code\",[t._v(\"User\")]),t._v(\"。在User类内部，我们有：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[t._v(\"一个\"),n(\"code\",[t._v(\"User.fromJson\")]),t._v(\" 构造函数, 用于从一个map构造出一个 \"),n(\"code\",[t._v(\"User\")]),t._v(\"实例 map structure\")]),t._v(\" \"),n(\"li\",[t._v(\"一个\"),n(\"code\",[t._v(\"toJson\")]),t._v(\" 方法, 将 \"),n(\"code\",[t._v(\"User\")]),t._v(\" 实例转化为一个map.\")])]),t._v(\" \"),n(\"p\",[t._v(\"这样，调用代码现在可以具有类型安全、自动补全字段（name和email）以及编译时异常。如果我们将拼写错误字段视为\"),n(\"code\",[t._v(\"int\")]),t._v(\"类型而不是\"),n(\"code\",[t._v(\"String\")]),t._v(\"， 那么我们的代码就不会通过编译，而不是在运行时崩溃。\")]),t._v(\" \"),n(\"p\",[n(\"strong\",[t._v(\"user.dart\")])]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"User\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String name\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String email\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"User\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"name\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"email\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  User\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fromJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Map\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" json\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" name \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" json\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'name'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        email \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" json\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'email'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  Map\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'name'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" name\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'email'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" email\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"现在，序列化逻辑移到了模型本身内部。采用这种新方法，我们可以非常容易地反序列化user.\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Map userMap \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" json\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"decode\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"json\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" user \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"User\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"fromJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"userMap\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Howdy, ${user.name}!'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'We sent the verification link to ${user.email}.'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"要序列化一个user，我们只是将该\"),n(\"code\",[t._v(\"User\")]),t._v(\"对象传递给该\"),n(\"code\",[t._v(\"json.encode\")]),t._v(\"方法。我们不需要手动调用\"),n(\"code\",[t._v(\"toJson\")]),t._v(\"这个方法，因为`JSON.encode内部会自动调用。\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"String json \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" json\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"encode\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"user\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"这样，调用代码就不用担心JSON序列化了，但是，Model类还是必须的。在实践中，\"),n(\"code\",[t._v(\"User.fromJson\")]),t._v(\"和\"),n(\"code\",[t._v(\"User.toJson\")]),t._v(\"方法都需要单元测试到位，以验证正确的行为。\")]),t._v(\" \"),n(\"p\",[t._v(\"另外，实际场景中，JSON对象很少会这么简单，嵌套的JSON对象并不罕见，如果有什么能为我们自动处理JSON序列化，那将会非常好。幸运的是，有！\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"自动生成model\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#自动生成model\"}},[t._v(\"#\")]),t._v(\" 自动生成Model\")]),t._v(\" \"),n(\"p\",[t._v(\"尽管还有其他库可用，但在本书中，我们介绍一下官方推荐的\"),n(\"a\",{attrs:{href:\"https://pub.dartlang.org/packages/json_serializable\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"json_serializable package\"),n(\"OutboundLink\")],1),t._v(\"包。 它是一个自动化的源代码生成器，可以在开发阶段为我们生成JSON序列化模板，这样一来，由于序列化代码不再由我们手写和维护，我们将运行时产生JSON序列化异常的风险降至最低。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"在项目中设置json-serializable\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#在项目中设置json-serializable\"}},[t._v(\"#\")]),t._v(\" 在项目中设置json_serializable\")]),t._v(\" \"),n(\"p\",[t._v(\"要包含\"),n(\"code\",[t._v(\"json_serializable\")]),t._v(\"到我们的项目中，我们需要一个常规和两个\"),n(\"strong\",[t._v(\"开发依赖\")]),t._v(\"项。简而言之，\"),n(\"strong\",[t._v(\"开发依赖项\")]),t._v(\"是不包含在我们的应用程序源代码中的依赖项，它是开发过程中的一些辅助工具、脚本，和node中的开发依赖项相似。\")]),t._v(\" \"),n(\"p\",[n(\"strong\",[t._v(\"pubspec.yaml\")])]),t._v(\" \"),n(\"div\",{staticClass:\"language-yaml extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"dependencies\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"# Your other regular dependencies here\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"json_annotation\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ^2.0.0\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"dev_dependencies\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"# Your other dev_dependencies here\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"build_runner\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ^1.0.0\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"json_serializable\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ^2.0.0\\n\")])])]),n(\"p\",[t._v(\"在您的项目根文件夹中运行 \"),n(\"code\",[t._v(\"flutter packages get\")]),t._v(\" (或者在编辑器中点击 “Packages Get”) 以在项目中使用这些新的依赖项.\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"以json-serializable的方式创建model类\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#以json-serializable的方式创建model类\"}},[t._v(\"#\")]),t._v(\" 以json_serializable的方式创建model类\")]),t._v(\" \"),n(\"p\",[t._v(\"让我们看看如何将我们的\"),n(\"code\",[t._v(\"User\")]),t._v(\"类转换为一个\"),n(\"code\",[t._v(\"json_serializable\")]),t._v(\"。为了简单起见，我们使用前面示例中的简化JSON model。\")]),t._v(\" \"),n(\"p\",[n(\"strong\",[t._v(\"user.dart\")])]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:json_annotation/json_annotation.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// user.g.dart 将在我们运行生成命令后自动生成\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"part\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'user.g.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"///这个标注是告诉生成器，这个类是需要生成Model类的\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@JsonSerializable\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"User\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"User\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"name\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"email\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  String name\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  String email\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//不同的类使用不同的mixin即可\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"factory\")]),t._v(\" User\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fromJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Map\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" json\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _$\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"UserFromJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"json\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  Map\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _$\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"UserToJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"  \\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"有了上面的设置，源码生成器将生成用于序列化\"),n(\"code\",[t._v(\"name\")]),t._v(\"和\"),n(\"code\",[t._v(\"email\")]),t._v(\"字段的JSON代码。\")]),t._v(\" \"),n(\"p\",[t._v(\"如果需要，自定义命名策略也很容易。例如，如果我们正在使用的API返回带有_snake_case_的对象，但我们想在我们的模型中使用_lowerCamelCase_， 那么我们可以使用@JsonKey标注：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//显式关联JSON字段名与Model属性的对应关系 \")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@JsonKey\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"name\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'registration_date_millis'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" int registrationDateMillis\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"h3\",{attrs:{id:\"运行代码生成程序\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#运行代码生成程序\"}},[t._v(\"#\")]),t._v(\" 运行代码生成程序\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"json_serializable\")]),t._v(\"第一次创建类时，您会看到与图11-4类似的错误。\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(547),alt:\"ide_warning\"}})]),t._v(\" \"),n(\"p\",[t._v(\"这些错误是完全正常的，这是因为Model类的生成代码还不存在。为了解决这个问题，我们必须运行代码生成器来为我们生成序列化模板。有两种运行代码生成器的方法：\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"一次性生成\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#一次性生成\"}},[t._v(\"#\")]),t._v(\" 一次性生成\")]),t._v(\" \"),n(\"p\",[t._v(\"通过在我们的项目根目录下运行:\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-shell extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[n(\"code\",[t._v(\"flutter packages pub run build_runner build\\n\")])])]),n(\"p\",[t._v(\"这触发了一次性构建，我们可以在需要时为我们的Model生成json序列化代码，它通过我们的源文件，找出需要生成Model类的源文件（包含@JsonSerializable标注的）来生成对应的.g.dart文件。一个好的建议是将所有Model类放在一个单独的目录下，然后在该目录下执行命令。\")]),t._v(\" \"),n(\"p\",[t._v(\"虽然这非常方便，但如果我们不需要每次在Model类中进行更改时都要手动运行构建命令的话会更好。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"持续生成\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#持续生成\"}},[t._v(\"#\")]),t._v(\" 持续生成\")]),t._v(\" \"),n(\"p\",[t._v(\"使用_watcher_可以使我们的源代码生成的过程更加方便。它会监视我们项目中文件的变化，并在需要时自动构建必要的文件，我们可以通过\"),n(\"code\",[t._v(\"flutter packages pub run build_runner watch\")]),t._v(\"在项目根目录下运行来启动_watcher_。只需启动一次观察器，然后它就会在后台运行，这是安全的。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"自动化生成模板\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#自动化生成模板\"}},[t._v(\"#\")]),t._v(\" 自动化生成模板\")]),t._v(\" \"),n(\"p\",[t._v(\"上面的方法有一个最大的问题就是要为每一个json写模板，这是比较枯燥的。如果有一个工具可以直接根据JSON文本生成模板，那我们就能彻底解放双手了。笔者自己用dart实现了一个脚本，它可以自动生成模板，并直接将JSON转为Model类，下面我们看看怎么做：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"p\",[t._v('定义一个\"模板的模板\"，名为\"template.dart\"：')]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:json_annotation/json_annotation.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"%\")]),t._v(\"t\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"part\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'%s.g.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@JsonSerializable\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"%\")]),t._v(\"s \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"%\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"s\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"%\")]),t._v(\"s\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"factory\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"%\")]),t._v(\"s\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fromJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Map\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" json\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _$\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"%\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"sFromJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"json\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    Map\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _$\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"%\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"sToJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"模板中的“%t”、“%s”为占位符，将在脚本运行时动态被替换为合适的导入头和类名。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"写一个自动生成模板的脚本(mo.dart)，它可以根据指定的JSON目录，遍历生成模板，在生成时我们定义一些规则：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[t._v(\"如果JSON文件名以下划线“_”开始，则忽略此JSON文件。\")]),t._v(\" \"),n(\"li\",[t._v(\"复杂的JSON对象往往会出现嵌套，我们可以通过一个特殊标志来手动指定嵌套的对象（后面举例）。\")])]),t._v(\" \"),n(\"p\",[t._v(\"脚本我们通过Dart来写，源码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'dart:convert'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'dart:io'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:path/path.dart'\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"as\")]),t._v(\" path\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" TAG\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"\\\\$\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" SRC\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"./json\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//JSON 目录\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" DIST\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"lib/models/\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//输出model目录\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"walk\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//遍历JSON目录生成模板\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" src \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Directory\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"SRC\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" list \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" src\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"listSync\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" template\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"File\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"./template.dart\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"readAsStringSync\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  File file\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  list\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"forEach\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"f\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"FileSystemEntity\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"isFileSync\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"f\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"path\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      file \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"File\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"f\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"path\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" paths\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"path\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"basename\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"f\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"path\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"split\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\".\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      String name\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"paths\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"first\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"paths\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"last\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toLowerCase\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"json\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"||\")]),t._v(\"name\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"startsWith\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"_\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"name\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"startsWith\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"_\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//下面生成模板\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" map \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" json\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"decode\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"file\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"readAsStringSync\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//为了避免重复导入相同的包，我们用Set来保存生成的import语句。\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"set\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Set\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      StringBuffer attrs\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StringBuffer\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"map \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"as\")]),t._v(\" Map\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"forEach\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" v\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"startsWith\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"_\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          attrs\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"write\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getType\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"v\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"set\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"name\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          attrs\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"write\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\" \"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          attrs\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"write\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          attrs\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"writeln\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\";\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          attrs\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"write\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"    \"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      String  className\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"name\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toUpperCase\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\"name\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"substring\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" dist\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"format\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"template\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"name\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"className\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"className\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"attrs\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                className\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"className\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"className\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" _import\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"set\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"join\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\";\\\\r\\\\n\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      _import\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+=\")]),t._v(\"_import\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isEmpty\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\";\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      dist\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"dist\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"replaceFirst\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"%t\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"_import \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//将生成的模板输出\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"File\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$DIST$name.dart\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"writeAsStringSync\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"dist\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\nString \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"changeFirstChar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String str\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"bool upper\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"upper\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\"str\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toUpperCase\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"str\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toLowerCase\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\"str\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"substring\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//将JSON类型转为对应的dart类型\")]),t._v(\"\\n String \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getType\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"v\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"Set\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"set\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"String current\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  current\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"current\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toLowerCase\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"v \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"is\")]),t._v(\" bool\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"bool\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"v \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"is\")]),t._v(\" num\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"num\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"v \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"is\")]),t._v(\" Map\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Map<String,dynamic>\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"v \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"is\")]),t._v(\" List\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"List\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"v \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"is\")]),t._v(\" String\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//处理特殊标志\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"v\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"startsWith\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$TAG[]\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" className\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"changeFirstChar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"v\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"substring\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"className\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toLowerCase\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\"current\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"set\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'import \\\"$className.dart\\\"'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"List<${changeFirstChar(className)}>\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"v\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"startsWith\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"TAG\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" fileName\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"changeFirstChar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"v\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"substring\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"fileName\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toLowerCase\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\"current\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"set\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'import \\\"$fileName.dart\\\"'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"changeFirstChar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"fileName\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"String\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"String\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//替换模板占位符\")]),t._v(\"\\nString \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"format\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String fmt\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" List\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Object\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" params\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  int matchIndex \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  String \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"replace\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Match m\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"matchIndex \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\" params\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"length\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"switch\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"m\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"%s\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" params\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"matchIndex\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"++\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"throw\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Exception\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Missing parameter for string format\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"throw\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Exception\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Invalid format string: \"')]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" m\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" fmt\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"replaceAllMapped\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"%s\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" replace\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"main\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"walk\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"写一个shell(mo.sh)，将生成模板和生成model串起来：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-sh extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-sh\"}},[n(\"code\",[t._v(\"dart mo.dart\\nflutter packages pub run build_runner build --delete-conflicting-outputs\\n\")])])])])]),t._v(\" \"),n(\"p\",[t._v(\"至此，我们的脚本写好了，我们在根目录下新建一个json目录，然后把user.json移进去，然后在lib目录下创建一个models目录，用于保存最终生成的Model类。现在我们只需要一句命令即可生成Model类了:\")]),t._v(\" \"),n(\"div\",{staticClass:\"language- extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[n(\"code\",[t._v(\"./mo.sh  \\n\")])])]),n(\"p\",[t._v(\"运行后，一切都将自动执行，现在好多了，不是吗？\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"嵌套json\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#嵌套json\"}},[t._v(\"#\")]),t._v(\" 嵌套JSON\")]),t._v(\" \"),n(\"p\",[t._v(\"我们定义一个person.json内容修改为：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-json extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-json\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"name\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"John Smith\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"email\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"john@example.com\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"mother\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"name\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Alice\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"email\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"alice@example.com\"')]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"friends\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"name\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Jack\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"email\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Jack@example.com\"')]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"name\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Nancy\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"email\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Nancy@example.com\"')]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"每个Person都有\"),n(\"code\",[t._v(\"name\")]),t._v(\" 、\"),n(\"code\",[t._v(\"email\")]),t._v(\" 、 \"),n(\"code\",[t._v(\"mother\")]),t._v(\"和\"),n(\"code\",[t._v(\"friends\")]),t._v(\"四个字段，由于\"),n(\"code\",[t._v(\"mother\")]),t._v(\"也是一个Person，朋友是多个Person(数组)，所以我们期望生成的Model是下面这样：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:json_annotation/json_annotation.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"part\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'person.g.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@JsonSerializable\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Person\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Person\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \\n    String name\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    String email\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    Person mother\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    List\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Person\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" friends\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"factory\")]),t._v(\" Person\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fromJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Map\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" json\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _$\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"PersonFromJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"json\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    Map\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _$\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"PersonToJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\")])])]),n(\"p\",[t._v(\"这时，我们只需要简单修改一下JSON，添加一些特殊标志，重新运行mo.sh即可：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-json extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-json\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"name\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"John Smith\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"email\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"john@example.com\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"mother\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$person\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"friends\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$[]person\"')]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们使用美元符“$”作为特殊标志符(如果与内容冲突，可以修改mo.dart中的\"),n(\"code\",[t._v(\"TAG\")]),t._v(\"常量，自定义标志符)，脚本在遇到特殊标志符后会先把相应字段转为相应的对象或对象数组，对象数组需要在标志符后面添加数组符“[]”，符号后面接具体的类型名，此例中是person。其它类型同理，加入我们给User添加一个Person类型的 \"),n(\"code\",[t._v(\"boss\")]),t._v(\"字段：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-json extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-json\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"name\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"John Smith\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"email\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"john@example.com\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"boss\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$person\"')]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"重新运行mo.sh，生成的user.dart如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:json_annotation/json_annotation.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"person.dart\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"part\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'user.g.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@JsonSerializable\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"User\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"User\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    String name\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    String email\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    Person boss\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"factory\")]),t._v(\" User\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fromJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Map\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" json\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _$\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"UserFromJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"json\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    Map\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _$\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"UserToJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"可以看到，\"),n(\"code\",[t._v(\"boss\")]),t._v(\"字段已自动添加，并自动导入了“person.dart”。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"json-model-包\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#json-model-包\"}},[t._v(\"#\")]),t._v(\" Json_model 包\")]),t._v(\" \"),n(\"p\",[t._v(\"如果每个项目都要构建一个上面这样的脚本显然很麻烦，为此，我们将上面脚本和生成模板封装了一个包,已经发布到了Pub上，包名为\"),n(\"a\",{attrs:{href:\"https://github.com/flutterchina/json_model\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Json_model\"),n(\"OutboundLink\")],1),t._v(\"，开发者把该包加入开发依赖后，便可以用一条命令，根据Json文件生成Dart类。另外\"),n(\"a\",{attrs:{href:\"https://github.com/flutterchina/json_model\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Json_model\"),n(\"OutboundLink\")],1),t._v(\" 处于迭代中，功能会逐渐完善，所以建议读者直接使用该包（而不是手动复制上面的代码）。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"使用ide插件生成model\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#使用ide插件生成model\"}},[t._v(\"#\")]),t._v(\" 使用IDE插件生成model\")]),t._v(\" \"),n(\"p\",[t._v(\"目前Android Studio(或IntelliJ)有几个插件，可以将json文件转成Model类，但插件质量参差不齐，甚至还有一些沾染上了抄袭风波，故笔者在此不做优先推荐，读者有兴趣可以自行了解。但是，我们还是要了解一下IDE插件和\"),n(\"a\",{attrs:{href:\"https://link.juejin.im?target=https%3A%2F%2Fgithub.com%2Fflutterchina%2Fjson_model\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Json_model\"),n(\"OutboundLink\")],1),t._v(\"的优劣：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"a\",{attrs:{href:\"https://link.juejin.im?target=https%3A%2F%2Fgithub.com%2Fflutterchina%2Fjson_model\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Json_model\"),n(\"OutboundLink\")],1),t._v(\"需要单独维护一个存放Json文件的文件夹，如果有改动，只需修改Json文件便可重新生成Model类；而IDE插件一般需要用户手动将Json内容拷贝复制到一个输入框中，这样生成之后Json文件没有存档的化，之后要改动就需要手动。\")]),t._v(\" \"),n(\"li\",[n(\"a\",{attrs:{href:\"https://link.juejin.im?target=https%3A%2F%2Fgithub.com%2Fflutterchina%2Fjson_model\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Json_model\"),n(\"OutboundLink\")],1),t._v(\"可以手动指定某个字段引用的其它Model类，可以避免生成重复的类；而IDE插件一般会为每一个Json文件中所有嵌套对象都单独生成一个Model类，即使这些嵌套对象可能在其它Model类中已经生成过。\")]),t._v(\" \"),n(\"li\",[n(\"a\",{attrs:{href:\"https://link.juejin.im?target=https%3A%2F%2Fgithub.com%2Fflutterchina%2Fjson_model\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Json_model\"),n(\"OutboundLink\")],1),t._v(\" 提供了命令行转化方式，可以方便集成到CI等非UI环境的场景。\")])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"faq\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#faq\"}},[t._v(\"#\")]),t._v(\" FAQ\")]),t._v(\" \"),n(\"p\",[t._v(\"很多人可能会问Flutter中有没有像Java开发中的Gson/Jackson一样的Json序列化类库？答案是没有！因为这样的库需要使用运行时反射，这在Flutter中是禁用的。运行时反射会干扰Dart的_tree shaking_，使用_tree shaking_，可以在release版中“去除”未使用的代码，这可以显著优化应用程序的大小。由于反射会默认应用到所有代码，因此_tree shaking_会很难工作，因为在启用反射时很难知道哪些代码未被使用，因此冗余代码很难剥离，所以Flutter中禁用了Dart的反射功能，而正因如此也就无法实现动态转化Model的功能。\")])])}),[],!1,null,null,null);s.default=r.exports}}]);"
  },
  {
    "path": "docs/assets/js/112.e8e91632.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[112],{548:function(t,s,n){t.exports=n.p+\"assets/img/11-2.f480cac6.png\"},848:function(t,s,n){\"use strict\";n.r(s);var a=n(45),e=Object(a.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_11-6-使用socket-api\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_11-6-使用socket-api\"}},[t._v(\"#\")]),t._v(\" 11.6 使用Socket API\")]),t._v(\" \"),a(\"p\",[t._v(\"我们之前介绍的Http协议和WebSocket协议都属于应用层协议，除了它们，应用层协议还有很多如：SMTP、FTP等，这些应用层协议的实现都是通过Socket API来实现的。其实，操作系统中提供的原生网络请求API是标准的，在C语言的Socket库中，它主要提供了端到端建立链接和发送数据的基础API，而高级编程语言中的Socket库其实都是对操作系统的socket API的一个封装。所以，如果我们需要自定义协议或者想直接来控制管理网络链接、又或者我们觉得自带的HttpClient不好用想重新实现一个，这时我们就需要使用Socket。Flutter的Socket API在dart：io包中，下面我们看一个使用Socket实现简单http请求的示例，以请求百度首页为例：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_request\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//建立连接\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" socket\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" Socket\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"connect\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"baidu.com\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//根据http协议，发送请求头\")]),t._v(\"\\n  socket\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"writeln\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"GET / HTTP/1.1\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  socket\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"writeln\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Host:baidu.com\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  socket\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"writeln\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Connection:close\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  socket\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"writeln\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" socket\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"flush\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//发送\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//读取返回内容\")]),t._v(\"\\n  _response \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" socket\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"transform\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"utf8\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"decoder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"join\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" socket\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"close\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"可以看到，使用Socket需要我们自己实现Http协议（需要自己实现和服务器的通信过程），本例只是一个简单示例，没有处理重定向、cookie等。本示例完整代码参考示例demo，运行后效果如图11-2所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(548),alt:\"图11-2\"}})]),t._v(\" \"),a(\"p\",[t._v(\"可以看到响应内容分两个部分，第一部分是响应头，第二部分是响应体，服务端可以根据请求信息动态来输出响应体。由于本示例请求头比较简单，所以响应体和浏览器中访问的会有差别，读者可以补充一些请求头(如user-agent)来看看输出的变化。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/113.d0f44add.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[113],{551:function(t,a,e){t.exports=e.p+\"assets/img/12-3.2326714a.png\"},855:function(t,a,e){\"use strict\";e.r(a);var r=e(45),n=Object(r.a)({},(function(){var t=this,a=t.$createElement,r=t._self._c||a;return r(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[r(\"h1\",{attrs:{id:\"_12-2-插件开发-平台通道简介\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_12-2-插件开发-平台通道简介\"}},[t._v(\"#\")]),t._v(\" 12.2 插件开发：平台通道简介\")]),t._v(\" \"),r(\"p\",[t._v(\"“平台特定”或“特定平台”中的平台指的就是Flutter应用程序运行的平台，如Android或IOS。我们知道一个完整的Flutter应用程序实际上包括原生代码和Flutter代码两部分。由于Flutter本身只是一个UI系统，它本身是无法提供一些系统能力，比如使用蓝牙、相机、GPS等，因此要在Flutter APP中调用这些能力就必须和原生平台进行通信。为此，Flutter中提供了一个平台通道（platform channel），用于Flutter和原生平台的通信。平台通道正是Flutter和原生之间通信的桥梁，它也是Flutter插件的底层基础设施。\")]),t._v(\" \"),r(\"p\",[t._v(\"Flutter使用了一个灵活的系统，允许您调用特定平台的API，无论在Android上的Java或Kotlin代码中，还是iOS上的ObjectiveC或Swift代码中均可用。\")]),t._v(\" \"),r(\"p\",[t._v(\"Flutter与原生之间的通信依赖灵活的消息传递方式：\")]),t._v(\" \"),r(\"ul\",[r(\"li\",[t._v(\"应用的Flutter部分通过平台通道（platform channel）将消息发送到其应用程序的所在的宿主（iOS或Android）应用（原生应用）。\")]),t._v(\" \"),r(\"li\",[t._v(\"宿主监听平台通道，并接收该消息。然后它会调用该平台的API，并将响应发送回客户端，即应用程序的Flutter部分。\")])]),t._v(\" \"),r(\"h3\",{attrs:{id:\"平台通道\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#平台通道\"}},[t._v(\"#\")]),t._v(\" 平台通道\")]),t._v(\" \"),r(\"p\",[t._v(\"使用平台通道在Flutter(client)和原生(host)之间传递消息，如下图所示：\")]),t._v(\" \"),r(\"p\",[r(\"img\",{attrs:{src:e(551),alt:\"平台通道\"}})]),t._v(\" \"),r(\"p\",[t._v(\"当在Flutter中调用原生方法时，调用信息通过平台通道传递到原生，原生收到调用信息后方可执行指定的操作，如需返回数据，则原生会将数据再通过平台通道传递给Flutter。值得注意的是消息传递是异步的，这确保了用户界面在消息传递时不会被挂起。\")]),t._v(\" \"),r(\"p\",[t._v(\"在客户端，\"),r(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/services/MethodChannel-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"MethodChannel  API\"),r(\"OutboundLink\")],1),t._v(\" 可以发送与方法调用相对应的消息。 在宿主平台上，\"),r(\"code\",[t._v(\"MethodChannel\")]),t._v(\" 在\"),r(\"a\",{attrs:{href:\"https://docs.flutter.io/javadoc/io/flutter/plugin/common/MethodChannel.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Android API\"),r(\"OutboundLink\")],1),t._v(\" 和 \"),r(\"a\",{attrs:{href:\"https://docs.flutter.io/objcdoc/Classes/FlutterMethodChannel.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"FlutterMethodChannel iOS API\"),r(\"OutboundLink\")],1),t._v(\"可以接收方法调用并返回结果。这些类可以帮助我们用很少的代码就能开发平台插件。\")]),t._v(\" \"),r(\"blockquote\",[r(\"p\",[r(\"strong\",[t._v(\"注意\")]),t._v(\": 如果需要，方法调用(消息传递)可以是反向的，即宿主作为客户端调用Dart中实现的API。 \"),r(\"a\",{attrs:{href:\"https://pub.dartlang.org/packages/quick_actions\",target:\"_blank\",rel:\"noopener noreferrer\"}},[r(\"code\",[t._v(\"quick_actions\")]),r(\"OutboundLink\")],1),t._v(\"插件就是一个具体的例子。\")])]),t._v(\" \"),r(\"h3\",{attrs:{id:\"平台通道数据类型支持\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#平台通道数据类型支持\"}},[t._v(\"#\")]),t._v(\" 平台通道数据类型支持\")]),t._v(\" \"),r(\"p\",[t._v(\"平台通道使用标准消息编/解码器对消息进行编解码，它可以高效的对消息进行二进制序列化与反序列化。由于Dart与原生平台之间数据类型有所差异，下面我们列出数据类型之间的映射关系。\")]),t._v(\" \"),r(\"table\",[r(\"thead\",[r(\"tr\",[r(\"th\",[t._v(\"Dart\")]),t._v(\" \"),r(\"th\",[t._v(\"Android\")]),t._v(\" \"),r(\"th\",[t._v(\"iOS\")])])]),t._v(\" \"),r(\"tbody\",[r(\"tr\",[r(\"td\",[t._v(\"null\")]),t._v(\" \"),r(\"td\",[t._v(\"null\")]),t._v(\" \"),r(\"td\",[t._v(\"nil (NSNull when nested)\")])]),t._v(\" \"),r(\"tr\",[r(\"td\",[t._v(\"bool\")]),t._v(\" \"),r(\"td\",[t._v(\"java.lang.Boolean\")]),t._v(\" \"),r(\"td\",[t._v(\"NSNumber numberWithBool:\")])]),t._v(\" \"),r(\"tr\",[r(\"td\",[t._v(\"int\")]),t._v(\" \"),r(\"td\",[t._v(\"java.lang.Integer\")]),t._v(\" \"),r(\"td\",[t._v(\"NSNumber numberWithInt:\")])]),t._v(\" \"),r(\"tr\",[r(\"td\",[t._v(\"int, 如果不足32位\")]),t._v(\" \"),r(\"td\",[t._v(\"java.lang.Long\")]),t._v(\" \"),r(\"td\",[t._v(\"NSNumber numberWithLong:\")])]),t._v(\" \"),r(\"tr\",[r(\"td\",[t._v(\"int, 如果不足64位\")]),t._v(\" \"),r(\"td\",[t._v(\"java.math.BigInteger\")]),t._v(\" \"),r(\"td\",[t._v(\"FlutterStandardBigInteger\")])]),t._v(\" \"),r(\"tr\",[r(\"td\",[t._v(\"double\")]),t._v(\" \"),r(\"td\",[t._v(\"java.lang.Double\")]),t._v(\" \"),r(\"td\",[t._v(\"NSNumber numberWithDouble:\")])]),t._v(\" \"),r(\"tr\",[r(\"td\",[t._v(\"String\")]),t._v(\" \"),r(\"td\",[t._v(\"java.lang.String\")]),t._v(\" \"),r(\"td\",[t._v(\"NSString\")])]),t._v(\" \"),r(\"tr\",[r(\"td\",[t._v(\"Uint8List\")]),t._v(\" \"),r(\"td\",[t._v(\"byte[]\")]),t._v(\" \"),r(\"td\",[t._v(\"FlutterStandardTypedData typedDataWithBytes:\")])]),t._v(\" \"),r(\"tr\",[r(\"td\",[t._v(\"Int32List\")]),t._v(\" \"),r(\"td\",[t._v(\"int[]\")]),t._v(\" \"),r(\"td\",[t._v(\"FlutterStandardTypedData typedDataWithInt32:\")])]),t._v(\" \"),r(\"tr\",[r(\"td\",[t._v(\"Int64List\")]),t._v(\" \"),r(\"td\",[t._v(\"long[]\")]),t._v(\" \"),r(\"td\",[t._v(\"FlutterStandardTypedData typedDataWithInt64:\")])]),t._v(\" \"),r(\"tr\",[r(\"td\",[t._v(\"Float64List\")]),t._v(\" \"),r(\"td\",[t._v(\"double[]\")]),t._v(\" \"),r(\"td\",[t._v(\"FlutterStandardTypedData typedDataWithFloat64:\")])]),t._v(\" \"),r(\"tr\",[r(\"td\",[t._v(\"List\")]),t._v(\" \"),r(\"td\",[t._v(\"java.util.ArrayList\")]),t._v(\" \"),r(\"td\",[t._v(\"NSArray\")])]),t._v(\" \"),r(\"tr\",[r(\"td\",[t._v(\"Map\")]),t._v(\" \"),r(\"td\",[t._v(\"java.util.HashMap\")]),t._v(\" \"),r(\"td\",[t._v(\"NSDictionary\")])])])]),t._v(\" \"),r(\"p\",[t._v(\"当在发送和接收值时，这些值在消息中的序列化和反序列化会自动进行。\")]),t._v(\" \"),r(\"h3\",{attrs:{id:\"自定义编解码器\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#自定义编解码器\"}},[t._v(\"#\")]),t._v(\" 自定义编解码器\")]),t._v(\" \"),r(\"p\",[t._v(\"除了上面提到的\"),r(\"code\",[t._v(\"MethodChannel\")]),t._v(\"，还可以使用\"),r(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/services/BasicMessageChannel-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[r(\"code\",[t._v(\"BasicMessageChannel\")]),r(\"OutboundLink\")],1),t._v(\"，它支持使用自定义消息编解码器进行基本的异步消息传递。 此外，可以使用专门的\"),r(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/services/BinaryCodec-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[r(\"code\",[t._v(\"BinaryCodec\")]),r(\"OutboundLink\")],1),t._v(\"、\"),r(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/services/StringCodec-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[r(\"code\",[t._v(\"StringCodec\")]),r(\"OutboundLink\")],1),t._v(\"和 \"),r(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/services/JSONMessageCodec-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[r(\"code\",[t._v(\"JSONMessageCodec\")]),r(\"OutboundLink\")],1),t._v(\"类，或创建自己的编解码器。\")]),t._v(\" \"),r(\"h3\",{attrs:{id:\"如何获取平台信息\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#如何获取平台信息\"}},[t._v(\"#\")]),t._v(\" 如何获取平台信息\")]),t._v(\" \"),r(\"p\",[t._v(\"Flutter 中提供了一个全局变量\"),r(\"code\",[t._v(\"defaultTargetPlatform\")]),t._v(\"来获取当前应用的平台信息，\"),r(\"code\",[t._v(\"defaultTargetPlatform\")]),t._v('定义在\"platform.dart\"中，它的类型是'),r(\"code\",[t._v(\"TargetPlatform\")]),t._v(\"，这是一个枚举类，定义如下：\")]),t._v(\" \"),r(\"div\",{staticClass:\"language-dart extra-class\"},[r(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[r(\"code\",[r(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"enum\")]),t._v(\" TargetPlatform \"),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  android\"),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  fuchsia\"),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  iOS\"),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),r(\"p\",[t._v(\"可以看到目前Flutter只支持这三个平台。我们可以通过如下代码判断平台：\")]),t._v(\" \"),r(\"div\",{staticClass:\"language-dart extra-class\"},[r(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[r(\"code\",[r(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"defaultTargetPlatform\"),r(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\"TargetPlatform\"),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"android\"),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),r(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 是安卓系统，do something\")]),t._v(\"\\n  \"),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\")])])]),r(\"p\",[t._v(\"由于不同平台有它们各自的交互规范，Flutter Material库中的一些组件都针对相应的平台做了一些适配，比如路由组件\"),r(\"code\",[t._v(\"MaterialPageRoute\")]),t._v(\"，它在android和ios中会应用各自平台规范的切换动画。那如果我们想让我们的APP在所有平台都表现一致，比如希望在所有平台路由切换动画都按照ios平台一致的左右滑动切换风格该怎么做？Flutter中提供了一种覆盖默认平台的机制，我们可以通过显式指定\"),r(\"code\",[t._v(\"debugDefaultTargetPlatformOverride\")]),t._v(\"全局变量的值来指定应用平台。比如：\")]),t._v(\" \"),r(\"div\",{staticClass:\"language-dart extra-class\"},[r(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[r(\"code\",[t._v(\"debugDefaultTargetPlatformOverride\"),r(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"TargetPlatform\"),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"iOS\"),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),r(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"defaultTargetPlatform\"),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),r(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 会输出TargetPlatform.iOS\")]),t._v(\"\\n\")])])]),r(\"p\",[t._v(\"上面代码即在Android中运行后，Flutter APP就会认为是当前系统是iOS，Material组件库中所有组件交互方式都会和iOS平台对齐，\"),r(\"code\",[t._v(\"defaultTargetPlatform\")]),t._v(\"的值也会变为\"),r(\"code\",[t._v(\"TargetPlatform.iOS\")]),t._v(\"。\")])])}),[],!1,null,null,null);a.default=n.exports}}]);"
  },
  {
    "path": "docs/assets/js/114.df1f86a1.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[114],{554:function(t,a,s){t.exports=s.p+\"assets/img/13-1.fcda4f48.jpeg\"},861:function(t,a,s){\"use strict\";s.r(a);var e=s(45),n=Object(e.a)({},(function(){var t=this,a=t.$createElement,e=t._self._c||a;return e(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[e(\"h1\",{attrs:{id:\"_13-1-让app支持多语言\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_13-1-让app支持多语言\"}},[t._v(\"#\")]),t._v(\" 13.1 让App支持多语言\")]),t._v(\" \"),e(\"p\",[t._v(\"如果我们的应用要支持多种语言，那么我们需要“国际化”它。这意味着我们在开发时需要为应用程序支持的每种语言环境设置“本地化”的一些值，如文本和布局。Flutter SDK已经提供了一些组件和类来帮助我们实现国际化，下面我们来介绍一下Flutter中实现国际化的步骤。\")]),t._v(\" \"),e(\"p\",[t._v(\"接下来我们以\"),e(\"code\",[t._v(\"MaterialApp\")]),t._v(\"类为入口的应用来说明如何支持国际化。\")]),t._v(\" \"),e(\"blockquote\",[e(\"p\",[t._v(\"大多数应用程序都是通过\"),e(\"code\",[t._v(\"MaterialApp\")]),t._v(\"为入口，但根据低级别的\"),e(\"code\",[t._v(\"WidgetsApp\")]),t._v(\"类为入口编写的应用程序也可以使用相同的类和逻辑进行国际化。\"),e(\"code\",[t._v(\"MaterialApp\")]),t._v(\"实际上也是\"),e(\"code\",[t._v(\"WidgetsApp\")]),t._v(\"的一个包装。\")])]),t._v(\" \"),e(\"p\",[t._v(\"注意，”本地化的值和资源“是指我们针对不同语言准备的不同资源，这些资源一般是指文案（字符串），当然也会有一些其他的资源会根据不同语言地区而不同，比如我们需要显示一个APP上架地的国旗图片，那么不同Locale区域我们就需要提供不同的的国旗图片。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"支持国际化\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#支持国际化\"}},[t._v(\"#\")]),t._v(\" 支持国际化\")]),t._v(\" \"),e(\"p\",[t._v(\"默认情况下，Flutter SDK中的组件仅提供美国英语本地化资源（主要是文本）。要添加对其他语言的支持，应用程序须添加一个名为“flutter_localizations”的包依赖，然后还需要在\"),e(\"code\",[t._v(\"MaterialApp\")]),t._v(\"中进行一些配置。 要使用\"),e(\"code\",[t._v(\"flutter_localizations\")]),t._v(\"包，首先需要添加依赖到\"),e(\"code\",[t._v(\"pubspec.yaml\")]),t._v(\"文件中：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-yaml extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"dependencies\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"flutter\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"sdk\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" flutter\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"flutter_localizations\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"sdk\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" flutter\\n\")])])]),e(\"p\",[t._v(\"接下来，下载\"),e(\"code\",[t._v(\"flutter_localizations\")]),t._v(\"库，然后指定\"),e(\"code\",[t._v(\"MaterialApp\")]),t._v(\"的\"),e(\"code\",[t._v(\"localizationsDelegates\")]),t._v(\"和\"),e(\"code\",[t._v(\"supportedLocales\")]),t._v(\"：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter_localizations/flutter_localizations.dart'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MaterialApp\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n localizationsDelegates\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n   \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 本地化的代理类\")]),t._v(\"\\n   GlobalMaterialLocalizations\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"delegate\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n   GlobalWidgetsLocalizations\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"delegate\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n supportedLocales\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Locale\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'en'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'US'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 美国英语\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Locale\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'zh'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'CN'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 中文简体\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//其它Locales\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// ...\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),e(\"blockquote\",[e(\"p\",[t._v(\"与\"),e(\"code\",[t._v(\"MaterialApp\")]),t._v(\"类为入口的应用不同, 对基于\"),e(\"code\",[t._v(\"WidgetsApp\")]),t._v(\"类为入口的应用程序进行国际化时,不需要\"),e(\"code\",[t._v(\"GlobalMaterialLocalizations.delegate\")]),t._v(\"。\")])]),t._v(\" \"),e(\"p\",[e(\"code\",[t._v(\"localizationsDelegates\")]),t._v(\"列表中的元素是生成本地化值集合的工厂。\"),e(\"code\",[t._v(\"GlobalMaterialLocalizations.delegate\")]),t._v(\" 为Material 组件库提供的本地化的字符串和其他值，它可以使Material 组件支持多语言。 \"),e(\"code\",[t._v(\"GlobalWidgetsLocalizations.delegate\")]),t._v(\"定义组件默认的文本方向，从左到右或从右到左，这是因为有些语言的阅读习惯并不是从左到右，比如如阿拉伯语就是从右向左的。\")]),t._v(\" \"),e(\"p\",[e(\"code\",[t._v(\"supportedLocales\")]),t._v(\"也接收一个Locale数组，表示我们的应用支持的语言列表，在本例中我们的应用只支持美国英语和中文简体两种语言。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"获取当前区域locale\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#获取当前区域locale\"}},[t._v(\"#\")]),t._v(\" 获取当前区域Locale\")]),t._v(\" \"),e(\"p\",[e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/dart-ui/Locale-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"Locale\")]),e(\"OutboundLink\")],1),t._v(\"类是用来标识用户的语言环境的，它包括语言和国家两个标志如：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Locale\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'zh'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'CN'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 中文简体\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"我们始终可以通过以下方式来获取应用的当前区域Locale：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[t._v(\"Locale myLocale \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Localizations\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"localeOf\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),e(\"p\",[e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/Localizations-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"Localizations\")]),e(\"OutboundLink\")],1),t._v(\" 组件一般位于widget树中其它业务组件的顶部，它的作用是定义区域Locale以及设置子树依赖的本地化资源。 如果系统的语言环境发生变化，\"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/WidgetsApp-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"WidgetsApp\"),e(\"OutboundLink\")],1),t._v(\"将创建一个新的Localizations 组件并重建它，这样子树中通过\"),e(\"code\",[t._v(\"Localizations.localeOf(context)\")]),t._v(\" 获取的Locale就会更新。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"监听系统语言切换\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#监听系统语言切换\"}},[t._v(\"#\")]),t._v(\" 监听系统语言切换\")]),t._v(\" \"),e(\"p\",[t._v(\"当我们更改系统语言设置时，APP中的Localizations组件会重新构建，\"),e(\"code\",[t._v(\"Localizations.localeOf(context)\")]),t._v(\" 获取的Locale就会更新，最终界面会重新build达到切换语言的效果。但是这个过程是隐式完成的，我们并没有主动去监听系统语言切换，但是有时我们需要在系统语言发生改变时做一些事，比如系统语言切换为一种我们APP不支持的语言时，我们需要设置一个默认的语言，这时我们就需要监听locale改变事件。\")]),t._v(\" \"),e(\"p\",[t._v(\"我们可以通过\"),e(\"code\",[t._v(\"localeResolutionCallback\")]),t._v(\"或\"),e(\"code\",[t._v(\"localeListResolutionCallback\")]),t._v(\"回调来监听locale改变的事件，我们先看看\"),e(\"code\",[t._v(\"localeResolutionCallback\")]),t._v(\"的回调函数签名：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[t._v(\"Locale \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"Function\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Locale locale\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Iterable\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Locale\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" supportedLocales\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),e(\"ul\",[e(\"li\",[e(\"p\",[t._v(\"参数\"),e(\"code\",[t._v(\"locale\")]),t._v(\"的值为当前的当前的系统语言设置，当应用启动时或用户动态改变系统语言设置时此locale即为系统的当前locale。当开发者手动指定APP的locale时，那么此locale参数代表开发者指定的locale，此时将忽略系统locale如：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MaterialApp\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n locale\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Locale\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'en'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'US'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//手动指定locale\")]),t._v(\"\\n \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"上面的例子中手动指定了应用locale为美国英语，指定后即使设备当前语言是中文简体，应用中的locale也依然是美国英语。如果\"),e(\"code\",[t._v(\"locale\")]),t._v(\"为\"),e(\"code\",[t._v(\"null\")]),t._v(\"，则表示Flutter未能获取到设备的Locale信息，所以我们在使用\"),e(\"code\",[t._v(\"locale\")]),t._v(\"之前一定要先判空。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"code\",[t._v(\"supportedLocales\")]),t._v(\" 为当前应用支持的locale列表，是开发者在MaterialApp中通过\"),e(\"code\",[t._v(\"supportedLocales\")]),t._v(\"属性注册的。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[t._v(\"返回值是一个\"),e(\"code\",[t._v(\"Locale\")]),t._v(\"，此\"),e(\"code\",[t._v(\"Locale\")]),t._v(\"为Flutter APP最终使用的\"),e(\"code\",[t._v(\"Locale\")]),t._v(\"。通常在不支持的语言区域时返回一个默认的\"),e(\"code\",[t._v(\"Locale\")]),t._v(\"。\")])])]),t._v(\" \"),e(\"p\",[e(\"code\",[t._v(\"localeListResolutionCallback\")]),t._v(\"和\"),e(\"code\",[t._v(\"localeResolutionCallback\")]),t._v(\"唯一的不同就在第一个参数类型，前者接收的是一个\"),e(\"code\",[t._v(\"Locale\")]),t._v(\"列表，而后者接收的是单个\"),e(\"code\",[t._v(\"Locale\")]),t._v(\"。\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[t._v(\"Locale \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"Function\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"List\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Locale\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" locales\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Iterable\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Locale\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" supportedLocales\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"在较新的Android系统中，用户可以设置一个语言列表，这样一来，支持多语言的应用就会得到这个列表，应用通常的处理方式就是按照列表的顺序依次尝试加载相应的Locale，如果某一种语言加载成功则会停止。图13-1是Android系统中设置语言列表的截图：\")]),t._v(\" \"),e(\"p\",[e(\"img\",{attrs:{src:s(554),alt:\"设置语言列表\"}})]),t._v(\" \"),e(\"p\",[t._v(\"在Flutter中，应该优先使用\"),e(\"code\",[t._v(\"localeListResolutionCallback\")]),t._v(\"，当然你不必担心Android系统的差异性，如果在低版本的Android系统中，Flutter会自动处理这种情况，这时Locale列表只会包含一项。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"localization-组件\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#localization-组件\"}},[t._v(\"#\")]),t._v(\" Localization 组件\")]),t._v(\" \"),e(\"p\",[t._v(\"Localizations组件用于加载和查找应用当前语言下的本地化值或资源。应用程序通过\"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/Localizations/of.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"Localizations.of(context,type)\")]),e(\"OutboundLink\")],1),t._v(\"来引用这些对象。 如果设备的Locale区域设置发生更改，则Localizations 组件会自动加载新区域的Locale值，然后重新build使用（依赖）了它们的组件，之所以会这样，是因为\"),e(\"code\",[t._v(\"Localizations\")]),t._v(\"内部使用了\"),e(\"a\",{attrs:{href:\"https://book.flutterchina.club/chapter7/inherited_widget.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"InheritedWidget\"),e(\"OutboundLink\")],1),t._v(\" ，我们在介绍该组件时讲过：当子组件的\"),e(\"code\",[t._v(\"build\")]),t._v(\"函数引用了\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"时，会创建对\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"的隐式依赖关系。因此，当\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"发生更改时，即\"),e(\"code\",[t._v(\"Localizations\")]),t._v(\"的Locale设置发生更改时，将重建所有依赖它的子组件。\")]),t._v(\" \"),e(\"p\",[t._v(\"本地化值由\"),e(\"code\",[t._v(\"Localizations\")]),t._v(\"的 \"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/LocalizationsDelegate-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"LocalizationsDelegates\"),e(\"OutboundLink\")],1),t._v(\" 列表加载 。 \"),e(\"strong\",[t._v(\"每个委托必须定义一个异步load() 方法\")]),t._v(\"，以生成封装了一系列本地化值的对象。通常这些对象为每个本地化值定义一个方法。\")]),t._v(\" \"),e(\"p\",[t._v(\"在大型应用程序中，不同模块或Package可能会与自己的本地化值捆绑在一起。 这就是为什么要用\"),e(\"code\",[t._v(\"Localizations\")]),t._v(\" 管理对象表的原因。 要使用由\"),e(\"code\",[t._v(\"LocalizationsDelegate\")]),t._v(\"的\"),e(\"code\",[t._v(\"load\")]),t._v(\"方法之一产生的对象，可以指定一个\"),e(\"code\",[t._v(\"BuildContext\")]),t._v(\"和对象的类型来找到它。例如，Material 组件库的本地化字符串由\"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/material/MaterialLocalizations-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"MaterialLocalizations\"),e(\"OutboundLink\")],1),t._v(\"类定义，此类的实例由\"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/material/MaterialApp-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"MaterialApp\"),e(\"OutboundLink\")],1),t._v(\"类提供的\"),e(\"code\",[t._v(\"LocalizationDelegate\")]),t._v(\"创建， 它们可以如下方式获取到：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[t._v(\"Localizations\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"of\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"MaterialLocalizations\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" MaterialLocalizations\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"这个特殊的\"),e(\"code\",[t._v(\"Localizations.of()\")]),t._v(\"表达式会经常使用，所以MaterialLocalizations类提供了一个便捷方法：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" MaterialLocalizations \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" Localizations\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"of\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"MaterialLocalizations\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" MaterialLocalizations\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 可以直接调用便捷方法\")]),t._v(\"\\ntooltip\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MaterialLocalizations\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"backButtonTooltip\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),e(\"h3\",{attrs:{id:\"使用打包好的localizationsdelegates\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#使用打包好的localizationsdelegates\"}},[t._v(\"#\")]),t._v(\" 使用打包好的LocalizationsDelegates\")]),t._v(\" \"),e(\"p\",[t._v(\"为了尽可能小而且简单，flutter软件包中仅提供美国英语值的\"),e(\"code\",[t._v(\"MaterialLocalizations\")]),t._v(\"和\"),e(\"code\",[t._v(\"WidgetsLocalizations\")]),t._v(\"接口的实现。 这些实现类分别称为\"),e(\"code\",[t._v(\"DefaultMaterialLocalizations\")]),t._v(\"和\"),e(\"code\",[t._v(\"DefaultWidgetsLocalizations\")]),t._v(\"。flutter_localizations 包包含\"),e(\"code\",[t._v(\"GlobalMaterialLocalizations\")]),t._v(\"和\"),e(\"code\",[t._v(\"GlobalWidgetsLocalizations\")]),t._v(\"的本地化接口的多语言实现， 国际化的应用程序必须按照本节开头说明的那样为这些类指定本地化Delegate。\")]),t._v(\" \"),e(\"p\",[t._v(\"上述的\"),e(\"code\",[t._v(\"GlobalMaterialLocalizations\")]),t._v(\"和\"),e(\"code\",[t._v(\"GlobalWidgetsLocalizations\")]),t._v(\"只是Material组件库的本地化实现，如果我们要让自己的布局支持多语言，那么就需要实现在即的\"),e(\"code\",[t._v(\"Localizations\")]),t._v(\"，我们将在下一节介绍其具体的实现方式。\")])])}),[],!1,null,null,null);a.default=n.exports}}]);"
  },
  {
    "path": "docs/assets/js/115.374ee72b.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[115],{558:function(t,a,s){t.exports=s.p+\"assets/img/14-4.4a6d698c.png\"},865:function(t,a,s){\"use strict\";s.r(a);var n=s(45),e=Object(n.a)({},(function(){var t=this,a=t.$createElement,n=t._self._c||a;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_14-5-图片加载原理与缓存\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_14-5-图片加载原理与缓存\"}},[t._v(\"#\")]),t._v(\" 14.5 图片加载原理与缓存\")]),t._v(\" \"),n(\"p\",[t._v(\"在本书前面章节已经介绍过\"),n(\"code\",[t._v(\"Image\")]),t._v(\" 组件，并提到Flutter框架对加载过的图片是有缓存的（内存），默认最大缓存数量是1000，最大缓存空间为100M。本节便详细介绍Image的原理及图片缓存机制，下面我们先看看\"),n(\"code\",[t._v(\"ImageProvider\")]),t._v(\" 类。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_14-5-1-imageprovider\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_14-5-1-imageprovider\"}},[t._v(\"#\")]),t._v(\" 14.5.1 ImageProvider\")]),t._v(\" \"),n(\"p\",[t._v(\"我们已经知道\"),n(\"code\",[t._v(\"Image\")]),t._v(\" 组件的\"),n(\"code\",[t._v(\"image\")]),t._v(\" 参数是一个必选参数，它是\"),n(\"code\",[t._v(\"ImageProvider\")]),t._v(\"类型。下面我们便详细介绍一下\"),n(\"code\",[t._v(\"ImageProvider\")]),t._v(\"，\"),n(\"code\",[t._v(\"ImageProvider\")]),t._v(\"是一个抽象类，定义了图片数据获取和加载的相关接口。它的主要职责有两个：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"提供图片数据源\")]),t._v(\" \"),n(\"li\",[t._v(\"缓存图片\")])]),t._v(\" \"),n(\"p\",[t._v(\"我们看看\"),n(\"code\",[t._v(\"ImageProvider\")]),t._v(\"抽象类的详细定义：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"abstract\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ImageProvider\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\n  ImageStream \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"resolve\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ImageConfiguration configuration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 实现代码省略\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"bool\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"evict\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" ImageCache cache\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      ImageConfiguration configuration \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" ImageConfiguration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"empty \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 实现代码省略\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"obtainKey\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ImageConfiguration configuration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@protected\")]),t._v(\"\\n  ImageStreamCompleter \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"load\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"T key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 需子类实现\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"h4\",{attrs:{id:\"load-t-key-方法\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#load-t-key-方法\"}},[t._v(\"#\")]),t._v(\" \"),n(\"code\",[t._v(\"load(T key)\")]),t._v(\"方法\")]),t._v(\" \"),n(\"p\",[t._v(\"加载图片数据源的接口，不同的数据源的加载方法不同，每个\"),n(\"code\",[t._v(\"ImageProvider\")]),t._v(\"的子类必须实现它。比如\"),n(\"code\",[t._v(\"NetworkImage\")]),t._v(\"类和\"),n(\"code\",[t._v(\"AssetImage\")]),t._v(\"类，它们都是\"),n(\"code\",[t._v(\"ImageProvider\")]),t._v(\"的子类，但它们需要从不同的数据源来加载图片数据：\"),n(\"code\",[t._v(\"NetworkImage\")]),t._v(\"是从网络来加载图片数据，而\"),n(\"code\",[t._v(\"AssetImage\")]),t._v(\"则是从最终的应用包里来加载（加载打到应用安装包里的资源图片）。 我们以\"),n(\"code\",[t._v(\"NetworkImage\")]),t._v(\"为例，看看其load方法的实现：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\nImageStreamCompleter \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"load\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"image_provider\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"NetworkImage key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" StreamController\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ImageChunkEvent\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" chunkEvents \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" StreamController\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ImageChunkEvent\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MultiFrameImageStreamCompleter\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    codec\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_loadAsync\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" chunkEvents\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//调用\")]),t._v(\"\\n    chunkEvents\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" chunkEvents\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"stream\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    scale\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"scale\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们看到，\"),n(\"code\",[t._v(\"load\")]),t._v(\"方法的返回值类型是\"),n(\"code\",[t._v(\"ImageStreamCompleter\")]),t._v(\" ，它是一个抽象类，定义了管理图片加载过程的一些接口，\"),n(\"code\",[t._v(\"Image\")]),t._v(\" Widget中正是通过它来监听图片加载状态的（我们将在下面介绍\"),n(\"code\",[t._v(\"Image\")]),t._v(\" 原理时详细介绍）。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"MultiFrameImageStreamCompleter\")]),t._v(\" 是 \"),n(\"code\",[t._v(\"ImageStreamCompleter\")]),t._v(\"的一个子类，是flutter sdk预置的类，通过该类，我们以方便、轻松地创建出一个\"),n(\"code\",[t._v(\"ImageStreamCompleter\")]),t._v(\"实例来做为\"),n(\"code\",[t._v(\"load\")]),t._v(\"方法的返回值。\")]),t._v(\" \"),n(\"p\",[t._v(\"我们可以看到，\"),n(\"code\",[t._v(\"MultiFrameImageStreamCompleter\")]),t._v(\" 需要一个\"),n(\"code\",[t._v(\"codec\")]),t._v(\"参数，该参数类型为\"),n(\"code\",[t._v(\"Future<ui.Codec>\")]),t._v(\"。\"),n(\"code\",[t._v(\"Codec\")]),t._v(\" 是处理图片编解码的类的一个handler，实际上，它只是一个flutter engine API 的包装类，也就是说图片的编解码逻辑不是在Dart 代码部分实现，而是在flutter engine中实现的。\"),n(\"code\",[t._v(\"Codec\")]),t._v(\"类部分定义如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@pragma\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'vm:entry-point'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Codec\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"NativeFieldWrapperClass2\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 此类由flutter engine创建，不应该手动实例化此类或直接继承此类。\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@pragma\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'vm:entry-point'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n  Codec\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// 图片中的帧数(动态图会有多帧)\")]),t._v(\"\\n  int \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" frameCount native \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Codec_frameCount'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// 动画重复的次数\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// * 0 表示只执行一次\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// * -1 表示循环执行\")]),t._v(\"\\n  int \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" repetitionCount native \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Codec_repetitionCount'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// 获取下一个动画帧\")]),t._v(\"\\n  Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"FrameInfo\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getNextFrame\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_futurize\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_getNextFrame\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  String \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_getNextFrame\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_Callback\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"FrameInfo\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" callback\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" native \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Codec_getNextFrame'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们可以看到\"),n(\"code\",[t._v(\"Codec\")]),t._v(\"最终的结果是一个或多个（动图）帧，而这些帧最终会绘制到屏幕上。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"MultiFrameImageStreamCompleter 的\")]),t._v(\" \"),n(\"code\",[t._v(\"codec\")]),t._v(\"参数值为\"),n(\"code\",[t._v(\"_loadAsync\")]),t._v(\"方法的返回值，我们继续看\"),n(\"code\",[t._v(\"_loadAsync\")]),t._v(\"方法的实现：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"\\n Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ui\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"Codec\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_loadAsync\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    NetworkImage key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    StreamController\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ImageChunkEvent\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" chunkEvents\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//下载图片\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Uri resolved \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Uri\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"base\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"resolve\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"url\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" HttpClientRequest request \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" _httpClient\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getUrl\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"resolved\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      headers\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"forEach\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String name\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" String value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        request\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"headers\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"name\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" HttpClientResponse response \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" request\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"close\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"response\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"statusCode \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" HttpStatus\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"ok\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"throw\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Exception\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 接收图片数据 \")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Uint8List bytes \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"consolidateHttpClientResponseBytes\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        response\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        onBytesReceived\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int cumulative\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int total\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          chunkEvents\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ImageChunkEvent\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            cumulativeBytesLoaded\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" cumulative\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            expectedTotalBytes\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" total\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"bytes\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"lengthInBytes \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"throw\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Exception\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'NetworkImage is an empty file: $resolved'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 对图片数据进行解码\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" PaintingBinding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"instance\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"instantiateImageCodec\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"bytes\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"finally\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      chunkEvents\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"close\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"可以看到\"),n(\"code\",[t._v(\"_loadAsync\")]),t._v(\"方法主要做了两件事：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"下载图片。\")]),t._v(\" \"),n(\"li\",[t._v(\"对下载的图片数据进行解码。\")])]),t._v(\" \"),n(\"p\",[t._v(\"下载逻辑比较简单：通过\"),n(\"code\",[t._v(\"HttpClient\")]),t._v(\"从网上下载图片，另外下载请求会设置一些自定义的header，开发者可以通过\"),n(\"code\",[t._v(\"NetworkImage\")]),t._v(\"的\"),n(\"code\",[t._v(\"headers\")]),t._v(\"命名参数来传递。\")]),t._v(\" \"),n(\"p\",[t._v(\"在图片下载完成后调用了\"),n(\"code\",[t._v(\"PaintingBinding.instance.instantiateImageCodec(bytes)\")]),t._v(\"对图片进行解码，值得注意的是\"),n(\"code\",[t._v(\"instantiateImageCodec(...)\")]),t._v(\"也是一个Native API的包装，实际上会调用Flutter engine的\"),n(\"code\",[t._v(\"instantiateImageCodec\")]),t._v(\"方法，源码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"String \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_instantiateImageCodec\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Uint8List list\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" _Callback\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Codec\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" callback\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" _ImageInfo imageInfo\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int targetWidth\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int targetHeight\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n  native \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'instantiateImageCodec'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"h4\",{attrs:{id:\"obtainkey-imageconfiguration-方法\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#obtainkey-imageconfiguration-方法\"}},[t._v(\"#\")]),t._v(\" \"),n(\"code\",[t._v(\"obtainKey(ImageConfiguration)\")]),t._v(\"方法\")]),t._v(\" \"),n(\"p\",[t._v(\"该接口主要是为了配合实现图片缓存，\"),n(\"code\",[t._v(\"ImageProvider\")]),t._v(\"从数据源加载完数据后，会在全局的\"),n(\"code\",[t._v(\"ImageCache\")]),t._v(\"中缓存图片数据，而图片数据缓存是一个Map，而Map的key便是调用此方法的返回值，不同的key代表不同的图片数据缓存。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"resolve-imageconfiguration-方法\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#resolve-imageconfiguration-方法\"}},[t._v(\"#\")]),t._v(\" \"),n(\"code\",[t._v(\"resolve(ImageConfiguration)\")]),t._v(\" 方法\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"resolve\")]),t._v(\"方法是\"),n(\"code\",[t._v(\"ImageProvider\")]),t._v(\"的暴露的给\"),n(\"code\",[t._v(\"Image\")]),t._v(\"的主入口方法，它接受一个\"),n(\"code\",[t._v(\"ImageConfiguration\")]),t._v(\"参数，返回\"),n(\"code\",[t._v(\"ImageStream\")]),t._v(\"，即图片数据流。我们重点看一下\"),n(\"code\",[t._v(\"resolve\")]),t._v(\"执行流程：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"ImageStream \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"resolve\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ImageConfiguration configuration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" ImageStream stream \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ImageStream\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  T obtainedKey\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//定义错误处理函数\")]),t._v(\"\\n  Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"handleError\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),t._v(\" exception\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" StackTrace stack\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n    stream\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setCompleter\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"imageCompleter\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    imageCompleter\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setError\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 创建一个新Zone，主要是为了当发生错误时不会干扰MainZone\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Zone dangerZone \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Zone\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"current\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fork\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \\n  dangerZone\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"runGuarded\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 先验证是否已经有缓存\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 生成缓存key，后面会根据此key来检测是否有缓存\")]),t._v(\"\\n      key \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"obtainKey\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"configuration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"catch\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"error\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" stackTrace\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"handleError\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"error\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" stackTrace\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"then\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"T key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      obtainedKey \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 缓存的处理逻辑在这里，记为A，下面详细介绍\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" ImageStreamCompleter completer \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" PaintingBinding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"instance\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"imageCache\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"putIfAbsent\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"load\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" onError\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" handleError\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"completer \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        stream\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setCompleter\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"completer\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"catchError\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"handleError\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" stream\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"ImageConfiguration\")]),t._v(\"  包含图片和设备的相关信息，如图片的大小、所在的\"),n(\"code\",[t._v(\"AssetBundle\")]),t._v(\"(只有打到安装包的图片存在)以及当前的设备平台、devicePixelRatio（设备像素比等）。Flutter SDK提供了一个便捷函数\"),n(\"code\",[t._v(\"createLocalImageConfiguration\")]),t._v(\"来创建\"),n(\"code\",[t._v(\"ImageConfiguration\")]),t._v(\"  对象：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"ImageConfiguration \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createLocalImageConfiguration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" Size size \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ImageConfiguration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    bundle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" DefaultAssetBundle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    devicePixelRatio\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MediaQuery\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" nullOk\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"devicePixelRatio \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    locale\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Localizations\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"localeOf\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" nullOk\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    textDirection\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Directionality\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    platform\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" defaultTargetPlatform\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们可以发现这些信息基本都是通过\"),n(\"code\",[t._v(\"Context\")]),t._v(\"来获取。\")]),t._v(\" \"),n(\"p\",[t._v(\"上面代码A处就是处理缓存的主要代码，这里的\"),n(\"code\",[t._v(\"PaintingBinding.instance.imageCache\")]),t._v(\" 是 \"),n(\"code\",[t._v(\"ImageCache\")]),t._v(\"的一个实例，它是\"),n(\"code\",[t._v(\"PaintingBinding\")]),t._v(\"的一个属性，而Flutter框架中的\"),n(\"code\",[t._v(\"PaintingBinding.instance\")]),t._v(\"是一个单例，\"),n(\"code\",[t._v(\"imageCache\")]),t._v(\"事实上也是一个单例，也就是说图片缓存是全局的，统一由\"),n(\"code\",[t._v(\"PaintingBinding.instance.imageCache\")]),t._v(\" 来管理。\")]),t._v(\" \"),n(\"p\",[t._v(\"下面我们看看\"),n(\"code\",[t._v(\"ImageCache\")]),t._v(\"类定义：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" int _kDefaultSize \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1000\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" int _kDefaultSizeBytes \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<<\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 100 MiB\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ImageCache\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 正在加载中的图片队列\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Map\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Object\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" _PendingImage\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _pendingImages \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Object\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" _PendingImage\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 缓存队列\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Map\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Object\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" _CachedImage\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _cache \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Object\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" _CachedImage\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 缓存数量上限(1000)\")]),t._v(\"\\n  int _maximumSize \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _kDefaultSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 缓存容量上限 (100 MB)\")]),t._v(\"\\n  int _maximumSizeBytes \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _kDefaultSizeBytes\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 缓存上限设置的setter\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"set\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"maximumSize\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"set\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"maximumSizeBytes\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 省略部分定义\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 清除所有缓存\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"clear\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// ...省略具体实现代码\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 清除指定key对应的图片缓存\")]),t._v(\"\\n  bool \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"evict\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Object key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// ...省略具体实现代码\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n \\n  ImageStreamCompleter \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"putIfAbsent\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Object key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" ImageStreamCompleter \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"loader\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" ImageErrorListener onError \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"loader \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    ImageStreamCompleter result \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _pendingImages\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"completer\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 图片还未加载成功，直接返回\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"result \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" result\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n \\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 有缓存，继续往下走\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 先移除缓存，后再添加，可以让最新使用过的缓存在_map中的位置更近一些，清理时会LRU来清除\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" _CachedImage image \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _cache\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"remove\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"image \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _cache\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"completer\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      result \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"loader\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"catch\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"error\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" stackTrace\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"onError \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onError\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"error\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" stackTrace\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"rethrow\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"listener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ImageInfo info\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" bool syncCall\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" int imageSize \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" info\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"image \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" info\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"height \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" info\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"width \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"4\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" _CachedImage image \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_CachedImage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"result\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" imageSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 下面是缓存处理的逻辑\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"maximumSizeBytes \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\" imageSize \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" maximumSizeBytes\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        _maximumSizeBytes \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" imageSize \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1000\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n      _currentSizeBytes \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+=\")]),t._v(\" imageSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" _PendingImage pendingImage \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _pendingImages\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"remove\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"pendingImage \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        pendingImage\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"removeListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n      _cache\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_checkCacheSize\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"maximumSize \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\" maximumSizeBytes \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" ImageStreamListener streamListener \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ImageStreamListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"listener\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      _pendingImages\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_PendingImage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"result\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" streamListener\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// Listener is removed in [_PendingImage.removeListener].\")]),t._v(\"\\n      result\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"streamListener\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" result\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 当缓存数量超过最大值或缓存的大小超过最大缓存容量，会调用此方法清理到缓存上限以内\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_checkCacheSize\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"while\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_currentSizeBytes \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _maximumSizeBytes \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"||\")]),t._v(\" _cache\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"length \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _maximumSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Object key \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _cache\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"keys\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"first\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" _CachedImage image \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _cache\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      _currentSizeBytes \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-=\")]),t._v(\" image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"sizeBytes\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      _cache\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"remove\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"有缓存则使用缓存，没有缓存则调用load方法加载图片，加载成功后:\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"先判断图片数据有没有缓存，如果有，则直接返回\"),n(\"code\",[t._v(\"ImageStream\")]),t._v(\"。\")]),t._v(\" \"),n(\"li\",[t._v(\"如果没有缓存，则调用\"),n(\"code\",[t._v(\"load(T key)\")]),t._v(\"方法从数据源加载图片数据，加载成功后先缓存，然后返回ImageStream。\")])]),t._v(\" \"),n(\"p\",[t._v(\"另外，我们可以看到\"),n(\"code\",[t._v(\"ImageCache\")]),t._v(\"类中有设置缓存上限的setter，所以，如果我们可以自定义缓存上限：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\" PaintingBinding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"instance\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"imageCache\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"maximumSize\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2000\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//最多2000张\")]),t._v(\"\\n PaintingBinding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"instance\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"imageCache\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"maximumSizeBytes \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<<\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//最大200M\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"现在我们看一下缓存的key，因为Map中相同key的值会被覆盖，也就是说key是图片缓存的一个唯一标识，只要是不同key，那么图片数据就会分别缓存（即使事实上是同一张图片）。那么图片的唯一标识是什么呢？跟踪源码，很容易发现key正是\"),n(\"code\",[t._v(\"ImageProvider.obtainKey()\")]),t._v(\"方法的返回值，而此方法需要\"),n(\"code\",[t._v(\"ImageProvider\")]),t._v(\"子类去重写，这也就意味着不同的\"),n(\"code\",[t._v(\"ImageProvider\")]),t._v(\"对key的定义逻辑会不同。其实也很好理解，比如对于\"),n(\"code\",[t._v(\"NetworkImage\")]),t._v(\"，将图片的url作为key会很合适，而对于\"),n(\"code\",[t._v(\"AssetImage\")]),t._v(\"，则应该将“包名+路径”作为唯一的key。下面我们以\"),n(\"code\",[t._v(\"NetworkImage\")]),t._v(\"为例，看一下它的\"),n(\"code\",[t._v(\"obtainKey()\")]),t._v(\"实现：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\nFuture\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"NetworkImage\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"obtainKey\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"image_provider\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"ImageConfiguration configuration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" SynchronousFuture\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"NetworkImage\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"代码很简单，创建了一个同步的future，然后直接将自身做为key返回。因为Map中在判断key（此时是\"),n(\"code\",[t._v(\"NetworkImage\")]),t._v(\"对象）是否相等时会使用“==”运算符，那么定义key的逻辑就是\"),n(\"code\",[t._v(\"NetworkImage\")]),t._v(\"的“==”运算符：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\nbool \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"operator\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),t._v(\" other\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" NetworkImage typedOther \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" other\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" url \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" typedOther\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"url\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\" scale \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" typedOther\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"scale\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"很清晰，对于网络图片来说，会将其“url+缩放比例”作为缓存的key。也就是说\"),n(\"strong\",[t._v(\"如果两张图片的url或scale只要有一个不同，便会重新下载并分别缓存\")]),t._v(\"。\")]),t._v(\" \"),n(\"p\",[t._v(\"另外，我们需要注意的是，图片缓存是在内存中，并没有进行本地文件持久化存储，这也是为什么网络图片在应用重启后需要重新联网下载的原因。\")]),t._v(\" \"),n(\"p\",[t._v(\"同时也意味着在应用生命周期内，如果缓存没有超过上限，相同的图片只会被下载一次。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"总结\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),n(\"p\",[t._v(\"上面主要结合源码，探索了\"),n(\"code\",[t._v(\"ImageProvider\")]),t._v(\"的主要功能和原理，如果要用一句话来总结\"),n(\"code\",[t._v(\"ImageProvider\")]),t._v(\"功能，那么应该是：加载图片数据并进行缓存、解码。在此再次提醒读者，Flutter的源码是非常好的第一手资料，建议读者多多探索，另外，在阅读源码学习的同时一定要有总结，这样才不至于在源码中迷失。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_14-5-2-image组件原理\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_14-5-2-image组件原理\"}},[t._v(\"#\")]),t._v(\" 14.5.2 Image组件原理\")]),t._v(\" \"),n(\"p\",[t._v(\"前面章节中我们介绍过\"),n(\"code\",[t._v(\"Image\")]),t._v(\"的基础用法，现在我们更深入一些，研究一下\"),n(\"code\",[t._v(\"Image\")]),t._v(\"是如何和\"),n(\"code\",[t._v(\"ImageProvider\")]),t._v(\"配合来获取最终解码后的数据，然后又如何将图片绘制到屏幕上的。\")]),t._v(\" \"),n(\"p\",[t._v(\"本节换一个思路，我们先不去直接看\"),n(\"code\",[t._v(\"Image\")]),t._v(\"的源码，而根据已经掌握的知识来实现一个简版的“\"),n(\"code\",[t._v(\"Image\")]),t._v(\"组件” \"),n(\"code\",[t._v(\"MyImage\")]),t._v(\"，代码大致如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyImage\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyImage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"imageProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"imageProvider \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" ImageProvider imageProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _MyImageState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_MyImageState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_MyImageState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"MyImage\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  ImageStream _imageStream\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  ImageInfo _imageInfo\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didChangeDependencies\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didChangeDependencies\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 依赖改变时，图片的配置信息可能会发生改变\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_getImage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didUpdateWidget\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"MyImage oldWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didUpdateWidget\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"oldWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"imageProvider \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" oldWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"imageProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_getImage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_getImage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" ImageStream oldImageStream \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _imageStream\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 调用imageProvider.resolve方法，获得ImageStream。\")]),t._v(\"\\n    _imageStream \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"\\n        widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"imageProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"resolve\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createLocalImageConfiguration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//判断新旧ImageStream是否相同，如果不同，则需要调整流的监听器\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_imageStream\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"key \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" oldImageStream\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" ImageStreamListener listener \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ImageStreamListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_updateImage\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      oldImageStream\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"removeListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"listener\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      _imageStream\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"listener\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_updateImage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ImageInfo imageInfo\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" bool synchronousCall\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// Trigger a build whenever the image changes.\")]),t._v(\"\\n      _imageInfo \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" imageInfo\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    _imageStream\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"removeListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ImageStreamListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_updateImage\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RawImage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _imageInfo\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// this is a dart:ui Image object\")]),t._v(\"\\n      scale\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _imageInfo\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"scale \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上面代码流程如下：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"通过\"),n(\"code\",[t._v(\"imageProvider.resolve\")]),t._v(\"方法可以得到一个\"),n(\"code\",[t._v(\"ImageStream\")]),t._v(\"（图片数据流），然后监听\"),n(\"code\",[t._v(\"ImageStream\")]),t._v(\"的变化。当图片数据源发生变化时，\"),n(\"code\",[t._v(\"ImageStream\")]),t._v(\"会触发相应的事件，而本例中我们只设置了图片成功的监听器\"),n(\"code\",[t._v(\"_updateImage\")]),t._v(\"，而\"),n(\"code\",[t._v(\"_updateImage\")]),t._v(\"中只更新了\"),n(\"code\",[t._v(\"_imageInfo\")]),t._v(\"。值得注意的是，如果是静态图，\"),n(\"code\",[t._v(\"ImageStream\")]),t._v(\"只会触发一次时间，如果是动态图，则会触发多次事件，每一次都会有一个解码后的图片帧。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"_imageInfo\")]),t._v(\" 更新后会rebuild，此时会创建一个\"),n(\"code\",[t._v(\"RawImage\")]),t._v(\" Widget。\"),n(\"code\",[t._v(\"RawImage\")]),t._v(\"最终会通过\"),n(\"code\",[t._v(\"RenderImage\")]),t._v(\"来将图片绘制在屏幕上。如果继续跟进\"),n(\"code\",[t._v(\"RenderImage\")]),t._v(\"类，我们会发现\"),n(\"code\",[t._v(\"RenderImage\")]),t._v(\"的\"),n(\"code\",[t._v(\"paint\")]),t._v(\" 方法中调用了\"),n(\"code\",[t._v(\"paintImage\")]),t._v(\"方法，而\"),n(\"code\",[t._v(\"paintImage\")]),t._v(\"方法中通过\"),n(\"code\",[t._v(\"Canvas\")]),t._v(\"的\"),n(\"code\",[t._v(\"drawImageRect(…)\")]),t._v(\"、\"),n(\"code\",[t._v(\"drawImageNine(...)\")]),t._v(\"等方法来完成最终的绘制。\")]),t._v(\" \"),n(\"li\",[t._v(\"最终的绘制由\"),n(\"code\",[t._v(\"RawImage\")]),t._v(\"来完成。\")])]),t._v(\" \"),n(\"p\",[t._v(\"下面测试一下\"),n(\"code\",[t._v(\"MyImage\")]),t._v(\"：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ImageInternalTestRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyImage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          imageProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"NetworkImage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"https://avatars2.githubusercontent.com/u/20411648?s=460&v=4\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图14-4所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(558),alt:\"图14-4\"}})]),t._v(\" \"),n(\"p\",[t._v(\"成功了！ 现在，想必\"),n(\"code\",[t._v(\"Image\")]),t._v(\" Widget的源码已经没必要在花费篇章去介绍了，读者有兴趣可以自行去阅读。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"总结-2\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结-2\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),n(\"p\",[t._v(\"本节主要介绍了Flutter 图片的加载、缓存和绘制流程。其中\"),n(\"code\",[t._v(\"ImageProvider\")]),t._v(\"主要负责图片数据的加载和缓存，而绘制部分逻辑主要是由\"),n(\"code\",[t._v(\"RawImage\")]),t._v(\"来完成。 而\"),n(\"code\",[t._v(\"Image\")]),t._v(\"正是连接起\"),n(\"code\",[t._v(\"ImageProvider\")]),t._v(\"和\"),n(\"code\",[t._v(\"RawImage\")]),t._v(\" 的桥梁。\")])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/116.5453b788.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[116],{559:function(t,a){t.exports=\"data:image/png;base64,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\"},867:function(t,a,s){\"use strict\";s.r(a);var n=s(45),e=Object(n.a)({},(function(){var t=this,a=t.$createElement,n=t._self._c||a;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_14-3-renderobject和renderbox\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_14-3-renderobject和renderbox\"}},[t._v(\"#\")]),t._v(\" 14.3 RenderObject和RenderBox\")]),t._v(\" \"),n(\"p\",[t._v(\"在上一节我们说过每个\"),n(\"code\",[t._v(\"Element\")]),t._v(\"都对应一个\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"，我们可以通过\"),n(\"code\",[t._v(\"Element.renderObject\")]),t._v(\" 来获取。并且我们也说过\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"的主要职责是Layout和绘制，所有的\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"会组成一棵渲染树Render Tree。本节我们将重点介绍一下\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"的作用。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"RenderObject\")]),t._v(\"就是渲染树中的一个对象，它拥有一个\"),n(\"code\",[t._v(\"parent\")]),t._v(\"和一个\"),n(\"code\",[t._v(\"parentData\")]),t._v(\" 插槽（slot），所谓插槽，就是指预留的一个接口或位置，这个接口和位置是由其它对象来接入或占据的，这个接口或位置在软件中通常用预留变量来表示，而\"),n(\"code\",[t._v(\"parentData\")]),t._v(\"正是一个预留变量，它正是由\"),n(\"code\",[t._v(\"parent\")]),t._v(\" 来赋值的，\"),n(\"code\",[t._v(\"parent\")]),t._v(\"通常会通过子\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"的\"),n(\"code\",[t._v(\"parentData\")]),t._v(\"存储一些和子元素相关的数据，如在Stack布局中，\"),n(\"code\",[t._v(\"RenderStack\")]),t._v(\"就会将子元素的偏移数据存储在子元素的\"),n(\"code\",[t._v(\"parentData\")]),t._v(\"中（具体可以查看\"),n(\"code\",[t._v(\"Positioned\")]),t._v(\"实现）。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"RenderObject\")]),t._v(\"类本身实现了一套基础的layout和绘制协议，但是并没有定义子节点模型（如一个节点可以有几个子节点，没有子节点？一个？两个？或者更多？）。 它也没有定义坐标系统（如子节点定位是在笛卡尔坐标中还是极坐标？）和具体的布局协议（是通过宽高还是通过constraint和size?，或者是否由父节点在子节点布局之前或之后设置子节点的大小和位置等）。为此，Flutter提供了一个\"),n(\"code\",[t._v(\"RenderBox\")]),t._v(\"类，它继承自``RenderObject\"),n(\"code\",[t._v(\"，布局坐标系统采用笛卡尔坐标系，这和Android和iOS原生坐标系是一致的，都是屏幕的top、left是原点，然后分宽高两个轴，大多数情况下，我们直接使用\")]),t._v(\"RenderBox\"),n(\"code\",[t._v(\"就可以了，除非遇到要自定义布局模型或坐标系统的情况，下面我们重点介绍一下\")]),t._v(\"RenderBox`。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_14-3-1-布局过程\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_14-3-1-布局过程\"}},[t._v(\"#\")]),t._v(\" 14.3.1 布局过程\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"constraints\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#constraints\"}},[t._v(\"#\")]),t._v(\" Constraints\")]),t._v(\" \"),n(\"p\",[t._v(\"在\"),n(\"code\",[t._v(\"RenderBox\")]),t._v(\" 中，有个\"),n(\"code\",[t._v(\"size\")]),t._v(\"属性用来保存控件的宽和高。\"),n(\"code\",[t._v(\"RenderBox\")]),t._v(\"的layout是通过在组件树中从上往下传递\"),n(\"code\",[t._v(\"BoxConstraints\")]),t._v(\"对象的实现的。\"),n(\"code\",[t._v(\"BoxConstraints\")]),t._v(\"对象可以限制子节点的最大和最小宽高，子节点必须遵守父节点给定的限制条件。\")]),t._v(\" \"),n(\"p\",[t._v(\"在布局阶段，父节点会调用子节点的\"),n(\"code\",[t._v(\"layout()\")]),t._v(\"方法，下面我们看看\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"中\"),n(\"code\",[t._v(\"layout()\")]),t._v(\"方法的大致实现（删掉了一些无关代码和异常捕获）:\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"layout\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Constraints constraints\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" bool parentUsesSize \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n   RenderObject relayoutBoundary\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"parentUsesSize \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"||\")]),t._v(\" sizedByParent \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"||\")]),t._v(\" constraints\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isTight \\n    \\t\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"||\")]),t._v(\" parent \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"is!\")]),t._v(\" RenderObject\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      relayoutBoundary \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" RenderObject parent \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"parent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      relayoutBoundary \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" parent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"_relayoutBoundary\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"sizedByParent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"performResize\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"performLayout\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"可以看到\"),n(\"code\",[t._v(\"layout\")]),t._v(\"方法需要传入两个参数，第一个为\"),n(\"code\",[t._v(\"constraints\")]),t._v(\"，即 父节点对子节点大小的限制，该值根据父节点的布局逻辑确定。另外一个参数是 \"),n(\"code\",[t._v(\"parentUsesSize\")]),t._v(\"，该值用于确定 \"),n(\"code\",[t._v(\"relayoutBoundary\")]),t._v(\"，该参数表示子节点布局变化是否影响父节点，如果为\"),n(\"code\",[t._v(\"true\")]),t._v(\"，当子节点布局发生变化时父节点都会标记为需要重新布局，如果为\"),n(\"code\",[t._v(\"false\")]),t._v(\"，则子节点布局发生变化后不会影响父节点。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"relayoutboundary\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#relayoutboundary\"}},[t._v(\"#\")]),t._v(\" relayoutBoundary\")]),t._v(\" \"),n(\"p\",[t._v(\"上面\"),n(\"code\",[t._v(\"layout()\")]),t._v(\"源码中定义了一个\"),n(\"code\",[t._v(\"relayoutBoundary\")]),t._v(\"变量，什么是 \"),n(\"code\",[t._v(\"relayoutBoundary\")]),t._v(\"？在前面介绍\"),n(\"code\",[t._v(\"Element\")]),t._v(\"时，我们讲过当一个\"),n(\"code\",[t._v(\"Element\")]),t._v(\"标记为 dirty 时便会重新build，这时\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"便会重新布局，我们是通过调用 \"),n(\"code\",[t._v(\"markNeedsBuild()\")]),t._v(\" 来标记\"),n(\"code\",[t._v(\"Element\")]),t._v(\"为dirty的。在\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"中有一个类似的\"),n(\"code\",[t._v(\"markNeedsLayout()\")]),t._v(\"方法，它会将\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"的布局状态标记为 dirty，这样在下一个frame中便会重新layout，我们看看\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"的\"),n(\"code\",[t._v(\"markNeedsLayout()\")]),t._v(\"的部分源码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"markNeedsLayout\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_relayoutBoundary \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_relayoutBoundary \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"markParentNeedsLayout\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    _needsLayout \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"owner \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n      owner\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"_nodesNeedingLayout\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      owner\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"requestVisualUpdate\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"代码大致逻辑是先判断自身是不是\"),n(\"code\",[t._v(\"relayoutBoundary\")]),t._v(\"，如果不是就继续向parent 查找，一直向上查找到是 \"),n(\"code\",[t._v(\"relayoutBoundary\")]),t._v(\" 的 \"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"为止，然后再将其标记为 dirty 的。这样来看它的作用就比较明显了，意思就是当一个控件的大小被改变时可能会影响到它的 parent，因此 parent 也需要被重新布局，那么到什么时候是个头呢？答案就是 \"),n(\"code\",[t._v(\"relayoutBoundary\")]),t._v(\"，如果一个 \"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\" 是 \"),n(\"code\",[t._v(\"relayoutBoundary\")]),t._v(\"，就表示它的大小变化不会再影响到 parent 的大小了，于是 parent 也就不用重新布局了。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"performresize-和-performlayout\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#performresize-和-performlayout\"}},[t._v(\"#\")]),t._v(\" performResize 和 performLayout\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"RenderBox\")]),t._v(\"实际的测量和布局逻辑是在\"),n(\"code\",[t._v(\"performResize()\")]),t._v(\" 和 \"),n(\"code\",[t._v(\"performLayout()\")]),t._v(\"两个方法中，RenderBox子类需要实现这两个方法来定制自身的布局逻辑。根据\"),n(\"code\",[t._v(\"layout()\")]),t._v(\" 源码可以看出只有 \"),n(\"code\",[t._v(\"sizedByParent\")]),t._v(\" 为 \"),n(\"code\",[t._v(\"true\")]),t._v(\" 时，\"),n(\"code\",[t._v(\"performResize()\")]),t._v(\" 才会被调用，而 \"),n(\"code\",[t._v(\"performLayout()\")]),t._v(\" 是每次布局都会被调用的。\"),n(\"code\",[t._v(\"sizedByParent\")]),t._v(\" 意为该节点的大小是否仅通过 parent 传给它的 constraints 就可以确定了，即该节点的大小与它自身的属性和其子节点无关，比如如果一个控件永远充满 parent 的大小，那么 \"),n(\"code\",[t._v(\"sizedByParent\")]),t._v(\"就应该返回\"),n(\"code\",[t._v(\"true\")]),t._v(\"，此时其大小在 \"),n(\"code\",[t._v(\"performResize()\")]),t._v(\" 中就确定了，在后面的 \"),n(\"code\",[t._v(\"performLayout()\")]),t._v(\" 方法中将不会再被修改了，这种情况下 \"),n(\"code\",[t._v(\"performLayout()\")]),t._v(\" 只负责布局子节点。\")]),t._v(\" \"),n(\"p\",[t._v(\"在 \"),n(\"code\",[t._v(\"performLayout()\")]),t._v(\" 方法中除了完成自身布局，也必须完成子节点的布局，这是因为只有父子节点全部完成后布局流程才算真正完成。所以最终的调用栈将会变成：\"),n(\"em\",[t._v(\"layout() > performResize()/performLayout() > child.layout() > ...\")]),t._v(\"  ，如此递归完成整个UI的布局。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"RenderBox\")]),t._v(\"子类要定制布局算法不应该重写\"),n(\"code\",[t._v(\"layout()\")]),t._v(\"方法，因为对于任何RenderBox的子类来说，它的layout流程基本是相同的，不同之处只在具体的布局算法，而具体的布局算法子类应该通过重写\"),n(\"code\",[t._v(\"performResize()\")]),t._v(\" 和 \"),n(\"code\",[t._v(\"performLayout()\")]),t._v(\"两个方法来实现，他们会在\"),n(\"code\",[t._v(\"layout()\")]),t._v(\"中被调用。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"parentdata\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#parentdata\"}},[t._v(\"#\")]),t._v(\" ParentData\")]),t._v(\" \"),n(\"p\",[t._v(\"当layout结束后，每个节点的位置（相对于父节点的偏移）就已经确定了，\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"就可以根据位置信息来进行最终的绘制。但是在layout过程中，节点的位置信息怎么保存？对于大多数\"),n(\"code\",[t._v(\"RenderBox\")]),t._v(\"子类来说如果子类只有一个子节点，那么子节点偏移一般都是\"),n(\"code\",[t._v(\"Offset.zero\")]),t._v(\" ，如果有多个子节点，则每个子节点的偏移就可能不同。而子节点在父节点的偏移数据正是通过\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"的\"),n(\"code\",[t._v(\"parentData\")]),t._v(\"属性来保存的。在\"),n(\"code\",[t._v(\"RenderBox\")]),t._v(\"中，其\"),n(\"code\",[t._v(\"parentData\")]),t._v(\"属性默认是一个\"),n(\"code\",[t._v(\"BoxParentData\")]),t._v(\"对象，该属性只能通过父节点的\"),n(\"code\",[t._v(\"setupParentData()\")]),t._v(\"方法来设置：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"abstract\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"RenderBox\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"RenderObject\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setupParentData\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"covariant\")]),t._v(\" RenderObject child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"parentData \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"is!\")]),t._v(\" BoxParentData\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"parentData \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxParentData\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"BoxParentData\")]),t._v(\"定义如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// Parentdata 会被RenderBox和它的子类使用.\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"BoxParentData\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ParentData\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// offset表示在子节点在父节点坐标系中的绘制偏移  \")]),t._v(\"\\n  Offset offset \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"zero\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  String \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'offset=$offset'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"blockquote\",[n(\"p\",[t._v(\"一定要注意，\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"的\"),n(\"code\",[t._v(\"parentData\")]),t._v(\" 只能通过父元素设置.\")])]),t._v(\" \"),n(\"p\",[t._v(\"当然，\"),n(\"code\",[t._v(\"ParentData\")]),t._v(\"并不仅仅可以用来存储偏移信息，通常所有和子节点特定的数据都可以存储到子节点的\"),n(\"code\",[t._v(\"ParentData\")]),t._v(\"中，如\"),n(\"code\",[t._v(\"ContainerBox\")]),t._v(\"的\"),n(\"code\",[t._v(\"ParentData\")]),t._v(\"就保存了指向兄弟节点的\"),n(\"code\",[t._v(\"previousSibling\")]),t._v(\"和\"),n(\"code\",[t._v(\"nextSibling\")]),t._v(\"，\"),n(\"code\",[t._v(\"Element.visitChildren()\")]),t._v(\"方法也正是通过它们来实现对子节点的遍历。再比如\"),n(\"code\",[t._v(\"KeepAlive\")]),t._v(\" 组件，它使用\"),n(\"code\",[t._v(\"KeepAliveParentDataMixin\")]),t._v(\"（继承自\"),n(\"code\",[t._v(\"ParentData\")]),t._v(\"） 来保存子节的\"),n(\"code\",[t._v(\"keepAlive\")]),t._v(\"状态。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_14-3-2-绘制过程\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_14-3-2-绘制过程\"}},[t._v(\"#\")]),t._v(\" 14.3.2 绘制过程\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"RenderObject\")]),t._v(\"可以通过\"),n(\"code\",[t._v(\"paint()\")]),t._v(\"方法来完成具体绘制逻辑，流程和布局流程相似，子类可以实现\"),n(\"code\",[t._v(\"paint()\")]),t._v(\"方法来完成自身的绘制逻辑，\"),n(\"code\",[t._v(\"paint()\")]),t._v(\"签名如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"paint\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"PaintingContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Offset offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"通过\"),n(\"code\",[t._v(\"context.canvas\")]),t._v(\"可以取到\"),n(\"code\",[t._v(\"Canvas\")]),t._v(\"对象，接下来就可以调用\"),n(\"code\",[t._v(\"Canvas\")]),t._v(\" API来实现具体的绘制逻辑。\")]),t._v(\" \"),n(\"p\",[t._v(\"如果节点有子节点，它除了完成自身绘制逻辑之外，还要调用子节点的绘制方法。我们以\"),n(\"code\",[t._v(\"RenderFlex\")]),t._v(\"对象为例说明：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"paint\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"PaintingContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Offset offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 如果子元素未超出当前边界，则绘制子元素  \")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_overflow \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"defaultPaint\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 如果size为空，则无需绘制\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isEmpty\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 剪裁掉溢出边界的部分\")]),t._v(\"\\n  context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pushClipRect\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"needsCompositing\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"zero \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&\")]),t._v(\" size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" defaultPaint\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String debugOverflowHints \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'...'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//溢出提示内容，省略\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 绘制溢出部分的错误提示样式\")]),t._v(\"\\n    Rect overflowChildRect\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"switch\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_direction\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" Axis\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"horizontal\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n        overflowChildRect \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Rect\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fromLTWH\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"width \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" _overflow\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"break\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" Axis\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"vertical\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n        overflowChildRect \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Rect\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fromLTWH\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"height \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" _overflow\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"break\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"  \\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"paintOverflowIndicator\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"zero \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&\")]),t._v(\" size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                           overflowChildRect\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" overflowHints\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" debugOverflowHints\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"代码很简单，首先判断有无溢出，如果没有则调用\"),n(\"code\",[t._v(\"defaultPaint(context, offset)\")]),t._v(\"来完成绘制，该方法源码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"defaultPaint\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"PaintingContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Offset offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  ChildType child \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" firstChild\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"while\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" ParentDataType childParentData \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"parentData\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//绘制子节点， \")]),t._v(\"\\n    context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"paintChild\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" childParentData\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"offset \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    child \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" childParentData\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"nextSibling\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"很明显，由于Flex本身没有需要绘制的东西，所以直接遍历其子节点，然后调用\"),n(\"code\",[t._v(\"paintChild()\")]),t._v(\"来绘制子节点，同时将子节点\"),n(\"code\",[t._v(\"ParentData\")]),t._v(\"中在layout阶段保存的offset加上自身偏移作为第二个参数传递给\"),n(\"code\",[t._v(\"paintChild()\")]),t._v(\"。而如果子节点还有子节点时，\"),n(\"code\",[t._v(\"paintChild()\")]),t._v(\"方法还会调用子节点的\"),n(\"code\",[t._v(\"paint()\")]),t._v(\"方法，如此递归完成整个节点树的绘制，最终调用栈为： \"),n(\"em\",[t._v(\"paint() > paintChild() > paint() ...\")]),t._v(\" 。\")]),t._v(\" \"),n(\"p\",[t._v(\"当需要绘制的内容大小溢出当前空间时，将会执行\"),n(\"code\",[t._v(\"paintOverflowIndicator()\")]),t._v(\" 来绘制溢出部分提示，这个就是我们经常看到的溢出提示，如图14-3所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(559),alt:\"overflow\"}})]),t._v(\" \"),n(\"h3\",{attrs:{id:\"repaintboundary\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#repaintboundary\"}},[t._v(\"#\")]),t._v(\" RepaintBoundary\")]),t._v(\" \"),n(\"p\",[t._v(\"我们已经在\"),n(\"code\",[t._v(\"CustomPaint\")]),t._v(\"一节中介绍过\"),n(\"code\",[t._v(\"RepaintBoundary\")]),t._v(\"，现在我们深入的了解一些。与 \"),n(\"code\",[t._v(\"RelayoutBoundary\")]),t._v(\" 相似，\"),n(\"code\",[t._v(\"RepaintBoundary\")]),t._v(\"是用于在确定重绘边界的，与\"),n(\"code\",[t._v(\"RelayoutBoundary\")]),t._v(\"不同的是，这个绘制边界需要由开发者通过\"),n(\"code\",[t._v(\"RepaintBoundary\")]),t._v(\" 组件自己指定，如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CustomPaint\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Size\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//指定画布大小\")]),t._v(\"\\n  painter\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyPainter\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RepaintBoundary\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"下面我们看看\"),n(\"code\",[t._v(\"RepaintBoundary\")]),t._v(\"的原理，\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"有一个\"),n(\"code\",[t._v(\"isRepaintBoundary\")]),t._v(\"属性，该属性决定这个\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"重绘时是否独立于其父元素，如果该属性值为\"),n(\"code\",[t._v(\"true\")]),t._v(\" ，则独立绘制，反之则一起绘制。那独立绘制是怎么实现的呢？ 答案就在\"),n(\"code\",[t._v(\"paintChild()\")]),t._v(\"源码中：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"paintChild\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"RenderObject child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Offset offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isRepaintBoundary\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"stopRecordingIfNeeded\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_compositeChild\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_paintWithContext\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们可以看到，在绘制子节点时，如果\"),n(\"code\",[t._v(\"child.isRepaintBoundary\")]),t._v(\" 为 \"),n(\"code\",[t._v(\"true\")]),t._v(\"则会调用\"),n(\"code\",[t._v(\"_compositeChild()\")]),t._v(\"方法，\"),n(\"code\",[t._v(\"_compositeChild()\")]),t._v(\"源码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_compositeChild\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"RenderObject child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Offset offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 给子节点创建一个layer ，然后再上面绘制子节点 \")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"_needsPaint\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"repaintCompositedChild\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" debugAlsoPaintedParent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"_layer \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"_layer\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"offset \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"appendLayer\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"_layer\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"很明显了，独立绘制是通过在不同的layer（层）上绘制的。所以，很明显，正确使用\"),n(\"code\",[t._v(\"isRepaintBoundary\")]),t._v(\"属性可以提高绘制效率，避免不必要的重绘。具体原理是：和触发重新build和layout类似，\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"也提供了一个\"),n(\"code\",[t._v(\"markNeedsPaint()\")]),t._v(\"方法，其源码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"markNeedsPaint\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//如果RenderObject.isRepaintBoundary 为true,则该RenderObject拥有layer，直接绘制  \")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"isRepaintBoundary\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"owner \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//找到最近的layer，绘制  \")]),t._v(\"\\n      owner\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"_nodesNeedingPaint\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      owner\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"requestVisualUpdate\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"parent \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"is\")]),t._v(\" RenderObject\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 没有自己的layer, 会和一个祖先节点共用一个layer  \")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_layer \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" RenderObject parent \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"parent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 向父级递归查找  \")]),t._v(\"\\n    parent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"markNeedsPaint\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"parent \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"parent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 如果直到根节点也没找到一个Layer，那么便需要绘制自身，因为没有其它节点可以绘制根节点。  \")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"owner \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      owner\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"requestVisualUpdate\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"可以看出，当调用 \"),n(\"code\",[t._v(\"markNeedsPaint()\")]),t._v(\" 方法时，会从当前 \"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\" 开始一直向父节点查找，直到找到 一个\"),n(\"code\",[t._v(\"isRepaintBoundary\")]),t._v(\" 为 \"),n(\"code\",[t._v(\"true\")]),t._v(\"的\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\" 时，才会触发重绘，这样便可以实现局部重绘。当 有\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\" 绘制的很频繁或很复杂时，可以通过RepaintBoundary Widget来指定\"),n(\"code\",[t._v(\"isRepaintBoundary\")]),t._v(\" 为 \"),n(\"code\",[t._v(\"true\")]),t._v(\"，这样在绘制时仅会重绘自身而无需重绘它的 parent，如此便可提高性能。\")]),t._v(\" \"),n(\"p\",[t._v(\"还有一个问题，通过\"),n(\"code\",[t._v(\"RepaintBoundary\")]),t._v(\" 如何设置\"),n(\"code\",[t._v(\"isRepaintBoundary\")]),t._v(\"属性呢？其实，如果使用了\"),n(\"code\",[t._v(\"RepaintBoundary\")]),t._v(\"，其对应的\"),n(\"code\",[t._v(\"RenderRepaintBoundary\")]),t._v(\"会自动将\"),n(\"code\",[t._v(\"isRepaintBoundary\")]),t._v(\"设为\"),n(\"code\",[t._v(\"true\")]),t._v(\"的：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"RenderRepaintBoundary\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"RenderProxyBox\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// Creates a repaint boundary around [child].\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RenderRepaintBoundary\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" RenderBox child \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  bool \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" isRepaintBoundary \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"h2\",{attrs:{id:\"_14-3-3-命中测试\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_14-3-3-命中测试\"}},[t._v(\"#\")]),t._v(\" 14.3.3 命中测试\")]),t._v(\" \"),n(\"p\",[t._v(\"我们在“事件处理与通知”一章中已经讲过Flutter事件机制和命中测试流程，本节我们看一下其内部实现原理。\")]),t._v(\" \"),n(\"p\",[t._v(\"一个对象是否可以响应事件，取决于其对命中测试的返回，当发生用户事件时，会从根节点（\"),n(\"code\",[t._v(\"RenderView\")]),t._v(\"）开始进行命中测试，下面是\"),n(\"code\",[t._v(\"RenderView\")]),t._v(\"的\"),n(\"code\",[t._v(\"hitTest()\")]),t._v(\"源码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"bool \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"hitTest\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"HitTestResult result\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" Offset position \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"hitTest\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"result\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//递归子RenderBox进行命中测试\")]),t._v(\"\\n  result\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"HitTestEntry\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//将测试结果添加到result中\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们再看看\"),n(\"code\",[t._v(\"RenderBox\")]),t._v(\"默认的\"),n(\"code\",[t._v(\"hitTest()\")]),t._v(\"实现：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"bool \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"hitTest\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"HitTestResult result\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" Offset position \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"  \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"contains\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"hitTestChildren\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"result\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"||\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"hitTestSelf\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      result\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxHitTestEntry\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们看到默认的实现里调用了\"),n(\"code\",[t._v(\"hitTestSelf()\")]),t._v(\"和\"),n(\"code\",[t._v(\"hitTestChildren()\")]),t._v(\"两个方法，这两个方法默认实现如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\" \\n\"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@protected\")]),t._v(\"\\nbool \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"hitTestSelf\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Offset position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n \\n\"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@protected\")]),t._v(\"\\nbool \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"hitTestChildren\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"HitTestResult result\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" Offset position \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"hitTest\")]),t._v(\" 方法用来判断该\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\" 是否在被点击的范围内，同时负责将被点击的 \"),n(\"code\",[t._v(\"RenderBox\")]),t._v(\" 添加到 \"),n(\"code\",[t._v(\"HitTestResult\")]),t._v(\" 列表中，参数 \"),n(\"code\",[t._v(\"position\")]),t._v(\" 为事件触发的坐标（如果有的话），返回 true 则表示有\"),n(\"code\",[t._v(\"RenderBox\")]),t._v(\" 通过了命中测试，需要响应事件，反之则认为当前\"),n(\"code\",[t._v(\"RenderBox\")]),t._v(\"没有命中。在继承\"),n(\"code\",[t._v(\"RenderBox\")]),t._v(\"时，可以直接重写\"),n(\"code\",[t._v(\"hitTest()\")]),t._v(\"方法，也可以重写 \"),n(\"code\",[t._v(\"hitTestSelf()\")]),t._v(\" 或 \"),n(\"code\",[t._v(\"hitTestChildren()\")]),t._v(\", 唯一不同的是 \"),n(\"code\",[t._v(\"hitTest()\")]),t._v(\"中需要将通过命中测试的节点信息添加到命中测试结果列表中，而 \"),n(\"code\",[t._v(\"hitTestSelf()\")]),t._v(\" 和 \"),n(\"code\",[t._v(\"hitTestChildren()\")]),t._v(\"则只需要简单的返回\"),n(\"code\",[t._v(\"true\")]),t._v(\"或\"),n(\"code\",[t._v(\"false\")]),t._v(\"。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_14-3-4-语义化\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_14-3-4-语义化\"}},[t._v(\"#\")]),t._v(\" 14.3.4 语义化\")]),t._v(\" \"),n(\"p\",[t._v(\"语义化即Semantics，主要是提供给读屏软件的接口，也是实现辅助功能的基础，通过语义化接口可以让机器理解页面上的内容，对于有视力障碍用户可以使用读屏软件来理解UI内容。如果一个\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"要支持语义化接口，可以实现 \"),n(\"code\",[t._v(\"describeApproximatePaintClip\")]),t._v(\"和 \"),n(\"code\",[t._v(\"visitChildrenForSemantics\")]),t._v(\"方法和\"),n(\"code\",[t._v(\"semanticsAnnotator\")]),t._v(\" getter。更多关于语义化的信息可以查看API文档。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_14-3-5-总结\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_14-3-5-总结\"}},[t._v(\"#\")]),t._v(\" 14.3.5 总结\")]),t._v(\" \"),n(\"p\",[t._v(\"本节我们介绍了\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"主要的功能和方法，理解这些内容可以帮助我们更好的理解Flutter UI底层原理。我们也可以看到，如果要从头到尾实现一个\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"是比较麻烦的，我们必须去实现layout、绘制和命中测试逻辑，但是值得庆幸的是，大多数时候我们可以直接在Widget层通过组合或者\"),n(\"code\",[t._v(\"CustomPaint\")]),t._v(\"完成自定义UI。如果遇到只能定义一个新\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"的场景时（如要实现一个新的layout算法的布局容器），可以直接继承自\"),n(\"code\",[t._v(\"RenderBox\")]),t._v(\"，这样可以帮我们减少一部分工作。\")])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/117.f7002db2.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[117],{567:function(t,s,n){t.exports=n.p+\"assets/img/15-5.bcaa565a.png\"},873:function(t,s,n){\"use strict\";n.r(s);var a=n(45),p=Object(a.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_15-7-登录页\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_15-7-登录页\"}},[t._v(\"#\")]),t._v(\" 15.7 登录页\")]),t._v(\" \"),a(\"p\",[t._v(\"我们说过Github有多种登录方式，为了简单起见，我们只实现通过用户名和密码登录。在实现登录页时有四点需要注意：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"可以自动填充上次登录的用户名（如果有）。\")]),t._v(\" \"),a(\"li\",[t._v(\"为了防止密码输入错误，密码框应该有开关可以看明文。\")]),t._v(\" \"),a(\"li\",[t._v(\"用户名或密码字段在调用登录接口前有本地合法性校验（比如不能为空）。\")]),t._v(\" \"),a(\"li\",[t._v(\"登录成功后需更新用户信息。\")])]),t._v(\" \"),a(\"p\",[t._v(\"实现代码如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'../index.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"LoginRoute\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _LoginRouteState \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_LoginRouteState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_LoginRouteState\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"LoginRoute\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  TextEditingController _unameController \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TextEditingController\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  TextEditingController _pwdController \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TextEditingController\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  bool pwdShow \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//密码是否显示明文\")]),t._v(\"\\n  GlobalKey _formKey \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"GlobalKey\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"FormState\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  bool _nameAutoFocus \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 自动填充上次登录的用户名，填充后将焦点定位到密码输入框\")]),t._v(\"\\n    _unameController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"text \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Global\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"lastLogin\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_unameController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"text \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _nameAutoFocus \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" gm \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" GmLocalizations\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      appBar\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"gm\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"login\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      body\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Form\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _formKey\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          autovalidate\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextFormField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  autofocus\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _nameAutoFocus\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _unameController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"InputDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    labelText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" gm\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"userName\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    hintText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" gm\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"userNameOrEmail\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    prefixIcon\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"person\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 校验用户名（不能为空）\")]),t._v(\"\\n                  validator\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"v\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" v\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"trim\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isNotEmpty \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" gm\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"userNameRequired\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextFormField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _pwdController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                autofocus\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"_nameAutoFocus\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"InputDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    labelText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" gm\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"password\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    hintText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" gm\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"password\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    prefixIcon\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"lock\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    suffixIcon\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"IconButton\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                      icon\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                          pwdShow \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"visibility_off \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"visibility\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                          pwdShow \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"pwdShow\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                obscureText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"pwdShow\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//校验密码（不能为空）\")]),t._v(\"\\n                validator\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"v\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" v\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"trim\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isNotEmpty \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" gm\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"passwordRequired\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"only\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"top\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"25\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ConstrainedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  constraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BoxConstraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"expand\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"55.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Theme\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"primaryColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _onLogin\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    textColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"gm\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"login\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_onLogin\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 提交前，先验证各个表单字段是否合法\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_formKey\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"currentState \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"as\")]),t._v(\" FormState\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"validate\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showLoading\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      User user\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        user \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Git\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"login\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_unameController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"text\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" _pwdController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"text\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 因为登录页返回后，首页会build，所以我们传false，更新user后不触发更新\")]),t._v(\"\\n        Provider\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"of\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"UserModel\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" listen\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"user \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" user\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"catch\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//登录失败则提示\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"response\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"statusCode \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"401\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showToast\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"GmLocalizations\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"userNameOrPasswordWrong\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showToast\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"finally\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 隐藏loading框\")]),t._v(\"\\n        Navigator\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"user \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 返回\")]),t._v(\"\\n        Navigator\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"代码很简单，关键地方都有注释，不再赘述，下面我们看一下运行效果，如图15-5所示。\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(567),alt:\"图15-5\"}})])])}),[],!1,null,null,null);s.default=p.exports}}]);"
  },
  {
    "path": "docs/assets/js/118.d4908451.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[118],{568:function(t,a,s){t.exports=s.p+\"assets/img/2-1.801e91b2.png\"},876:function(t,a,s){\"use strict\";s.r(a);var n=s(45),e=Object(n.a)({},(function(){var t=this,a=t.$createElement,n=t._self._c||a;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_2-1-计数器应用示例\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_2-1-计数器应用示例\"}},[t._v(\"#\")]),t._v(\" 2.1 计数器应用示例\")]),t._v(\" \"),n(\"p\",[t._v(\"用Android Studio和VS Code创建的Flutter应用模板默认是一个简单的计数器示例。本节先仔细讲解一下这个计数器Demo的源码，让读者对Flutter应用程序结构有个基本了解，然后在随后的小节中将会基于此示例，一步一步添加一些新的功能来介绍Flutter应用的其它概念与技术。\")]),t._v(\" \"),n(\"p\",[t._v(\"对于接下来的示例，希望读者可以跟着笔者一起亲自动手来写一下，这样不仅可以加深印象，而且也会对介绍的概念与技术有一个真切的体会。如果你还不是很熟悉Dart语言或者没有移动开发经验，不用担心，只要你熟悉面向对象和基本编程概念（如变量、循环和条件控制），则可以完成本示例。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_2-1-1-创建flutter应用模板\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_2-1-1-创建flutter应用模板\"}},[t._v(\"#\")]),t._v(\" 2.1.1 创建Flutter应用模板\")]),t._v(\" \"),n(\"p\",[t._v('通过Android Studio或VS Code创建一个新的Flutter工程，命名为\"first_flutter_app\"。创建好后，就会得到一个计数器应用的Demo。')]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[t._v(\"注意，默认Demo示例可能随着编辑器Flutter插件的版本变化而变化，本例中会介绍计数器示例的全部代码，所以不会对本示例产生影响。\")])]),t._v(\" \"),n(\"p\",[t._v(\"我们先运行创建的工程，效果如图2-1所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(568),alt:\"图2-1\"}})]),t._v(\" \"),n(\"p\",[t._v(\"该计数器示例中，每点击一次右下角带“+”号的悬浮按钮，屏幕中央的数字就会加1。\")]),t._v(\" \"),n(\"p\",[t._v(\"在这个示例中，主要Dart代码是在 \"),n(\"strong\",[t._v(\"lib/main.dart\")]),t._v(\" 文件中，下面是它的源码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"main\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"runApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyApp\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MaterialApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Flutter Demo'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      theme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ThemeData\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        primarySwatch\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      home\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyHomePage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Flutter Demo Home Page'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyHomePage\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyHomePage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _MyHomePageState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_MyHomePageState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_MyHomePageState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"MyHomePage\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  int _counter \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_incrementCounter\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _counter\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"++\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Scaffold\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      appBar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AppBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      body\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          mainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'You have pushed the button this many times:'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'$_counter'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Theme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"textTheme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"headline4\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      floatingActionButton\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FloatingActionButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _incrementCounter\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        tooltip\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Increment'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"add\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// This trailing comma makes auto-formatting nicer for build methods.\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\")])])]),n(\"h3\",{attrs:{id:\"分析\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#分析\"}},[t._v(\"#\")]),t._v(\" 分析\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"p\",[t._v(\"导入包。\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"此行代码作用是导入了Material UI组件库。\"),n(\"a\",{attrs:{href:\"https://material.io/guidelines/\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Material\"),n(\"OutboundLink\")],1),t._v(\"是一种标准的移动端和web端的视觉设计语言， Flutter默认提供了一套丰富的Material风格的UI组件。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"应用入口。\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"main\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"runApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"ul\",[n(\"li\",[t._v(\"与C/C++、Java类似，Flutter 应用中\"),n(\"code\",[t._v(\"main\")]),t._v(\"函数为应用程序的入口。\"),n(\"code\",[t._v(\"main\")]),t._v(\"函数中调用了\"),n(\"code\",[t._v(\"runApp\")]),t._v(\" 方法，它的功能是启动Flutter应用。\"),n(\"code\",[t._v(\"runApp\")]),t._v(\"它接受一个\"),n(\"code\",[t._v(\"Widget\")]),t._v(\"参数，在本示例中它是一个\"),n(\"code\",[t._v(\"MyApp\")]),t._v(\"对象，\"),n(\"code\",[t._v(\"MyApp()\")]),t._v(\"是Flutter应用的根组件。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"main\")]),t._v(\"函数使用了(\"),n(\"code\",[t._v(\"=>\")]),t._v(\")符号，这是Dart中单行函数或方法的简写。\")])])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"应用结构。\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyApp\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MaterialApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//应用名称  \")]),t._v(\"\\n      title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Flutter Demo'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n      theme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ThemeData\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//蓝色主题  \")]),t._v(\"\\n        primarySwatch\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//应用首页路由  \")]),t._v(\"\\n      home\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyHomePage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Flutter Demo Home Page'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"ul\",[n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"MyApp\")]),t._v(\"类代表Flutter应用，它继承了 \"),n(\"code\",[t._v(\"StatelessWidget\")]),t._v(\"类，这也就意味着应用本身也是一个widget。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"在Flutter中，大多数东西都是widget（后同“组件”或“部件”），包括对齐(alignment)、填充(padding)和布局(layout)等，它们都是以widget的形式提供。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"Flutter在构建页面时，会调用组件的\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法，widget的主要工作是提供一个build()方法来描述如何构建UI界面（通常是通过组合、拼装其它基础widget）。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"MaterialApp\")]),t._v(\" 是Material库中提供的Flutter APP框架，通过它可以设置应用的名称、主题、语言、首页及路由列表等。\"),n(\"code\",[t._v(\"MaterialApp\")]),t._v(\"也是一个widget。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"home\")]),t._v(\" 为Flutter应用的首页，它也是一个widget。\")])])])])]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_2-1-2-首页\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_2-1-2-首页\"}},[t._v(\"#\")]),t._v(\" 2.1.2 首页\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyHomePage\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyHomePage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _MyHomePageState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_MyHomePageState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_MyHomePageState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"MyHomePage\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"MyHomePage\")]),t._v(\" 是Flutter应用的首页，它继承自\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"类，表示它是一个有状态的组件（Stateful widget）。关于Stateful widget我们将在第三章“Widget简介”一节仔细介绍，现在我们只需简单认为有状态的组件（Stateful widget） 和无状态的组件（Stateless widget）有两点不同：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"p\",[t._v(\"Stateful widget可以拥有状态，这些状态在widget生命周期中是可以变的，而Stateless widget是不可变的。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"Stateful widget至少由两个类组成：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[t._v(\"一个\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"类。\")]),t._v(\" \"),n(\"li\",[t._v(\"一个 \"),n(\"code\",[t._v(\"State\")]),t._v(\"类； \"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"类本身是不变的，但是\"),n(\"code\",[t._v(\"State\")]),t._v(\"类中持有的状态在widget生命周期中可能会发生变化。\")])]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"_MyHomePageState\")]),t._v(\"类是\"),n(\"code\",[t._v(\"MyHomePage\")]),t._v(\"类对应的状态类。看到这里，读者可能已经发现：和\"),n(\"code\",[t._v(\"MyApp\")]),t._v(\" 类不同， \"),n(\"code\",[t._v(\"MyHomePage\")]),t._v(\"类中并没有\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法，取而代之的是，\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法被挪到了\"),n(\"code\",[t._v(\"_MyHomePageState\")]),t._v(\"方法中，至于为什么这么做，先留个疑问，在分析完完整代码后再来解答。\")])])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"state类\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#state类\"}},[t._v(\"#\")]),t._v(\" State类\")]),t._v(\" \"),n(\"p\",[t._v(\"接下来，我们看看\"),n(\"code\",[t._v(\"_MyHomePageState\")]),t._v(\"中都包含哪些东西：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"p\",[t._v(\"该组件的状态。由于我们只需要维护一个点击次数计数器，所以定义一个\"),n(\"code\",[t._v(\"_counter\")]),t._v(\"状态：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"int _counter \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//用于记录按钮点击的总次数\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"_counter\")]),t._v(\" 为保存屏幕右下角带“+”号按钮点击次数的状态。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"设置状态的自增函数。\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_incrementCounter\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n     _counter\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"++\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"当按钮点击时，会调用此函数，该函数的作用是先自增\"),n(\"code\",[t._v(\"_counter\")]),t._v(\"，然后调用\"),n(\"code\",[t._v(\"setState\")]),t._v(\" 方法。\"),n(\"code\",[t._v(\"setState\")]),t._v(\"方法的作用是通知Flutter框架，有状态发生了改变，Flutter框架收到通知后，会执行\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法来根据新的状态重新构建界面， Flutter 对此方法做了优化，使重新执行变的很快，所以你可以重新构建任何需要更新的东西，而无需分别去修改各个widget。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"构建UI界面\")]),t._v(\" \"),n(\"p\",[t._v(\"构建UI界面的逻辑在\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法中，当\"),n(\"code\",[t._v(\"MyHomePage\")]),t._v(\"第一次创建时，\"),n(\"code\",[t._v(\"_MyHomePageState\")]),t._v(\"类会被创建，当初始化完成后，Flutter框架会调用Widget的\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法来构建widget树，最终将widget树渲染到设备屏幕上。所以，我们看看\"),n(\"code\",[t._v(\"_MyHomePageState\")]),t._v(\"的\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法中都干了什么事：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Scaffold\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      appBar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AppBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      body\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          mainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'You have pushed the button this many times:'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'$_counter'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Theme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"textTheme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"headline4\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      floatingActionButton\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FloatingActionButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _incrementCounter\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        tooltip\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Increment'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"add\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"Scaffold\")]),t._v(\" 是 Material 库中提供的页面脚手架，它提供了默认的导航栏、标题和包含主屏幕widget树（后同“组件树”或“部件树”）的\"),n(\"code\",[t._v(\"body\")]),t._v(\"属性，组件树可以很复杂。本书后面示例中，路由默认都是通过\"),n(\"code\",[t._v(\"Scaffold\")]),t._v(\"创建。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"body\")]),t._v(\"的组件树中包含了一个\"),n(\"code\",[t._v(\"Center\")]),t._v(\" 组件，\"),n(\"code\",[t._v(\"Center\")]),t._v(\" 可以将其子组件树对齐到屏幕中心。此例中， \"),n(\"code\",[t._v(\"Center\")]),t._v(\" 子组件是一个\"),n(\"code\",[t._v(\"Column\")]),t._v(\" 组件，\"),n(\"code\",[t._v(\"Column\")]),t._v(\"的作用是将其所有子组件沿屏幕垂直方向依次排列； 此例中\"),n(\"code\",[t._v(\"Column\")]),t._v(\"子组件是两个 \"),n(\"code\",[t._v(\"Text\")]),t._v(\"，第一个\"),n(\"code\",[t._v(\"Text\")]),t._v(\" 显示固定文本 “You have pushed the button this many times:”，第二个\"),n(\"code\",[t._v(\"Text\")]),t._v(\" 显示\"),n(\"code\",[t._v(\"_counter\")]),t._v(\"状态的数值。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"floatingActionButton\")]),t._v(\"是页面右下角的带“+”的悬浮按钮，它的\"),n(\"code\",[t._v(\"onPressed\")]),t._v(\"属性接受一个回调函数，代表它被点击后的处理器，本例中直接将\"),n(\"code\",[t._v(\"_incrementCounter\")]),t._v(\"方法作为其处理函数。\")])])])]),t._v(\" \"),n(\"p\",[t._v(\"现在，我们将整个计数器执行流程串起来：当右下角的\"),n(\"code\",[t._v(\"floatingActionButton\")]),t._v(\"按钮被点击之后，会调用\"),n(\"code\",[t._v(\"_incrementCounter\")]),t._v(\"方法。在\"),n(\"code\",[t._v(\"_incrementCounter\")]),t._v(\"方法中，首先会自增\"),n(\"code\",[t._v(\"_counter\")]),t._v(\"计数器（状态），然后\"),n(\"code\",[t._v(\"setState\")]),t._v(\"会通知Flutter框架状态发生变化，接着，Flutter框架会调用\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法以新的状态重新构建UI，最终显示在设备屏幕上。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"为什么要将build方法放在state中-而不是放在statefulwidget中\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#为什么要将build方法放在state中-而不是放在statefulwidget中\"}},[t._v(\"#\")]),t._v(\" 为什么要将build方法放在State中，而不是放在StatefulWidget中？\")]),t._v(\" \"),n(\"p\",[t._v(\"现在，我们回答之前提出的问题，为什么\"),n(\"code\",[t._v(\"build()\")]),t._v(\"方法放在State（而不是\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"）中 ？这主要是为了提高开发的灵活性。如果将\"),n(\"code\",[t._v(\"build()\")]),t._v(\"方法放在\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"中则会有两个问题：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"p\",[t._v(\"状态访问不便\")]),t._v(\" \"),n(\"p\",[t._v(\"试想一下，如果我们的\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"有很多状态，而每次状态改变都要调用\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法，由于状态是保存在State中的，如果\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法在\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"中，那么\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法和状态分别在两个类中，那么构建时读取状态将会很不方便！试想一下，如果真的将\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法放在StatefulWidget中的话，由于构建用户界面过程需要依赖State，所以\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法将必须加一个\"),n(\"code\",[t._v(\"State\")]),t._v(\"参数，大概是下面这样：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" State state\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//state.counter\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"这样的话就只能将State的所有状态声明为公开的状态，这样才能在State类外部访问状态！但是，将状态设置为公开后，状态将不再具有私密性，这就会导致对状态的修改将会变的不可控。但如果将\"),n(\"code\",[t._v(\"build()\")]),t._v(\"方法放在State中的话，构建过程不仅可以直接访问状态，而且也无需公开私有状态，这会非常方便。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"继承\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"不便\")]),t._v(\" \"),n(\"p\",[t._v(\"例如，Flutter中有一个动画widget的基类\"),n(\"code\",[t._v(\"AnimatedWidget\")]),t._v(\"，它继承自\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"类。\"),n(\"code\",[t._v(\"AnimatedWidget\")]),t._v(\"中引入了一个抽象方法\"),n(\"code\",[t._v(\"build(BuildContext context)\")]),t._v(\"，继承自\"),n(\"code\",[t._v(\"AnimatedWidget\")]),t._v(\"的动画widget都要实现这个\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法。现在设想一下，如果\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\" 类中已经有了一个\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法，正如上面所述，此时\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法需要接收一个state对象，这就意味着\"),n(\"code\",[t._v(\"AnimatedWidget\")]),t._v(\"必须将自己的State对象(记为_animatedWidgetState)提供给其子类，因为子类需要在其\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法中调用父类的\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法，代码可能如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyAnimationWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimatedWidget\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n    Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" State state\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//由于子类要用到AnimatedWidget的状态对象_animatedWidgetState，\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//所以AnimatedWidget必须通过某种方式将其状态对象_animatedWidgetState\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//暴露给其子类   \")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" _animatedWidgetState\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"这样很显然是不合理的，因为\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"code\",[t._v(\"AnimatedWidget\")]),t._v(\"的状态对象是\"),n(\"code\",[t._v(\"AnimatedWidget\")]),t._v(\"内部实现细节，不应该暴露给外部。\")]),t._v(\" \"),n(\"li\",[t._v(\"如果要将父类状态暴露给子类，那么必须得有一种传递机制，而做这一套传递机制是无意义的，因为父子类之间状态的传递和子类本身逻辑是无关的。\")])])])]),t._v(\" \"),n(\"p\",[t._v(\"综上所述，可以发现，对于\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"，将\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法放在State中，可以给开发带来很大的灵活性。\")])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/119.baead276.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[119],{582:function(t,a,s){t.exports=s.p+\"assets/img/2-12.eb7484c9.png\"},883:function(t,a,s){\"use strict\";s.r(a);var n=s(45),r=Object(n.a)({},(function(){var t=this,a=t.$createElement,n=t._self._c||a;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_2-6-flutter异常捕获\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_2-6-flutter异常捕获\"}},[t._v(\"#\")]),t._v(\" 2.6 Flutter异常捕获\")]),t._v(\" \"),n(\"p\",[t._v(\"在介绍Flutter异常捕获之前必须先了解一下Dart单线程模型，只有了解了Dart的代码执行流程，我们才能知道该在什么地方去捕获异常。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_2-6-1-dart单线程模型\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_2-6-1-dart单线程模型\"}},[t._v(\"#\")]),t._v(\" 2.6.1 Dart单线程模型\")]),t._v(\" \"),n(\"p\",[t._v(\"在Java和Objective-C（以下简称“OC”）中，如果程序发生异常且没有被捕获，那么程序将会终止，但是这在Dart或JavaScript中则不会！究其原因，这和它们的运行机制有关系。Java和OC都是多线程模型的编程语言，任意一个线程触发异常且该异常未被捕获时，就会导致整个进程退出。但Dart和JavaScript不会，它们都是单线程模型，运行机制很相似(但有区别)，下面我们通过Dart官方提供的一张图来看看Dart大致运行原理：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(582),alt:\"图2-12\"}})]),t._v(\" \"),n(\"p\",[t._v(\"Dart 在单线程中是以消息循环机制来运行的，其中包含两个任务队列，一个是“微任务队列”  \"),n(\"strong\",[t._v(\"microtask queue\")]),t._v(\"，另一个叫做“事件队列” \"),n(\"strong\",[t._v(\"event queue\")]),t._v(\"。从图中可以发现，微任务队列的执行优先级高于事件队列。\")]),t._v(\" \"),n(\"p\",[t._v(\"现在我们来介绍一下Dart线程运行过程，如上图中所示，入口函数 main() 执行完后，消息循环机制便启动了。首先会按照先进先出的顺序逐个执行微任务队列中的任务，事件任务执行完毕后程序便会退出，但是，在事件任务执行的过程中也可以插入新的微任务和事件任务，在这种情况下，整个线程的执行过程便是一直在循环，不会退出，而Flutter中，主线程的执行过程正是如此，永不终止。\")]),t._v(\" \"),n(\"p\",[t._v(\"在Dart中，所有的外部事件任务都在事件队列中，如IO、计时器、点击、以及绘制事件等，而微任务通常来源于Dart内部，并且微任务非常少，之所以如此，是因为微任务队列优先级高，如果微任务太多，执行时间总和就越久，事件队列任务的延迟也就越久，对于GUI应用来说最直观的表现就是比较卡，所以必须得保证微任务队列不会太长。值得注意的是，我们可以通过\"),n(\"code\",[t._v(\"Future.microtask(…)\")]),t._v(\"方法向微任务队列插入一个任务。\")]),t._v(\" \"),n(\"p\",[t._v(\"在事件循环中，当某个任务发生异常并没有被捕获时，程序并不会退出，而直接导致的结果是\"),n(\"strong\",[t._v(\"当前任务\")]),t._v(\"的后续代码就不会被执行了，也就是说一个任务中的异常是不会影响其它任务执行的。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_2-6-2-flutter异常捕获\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_2-6-2-flutter异常捕获\"}},[t._v(\"#\")]),t._v(\" 2.6.2 Flutter异常捕获\")]),t._v(\" \"),n(\"p\",[t._v(\"Dart中可以通过\"),n(\"code\",[t._v(\"try/catch/finally\")]),t._v(\"来捕获代码块异常，这个和其它编程语言类似，如果读者不清楚，可以查看Dart语言文档，不再赘述，下面我们看看Flutter中的异常捕获。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"flutter框架异常捕获\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#flutter框架异常捕获\"}},[t._v(\"#\")]),t._v(\" Flutter框架异常捕获\")]),t._v(\" \"),n(\"p\",[t._v(\"Flutter 框架为我们在很多关键的方法进行了异常捕获。这里举一个例子，当我们布局发生越界或不合规范时，Flutter就会自动弹出一个错误界面，这是因为Flutter已经在执行build方法时添加了异常捕获，最终的源码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"performRebuild\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//执行build方法  \")]),t._v(\"\\n    built \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"catch\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" stack\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 有异常时则弹出错误提示  \")]),t._v(\"\\n    built \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" ErrorWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_debugReportException\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'building $this'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" stack\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"      \\n\")])])]),n(\"p\",[t._v(\"可以看到，在发生异常时，Flutter默认的处理方式是弹一个ErrorWidget，但如果我们想自己捕获异常并上报到报警平台的话应该怎么做？我们进入\"),n(\"code\",[t._v(\"_debugReportException()\")]),t._v(\"方法看看：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"FlutterErrorDetails \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_debugReportException\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  String context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),t._v(\" exception\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  StackTrace stack\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  InformationCollector informationCollector\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//构建错误详情对象  \")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" FlutterErrorDetails details \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlutterErrorDetails\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    exception\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" exception\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    stack\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" stack\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"library\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'widgets library'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    informationCollector\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" informationCollector\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//报告错误 \")]),t._v(\"\\n  FlutterError\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"reportError\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们发现，错误是通过\"),n(\"code\",[t._v(\"FlutterError.reportError\")]),t._v(\"方法上报的，继续跟踪：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"reportError\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"FlutterErrorDetails details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"onError \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onError\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//调用了onError回调\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们发现\"),n(\"code\",[t._v(\"onError\")]),t._v(\"是\"),n(\"code\",[t._v(\"FlutterError\")]),t._v(\"的一个静态属性，它有一个默认的处理方法 \"),n(\"code\",[t._v(\"dumpErrorToConsole\")]),t._v(\"，到这里就清晰了，如果我们想自己上报异常，只需要提供一个自定义的错误处理回调即可，如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"main\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  FlutterError\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onError \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"FlutterErrorDetails details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"reportError\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"这样我们就可以处理那些Flutter为我们捕获的异常了，接下来我们看看如何捕获其它异常。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"其它异常捕获与日志收集\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#其它异常捕获与日志收集\"}},[t._v(\"#\")]),t._v(\" 其它异常捕获与日志收集\")]),t._v(\" \"),n(\"p\",[t._v(\"在Flutter中，还有一些Flutter没有为我们捕获的异常，如调用空对象方法异常、Future中的异常。在Dart中，异常分两类：同步异常和异步异常，同步异常可以通过\"),n(\"code\",[t._v(\"try/catch\")]),t._v(\"捕获，而异步异常则比较麻烦，如下面的代码是捕获不了\"),n(\"code\",[t._v(\"Future\")]),t._v(\"的异常的：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Future\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"delayed\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Future\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"error\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"xxx\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"catch\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"Dart中有一个\"),n(\"code\",[t._v(\"runZoned(...)\")]),t._v(\" 方法，可以给执行对象指定一个Zone。Zone表示一个代码执行的环境范围，为了方便理解，读者可以将Zone类比为一个代码执行沙箱，不同沙箱的之间是隔离的，沙箱可以捕获、拦截或修改一些代码行为，如Zone中可以捕获日志输出、Timer创建、微任务调度的行为，同时Zone也可以捕获所有未处理的异常。下面我们看看\"),n(\"code\",[t._v(\"runZoned(...)\")]),t._v(\"方法定义：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"R runZoned\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"R\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"R \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"body\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Map zoneValues\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n    ZoneSpecification zoneSpecification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"Function\")]),t._v(\" onError\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \\n\")])])]),n(\"ul\",[n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"zoneValues\")]),t._v(\": Zone 的私有数据，可以通过实例\"),n(\"code\",[t._v(\"zone[key]\")]),t._v(\"获取，可以理解为每个“沙箱”的私有数据。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"zoneSpecification\")]),t._v(\"：Zone的一些配置，可以自定义一些代码行为，比如拦截日志输出行为等，举个例子：\")]),t._v(\" \"),n(\"p\",[t._v(\"下面是拦截应用中所有调用\"),n(\"code\",[t._v(\"print\")]),t._v(\"输出日志的行为。\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"main\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"runZoned\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"runApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" zoneSpecification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ZoneSpecification\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      print\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Zone self\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" ZoneDelegate parent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Zone zone\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" String line\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        parent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"zone\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Intercepted: $line\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"这样一来，我们APP中所有调用\"),n(\"code\",[t._v(\"print\")]),t._v(\"方法输出日志的行为都会被拦截，通过这种方式，我们也可以在应用中记录日志，等到应用触发未捕获的异常时，将异常信息和日志统一上报。ZoneSpecification还可以自定义一些其他行为，读者可以查看API文档。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"onError\")]),t._v(\"：Zone中未捕获异常处理回调，如果开发者提供了onError回调或者通过\"),n(\"code\",[t._v(\"ZoneSpecification.handleUncaughtError\")]),t._v(\"指定了错误处理回调，那么这个zone将会变成一个error-zone，该error-zone中发生未捕获异常(无论同步还是异步)时都会调用开发者提供的回调，如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"runZoned\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"runApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" onError\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Object obj\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" StackTrace stack\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" details\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"makeDetails\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"obj\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"stack\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"reportError\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"这样一来，结合上面的\"),n(\"code\",[t._v(\"FlutterError.onError\")]),t._v(\"我们就可以捕获我们Flutter应用中全部错误了！需要注意的是，error-zone内部发生的错误是不会跨越当前error-zone的边界的，如果想跨越error-zone边界去捕获异常，可以通过共同的“源”zone来捕获，如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" future \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Future\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"499\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"runZoned\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\t\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" future2 \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" future\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"throw\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"error in first error-zone\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\t\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"runZoned\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\t\\t\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" future3 \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" future2\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"catchError\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Never reached!\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\t\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" onError\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"unused error handler\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" onError\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"catches error of first error-zone.\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\")])])])])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"总结\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),n(\"p\",[t._v(\"我们最终的异常捕获和上报代码大致如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"collectLog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String line\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//收集日志\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"reportErrorAndLog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"FlutterErrorDetails details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//上报错误和日志逻辑\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\nFlutterErrorDetails \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"makeDetails\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Object obj\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" StackTrace stack\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 构建错误信息\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"main\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  FlutterError\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onError \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"FlutterErrorDetails details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"reportErrorAndLog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"runZoned\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"runApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    zoneSpecification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ZoneSpecification\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      print\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Zone self\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" ZoneDelegate parent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Zone zone\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" String line\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"collectLog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"line\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 收集日志\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    onError\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Object obj\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" StackTrace stack\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" details \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"makeDetails\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"obj\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" stack\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"reportErrorAndLog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])])])}),[],!1,null,null,null);a.default=r.exports}}]);"
  },
  {
    "path": "docs/assets/js/12.ef586fcc.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[12],{662:function(t,s,a){t.exports=a.p+\"assets/img/6-2.1d35c6fc.png\"},663:function(t,s,a){t.exports=a.p+\"assets/img/6-3.e023acf1.png\"},664:function(t,s,a){t.exports=a.p+\"assets/img/6-4.288d3ff1.png\"},665:function(t,s,a){t.exports=a.p+\"assets/img/6-5.2947b7f7.png\"},666:function(t,s,a){t.exports=a.p+\"assets/img/6-6.e48bfae5.png\"},667:function(t,s,a){t.exports=a.p+\"assets/img/6-7.c443522f.png\"},668:function(t,s,a){t.exports=a.p+\"assets/img/6-8.e48bfae5.png\"},912:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_6-3-listview\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_6-3-listview\"}},[t._v(\"#\")]),t._v(\" 6.3 ListView\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"ListView\")]),t._v(\"是最常用的可滚动组件之一，它可以沿一个方向线性排布所有子组件，并且它也支持基于Sliver的延迟构建模型。我们看看ListView的默认构造函数定义：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"  \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//可滚动widget公共参数\")]),t._v(\"\\n  Axis scrollDirection \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Axis\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"vertical\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  bool reverse \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  ScrollController controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  bool primary\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  ScrollPhysics physics\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  EdgeInsetsGeometry padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//ListView各个构造函数的共同参数  \")]),t._v(\"\\n  double itemExtent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  bool shrinkWrap \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  bool addAutomaticKeepAlives \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  bool addRepaintBoundaries \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  double cacheExtent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//子widget列表\")]),t._v(\"\\n  List\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" children \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上面参数分为两组：第一组是可滚动组件的公共参数，本章第一节中已经介绍过，不再赘述；第二组是\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"各个构造函数（\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"有多个构造函数）的共同参数，我们重点来看看这些参数，：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"itemExtent\")]),t._v(\"：该参数如果不为\"),n(\"code\",[t._v(\"null\")]),t._v(\"，则会强制\"),n(\"code\",[t._v(\"children\")]),t._v(\"的“长度”为\"),n(\"code\",[t._v(\"itemExtent\")]),t._v(\"的值；这里的“长度”是指滚动方向上子组件的长度，也就是说如果滚动方向是垂直方向，则\"),n(\"code\",[t._v(\"itemExtent\")]),t._v(\"代表子组件的高度；如果滚动方向为水平方向，则\"),n(\"code\",[t._v(\"itemExtent\")]),t._v(\"就代表子组件的宽度。在\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"中，指定\"),n(\"code\",[t._v(\"itemExtent\")]),t._v(\"比让子组件自己决定自身长度会更高效，这是因为指定\"),n(\"code\",[t._v(\"itemExtent\")]),t._v(\"后，滚动系统可以提前知道列表的长度，而无需每次构建子组件时都去再计算一下，尤其是在滚动位置频繁变化时（滚动系统需要频繁去计算列表高度）。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"shrinkWrap\")]),t._v(\"：该属性表示是否根据子组件的总长度来设置\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"的长度，默认值为\"),n(\"code\",[t._v(\"false\")]),t._v(\" 。默认情况下，\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"的会在滚动方向尽可能多的占用空间。当\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"在一个无边界(滚动方向上)的容器中时，\"),n(\"code\",[t._v(\"shrinkWrap\")]),t._v(\"必须为\"),n(\"code\",[t._v(\"true\")]),t._v(\"。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"addAutomaticKeepAlives\")]),t._v(\"：该属性表示是否将列表项（子组件）包裹在\"),n(\"code\",[t._v(\"AutomaticKeepAlive\")]),t._v(\" 组件中；典型地，在一个懒加载列表中，如果将列表项包裹在\"),n(\"code\",[t._v(\"AutomaticKeepAlive\")]),t._v(\"中，在该列表项滑出视口时它也不会被GC（垃圾回收），它会使用\"),n(\"code\",[t._v(\"KeepAliveNotification\")]),t._v(\"来保存其状态。如果列表项自己维护其\"),n(\"code\",[t._v(\"KeepAlive\")]),t._v(\"状态，那么此参数必须置为\"),n(\"code\",[t._v(\"false\")]),t._v(\"。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"addRepaintBoundaries\")]),t._v(\"：该属性表示是否将列表项（子组件）包裹在\"),n(\"code\",[t._v(\"RepaintBoundary\")]),t._v(\"组件中。当可滚动组件滚动时，将列表项包裹在\"),n(\"code\",[t._v(\"RepaintBoundary\")]),t._v(\"中可以避免列表项重绘，但是当列表项重绘的开销非常小（如一个颜色块，或者一个较短的文本）时，不添加\"),n(\"code\",[t._v(\"RepaintBoundary\")]),t._v(\"反而会更高效。和\"),n(\"code\",[t._v(\"addAutomaticKeepAlive\")]),t._v(\"一样，如果列表项自己维护其\"),n(\"code\",[t._v(\"KeepAlive\")]),t._v(\"状态，那么此参数必须置为\"),n(\"code\",[t._v(\"false\")]),t._v(\"。\")])]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[t._v(\"注意：上面这些参数并非\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"特有，在本章后面介绍的其它可滚动组件也可能会拥有这些参数，它们的含义是相同的。\")])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"默认构造函数\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#默认构造函数\"}},[t._v(\"#\")]),t._v(\" 默认构造函数\")]),t._v(\" \"),n(\"p\",[t._v(\"默认构造函数有一个\"),n(\"code\",[t._v(\"children\")]),t._v(\"参数，它接受一个Widget列表（List\"),n(\"Widget\",[t._v(\"）。这种方式适合只有少量的子组件的情况，因为这种方式需要将所有\"),n(\"code\",[t._v(\"children\")]),t._v(\"都提前创建好（这需要做大量工作），而不是等到子widget真正显示的时候再创建，也就是说通过默认构造函数构建的ListView没有应用基于Sliver的懒加载模型。实际上通过此方式创建的\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"和使用\"),n(\"code\",[t._v(\"SingleChildScrollView\")]),t._v(\"+\"),n(\"code\",[t._v(\"Column\")]),t._v(\"的方式没有本质的区别。下面是一个例子：\")])],1),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  shrinkWrap\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n  padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'I\\\\'m dedicating every day to you'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Domestic life was never quite my style'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'When you smile, you knock me out, I fall apart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'And I thought I was so smart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"blockquote\",[n(\"p\",[t._v(\"再次强调，可滚动组件通过一个List\"),n(\"Widget\",[t._v(\"来作为其children属性时，只适用于子组件较少的情况，这是一个通用规律，并非\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"自己的特性，像\"),n(\"code\",[t._v(\"GridView\")]),t._v(\"也是如此。\")])],1)]),t._v(\" \"),n(\"h3\",{attrs:{id:\"listview-builder\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#listview-builder\"}},[t._v(\"#\")]),t._v(\" ListView.builder\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"ListView.builder\")]),t._v(\"适合列表项比较多（或者无限）的情况，因为只有当子组件真正显示的时候才会被创建，也就说通过该构造函数创建的\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"是支持基于Sliver的懒加载模型的。下面看一下\"),n(\"code\",[t._v(\"ListView.builder\")]),t._v(\"的核心参数列表：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"ListView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// ListView公共参数已省略  \")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" IndexedWidgetBuilder itemBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  int itemCount\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"itemBuilder\")]),t._v(\"：它是列表项的构建器，类型为\"),n(\"code\",[t._v(\"IndexedWidgetBuilder\")]),t._v(\"，返回值为一个widget。当列表滚动到具体的\"),n(\"code\",[t._v(\"index\")]),t._v(\"位置时，会调用该构建器构建列表项。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"itemCount\")]),t._v(\"：列表项的数量，如果为\"),n(\"code\",[t._v(\"null\")]),t._v(\"，则为无限列表。\")])]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[t._v(\"可滚动组件的构造函数如果需要一个列表项Builder，那么通过该构造函数构建的可滚动组件通常就是支持基于Sliver的懒加载模型的，反之则不支持，这是个一般规律。我们在后面在介绍可滚动组件的构造函数时将不再专门说明其是否支持基于Sliver的懒加载模型了。\")])]),t._v(\" \"),n(\"p\",[t._v(\"下面看一个例子：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"ListView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    itemCount\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    itemExtent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//强制高度为50.0\")]),t._v(\"\\n    itemBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$index\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图6-2所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(662),alt:\"图6-2\"}})]),t._v(\" \"),n(\"h3\",{attrs:{id:\"listview-separated\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#listview-separated\"}},[t._v(\"#\")]),t._v(\" ListView.separated\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"ListView.separated\")]),t._v(\"可以在生成的列表项之间添加一个分割组件，它比\"),n(\"code\",[t._v(\"ListView.builder\")]),t._v(\"多了一个\"),n(\"code\",[t._v(\"separatorBuilder\")]),t._v(\"参数，该参数是一个分割组件生成器。\")]),t._v(\" \"),n(\"p\",[t._v(\"下面我们看一个例子：奇数行添加一条蓝色下划线，偶数行添加一条绿色下划线。\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ListView3\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//下划线widget预定义以供复用。  \")]),t._v(\"\\n    Widget divider1\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Divider\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    Widget divider2\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Divider\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"green\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" ListView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"separated\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        itemCount\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//列表项构造器\")]),t._v(\"\\n        itemBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$index\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//分割器构造器\")]),t._v(\"\\n        separatorBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" index\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"%\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\"divider1\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"divider2\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"img\",{attrs:{src:a(663),alt:\"图6-3\"}})]),t._v(\" \"),n(\"h3\",{attrs:{id:\"实例-无限加载列表\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#实例-无限加载列表\"}},[t._v(\"#\")]),t._v(\" 实例：无限加载列表\")]),t._v(\" \"),n(\"p\",[t._v(\"假设我们要从数据源异步分批拉取一些数据，然后用\"),n(\"code\",[t._v(\"ListView\")]),t._v('展示，当我们滑动到列表末尾时，判断是否需要再去拉取数据，如果是，则去拉取，拉取过程中在表尾显示一个loading，拉取成功后将数据插入列表；如果不需要再去拉取，则在表尾提示\"没有更多\"。代码如下：')]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"InfiniteListView\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _InfiniteListViewState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_InfiniteListViewState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_InfiniteListViewState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"InfiniteListView\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" loadingTag \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"##loading##\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//表尾标记\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" _words \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"loadingTag\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_retrieveData\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" ListView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"separated\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      itemCount\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _words\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"length\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      itemBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//如果到了表尾\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_words\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" loadingTag\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//不足100条，继续获取数据\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_words\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"length \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//获取数据\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_retrieveData\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//加载时显示loading\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"24.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"24.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircularProgressIndicator\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"strokeWidth\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//已经加载了100条数据，不再获取数据。\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"没有更多了\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//显示单词列表项\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_words\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      separatorBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Divider\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_retrieveData\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Future\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"delayed\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//重新构建列表\")]),t._v(\"\\n\\t\\t_words\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"insertAll\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_words\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"length \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//每次生成20个单词\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"generateWordPairs\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"take\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"map\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"asPascalCase\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toList\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \\t\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行后效果如图6-4、6-5所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(664),alt:\"图6-4\"}}),n(\"img\",{attrs:{src:a(665),alt:\"图6-5\"}})]),t._v(\" \"),n(\"p\",[t._v(\"代码比较简单，读者可以参照代码中的注释理解，故不再赘述。需要说明的是，\"),n(\"code\",[t._v(\"_retrieveData()\")]),t._v(\"的功能是模拟从数据源异步获取数据，我们使用english_words包的\"),n(\"code\",[t._v(\"generateWordPairs()\")]),t._v(\"方法每次生成20个单词。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"添加固定列表头\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#添加固定列表头\"}},[t._v(\"#\")]),t._v(\" 添加固定列表头\")]),t._v(\" \"),n(\"p\",[t._v(\"很多时候我们需要给列表添加一个固定表头，比如我们想实现一个商品列表，需要在列表顶部添加一个“商品列表”标题，期望的效果如图6-6所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(666),alt:\"图6-6\"}})]),t._v(\" \"),n(\"p\",[t._v(\"我们按照之前经验，写出如下代码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\nWidget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"商品列表\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    ListView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"itemBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$index\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"然后运行，发现并没有出现我们期望的效果，相反触发了一个异常；\")]),t._v(\" \"),n(\"div\",{staticClass:\"language- extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[n(\"code\",[t._v(\"Error caught by rendering library, thrown during performResize()。\\nVertical viewport was given unbounded height ...\\n\")])])]),n(\"p\",[t._v(\"从异常信息中我们可以看到是因为\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"高度边界无法确定引起，所以解决的办法也很明显，我们需要给\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"指定边界，我们通过\"),n(\"code\",[t._v(\"SizedBox\")]),t._v(\"指定一个列表高度看看是否生效：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"400\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//指定列表高度为400\")]),t._v(\"\\n    child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ListView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"itemBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$index\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图6-7所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(667),alt:\"图6-7\"}})]),t._v(\" \"),n(\"p\",[t._v(\"可以看到，现在没有触发异常并且列表已经显示出来了，但是我们的手机屏幕高度要大于400，所以底部会有一些空白。那如果我们要实现列表铺满除表头以外的屏幕空间应该怎么做？直观的方法是我们去动态计算，用屏幕高度减去状态栏、导航栏、表头的高度即为剩余屏幕高度，代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//Material设计规范中状态栏、导航栏、ListTile高度分别为24、56、56 \")]),t._v(\"\\n  height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MediaQuery\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"height\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"24\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"56\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"56\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ListView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"itemBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$index\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"    \\n\")])])]),n(\"p\",[t._v(\"运行效果如下图6-8所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(668),alt:\"图6-8\"}})]),t._v(\" \"),n(\"p\",[t._v(\"可以看到，我们期望的效果实现了，但是这种方法并不优雅，如果页面布局发生变化，比如表头布局调整导致表头高度改变，那么剩余空间的高度就得重新计算。那么有什么方法可以自动拉伸\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"以填充屏幕剩余空间的方法吗？当然有！答案就是\"),n(\"code\",[t._v(\"Flex\")]),t._v(\"。前面已经介绍过在弹性布局中，可以使用\"),n(\"code\",[t._v(\"Expanded\")]),t._v(\"自动拉伸组件大小，并且我们也说过\"),n(\"code\",[t._v(\"Column\")]),t._v(\"是继承自\"),n(\"code\",[t._v(\"Flex\")]),t._v(\"的，所以我们可以直接使用\"),n(\"code\",[t._v(\"Column\")]),t._v(\"+\"),n(\"code\",[t._v(\"Expanded\")]),t._v(\"来实现，代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\nWidget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"商品列表\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Expanded\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ListView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"itemBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$index\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行后，和上图一样，完美实现了！\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"总结\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),n(\"p\",[t._v(\"本节主要介绍了\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"的一些公共参数以及常用的构造函数。不同的构造函数对应了不同的列表项生成模型，如果需要自定义列表项生成模型，可以通过\"),n(\"code\",[t._v(\"ListView.custom\")]),t._v(\"来自定义，它需要实现一个\"),n(\"code\",[t._v(\"SliverChildDelegate\")]),t._v(\"用来给ListView生成列表项组件，更多详情请参考API文档。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/120.681a706e.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[120],{611:function(t,s,a){t.exports=a.p+\"assets/img/3-23.a249df4c.png\"},890:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_3-6-单选开关和复选框\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-6-单选开关和复选框\"}},[t._v(\"#\")]),t._v(\" 3.6 单选开关和复选框\")]),t._v(\" \"),n(\"p\",[t._v(\"Material 组件库中提供了Material风格的单选开关\"),n(\"code\",[t._v(\"Switch\")]),t._v(\"和复选框\"),n(\"code\",[t._v(\"Checkbox\")]),t._v(\"，虽然它们都是继承自\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"，但它们本身不会保存当前选中状态，选中状态都是由父组件来管理的。当\"),n(\"code\",[t._v(\"Switch\")]),t._v(\"或\"),n(\"code\",[t._v(\"Checkbox\")]),t._v(\"被点击时，会触发它们的\"),n(\"code\",[t._v(\"onChanged\")]),t._v(\"回调，我们可以在此回调中处理选中状态改变逻辑。下面看一个简单的例子：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"SwitchAndCheckBoxTestRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _SwitchAndCheckBoxTestRouteState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_SwitchAndCheckBoxTestRouteState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_SwitchAndCheckBoxTestRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"SwitchAndCheckBoxTestRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  bool _switchSelected\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//维护单选开关状态\")]),t._v(\"\\n  bool _checkboxSelected\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//维护复选框状态\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Switch\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _switchSelected\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//当前状态\")]),t._v(\"\\n          onChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//重新构建页面  \")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              _switchSelected\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Checkbox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _checkboxSelected\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          activeColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//选中时的颜色\")]),t._v(\"\\n          onChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              _checkboxSelected\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上面代码中，由于需要维护\"),n(\"code\",[t._v(\"Switch\")]),t._v(\"和\"),n(\"code\",[t._v(\"Checkbox\")]),t._v(\"的选中状态，所以\"),n(\"code\",[t._v(\"SwitchAndCheckBoxTestRoute\")]),t._v(\"继承自\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\" 。在其\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法中分别构建了一个\"),n(\"code\",[t._v(\"Switch\")]),t._v(\"和\"),n(\"code\",[t._v(\"Checkbox\")]),t._v(\"，初始状态都为选中状态，当用户点击时，会将状态置反，然后回调用\"),n(\"code\",[t._v(\"setState()\")]),t._v(\"通知Flutter framework重新构建UI。\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(611),alt:\"图3-23\"}})]),t._v(\" \"),n(\"h3\",{attrs:{id:\"属性及外观\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#属性及外观\"}},[t._v(\"#\")]),t._v(\" 属性及外观\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"Switch\")]),t._v(\"和\"),n(\"code\",[t._v(\"Checkbox\")]),t._v(\"属性比较简单，读者可以查看API文档，它们都有一个\"),n(\"code\",[t._v(\"activeColor\")]),t._v(\"属性，用于设置激活态的颜色。至于大小，到目前为止，\"),n(\"code\",[t._v(\"Checkbox\")]),t._v(\"的大小是固定的，无法自定义，而\"),n(\"code\",[t._v(\"Switch\")]),t._v(\"只能定义宽度，高度也是固定的。值得一提的是\"),n(\"code\",[t._v(\"Checkbox\")]),t._v(\"有一个属性\"),n(\"code\",[t._v(\"tristate\")]),t._v(\" ，表示是否为三态，其默认值为\"),n(\"code\",[t._v(\"false\")]),t._v(\" ，这时Checkbox有两种状态即“选中”和“不选中”，对应的value值为\"),n(\"code\",[t._v(\"true\")]),t._v(\"和\"),n(\"code\",[t._v(\"false\")]),t._v(\" 。如果\"),n(\"code\",[t._v(\"tristate\")]),t._v(\"值为\"),n(\"code\",[t._v(\"true\")]),t._v(\"时，value的值会增加一个状态\"),n(\"code\",[t._v(\"null\")]),t._v(\"，读者可以自行了解。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"总结\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),n(\"p\",[t._v(\"通过\"),n(\"code\",[t._v(\"Switch\")]),t._v(\"和\"),n(\"code\",[t._v(\"Checkbox\")]),t._v(\"我们可以看到，虽然它们本身是与状态（是否选中）关联的，但它们却不是自己来维护状态，而是需要父组件来管理状态，然后当用户点击时，再通过事件通知给父组件，这样是合理的，因为\"),n(\"code\",[t._v(\"Switch\")]),t._v(\"和\"),n(\"code\",[t._v(\"Checkbox\")]),t._v(\"是否选中本就和用户数据关联，而这些用户数据也不可能是它们的私有状态。我们在自定义组件时也应该思考一下哪种状态的管理方式最为合理。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/121.aef1325d.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[121],{612:function(t,a,s){t.exports=s.p+\"assets/img/3-4.8e70e140.png\"},891:function(t,a,s){\"use strict\";s.r(a);var n=s(45),e=Object(n.a)({},(function(){var t=this,a=t.$createElement,n=t._self._c||a;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_3-2-状态管理\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-2-状态管理\"}},[t._v(\"#\")]),t._v(\" 3.2 状态管理\")]),t._v(\" \"),n(\"p\",[t._v(\"响应式的编程框架中都会有一个永恒的主题——“状态(State)管理”，无论是在React/Vue（两者都是支持响应式编程的Web开发框架）还是Flutter中，他们讨论的问题和解决的思想都是一致的。所以，如果你对React/Vue的状态管理有了解，可以跳过本节。言归正传，我们想一个问题，\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"的状态应该被谁管理？Widget本身？父Widget？都会？还是另一个对象？答案是取决于实际情况！以下是管理状态的最常见的方法：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[t._v(\"Widget管理自己的状态。\")]),t._v(\" \"),n(\"li\",[t._v(\"Widget管理子Widget状态。\")]),t._v(\" \"),n(\"li\",[t._v(\"混合管理（父Widget和子Widget都管理状态）。\")])]),t._v(\" \"),n(\"p\",[t._v(\"如何决定使用哪种管理方法？下面是官方给出的一些原则可以帮助你做决定：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[t._v(\"如果状态是用户数据，如复选框的选中状态、滑块的位置，则该状态最好由父Widget管理。\")]),t._v(\" \"),n(\"li\",[t._v(\"如果状态是有关界面外观效果的，例如颜色、动画，那么状态最好由Widget本身来管理。\")]),t._v(\" \"),n(\"li\",[t._v(\"如果某一个状态是不同Widget共享的则最好由它们共同的父Widget管理。\")])]),t._v(\" \"),n(\"p\",[t._v(\"在Widget内部管理状态封装性会好一些，而在父Widget中管理会比较灵活。有些时候，如果不确定到底该怎么管理状态，那么推荐的首选是在父widget中管理（灵活会显得更重要一些）。\")]),t._v(\" \"),n(\"p\",[t._v(\"接下来，我们将通过创建三个简单示例TapboxA、TapboxB和TapboxC来说明管理状态的不同方式。 这些例子功能是相似的 ——创建一个盒子，当点击它时，盒子背景会在绿色与灰色之间切换。状态 \"),n(\"code\",[t._v(\"_active\")]),t._v(\"确定颜色：绿色为\"),n(\"code\",[t._v(\"true\")]),t._v(\" ，灰色为\"),n(\"code\",[t._v(\"false\")]),t._v(\"，如图3-4所示。\"),n(\"img\",{attrs:{src:s(612),alt:\"a large grey box with the text, 'Inactive'\"}})]),t._v(\" \"),n(\"p\",[t._v(\"下面的例子将使用\"),n(\"code\",[t._v(\"GestureDetector\")]),t._v(\"来识别点击事件，关于该\"),n(\"code\",[t._v(\"GestureDetector\")]),t._v(\"的详细内容我们将在后面“事件处理”一章中介绍。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"_3-2-1-widget管理自身状态\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-2-1-widget管理自身状态\"}},[t._v(\"#\")]),t._v(\" 3.2.1 Widget管理自身状态\")]),t._v(\" \"),n(\"p\",[t._v(\"_TapboxAState 类:\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[t._v(\"管理TapboxA的状态。\")]),t._v(\" \"),n(\"li\",[t._v(\"定义\"),n(\"code\",[t._v(\"_active\")]),t._v(\"：确定盒子的当前颜色的布尔值。\")]),t._v(\" \"),n(\"li\",[t._v(\"定义\"),n(\"code\",[t._v(\"_handleTap()\")]),t._v(\"函数，该函数在点击该盒子时更新\"),n(\"code\",[t._v(\"_active\")]),t._v(\"，并调用\"),n(\"code\",[t._v(\"setState()\")]),t._v(\"更新UI。\")]),t._v(\" \"),n(\"li\",[t._v(\"实现widget的所有交互式行为。\")])]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// TapboxA 管理自身状态.\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//------------------------- TapboxA ----------------------------------\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TapboxA\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TapboxA\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _TapboxAState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_TapboxAState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_TapboxAState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"TapboxA\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  bool _active \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_handleTap\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _active \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"_active\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"GestureDetector\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      onTap\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _handleTap\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            _active \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Active'\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Inactive'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"fontSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"32.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        decoration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"BoxDecoration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _active \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"lightGreen\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"700\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"600\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\")])])]),n(\"h3\",{attrs:{id:\"_3-2-2-父widget管理子widget的状态\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-2-2-父widget管理子widget的状态\"}},[t._v(\"#\")]),t._v(\" 3.2.2 父Widget管理子Widget的状态\")]),t._v(\" \"),n(\"p\",[t._v(\"对于父Widget来说，管理状态并告诉其子Widget何时更新通常是比较好的方式。 例如，\"),n(\"code\",[t._v(\"IconButton\")]),t._v(\"是一个图标按钮，但它是一个无状态的Widget，因为我们认为父Widget需要知道该按钮是否被点击来采取相应的处理。\")]),t._v(\" \"),n(\"p\",[t._v(\"在以下示例中，TapboxB通过回调将其状态导出到其父组件，状态由父组件管理，因此它的父组件为\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"。但是由于TapboxB不管理任何状态，所以\"),n(\"code\",[t._v(\"TapboxB\")]),t._v(\"为\"),n(\"code\",[t._v(\"StatelessWidget\")]),t._v(\"。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"ParentWidgetState\")]),t._v(\" 类:\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[t._v(\"为TapboxB 管理\"),n(\"code\",[t._v(\"_active\")]),t._v(\"状态。\")]),t._v(\" \"),n(\"li\",[t._v(\"实现\"),n(\"code\",[t._v(\"_handleTapboxChanged()\")]),t._v(\"，当盒子被点击时调用的方法。\")]),t._v(\" \"),n(\"li\",[t._v(\"当状态改变时，调用\"),n(\"code\",[t._v(\"setState()\")]),t._v(\"更新UI。\")])]),t._v(\" \"),n(\"p\",[t._v(\"TapboxB 类:\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[t._v(\"继承\"),n(\"code\",[t._v(\"StatelessWidget\")]),t._v(\"类，因为所有状态都由其父组件处理。\")]),t._v(\" \"),n(\"li\",[t._v(\"当检测到点击时，它会通知父组件。\")])]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// ParentWidget 为 TapboxB 管理状态.\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//------------------------ ParentWidget --------------------------------\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ParentWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _ParentWidgetState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_ParentWidgetState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_ParentWidgetState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ParentWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  bool _active \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_handleTapboxChanged\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"bool newValue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _active \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" newValue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TapboxB\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        active\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _active\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        onChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _handleTapboxChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//------------------------- TapboxB ----------------------------------\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TapboxB\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TapboxB\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"active\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" bool active\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" ValueChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"bool\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" onChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_handleTap\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onChanged\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"active\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"GestureDetector\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      onTap\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _handleTap\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            active \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Active'\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Inactive'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"fontSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"32.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        decoration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"BoxDecoration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" active \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"lightGreen\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"700\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"600\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"h3\",{attrs:{id:\"_3-2-3-混合状态管理\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-2-3-混合状态管理\"}},[t._v(\"#\")]),t._v(\" 3.2.3 混合状态管理\")]),t._v(\" \"),n(\"p\",[t._v(\"对于一些组件来说，混合管理的方式会非常有用。在这种情况下，组件自身管理一些内部状态，而父组件管理一些其他外部状态。\")]),t._v(\" \"),n(\"p\",[t._v(\"在下面TapboxC示例中，手指按下时，盒子的周围会出现一个深绿色的边框，抬起时，边框消失。点击完成后，盒子的颜色改变。 TapboxC将其\"),n(\"code\",[t._v(\"_active\")]),t._v(\"状态导出到其父组件中，但在内部管理其\"),n(\"code\",[t._v(\"_highlight\")]),t._v(\"状态。这个例子有两个状态对象\"),n(\"code\",[t._v(\"_ParentWidgetState\")]),t._v(\"和\"),n(\"code\",[t._v(\"_TapboxCState\")]),t._v(\"。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"_ParentWidgetStateC\")]),t._v(\"类:\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[t._v(\"管理\"),n(\"code\",[t._v(\"_active\")]),t._v(\" 状态。\")]),t._v(\" \"),n(\"li\",[t._v(\"实现 \"),n(\"code\",[t._v(\"_handleTapboxChanged()\")]),t._v(\" ，当盒子被点击时调用。\")]),t._v(\" \"),n(\"li\",[t._v(\"当点击盒子并且\"),n(\"code\",[t._v(\"_active\")]),t._v(\"状态改变时调用\"),n(\"code\",[t._v(\"setState()\")]),t._v(\"更新UI。\")])]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"_TapboxCState\")]),t._v(\" 对象:\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[t._v(\"管理\"),n(\"code\",[t._v(\"_highlight\")]),t._v(\" 状态。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"GestureDetector\")]),t._v(\"监听所有tap事件。当用户点下时，它添加高亮（深绿色边框）；当用户释放时，会移除高亮。\")]),t._v(\" \"),n(\"li\",[t._v(\"当按下、抬起、或者取消点击时更新\"),n(\"code\",[t._v(\"_highlight\")]),t._v(\"状态，调用\"),n(\"code\",[t._v(\"setState()\")]),t._v(\"更新UI。\")]),t._v(\" \"),n(\"li\",[t._v(\"当点击时，将状态的改变传递给父组件。\")])]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//---------------------------- ParentWidget ----------------------------\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ParentWidgetC\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _ParentWidgetCState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_ParentWidgetCState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_ParentWidgetCState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ParentWidgetC\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  bool _active \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_handleTapboxChanged\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"bool newValue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _active \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" newValue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TapboxC\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        active\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _active\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        onChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _handleTapboxChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//----------------------------- TapboxC ------------------------------\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TapboxC\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TapboxC\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"active\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" bool active\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" ValueChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"bool\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" onChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _TapboxCState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_TapboxCState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_TapboxCState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"TapboxC\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  bool _highlight \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_handleTapDown\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"TapDownDetails details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _highlight \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_handleTapUp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"TapUpDetails details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _highlight \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_handleTapCancel\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _highlight \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_handleTap\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onChanged\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"active\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 在按下时添加绿色边框，当抬起时，取消高亮  \")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"GestureDetector\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      onTapDown\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _handleTapDown\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 处理按下事件\")]),t._v(\"\\n      onTapUp\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _handleTapUp\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 处理抬起事件\")]),t._v(\"\\n      onTap\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _handleTap\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      onTapCancel\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _handleTapCancel\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"active \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Active'\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Inactive'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"fontSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"32.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        decoration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"BoxDecoration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"active \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"lightGreen\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"700\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"600\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          border\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _highlight\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Border\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"all\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"teal\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"700\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"10.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"另一种实现可能会将高亮状态导出到父组件，但同时保持\"),n(\"code\",[t._v(\"_active\")]),t._v(\"状态为内部状态，但如果你要将该TapBox给其它人使用，可能没有什么意义。 开发人员只会关心该框是否处于Active状态，而不在乎高亮显示是如何管理的，所以应该让TapBox内部处理这些细节。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"_3-2-4-全局状态管理\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-2-4-全局状态管理\"}},[t._v(\"#\")]),t._v(\" 3.2.4 全局状态管理\")]),t._v(\" \"),n(\"p\",[t._v(\"当应用中需要一些跨组件（包括跨路由）的状态需要同步时，上面介绍的方法便很难胜任了。比如，我们有一个设置页，里面可以设置应用的语言，我们为了让设置实时生效，我们期望在语言状态发生改变时，APP中依赖应用语言的组件能够重新build一下，但这些依赖应用语言的组件和设置页并不在一起，所以这种情况用上面的方法很难管理。这时，正确的做法是通过一个全局状态管理器来处理这种相距较远的组件之间的通信。目前主要有两种办法：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"实现一个全局的事件总线，将语言状态改变对应为一个事件，然后在APP中依赖应用语言的组件的\"),n(\"code\",[t._v(\"initState\")]),t._v(\" 方法中订阅语言改变的事件。当用户在设置页切换语言后，我们发布语言改变事件，而订阅了此事件的组件就会收到通知，收到通知后调用\"),n(\"code\",[t._v(\"setState(...)\")]),t._v(\"方法重新\"),n(\"code\",[t._v(\"build\")]),t._v(\"一下自身即可。\")]),t._v(\" \"),n(\"li\",[t._v(\"使用一些专门用于状态管理的包，如Provider、Redux，读者可以在pub上查看其详细信息。\")])]),t._v(\" \"),n(\"p\",[t._v('本书将在\"功能型组件\"一章中介绍Provider包的实现原理及用法，同时也将会在\"事件处理与通知\"一章中实现一个全局事件总线，读者有需要可以直接翻看。')])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/122.59b7f284.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[122],{622:function(t,s,n){t.exports=n.p+\"assets/img/4-5.67110f64.png\"},895:function(t,s,n){\"use strict\";n.r(s);var a=n(45),e=Object(a.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_4-3-弹性布局-flex\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_4-3-弹性布局-flex\"}},[t._v(\"#\")]),t._v(\" 4.3 弹性布局（Flex）\")]),t._v(\" \"),a(\"p\",[t._v(\"弹性布局允许子组件按照一定比例来分配父容器空间。弹性布局的概念在其它UI系统中也都存在，如H5中的弹性盒子布局，Android中的\"),a(\"code\",[t._v(\"FlexboxLayout\")]),t._v(\"等。Flutter中的弹性布局主要通过\"),a(\"code\",[t._v(\"Flex\")]),t._v(\"和\"),a(\"code\",[t._v(\"Expanded\")]),t._v(\"来配合实现。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"flex\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#flex\"}},[t._v(\"#\")]),t._v(\" Flex\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"Flex\")]),t._v(\"组件可以沿着水平或垂直方向排列子组件，如果你知道主轴方向，使用\"),a(\"code\",[t._v(\"Row\")]),t._v(\"或\"),a(\"code\",[t._v(\"Column\")]),t._v(\"会方便一些，因为\"),a(\"code\",[t._v(\"Row\")]),t._v(\"和\"),a(\"code\",[t._v(\"Column\")]),t._v(\"都继承自\"),a(\"code\",[t._v(\"Flex\")]),t._v(\"，参数基本相同，所以能使用Flex的地方基本上都可以使用\"),a(\"code\",[t._v(\"Row\")]),t._v(\"或\"),a(\"code\",[t._v(\"Column\")]),t._v(\"。\"),a(\"code\",[t._v(\"Flex\")]),t._v(\"本身功能是很强大的，它也可以和\"),a(\"code\",[t._v(\"Expanded\")]),t._v(\"组件配合实现弹性布局。接下来我们只讨论\"),a(\"code\",[t._v(\"Flex\")]),t._v(\"和弹性布局相关的属性(其它属性已经在介绍\"),a(\"code\",[t._v(\"Row\")]),t._v(\"和\"),a(\"code\",[t._v(\"Column\")]),t._v(\"时介绍过了)。\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Flex\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"direction\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//弹性布局的方向, Row默认为水平方向，Column默认为垂直方向\")]),t._v(\"\\n  List\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" children \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[a(\"code\",[t._v(\"Flex\")]),t._v(\"继承自\"),a(\"code\",[t._v(\"MultiChildRenderObjectWidget\")]),t._v(\"，对应的\"),a(\"code\",[t._v(\"RenderObject\")]),t._v(\"为\"),a(\"code\",[t._v(\"RenderFlex\")]),t._v(\"，\"),a(\"code\",[t._v(\"RenderFlex\")]),t._v(\"中实现了其布局算法。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"expanded\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#expanded\"}},[t._v(\"#\")]),t._v(\" Expanded\")]),t._v(\" \"),a(\"p\",[t._v(\"可以按比例“扩伸” \"),a(\"code\",[t._v(\"Row\")]),t._v(\"、\"),a(\"code\",[t._v(\"Column\")]),t._v(\"和\"),a(\"code\",[t._v(\"Flex\")]),t._v(\"子组件所占用的空间。\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Expanded\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  int flex \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" Widget child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[a(\"code\",[t._v(\"flex\")]),t._v(\"参数为弹性系数，如果为0或\"),a(\"code\",[t._v(\"null\")]),t._v(\"，则\"),a(\"code\",[t._v(\"child\")]),t._v(\"是没有弹性的，即不会被扩伸占用的空间。如果大于0，所有的\"),a(\"code\",[t._v(\"Expanded\")]),t._v(\"按照其flex的比例来分割主轴的全部空闲空间。下面我们看一个例子：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FlexLayoutTestRoute\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//Flex的两个子widget按1：2来占据水平空间  \")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Flex\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          direction\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Axis\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"horizontal\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Expanded\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              flex\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"30.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Expanded\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              flex\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"30.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"green\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"only\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"top\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//Flex的三个子widget，在垂直方向按2：1：1来占用100像素的空间  \")]),t._v(\"\\n            child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Flex\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              direction\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Axis\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"vertical\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Expanded\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  flex\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"30.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Spacer\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  flex\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Expanded\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  flex\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"30.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"green\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"运行效果如图4-5所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(622),alt:\"弹性布局\"}})]),t._v(\" \"),a(\"p\",[t._v(\"示例中的\"),a(\"code\",[t._v(\"Spacer\")]),t._v(\"的功能是占用指定比例的空间，实际上它只是\"),a(\"code\",[t._v(\"Expanded\")]),t._v(\"的一个包装类，\"),a(\"code\",[t._v(\"Spacer\")]),t._v(\"的源码如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Spacer\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Spacer\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"Key key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"flex \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"flex \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"flex \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" int flex\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Expanded\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      flex\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" flex\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" SizedBox\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"shrink\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"h3\",{attrs:{id:\"小结\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#小结\"}},[t._v(\"#\")]),t._v(\" 小结\")]),t._v(\" \"),a(\"p\",[t._v(\"弹性布局比较简单，唯一需要注意的就是\"),a(\"code\",[t._v(\"Row\")]),t._v(\"、\"),a(\"code\",[t._v(\"Column\")]),t._v(\"以及\"),a(\"code\",[t._v(\"Flex\")]),t._v(\"的关系。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/123.b83bac47.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[123],{643:function(t,a,n){t.exports=n.p+\"assets/img/5-9.47017753.png\"},903:function(t,a,n){\"use strict\";n.r(a);var s=n(45),e=Object(s.a)({},(function(){var t=this,a=t.$createElement,s=t._self._c||a;return s(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[s(\"h1\",{attrs:{id:\"_5-3-装饰容器decoratedbox\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_5-3-装饰容器decoratedbox\"}},[t._v(\"#\")]),t._v(\" 5.3 装饰容器DecoratedBox\")]),t._v(\" \"),s(\"p\",[s(\"code\",[t._v(\"DecoratedBox\")]),t._v(\"可以在其子组件绘制前(或后)绘制一些装饰（Decoration），如背景、边框、渐变等。\"),s(\"code\",[t._v(\"DecoratedBox\")]),t._v(\"定义如下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecoratedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  Decoration decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  DecorationPosition position \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" DecorationPosition\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"background\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  Widget child\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),s(\"ul\",[s(\"li\",[s(\"code\",[t._v(\"decoration\")]),t._v(\"：代表将要绘制的装饰，它的类型为\"),s(\"code\",[t._v(\"Decoration\")]),t._v(\"。\"),s(\"code\",[t._v(\"Decoration\")]),t._v(\"是一个抽象类，它定义了一个接口 \"),s(\"code\",[t._v(\"createBoxPainter()\")]),t._v(\"，子类的主要职责是需要通过实现它来创建一个画笔，该画笔用于绘制装饰。\")]),t._v(\" \"),s(\"li\",[s(\"code\",[t._v(\"position\")]),t._v(\"：此属性决定在哪里绘制\"),s(\"code\",[t._v(\"Decoration\")]),t._v(\"，它接收\"),s(\"code\",[t._v(\"DecorationPosition\")]),t._v(\"的枚举类型，该枚举类有两个值：\\n\"),s(\"ul\",[s(\"li\",[s(\"code\",[t._v(\"background\")]),t._v(\"：在子组件之后绘制，即背景装饰。\")]),t._v(\" \"),s(\"li\",[s(\"code\",[t._v(\"foreground\")]),t._v(\"：在子组件之上绘制，即前景。\")])])])]),t._v(\" \"),s(\"h4\",{attrs:{id:\"boxdecoration\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#boxdecoration\"}},[t._v(\"#\")]),t._v(\" BoxDecoration\")]),t._v(\" \"),s(\"p\",[t._v(\"我们通常会直接使用\"),s(\"code\",[t._v(\"BoxDecoration\")]),t._v(\"类，它是一个Decoration的子类，实现了常用的装饰元素的绘制。\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  Color color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//颜色\")]),t._v(\"\\n  DecorationImage image\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//图片\")]),t._v(\"\\n  BoxBorder border\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//边框\")]),t._v(\"\\n  BorderRadiusGeometry borderRadius\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//圆角\")]),t._v(\"\\n  List\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"BoxShadow\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" boxShadow\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//阴影,可以指定多个\")]),t._v(\"\\n  Gradient gradient\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//渐变\")]),t._v(\"\\n  BlendMode backgroundBlendMode\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//背景混合模式\")]),t._v(\"\\n  BoxShape shape \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" BoxShape\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"rectangle\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//形状\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"各个属性名都是自解释的，详情读者可以查看API文档。下面我们实现一个带阴影的背景色渐变的按钮：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecoratedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      gradient\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"LinearGradient\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"orange\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"700\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//背景渐变\")]),t._v(\"\\n      borderRadius\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BorderRadius\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"circular\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3.0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//3像素圆角\")]),t._v(\"\\n      boxShadow\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//阴影\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxShadow\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"black54\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            offset\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2.0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2.0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            blurRadius\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"4.0\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"padding\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"symmetric\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"horizontal\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" vertical\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"18.0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Login\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" style\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"运行后效果如图5-9所示：\")]),t._v(\" \"),s(\"p\",[s(\"img\",{attrs:{src:n(643),alt:\"图5-9\"}})]),t._v(\" \"),s(\"p\",[t._v(\"怎么样，通过\"),s(\"code\",[t._v(\"BoxDecoration\")]),t._v(\"我们实现了一个渐变按钮的外观，但此示例还不是一个标准的按钮，因为它还不能响应点击事件，我们将在后面“自定义组件”一章中实现一个完整功能的\"),s(\"code\",[t._v(\"GradientButton\")]),t._v(\"。另外，上面的例子中使用了\"),s(\"code\",[t._v(\"LinearGradient\")]),t._v(\"类，它用于定义线性渐变的类，Flutter中还提供了其它渐变配置类，如\"),s(\"code\",[t._v(\"RadialGradient\")]),t._v(\"、\"),s(\"code\",[t._v(\"SweepGradient\")]),t._v(\"，读者若有需要可以自行查看API文档。\")])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/124.7a950a7c.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[124],{644:function(t,s,n){t.exports=n.p+\"assets/img/5-1.239dadc0.png\"},905:function(t,s,n){\"use strict\";n.r(s);var a=n(45),e=Object(a.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h2\",{attrs:{id:\"_5-1-填充-padding\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_5-1-填充-padding\"}},[t._v(\"#\")]),t._v(\" 5.1 填充（Padding）\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"Padding\")]),t._v(\"可以给其子节点添加填充（留白），和边距效果类似。我们在前面很多示例中都已经使用过它了，现在来看看它的定义：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  EdgeInsetsGeometry padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  Widget child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[a(\"code\",[t._v(\"EdgeInsetsGeometry\")]),t._v(\"是一个抽象类，开发中，我们一般都使用\"),a(\"code\",[t._v(\"EdgeInsets\")]),t._v(\"类，它是\"),a(\"code\",[t._v(\"EdgeInsetsGeometry\")]),t._v(\"的一个子类，定义了一些设置填充的便捷方法。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"edgeinsets\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#edgeinsets\"}},[t._v(\"#\")]),t._v(\" EdgeInsets\")]),t._v(\" \"),a(\"p\",[t._v(\"我们看看\"),a(\"code\",[t._v(\"EdgeInsets\")]),t._v(\"提供的便捷方法：\")]),t._v(\" \"),a(\"ul\",[a(\"li\",[a(\"code\",[t._v(\"fromLTRB(double left, double top, double right, double bottom)\")]),t._v(\"：分别指定四个方向的填充。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"all(double value)\")]),t._v(\" : 所有方向均使用相同数值的填充。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"only({left, top, right ,bottom })\")]),t._v(\"：可以设置具体某个方向的填充(可以同时指定多个方向)。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"symmetric({ vertical, horizontal })\")]),t._v(\"：用于设置对称方向的填充，\"),a(\"code\",[t._v(\"vertical\")]),t._v(\"指\"),a(\"code\",[t._v(\"top\")]),t._v(\"和\"),a(\"code\",[t._v(\"bottom\")]),t._v(\"，\"),a(\"code\",[t._v(\"horizontal\")]),t._v(\"指\"),a(\"code\",[t._v(\"left\")]),t._v(\"和\"),a(\"code\",[t._v(\"right\")]),t._v(\"。\")])]),t._v(\" \"),a(\"h3\",{attrs:{id:\"示例\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),a(\"p\",[t._v(\"下面的示例主要展示了\"),a(\"code\",[t._v(\"EdgeInsets\")]),t._v(\"的不同用法，比较简单，源码如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"PaddingTestRoute\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//上下左右各添加16像素补白\")]),t._v(\"\\n      padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//显式指定对齐方式为左对齐，排除对齐干扰\")]),t._v(\"\\n        crossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" CrossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//左边添加8像素补白\")]),t._v(\"\\n            padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"only\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"left\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"8.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hello world\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//上下各添加8像素补白\")]),t._v(\"\\n            padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"symmetric\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"vertical\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"8.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"I am Jack\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 分别指定四个方向的补白\")]),t._v(\"\\n            padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fromLTRB\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Your friend\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"运行效果如图5-1所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(644),alt:\"图5-1\"}})])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/125.275c37b1.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[125],{672:function(t,s,a){t.exports=a.p+\"assets/img/6-1.a5c8558b.png\"},914:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_6-2-singlechildscrollview\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_6-2-singlechildscrollview\"}},[t._v(\"#\")]),t._v(\" 6.2 SingleChildScrollView\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"SingleChildScrollView\")]),t._v(\"类似于Android中的\"),n(\"code\",[t._v(\"ScrollView\")]),t._v(\"，它只能接收一个子组件。定义如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SingleChildScrollView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"scrollDirection \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Axis\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"vertical\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//滚动方向，默认是垂直方向\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"reverse \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n  bool primary\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"physics\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"除了上一节我们介绍过的可滚动组件的通用属性外，我们重点看一下\"),n(\"code\",[t._v(\"reverse\")]),t._v(\"和\"),n(\"code\",[t._v(\"primary\")]),t._v(\"两个属性：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"reverse\")]),t._v(\"：该属性API文档解释是：是否按照阅读方向相反的方向滑动，如：\"),n(\"code\",[t._v(\"scrollDirection\")]),t._v(\"值为\"),n(\"code\",[t._v(\"Axis.horizontal\")]),t._v(\"，如果阅读方向是从左到右(取决于语言环境，阿拉伯语就是从右到左)。\"),n(\"code\",[t._v(\"reverse\")]),t._v(\"为\"),n(\"code\",[t._v(\"true\")]),t._v(\"时，那么滑动方向就是从右往左。其实此属性本质上是决定可滚动组件的初始滚动位置是在“头”还是“尾”，取\"),n(\"code\",[t._v(\"false\")]),t._v(\"时，初始滚动位置在“头”，反之则在“尾”，读者可以自己试验。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"primary\")]),t._v(\"：指是否使用widget树中默认的\"),n(\"code\",[t._v(\"PrimaryScrollController\")]),t._v(\"；当滑动方向为垂直方向（\"),n(\"code\",[t._v(\"scrollDirection\")]),t._v(\"值为\"),n(\"code\",[t._v(\"Axis.vertical\")]),t._v(\"）并且没有指定\"),n(\"code\",[t._v(\"controller\")]),t._v(\"时，\"),n(\"code\",[t._v(\"primary\")]),t._v(\"默认为\"),n(\"code\",[t._v(\"true\")]),t._v(\".\")])]),t._v(\" \"),n(\"p\",[t._v(\"需要注意的是，通常\"),n(\"code\",[t._v(\"SingleChildScrollView\")]),t._v(\"只应在期望的内容不会超过屏幕太多时使用，这是因为\"),n(\"code\",[t._v(\"SingleChildScrollView\")]),t._v(\"不支持基于Sliver的延迟实例化模型，所以如果预计视口可能包含超出屏幕尺寸太多的内容时，那么使用\"),n(\"code\",[t._v(\"SingleChildScrollView\")]),t._v(\"将会非常昂贵（性能差），此时应该使用一些支持Sliver延迟加载的可滚动组件，如\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"示例\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),n(\"p\",[t._v(\"下面是一个将大写字母A-Z沿垂直方向显示的例子，由于垂直方向空间会超过屏幕视口高度，所以我们使用\"),n(\"code\",[t._v(\"SingleChildScrollView\")]),t._v(\"：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"SingleChildScrollViewTestRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    String str \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scrollbar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 显示进度条\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SingleChildScrollView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//动态创建一个List<Widget>  \")]),t._v(\"\\n            children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" str\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"split\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//每一个字母都用一个Text显示,字体为原来的两倍\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"map\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"c\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"c\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" textScaleFactor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toList\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图6-1所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(672),alt:\"图6-1\"}})])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/126.a62ac904.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[126],{675:function(t,a,s){t.exports=s.p+\"assets/img/7-1.fc1ee2fb.png\"},917:function(t,a,s){\"use strict\";s.r(a);var e=s(45),n=Object(e.a)({},(function(){var t=this,a=t.$createElement,e=t._self._c||a;return e(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[e(\"h1\",{attrs:{id:\"_7-2-数据共享-inheritedwidget\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_7-2-数据共享-inheritedwidget\"}},[t._v(\"#\")]),t._v(\" 7.2 数据共享（InheritedWidget）\")]),t._v(\" \"),e(\"p\",[e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"是Flutter中非常重要的一个功能型组件，它提供了一种数据在widget树中从上到下传递、共享的方式，比如我们在应用的根widget中通过\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"共享了一个数据，那么我们便可以在任意子widget中来获取该共享的数据！这个特性在一些需要在widget树中共享数据的场景中非常方便！如Flutter SDK中正是通过InheritedWidget来共享应用主题（\"),e(\"code\",[t._v(\"Theme\")]),t._v(\"）和Locale (当前语言环境)信息的。\")]),t._v(\" \"),e(\"blockquote\",[e(\"p\",[e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"和React中的context功能类似，和逐级传递数据相比，它们能实现组件跨级传递数据。\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"的在widget树中数据传递方向是从上到下的，这和通知\"),e(\"code\",[t._v(\"Notification\")]),t._v(\"（将在下一章中介绍）的传递方向正好相反。\")])]),t._v(\" \"),e(\"h3\",{attrs:{id:\"didchangedependencies\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#didchangedependencies\"}},[t._v(\"#\")]),t._v(\" didChangeDependencies\")]),t._v(\" \"),e(\"p\",[t._v(\"在之前介绍\"),e(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"时，我们提到\"),e(\"code\",[t._v(\"State\")]),t._v(\"对象有一个\"),e(\"code\",[t._v(\"didChangeDependencies\")]),t._v(\"回调，它会在“依赖”发生变化时被Flutter Framework调用。而这个“依赖”指的就是子widget是否使用了父widget中\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"的数据！如果使用了，则代表子widget依赖有依赖\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"；如果没有使用则代表没有依赖。这种机制可以使子组件在所依赖的\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"变化时来更新自身！比如当主题、locale(语言)等发生变化时，依赖其的子widget的\"),e(\"code\",[t._v(\"didChangeDependencies\")]),t._v(\"方法将会被调用。\")]),t._v(\" \"),e(\"p\",[t._v(\"下面我们看一下之前“计数器”示例应用程序的\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"版本。需要说明的是，本示例主要是为了演示\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"的功能特性，并不是计数器的推荐实现方式。\")]),t._v(\" \"),e(\"p\",[t._v(\"首先，我们通过继承\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"，将当前计数器点击次数保存在\"),e(\"code\",[t._v(\"ShareDataWidget\")]),t._v(\"的\"),e(\"code\",[t._v(\"data\")]),t._v(\"属性中：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ShareDataWidget\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"InheritedWidget\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ShareDataWidget\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    Widget child\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" child\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" int data\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//需要在子树中共享的数据，保存点击次数\")]),t._v(\"\\n    \\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//定义一个便捷方法，方便子树中的widget获取共享数据  \")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" ShareDataWidget \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dependOnInheritedWidgetOfExactType\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ShareDataWidget\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//该回调决定当data发生变化时，是否通知子树中依赖data的Widget  \")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  bool \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"updateShouldNotify\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ShareDataWidget old\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//如果返回true，则子树中依赖(build函数中有调用)本widget\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//的子widget的`state.didChangeDependencies`会被调用\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" old\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" data\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"然后我们实现一个子组件\"),e(\"code\",[t._v(\"_TestWidget\")]),t._v(\"，在其\"),e(\"code\",[t._v(\"build\")]),t._v(\"方法中引用\"),e(\"code\",[t._v(\"ShareDataWidget\")]),t._v(\"中的数据。同时，在其\"),e(\"code\",[t._v(\"didChangeDependencies()\")]),t._v(\" 回调中打印日志：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_TestWidget\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  __TestWidgetState \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"__TestWidgetState\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"__TestWidgetState\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"_TestWidget\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//使用InheritedWidget中的共享数据\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ShareDataWidget\\n        \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n        \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\\n        \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didChangeDependencies\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didChangeDependencies\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//父或祖先widget中的InheritedWidget改变(updateShouldNotify返回true)时会被调用。\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//如果build中没有依赖InheritedWidget，则此回调不会被调用。\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Dependencies change\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"最后，我们创建一个按钮，每点击一次，就将\"),e(\"code\",[t._v(\"ShareDataWidget\")]),t._v(\"的值自增：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"InheritedWidgetTestRoute\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _InheritedWidgetTestRouteState \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_InheritedWidgetTestRouteState\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_InheritedWidgetTestRouteState\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"InheritedWidgetTestRoute\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  int count \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\"  \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ShareDataWidget\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//使用ShareDataWidget\")]),t._v(\"\\n        data\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" count\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          mainAxisAlignment\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          children\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              padding\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"only\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"bottom\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              child\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_TestWidget\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//子widget中依赖ShareDataWidget\")]),t._v(\"\\n            \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              child\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Increment\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//每点击一次，将count自增，然后重新build,ShareDataWidget的data将被更新  \")]),t._v(\"\\n              onPressed\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"++\")]),t._v(\"count\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n          \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"运行后界面如图7-1所示：\")]),t._v(\" \"),e(\"p\",[e(\"img\",{attrs:{src:s(675),alt:\"图7-1\"}})]),t._v(\" \"),e(\"p\",[t._v(\"每点击一次按钮，计数器就会自增，控制台就会打印一句日志：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language- extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[e(\"code\",[t._v(\"I/flutter ( 8513): Dependencies change\\n\")])])]),e(\"p\",[t._v(\"可见依赖发生变化后，其\"),e(\"code\",[t._v(\"didChangeDependencies()\")]),t._v(\"会被调用。但是读者要注意，\"),e(\"strong\",[t._v(\"如果_TestWidget的build方法中没有使用ShareDataWidget的数据，那么它的\"),e(\"code\",[t._v(\"didChangeDependencies()\")]),t._v(\"将不会被调用，因为它并没有依赖ShareDataWidget\")]),t._v(\"。例如，我们将\"),e(\"code\",[t._v(\"__TestWidgetState\")]),t._v(\"代码改为下面这样，\"),e(\"code\",[t._v(\"didChangeDependencies()\")]),t._v(\"将不会被调用:\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"__TestWidgetState\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"_TestWidget\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 使用InheritedWidget中的共享数据\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//    return Text(ShareDataWidget\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//        .of(context)\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//        .data\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//        .toString());\")]),t._v(\"\\n     \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"text\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didChangeDependencies\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didChangeDependencies\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// build方法中没有依赖InheritedWidget，此回调不会被调用。\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Dependencies change\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"上面的代码中，我们将\"),e(\"code\",[t._v(\"build()\")]),t._v(\"方法中依赖\"),e(\"code\",[t._v(\"ShareDataWidget\")]),t._v(\"的代码注释掉了，然后返回一个固定\"),e(\"code\",[t._v(\"Text\")]),t._v(\"，这样一来，当点击Increment按钮后，\"),e(\"code\",[t._v(\"ShareDataWidget\")]),t._v(\"的\"),e(\"code\",[t._v(\"data\")]),t._v(\"虽然发生变化，但由于\"),e(\"code\",[t._v(\"__TestWidgetState\")]),t._v(\"并未依赖\"),e(\"code\",[t._v(\"ShareDataWidget\")]),t._v(\"，所以\"),e(\"code\",[t._v(\"__TestWidgetState\")]),t._v(\"的\"),e(\"code\",[t._v(\"didChangeDependencies\")]),t._v(\"方法不会被调用。其实，这个机制很好理解，因为在数据发生变化时只对使用该数据的Widget更新是合理并且性能友好的。\")]),t._v(\" \"),e(\"blockquote\",[e(\"p\",[t._v(\"思考题：Flutter framework是怎么知道子widget有没有依赖InheritedWidget的？\")])]),t._v(\" \"),e(\"h4\",{attrs:{id:\"应该在didchangedependencies-中做什么\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#应该在didchangedependencies-中做什么\"}},[t._v(\"#\")]),t._v(\" 应该在didChangeDependencies()中做什么？\")]),t._v(\" \"),e(\"p\",[t._v(\"一般来说，子widget很少会重写此方法，因为在依赖改变后framework也都会调用\"),e(\"code\",[t._v(\"build()\")]),t._v(\"方法。但是，如果你需要在依赖改变后执行一些昂贵的操作，比如网络请求，这时最好的方式就是在此方法中执行，这样可以避免每次\"),e(\"code\",[t._v(\"build()\")]),t._v(\"都执行这些昂贵操作。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"深入了解inheritedwidget\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#深入了解inheritedwidget\"}},[t._v(\"#\")]),t._v(\" 深入了解InheritedWidget\")]),t._v(\" \"),e(\"p\",[t._v(\"现在来思考一下，如果我们只想在\"),e(\"code\",[t._v(\"__TestWidgetState\")]),t._v(\"中引用\"),e(\"code\",[t._v(\"ShareDataWidget\")]),t._v(\"数据，但却不希望在\"),e(\"code\",[t._v(\"ShareDataWidget\")]),t._v(\"发生变化时调用\"),e(\"code\",[t._v(\"__TestWidgetState\")]),t._v(\"的\"),e(\"code\",[t._v(\"didChangeDependencies()\")]),t._v(\"方法应该怎么办？其实答案很简单，我们只需要将\"),e(\"code\",[t._v(\"ShareDataWidget.of()\")]),t._v(\"的实现改一下即可：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//定义一个便捷方法，方便子树中的widget获取共享数据\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" ShareDataWidget \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//return context.dependOnInheritedWidgetOfExactType<ShareDataWidget>();\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"getElementForInheritedWidgetOfExactType\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ShareDataWidget\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"widget\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"唯一的改动就是获取\"),e(\"code\",[t._v(\"ShareDataWidget\")]),t._v(\"对象的方式，把\"),e(\"code\",[t._v(\"dependOnInheritedWidgetOfExactType()\")]),t._v(\"方法换成了\"),e(\"code\",[t._v(\"context.getElementForInheritedWidgetOfExactType<ShareDataWidget>().widget\")]),t._v(\"，那么他们到底有什么区别呢，我们看一下这两个方法的源码（实现代码在\"),e(\"code\",[t._v(\"Element\")]),t._v(\"类中，\"),e(\"code\",[t._v(\"Context\")]),t._v(\"和\"),e(\"code\",[t._v(\"Element\")]),t._v(\"的关系我们将在后面专门介绍）：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\nInheritedElement getElementForInheritedWidgetOfExactType\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"InheritedWidget\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_debugCheckStateIsActiveForAncestorLookup\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" InheritedElement ancestor \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _inheritedWidgets \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _inheritedWidgets\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"T\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" ancestor\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\nInheritedWidget \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dependOnInheritedWidgetOfExactType\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" Object aspect \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_debugCheckStateIsActiveForAncestorLookup\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" InheritedElement ancestor \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _inheritedWidgets \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _inheritedWidgets\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"T\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//多出的部分\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ancestor \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ancestor \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"is\")]),t._v(\" InheritedElement\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dependOnInheritedElement\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ancestor\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" aspect\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" aspect\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"as\")]),t._v(\" T\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  _hadUnsatisfiedDependencies \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"我们可以看到，\"),e(\"code\",[t._v(\"dependOnInheritedWidgetOfExactType()\")]),t._v(\" 比 \"),e(\"code\",[t._v(\"getElementForInheritedWidgetOfExactType()\")]),t._v(\"多调了\"),e(\"code\",[t._v(\"dependOnInheritedElement\")]),t._v(\"方法，\"),e(\"code\",[t._v(\"dependOnInheritedElement\")]),t._v(\"源码如下：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[t._v(\"  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  InheritedWidget \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dependOnInheritedElement\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"InheritedElement ancestor\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" Object aspect \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ancestor \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    _dependencies \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" HashSet\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"InheritedElement\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    _dependencies\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ancestor\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    ancestor\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"updateDependencies\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" aspect\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" ancestor\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"widget\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"可以看到\"),e(\"code\",[t._v(\"dependOnInheritedElement\")]),t._v(\"方法中主要是注册了依赖关系！看到这里也就清晰了，\"),e(\"strong\",[t._v(\"调用\"),e(\"code\",[t._v(\"dependOnInheritedWidgetOfExactType()\")]),t._v(\" 和 \"),e(\"code\",[t._v(\"getElementForInheritedWidgetOfExactType()\")]),t._v(\"的区别就是前者会注册依赖关系，而后者不会\")]),t._v(\"，所以在调用\"),e(\"code\",[t._v(\"dependOnInheritedWidgetOfExactType()\")]),t._v(\"时，\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"和依赖它的子孙组件关系便完成了注册，之后当\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"发生变化时，就会更新依赖它的子孙组件，也就是会调这些子孙组件的\"),e(\"code\",[t._v(\"didChangeDependencies()\")]),t._v(\"方法和\"),e(\"code\",[t._v(\"build()\")]),t._v(\"方法。而当调用的是 \"),e(\"code\",[t._v(\"getElementForInheritedWidgetOfExactType()\")]),t._v(\"时，由于没有注册依赖关系，所以之后当\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"发生变化时，就不会更新相应的子孙Widget。\")]),t._v(\" \"),e(\"p\",[t._v(\"注意，如果将上面示例中\"),e(\"code\",[t._v(\"ShareDataWidget.of()\")]),t._v(\"方法实现改成调用\"),e(\"code\",[t._v(\"getElementForInheritedWidgetOfExactType()\")]),t._v('，运行示例后，点击\"Increment\"按钮，会发现'),e(\"code\",[t._v(\"__TestWidgetState\")]),t._v(\"的\"),e(\"code\",[t._v(\"didChangeDependencies()\")]),t._v(\"方法确实不会再被调用，但是其\"),e(\"code\",[t._v(\"build()\")]),t._v('仍然会被调用！造成这个的原因其实是，点击\"Increment\"按钮后，会调用'),e(\"code\",[t._v(\"_InheritedWidgetTestRouteState\")]),t._v(\"的\"),e(\"code\",[t._v(\"setState()\")]),t._v(\"方法，此时会重新构建整个页面，由于示例中，\"),e(\"code\",[t._v(\"__TestWidget\")]),t._v(\" 并没有任何缓存，所以它也都会被重新构建，所以也会调用\"),e(\"code\",[t._v(\"build()\")]),t._v(\"方法。\")]),t._v(\" \"),e(\"p\",[t._v(\"那么，现在就带来了一个问题：实际上，我们只想更新子树中依赖了\"),e(\"code\",[t._v(\"ShareDataWidget\")]),t._v(\"的组件，而现在只要调用\"),e(\"code\",[t._v(\"_InheritedWidgetTestRouteState\")]),t._v(\"的\"),e(\"code\",[t._v(\"setState()\")]),t._v(\"方法，所有子节点都会被重新build，这很没必要，那么有什么办法可以避免呢？答案是缓存！一个简单的做法就是通过封装一个\"),e(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"，将子Widget树缓存起来，具体做法下一节我们将通过实现一个\"),e(\"code\",[t._v(\"Provider\")]),t._v(\" Widget 来演示如何缓存，以及如何利用\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\" 来实现Flutter全局状态共享。\")])])}),[],!1,null,null,null);a.default=n.exports}}]);"
  },
  {
    "path": "docs/assets/js/127.76aaf7da.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[127],{686:function(t,n,s){t.exports=s.p+\"assets/img/8-1.fd0e65f1.png\"},924:function(t,n,s){\"use strict\";s.r(n);var a=s(45),e=Object(a.a)({},(function(){var t=this,n=t.$createElement,a=t._self._c||n;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_8-1-原始指针事件处理\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_8-1-原始指针事件处理\"}},[t._v(\"#\")]),t._v(\" 8.1 原始指针事件处理\")]),t._v(\" \"),a(\"p\",[t._v(\"本节先来介绍一下原始指针事件(Pointer Event，在移动设备上通常为触摸事件)，下一节再介绍手势处理。\")]),t._v(\" \"),a(\"p\",[t._v(\"在移动端，各个平台或UI系统的原始指针事件模型基本都是一致，即：一次完整的事件分为三个阶段：手指按下、手指移动、和手指抬起，而更高级别的手势（如点击、双击、拖动等）都是基于这些原始事件的。\")]),t._v(\" \"),a(\"p\",[t._v(\"当指针按下时，Flutter会对应用程序执行\"),a(\"strong\",[t._v(\"命中测试(Hit Test)\")]),t._v(\"，以确定指针与屏幕接触的位置存在哪些组件（widget）， 指针按下事件（以及该指针的后续事件）然后被分发到由命中测试发现的最内部的组件，然后从那里开始，事件会在组件树中向上冒泡，这些事件会从最内部的组件被分发到组件树根的路径上的所有组件，这和Web开发中浏览器的\"),a(\"strong\",[t._v(\"事件冒泡\")]),t._v(\"机制相似， 但是Flutter中没有机制取消或停止“冒泡”过程，而浏览器的冒泡是可以停止的。注意，只有通过命中测试的组件才能触发事件。\")]),t._v(\" \"),a(\"p\",[t._v(\"Flutter中可以使用\"),a(\"code\",[t._v(\"Listener\")]),t._v(\"来监听原始触摸事件，按照本书对组件的分类，则\"),a(\"code\",[t._v(\"Listener\")]),t._v(\"也是一个功能性组件。下面是\"),a(\"code\",[t._v(\"Listener\")]),t._v(\"的构造函数定义：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Listener\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  Key key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onPointerDown\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//手指按下回调\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onPointerMove\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//手指移动回调\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onPointerUp\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//手指抬起回调\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onPointerCancel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//触摸事件取消回调\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"behavior \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" HitTestBehavior\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"deferToChild\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//在命中测试期间如何表现\")]),t._v(\"\\n  Widget child\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"我们先看一个示例，后面再单独讨论一下\"),a(\"code\",[t._v(\"behavior\")]),t._v(\"属性。\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//定义一个状态，保存当前指针位置\")]),t._v(\"\\nPointerEvent _event\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Listener\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"150.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_event\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"style\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  onPointerDown\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"PointerDownEvent event\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"_event\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"event\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  onPointerMove\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"PointerMoveEvent event\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"_event\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"event\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  onPointerUp\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"PointerUpEvent event\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"_event\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"event\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"运行后效果如图8-1所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:s(686),alt:\"图8-1\"}})]),t._v(\" \"),a(\"p\",[t._v(\"手指在蓝色矩形区域内移动即可看到当前指针偏移，当触发指针事件时，参数\"),a(\"code\",[t._v(\"PointerDownEvent\")]),t._v(\"、\"),a(\"code\",[t._v(\"PointerMoveEvent\")]),t._v(\"、\"),a(\"code\",[t._v(\"PointerUpEvent\")]),t._v(\"都是\"),a(\"code\",[t._v(\"PointerEvent\")]),t._v(\"的一个子类，\"),a(\"code\",[t._v(\"PointerEvent\")]),t._v(\"类中包括当前指针的一些信息，如：\")]),t._v(\" \"),a(\"ul\",[a(\"li\",[a(\"code\",[t._v(\"position\")]),t._v(\"：它是鼠标相对于当对于全局坐标的偏移。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"delta\")]),t._v(\"：两次指针移动事件（\"),a(\"code\",[t._v(\"PointerMoveEvent\")]),t._v(\"）的距离。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"pressure\")]),t._v(\"：按压力度，如果手机屏幕支持压力传感器(如iPhone的3D Touch)，此属性会更有意义，如果手机不支持，则始终为1。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"orientation\")]),t._v(\"：指针移动方向，是一个角度值。\")])]),t._v(\" \"),a(\"p\",[t._v(\"上面只是\"),a(\"code\",[t._v(\"PointerEvent\")]),t._v(\"一些常用属性，除了这些它还有很多属性，读者可以查看API文档。\")]),t._v(\" \"),a(\"p\",[t._v(\"现在，我们重点来介绍一下\"),a(\"code\",[t._v(\"behavior\")]),t._v(\"属性，它决定子组件如何响应命中测试，它的值类型为\"),a(\"code\",[t._v(\"HitTestBehavior\")]),t._v(\"，这是一个枚举类，有三个枚举值：\")]),t._v(\" \"),a(\"ul\",[a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"deferToChild\")]),t._v(\"：子组件会一个接一个的进行命中测试，如果子组件中有测试通过的，则当前组件通过，这就意味着，如果指针事件作用于子组件上时，其父级组件也肯定可以收到该事件。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"opaque\")]),t._v(\"：在命中测试时，将当前组件当成不透明处理(即使本身是透明的)，最终的效果相当于当前Widget的整个区域都是点击区域。举个例子：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Listener\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ConstrainedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        constraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BoxConstraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"tight\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Size\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"150.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Box A\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//behavior: HitTestBehavior.opaque,\")]),t._v(\"\\n    onPointerDown\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"event\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"down A\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"上例中，只有点击文本内容区域才会触发点击事件，因为 \"),a(\"code\",[t._v(\"deferToChild\")]),t._v(\" 会去子组件判断是否命中测试，而该例中子组件就是 \"),a(\"code\",[t._v('Text(\"Box A\")')]),t._v(\" 。\\n如果我们想让整个300×150的矩形区域都能点击我们可以将\"),a(\"code\",[t._v(\"behavior\")]),t._v(\"设为\"),a(\"code\",[t._v(\"HitTestBehavior.opaque\")]),t._v(\"。注意，该属性并不能用于在组件树中拦截（忽略）事件，它只是决定命中测试时的组件大小。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"translucent\")]),t._v(\"：当点击组件透明区域时，可以对自身边界内及底部可视区域都进行命中测试，这意味着点击顶部组件透明区域时，顶部组件和底部组件都可以接收到事件，例如：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Stack\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Listener\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ConstrainedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        constraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BoxConstraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"tight\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Size\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecoratedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      onPointerDown\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"event\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"down0\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Listener\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ConstrainedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        constraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BoxConstraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"tight\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Size\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"左上角200*100范围内非文本区域点击\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      onPointerDown\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"event\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"down1\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v('//behavior: HitTestBehavior.translucent, //放开此行注释后可以\"点透\"')]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"上例中，当注释掉最后一行代码后，在左上角200*100范围内非文本区域点击时（顶部组件透明区域），控制台只会打印“down0”，也就是说顶部组件没有接收到事件，而只有底部接收到了。当放开注释后，再点击时顶部和底部都会接收到事件，此时会打印：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language- extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[a(\"code\",[t._v(\"I/flutter ( 3039): down1\\nI/flutter ( 3039): down0\\n\")])])]),a(\"p\",[t._v(\"如果\"),a(\"code\",[t._v(\"behavior\")]),t._v(\"值改为\"),a(\"code\",[t._v(\"HitTestBehavior.opaque\")]),t._v('，则只会打印\"down1\"。')])])]),t._v(\" \"),a(\"h3\",{attrs:{id:\"忽略pointerevent\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#忽略pointerevent\"}},[t._v(\"#\")]),t._v(\" 忽略PointerEvent\")]),t._v(\" \"),a(\"p\",[t._v(\"假如我们不想让某个子树响应\"),a(\"code\",[t._v(\"PointerEvent\")]),t._v(\"的话，我们可以使用\"),a(\"code\",[t._v(\"IgnorePointer\")]),t._v(\"和\"),a(\"code\",[t._v(\"AbsorbPointer\")]),t._v(\"，这两个组件都能阻止子树接收指针事件，不同之处在于\"),a(\"code\",[t._v(\"AbsorbPointer\")]),t._v(\"本身会参与命中测试，而\"),a(\"code\",[t._v(\"IgnorePointer\")]),t._v(\"本身不会参与，这就意味着\"),a(\"code\",[t._v(\"AbsorbPointer\")]),t._v(\"本身是可以接收指针事件的(但其子树不行)，而\"),a(\"code\",[t._v(\"IgnorePointer\")]),t._v(\"不可以。一个简单的例子如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Listener\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AbsorbPointer\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Listener\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      onPointerDown\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"event\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"in\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  onPointerDown\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"event\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"up\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"点击\"),a(\"code\",[t._v(\"Container\")]),t._v(\"时，由于它在\"),a(\"code\",[t._v(\"AbsorbPointer\")]),t._v('的子树上，所以不会响应指针事件，所以日志不会输出\"in\"，但'),a(\"code\",[t._v(\"AbsorbPointer\")]),t._v('本身是可以接收指针事件的，所以会输出\"up\"。如果将'),a(\"code\",[t._v(\"AbsorbPointer\")]),t._v(\"换成\"),a(\"code\",[t._v(\"IgnorePointer\")]),t._v(\"，那么两个都不会输出。\")])])}),[],!1,null,null,null);n.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/128.c75c7015.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[128],{687:function(t,s,a){t.exports=a.p+\"assets/img/8-6.ce4b57e6.png\"},925:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_8-4-notification\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_8-4-notification\"}},[t._v(\"#\")]),t._v(\" 8.4 Notification\")]),t._v(\" \"),n(\"p\",[t._v(\"通知（Notification）是Flutter中一个重要的机制，在widget树中，每一个节点都可以分发通知，通知会沿着当前节点向上传递，所有父节点都可以通过\"),n(\"code\",[t._v(\"NotificationListener\")]),t._v(\"来监听通知。Flutter中将这种由子向父的传递通知的机制称为\"),n(\"strong\",[t._v(\"通知冒泡\")]),t._v(\"（Notification Bubbling）。通知冒泡和用户触摸事件冒泡是相似的，但有一点不同：通知冒泡可以中止，但用户触摸事件不行。\")]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[t._v(\"通知冒泡和Web开发中浏览器事件冒泡原理是相似的，都是事件从出发源逐层向上传递，我们可以在上层节点任意位置来监听通知/事件，也可以终止冒泡过程，终止冒泡后，通知将不会再向上传递。\")])]),t._v(\" \"),n(\"p\",[t._v(\"Flutter中很多地方使用了通知，如可滚动组件（Scrollable Widget）滑动时就会分发\"),n(\"strong\",[t._v(\"滚动通知\")]),t._v(\"（ScrollNotification），而Scrollbar正是通过监听ScrollNotification来确定滚动条位置的。\")]),t._v(\" \"),n(\"p\",[t._v(\"下面是一个监听可滚动组件滚动通知的例子：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"NotificationListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  onNotification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"notification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"switch\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"notification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"runtimeType\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" ScrollStartNotification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"开始滚动\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"break\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" ScrollUpdateNotification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"正在滚动\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"break\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" ScrollEndNotification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"滚动停止\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"break\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" OverscrollNotification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"滚动到边界\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"break\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ListView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      itemCount\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      itemBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$index\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上例中的滚动通知如\"),n(\"code\",[t._v(\"ScrollStartNotification\")]),t._v(\"、\"),n(\"code\",[t._v(\"ScrollUpdateNotification\")]),t._v(\"等都是继承自\"),n(\"code\",[t._v(\"ScrollNotification\")]),t._v(\"类，不同类型的通知子类会包含不同的信息，比如\"),n(\"code\",[t._v(\"ScrollUpdateNotification\")]),t._v(\"有一个\"),n(\"code\",[t._v(\"scrollDelta\")]),t._v(\"属性，它记录了移动的位移，其它通知属性读者可以自己查看SDK文档。\")]),t._v(\" \"),n(\"p\",[t._v(\"上例中，我们通过\"),n(\"code\",[t._v(\"NotificationListener\")]),t._v(\"来监听子\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"的滚动通知的，\"),n(\"code\",[t._v(\"NotificationListener\")]),t._v(\"定义如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"NotificationListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Notification\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"NotificationListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onNotification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码 \")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"  \\n\")])])]),n(\"p\",[t._v(\"我们可以看到：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"NotificationListener\")]),t._v(\" 继承自\"),n(\"code\",[t._v(\"StatelessWidget\")]),t._v(\"类，所以它可以直接嵌套到Widget树中。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"NotificationListener\")]),t._v(\" 可以指定一个模板参数，该模板参数类型必须是继承自\"),n(\"code\",[t._v(\"Notification\")]),t._v(\"；当显式指定模板参数时，\"),n(\"code\",[t._v(\"NotificationListener\")]),t._v(\" 便只会接收该参数类型的通知。举个例子，如果我们将上例子代码改为：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//指定监听通知的类型为滚动结束通知(ScrollEndNotification)\")]),t._v(\"\\nNotificationListener\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ScrollEndNotification\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  onNotification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"notification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//只会在滚动结束时才会触发此回调\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"notification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ListView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      itemCount\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      itemBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$index\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上面代码运行后便只会在滚动结束时在控制台打印出通知的信息。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"onNotification\")]),t._v(\"回调为通知处理回调，其函数签名如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"typedef\")]),t._v(\" NotificationListenerCallback\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Notification\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" bool \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"Function\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"T notification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"它的返回值类型为布尔值，当返回值为\"),n(\"code\",[t._v(\"true\")]),t._v(\"时，阻止冒泡，其父级Widget将再也收不到该通知；当返回值为\"),n(\"code\",[t._v(\"false\")]),t._v(\" 时继续向上冒泡通知。\")])])]),t._v(\" \"),n(\"p\",[t._v(\"Flutter的UI框架实现中，除了在可滚动组件在滚动过程中会发出\"),n(\"code\",[t._v(\"ScrollNotification\")]),t._v(\"之外，还有一些其它的通知，如\"),n(\"code\",[t._v(\"SizeChangedLayoutNotification\")]),t._v(\"、\"),n(\"code\",[t._v(\"KeepAliveNotification\")]),t._v(\" 、\"),n(\"code\",[t._v(\"LayoutChangedNotification\")]),t._v(\"等，Flutter正是通过这种通知机制来使父元素可以在一些特定时机来做一些事情。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"自定义通知\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#自定义通知\"}},[t._v(\"#\")]),t._v(\" 自定义通知\")]),t._v(\" \"),n(\"p\",[t._v(\"除了Flutter内部通知，我们也可以自定义通知，下面我们看看如何实现自定义通知：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"p\",[t._v(\"定义一个通知类，要继承自Notification类；\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyNotification\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Notification\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyNotification\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"msg\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String msg\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"分发通知。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"Notification\")]),t._v(\"有一个\"),n(\"code\",[t._v(\"dispatch(context)\")]),t._v(\"方法，它是用于分发通知的，我们说过\"),n(\"code\",[t._v(\"context\")]),t._v(\"实际上就是操作\"),n(\"code\",[t._v(\"Element\")]),t._v(\"的一个接口，它与\"),n(\"code\",[t._v(\"Element\")]),t._v(\"树上的节点是对应的，通知会从\"),n(\"code\",[t._v(\"context\")]),t._v(\"对应的\"),n(\"code\",[t._v(\"Element\")]),t._v(\"节点向上冒泡。\")])])]),t._v(\" \"),n(\"p\",[t._v(\"下面我们看一个完整的例子：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"NotificationRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  NotificationRouteState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"NotificationRouteState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"NotificationRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"NotificationRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  String _msg\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//监听通知  \")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" NotificationListener\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"MyNotification\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      onNotification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"notification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          _msg\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+=\")]),t._v(\"notification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"msg\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"  \"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n       \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          mainAxisSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"min\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//          RaisedButton(\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v('//           onPressed: () => MyNotification(\"Hi\").dispatch(context),')]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v('//           child: Text(\"Send Notification\"),')]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//          ),  \")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//按钮点击时分发通知  \")]),t._v(\"\\n                  onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyNotification\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hi\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispatch\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Send Notification\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_msg\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyNotification\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Notification\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyNotification\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"msg\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String msg\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上面代码中，我们每点一次按钮就会分发一个\"),n(\"code\",[t._v(\"MyNotification\")]),t._v(\"类型的通知，我们在Widget根上监听通知，收到通知后我们将通知通过Text显示在屏幕上。\")]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[t._v(\"注意：代码中注释的部分是不能正常工作的，因为这个\"),n(\"code\",[t._v(\"context\")]),t._v(\"是根Context，而NotificationListener是监听的子树，所以我们通过\"),n(\"code\",[t._v(\"Builder\")]),t._v(\"来构建RaisedButton，来获得按钮位置的context。\")])]),t._v(\" \"),n(\"p\",[t._v(\"运行效果如图8-6所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(687),alt:\"图8-6\"}})]),t._v(\" \"),n(\"h3\",{attrs:{id:\"阻止冒泡\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#阻止冒泡\"}},[t._v(\"#\")]),t._v(\" 阻止冒泡\")]),t._v(\" \"),n(\"p\",[t._v(\"我们将上面的例子改为：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"NotificationRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"NotificationRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  String _msg\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//监听通知\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" NotificationListener\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"MyNotification\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      onNotification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"notification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"notification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"msg\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//打印通知\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" NotificationListener\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"MyNotification\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        onNotification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"notification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            _msg\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+=\")]),t._v(\"notification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"msg\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"  \"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略重复代码\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上列中两个\"),n(\"code\",[t._v(\"NotificationListener\")]),t._v(\"进行了嵌套，子\"),n(\"code\",[t._v(\"NotificationListener\")]),t._v(\"的\"),n(\"code\",[t._v(\"onNotification\")]),t._v(\"回调返回了\"),n(\"code\",[t._v(\"false\")]),t._v(\"，表示不阻止冒泡，所以父\"),n(\"code\",[t._v(\"NotificationListener\")]),t._v(\"仍然会受到通知，所以控制台会打印出通知信息；如果将子\"),n(\"code\",[t._v(\"NotificationListener\")]),t._v(\"的\"),n(\"code\",[t._v(\"onNotification\")]),t._v(\"回调的返回值改为\"),n(\"code\",[t._v(\"true\")]),t._v(\"，则父\"),n(\"code\",[t._v(\"NotificationListener\")]),t._v(\"便不会再打印通知了，因为子\"),n(\"code\",[t._v(\"NotificationListener\")]),t._v(\"已经终止通知冒泡了。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"通知冒泡原理\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#通知冒泡原理\"}},[t._v(\"#\")]),t._v(\" 通知冒泡原理\")]),t._v(\" \"),n(\"p\",[t._v(\"我们在上面介绍了通知冒泡的现象及使用，现在我们更深入一些，介绍一下Flutter框架中是如何实现通知冒泡的。为了搞清楚这个问题，就必须看一下源码，我们从通知分发的的源头出发，然后再顺藤摸瓜。由于通知是通过\"),n(\"code\",[t._v(\"Notification\")]),t._v(\"的\"),n(\"code\",[t._v(\"dispatch(context)\")]),t._v(\"方法发出的，那我们先看看\"),n(\"code\",[t._v(\"dispatch(context)\")]),t._v(\"方法中做了什么，下面是相关源码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispatch\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext target\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  target\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"visitAncestorElements\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"visitAncestor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"dispatch(context)\")]),t._v(\"中调用了当前context的\"),n(\"code\",[t._v(\"visitAncestorElements\")]),t._v(\"方法，该方法会从当前Element开始向上遍历父级元素；\"),n(\"code\",[t._v(\"visitAncestorElements\")]),t._v(\"有一个遍历回调参数，在遍历过程中对遍历到的父级元素都会执行该回调。遍历的终止条件是：已经遍历到根Element或某个遍历回调返回\"),n(\"code\",[t._v(\"false\")]),t._v(\"。源码中传给\"),n(\"code\",[t._v(\"visitAncestorElements\")]),t._v(\"方法的遍历回调为\"),n(\"code\",[t._v(\"visitAncestor\")]),t._v(\"方法，我们看看\"),n(\"code\",[t._v(\"visitAncestor\")]),t._v(\"方法的实现：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//遍历回调，会对每一个父级Element执行此回调\")]),t._v(\"\\nbool \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"visitAncestor\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Element element\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//判断当前element对应的Widget是否是NotificationListener。\")]),t._v(\"\\n  \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//由于NotificationListener是继承自StatelessWidget，\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//故先判断是否是StatelessElement\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"element \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"is\")]),t._v(\" StatelessElement\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//是StatelessElement，则获取element对应的Widget，判断\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//是否是NotificationListener 。\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" StatelessWidget widget \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" element\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"is\")]),t._v(\" NotificationListener\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Notification\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//是NotificationListener，则调用该NotificationListener的_dispatch方法\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_dispatch\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" element\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"visitAncestor\")]),t._v(\"会判断每一个遍历到的父级Widget是否是\"),n(\"code\",[t._v(\"NotificationListener\")]),t._v(\"，如果不是，则返回\"),n(\"code\",[t._v(\"true\")]),t._v(\"继续向上遍历，如果是，则调用\"),n(\"code\",[t._v(\"NotificationListener\")]),t._v(\"的\"),n(\"code\",[t._v(\"_dispatch\")]),t._v(\"方法，我们看看\"),n(\"code\",[t._v(\"_dispatch\")]),t._v(\"方法的源码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"  bool \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_dispatch\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Notification notification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Element element\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 如果通知监听器不为空，并且当前通知类型是该NotificationListener\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 监听的通知类型，则调用当前NotificationListener的onNotification\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"onNotification \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\" notification \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"is\")]),t._v(\" T\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" bool result \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onNotification\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"notification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 返回值决定是否继续向上遍历\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" result \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们可以看到\"),n(\"code\",[t._v(\"NotificationListener\")]),t._v(\"的\"),n(\"code\",[t._v(\"onNotification\")]),t._v(\"回调最终是在\"),n(\"code\",[t._v(\"_dispatch\")]),t._v(\"方法中执行的，然后会根据返回值来确定是否继续向上冒泡。上面的源码实现其实并不复杂，通过阅读这些源码，一些额外的点读者可以注意一下：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"code\",[t._v(\"Context\")]),t._v(\"上也提供了遍历Element树的方法。\")]),t._v(\" \"),n(\"li\",[t._v(\"我们可以通过\"),n(\"code\",[t._v(\"Element.widget\")]),t._v(\"得到\"),n(\"code\",[t._v(\"element\")]),t._v(\"节点对应的widget；我们已经反复讲过Widget和Element的对应关系，读者通过这些源码来加深理解。\")])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"总结\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),n(\"p\",[t._v(\"Flutter中通过通知冒泡实现了一套自低向上的消息传递机制，这个和Web开发中浏览器的事件冒泡原理类似，Web开发者可以类比学习。另外我们通过源码了解了Flutter 通知冒泡的流程和原理，便于读者加深理解和学习Flutter的框架设计思想，在此，再次建议读者在平时学习中能多看看源码，定会受益匪浅。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/129.88b4391d.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[129],{717:function(t,e,s){\"use strict\";s.r(e);var n=[\"There's nothing here.\",\"How did we get here?\",\"That's a Four-Oh-Four.\",\"Looks like we've got some broken links.\"],o={methods:{getMsg:function(){return n[Math.floor(Math.random()*n.length)]}}},i=s(45),h=Object(i.a)(o,(function(){var t=this.$createElement,e=this._self._c||t;return e(\"div\",{staticClass:\"theme-container\"},[e(\"div\",{staticClass:\"theme-default-content\"},[e(\"h1\",[this._v(\"404\")]),this._v(\" \"),e(\"blockquote\",[this._v(this._s(this.getMsg()))]),this._v(\" \"),e(\"RouterLink\",{attrs:{to:\"/\"}},[this._v(\"\\n      Take me home.\\n    \")])],1)])}),[],!1,null,null,null);e.default=h.exports}}]);"
  },
  {
    "path": "docs/assets/js/13.e93e35ca.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[13],{416:function(t,a,s){t.exports=s.p+\"assets/img/3-17.a063365a.png\"},417:function(t,a,s){t.exports=s.p+\"assets/img/3-18.cb8a4b0f.png\"},418:function(t,a,s){t.exports=s.p+\"assets/img/3-19.feb336de.png\"},419:function(t,a,s){t.exports=s.p+\"assets/img/3-20.9c7569a6.png\"},420:function(t,a){t.exports=\"data:image/png;base64,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\"},421:function(t,a){t.exports=\"data:image/png;base64,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\"},774:function(t,a,s){\"use strict\";s.r(a);var n=s(45),e=Object(n.a)({},(function(){var t=this,a=t.$createElement,n=t._self._c||a;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h2\",{attrs:{id:\"_3-5-图片及icon\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-5-图片及icon\"}},[t._v(\"#\")]),t._v(\" 3.5 图片及ICON\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_3-5-1-图片\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-5-1-图片\"}},[t._v(\"#\")]),t._v(\" 3.5.1 图片\")]),t._v(\" \"),n(\"p\",[t._v(\"Flutter中，我们可以通过\"),n(\"code\",[t._v(\"Image\")]),t._v(\"组件来加载并显示图片，\"),n(\"code\",[t._v(\"Image\")]),t._v(\"的数据源可以是asset、文件、内存以及网络。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"imageprovider\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#imageprovider\"}},[t._v(\"#\")]),t._v(\" ImageProvider\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"ImageProvider\")]),t._v(\" 是一个抽象类，主要定义了图片数据获取的接口\"),n(\"code\",[t._v(\"load()\")]),t._v(\"，从不同的数据源获取图片需要实现不同的\"),n(\"code\",[t._v(\"ImageProvider\")]),t._v(\" ，如\"),n(\"code\",[t._v(\"AssetImage\")]),t._v(\"是实现了从Asset中加载图片的ImageProvider，而\"),n(\"code\",[t._v(\"NetworkImage\")]),t._v(\"实现了从网络加载图片的ImageProvider。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"image\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#image\"}},[t._v(\"#\")]),t._v(\" Image\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"Image\")]),t._v(\" widget有一个必选的\"),n(\"code\",[t._v(\"image\")]),t._v(\"参数，它对应一个ImageProvider。下面我们分别演示一下如何从asset和网络加载图片。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"从asset中加载图片\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#从asset中加载图片\"}},[t._v(\"#\")]),t._v(\" 从asset中加载图片\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"p\",[t._v(\"在工程根目录下创建一个\"),n(\"code\",[t._v(\"images目录\")]),t._v(\"，并将图片avatar.png拷贝到该目录。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"在\"),n(\"code\",[t._v(\"pubspec.yaml\")]),t._v(\"中的\"),n(\"code\",[t._v(\"flutter\")]),t._v(\"部分添加如下内容：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-yaml extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[n(\"code\",[t._v(\"  \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"assets\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\" images/avatar.png\\n\")])])])])]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[t._v(\"注意: 由于 yaml 文件对缩进严格，所以必须严格按照每一层两个空格的方式进行缩进，此处assets前面应有两个空格。\")])]),t._v(\" \"),n(\"ol\",{attrs:{start:\"3\"}},[n(\"li\",[n(\"p\",[t._v(\"加载该图片\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Image\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AssetImage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"images/avatar.png\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"Image也提供了一个快捷的构造函数\"),n(\"code\",[t._v(\"Image.asset\")]),t._v(\"用于从asset中加载、显示图片：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"asset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"images/avatar.png\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])])])]),t._v(\" \"),n(\"h4\",{attrs:{id:\"从网络加载图片\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#从网络加载图片\"}},[t._v(\"#\")]),t._v(\" 从网络加载图片\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Image\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"NetworkImage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"https://avatars2.githubusercontent.com/u/20411648?s=460&v=4\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"Image也提供了一个快捷的构造函数\"),n(\"code\",[t._v(\"Image.network\")]),t._v(\"用于从网络加载、显示图片：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"network\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"https://avatars2.githubusercontent.com/u/20411648?s=460&v=4\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行上面两个示例，图片加载成功后如图3-17所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(416),alt:\"图3-17\"}})]),t._v(\" \"),n(\"h4\",{attrs:{id:\"参数\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#参数\"}},[t._v(\"#\")]),t._v(\" 参数\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"Image\")]),t._v(\"在显示图片时定义了一系列参数，通过这些参数我们可以控制图片的显示外观、大小、混合效果等。我们看一下Image的主要参数：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Image\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//图片的宽\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//图片高度\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//图片的混合色值\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"colorBlendMode\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//混合模式\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"fit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//缩放模式\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"alignment \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//对齐方式\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"repeat \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" ImageRepeat\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"noRepeat\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//重复方式\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"ul\",[n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"width\")]),t._v(\"、\"),n(\"code\",[t._v(\"height\")]),t._v(\"：用于设置图片的宽、高，当不指定宽高时，图片会根据当前父容器的限制，尽可能的显示其原始大小，如果只设置\"),n(\"code\",[t._v(\"width\")]),t._v(\"、\"),n(\"code\",[t._v(\"height\")]),t._v(\"的其中一个，那么另一个属性默认会按比例缩放，但可以通过下面介绍的\"),n(\"code\",[t._v(\"fit\")]),t._v(\"属性来指定适应规则。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"fit\")]),t._v(\"：该属性用于在图片的显示空间和图片本身大小不同时指定图片的适应模式。适应模式是在\"),n(\"code\",[t._v(\"BoxFit\")]),t._v(\"中定义，它是一个枚举类型，有如下值：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"fill\")]),t._v(\"：会拉伸填充满显示空间，图片本身长宽比会发生变化，图片会变形。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"cover\")]),t._v(\"：会按图片的长宽比放大后居中填满显示空间，图片不会变形，超出显示空间部分会被剪裁。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"contain\")]),t._v(\"：这是图片的默认适应规则，图片会在保证图片本身长宽比不变的情况下缩放以适应当前显示空间，图片不会变形。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"fitWidth\")]),t._v(\"：图片的宽度会缩放到显示空间的宽度，高度会按比例缩放，然后居中显示，图片不会变形，超出显示空间部分会被剪裁。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"fitHeight\")]),t._v(\"：图片的高度会缩放到显示空间的高度，宽度会按比例缩放，然后居中显示，图片不会变形，超出显示空间部分会被剪裁。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"none\")]),t._v(\"：图片没有适应策略，会在显示空间内显示图片，如果图片比显示空间大，则显示空间只会显示图片中间部分。\")])]),t._v(\" \"),n(\"p\",[t._v(\"一图胜万言！ 我们对一个宽高相同的头像图片应用不同的\"),n(\"code\",[t._v(\"fit\")]),t._v(\"值，效果如图3-18所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(417),alt:\"图3-18\"}})])]),t._v(\" \"),n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"color\")]),t._v(\"和 \"),n(\"code\",[t._v(\"colorBlendMode\")]),t._v(\"：在图片绘制时可以对每一个像素进行颜色混合处理，\"),n(\"code\",[t._v(\"color\")]),t._v(\"指定混合色，而\"),n(\"code\",[t._v(\"colorBlendMode\")]),t._v(\"指定混合模式，下面是一个简单的示例：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Image\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AssetImage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"images/avatar.png\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  colorBlendMode\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BlendMode\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"difference\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])])])]),t._v(\" \"),n(\"p\",[t._v(\"运行效果如图3-19所示（彩色）:\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(418),alt:\"图3-19\"}})]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"repeat\")]),t._v(\"：当图片本身大小小于显示空间时，指定图片的重复规则。简单示例如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Image\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AssetImage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"images/avatar.png\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  repeat\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ImageRepeat\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"repeatY \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行后效果如图3-20所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(419),alt:\"图3-20\"}})])])]),t._v(\" \"),n(\"p\",[t._v(\"完整的示例代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ImageAndIconRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" img\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AssetImage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"imgs/avatar.png\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SingleChildScrollView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Image\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Image\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" img\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            fit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BoxFit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"fill\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Image\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" img\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            fit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BoxFit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"contain\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Image\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" img\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            fit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BoxFit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cover\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Image\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" img\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            fit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BoxFit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"fitWidth\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Image\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" img\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            fit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BoxFit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"fitHeight\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Image\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" img\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            fit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BoxFit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"scaleDown\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Image\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" img\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            fit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BoxFit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"none\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Image\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" img\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            colorBlendMode\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BlendMode\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"difference\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            fit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BoxFit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"fill\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Image\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" img\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            repeat\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ImageRepeat\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"repeatY \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"map\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"fit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toList\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"h3\",{attrs:{id:\"image缓存\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#image缓存\"}},[t._v(\"#\")]),t._v(\" Image缓存\")]),t._v(\" \"),n(\"p\",[t._v(\"Flutter框架对加载过的图片是有缓存的（内存），默认最大缓存数量是1000，最大缓存空间为100M。关于Image的详细内容及原理我们将会在后面进阶部分深入介绍。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_3-5-2-icon\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-5-2-icon\"}},[t._v(\"#\")]),t._v(\" 3.5.2 ICON\")]),t._v(\" \"),n(\"p\",[t._v(\"Flutter中，可以像Web开发一样使用iconfont，iconfont即“字体图标”，它是将图标做成字体文件，然后通过指定不同的字符而显示不同的图片。\")]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[t._v(\"在字体文件中，每一个字符都对应一个位码，而每一个位码对应一个显示字形，不同的字体就是指字形不同，即字符对应的字形是不同的。而在iconfont中，只是将位码对应的字形做成了图标，所以不同的字符最终就会渲染成不同的图标。\")])]),t._v(\" \"),n(\"p\",[t._v(\"在Flutter开发中，iconfont和图片相比有如下优势：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"体积小：可以减小安装包大小。\")]),t._v(\" \"),n(\"li\",[t._v(\"矢量的：iconfont都是矢量图标，放大不会影响其清晰度。\")]),t._v(\" \"),n(\"li\",[t._v(\"可以应用文本样式：可以像文本一样改变字体图标的颜色、大小对齐等。\")]),t._v(\" \"),n(\"li\",[t._v(\"可以通过TextSpan和文本混用。\")])]),t._v(\" \"),n(\"h5\",{attrs:{id:\"使用material-design字体图标\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#使用material-design字体图标\"}},[t._v(\"#\")]),t._v(\" 使用Material Design字体图标\")]),t._v(\" \"),n(\"p\",[t._v(\"Flutter默认包含了一套Material Design的字体图标，在\"),n(\"code\",[t._v(\"pubspec.yaml\")]),t._v(\"文件中的配置如下\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-yaml extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"flutter\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"uses-material-design\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean important\"}},[t._v(\"true\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"Material Design所有图标可以在其官网查看：https://material.io/tools/icons/\")]),t._v(\" \"),n(\"p\",[t._v(\"我们看一个简单的例子：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"String icons \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// accessible: &#xE914; or 0xE914 or E914\")]),t._v(\"\\nicons \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"\\\\uE914\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// error: &#xE000; or 0xE000 or E000\")]),t._v(\"\\nicons \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\" \\\\uE000\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// fingerprint: &#xE90D; or 0xE90D or E90D\")]),t._v(\"\\nicons \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\" \\\\uE90D\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      fontFamily\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"MaterialIcons\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      fontSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"24.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"green\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图3-21所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(420),alt:\"图3-21\"}})]),t._v(\" \"),n(\"p\",[t._v(\"通过这个示例可以看到，使用图标就像使用文本一样，但是这种方式需要我们提供每个图标的码点，这并对开发者不友好，所以，Flutter封装了\"),n(\"code\",[t._v(\"IconData\")]),t._v(\"和\"),n(\"code\",[t._v(\"Icon\")]),t._v(\"来专门显示字体图标，上面的例子也可以用如下方式实现：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  mainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"accessible\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"green\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"error\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"green\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"fingerprint\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"green\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"Icons\")]),t._v(\"类中包含了所有Material Design图标的\"),n(\"code\",[t._v(\"IconData\")]),t._v(\"静态变量定义。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"使用自定义字体图标\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#使用自定义字体图标\"}},[t._v(\"#\")]),t._v(\" 使用自定义字体图标\")]),t._v(\" \"),n(\"p\",[t._v(\"我们也可以使用自定义字体图标。iconfont.cn上有很多字体图标素材，我们可以选择自己需要的图标打包下载后，会生成一些不同格式的字体文件，在Flutter中，我们使用ttf格式即可。\")]),t._v(\" \"),n(\"p\",[t._v(\"假设我们项目中需要使用一个书籍图标和微信图标，我们打包下载后导入：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"p\",[t._v('导入字体图标文件；这一步和导入字体文件相同，假设我们的字体图标文件保存在项目根目录下，路径为\"fonts/iconfont.ttf\"：')]),t._v(\" \"),n(\"div\",{staticClass:\"language-yaml extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"fonts\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"family\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" myIcon  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"#指定一个字体名\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"fonts\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"asset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" fonts/iconfont.ttf\\n\")])])])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"为了使用方便，我们定义一个\"),n(\"code\",[t._v(\"MyIcons\")]),t._v(\"类，功能和\"),n(\"code\",[t._v(\"Icons\")]),t._v(\"类一样：将字体文件中的所有图标都定义成静态变量：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyIcons\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// book 图标\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" IconData book \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"IconData\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0xe614\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n      fontFamily\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'myIcon'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n      matchTextDirection\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 微信图标\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" IconData wechat \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"IconData\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0xec7d\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"  \\n      fontFamily\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'myIcon'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n      matchTextDirection\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"使用\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  mainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"MyIcons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"book\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"purple\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"MyIcons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"wechat\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"green\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行后效果如图3-22所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(421),alt:\"图3-22\"}})])])])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/130.f1922c51.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[130],{724:function(t,a,s){\"use strict\";s.r(a);var n=s(45),r=Object(n.a)({},(function(){var t=this,a=t.$createElement,s=t._self._c||a;return s(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[s(\"h1\",{attrs:{id:\"_1-4-dart语言简介\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-4-dart语言简介\"}},[t._v(\"#\")]),t._v(\" 1.4 Dart语言简介\")]),t._v(\" \"),s(\"p\",[t._v(\"在之前我们已经介绍过Dart语言的相关特性，读者可以翻看一下，如果读者已经熟悉Dart语法，可以跳过本节，如果你还不了解Dart，也不用担心，按照笔者经验，如果你有过其他编程语言经验（尤其是Java和JavaScript）的话会非常容易上手Dart。当然，如果你是iOS开发者，也不用担心，Dart中也有一些与Swift比较相似的特性，如命名参数等，笔者当时学习Dart时，只是花了一个小时，看完Dart官网的Language Tour，就开始动手写Flutter了。\")]),t._v(\" \"),s(\"p\",[t._v(\"在笔者看来，Dart的设计目标应该是同时借鉴了Java和JavaScript。Dart在静态语法方面和Java非常相似，如类型定义、函数声明、泛型等，而在动态特性方面又和JavaScript很像，如函数式特性、异步支持等。除了融合Java和JavaScript语言之所长之外，Dart也具有一些其它具有表现力的语法，如可选命名参数、\"),s(\"code\",[t._v(\"..\")]),t._v(\"（级联运算符）和\"),s(\"code\",[t._v(\"?.\")]),t._v(\"（条件成员访问运算符）以及\"),s(\"code\",[t._v(\"??\")]),t._v(\"（判空赋值运算符）。其实，对编程语言了解比较多的读者会发现，在Dart中其实看到的不仅有Java和JavaScript的影子，它还具有其它编程语言中的身影，如命名参数在Objective-C和Swift中早就很普遍，而\"),s(\"code\",[t._v(\"??\")]),t._v(\"操作符在PHP 7.0语法中就已经存在了，因此我们可以看到Google对Dart语言给予厚望，是想把Dart打造成一门集百家之所长的编程语言。\")]),t._v(\" \"),s(\"p\",[t._v(\"接下来，我们先对Dart语法做一个简单的介绍，然后再将Dart与JavaScript和Java做一个简要的对比，方便读者更好的理解。\")]),t._v(\" \"),s(\"blockquote\",[s(\"p\",[t._v(\"注意：由于本书并非专门介绍Dart语言的书籍，所以本章主要会介绍一下在Flutter开发中常用的语法特性，如果想更多了解Dart，读者可以去Dart官网学习，现在互联网上Dart相关资料已经很多了。另外Dart 2.0已经正式发布，所以本书所有示例均采用Dart 2.0语法。\")])]),t._v(\" \"),s(\"h2\",{attrs:{id:\"_1-4-1-变量声明\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-4-1-变量声明\"}},[t._v(\"#\")]),t._v(\" 1.4.1 变量声明\")]),t._v(\" \"),s(\"ol\",[s(\"li\",[s(\"p\",[s(\"strong\",[t._v(\"var\")])]),t._v(\" \"),s(\"p\",[t._v(\"类似于JavaScript中的\"),s(\"code\",[t._v(\"var\")]),t._v(\"，它可以接收任何类型的变量，但最大的不同是Dart中var变量一旦赋值，类型便会确定，则不能再改变其类型，如：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" t\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\nt \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"hi world\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 下面代码在dart中会报错，因为变量t的类型已经确定为String，\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 类型一旦确定后则不能再更改其类型。\")]),t._v(\"\\nt \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1000\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"上面的代码在JavaScript是没有问题的，前端开发者需要注意一下，之所以有此差异是因为Dart本身是一个强类型语言，任何变量都是有确定类型的，在Dart中，当用\"),s(\"code\",[t._v(\"var\")]),t._v(\"声明一个变量后，Dart在编译时会根据第一次赋值数据的类型来推断其类型，编译结束后其类型就已经被确定，而JavaScript是纯粹的弱类型脚本语言，var只是变量的声明方式而已。\")])]),t._v(\" \"),s(\"li\",[s(\"p\",[s(\"strong\",[t._v(\"dynamic\")]),t._v(\"和\"),s(\"strong\",[t._v(\"Object\")])]),t._v(\" \"),s(\"p\",[s(\"code\",[t._v(\"Object\")]),t._v(\" 是Dart所有对象的根基类，也就是说所有类型都是\"),s(\"code\",[t._v(\"Object\")]),t._v(\"的子类(包括Function和Null)，所以任何类型的数据都可以赋值给\"),s(\"code\",[t._v(\"Object\")]),t._v(\"声明的对象.\\n\"),s(\"code\",[t._v(\"dynamic\")]),t._v(\"与\"),s(\"code\",[t._v(\"var\")]),t._v(\"一样都是关键词,声明的变量可以赋值任意对象。\\n而\"),s(\"code\",[t._v(\"dynamic\")]),t._v(\"与\"),s(\"code\",[t._v(\"Object\")]),t._v(\"相同之处在于,他们声明的变量可以在后期改变赋值类型。\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),t._v(\" t\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\nObject x\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\nt \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"hi world\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\nx \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Hello Object'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//下面代码没有问题\")]),t._v(\"\\nt \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1000\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\nx \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1000\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"p\",[s(\"code\",[t._v(\"dynamic\")]),t._v(\"与\"),s(\"code\",[t._v(\"Object\")]),t._v(\"不同的是,\"),s(\"code\",[t._v(\"dynamic\")]),t._v(\"声明的对象编译器会提供所有可能的组合,\\n而\"),s(\"code\",[t._v(\"Object\")]),t._v(\"声明的对象只能使用Object的属性与方法, 否则编译器会报错。如:\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),t._v(\" a\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n Object b\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"main\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n     a \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n     b \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n     \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"printLengths\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"   \\n\\n \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"printLengths\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n     \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// no warning\")]),t._v(\"\\n     \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"a\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"length\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n     \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// warning:\")]),t._v(\"\\n     \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// The getter 'length' is not defined for the class 'Object'\")]),t._v(\"\\n     \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"b\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"length\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"变量a不会报错, 变量b编译器会报错\")]),t._v(\" \"),s(\"p\",[s(\"code\",[t._v(\"dynamic\")]),t._v(\"的这个特性与\"),s(\"code\",[t._v(\"Objective-C\")]),t._v(\"中的\"),s(\"code\",[t._v(\"id\")]),t._v(\"作用很像.\\n\"),s(\"code\",[t._v(\"dynamic\")]),t._v(\"的这个特点使得我们在使用它时需要格外注意,这很容易引入一个运行时错误.\")])]),t._v(\" \"),s(\"li\",[s(\"p\",[s(\"strong\",[t._v(\"final\")]),t._v(\"和\"),s(\"strong\",[t._v(\"const\")])]),t._v(\" \"),s(\"p\",[t._v(\"如果您从未打算更改一个变量，那么使用 \"),s(\"code\",[t._v(\"final\")]),t._v(\" 或 \"),s(\"code\",[t._v(\"const\")]),t._v(\"，不是\"),s(\"code\",[t._v(\"var\")]),t._v(\"，也不是一个类型。 一个 \"),s(\"code\",[t._v(\"final\")]),t._v(\" 变量只能被设置一次，两者区别在于：\"),s(\"code\",[t._v(\"const\")]),t._v(\" 变量是一个编译时常量，\"),s(\"code\",[t._v(\"final\")]),t._v(\"变量在第一次使用时被初始化。被\"),s(\"code\",[t._v(\"final\")]),t._v(\"或者\"),s(\"code\",[t._v(\"const\")]),t._v(\"修饰的变量，变量类型可以省略，如：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//可以省略String这个类型声明\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" str \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"hi world\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v('//final String str = \"hi world\"; ')]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" str1 \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"hi world\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v('//const String str1 = \"hi world\";')]),t._v(\"\\n\")])])])])]),t._v(\" \"),s(\"h2\",{attrs:{id:\"_1-4-2-函数\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-4-2-函数\"}},[t._v(\"#\")]),t._v(\" 1.4.2 函数\")]),t._v(\" \"),s(\"p\",[t._v(\"Dart是一种真正的面向对象的语言，所以即使是函数也是对象，并且有一个类型\"),s(\"strong\",[t._v(\"Function\")]),t._v(\"。这意味着函数可以赋值给变量或作为参数传递给其他函数，这是函数式编程的典型特征。\")]),t._v(\" \"),s(\"ol\",[s(\"li\",[s(\"p\",[t._v(\"函数声明\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"bool \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"isNoble\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int atomicNumber\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" _nobleGases\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"atomicNumber\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"Dart函数声明如果没有显式声明返回值类型时会默认当做\"),s(\"code\",[t._v(\"dynamic\")]),t._v(\"处理，注意，函数返回值没有类型推断：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"typedef\")]),t._v(\" bool \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CALLBACK\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//不指定返回类型，此时默认为dynamic，不是bool\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"isNoble\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int atomicNumber\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" _nobleGases\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"atomicNumber\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"test\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"CALLBACK cb\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"cb\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//报错，isNoble不是bool类型\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"test\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"isNoble\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])])]),t._v(\" \"),s(\"li\",[s(\"p\",[t._v(\"对于只包含一个表达式的函数，可以使用简写语法\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"bool isNoble \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int atomicNumber\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _nobleGases \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\" atomicNumber \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" ！\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"   \\n\")])])])]),t._v(\" \"),s(\"li\",[s(\"p\",[t._v(\"函数作为变量\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" say \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"str\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"str\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"say\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"hi world\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])])]),t._v(\" \"),s(\"li\",[s(\"p\",[t._v(\"函数作为参数传递\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"execute\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" callback\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"callback\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"execute\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"xxx\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])])]),t._v(\" \"),s(\"li\",[s(\"p\",[t._v(\"可选的位置参数\")]),t._v(\" \"),s(\"p\",[t._v(\"包装一组函数参数，用[]标记为可选的位置参数，并放在参数列表的最后面：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"String \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"say\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String from\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" String msg\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"String device\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" result \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'$from says $msg'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"device \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    result \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'$result with a $device'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" result\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"下面是一个不带可选参数调用这个函数的例子：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"say\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Bob'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Howdy'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//结果是： Bob says Howdy\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"下面是用第三个参数调用这个函数的例子：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"say\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Bob'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Howdy'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'smoke signal'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//结果是：Bob says Howdy with a smoke signal\")]),t._v(\"\\n\")])])])]),t._v(\" \"),s(\"li\",[s(\"p\",[t._v(\"可选的命名参数\")]),t._v(\" \"),s(\"p\",[t._v(\"定义函数时，使用{param1, param2, …}，放在参数列表的最后面，用于指定命名参数。例如：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//设置[bold]和[hidden]标志\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"enableFlags\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"bool bold\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" bool hidden\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// ... \")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"调用函数时，可以使用指定命名参数。例如：\"),s(\"code\",[t._v(\"paramName: value\")])]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"enableFlags\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"bold\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" hidden\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"可选命名参数在Flutter中使用非常多。\")]),t._v(\" \"),s(\"p\",[s(\"strong\",[t._v(\"注意，不能同时使用可选的位置参数和可选的命名参数\")])])])]),t._v(\" \"),s(\"h2\",{attrs:{id:\"_1-4-3-异步支持\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-4-3-异步支持\"}},[t._v(\"#\")]),t._v(\" 1.4.3 异步支持\")]),t._v(\" \"),s(\"p\",[t._v(\"Dart类库有非常多的返回\"),s(\"code\",[t._v(\"Future\")]),t._v(\"或者\"),s(\"code\",[t._v(\"Stream\")]),t._v(\"对象的函数。 这些函数被称为\"),s(\"strong\",[t._v(\"异步函数\")]),t._v(\"：它们只会在设置好一些耗时操作之后返回，比如像 IO操作。而不是等到这个操作完成。\")]),t._v(\" \"),s(\"p\",[s(\"code\",[t._v(\"async\")]),t._v(\"和\"),s(\"code\",[t._v(\"await\")]),t._v(\"关键词支持了异步编程，允许您写出和同步代码很像的异步代码。\")]),t._v(\" \"),s(\"h3\",{attrs:{id:\"future\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#future\"}},[t._v(\"#\")]),t._v(\" Future\")]),t._v(\" \"),s(\"p\",[s(\"code\",[t._v(\"Future\")]),t._v(\"与JavaScript中的\"),s(\"code\",[t._v(\"Promise\")]),t._v(\"非常相似，表示一个异步操作的最终完成（或失败）及其结果值的表示。简单来说，它就是用于处理异步操作的，异步处理成功了就执行成功的操作，异步处理失败了就捕获错误或者停止后续操作。一个Future只会对应一个结果，要么成功，要么失败。\")]),t._v(\" \"),s(\"p\",[t._v(\"由于本身功能较多，这里我们只介绍其常用的API及特性。还有，请记住，\"),s(\"code\",[t._v(\"Future\")]),t._v(\" 的所有API的返回值仍然是一个\"),s(\"code\",[t._v(\"Future\")]),t._v(\"对象，所以可以很方便的进行链式调用。\")]),t._v(\" \"),s(\"h4\",{attrs:{id:\"future-then\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#future-then\"}},[t._v(\"#\")]),t._v(\" Future.then\")]),t._v(\" \"),s(\"p\",[t._v(\"为了方便示例，在本例中我们使用\"),s(\"code\",[t._v(\"Future.delayed\")]),t._v(' 创建了一个延时任务（实际场景会是一个真正的耗时任务，比如一次网络请求），即2秒后返回结果字符串\"hi world!\"，然后我们在'),s(\"code\",[t._v(\"then\")]),t._v(\"中接收异步结果并打印结果，代码如下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"Future\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"delayed\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Duration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"hi world!\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"data\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"data\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"h4\",{attrs:{id:\"future-catcherror\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#future-catcherror\"}},[t._v(\"#\")]),t._v(\" Future.catchError\")]),t._v(\" \"),s(\"p\",[t._v(\"如果异步任务发生错误，我们可以在\"),s(\"code\",[t._v(\"catchError\")]),t._v(\"中捕获错误，我们将上面示例改为：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"Future\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"delayed\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Duration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v('//return \"hi world!\";')]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"throw\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AssertionError\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Error\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"  \\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"data\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//执行成功会走到这里  \")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"success\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"catchError\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//执行失败会走到这里  \")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"在本示例中，我们在异步任务中抛出了一个异常，\"),s(\"code\",[t._v(\"then\")]),t._v(\"的回调函数将不会被执行，取而代之的是 \"),s(\"code\",[t._v(\"catchError\")]),t._v(\"回调函数将被调用；但是，并不是只有 \"),s(\"code\",[t._v(\"catchError\")]),t._v(\"回调才能捕获错误，\"),s(\"code\",[t._v(\"then\")]),t._v(\"方法还有一个可选参数\"),s(\"code\",[t._v(\"onError\")]),t._v(\"，我们也可以它来捕获异常：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"Future\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"delayed\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Duration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\t\"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v('//return \"hi world!\";')]),t._v(\"\\n\\t\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"throw\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AssertionError\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Error\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"data\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\t\"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"success\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" onError\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\t\"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"h4\",{attrs:{id:\"future-whencomplete\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#future-whencomplete\"}},[t._v(\"#\")]),t._v(\" Future.whenComplete\")]),t._v(\" \"),s(\"p\",[t._v(\"有些时候，我们会遇到无论异步任务执行成功或失败都需要做一些事的场景，比如在网络请求前弹出加载对话框，在请求结束后关闭对话框。这种场景，有两种方法，第一种是分别在\"),s(\"code\",[t._v(\"then\")]),t._v(\"或\"),s(\"code\",[t._v(\"catch\")]),t._v(\"中关闭一下对话框，第二种就是使用\"),s(\"code\",[t._v(\"Future\")]),t._v(\"的\"),s(\"code\",[t._v(\"whenComplete\")]),t._v(\"回调，我们将上面示例改一下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"Future\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"delayed\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Duration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v('//return \"hi world!\";')]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"throw\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AssertionError\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Error\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"data\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//执行成功会走到这里 \")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"data\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"catchError\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//执行失败会走到这里   \")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"whenComplete\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//无论成功或失败都会走到这里\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"h4\",{attrs:{id:\"future-wait\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#future-wait\"}},[t._v(\"#\")]),t._v(\" Future.wait\")]),t._v(\" \"),s(\"p\",[t._v(\"有些时候，我们需要等待多个异步任务都执行结束后才进行一些操作，比如我们有一个界面，需要先分别从两个网络接口获取数据，获取成功后，我们需要将两个接口数据进行特定的处理后再显示到UI界面上，应该怎么做？答案是\"),s(\"code\",[t._v(\"Future.wait\")]),t._v(\"，它接受一个\"),s(\"code\",[t._v(\"Future\")]),t._v(\"数组参数，只有数组中所有\"),s(\"code\",[t._v(\"Future\")]),t._v(\"都执行成功后，才会触发\"),s(\"code\",[t._v(\"then\")]),t._v(\"的成功回调，只要有一个\"),s(\"code\",[t._v(\"Future\")]),t._v(\"执行失败，就会触发错误回调。下面，我们通过模拟\"),s(\"code\",[t._v(\"Future.delayed\")]),t._v(\" 来模拟两个数据获取的异步任务，等两个异步任务都执行成功时，将两个异步任务的结果拼接打印出来，代码如下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"Future\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"wait\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 2秒后返回结果  \")]),t._v(\"\\n  Future\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"delayed\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Duration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"hello\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 4秒后返回结果  \")]),t._v(\"\\n  Future\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"delayed\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Duration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"4\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\" world\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"results\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"results\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\"results\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"catchError\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"执行上面代码，4秒后你会在控制台中看到“hello world”。\")]),t._v(\" \"),s(\"h3\",{attrs:{id:\"async-await\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#async-await\"}},[t._v(\"#\")]),t._v(\" Async/await\")]),t._v(\" \"),s(\"p\",[t._v(\"Dart中的\"),s(\"code\",[t._v(\"async/await\")]),t._v(\" 和JavaScript中的\"),s(\"code\",[t._v(\"async/await\")]),t._v(\"功能和用法是一模一样的，如果你已经了解JavaScript中的\"),s(\"code\",[t._v(\"async/await\")]),t._v(\"的用法，可以直接跳过本节。\")]),t._v(\" \"),s(\"h4\",{attrs:{id:\"回调地狱-callback-hell\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#回调地狱-callback-hell\"}},[t._v(\"#\")]),t._v(\" 回调地狱(Callback Hell)\")]),t._v(\" \"),s(\"p\",[t._v(\"如果代码中有大量异步逻辑，并且出现大量异步任务依赖其它异步任务的结果时，必然会出现\"),s(\"code\",[t._v(\"Future.then\")]),t._v(\"回调中套回调情况。举个例子，比如现在有个需求场景是用户先登录，登录成功后会获得用户ID，然后通过用户ID，再去请求用户个人信息，获取到用户个人信息后，为了使用方便，我们需要将其缓存在本地文件系统，代码如下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//先分别定义各个异步任务\")]),t._v(\"\\nFuture\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"login\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String userName\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" String pwd\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\t\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//用户登录\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\nFuture\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getUserInfo\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String id\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\t\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//获取用户信息 \")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\nFuture \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"saveUserInfo\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String userInfo\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\t\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\\t\"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 保存用户信息 \")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \\n\")])])]),s(\"p\",[t._v(\"接下来，执行整个任务流：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"login\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"alice\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"******\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"id\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//登录成功后通过，id获取用户信息    \")]),t._v(\"\\n \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getUserInfo\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"id\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"userInfo\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//获取用户信息后保存 \")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"saveUserInfo\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"userInfo\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n       \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//保存用户信息，接下来执行其它操作\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"可以感受一下，如果业务逻辑中有大量异步依赖的情况，将会出现上面这种在回调里面套回调的情况，过多的嵌套会导致的代码可读性下降以及出错率提高，并且非常难维护，这个问题被形象的称为\"),s(\"strong\",[t._v(\"回调地狱（Callback Hell）\")]),t._v(\"。回调地狱问题在之前JavaScript中非常突出，也是JavaScript被吐槽最多的点，但随着ECMAScript6和ECMAScript7标准发布后，这个问题得到了非常好的解决，而解决回调地狱的两大神器正是ECMAScript6引入了\"),s(\"code\",[t._v(\"Promise\")]),t._v(\"，以及ECMAScript7中引入的\"),s(\"code\",[t._v(\"async/await\")]),t._v(\"。 而在Dart中几乎是完全平移了JavaScript中的这两者：\"),s(\"code\",[t._v(\"Future\")]),t._v(\"相当于\"),s(\"code\",[t._v(\"Promise\")]),t._v(\"，而\"),s(\"code\",[t._v(\"async/await\")]),t._v(\"连名字都没改。接下来我们看看通过\"),s(\"code\",[t._v(\"Future\")]),t._v(\"和\"),s(\"code\",[t._v(\"async/await\")]),t._v(\"如何消除上面示例中的嵌套问题。\")]),t._v(\" \"),s(\"h5\",{attrs:{id:\"使用future消除callback-hell\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#使用future消除callback-hell\"}},[t._v(\"#\")]),t._v(\" 使用Future消除Callback Hell\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"login\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"alice\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"******\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"id\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \\t\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getUserInfo\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"id\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"userInfo\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"saveUserInfo\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"userInfo\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//执行接下来的操作 \")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"catchError\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//错误处理  \")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"正如上文所述， \"),s(\"em\",[t._v(\"“\"),s(\"code\",[t._v(\"Future\")]),t._v(\" 的所有API的返回值仍然是一个\"),s(\"code\",[t._v(\"Future\")]),t._v(\"对象，所以可以很方便的进行链式调用”\")]),t._v(\" ，如果在then中返回的是一个\"),s(\"code\",[t._v(\"Future\")]),t._v(\"的话，该\"),s(\"code\",[t._v(\"future\")]),t._v(\"会执行，执行结束后会触发后面的\"),s(\"code\",[t._v(\"then\")]),t._v(\"回调，这样依次向下，就避免了层层嵌套。\")]),t._v(\" \"),s(\"h5\",{attrs:{id:\"使用async-await消除callback-hell\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#使用async-await消除callback-hell\"}},[t._v(\"#\")]),t._v(\" 使用async/await消除callback hell\")]),t._v(\" \"),s(\"p\",[t._v(\"通过\"),s(\"code\",[t._v(\"Future\")]),t._v(\"回调中再返回\"),s(\"code\",[t._v(\"Future\")]),t._v(\"的方式虽然能避免层层嵌套，但是还是有一层回调，有没有一种方式能够让我们可以像写同步代码那样来执行异步任务而不使用回调的方式？答案是肯定的，这就要使用\"),s(\"code\",[t._v(\"async/await\")]),t._v(\"了，下面我们先直接看代码，然后再解释，代码如下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"task\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    String id \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"login\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"alice\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"******\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    String userInfo \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getUserInfo\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"id\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"saveUserInfo\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"userInfo\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//执行接下来的操作   \")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"catch\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//错误处理   \")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"   \\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"  \\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"ul\",[s(\"li\",[s(\"code\",[t._v(\"async\")]),t._v(\"用来表示函数是异步的，定义的函数会返回一个\"),s(\"code\",[t._v(\"Future\")]),t._v(\"对象，可以使用then方法添加回调函数。\")]),t._v(\" \"),s(\"li\",[s(\"code\",[t._v(\"await\")]),t._v(\" 后面是一个\"),s(\"code\",[t._v(\"Future\")]),t._v(\"，表示等待该异步任务完成，异步完成后才会往下走；\"),s(\"code\",[t._v(\"await\")]),t._v(\"必须出现在 \"),s(\"code\",[t._v(\"async\")]),t._v(\" 函数内部。\")])]),t._v(\" \"),s(\"p\",[t._v(\"可以看到，我们通过\"),s(\"code\",[t._v(\"async/await\")]),t._v(\"将一个异步流用同步的代码表示出来了。\")]),t._v(\" \"),s(\"blockquote\",[s(\"p\",[t._v(\"其实，无论是在JavaScript还是Dart中，\"),s(\"code\",[t._v(\"async/await\")]),t._v(\"都只是一个语法糖，编译器或解释器最终都会将其转化为一个Promise（Future）的调用链。\")])]),t._v(\" \"),s(\"h2\",{attrs:{id:\"_1-4-4-stream\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-4-4-stream\"}},[t._v(\"#\")]),t._v(\" 1.4.4 Stream\")]),t._v(\" \"),s(\"p\",[s(\"code\",[t._v(\"Stream\")]),t._v(\" 也是用于接收异步事件数据，和\"),s(\"code\",[t._v(\"Future\")]),t._v(\" 不同的是，它可以接收多个异步操作的结果（成功或失败）。 也就是说，在执行异步任务时，可以通过多次触发成功或失败事件来传递结果数据或错误异常。 \"),s(\"code\",[t._v(\"Stream\")]),t._v(\" 常用于会多次读取数据的异步任务场景，如网络内容下载、文件读写等。举个例子：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"Stream\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fromFutures\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 1秒后返回结果\")]),t._v(\"\\n  Future\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"delayed\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Duration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"hello 1\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 抛出一个异常\")]),t._v(\"\\n  Future\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"delayed\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Duration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"throw\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AssertionError\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Error\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 3秒后返回结果\")]),t._v(\"\\n  Future\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"delayed\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Duration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"hello 3\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"listen\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"data\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"data\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" onError\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"message\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"onDone\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"上面的代码依次会输出：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language- extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[s(\"code\",[t._v(\"I/flutter (17666): hello 1\\nI/flutter (17666): Error\\nI/flutter (17666): hello 3\\n\")])])]),s(\"p\",[t._v(\"代码很简单，就不赘述了。\")]),t._v(\" \"),s(\"blockquote\",[s(\"p\",[t._v(\"思考题：既然Stream可以接收多次事件，那能不能用Stream来实现一个订阅者模式的事件总线？\")])]),t._v(\" \"),s(\"h2\",{attrs:{id:\"_1-4-5-dart和java及javascript对比\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-4-5-dart和java及javascript对比\"}},[t._v(\"#\")]),t._v(\" 1.4.5 Dart和Java及JavaScript对比\")]),t._v(\" \"),s(\"p\",[t._v(\"通过上面介绍，相信你对Dart应该有了一个初步的印象，由于笔者平时也使用Java和JavaScript，下面笔者根据自己的经验，结合Java和JavaScript，谈一下自己的看法。\")]),t._v(\" \"),s(\"blockquote\",[s(\"p\",[t._v(\"之所以将Dart与Java和JavaScript对比，是因为，这两者分别是强类型语言和弱类型语言的典型代表，并且Dart 语法中很多地方也都借鉴了Java和JavaScript。\")])]),t._v(\" \"),s(\"h3\",{attrs:{id:\"dart-vs-java\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#dart-vs-java\"}},[t._v(\"#\")]),t._v(\" Dart vs Java\")]),t._v(\" \"),s(\"p\",[t._v(\"客观的来讲，Dart在语法层面确实比Java更有表现力；在VM层面，Dart VM在内存回收和吞吐量都进行了反复的优化，但具体的性能对比，笔者没有找到相关测试数据，但在笔者看来，只要Dart语言能流行，VM的性能就不用担心，毕竟Google在Go（没用VM但有GC）、JavaScript（v8）、Dalvik（Android上的Java VM）上已经有了很多技术积淀。值得注意的是Dart在Flutter中已经可以将GC做到10ms以内，所以Dart和Java相比，决胜因素并不会是在性能方面。而在语法层面，Dart要比Java更有表现力，最重要的是Dart对函数式编程支持要远强于Java(目前只停留在Lambda表达式)，而Dart目前真正的不足是\"),s(\"strong\",[t._v(\"生态\")]),t._v(\"，但笔者相信，随着Flutter的逐渐火热，会回过头来反推Dart生态加速发展，对于Dart来说，现在需要的是时间。\")]),t._v(\" \"),s(\"h3\",{attrs:{id:\"dart-vs-javascript\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#dart-vs-javascript\"}},[t._v(\"#\")]),t._v(\" Dart vs JavaScript\")]),t._v(\" \"),s(\"p\",[t._v(\"JavaScript的弱类型一直被抓短，所以TypeScript、CoffeeScript甚至是Facebook的flow（虽然并不能算JavaScript的一个超集，但也通过标注和打包工具提供了静态类型检查）才有市场。就笔者使用过的脚本语言中（笔者曾使用过Python、PHP），JavaScript无疑是\"),s(\"strong\",[t._v(\"动态化\")]),t._v(\"支持最好的脚本语言，比如在JavaScript中，可以给任何对象在任何时候动态扩展属性，对于精通JavaScript的高手来说，这无疑是一把利剑。但是，任何事物都有两面性，JavaScript的强大的动态化特性也是把双刃剑，你可经常听到另一个声音，认为JavaScript的这种动态性糟糕透了，太过灵活反而导致代码很难预期，无法限制不被期望的修改。毕竟有些人总是对自己或别人写的代码不放心，他们希望能够让代码变得可控，并期望有一套静态类型检查系统来帮助自己减少错误。正因如此，在Flutter中，Dart几乎放弃了脚本语言动态化的特性，如不支持反射、也不支持动态创建函数等。并且Dart在2.0强制开启了类型检查（Strong Mode），原先的检查模式（checked mode）和可选类型（optional type）将淡出，所以在类型安全这个层面来说，Dart和TypeScript、CoffeeScript是差不多的，所以单从这一点来看，Dart并不具备什么明显优势，但综合起来看，Dart既能进行服务端脚本、APP开发、web开发，这就有优势了！\")]),t._v(\" \"),s(\"p\",[t._v(\"综上所述，笔者还是很看好Dart语言的将来，之所以表这个态，是因为在新技术发展初期，很多人可能还有所摇摆，有所犹豫，所以有必要给大家打一剂强心针，当然，这是一个见仁见智的问题，大家可以各抒己见。\")])])}),[],!1,null,null,null);a.default=r.exports}}]);"
  },
  {
    "path": "docs/assets/js/131.7f51717c.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[131],{722:function(t,r,e){\"use strict\";e.r(r);var l=e(45),a=Object(l.a)({},(function(){var t=this,r=t.$createElement,e=t._self._c||r;return e(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[e(\"h2\",{attrs:{id:\"本章目录\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本章目录\"}},[t._v(\"#\")]),t._v(\" 本章目录\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter1/mobile_development_intro.html\"}},[t._v(\"移动开发技术简介\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter1/flutter_intro.html\"}},[t._v(\"Flutter简介\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter1/install_flutter.html\"}},[t._v(\"搭建Flutter开发环境\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter1/dart.html\"}},[t._v(\"Dart语言简介\")])],1)])])}),[],!1,null,null,null);r.default=a.exports}}]);"
  },
  {
    "path": "docs/assets/js/132.4b82358d.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[132],{728:function(t,a,e){\"use strict\";e.r(a);var s=e(45),v=Object(s.a)({},(function(){var t=this,a=t.$createElement,e=t._self._c||a;return e(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[e(\"h1\",{attrs:{id:\"_1-1-移动开发技术简介\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-1-移动开发技术简介\"}},[t._v(\"#\")]),t._v(\" 1.1 移动开发技术简介\")]),t._v(\" \"),e(\"p\",[t._v(\"本节将主要介绍一下移动开发技术的进化历程，主要是想让读者知道Flutter技术出现的背景。笔者认为，了解一门新技术出现的背景是非常重要的，因为只有了解之前是什么样的，才能理解为什么会是现在这样。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"_1-1-1-原生开发与跨平台技术\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-1-1-原生开发与跨平台技术\"}},[t._v(\"#\")]),t._v(\" 1.1.1 原生开发与跨平台技术\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"原生开发\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#原生开发\"}},[t._v(\"#\")]),t._v(\" 原生开发\")]),t._v(\" \"),e(\"p\",[t._v(\"原生应用程序是指某一个移动平台（比如iOS或安卓）所特有的应用，使用相应平台支持的开发工具和语言，并直接调用系统提供的SDK API。比如Android原生应用就是指使用Java或Kotlin语言直接调用Android SDK开发的应用程序；而iOS原生应用就是指通过Objective-C或Swift语言直接调用iOS SDK开发的应用程序。原生开发有以下主要优势：\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"可访问平台全部功能（GPS、摄像头）；\")]),t._v(\" \"),e(\"li\",[t._v(\"速度快、性能高、可以实现复杂动画及绘制，整体用户体验好；\")])]),t._v(\" \"),e(\"p\",[t._v(\"主要缺点：\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"平台特定，开发成本高；不同平台必须维护不同代码，人力成本随之变大；\")]),t._v(\" \"),e(\"li\",[t._v(\"内容固定，动态化弱，大多数情况下，有新功能更新时只能发版；\")])]),t._v(\" \"),e(\"p\",[t._v(\"在移动互联网发展初期，业务场景并不复杂，原生开发还可以应对产品需求迭代。 但近几年，随着物联网时代到来、移动互联网高歌猛进，日新月异，在很多业务场景中，传统的纯原生开发已经不能满足日益增长的业务需求。主要表现在：\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"动态化内容需求增大；当需求发生变化时，纯原生应用需要通过版本升级来更新内容，但应用上架、审核是需要周期的，这对高速变化的互联网时代来说是很难接受的，所以，对应用动态化(不发版也可以更新应用内容)的需求就变的迫在眉睫。\")]),t._v(\" \"),e(\"li\",[t._v(\"业务需求变化快，开发成本变大；由于原生开发一般都要维护Android、iOS两个开发团队，版本迭代时，无论人力成本，还是测试成本都会变大。\")])]),t._v(\" \"),e(\"p\",[t._v(\"总结一下，纯原生开发主要面临动态化和开发成本两个问题，而针对这两个问题，诞生了一些跨平台的动态化框架。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"跨平台技术简介\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#跨平台技术简介\"}},[t._v(\"#\")]),t._v(\" 跨平台技术简介\")]),t._v(\" \"),e(\"p\",[t._v(\"针对原生开发面临问题，人们一直都在努力寻找好的解决方案，而时至今日，已经有很多跨平台框架(注意，本书中所指的“跨平台”若无特殊说明，即特指Android和iOS两个平台)，根据其原理，主要分为三类：\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"H5+原生（Cordova、Ionic、微信小程序）\")]),t._v(\" \"),e(\"li\",[t._v(\"JavaScript开发+原生渲染 （React Native、Weex、快应用）\")]),t._v(\" \"),e(\"li\",[t._v(\"自绘UI+原生(QT for mobile、Flutter)\")])]),t._v(\" \"),e(\"p\",[t._v(\"在接下来的章节中我们逐个来看看这三类框架的原理及优缺点。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"_1-1-2-hybrid技术简介\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-1-2-hybrid技术简介\"}},[t._v(\"#\")]),t._v(\" 1.1.2 Hybrid技术简介\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"h5-原生混合开发\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#h5-原生混合开发\"}},[t._v(\"#\")]),t._v(\" H5+原生混合开发\")]),t._v(\" \"),e(\"p\",[t._v(\"这类框架主要原理就是将APP的一部分需要动态变动的内容通过H5来实现，通过原生的网页加载控件WebView (Android)或WKWebView（iOS）来加载（以后若无特殊说明，我们用WebView来统一指代android和iOS中的网页加载控件）。这样以来，H5部分是可以随时改变而不用发版，动态化需求能满足；同时，由于h5代码只需要一次开发，就能同时在Android和iOS两个平台运行，这也可以减小开发成本，也就是说，H5部分功能越多，开发成本就越小。我们称这种h5+原生的开发模式为\"),e(\"strong\",[t._v(\"混合开发 ** ，采用混合模式开发的APP我们称之为\")]),t._v(\"混合应用\"),e(\"strong\",[t._v(\"或\")]),t._v(\"Hybrid APP**  ，如果一个应用的大多数功能都是H5实现的话，我们称其为\"),e(\"strong\",[t._v(\"Web APP\")]),t._v(\" 。\")]),t._v(\" \"),e(\"p\",[t._v(\"目前混合开发框架的典型代表有：Cordova、Ionic 和微信小程序，值得一提的是微信小程序目前是在webview中渲染的，并非原生渲染，但将来有可能会采用原生渲染。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"混合开发技术点\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#混合开发技术点\"}},[t._v(\"#\")]),t._v(\" 混合开发技术点\")]),t._v(\" \"),e(\"p\",[t._v(\"如之前所述，原生开发可以访问平台所有功能，而混合开发中，H5代码是运行在WebView中，而WebView实质上就是一个浏览器内核，其JavaScript依然运行在一个权限受限的沙箱中，所以对于大多数系统能力都没有访问权限，如无法访问文件系统、不能使用蓝牙等。所以，对于H5不能实现的功能，都需要原生去做。而混合框架一般都会在原生代码中预先实现一些访问系统能力的API， 然后暴露给WebView以供JavaScript调用，这样一来，WebView就成为了JavaScript与原生API之间通信的桥梁，主要负责JavaScript与原生之间传递调用消息，而消息的传递必须遵守一个标准的协议，它规定了消息的格式与含义，我们把依赖于WebView的用于在JavaScript与原生之间通信并实现了某种消息传输协议的工具称之为\"),e(\"strong\",[t._v(\"WebView JavaScript Bridge\")]),t._v(\", 简称 \"),e(\"strong\",[t._v(\"JsBridge\")]),t._v(\"，它也是混合开发框架的核心。\")]),t._v(\" \"),e(\"h4\",{attrs:{id:\"示例-javascript调用原生api获取手机型号\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例-javascript调用原生api获取手机型号\"}},[t._v(\"#\")]),t._v(\" 示例：JavaScript调用原生API获取手机型号\")]),t._v(\" \"),e(\"p\",[t._v(\"下面我们以Android为例，实现一个获取手机型号的原生API供JavaScript调用。在这个示例中将展示JavaScript调用原生API的流程，读者可以直观的感受一下调用流程。我们选用笔者在Github上开源的dsBridge作为JsBridge来进行通信。dsBridge是一个支持同步调用的跨平台的JsBridge，此示例中只使用其同步调用功能。\")]),t._v(\" \"),e(\"ol\",[e(\"li\",[e(\"p\",[t._v(\"首先在原生中实现获取手机型号的API \"),e(\"code\",[t._v(\"getPhoneModel\")])]),t._v(\" \"),e(\"div\",{staticClass:\"language-java extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-java\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" JSAPI \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n \"),e(\"span\",{pre:!0,attrs:{class:\"token annotation punctuation\"}},[t._v(\"@JavascriptInterface\")]),t._v(\"\\n \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"public\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Object\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getPhoneModel\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Object\")]),t._v(\" msg\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Build\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"MODEL\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"    \\n\")])])])]),t._v(\" \"),e(\"li\",[e(\"p\",[t._v(\"将原生API通过WebView注册到JsBridge中\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-java extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-java\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token namespace\"}},[t._v(\"wendu\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dsbridge\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")])]),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"DWebView\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//DWebView继承自WebView，由dsBridge提供  \")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"DWebView\")]),t._v(\" dwebView \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"DWebView\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"findViewById\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"R\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"id\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dwebview\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//注册原生API到JsBridge\")]),t._v(\"\\ndwebView\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addJavascriptObject\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"JsAPI\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])])]),t._v(\" \"),e(\"li\",[e(\"p\",[t._v(\"在JavaScript中调用原生API\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-javascript extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-javascript\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" dsBridge \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"require\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"dsbridge\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//直接调用原生API `getPhoneModel`\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" model \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dsBridge\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"call\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"getPhoneModel\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//打印机型\")]),t._v(\"\\nconsole\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"log\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"model\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])])])]),t._v(\" \"),e(\"p\",[t._v(\"上面示例演示了JavaScript调用原生API的过程，同样的，一般来说优秀的JsBridge也支持原生调用JavaScript，dsBridge也是支持的，如果您感兴趣，可以去github dsBridge项目主页查看。\")]),t._v(\" \"),e(\"p\",[t._v(\"现在，我们回头来看一下，混合应用无非就是在第一步中预先实现一系列API供JavaScript调用，让JavaScript有访问系统的能力，看到这里，我相信你也可以自己实现一个混合开发框架了。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"总结\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),e(\"p\",[t._v(\"混合应用的优点是动态内容是H5，web技术栈，社区及资源丰富，缺点是性能不好，对于复杂用户界面或动画，WebView不堪重任。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"_1-1-3-react-native、weex及快应用\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-1-3-react-native、weex及快应用\"}},[t._v(\"#\")]),t._v(\" 1.1.3 React Native、Weex及快应用\")]),t._v(\" \"),e(\"p\",[t._v(\"本篇主要介绍一下 \"),e(\"strong\",[t._v(\"JavaScript开发+原生渲染\")]),t._v(\"的跨平台框架原理。\")]),t._v(\" \"),e(\"p\",[t._v(\"React Native (简称RN)是Facebook于2015年4月开源的跨平台移动应用开发框架，是Facebook早先开源的JS框架 React 在原生移动应用平台的衍生产物，目前支持iOS和Android两个平台。RN使用Javascript语言，类似于HTML的JSX，以及CSS来开发移动应用，因此熟悉Web前端开发的技术人员只需很少的学习就可以进入移动应用开发领域。\")]),t._v(\" \"),e(\"p\",[t._v(\"由于RN和React原理相通，并且Flutter也是受React启发，很多思想也都是相通的，万丈高楼平地起，我们有必要深入了解一下React原理。React是一个响应式的Web框架，我们先了解一下两个重要的概念：DOM树与响应式编程。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"dom树与控件树\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#dom树与控件树\"}},[t._v(\"#\")]),t._v(\" DOM树与控件树\")]),t._v(\" \"),e(\"p\",[t._v(\"文档对象模型（Document Object Model，简称DOM），是W3C组织推荐的处理可扩展标志语言的标准编程接口，一种独立于平台和语言的方式访问和修改一个文档的内容和结构。换句话说，这是表示和处理一个HTML或XML文档的标准接口。简单来说，DOM就是文档树，与用户界面控件树对应，在前端开发中通常指HTML对应的渲染树，但广义的DOM也可以指Android中的XML布局文件对应的控件树，而术语\"),e(\"strong\",[t._v(\"DOM操作\")]),t._v(\"就是指直接来操作渲染树（或控件树）， 因此，可以看到其实DOM树和控件树是等价的概念，只不过前者常用于Web开发中，而后者常用于原生开发中。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"响应式编程\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#响应式编程\"}},[t._v(\"#\")]),t._v(\" 响应式编程\")]),t._v(\" \"),e(\"p\",[t._v(\"React中提出一个重要思想：状态改变则UI随之自动改变，而React框架本身就是响应用户状态改变的事件而执行重新构建用户界面的工作，这就是典型的\"),e(\"strong\",[t._v(\"响应式\")]),t._v(\"编程范式，下面我们总结一下React中响应式原理：\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"开发者只需关注状态转移（数据），当状态发生变化，React框架会自动根据新的状态重新构建UI。\")]),t._v(\" \"),e(\"li\",[t._v(\"React框架在接收到用户状态改变通知后，会根据当前渲染树，结合最新的状态改变，通过Diff算法，计算出树中变化的部分，然后只更新变化的部分（DOM操作），从而避免整棵树重构，提高性能。\")])]),t._v(\" \"),e(\"p\",[t._v(\"值得注意的是，在第二步中，状态变化后React框架并不会立即去计算并渲染DOM树的变化部分，相反，React会在DOM的基础上建立一个抽象层，即\"),e(\"strong\",[t._v(\"虚拟DOM\")]),t._v(\"树，对数据和状态所做的任何改动，都会被自动且高效的同步到虚拟DOM，最后再批量同步到真实DOM中，而不是每次改变都去操作一下DOM。为什么不能每次改变都直接去操作DOM树？这是因为在浏览器中每一次DOM操作都有可能引起浏览器的重绘或回流：\")]),t._v(\" \"),e(\"ol\",[e(\"li\",[t._v(\"如果DOM只是外观风格发生变化，如颜色变化，会导致浏览器重绘界面。\")]),t._v(\" \"),e(\"li\",[t._v(\"如果DOM树的结构发生变化，如尺寸、布局、节点隐藏等导致，浏览器就需要回流（及重新排版布局）。\")])]),t._v(\" \"),e(\"p\",[t._v(\"而浏览器的重绘和回流都是比较昂贵的操作，如果每一次改变都直接对DOM进行操作，这会带来性能问题，而批量操作只会触发一次DOM更新。\")]),t._v(\" \"),e(\"blockquote\",[e(\"p\",[t._v(\"思考题：Diff操作和DOM批量更新难道不应该是浏览器的职责吗？第三方框架中去做合不合适？\")])]),t._v(\" \"),e(\"blockquote\",[e(\"p\",[t._v(\"此处需要有一张插图\")])]),t._v(\" \"),e(\"h3\",{attrs:{id:\"react-native\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#react-native\"}},[t._v(\"#\")]),t._v(\" React Native\")]),t._v(\" \"),e(\"p\",[t._v(\"上文已经提到React Native 是React 在原生移动应用平台的衍生产物，那两者主要的区别是什么呢？其实，主要的区别在于虚拟DOM映射的对象是什么？React中虚拟DOM最终会映射为浏览器DOM树，而RN中虚拟DOM会通过 JavaScriptCore 映射为原生控件树。\")]),t._v(\" \"),e(\"p\",[t._v(\"JavaScriptCore 是一个JavaScript解释器，它在React Native中主要有两个作用：\")]),t._v(\" \"),e(\"ol\",[e(\"li\",[t._v(\"为JavaScript提供运行环境。\")]),t._v(\" \"),e(\"li\",[t._v(\"是JavaScript与原生应用之间通信的桥梁，作用和JsBridge一样，事实上，在iOS中，很多JsBridge的实现都是基于 JavaScriptCore 。\")])]),t._v(\" \"),e(\"p\",[t._v(\"而RN中将虚拟DOM映射为原生控件的过程中分两步：\")]),t._v(\" \"),e(\"ol\",[e(\"li\",[t._v(\"布局消息传递； 将虚拟DOM布局信息传递给原生；\")]),t._v(\" \"),e(\"li\",[t._v(\"原生根据布局信息通过对应的原生控件渲染控件树；\")])]),t._v(\" \"),e(\"p\",[t._v(\"至此，React Native 便实现了跨平台。 相对于混合应用，由于React Native是原生控件渲染，所以性能会比混合应用中H5好很多，同时React Native使用了Web开发技术栈，也只需维护一份代码，同样是跨平台框架。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"weex\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#weex\"}},[t._v(\"#\")]),t._v(\" Weex\")]),t._v(\" \"),e(\"p\",[t._v(\"Weex是阿里巴巴于2016年发布的跨平台移动端开发框架，思想及原理和React Native类似，最大的不同是语法层面，Weex支持Vue语法和Rax语法，Rax 的 DSL(Domain Specific Language) 语法是基于 React JSX 语法而创造。与 React 不同，在 Rax 中 JSX 是必选的，它不支持通过其它方式创建组件，所以学习 JSX 是使用 Rax 的必要基础。而React Native只支持JSX语法。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"快应用\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#快应用\"}},[t._v(\"#\")]),t._v(\" 快应用\")]),t._v(\" \"),e(\"p\",[t._v(\"快应用是华为、小米、OPPO、魅族等国内9大主流手机厂商共同制定的轻量级应用标准，目标直指微信小程序。它也是采用JavaScript语言开发，原生控件渲染，与React Native和Weex相比主要有两点不同：\")]),t._v(\" \"),e(\"ol\",[e(\"li\",[t._v(\"快应用自身不支持Vue或React语法，其采用原生JavaScript开发，其开发框架和微信小程序很像，值得一提的是小程序目前已经可以使用Vue语法开发（mpvue），从原理上来讲，Vue的语法也可以移植到快应用上。\")]),t._v(\" \"),e(\"li\",[t._v(\"React Native和Weex的渲染/排版引擎是集成到框架中的，每一个APP都需要打包一份，安装包体积较大；而快应用渲染/排版引擎是集成到ROM中的，应用中无需打包，安装包体积小，正因如此，快应用才能在保证性能的同时做到快速分发。\")])]),t._v(\" \"),e(\"h3\",{attrs:{id:\"总结-2\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结-2\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),e(\"p\",[t._v(\"JavaScript开发+原生渲染的方式主要优点如下：\")]),t._v(\" \"),e(\"ol\",[e(\"li\",[t._v(\"采用Web开发技术栈，社区庞大、上手快、开发成本相对较低。\")]),t._v(\" \"),e(\"li\",[t._v(\"原生渲染，性能相比H5提高很多。\")]),t._v(\" \"),e(\"li\",[t._v(\"动态化较好，支持热更新。\")])]),t._v(\" \"),e(\"p\",[t._v(\"不足：\")]),t._v(\" \"),e(\"ol\",[e(\"li\",[t._v(\"渲染时需要JavaScript和原生之间通信，在有些场景如拖动可能会因为通信频繁导致卡顿。\")]),t._v(\" \"),e(\"li\",[t._v(\"JavaScript为脚本语言，执行时需要JIT(Just In Time)，执行效率和AOT(Ahead Of Time)代码仍有差距。\")]),t._v(\" \"),e(\"li\",[t._v(\"由于渲染依赖原生控件，不同平台的控件需要单独维护，并且当系统更新时，社区控件可能会滞后；除此之外，其控件系统也会受到原生UI系统限制，例如，在Android中，手势冲突消歧规则是固定的，这在使用不同人写的控件嵌套时，手势冲突问题将会变得非常棘手。\")])]),t._v(\" \"),e(\"h2\",{attrs:{id:\"_1-1-4-qt-mobile\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-1-4-qt-mobile\"}},[t._v(\"#\")]),t._v(\" 1.1.4 QT Mobile\")]),t._v(\" \"),e(\"p\",[t._v(\"在本篇中，我们看看最后一种跨平台技术：自绘UI+原生。这种技术的思路是，通过在不同平台实现一个统一接口的渲染引擎来绘制UI，而不依赖系统原生控件，所以可以做到不同平台UI的一致性。注意，自绘引擎解决的是UI的跨平台问题，如果涉及其它系统能力调用，依然要涉及原生开发。这种平台技术的优点如下：\")]),t._v(\" \"),e(\"ol\",[e(\"li\",[e(\"p\",[t._v(\"性能高；由于自绘引擎是直接调用系统API来绘制UI，所以性能和原生控件接近。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[t._v(\"灵活、组件库易维护、UI外观保真度和一致性高；由于UI渲染不依赖原生控件，也就不需要根据不同平台的控件单独维护一套组件库，所以代码容易维护。由于组件库是同一套代码、同一个渲染引擎，所以在不同平台，组件显示外观可以做到高保真和高一致性；另外，由于不依赖原生控件，也就不会受原生布局系统的限制，这样布局系统会非常灵活。\")])])]),t._v(\" \"),e(\"p\",[t._v(\"不足：\")]),t._v(\" \"),e(\"ol\",[e(\"li\",[t._v(\"动态性不足；为了保证UI绘制性能，自绘UI系统一般都会采用AOT模式编译其发布包，所以应用发布后，不能像Hybrid和RN那些使用JavaScript（JIT）作为开发语言的框架那样动态下发代码。\")]),t._v(\" \"),e(\"li\",[t._v(\"开发效率低：QT使用C++作为其开发语言，而编程效率是直接会影响APP开发效率的，C++作为一门静态语言，在UI开发方面灵活性不及JavaScript这样的动态语言，另外，C++需要开发者手动去管理内存分配，没有JavaScript及Java中垃圾回收（GC）的机制。\")])]),t._v(\" \"),e(\"p\",[t._v(\"也许你已经猜到Flutter就属于这一类跨平台技术，没错，Flutter正是实现一套自绘引擎，并拥有一套自己的UI布局系统。不过，自绘制引擎的思路并不是什么新概念，Flutter并不是第一个尝试这么做的，在它之前有一个典型的代表，即大名鼎鼎的QT。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"qt简介\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#qt简介\"}},[t._v(\"#\")]),t._v(\" QT简介\")]),t._v(\" \"),e(\"p\",[t._v(\"Qt是一个1991年由Qt Company开发的跨平台C++图形用户界面应用程序开发框架。2008年，Qt Company科技被诺基亚公司收购，Qt也因此成为诺基亚旗下的编程语言工具。2012年，Qt被Digia收购。2014年4月，跨平台集成开发环境Qt Creator 3.1.0正式发布，实现了对于iOS的完全支持，新增WinRT、Beautifier等插件，废弃了无Python接口的GDB调试支持，集成了基于Clang的C/C++代码模块，并对Android支持做出了调整，至此实现了全面支持iOS、Android、WP，它提供给应用程序开发者构建图形用户界面所需的所有功能。但是，QT虽然在PC端获得了巨大成功，备受社区追捧，然而其在移动端却表现不佳，在近几年，虽然偶尔能听到QT的声音，但一直很弱，无论QT本身技术如何、设计思想如何，但事实上终究是败了，究其原因，笔者认为主要有四：\")]),t._v(\" \"),e(\"p\",[t._v(\"第一：QT移动开发社区太小，学习资料不足，生态不好。\")]),t._v(\" \"),e(\"p\",[t._v(\"第二：官方推广不利，支持不够。\")]),t._v(\" \"),e(\"p\",[t._v(\"第三：移动端发力较晚，市场已被其它动态化框架占领（Hybrid和RN)。\")]),t._v(\" \"),e(\"p\",[t._v(\"第四：在移动开发中，C++开发和Web开发栈相比有着先天的劣势，直接结果就是QT开发效率太低。\")]),t._v(\" \"),e(\"p\",[t._v(\"基于此四点，尽管QT是移动端开发跨平台自绘引擎的先驱，但却成为了烈士。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"_1-1-5-flutter出世\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-1-5-flutter出世\"}},[t._v(\"#\")]),t._v(\" 1.1.5 Flutter出世\")]),t._v(\" \"),e(\"p\",[t._v(\"“千呼万唤始出来”，铺垫这么久，现在终于等到本书的主角出场了！\")]),t._v(\" \"),e(\"p\",[t._v(\"Flutter是Google发布的一个用于创建跨平台、高性能移动应用的框架。Flutter和QT mobile一样，都没有使用原生控件，相反都实现了一个自绘引擎，使用自身的布局、绘制系统。那么，我们会担心，QT mobile面对的问题Flutter是否也一样，Flutter会不会步入QT mobile后尘，成为另一个烈士？要回到这个问题，我们先来看看Flutter诞生过程：\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"2017 年 Google I/O 大会上，Google 首次推出了一款新的用于创建跨平台、高性能的移动应用框架——Flutter。\")]),t._v(\" \"),e(\"li\",[t._v(\"2018年2月，Flutter发布了第一个Beta版本，同年五月， 在2018年Google I/O 大会上，Flutter 更新到了 beta 3 版本。\")]),t._v(\" \"),e(\"li\",[t._v(\"2018年6月，Flutter发布了首个预览版本，这意味着 Flutter 进入了正式版（1.0）发布前的最后阶段。\")])]),t._v(\" \"),e(\"p\",[t._v(\"观其发展，在2018年5月份，Flutter 进入了 GitHub stars 排行榜前 100 名，已有 27k  star。而今天(2019年5月29日)，已经有65K的Star。经历了短短2年多的时间，Flutter 生态系统得以快速增长，由此可见，Flutter在开发者中受到了热烈的欢迎，其未来发展值得期待！\")]),t._v(\" \"),e(\"p\",[t._v(\"现在，我们来和QT mobile做一个对比：\")]),t._v(\" \"),e(\"ol\",[e(\"li\",[t._v(\"生态：从Github上来看，目前Flutter活跃用户正在高速增长。从Stackoverflow上提问来看，Flutter社区现在已经很庞大。Flutter的文档、资源也越来越丰富，开发过程中遇到的很多问题都可以在Stackoverflow或其github issue中找到答案。\")]),t._v(\" \"),e(\"li\",[t._v(\"技术支持：现在Google正在大力推广Flutter，Flutter的作者中很多人都是来自Chromium团队，并且github上活跃度很高。另一个角度，从今年上半年Flutter频繁的版本发布也可以看出Google对Flutter的投入的资源不小，所以在官方技术支持这方面，大可不必担心。\")]),t._v(\" \"),e(\"li\",[t._v(\"开发效率：Flutter的热重载可帮助开发者快速地进行测试、构建UI、添加功能并更快地修复错误。在iOS和Android模拟器或真机上可以实现毫秒级热重载，并且不会丢失状态。这真的很棒，相信我，如果你是一名原生开发者，体验了Flutter开发流后，很可能就不想重新回去做原生了，毕竟很少有人不吐槽原生开发的编译速度。\")])]),t._v(\" \"),e(\"p\",[t._v(\"基于以上三点，相信读者和笔者一样，Flutter未来如何，心中自有定论。到现在为止，我们已经对移动端开发技术有了一个全面的了解，接下来我们便要进入本书的主题，你准备好了吗！\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"_1-1-6-小结\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-1-6-小结\"}},[t._v(\"#\")]),t._v(\" 1.1.6 小结\")]),t._v(\" \"),e(\"p\",[t._v(\"本章主要介绍了目前移动开发中三种跨平台技术，现在我们从框架角度对比一下它们，如表1-1所示：\")]),t._v(\" \"),e(\"table\",[e(\"thead\",[e(\"tr\",[e(\"th\",[t._v(\"技术类型\")]),t._v(\" \"),e(\"th\",[t._v(\"UI渲染方式\")]),t._v(\" \"),e(\"th\",[t._v(\"性能\")]),t._v(\" \"),e(\"th\",[t._v(\"开发效率\")]),t._v(\" \"),e(\"th\",[t._v(\"动态化\")]),t._v(\" \"),e(\"th\",[t._v(\"框架代表\")])])]),t._v(\" \"),e(\"tbody\",[e(\"tr\",[e(\"td\",[t._v(\"H5+原生\")]),t._v(\" \"),e(\"td\",[t._v(\"WebView渲染\")]),t._v(\" \"),e(\"td\",[t._v(\"一般\")]),t._v(\" \"),e(\"td\",[t._v(\"高\")]),t._v(\" \"),e(\"td\",[t._v(\"支持\")]),t._v(\" \"),e(\"td\",[t._v(\"Cordova、Ionic\")])]),t._v(\" \"),e(\"tr\",[e(\"td\",[t._v(\"JavaScript+原生渲染\")]),t._v(\" \"),e(\"td\",[t._v(\"原生控件渲染\")]),t._v(\" \"),e(\"td\",[t._v(\"好\")]),t._v(\" \"),e(\"td\",[t._v(\"中\")]),t._v(\" \"),e(\"td\",[t._v(\"支持\")]),t._v(\" \"),e(\"td\",[t._v(\"RN、Weex\")])]),t._v(\" \"),e(\"tr\",[e(\"td\",[t._v(\"自绘UI+原生\")]),t._v(\" \"),e(\"td\",[t._v(\"调用系统API渲染\")]),t._v(\" \"),e(\"td\",[t._v(\"好\")]),t._v(\" \"),e(\"td\",[t._v(\"Flutter高, QT低\")]),t._v(\" \"),e(\"td\",[t._v(\"默认不支持\")]),t._v(\" \"),e(\"td\",[t._v(\"QT、Flutter\")])])])]),t._v(\" \"),e(\"center\",[t._v(\"表1-1: 跨平台技术对比\")]),t._v(\"\\n上表中开发语言主要指UI的开发语言。而开发效率，是指整个开发周期的效率，包括编码时间、调试时间、以及排错、兼容时间。动态化主要指是否支持动态下发代码和是否支持热更新。值得注意的是Flutter的Release包默认是使用Dart AOT模式编译的，所以不支持动态化，但Dart还有JIT或snapshot运行方式，这些模式都是支持动态化的。\\n\")],1)}),[],!1,null,null,null);a.default=v.exports}}]);"
  },
  {
    "path": "docs/assets/js/133.85003caa.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[133],{727:function(t,r,e){\"use strict\";e.r(r);var a=e(45),o=Object(a.a)({},(function(){var t=this,r=t.$createElement,e=t._self._c||r;return e(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[e(\"h1\",{attrs:{id:\"本章目录\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本章目录\"}},[t._v(\"#\")]),t._v(\" 本章目录\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter10/intro.html\"}},[t._v(\"10.1：自定义组件方法简介\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter10/combine.html\"}},[t._v(\"10.2：组合现有组件\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter10/turn_box.html\"}},[t._v(\"10.3：组合实例：TurnBox\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter10/custom_paint.html\"}},[t._v(\"10.4：自绘组件（CustomPaint与Canvas）\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter10/gradient_circular_progress_demo.html\"}},[t._v(\"10.5：自绘实例：圆形渐变进度条(自绘)\")])],1)])])}),[],!1,null,null,null);r.default=o.exports}}]);"
  },
  {
    "path": "docs/assets/js/134.68a25fe9.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[134],{729:function(e,t,v){\"use strict\";v.r(t);var _=v(45),a=Object(_.a)({},(function(){var e=this,t=e.$createElement,v=e._self._c||t;return v(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":e.$parent.slotKey}},[v(\"h1\",{attrs:{id:\"_10-1-自定义组件方法简介\"}},[v(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_10-1-自定义组件方法简介\"}},[e._v(\"#\")]),e._v(\" 10.1 自定义组件方法简介\")]),e._v(\" \"),v(\"p\",[e._v(\"当Flutter提供的现有组件无法满足我们的需求，或者我们为了共享代码需要封装一些通用组件，这时我们就需要自定义组件。在Flutter中自定义组件有三种方式：通过组合其它组件、自绘和实现RenderObject。本节我们先分别介绍一下这三种方式的特点，后面章节中则详细介绍它们的细节。\")]),e._v(\" \"),v(\"h3\",{attrs:{id:\"组合其它widget\"}},[v(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#组合其它widget\"}},[e._v(\"#\")]),e._v(\" 组合其它Widget\")]),e._v(\" \"),v(\"p\",[e._v(\"这种方式是通过拼装其它组件来组合成一个新的组件。例如我们之前介绍的\"),v(\"code\",[e._v(\"Container\")]),e._v(\"就是一个组合组件，它是由\"),v(\"code\",[e._v(\"DecoratedBox\")]),e._v(\"、\"),v(\"code\",[e._v(\"ConstrainedBox\")]),e._v(\"、\"),v(\"code\",[e._v(\"Transform\")]),e._v(\"、\"),v(\"code\",[e._v(\"Padding\")]),e._v(\"、\"),v(\"code\",[e._v(\"Align\")]),e._v(\"等组件组成。\")]),e._v(\" \"),v(\"p\",[e._v(\"在Flutter中，组合的思想非常重要，Flutter提供了非常多的基础组件，而我们的界面开发其实就是按照需要组合这些组件来实现各种不同的布局而已。\")]),e._v(\" \"),v(\"h3\",{attrs:{id:\"自绘\"}},[v(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#自绘\"}},[e._v(\"#\")]),e._v(\" 自绘\")]),e._v(\" \"),v(\"p\",[e._v(\"如果遇到无法通过现有的组件来实现需要的UI时，我们可以通过自绘组件的方式来实现，例如我们需要一个颜色渐变的圆形进度条，而Flutter提供的\"),v(\"code\",[e._v(\"CircularProgressIndicator\")]),e._v(\"并不支持在显示精确进度时对进度条应用渐变色（其\"),v(\"code\",[e._v(\"valueColor\")]),e._v(\" 属性只支持执行旋转动画时变化Indicator的颜色），这时最好的方法就是通过自定义组件来绘制出我们期望的外观。我们可以通过Flutter中提供的\"),v(\"code\",[e._v(\"CustomPaint\")]),e._v(\"和\"),v(\"code\",[e._v(\"Canvas\")]),e._v(\"来实现UI自绘。\")]),e._v(\" \"),v(\"h3\",{attrs:{id:\"实现renderobject\"}},[v(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#实现renderobject\"}},[e._v(\"#\")]),e._v(\" 实现RenderObject\")]),e._v(\" \"),v(\"p\",[e._v(\"Flutter提供的自身具有UI外观的组件，如文本\"),v(\"code\",[e._v(\"Text\")]),e._v(\"、\"),v(\"code\",[e._v(\"Image\")]),e._v(\"都是通过相应的\"),v(\"code\",[e._v(\"RenderObject\")]),e._v(\"（我们将在“Flutter核心原理”一章中详细介绍\"),v(\"code\",[e._v(\"RenderObject\")]),e._v(\"）渲染出来的，如Text是由\"),v(\"code\",[e._v(\"RenderParagraph\")]),e._v(\"渲染；而\"),v(\"code\",[e._v(\"Image\")]),e._v(\"是由\"),v(\"code\",[e._v(\"RenderImage\")]),e._v(\"渲染。\"),v(\"code\",[e._v(\"RenderObject\")]),e._v(\"是一个抽象类，它定义了一个抽象方法\"),v(\"code\",[e._v(\"paint(...)\")]),e._v(\"：\")]),e._v(\" \"),v(\"div\",{staticClass:\"language-dart extra-class\"},[v(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[v(\"code\",[v(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[e._v(\"void\")]),e._v(\" \"),v(\"span\",{pre:!0,attrs:{class:\"token function\"}},[e._v(\"paint\")]),v(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[e._v(\"(\")]),e._v(\"PaintingContext context\"),v(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[e._v(\",\")]),e._v(\" Offset offset\"),v(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[e._v(\")\")]),e._v(\"\\n\")])])]),v(\"p\",[v(\"code\",[e._v(\"PaintingContext\")]),e._v(\"代表组件的绘制上下文，通过\"),v(\"code\",[e._v(\"PaintingContext.canvas\")]),e._v(\"可以获得\"),v(\"code\",[e._v(\"Canvas\")]),e._v(\"，而绘制逻辑主要是通过\"),v(\"code\",[e._v(\"Canvas\")]),e._v(\" API来实现。子类需要重写此方法以实现自身的绘制逻辑，如\"),v(\"code\",[e._v(\"RenderParagraph\")]),e._v(\"需要实现文本绘制逻辑，而\"),v(\"code\",[e._v(\"RenderImage\")]),e._v(\"需要实现图片绘制逻辑。\")]),e._v(\" \"),v(\"p\",[e._v(\"可以发现，\"),v(\"code\",[e._v(\"RenderObject\")]),e._v(\"中最终也是通过\"),v(\"code\",[e._v(\"Canvas\")]),e._v(\" API来绘制的，那么通过实现\"),v(\"code\",[e._v(\"RenderObject\")]),e._v(\"的方式和上面介绍的通过\"),v(\"code\",[e._v(\"CustomPaint\")]),e._v(\"和\"),v(\"code\",[e._v(\"Canvas\")]),e._v(\"自绘的方式有什么区别？其实答案很简单，\"),v(\"code\",[e._v(\"CustomPaint\")]),e._v(\"只是为了方便开发者封装的一个代理类，它直接继承自\"),v(\"code\",[e._v(\"SingleChildRenderObjectWidget\")]),e._v(\"，通过\"),v(\"code\",[e._v(\"RenderCustomPaint\")]),e._v(\"的\"),v(\"code\",[e._v(\"paint\")]),e._v(\"方法将\"),v(\"code\",[e._v(\"Canvas\")]),e._v(\"和画笔\"),v(\"code\",[e._v(\"Painter\")]),e._v(\"(需要开发者实现，后面章节介绍)连接起来实现了最终的绘制（绘制逻辑在\"),v(\"code\",[e._v(\"Painter\")]),e._v(\"中）。\")]),e._v(\" \"),v(\"h3\",{attrs:{id:\"总结\"}},[v(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[e._v(\"#\")]),e._v(\" 总结\")]),e._v(\" \"),v(\"p\",[e._v(\"“组合”是自定义组件最简单的方法，在任何需要自定义组件的场景下，我们都应该优先考虑是否能够通过组合来实现。而自绘和通过实现\"),v(\"code\",[e._v(\"RenderObject\")]),e._v(\"的方法本质上是一样的，都需要开发者调用\"),v(\"code\",[e._v(\"Canvas\")]),e._v(\" API手动去绘制UI，优点是强大灵活，理论上可以实现任何外观的UI，而缺点是必须了解\"),v(\"code\",[e._v(\"Canvas\")]),e._v(\" API细节，并且得自己去实现绘制逻辑。\")]),e._v(\" \"),v(\"p\",[e._v(\"在本章接下来的小节中，我们将通过一些实例来详细介绍自定义UI的过程，由于后两种方法本质是相同的，并且Flutter中很多基础组件都是通过\"),v(\"code\",[e._v(\"RenderObject\")]),e._v(\"的形式来实现的，所以后续我们只介绍\"),v(\"code\",[e._v(\"CustomPaint\")]),e._v(\"和\"),v(\"code\",[e._v(\"Canvas\")]),e._v(\"的方式，读者如果对自定义\"),v(\"code\",[e._v(\"RenderObject\")]),e._v(\"的方法好奇，可以查看Flutter中相关基础组件对应的\"),v(\"code\",[e._v(\"RenderObject\")]),e._v(\"的实现源码，如\"),v(\"code\",[e._v(\"RenderParagraph\")]),e._v(\"或\"),v(\"code\",[e._v(\"RenderImage\")]),e._v(\"。\")])])}),[],!1,null,null,null);t.default=a.exports}}]);"
  },
  {
    "path": "docs/assets/js/135.765bad57.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[135],{732:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_11-3-http请求-dio-http库\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_11-3-http请求-dio-http库\"}},[t._v(\"#\")]),t._v(\" 11.3 Http请求-Dio http库\")]),t._v(\" \"),a(\"p\",[t._v(\"通过上一节介绍，我们可以发现直接使用HttpClient发起网络请求是比较麻烦的，很多事情得我们手动处理，如果再涉及到文件上传/下载、Cookie管理等就会非常繁琐。幸运的是，Dart社区有一些第三方http请求库，用它们来发起http请求将会简单的多，本节我们介绍一下目前人气较高的\"),a(\"a\",{attrs:{href:\"https://github.com/flutterchina/dio\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"dio\"),a(\"OutboundLink\")],1),t._v(\"库。\")]),t._v(\" \"),a(\"blockquote\",[a(\"p\",[t._v(\"dio是一个强大的Dart Http请求库，支持Restful API、FormData、拦截器、请求取消、Cookie管理、文件上传/下载、超时等。dio的使用方式随着其版本升级可能会发生变化，如果本节所述内容和dio官方有差异，请以dio官方文档为准。\")])]),t._v(\" \"),a(\"h3\",{attrs:{id:\"引入\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#引入\"}},[t._v(\"#\")]),t._v(\" 引入\")]),t._v(\" \"),a(\"p\",[t._v(\"引入dio:\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-yaml extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"dependencies\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"dio\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ^x.x.x \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"#请使用pub上的最新版本\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"导入并创建dio实例：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:dio/dio.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\nDio dio \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Dio\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"接下来就可以通过 dio实例来发起网络请求了，注意，一个dio实例可以发起多个http请求，一般来说，APP只有一个http数据源时，dio应该使用单例模式。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"示例\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),a(\"p\",[t._v(\"发起 \"),a(\"code\",[t._v(\"GET\")]),t._v(\" 请求 :\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[t._v(\"Response response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\nresponse\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" dio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"/test?id=12&name=wendu\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"对于\"),a(\"code\",[t._v(\"GET\")]),t._v(\"请求我们可以将query参数通过对象来传递，上面的代码等同于：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[t._v(\"response\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" dio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"/test\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"queryParameters\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"id\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"12\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"name\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"wendu\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"发起一个 \"),a(\"code\",[t._v(\"POST\")]),t._v(\" 请求:\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[t._v(\"response\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" dio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"post\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"/test\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"data\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"id\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"12\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"name\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"wendu\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"发起多个并发请求:\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[t._v(\"response\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" Future\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"wait\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"dio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"post\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"/info\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"dio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"/token\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"下载文件:\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[t._v(\"response\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" dio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"download\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"https://www.google.com/\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"_savePath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"发送 FormData:\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[t._v(\"FormData formData \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FormData\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"from\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"name\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"wendux\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n   \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"age\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"25\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\nresponse \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" dio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"post\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"/info\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" data\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" formData\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"如果发送的数据是FormData，则dio会将请求header的\"),a(\"code\",[t._v(\"contentType\")]),t._v(\"设为“multipart/form-data”。\")]),t._v(\" \"),a(\"p\",[t._v(\"通过FormData上传多个文件:\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[t._v(\"FormData formData \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FormData\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"from\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"name\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"wendux\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n   \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"age\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"25\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n   \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"file1\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"UploadFileInfo\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"File\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"./upload.txt\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"upload1.txt\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n   \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"file2\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"UploadFileInfo\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"File\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"./upload.txt\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"upload2.txt\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n     \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 支持文件数组上传\")]),t._v(\"\\n   \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"files\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"UploadFileInfo\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"File\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"./example/upload.txt\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"upload.txt\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"UploadFileInfo\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"File\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"./example/upload.txt\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"upload.txt\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\nresponse \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" dio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"post\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"/info\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" data\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" formData\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"值得一提的是，dio内部仍然使用HttpClient发起的请求，所以代理、请求认证、证书校验等和HttpClient是相同的，我们可以在\"),a(\"code\",[t._v(\"onHttpClientCreate\")]),t._v(\"回调中设置，例如：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"dio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"httpClientAdapter \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"as\")]),t._v(\" DefaultHttpClientAdapter\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onHttpClientCreate \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"client\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//设置代理 \")]),t._v(\"\\n    client\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"findProxy \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"uri\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"PROXY 192.168.1.2:8888\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//校验证书\")]),t._v(\"\\n    httpClient\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"badCertificateCallback\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"X509Certificate cert\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" String host\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int port\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"cert\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"pem\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\"PEM\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//证书一致，则允许发送数据\")]),t._v(\"\\n     \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n     \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"   \\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"注意，\"),a(\"code\",[t._v(\"onHttpClientCreate\")]),t._v(\"会在当前dio实例内部需要创建HttpClient时调用，所以通过此回调配置HttpClient会对整个dio实例生效，如果你想针对某个应用请求单独的代理或证书校验策略，可以创建一个新的dio实例即可。\")]),t._v(\" \"),a(\"p\",[t._v(\"怎么样，是不是很简单，除了这些基本的用法，dio还支持请求配置、拦截器等，官方资料比较详细，故本书不再赘述，详情可以参考dio主页：https://github.com/flutterchina/dio 。 下一节我们将使用dio实现一个分块下载器。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"实例\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#实例\"}},[t._v(\"#\")]),t._v(\" 实例\")]),t._v(\" \"),a(\"p\",[t._v(\"我们通过Github开放的API来请求flutterchina组织下的所有公开的开源项目，实现：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"在请求阶段弹出loading\")]),t._v(\" \"),a(\"li\",[t._v(\"请求结束后，如果请求失败，则展示错误信息；如果成功，则将项目名称列表展示出来。\")])]),t._v(\" \"),a(\"p\",[t._v(\"代码如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_FutureBuilderRouteState\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"FutureBuilderRoute\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  Dio _dio \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Dio\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FutureBuilder\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          future\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _dio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"https://api.github.com/orgs/flutterchina/repos\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          builder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" AsyncSnapshot snapshot\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//请求完成\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"snapshot\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"connectionState \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" ConnectionState\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"done\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              Response response \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" snapshot\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//发生错误\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"snapshot\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"hasError\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"snapshot\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"error\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//请求成功，通过项目信息构建用于显示项目名称的ListView\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListView\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"map\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"full_name\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toList\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//请求未完成时弹出loading\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircularProgressIndicator\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/136.46f11ea2.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[136],{734:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_11-4-实例-http分块下载\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_11-4-实例-http分块下载\"}},[t._v(\"#\")]),t._v(\" 11.4 实例：Http分块下载\")]),t._v(\" \"),a(\"p\",[t._v(\"本节将通过一个“Http分块下载”的示例演示一下dio的具体用法。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"原理\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#原理\"}},[t._v(\"#\")]),t._v(\" 原理\")]),t._v(\" \"),a(\"p\",[t._v('Http协议定义了分块传输的响应header字段，但具体是否支持取决于Server的实现，我们可以指定请求头的\"range\"字段来验证服务器是否支持分块传输。例如，我们可以利用curl命令来验证：')]),t._v(\" \"),a(\"div\",{staticClass:\"language-shell extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[a(\"code\",[t._v(\"bogon:~ duwen$ \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"curl\")]),t._v(\" -H \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Range: bytes=0-10\"')]),t._v(\" http://download.dcloud.net.cn/HBuilder.9.0.2.macosx_64.dmg -v\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"# 请求头\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" GET /HBuilder.9.0.2.macosx_64.dmg HTTP/1.1\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Host: download.dcloud.net.cn\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" User-Agent: curl/7.54.0\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Accept: */*\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Range: \"),a(\"span\",{pre:!0,attrs:{class:\"token assign-left variable\"}},[t._v(\"bytes\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),t._v(\"-10\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"# 响应头\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\" HTTP/1.1 \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"206\")]),t._v(\" Partial Content\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\" Content-Type: application/octet-stream\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\" Content-Length: \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"11\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\" Connection: keep-alive\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\" Date: Thu, \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"21\")]),t._v(\" Feb \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2019\")]),t._v(\" 06:25:15 GMT\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\" Content-Range: bytes \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),t._v(\"-10/233295878\\n\\n\")])])]),a(\"p\",[t._v('我们在请求头中添加\"Range: bytes=0-10\"的作用是，告诉服务器本次请求我们只想获取文件0-10(包括10，共11字节)这块内容。如果服务器支持分块传输，则响应状态码为206，表示“部分内容”，并且同时响应头中包含“Content-Range”字段，如果不支持则不会包含。我们看看上面“Content-Range”的内容：')]),t._v(\" \"),a(\"div\",{staticClass:\"language- extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[a(\"code\",[t._v(\"Content-Range: bytes 0-10/233295878\\n\")])])]),a(\"p\",[t._v(\"0-10表示本次返回的区块，233295878代表文件的总长度，单位都是byte,  也就是该文件大概233M多一点。\")]),t._v(\" \"),a(\"p\",[t._v(\"基于此，我们可以设计一个简单的多线程的文件分块下载器，实现的思路是：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"先检测是否支持分块传输，如果不支持，则直接下载；若支持，则将剩余内容分块下载。\")]),t._v(\" \"),a(\"li\",[t._v(\"各个分块下载时保存到各自临时文件，等到所有分块下载完后合并临时文件。\")]),t._v(\" \"),a(\"li\",[t._v(\"删除临时文件。\")])]),t._v(\" \"),a(\"h3\",{attrs:{id:\"实现\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#实现\"}},[t._v(\"#\")]),t._v(\" 实现\")]),t._v(\" \"),a(\"p\",[t._v(\"下面是整体的流程：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 通过第一个分块请求检测服务器是否支持分块传输  \")]),t._v(\"\\nResponse response \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"downloadChunk\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"url\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" firstChunkSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"statusCode \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"206\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//如果支持\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//解析文件总长度，进而算出剩余长度\")]),t._v(\"\\n    total \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" int\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"parse\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"headers\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"value\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"HttpHeaders\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"contentRangeHeader\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"split\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"/\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"last\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    int reserved \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" total \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\"\\n        int\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"parse\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"headers\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"value\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"HttpHeaders\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"contentLengthHeader\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//文件的总块数(包括第一块)\")]),t._v(\"\\n    int chunk \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"reserved \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" firstChunkSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ceil\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"chunk \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        int chunkSize \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" firstChunkSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"chunk \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" maxChunk \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            chunk \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" maxChunk \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            chunkSize \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"reserved \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" maxChunk\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ceil\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" futures \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Future\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"for\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int i \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" i \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\" maxChunk\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"++\")]),t._v(\"i\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            int start \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" firstChunkSize \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" i \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" chunkSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//分块下载剩余文件  \")]),t._v(\"\\n            futures\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"downloadChunk\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"url\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" start\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" start \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" chunkSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" i \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//等待所有分块全部下载完成\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" Future\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"wait\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"futures\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//合并文件文件  \")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"mergeTempFiles\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"chunk\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"下面我们使用dio的\"),a(\"code\",[t._v(\"download\")]),t._v(\" API 实现\"),a(\"code\",[t._v(\"downloadChunk\")]),t._v(\"：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//start 代表当前块的起始位置，end代表结束位置\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//no 代表当前是第几块\")]),t._v(\"\\nFuture\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Response\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"downloadChunk\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"url\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" start\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" end\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" no\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  progress\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//progress记录每一块已接收数据的长度\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"--\")]),t._v(\"end\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" dio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"download\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    url\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    savePath \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"temp$no\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//临时文件按照块的序号命名，方便最后合并\")]),t._v(\"\\n    onReceiveProgress\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createCallback\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"no\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 创建进度回调，后面实现\")]),t._v(\"\\n    options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Options\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      headers\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"range\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"bytes=$start-$end\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//指定请求的内容区间\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"接下来实现\"),a(\"code\",[t._v(\"mergeTempFiles\")]),t._v(\":\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[t._v(\"Future \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"mergeTempFiles\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"chunk\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  File f \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"File\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"savePath \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"temp0\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  IOSink ioSink\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" f\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"openWrite\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"mode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" FileMode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"writeOnlyAppend\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//合并临时文件  \")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"for\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int i \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" i \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\" chunk\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"++\")]),t._v(\"i\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    File _f \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"File\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"savePath \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"temp$i\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" ioSink\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addStream\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_f\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"openRead\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" _f\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"delete\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//删除临时文件\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" ioSink\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"close\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" f\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"rename\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"savePath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//合并后的文件重命名为真正的名称\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"下面我们看一下完整实现：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// Downloading by spiting as file in chunks\")]),t._v(\"\\nFuture \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"downloadWithChunks\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  url\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  savePath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  ProgressCallback onReceiveProgress\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" firstChunkSize \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"102\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" maxChunk \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  int total \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" dio \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Dio\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" progress \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"int\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createCallback\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"no\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int received\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" _\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      progress\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"no\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" received\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"onReceiveProgress \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\" total \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onReceiveProgress\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"progress\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"reduce\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"a\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" b\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" a \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" b\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" total\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  Future\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Response\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"downloadChunk\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"url\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" start\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" end\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" no\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    progress\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"--\")]),t._v(\"end\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" dio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"download\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      url\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      savePath \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"temp$no\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      onReceiveProgress\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createCallback\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"no\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Options\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        headers\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"range\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"bytes=$start-$end\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  Future \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"mergeTempFiles\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"chunk\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    File f \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"File\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"savePath \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"temp0\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    IOSink ioSink\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" f\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"openWrite\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"mode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" FileMode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"writeOnlyAppend\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"for\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int i \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" i \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\" chunk\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"++\")]),t._v(\"i\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      File _f \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"File\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"savePath \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"temp$i\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" ioSink\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addStream\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_f\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"openRead\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" _f\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"delete\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" ioSink\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"close\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" f\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"rename\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"savePath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  Response response \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"downloadChunk\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"url\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" firstChunkSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"statusCode \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"206\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    total \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" int\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"parse\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"headers\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"value\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"HttpHeaders\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"contentRangeHeader\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"split\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"/\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"last\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    int reserved \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" total \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\"\\n        int\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"parse\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"headers\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"value\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"HttpHeaders\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"contentLengthHeader\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    int chunk \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"reserved \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" firstChunkSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ceil\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"chunk \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      int chunkSize \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" firstChunkSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"chunk \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" maxChunk \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        chunk \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" maxChunk \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        chunkSize \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"reserved \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" maxChunk\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ceil\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" futures \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Future\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"for\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int i \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" i \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\" maxChunk\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"++\")]),t._v(\"i\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        int start \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" firstChunkSize \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" i \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" chunkSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        futures\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"downloadChunk\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"url\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" start\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" start \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" chunkSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" i \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" Future\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"wait\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"futures\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"mergeTempFiles\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"chunk\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"现在可以进行分块下载了：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"main\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" url \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"http://download.dcloud.net.cn/HBuilder.9.0.2.macosx_64.dmg\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" savePath \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"./example/HBuilder.9.0.2.macosx_64.dmg\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"downloadWithChunks\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"url\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" savePath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" onReceiveProgress\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"received\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" total\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"total \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"${(received / total * 100).floor()}%\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"h3\",{attrs:{id:\"思考\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#思考\"}},[t._v(\"#\")]),t._v(\" 思考\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[a(\"p\",[t._v(\"分块下载真的能提高下载速度吗？\")]),t._v(\" \"),a(\"p\",[t._v(\"其实下载速度的主要瓶颈是取决于网络速度和服务器的出口速度，如果是同一个数据源，分块下载的意义并不大，因为服务器是同一个，出口速度确定的，主要取决于网速，而上面的例子正式同源分块下载，读者可以自己对比一下分块和不分块的的下载速度。如果有多个下载源，并且每个下载源的出口带宽都是有限制的，这时分块下载可能会更快一下，之所以说“可能”，是由于这并不是一定的，比如有三个源，三个源的出口带宽都为1G/s，而我们设备所连网络的峰值假设只有800M/s，那么瓶颈就在我们的网络。即使我们设备的带宽大于任意一个源，下载速度依然不一定就比单源单线下载快，试想一下，假设有两个源A和B，速度A源是B源的3倍，如果采用分块下载，两个源各下载一半的话，读者可以算一下所需的下载时间，然后再算一下只从A源下载所需的时间，看看哪个更快。\")]),t._v(\" \"),a(\"p\",[t._v(\"分块下载的最终速度受设备所在网络带宽、源出口速度、每个块大小、以及分块的数量等诸多因素影响，实际过程中很难保证速度最优。在实际开发中，读者可可以先测试对比后再决定是否使用。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"分块下载有什么实际的用处吗？\")]),t._v(\" \"),a(\"p\",[t._v(\"分块下载还有一个比较使用的场景是断点续传，可以将文件分为若干个块，然后维护一个下载状态文件用以记录每一个块的状态，这样即使在网络中断后，也可以恢复中断前的状态，具体实现读者可以自己尝试一下，还是有一些细节需要特别注意的，比如分块大小多少合适？下载到一半的块如何处理？要不要维护一个任务队列？\")])])])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/137.0345664d.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[137],{735:function(t,a,s){\"use strict\";s.r(a);var n=s(45),e=Object(n.a)({},(function(){var t=this,a=t.$createElement,s=t._self._c||a;return s(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[s(\"h1\",{attrs:{id:\"_11-1-文件操作\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_11-1-文件操作\"}},[t._v(\"#\")]),t._v(\" 11.1 文件操作\")]),t._v(\" \"),s(\"p\",[t._v(\"Dart的IO库包含了文件读写的相关类，它属于Dart语法标准的一部分，所以通过Dart IO库，无论是Dart VM下的脚本还是Flutter，都是通过Dart IO库来操作文件的，不过和Dart VM相比，Flutter有一个重要差异是文件系统路径不同，这是因为Dart VM是运行在PC或服务器操作系统下，而Flutter是运行在移动操作系统中，他们的文件系统会有一些差异。\")]),t._v(\" \"),s(\"h4\",{attrs:{id:\"app目录\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#app目录\"}},[t._v(\"#\")]),t._v(\" APP目录\")]),t._v(\" \"),s(\"p\",[t._v(\"Android和iOS的应用存储目录不同，\"),s(\"a\",{attrs:{href:\"https://pub.dartlang.org/packages/path_provider\",target:\"_blank\",rel:\"noopener noreferrer\"}},[s(\"code\",[t._v(\"PathProvider\")]),s(\"OutboundLink\")],1),t._v(\" 插件提供了一种平台透明的方式来访问设备文件系统上的常用位置。该类当前支持访问两个文件系统位置：\")]),t._v(\" \"),s(\"ul\",[s(\"li\",[s(\"strong\",[t._v(\"临时目录:\")]),t._v(\"  可以使用 \"),s(\"code\",[t._v(\"getTemporaryDirectory()\")]),t._v(\" 来获取临时目录； 系统可随时清除的临时目录（缓存）。在iOS上，这对应于\"),s(\"a\",{attrs:{href:\"https://developer.apple.com/reference/foundation/1409211-nstemporarydirectory\",target:\"_blank\",rel:\"noopener noreferrer\"}},[s(\"code\",[t._v(\"NSTemporaryDirectory()\")]),s(\"OutboundLink\")],1),t._v(\" 返回的值。在Android上，这是\"),s(\"a\",{attrs:{href:\"https://developer.android.com/reference/android/content/Context.html#getCacheDir()\",target:\"_blank\",rel:\"noopener noreferrer\"}},[s(\"code\",[t._v(\"getCacheDir()\")]),s(\"OutboundLink\")],1),t._v(\"返回的值。\")]),t._v(\" \"),s(\"li\",[s(\"strong\",[t._v(\"文档目录:\")]),t._v(\" 可以使用\"),s(\"code\",[t._v(\"getApplicationDocumentsDirectory()\")]),t._v(\"来获取应用程序的文档目录，该目录用于存储只有自己可以访问的文件。只有当应用程序被卸载时，系统才会清除该目录。在iOS上，这对应于\"),s(\"code\",[t._v(\"NSDocumentDirectory\")]),t._v(\"。在Android上，这是\"),s(\"code\",[t._v(\"AppData\")]),t._v(\"目录。\")]),t._v(\" \"),s(\"li\",[s(\"strong\",[t._v(\"外部存储目录\")]),t._v(\"：可以使用\"),s(\"code\",[t._v(\"getExternalStorageDirectory()\")]),t._v(\"来获取外部存储目录，如SD卡；由于iOS不支持外部目录，所以在iOS下调用该方法会抛出\"),s(\"code\",[t._v(\"UnsupportedError\")]),t._v(\"异常，而在Android下结果是android SDK中\"),s(\"code\",[t._v(\"getExternalStorageDirectory\")]),t._v(\"的返回值。\")])]),t._v(\" \"),s(\"p\",[t._v(\"一旦你的Flutter应用程序有一个文件位置的引用，你可以使用\"),s(\"a\",{attrs:{href:\"https://api.dartlang.org/stable/dart-io/dart-io-library.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"dart:io\"),s(\"OutboundLink\")],1),t._v(\"API来执行对文件系统的读/写操作。有关使用Dart处理文件和目录的详细内容可以参考Dart语言文档，下面我们看一个简单的例子。\")]),t._v(\" \"),s(\"h4\",{attrs:{id:\"示例\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),s(\"p\",[t._v(\"我们还是以计数器为例，实现在应用退出重启后可以恢复点击次数。 这里，我们使用文件来保存数据：\")]),t._v(\" \"),s(\"ol\",[s(\"li\",[s(\"p\",[t._v(\"引入PathProvider插件；在\"),s(\"code\",[t._v(\"pubspec.yaml\")]),t._v(\"文件中添加如下声明：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-yaml extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"path_provider\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ^0.4.1\\n\")])])]),s(\"p\",[t._v(\"添加后，执行\"),s(\"code\",[t._v(\"flutter packages get\")]),t._v(\" 获取一下, 版本号可能随着时间推移会发生变化，读者可以使用最新版。\")])]),t._v(\" \"),s(\"li\",[s(\"p\",[t._v(\"实现：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'dart:io'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'dart:async'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:path_provider/path_provider.dart'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FileOperationRoute\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FileOperationRoute\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"Key key\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _FileOperationRouteState \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_FileOperationRouteState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_FileOperationRouteState\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"FileOperationRoute\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  int _counter\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//从文件读取点击次数\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_readCounter\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int value\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        _counter \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" value\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  Future\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"File\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_getLocalFile\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 获取应用目录\")]),t._v(\"\\n    String dir \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getApplicationDocumentsDirectory\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"path\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"File\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'$dir/counter.txt'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  Future\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"int\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_readCounter\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      File file \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_getLocalFile\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 读取点击次数（以字符串）\")]),t._v(\"\\n      String contents \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" file\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"readAsString\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" int\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"parse\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"contents\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"on\")]),t._v(\" FileSystemException \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  Future\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Null\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_incrementCounter\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _counter\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"++\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 将点击次数以字符串类型写到文件中\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_getLocalFile\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"writeAsString\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'$_counter'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Scaffold\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      appBar\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AppBar\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'文件操作'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      body\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Center\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'点击了 $_counter 次'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      floatingActionButton\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FloatingActionButton\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        onPressed\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _incrementCounter\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        tooltip\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Increment'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Icon\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"add\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"上面代码比较简单，不再赘述，需要说明的是，本示例只是为了演示文件读写，而在实际开发中，如果要存储一些简单的数据，使用shared_preferences插件会比较简单。\")]),t._v(\" \"),s(\"blockquote\",[s(\"p\",[t._v(\"注意，Dart IO库操作文件的API非常丰富，但本书不是介绍Dart语言的，故不详细说明，读者需要的话可以自行学习。\")])])])])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/138.1a184d5a.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[138],{736:function(t,e,o){\"use strict\";o.r(e);var r=o(45),a=Object(r.a)({},(function(){var t=this,e=t.$createElement,o=t._self._c||e;return o(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[o(\"h1\",{attrs:{id:\"本章目录\"}},[o(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本章目录\"}},[t._v(\"#\")]),t._v(\" 本章目录\")]),t._v(\" \"),o(\"ul\",[o(\"li\",[o(\"RouterLink\",{attrs:{to:\"/chapter11/file_operation.html\"}},[t._v(\"11.1：文件操作\")])],1),t._v(\" \"),o(\"li\",[o(\"RouterLink\",{attrs:{to:\"/chapter11/http.html\"}},[t._v(\"11.2：Http请求-HttpClient\")])],1),t._v(\" \"),o(\"li\",[o(\"RouterLink\",{attrs:{to:\"/chapter11/dio.html\"}},[t._v(\"11.3：Http请求-Dio package\")])],1),t._v(\" \"),o(\"li\",[o(\"RouterLink\",{attrs:{to:\"/chapter11/download_with_chunks.html\"}},[t._v(\"11.4：实例：Http分块下载\")])],1),t._v(\" \"),o(\"li\",[o(\"RouterLink\",{attrs:{to:\"/chapter11/websocket.html\"}},[t._v(\"11.5：WebSocket\")])],1),t._v(\" \"),o(\"li\",[o(\"RouterLink\",{attrs:{to:\"/chapter11/socket.html\"}},[t._v(\"11.6：使用Socket API\")])],1),t._v(\" \"),o(\"li\",[o(\"RouterLink\",{attrs:{to:\"/chapter11/json_model.html\"}},[t._v(\"11.7：Json转Dart Model类\")])],1)])])}),[],!1,null,null,null);e.default=a.exports}}]);"
  },
  {
    "path": "docs/assets/js/139.94803f35.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[139],{738:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"使用websockets\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#使用websockets\"}},[t._v(\"#\")]),t._v(\" 使用WebSockets\")]),t._v(\" \"),a(\"p\",[t._v(\"Http协议是无状态的，只能由客户端主动发起，服务端再被动响应，服务端无法向客户端主动推送内容，并且一旦服务器响应结束，链接就会断开(见注解部分)，所以无法进行实时通信。WebSocket协议正是为解决客户端与服务端实时通信而产生的技术，现在已经被主流浏览器支持，所以对于Web开发者来说应该比较熟悉了，Flutter也提供了专门的包来支持WebSocket协议。\")]),t._v(\" \"),a(\"blockquote\",[a(\"p\",[t._v(\"注意：Http协议中虽然可以通过keep-alive机制使服务器在响应结束后链接会保持一段时间，但最终还是会断开，keep-alive机制主要是用于避免在同一台服务器请求多个资源时频繁创建链接，它本质上是支持链接复用的技术，而并非用于实时通信，读者需要知道这两者的区别。\")])]),t._v(\" \"),a(\"p\",[t._v(\"WebSocket协议本质上是一个基于tcp的协议，它是先通过HTTP协议发起一条特殊的http请求进行握手后，如果服务端支持WebSocket协议，则会进行协议升级。WebSocket会使用http协议握手后创建的tcp链接，和http协议不同的是，WebSocket的tcp链接是个长链接（不会断开），所以服务端与客户端就可以通过此TCP连接进行实时通信。有关WebSocket协议细节，读者可以看RFC文档，下面我们重点看看Flutter中如何使用WebSocket。\")]),t._v(\" \"),a(\"p\",[t._v(\"在接下来例子中，我们将连接到由\"),a(\"a\",{attrs:{href:\"http://www.websocket.org/echo.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"websocket.org提供的测试服务器\"),a(\"OutboundLink\")],1),t._v(\"。服务器将简单地返回我们发送给它的相同消息！\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"步骤\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#步骤\"}},[t._v(\"#\")]),t._v(\" 步骤\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"连接到WebSocket服务器。\")]),t._v(\" \"),a(\"li\",[t._v(\"监听来自服务器的消息。\")]),t._v(\" \"),a(\"li\",[t._v(\"将数据发送到服务器。\")]),t._v(\" \"),a(\"li\",[t._v(\"关闭WebSocket连接。\")])]),t._v(\" \"),a(\"h3\",{attrs:{id:\"_1-连接到websocket服务器\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-连接到websocket服务器\"}},[t._v(\"#\")]),t._v(\" 1. 连接到WebSocket服务器\")]),t._v(\" \"),a(\"p\",[a(\"a\",{attrs:{href:\"https://pub.dartlang.org/packages/web_socket_channel\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"web_socket_channel\"),a(\"OutboundLink\")],1),t._v(\" package 提供了我们需要连接到WebSocket服务器的工具。该package提供了一个\"),a(\"code\",[t._v(\"WebSocketChannel\")]),t._v(\"允许我们既可以监听来自服务器的消息，又可以将消息发送到服务器的方法。\")]),t._v(\" \"),a(\"p\",[t._v(\"在Flutter中，我们可以创建一个\"),a(\"code\",[t._v(\"WebSocketChannel\")]),t._v(\"连接到一台服务器：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" channel \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" IOWebSocketChannel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"connect\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'ws://echo.websocket.org'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"h3\",{attrs:{id:\"_2-监听来自服务器的消息\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_2-监听来自服务器的消息\"}},[t._v(\"#\")]),t._v(\" 2. 监听来自服务器的消息\")]),t._v(\" \"),a(\"p\",[t._v(\"现在我们建立了连接，我们可以监听来自服务器的消息，在我们发送消息给测试服务器之后，它会返回相同的消息。\")]),t._v(\" \"),a(\"p\",[t._v(\"我们如何收取消息并显示它们？在这个例子中，我们将使用一个\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/StreamBuilder-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"StreamBuilder\")]),a(\"OutboundLink\")],1),t._v(\" 来监听新消息， 并用一个Text来显示它们。\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StreamBuilder\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  stream\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" widget\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"channel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"stream\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  builder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" snapshot\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"snapshot\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"hasData \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'${snapshot.data}'\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"''\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"h4\",{attrs:{id:\"工作原理\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#工作原理\"}},[t._v(\"#\")]),t._v(\" 工作原理\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"WebSocketChannel\")]),t._v(\"提供了一个来自服务器的消息\"),a(\"code\",[t._v(\"Stream\")]),t._v(\" 。该\"),a(\"code\",[t._v(\"Stream\")]),t._v(\"类是\"),a(\"code\",[t._v(\"dart:async\")]),t._v(\"包中的一个基础类。它提供了一种方法来监听来自数据源的异步事件。与\"),a(\"code\",[t._v(\"Future\")]),t._v(\"返回单个异步响应不同，\"),a(\"code\",[t._v(\"Stream\")]),t._v(\"类可以随着时间推移传递很多事件。该\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/StreamBuilder-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"StreamBuilder\")]),a(\"OutboundLink\")],1),t._v(\" 组件将连接到一个\"),a(\"code\",[t._v(\"Stream\")]),t._v(\"， 并在每次收到消息时通知Flutter重新构建界面。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"_3-将数据发送到服务器\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-将数据发送到服务器\"}},[t._v(\"#\")]),t._v(\" 3. 将数据发送到服务器\")]),t._v(\" \"),a(\"p\",[t._v(\"为了将数据发送到服务器，我们会\"),a(\"code\",[t._v(\"add\")]),t._v(\"消息给\"),a(\"code\",[t._v(\"WebSocketChannel\")]),t._v(\"提供的sink。\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[t._v(\"channel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"sink\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Hello!'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"h4\",{attrs:{id:\"工作原理-2\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#工作原理-2\"}},[t._v(\"#\")]),t._v(\" 工作原理\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"WebSocketChannel\")]),t._v(\"提供了一个\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/dart-async/StreamSink-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"StreamSink\")]),a(\"OutboundLink\")],1),t._v(\"，它将消息发给服务器。\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"StreamSink\")]),t._v(\"类提供了给数据源同步或异步添加事件的一般方法。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"_4-关闭websocket连接\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_4-关闭websocket连接\"}},[t._v(\"#\")]),t._v(\" 4. 关闭WebSocket连接\")]),t._v(\" \"),a(\"p\",[t._v(\"在我们使用\"),a(\"code\",[t._v(\"WebSocket\")]),t._v(\"后，要关闭连接：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[t._v(\"channel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"sink\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"close\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"h3\",{attrs:{id:\"完整的例子\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#完整的例子\"}},[t._v(\"#\")]),t._v(\" 完整的例子\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:web_socket_channel/io.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"WebSocketRoute\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _WebSocketRouteState \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_WebSocketRouteState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_WebSocketRouteState\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"WebSocketRoute\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  TextEditingController _controller \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TextEditingController\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  IOWebSocketChannel channel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  String _text \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//创建websocket连接\")]),t._v(\"\\n    channel \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"IOWebSocketChannel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"connect\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'ws://echo.websocket.org'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Scaffold\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      appBar\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AppBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        title\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"WebSocket(内容回显)\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      body\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          crossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" CrossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Form\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TextFormField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"InputDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"labelText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Send a message'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StreamBuilder\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              stream\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" channel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"stream\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              builder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" snapshot\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//网络不通会走到这\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"snapshot\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"hasError\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                  _text \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"网络不通...\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"snapshot\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"hasData\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                  _text \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"echo: \"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\"snapshot\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"symmetric\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"vertical\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"24.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_text\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      floatingActionButton\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FloatingActionButton\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _sendMessage\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        tooltip\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Send message'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"send\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_sendMessage\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"text\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isNotEmpty\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      channel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"sink\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"text\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    channel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"sink\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"close\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"上面的例子比较简单，不再赘述。我们现在思考一个问题，假如我们想通过WebSocket传输二进制数据应该怎么做（比如要从服务器接收一张图片）？我们发现\"),a(\"code\",[t._v(\"StreamBuilder\")]),t._v(\"和\"),a(\"code\",[t._v(\"Stream\")]),t._v(\"都没有指定接收类型的参数，并且在创建WebSocket链接时也没有相应的配置，貌似没有什么办法……其实很简单，要接收二进制数据仍然使用\"),a(\"code\",[t._v(\"StreamBuilder\")]),t._v(\"，因为WebSocket中所有发送的数据使用帧的形式发送，而帧是有固定格式，每一个帧的数据类型都可以通过Opcode字段指定，它可以指定当前帧是文本类型还是二进制类型（还有其它类型），所以客户端在收到帧时就已经知道了其数据类型，所以flutter完全可以在收到数据后解析出正确的类型，所以就无需开发者去关心，当服务器传输的数据是指定为二进制时，\"),a(\"code\",[t._v(\"StreamBuilder\")]),t._v(\"的\"),a(\"code\",[t._v(\"snapshot.data\")]),t._v(\"的类型就是\"),a(\"code\",[t._v(\"List<int>\")]),t._v(\"，是文本时，则为\"),a(\"code\",[t._v(\"String\")]),t._v(\"。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/14.4d0ac363.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[14],{422:function(t,s,n){t.exports=n.p+\"assets/img/3-24.68d03561.png\"},423:function(t,s,n){t.exports=n.p+\"assets/img/3-25.18d09233.png\"},424:function(t,s){t.exports=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAWgAAAArCAYAAABVXhKjAAAMJWlDQ1BJQ0MgUHJvZmlsZQAASImVVwdUk8kWnr8kISGhBUKREnoTpUiXGloEAamCjZAEEkoMgSBiBVlUYC2oWLCiqyKKrgWQxYZdWRTsdUFERVlFXWyovEkC6LqvnHfPmZnv3Ln3znennRkAVCM5YnEGqgZApihHEhXsz5yckMgkPQIo0AbKgATGcrjZYr/IyDAAZbj9u7y7CRBZe81OFuuf/f9V1Hn8bC4ASCTEybxsbibEhwHAXbhiSQ4AhF6oN52VI4aYCFkCTQkkCLGZDKcqsJsMJytwmNwmJooFcRIASlQOR5IKgIqMFzOXmwrjqJRBbC/iCUUQN0HszRVweBB/hnh0ZuZMiFWtILZK/i5O6t9iJo/E5HBSR7AiF7koBQizxRmc2f/ndPxvycyQDo9hCgtVIAmJkuUsm7f0maEyTIX4gig5PAJiDYivC3lyexl+IpCGxA7Zf+Bms+CcAQYAKJXHCQiFWB9iE2l6rN8Q9uZI5L7QHk3MF8TEK+KjIsnMqKH4aL4oIzxsKE6ZgM8exlX87MDoYZsUYRAbYriGaIMwhx0zFPNCrjAuHGIViO9np0eHDvk+zxewwkfGkkbJOMM1x0Bm9nAumFmKJChKYY+5CITs8CF9WI4gJkThi03ncuQcdCBO42dPDhvmw+MHBCr4YIV8UewQT6xcnOMfNWS/Q5wROWSPNfEzgmV6E4hbs3Ojh337cuBmU+SCgzTOhEjFuLimOCcyRsENZ4IwwAIBgAmksCSDmSANCFt763vBcE8Q4AAJSAV8YDekGfaIl/eIYB0N8sGfEPFB9oifv7yXD3Kh/suIVlHbgRR5b67cIx08gTgT18O9cU88DNa+sDjibrj7sB9TdXhUYiAxgBhCDCJazxAWSn6IywRcmEEGLBIQCls+zErGQTTM/VscwhNCG+ER4Qahg3AHxIHH0E74jwy/RROO6CaCDhg1aCi75O+zwy0ga2fcH/eC/CF3nIHrATt8HMzED/eBuTlD7bdZ+3fcpcOsyfZklKxN9iVb/WinYqPiPOIjy+17ngpeySOZsEZ6fhyN9V1uPNiG/miJLcEOYeexU9hFrAmrB0zsBNaAtWDHZHhkbzyW743h0aLkfNJhHOGwjX2NfY/95x/G5gyNL5GvP8jh5+XIDg5rpni2RJgqyGH6wduaz2SLuGNGMx3tHeAtKrv7FVfLW4b8TkcYl77pCuYAMP7Q4OBg0zddGLxZjhgAQHn1TWf1Hh5newAuFHClklyFDpdVBEABqvCk6AJDeHdZwYwcgQvwBL4gEEwAESAGJIDpcJ4FIBOyngXmggJQDErBCrAGbABbwHawG+wDB0E9aAKnwDlwGVwFN8A9uFe6wQvQB96BAQRBSAgNoSO6iBFijtgijogb4o0EImFIFJKAJCGpiAiRInORRUgpUo5sQLYh1civyFHkFHIRaUPuIJ1ID/IG+YRiKBXVRA1QC3Qs6ob6oaFoDDoNTUWz0Hy0CF2GrkOr0L1oHXoKvYzeQDvQF2g/BjBljIEZY3aYG8bCIrBELAWTYPOxEqwCq8JqsUa40tewDqwX+4gTcTrOxO3gfg3BY3EunoXPx8vwDfhuvA4/g1/DO/E+/CuBRtAn2BI8CGzCZEIqYRahmFBB2Ek4QjgLz1Q34R2RSGQQLYmu8KwmENOIc4hlxE3E/cSTxDZiF7GfRCLpkmxJXqQIEoeUQyomrSftJZ0gtZO6SR+UlJWMlByVgpQSlURKhUoVSnuUjiu1Kz1VGiCrkc3JHuQIMo88m7ycvIPcSL5C7iYPUNQplhQvSgwljVJAWUeppZyl3Ke8VVZWNlF2V56kLFReqLxO+YDyBeVO5Y9UDaoNlUWdSpVSl1F3UU9S71Df0mg0C5ovLZGWQ1tGq6adpj2kfVChq4xRYavwVBaoVKrUqbSrvFQlq5qr+qlOV81XrVA9pHpFtVeNrGahxlLjqM1Xq1Q7qnZLrV+dru6gHqGeqV6mvkf9ovozDZKGhUagBk+jSGO7xmmNLjpGN6Wz6Fz6IvoO+ll6tyZR01KTrZmmWaq5T7NVs09LQ2ucVpxWnlal1jGtDgbGsGCwGRmM5YyDjJuMT9oG2n7afO2l2rXa7drvdUbp+OrwdUp09uvc0Pmky9QN1E3XXalbr/tAD9ez0ZukN0tvs95Zvd5RmqM8R3FHlYw6OOquPqpvox+lP0d/u36Lfr+BoUGwgdhgvcFpg15DhqGvYZrhasPjhj1GdCNvI6HRaqMTRs+ZWkw/ZgZzHfMMs89Y3zjEWGq8zbjVeMDE0iTWpNBkv8kDU4qpm2mK6WrTZtM+MyOziWZzzWrM7pqTzd3MBeZrzc+bv7ewtIi3WGxRb/HMUseSbZlvWWN534pm5WOVZVVldd2aaO1mnW69yfqqDWrjbCOwqbS5YovautgKbTfZto0mjHYfLRpdNfqWHdXOzy7XrsaucwxjTNiYwjH1Y16ONRubOHbl2PNjv9o722fY77C/56DhMMGh0KHR4Y2jjSPXsdLxuhPNKchpgVOD0+txtuP44zaPu+1Md57ovNi52fmLi6uLxKXWpcfVzDXJdaPrLTdNt0i3MrcL7gR3f/cF7k3uHz1cPHI8Dnq88rTzTPfc4/lsvOV4/vgd47u8TLw4Xtu8OryZ3kneW707fIx9OD5VPo98TX15vjt9n/pZ+6X57fV76W/vL/E/4v+e5cGaxzoZgAUEB5QEtAZqBMYGbgh8GGQSlBpUE9QX7Bw8J/hkCCEkNGRlyC22AZvLrmb3TXCdMG/CmVBqaHTohtBHYTZhkrDGiejECRNXTbwfbh4uCq+PABHsiFURDyItI7Mif5tEnBQ5qXLSkyiHqLlR56Pp0TOi90S/i/GPWR5zL9YqVhrbHKcaNzWuOu59fEB8eXzH5LGT502+nKCXIExoSCQlxiXuTOyfEjhlzZTuqc5Ti6fenGY5LW/axel60zOmH5uhOoMz41ASISk+aU/SZ04Ep4rTn8xO3pjcx2Vx13Jf8Hx5q3k9fC9+Of9pildKecqzVK/UVak9Ah9BhaBXyBJuEL5OC0nbkvY+PSJ9V/pgRnzG/kylzKTMoyINUbrozEzDmXkz28S24mJxR5ZH1pqsPkmoZGc2kj0tuyFHEz6yW6RW0p+knbneuZW5H2bFzTqUp54nymuZbTN76eyn+UH5v8zB53DnNM81nlswt3Oe37xt85H5yfObF5guKFrQvTB44e4CSkF6we+F9oXlhX8til/UWGRQtLCo66fgn2qKVYolxbcWey7esgRfIlzSutRp6fqlX0t4JZdK7UsrSj+Xccsu/ezw87qfB5elLGtd7rJ88wriCtGKmyt9Vu4uVy/PL+9aNXFV3Wrm6pLVf62ZseZixbiKLWspa6VrO9aFrWtYb7Z+xfrPGwQbblT6V+7fqL9x6cb3m3ib2jf7bq7dYrCldMunrcKtt7cFb6ursqiq2E7cnrv9yY64Hed/cfuleqfeztKdX3aJdnXsjtp9ptq1unqP/p7lNWiNtKZn79S9V/cF7Guotavdtp+xv/QAOCA98PzXpF9vHgw92HzI7VDtYfPDG4/Qj5TUIXWz6/rqBfUdDQkNbUcnHG1u9Gw88tuY33Y1GTdVHtM6tvw45XjR8cET+Sf6T4pP9p5KPdXVPKP53unJp6+fmXSm9Wzo2Qvngs6dPu93/sQFrwtNFz0uHr3kdqn+ssvluhbnliO/O/9+pNWlte6K65WGq+5XG9vGtx1v92k/dS3g2rnr7OuXb4TfaLsZe/P2ram3Om7zbj+7k3Hn9d3cuwP3Ft4n3C95oPag4qH+w6o/rP/Y3+HScawzoLPlUfSje13crhePsx9/7i56QntS8dToafUzx2dNPUE9V59Ped79QvxioLf4T/U/N760enn4le+rlr7Jfd2vJa8H35S91X27669xfzX3R/Y/fJf5buB9yQfdD7s/un08/yn+09OBWZ9Jn9d9sf7S+DX06/3BzMFBMUfCkT8FMFjQlBQA3uwCgJYAAP0qfD9MUfzN5IIo/pNyBP4TVvzf5OICQC1sZM9w1kkADsBi6QtjwyJ7jsf4AtTJaaQMSXaKk6MiFhX+cAgfBgffwncMqRGAL5LBwYFNg4NfdkCydwA4maX4E8pE9gfdKo/RzshbCH6QfwEOk3CtyyHjHgAAAAlwSFlzAAAWJQAAFiUBSVIk8AAAAVlpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDUuNC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KTMInWQAAEDxJREFUeAHtXXt0FOUV/+1mkk0IhIR3EhIgBOQRoCoYotRKS0Gtj9rqaWtFT1v7PtRHqUd7Tqvt4Y+qR/uyFg71VFvRHsVXbWspRRR5lEcCorwJCYQ3ISEJeexms9t778zszmw2sIQN4eD9kuzOfN+997vzm8lvbu53Z+Px+/1haFMEFAFFQBG46BAw6uvrLzqn1CFFQBFQBBQBwNPS0qIRtF4JioAioAhchAgYqampF6Fb6pIioAgoAoqAEQ5rAH2ul0EYYXjo61Jv53Oc56PbW7he6j7zb/qlf9X21tXTM/N6e8bspW31k0DOfAbP5zjPR7e3rp5L3Wcl5966sro/rxJ097FTTUVAEVAEehQBJegehVeNKwKKgCLQfQSUoLuPnWoqAoqAItCjCChB9yi8alwRUAQUge4joATdfexUUxFQBBSBHkXA6K51Dy0JX2qrwiGtOOzu5aB6ioAi0AMISB20h9k2gcb8xSF3axDYURtGoIP2RZVfbHZLzFYC08UXsaey3+NLOXptv7iLlDrpmeNMzuMHeZCdDnSESIzltCkCioAi0IsIyJOEwSAxboKNiaudCKz8BHAqaCAVISK0DnhTDOG+UEdHhKoTNNmDYvRAicdLvqVA/AqT4664PzruD3ZgZHYYA+gAO5Sce/CcqGlFQBFIFAGjvLwcEyZMQFpaGhJ7qtAkNSMUwLEdFRiam4+swfk4vn83ggE/ho4aR6ToIVuJutBTcuSn14tAazOO7t6JwQXFyOyXjRDdTOwwmsm73d9K49vRL3cUUlIG01iQKFwZuqfOitpVBBSBxBHwPv30U2hubhaNDo5+4zBriPpCoRDClAfgYY83RYjvt7+8H0eqdsFreLFt7X+w4Z2XITYoESI6ll6IIle2y5wd+2PK0bhjTOZyyLM+y9kytk5nu5afJNtB/nJCprnxFJY+cR9OnTgCD0X5YscaD9ONhMdffeJ+nDxygCJtLz5Y9T7+vWwZkXWKYKIvioAioAj0FgLe/v2zYRiG/KSnp8u7TdL2u4+ia5/PRz9pEltKP5Fbfm6epA+I72Ck+ZCWnmkdB1Mp9Rmp8PVJp/50IseUzuRPiikkk+ajcbIn7E96aRlu+VQaZzkZFx0DPpJhu5xaEX+on28crMt9vvQMGD4mWQ+yhgyXaJp9YpZnovZlZJC+QTcXA/0G5cJLunxwH23dgpUr3zP9EQV9UQQUAUWgdxAwmHgbGxtRUVGBpqYmTJ8+HUOGDEF7e7uQNUezGzduxN69ezF+/HhMKplI0aVXSJHHJKwV3zm6NomZd1OI+GoPV6GG0iBpGZkomlyG9L790RFsj5AxE2nd4WrUHqrCiInTiMz7or2tBdtWv4NRk6ej3wDyw9+G3RvfQzaR7MC8kUK0jbVHsO/DdXJTGDWpFJnZg2juEFoa6rB/ezkKx1+Bmp0VyOw/UPQ62gOmh+QfE3JLw0nsqfgAmVkDaDxX7HA0zgueWVn9MXBAu8jriyKgCCgCvYmAsWvXLixevFgImbdfeeUVPP744ygoKBDi5rE1a9agqKgIf3h2Ie79xl249WvfIjLzICBpBKf7RND0zcS7Y91yrH5tIXKGFQrJblnxOuZ88xEMGl6EoEWYnCMOdQTxzPdm4dG39xLRZqHuyHEsfvCLeOilciLPoWg7HcDvvj0TDzy/FnnFxWR3FVb89SmyM5py3m1gu7Pu+QkKxk1AbU09Fs67EVd/+Ttorq/F1BvuRM7QAom8w+SrkebBkcpq/GvRL+hGESAbxQjRDePwjnIzgqZD8ZJP0duM89h0WxFQBBSBC4uAsWPHDjz22GMoLS1FQ0MDSkpKsGfPHiHoFStWYP369ViyZAkyKCVw4MAB3DKzBEVTrkG/jAKcbDUjU9tlJsGU1DTUHtyHl3/+ddzz5BsYO3UGEXIHPli6CMtfeBK3PfAEpRb6CDFz1Ntv4FCMmXqdLDByBLt382oxt/N/yzF87BS6cbThstLPY2BuIeqP1eL1px7El+b/GpdN+zTZANa+9Wf884+P4p4Ff6GUBtXIUSu+/FpcMft2pNJfB7UH91PqwiOpmGAgjPVvvyA3jZu+/xiNp2JvxVpsenOhkDJnWVLo5qL5Z4FRXxQBRaCXEfBef/31GDt2rKQnOAfN+6dPn5bFPk5t8FhNTQ242qOBUiG79zXh+IlaSn9QRXSQF+KsxsEzpxAo11yzazOKp99ARF4Gf6uf+NGLyZ+5BWtfW4TmU3VCgsyIXPrmy+iLstvuxc71K0i2Ha1NDbjj4WeIjA9K2qJy8xqMK5tN0W4+TtRUSrQ7suQqBPzt5GMQV8y6HdWb36OxPZKn5n8/MHbadbSdRjcGqsgg1uUonW8cTXXHsOxPCzDtxjslym8PBDEwfxRGTZstx0vBM/gfGHBOnpszZSMd+qIIKAKKwAVEQB715soLbkxInHvmCLK1tVVy0ocOHZIoeuvWrdi0aRN+v+hZFI0sRBuRG9LcDyJyyoKj6HYqt0vvm2UuzJFNk7hNWU4hSKNo1Yy4U9B/UB6qtq6TXPSxqp24mgg7a9AwHNq9lXLU+4lcU4lQOXWSggFULsc5ZS6FM9MWPqRm9oddy83WpeaZ/HCWy9F0EcL10WIm63LjnDj/kJscaAtRt7W1CbGLgL4oAoqAItBLCLgZlpwwI84QOJpmoi4rK8PcuXOFAHmfx5uJm9esO468jLSo2xbhMkmn+TIkEhYSJHnWYRLkJguLthb1hygKH5g/kvLLk1D10XpkD82nFIhPFvh2bliBPlk5GDnxKklnhCkSPlmzFwZFw0S3kjf2U52zv4kjevNfd0n+mNlYGJknkh6LgM2bg7/1tHnzIJLm6hA5LpJkzh49ZgxyBue5/WQz2hQBRUARuMAIeGP/JyFXdfCTgfxn/qdnzJD8NEfRvN9EqY9//P1NVB04jEwquWumOmMmX26cxuDKDU4nFIy7HFWUdtiz6X0hW+7b/N+luPar89A3Z6CkJphBbeLOGVYg+i/+7G5MmXkbkSc9dk1pjY9XvY1KykkPLiymqBzIyS3EmNJZ2LtlDS34pQrJbnznJRRfNQdDRoyRPPaAkWNjIPRIFQkvCmZRvvsLP1yANW88Z+a8jRRaNNyOQ9s3SGqDMiIYT4uNZWXThaDtY4sxqLuKgCKgCFwQBIzq6mrzT3+LaCsrK8F/4nObPWcO5s+fj3nz5iE/P58IuhmjC/NQUjoTwWNBNKxbhuDc7xK5Am3NTWhprJcKDY6I71qwBKtpYZCrLAL0tB6nFT53948l5xxsN/PSPAfHtwYtzOUMG867VGrH6QegL5XO1e77GCXX3ixRLj+lmE2R7a0/+hXeXfIbfPjuG1LFwbnmm3+wAOmZ6Th52I+j1bsj6Qu2x08OHvx4rfhF02D6TXdj+fNP4rmHvoIh9NRjBpX2nTh6SKo6eJFy6at/Q83ROvz0kYfRTo/Am7cftqRNEVAEFIELi4Bn5cqV4SuvvBJ9+vRBkHLRa6mkLjc3F8VU0sYRJP9s374d27ZtQ+GIESidNhXNHQZerGjA5vINyB9RRCmKIqo73ixRKUfPnObg1EED1StXb11PEWwfjL58BlVNZEiqwxWZysIiRef1x6nE7gDyx06WBTzOSRzc9SGV2uVRPjo3kiLh9EZLYx32lK+Shb/Rn7oGGVRfzUTMN4hqSpMUTbmaCDuLkAzTjaNRaqPzx0wi0h9MvnngbzkttdWcJx8xcSoO7tyCzKGFuO+zI9BQswONzW2YMnmypjku7LWosykCikAMAh5awAvzAhvnhpk47ZRHIMA5Y/7cjWgf63IuuaEtjLcqPTgdpvwthbtB6jNSfZL35QU8XnBjXa6HNlL5CUIq+CB7XFZnJod53NnM6g+Wj+pTZE1kzMTLKZKonpl7NqwFyiAtVpqfr8E1zClE2oZUb4RJj5vd10H5C1uObyCplCJhvzp4UZRuJvxhSXcUBZGfQ8dBLRCghUg6dm2KgCKgCPQWAobf75eHM2wy4n3eNvdNgmKy4koMbgY9RUiDaAuGiajb4EuhagraD7eaaREmP2lh0iViDVM5HDexx4RnmpG+yAv3y+eAcFkc26cRkgsHrWoKHo80S5arSKhF7PIO26D+s/fRU4f0kAs3kaXtAPkbprlDlKumsm3CxDmniOqLIqAIKAIXFAEjUvZmTRu7z91MYvxjcytz19AMIJNqoYmfbU17w/HOgxEBR3+8zXiyFtl3Eo8ny0Lx+hPp84DuN+DSbjpQ/tamCCgCikCvI+Ch6Njm3XNyxgqoz0nnYhdWYr7Yz5D6pwh8shDoVAed6OErmSWKlMopAoqAItA9BLrKIXTPmmopAoqAIqAIJA0BJeikQamGFAFFQBFILgJK0MnFU60pAoqAIpA0BJSgkwalGlIEFAFFILkIKEEnF0+1pggoAopA0hBQgk4alGpIEVAEFIHkIqAEnVw81ZoioAgoAklDwLAf4U6aRTHEj+J16/mXs7ohTzTyUzJnnMI56NzmBwXpici4T9k45ZzbZ3XJLZCoaie5Th1uu7x3RpEzDoqtro89diq3rYieu7uzQ53GY+0mth+Zj2fo8nzF2HLO3dV2jEoydxP20zmpw0+XvqPfKZ7sbdeciRrvyreu+hO1m6DcmX3uwok43We243bmXGTdmuexZ/ls1NXVnYcVVb20EeCbLF8p2hQBRaA3EDAyMzN7Y16dUxFQBBQBReAsCHjoo0bNXAS90n8PdIlzaM/NmRKgjxKSoIpledsek23ui9Fx/nngGrNCeJnAuS0d7pdYGzyn3We/s4ZsW35FDsU8BMm4iM/Wn8wsy822ZW/zu9Mm70uL8TGuDAlG+knew5/oZ+mFPSZeljV5E3/kU/TiYMnyrE/NPi9d+ezsFwV6ifhhd1h9jAv7EsHHMS6n1vLZPq88LPb50iB3BC8+7/TlnMO+Lmy7seMRO7ThtM39sc1l1zpfLOPsj+gIxBZOVuoq4q8ouX2O6Dnsie+0H9dn61hNUzJZXP9dvrE77l8lVo+2Lny2sY3FOaroxsD5O+fE1O6P6HXhT1c+u/ptI2fy2fm75MArohrnHMbOkQyfeT4nDoxj5Nw6rg3BmX3ik0TfneYmO07/nNv2MfF7J704OIsuTWJzgcs/pzF7O8Znw/4fgab/7quKjXNzGyW3qFsuZnIxOmZ6F6vjPDjXmCluuuXcNntcr7E2IgA7iJoVRI7BEL9ME9YhRPy0bTl9cW5H7Fgn1LTS+dW2Ezvi7rewYli7IGi+QMhdy2cTf/FfSNQNjNPPrrZtf9x+mL1mH2+7z7OtY0mZ59dx/LaenHfpN/1yzsE+87epZh0HKUSvDxq2Toazzz235YFDzzWHoz+iJ9OyP9Hr1PaX+3hKcz6WcR931HZ0LNrH2rHNPp9uOyzl0ouaizVg7tO44EV7NhZRfeccnQ1F5aKm4/VFR2mrsxkZduk5ZFz9tqEz+Mzy3M6OcxSnuHPYc/G7wx9Xt/MacMi4fbA0zuKzTdDxJnP659x2+tJp2+GPPSa6fN1R4BO9Fu3ROO8xPv8fRqEdOIGc4cwAAAAASUVORK5CYII=\"},425:function(t,s,n){t.exports=n.p+\"assets/img/3-27.6ff2b58c.png\"},426:function(t,s,n){t.exports=n.p+\"assets/img/3-28.b6b9c9b2.png\"},427:function(t,s,n){t.exports=n.p+\"assets/img/3-29.ff4de036.png\"},776:function(t,s,n){\"use strict\";n.r(s);var a=n(45),e=Object(a.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_3-7-输入框及表单\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-7-输入框及表单\"}},[t._v(\"#\")]),t._v(\" 3.7 输入框及表单\")]),t._v(\" \"),a(\"p\",[t._v(\"Material组件库中提供了输入框组件\"),a(\"code\",[t._v(\"TextField\")]),t._v(\"和表单组件\"),a(\"code\",[t._v(\"Form\")]),t._v(\"。下面我们分别介绍一下。\")]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_3-7-1-textfield\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-7-1-textfield\"}},[t._v(\"#\")]),t._v(\" 3.7.1 TextField\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"TextField\")]),t._v(\"用于文本输入，它提供了很多属性，我们先简单介绍一下主要属性的作用，然后通过几个示例来演示一下关键属性的用法。\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  TextEditingController controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n  FocusNode focusNode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  InputDecoration decoration \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"InputDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  TextInputType keyboardType\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  TextInputAction textInputAction\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  TextStyle style\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  TextAlign textAlign \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" TextAlign\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  bool autofocus \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  bool obscureText \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  int maxLines \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  int maxLength\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  bool maxLengthEnforced \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  ValueChanged\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" onChanged\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  VoidCallback onEditingComplete\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  ValueChanged\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" onSubmitted\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  List\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"TextInputFormatter\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" inputFormatters\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  bool enabled\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cursorWidth \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cursorRadius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cursorColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"ul\",[a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"controller\")]),t._v(\"：编辑框的控制器，通过它可以设置/获取编辑框的内容、选择编辑内容、监听编辑文本改变事件。大多数情况下我们都需要显式提供一个\"),a(\"code\",[t._v(\"controller\")]),t._v(\"来与文本框交互。如果没有提供\"),a(\"code\",[t._v(\"controller\")]),t._v(\"，则\"),a(\"code\",[t._v(\"TextField\")]),t._v(\"内部会自动创建一个。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"focusNode\")]),t._v(\"：用于控制\"),a(\"code\",[t._v(\"TextField\")]),t._v(\"是否占有当前键盘的输入焦点。它是我们和键盘交互的一个句柄（handle）。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"InputDecoration\")]),t._v(\"：用于控制\"),a(\"code\",[t._v(\"TextField\")]),t._v(\"的外观显示，如提示文本、背景颜色、边框等。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"keyboardType\")]),t._v(\"：用于设置该输入框默认的键盘输入类型，取值如下：\")]),t._v(\" \"),a(\"table\",[a(\"thead\",[a(\"tr\",[a(\"th\",[t._v(\"TextInputType枚举值\")]),t._v(\" \"),a(\"th\",[t._v(\"含义\")])])]),t._v(\" \"),a(\"tbody\",[a(\"tr\",[a(\"td\",[t._v(\"text\")]),t._v(\" \"),a(\"td\",[t._v(\"文本输入键盘\")])]),t._v(\" \"),a(\"tr\",[a(\"td\",[t._v(\"multiline\")]),t._v(\" \"),a(\"td\",[t._v(\"多行文本，需和maxLines配合使用(设为null或大于1)\")])]),t._v(\" \"),a(\"tr\",[a(\"td\",[t._v(\"number\")]),t._v(\" \"),a(\"td\",[t._v(\"数字；会弹出数字键盘\")])]),t._v(\" \"),a(\"tr\",[a(\"td\",[t._v(\"phone\")]),t._v(\" \"),a(\"td\",[t._v(\"优化后的电话号码输入键盘；会弹出数字键盘并显示“* #”\")])]),t._v(\" \"),a(\"tr\",[a(\"td\",[t._v(\"datetime\")]),t._v(\" \"),a(\"td\",[t._v(\"优化后的日期输入键盘；Android上会显示“: -”\")])]),t._v(\" \"),a(\"tr\",[a(\"td\",[t._v(\"emailAddress\")]),t._v(\" \"),a(\"td\",[t._v(\"优化后的电子邮件地址；会显示“@ .”\")])]),t._v(\" \"),a(\"tr\",[a(\"td\",[t._v(\"url\")]),t._v(\" \"),a(\"td\",[t._v(\"优化后的url输入键盘； 会显示“/ .”\")])])])])]),t._v(\" \"),a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"textInputAction\")]),t._v(\"：键盘动作按钮图标(即回车键位图标)，它是一个枚举值，有多个可选值，全部的取值列表读者可以查看API文档，下面是当值为\"),a(\"code\",[t._v(\"TextInputAction.search\")]),t._v(\"时，原生Android系统下键盘样式如图3-24所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(422),alt:\"图3-24\"}})])]),t._v(\" \"),a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"style\")]),t._v(\"：正在编辑的文本样式。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"textAlign\")]),t._v(\": 输入框内编辑文本在水平方向的对齐方式。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"autofocus\")]),t._v(\": 是否自动获取焦点。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"obscureText\")]),t._v(\"：是否隐藏正在编辑的文本，如用于输入密码的场景等，文本内容会用“•”替换。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"maxLines\")]),t._v(\"：输入框的最大行数，默认为1；如果为\"),a(\"code\",[t._v(\"null\")]),t._v(\"，则无行数限制。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"maxLength\")]),t._v(\"和\"),a(\"code\",[t._v(\"maxLengthEnforced\")]),t._v(\" ：\"),a(\"code\",[t._v(\"maxLength\")]),t._v(\"代表输入框文本的最大长度，设置后输入框右下角会显示输入的文本计数。\"),a(\"code\",[t._v(\"maxLengthEnforced\")]),t._v(\"决定当输入文本长度超过\"),a(\"code\",[t._v(\"maxLength\")]),t._v(\"时是否阻止输入，为\"),a(\"code\",[t._v(\"true\")]),t._v(\"时会阻止输入，为\"),a(\"code\",[t._v(\"false\")]),t._v(\"时不会阻止输入但输入框会变红。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"onChange\")]),t._v(\"：输入框内容改变时的回调函数；注：内容改变事件也可以通过\"),a(\"code\",[t._v(\"controller\")]),t._v(\"来监听。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"onEditingComplete\")]),t._v(\"和\"),a(\"code\",[t._v(\"onSubmitted\")]),t._v(\"：这两个回调都是在输入框输入完成时触发，比如按了键盘的完成键（对号图标）或搜索键（🔍图标）。不同的是两个回调签名不同，\"),a(\"code\",[t._v(\"onSubmitted\")]),t._v(\"回调是\"),a(\"code\",[t._v(\"ValueChanged<String>\")]),t._v(\"类型，它接收当前输入内容做为参数，而\"),a(\"code\",[t._v(\"onEditingComplete\")]),t._v(\"不接收参数。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"inputFormatters\")]),t._v(\"：用于指定输入格式；当用户输入内容改变时，会根据指定的格式来校验。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"enable\")]),t._v(\"：如果为\"),a(\"code\",[t._v(\"false\")]),t._v(\"，则输入框会被禁用，禁用状态不接收输入和事件，同时显示禁用态样式（在其\"),a(\"code\",[t._v(\"decoration\")]),t._v(\"中定义）。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"cursorWidth\")]),t._v(\"、\"),a(\"code\",[t._v(\"cursorRadius\")]),t._v(\"和\"),a(\"code\",[t._v(\"cursorColor\")]),t._v(\"：这三个属性是用于自定义输入框光标宽度、圆角和颜色的。\")])])]),t._v(\" \"),a(\"h4\",{attrs:{id:\"示例-登录输入框\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例-登录输入框\"}},[t._v(\"#\")]),t._v(\" 示例：登录输入框\")]),t._v(\" \"),a(\"h5\",{attrs:{id:\"布局\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#布局\"}},[t._v(\"#\")]),t._v(\" 布局\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            autofocus\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"InputDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                labelText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"用户名\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                hintText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"用户名或邮箱\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                prefixIcon\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"person\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"InputDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                labelText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"密码\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                hintText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"您的登录密码\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                prefixIcon\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"lock\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            obscureText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"运行后，效果如图3-25所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(423),alt:\"图3-25\"}})]),t._v(\" \"),a(\"h5\",{attrs:{id:\"获取输入内容\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#获取输入内容\"}},[t._v(\"#\")]),t._v(\" 获取输入内容\")]),t._v(\" \"),a(\"p\",[t._v(\"获取输入内容有两种方式：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"定义两个变量，用于保存用户名和密码，然后在\"),a(\"code\",[t._v(\"onChange\")]),t._v(\"触发时，各自保存一下输入内容。\")]),t._v(\" \"),a(\"li\",[t._v(\"通过\"),a(\"code\",[t._v(\"controller\")]),t._v(\"直接获取。\")])]),t._v(\" \"),a(\"p\",[t._v(\"第一种方式比较简单，不在举例，我们来重点看一下第二种方式，我们以用户名输入框举例：\")]),t._v(\" \"),a(\"p\",[t._v(\"定义一个\"),a(\"code\",[t._v(\"controller\")]),t._v(\"：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//定义一个controller\")]),t._v(\"\\nTextEditingController _unameController \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextEditingController\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"然后设置输入框controller：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    autofocus\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _unameController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//设置controller\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"通过controller获取输入框内容\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_unameController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"text\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"h5\",{attrs:{id:\"监听文本变化\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#监听文本变化\"}},[t._v(\"#\")]),t._v(\" 监听文本变化\")]),t._v(\" \"),a(\"p\",[t._v(\"监听文本变化也有两种方式：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[a(\"p\",[t._v(\"设置\"),a(\"code\",[t._v(\"onChange\")]),t._v(\"回调，如：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    autofocus\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    onChanged\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"v\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"onChange: $v\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"通过\"),a(\"code\",[t._v(\"controller\")]),t._v(\"监听，如：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//监听输入改变  \")]),t._v(\"\\n  _unameController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addListener\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_unameController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"text\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])])])]),t._v(\" \"),a(\"p\",[t._v(\"两种方式相比，\"),a(\"code\",[t._v(\"onChanged\")]),t._v(\"是专门用于监听文本变化，而\"),a(\"code\",[t._v(\"controller\")]),t._v(\"的功能却多一些，除了能监听文本变化外，它还可以设置默认值、选择文本，下面我们看一个例子：\")]),t._v(\" \"),a(\"p\",[t._v(\"创建一个\"),a(\"code\",[t._v(\"controller\")]),t._v(\":\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[t._v(\"TextEditingController _selectionController \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextEditingController\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"设置默认值，并从第三个字符开始选中后面的字符\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[t._v(\"_selectionController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"text\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"hello world!\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n_selectionController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"selection\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextSelection\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    baseOffset\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    extentOffset\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _selectionController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"text\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"length\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"设置\"),a(\"code\",[t._v(\"controlle\")]),t._v(\"r:\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _selectionController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"运行效果如图3-26所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(424),alt:\"图3-26\"}})]),t._v(\" \"),a(\"h5\",{attrs:{id:\"控制焦点\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#控制焦点\"}},[t._v(\"#\")]),t._v(\" 控制焦点\")]),t._v(\" \"),a(\"p\",[t._v(\"焦点可以通过\"),a(\"code\",[t._v(\"FocusNode\")]),t._v(\"和\"),a(\"code\",[t._v(\"FocusScopeNode\")]),t._v(\"来控制，默认情况下，焦点由\"),a(\"code\",[t._v(\"FocusScope\")]),t._v(\"来管理，它代表焦点控制范围，可以在这个范围内可以通过\"),a(\"code\",[t._v(\"FocusScopeNode\")]),t._v(\"在输入框之间移动焦点、设置默认焦点等。我们可以通过\"),a(\"code\",[t._v(\"FocusScope.of(context)\")]),t._v(\" 来获取Widget树中默认的\"),a(\"code\",[t._v(\"FocusScopeNode\")]),t._v(\"。下面看一个示例，在此示例中创建两个\"),a(\"code\",[t._v(\"TextField\")]),t._v(\"，第一个自动获取焦点，然后创建两个按钮：\")]),t._v(\" \"),a(\"ul\",[a(\"li\",[t._v(\"点击第一个按钮可以将焦点从第一个\"),a(\"code\",[t._v(\"TextField\")]),t._v(\"挪到第二个\"),a(\"code\",[t._v(\"TextField\")]),t._v(\"。\")]),t._v(\" \"),a(\"li\",[t._v(\"点击第二个按钮可以关闭键盘。\")])]),t._v(\" \"),a(\"p\",[t._v(\"我们要实现的效果如图3-27所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(425),alt:\"图3-27\"}})]),t._v(\" \"),a(\"p\",[t._v(\"代码如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FocusTestRoute\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _FocusTestRouteState \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_FocusTestRouteState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_FocusTestRouteState\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"FocusTestRoute\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  FocusNode focusNode1 \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FocusNode\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  FocusNode focusNode2 \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FocusNode\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  FocusScopeNode focusScopeNode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            autofocus\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n            focusNode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" focusNode1\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//关联focusNode1\")]),t._v(\"\\n            decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"InputDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                labelText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"input1\"')]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            focusNode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" focusNode2\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//关联focusNode2\")]),t._v(\"\\n            decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"InputDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                labelText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"input2\"')]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ctx\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"移动焦点\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//将焦点从第一个TextField移到第二个TextField\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 这是一种写法 FocusScope.of(context).requestFocus(focusNode2);\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 这是第二种写法\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" focusScopeNode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                      focusScopeNode \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" FocusScope\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n                    focusScopeNode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"requestFocus\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"focusNode2\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"隐藏键盘\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 当所有编辑框都失去焦点时键盘就会收起  \")]),t._v(\"\\n                    focusNode1\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"unfocus\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                    focusNode2\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"unfocus\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[a(\"code\",[t._v(\"FocusNode\")]),t._v(\"和\"),a(\"code\",[t._v(\"FocusScopeNode\")]),t._v(\"还有一些其它的方法，详情可以查看API文档。\")]),t._v(\" \"),a(\"h5\",{attrs:{id:\"监听焦点状态改变事件\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#监听焦点状态改变事件\"}},[t._v(\"#\")]),t._v(\" 监听焦点状态改变事件\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"FocusNode\")]),t._v(\"继承自\"),a(\"code\",[t._v(\"ChangeNotifier\")]),t._v(\"，通过\"),a(\"code\",[t._v(\"FocusNode\")]),t._v(\"可以监听焦点的改变事件，如：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 创建 focusNode   \")]),t._v(\"\\nFocusNode focusNode \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FocusNode\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// focusNode绑定输入框   \")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"focusNode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" focusNode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 监听焦点变化    \")]),t._v(\"\\nfocusNode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addListener\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"focusNode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"hasFocus\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"获得焦点时\"),a(\"code\",[t._v(\"focusNode.hasFocus\")]),t._v(\"值为\"),a(\"code\",[t._v(\"true\")]),t._v(\"，失去焦点时为\"),a(\"code\",[t._v(\"false\")]),t._v(\"。\")]),t._v(\" \"),a(\"h5\",{attrs:{id:\"自定义样式\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#自定义样式\"}},[t._v(\"#\")]),t._v(\" 自定义样式\")]),t._v(\" \"),a(\"p\",[t._v(\"虽然我们可以通过\"),a(\"code\",[t._v(\"decoration\")]),t._v(\"属性来定义输入框样式，下面以自定义输入框下划线颜色为例来介绍一下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"InputDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    labelText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"请输入用户名\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    prefixIcon\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"person\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 未获得焦点下划线设为灰色\")]),t._v(\"\\n    enabledBorder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"UnderlineInputBorder\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      borderSide\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BorderSide\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//获得焦点下划线设为蓝色\")]),t._v(\"\\n    focusedBorder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"UnderlineInputBorder\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      borderSide\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BorderSide\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"上面代码我们直接通过InputDecoration的enabledBorder和focusedBorder来分别设置了输入框在未获取焦点和获得焦点后的下划线颜色。另外，我们也可以通过主题来自定义输入框的样式，下面我们探索一下如何在不使用enabledBorder和focusedBorder的情况下来自定义下滑线颜色。\")]),t._v(\" \"),a(\"p\",[t._v(\"由于\"),a(\"code\",[t._v(\"TextField\")]),t._v(\"在绘制下划线时使用的颜色是主题色里面的\"),a(\"code\",[t._v(\"hintColor\")]),t._v(\"，但提示文本颜色也是用的\"),a(\"code\",[t._v(\"hintColor\")]),t._v(\"， 如果我们直接修改\"),a(\"code\",[t._v(\"hintColor\")]),t._v(\"，那么下划线和提示文本的颜色都会变。值得高兴的是\"),a(\"code\",[t._v(\"decoration\")]),t._v(\"中可以设置\"),a(\"code\",[t._v(\"hintStyle\")]),t._v(\"，它可以覆盖\"),a(\"code\",[t._v(\"hintColor\")]),t._v(\"，并且主题中可以通过\"),a(\"code\",[t._v(\"inputDecorationTheme\")]),t._v(\"来设置输入框默认的\"),a(\"code\",[t._v(\"decoration\")]),t._v(\"。所以我们可以通过主题来自定义，代码如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Theme\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  data\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Theme\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"copyWith\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      hintColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//定义下划线颜色\")]),t._v(\"\\n      inputDecorationTheme\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"InputDecorationTheme\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          labelStyle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//定义label字体样式\")]),t._v(\"\\n          hintStyle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" fontSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"14.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//定义提示文本样式\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"InputDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            labelText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"用户名\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            hintText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"用户名或邮箱\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            prefixIcon\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"person\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"InputDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            prefixIcon\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"lock\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            labelText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"密码\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            hintText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"您的登录密码\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            hintStyle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" fontSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"13.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        obscureText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"运行效果如图3-28所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(426),alt:\"图3-28\"}})]),t._v(\" \"),a(\"p\",[t._v(\"我们成功的自定义了下划线颜色和提问文字样式，细心的读者可能已经发现，通过这种方式自定义后，输入框在获取焦点时，\"),a(\"code\",[t._v(\"labelText\")]),t._v('不会高亮显示了，正如上图中的\"用户名\"本应该显示蓝色，但现在却显示为灰色，并且我们还是无法定义下划线宽度。另一种灵活的方式是直接隐藏掉'),a(\"code\",[t._v(\"TextField\")]),t._v(\"本身的下划线，然后通过\"),a(\"code\",[t._v(\"Container\")]),t._v(\"去嵌套定义样式，如:\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    keyboardType\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" TextInputType\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"emailAddress\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"InputDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        labelText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Email\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        hintText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"电子邮件地址\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        prefixIcon\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"email\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        border\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" InputBorder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"none \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//隐藏下划线\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 下滑线浅灰色，宽度1像素\")]),t._v(\"\\n      border\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Border\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"bottom\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BorderSide\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"运行效果：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:\"https://cdn.jsdelivr.net/gh/flutterchina/flutter-in-action@1.0/docs/imgs/image-20180904150511545.png\",alt:\"image-20180904150511545\"}})]),t._v(\" \"),a(\"p\",[t._v(\"通过这种组件组合的方式，也可以定义背景圆角等。一般来说，优先通过\"),a(\"code\",[t._v(\"decoration\")]),t._v(\"来自定义样式，如果\"),a(\"code\",[t._v(\"decoration\")]),t._v(\"实现不了，再用widget组合的方式。\")]),t._v(\" \"),a(\"blockquote\",[a(\"p\",[t._v(\"思考题：在这个示例中，下划线颜色是固定的，所以获得焦点后颜色仍然为灰色，如何实现点击后下滑线也变色呢？\")])]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_3-7-2-表单form\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-7-2-表单form\"}},[t._v(\"#\")]),t._v(\" 3.7.2 表单Form\")]),t._v(\" \"),a(\"p\",[t._v(\"实际业务中，在正式向服务器提交数据前，都会对各个输入框数据进行合法性校验，但是对每一个\"),a(\"code\",[t._v(\"TextField\")]),t._v(\"都分别进行校验将会是一件很麻烦的事。还有，如果用户想清除一组\"),a(\"code\",[t._v(\"TextField\")]),t._v(\"的内容，除了一个一个清除有没有什么更好的办法呢？为此，Flutter提供了一个\"),a(\"code\",[t._v(\"Form\")]),t._v(\" 组件，它可以对输入框进行分组，然后进行一些统一操作，如输入内容校验、输入框重置以及输入内容保存。\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"form\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#form\"}},[t._v(\"#\")]),t._v(\" Form\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"Form\")]),t._v(\"继承自\"),a(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"对象，它对应的状态类为\"),a(\"code\",[t._v(\"FormState\")]),t._v(\"。我们先看看\"),a(\"code\",[t._v(\"Form\")]),t._v(\"类的定义：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Form\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" Widget child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  bool autovalidate \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  WillPopCallback onWillPop\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  VoidCallback onChanged\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"ul\",[a(\"li\",[a(\"code\",[t._v(\"autovalidate\")]),t._v(\"：是否自动校验输入内容；当为\"),a(\"code\",[t._v(\"true\")]),t._v(\"时，每一个子FormField内容发生变化时都会自动校验合法性，并直接显示错误信息。否则，需要通过调用\"),a(\"code\",[t._v(\"FormState.validate()\")]),t._v(\"来手动校验。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"onWillPop\")]),t._v(\"：决定\"),a(\"code\",[t._v(\"Form\")]),t._v(\"所在的路由是否可以直接返回（如点击返回按钮），该回调返回一个\"),a(\"code\",[t._v(\"Future\")]),t._v(\"对象，如果Future的最终结果是\"),a(\"code\",[t._v(\"false\")]),t._v(\"，则当前路由不会返回；如果为\"),a(\"code\",[t._v(\"true\")]),t._v(\"，则会返回到上一个路由。此属性通常用于拦截返回按钮。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"onChanged\")]),t._v(\"：\"),a(\"code\",[t._v(\"Form\")]),t._v(\"的任意一个子\"),a(\"code\",[t._v(\"FormField\")]),t._v(\"内容发生变化时会触发此回调。\")])]),t._v(\" \"),a(\"h4\",{attrs:{id:\"formfield\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#formfield\"}},[t._v(\"#\")]),t._v(\" FormField\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"Form\")]),t._v(\"的子孙元素必须是\"),a(\"code\",[t._v(\"FormField\")]),t._v(\"类型，\"),a(\"code\",[t._v(\"FormField\")]),t._v(\"是一个抽象类，定义几个属性，\"),a(\"code\",[t._v(\"FormState\")]),t._v(\"内部通过它们来完成操作，\"),a(\"code\",[t._v(\"FormField\")]),t._v(\"部分定义如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FormField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  FormFieldSetter\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" onSaved\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//保存回调\")]),t._v(\"\\n  FormFieldValidator\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"  validator\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//验证回调\")]),t._v(\"\\n  T initialValue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//初始值\")]),t._v(\"\\n  bool autovalidate \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//是否自动校验。\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"为了方便使用，Flutter提供了一个\"),a(\"code\",[t._v(\"TextFormField\")]),t._v(\"组件，它继承自\"),a(\"code\",[t._v(\"FormField\")]),t._v(\"类，也是\"),a(\"code\",[t._v(\"TextField\")]),t._v(\"的一个包装类，所以除了\"),a(\"code\",[t._v(\"FormField\")]),t._v(\"定义的属性之外，它还包括\"),a(\"code\",[t._v(\"TextField\")]),t._v(\"的属性。\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"formstate\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#formstate\"}},[t._v(\"#\")]),t._v(\" FormState\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"FormState\")]),t._v(\"为\"),a(\"code\",[t._v(\"Form\")]),t._v(\"的\"),a(\"code\",[t._v(\"State\")]),t._v(\"类，可以通过\"),a(\"code\",[t._v(\"Form.of()\")]),t._v(\"或\"),a(\"code\",[t._v(\"GlobalKey\")]),t._v(\"获得。我们可以通过它来对\"),a(\"code\",[t._v(\"Form\")]),t._v(\"的子孙\"),a(\"code\",[t._v(\"FormField\")]),t._v(\"进行统一操作。我们看看其常用的三个方法：\")]),t._v(\" \"),a(\"ul\",[a(\"li\",[a(\"code\",[t._v(\"FormState.validate()\")]),t._v(\"：调用此方法后，会调用\"),a(\"code\",[t._v(\"Form\")]),t._v(\"子孙\"),a(\"code\",[t._v(\"FormField的validate\")]),t._v(\"回调，如果有一个校验失败，则返回false，所有校验失败项都会返回用户返回的错误提示。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"FormState.save()\")]),t._v(\"：调用此方法后，会调用\"),a(\"code\",[t._v(\"Form\")]),t._v(\"子孙\"),a(\"code\",[t._v(\"FormField\")]),t._v(\"的\"),a(\"code\",[t._v(\"save\")]),t._v(\"回调，用于保存表单内容\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"FormState.reset()\")]),t._v(\"：调用此方法后，会将子孙\"),a(\"code\",[t._v(\"FormField\")]),t._v(\"的内容清空。\")])]),t._v(\" \"),a(\"h4\",{attrs:{id:\"示例\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),a(\"p\",[t._v(\"我们修改一下上面用户登录的示例，在提交之前校验：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"用户名不能为空，如果为空则提示“用户名不能为空”。\")]),t._v(\" \"),a(\"li\",[t._v(\"密码不能小于6位，如果小于6为则提示“密码不能少于6位”。\")])]),t._v(\" \"),a(\"p\",[t._v(\"完整代码：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FormTestRoute\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _FormTestRouteState \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_FormTestRouteState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_FormTestRouteState\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"FormTestRoute\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  TextEditingController _unameController \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TextEditingController\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  TextEditingController _pwdController \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TextEditingController\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  GlobalKey _formKey\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"GlobalKey\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"FormState\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      appBar\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        title\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Form Test\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      body\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"symmetric\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"vertical\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" horizontal\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"24.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Form\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _formKey\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//设置globalKey，用于后面获取FormState\")]),t._v(\"\\n          autovalidate\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//开启自动校验\")]),t._v(\"\\n          child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextFormField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  autofocus\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _unameController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"InputDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                      labelText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"用户名\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      hintText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"用户名或邮箱\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      icon\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"person\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 校验用户名\")]),t._v(\"\\n                  validator\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"v\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" v\\n                        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"trim\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"length \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"用户名不能为空\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextFormField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _pwdController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"InputDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                      labelText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"密码\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      hintText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"您的登录密码\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      icon\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"lock\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  obscureText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//校验密码\")]),t._v(\"\\n                  validator\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"v\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" v\\n                        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"trim\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"length \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"5\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"密码不能少于6位\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 登录按钮\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"only\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"top\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"28.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Expanded\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                        padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"15.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"登录\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Theme\\n                            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"primaryColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        textColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//在这里不能通过此方式获取FormState，context不对\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//print(Form.of(context));\")]),t._v(\"\\n                            \\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 通过_formKey.currentState 获取FormState后，\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 调用validate()方法校验用户名密码是否合法，校验\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 通过后再提交数据。 \")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_formKey\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"currentState \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"as\")]),t._v(\" FormState\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"validate\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                            \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//验证通过提交数据\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n                        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"运行后效果如图3-29所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(427),alt:\"图3-29\"}})]),t._v(\" \"),a(\"p\",[t._v(\"注意，登录按钮的\"),a(\"code\",[t._v(\"onPressed\")]),t._v(\"方法中不能通过\"),a(\"code\",[t._v(\"Form.of(context)\")]),t._v(\"来获取，原因是，此处的\"),a(\"code\",[t._v(\"context\")]),t._v(\"为\"),a(\"code\",[t._v(\"FormTestRoute\")]),t._v(\"的context，而\"),a(\"code\",[t._v(\"Form.of(context)\")]),t._v(\"是根据所指定\"),a(\"code\",[t._v(\"context\")]),t._v(\"向根去查找，而\"),a(\"code\",[t._v(\"FormState\")]),t._v(\"是在\"),a(\"code\",[t._v(\"FormTestRoute\")]),t._v(\"的子树中，所以不行。正确的做法是通过\"),a(\"code\",[t._v(\"Builder\")]),t._v(\"来构建登录按钮，\"),a(\"code\",[t._v(\"Builder\")]),t._v(\"会将\"),a(\"code\",[t._v(\"widget\")]),t._v(\"节点的\"),a(\"code\",[t._v(\"context\")]),t._v(\"作为回调参数：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Expanded\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 通过Builder来获取RaisedButton所在widget树的真正context(Element) \")]),t._v(\"\\n  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n      onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//由于本widget也是Form的子代widget，所以可以通过下面方式获取FormState  \")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Form\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"validate\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//验证通过提交数据\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"其实\"),a(\"code\",[t._v(\"context\")]),t._v(\"正是操作Widget所对应的\"),a(\"code\",[t._v(\"Element\")]),t._v(\"的一个接口，由于Widget树对应的\"),a(\"code\",[t._v(\"Element\")]),t._v(\"都是不同的，所以\"),a(\"code\",[t._v(\"context\")]),t._v(\"也都是不同的，有关\"),a(\"code\",[t._v(\"context\")]),t._v(\"的更多内容会在后面高级部分详细讨论。Flutter中有很多“of(context)”这种方法，读者在使用时一定要注意\"),a(\"code\",[t._v(\"context\")]),t._v(\"是否正确。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/140.c76b4d30.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[140],{741:function(t,a,s){\"use strict\";s.r(a);var n=s(45),e=Object(n.a)({},(function(){var t=this,a=t.$createElement,s=t._self._c||a;return s(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[s(\"h1\",{attrs:{id:\"_12-4-插件开发-android端api实现\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_12-4-插件开发-android端api实现\"}},[t._v(\"#\")]),t._v(\" 12.4 插件开发：Android端API实现\")]),t._v(\" \"),s(\"p\",[t._v('本节我们接着上一节\"获取电池电量\"插件的示例，来完成Android端API的实现。以下步骤是使用Java的示例，如果您更喜欢Kotlin，可以直接跳到后面Kotlin部分。')]),t._v(\" \"),s(\"p\",[t._v(\"首先在Android Studio中打开您的Flutter应用的Android部分：\")]),t._v(\" \"),s(\"ol\",[s(\"li\",[t._v(\"启动 Android Studio\")]),t._v(\" \"),s(\"li\",[t._v(\"选择 File > Open…\")]),t._v(\" \"),s(\"li\",[t._v(\"定位到您 Flutter app目录, 然后选择里面的 \"),s(\"code\",[t._v(\"android\")]),t._v(\"文件夹，点击 OK\")]),t._v(\" \"),s(\"li\",[t._v(\"在\"),s(\"code\",[t._v(\"java\")]),t._v(\"目录下打开 \"),s(\"code\",[t._v(\"MainActivity.java\")])])]),t._v(\" \"),s(\"p\",[t._v(\"接下来，在\"),s(\"code\",[t._v(\"onCreate\")]),t._v(\"里创建MethodChannel并设置一个\"),s(\"code\",[t._v(\"MethodCallHandler\")]),t._v(\"。确保使用和Flutter客户端中使用的通道名称相同的名称。\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" io\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"flutter\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"app\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"FlutterActivity\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" io\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"flutter\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"plugin\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"common\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"MethodCall\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" io\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"flutter\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"plugin\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"common\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"MethodChannel\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" io\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"flutter\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"plugin\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"common\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"MethodChannel\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"MethodCallHandler\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" io\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"flutter\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"plugin\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"common\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"MethodChannel\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"Result\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\npublic \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MainActivity\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FlutterActivity\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    private \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String CHANNEL \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"samples.flutter.io/battery\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@Override\")]),t._v(\"\\n    public \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onCreate\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Bundle savedInstanceState\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onCreate\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"savedInstanceState\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MethodChannel\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getFlutterView\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" CHANNEL\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setMethodCallHandler\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MethodCallHandler\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n             \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@Override\")]),t._v(\"\\n             public \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onMethodCall\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"MethodCall call\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Result result\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                 \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// TODO\")]),t._v(\"\\n             \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"接下来，我们添加Java代码，使用Android电池API来获取电池电量。此代码和在原生Android应用中编写的代码完全相同。\")]),t._v(\" \"),s(\"p\",[t._v(\"首先，添加需要导入的依赖。\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" android\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"content\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"ContextWrapper\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" android\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"content\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"Intent\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" android\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"content\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"IntentFilter\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" android\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"os\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"BatteryManager\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" android\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"os\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"Build\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"VERSION\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" android\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"os\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"Build\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"VERSION_CODES\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" android\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"os\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"Bundle\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"然后，将下面的新方法添加到activity类中的，位于onCreate 方法下方：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-java extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-java\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"private\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"int\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getBatteryLevel\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"int\")]),t._v(\" batteryLevel \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"VERSION\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"SDK_INT \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">=\")]),t._v(\" VERSION_CODES\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"LOLLIPOP\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"BatteryManager\")]),t._v(\" batteryManager \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"BatteryManager\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getSystemService\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BATTERY_SERVICE\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    batteryLevel \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" batteryManager\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getIntProperty\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"BatteryManager\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"BATTERY_PROPERTY_CAPACITY\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Intent\")]),t._v(\" intent \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ContextWrapper\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getApplicationContext\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"registerReceiver\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"IntentFilter\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Intent\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"ACTION_BATTERY_CHANGED\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    batteryLevel \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"intent\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getIntExtra\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"BatteryManager\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"EXTRA_LEVEL\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\"\\n        intent\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getIntExtra\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"BatteryManager\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"EXTRA_SCALE\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" batteryLevel\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"最后，我们完成之前添加的\"),s(\"code\",[t._v(\"onMethodCall\")]),t._v(\"方法。我们需要处理平台方法名为\"),s(\"code\",[t._v(\"getBatteryLevel\")]),t._v(\"的调用消息，所以我们需要先在call参数判断调用的方法是否为\"),s(\"code\",[t._v(\"getBatteryLevel\")]),t._v(\"。 这个平台方法的实现只需调用我们在前一步中编写的Android代码，并通过result参数返回成功或错误情况的响应信息。如果调用了未定义的API，我们也会通知返回：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-java extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-java\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token annotation punctuation\"}},[t._v(\"@Override\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"public\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onMethodCall\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MethodCall\")]),t._v(\" call\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Result\")]),t._v(\" result\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"call\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"method\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"equals\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"getBatteryLevel\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"int\")]),t._v(\" batteryLevel \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getBatteryLevel\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"batteryLevel \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            result\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"success\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"batteryLevel\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            result\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"error\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"UNAVAILABLE\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Battery level not available.\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        result\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"notImplemented\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"  \\n\")])])]),s(\"p\",[t._v('现在就可以在Android上运行该应用程序了，如果使用的是Android模拟器，则可以通过工具栏中的\"...\"按钮访问Extended Controls面板中的电池电量。')]),t._v(\" \"),s(\"h3\",{attrs:{id:\"使用kotlin添加android平台特定的实现\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#使用kotlin添加android平台特定的实现\"}},[t._v(\"#\")]),t._v(\" 使用Kotlin添加Android平台特定的实现\")]),t._v(\" \"),s(\"p\",[t._v(\"使用Kotlin和使用Java的步骤类似，首先在Android Studio中打开您的Flutter应用的Android部分：\")]),t._v(\" \"),s(\"ol\",[s(\"li\",[t._v(\"启动 Android Studio。\")]),t._v(\" \"),s(\"li\",[t._v('选择 the menu item \"File > Open…\"。')]),t._v(\" \"),s(\"li\",[t._v(\"定位到 Flutter app目录, 然后选择里面的 \"),s(\"code\",[t._v(\"android\")]),t._v(\"文件夹，点击 OK。\")]),t._v(\" \"),s(\"li\",[t._v(\"在\"),s(\"code\",[t._v(\"kotlin\")]),t._v(\"目录中打开\"),s(\"code\",[t._v(\"MainActivity.kt\")]),t._v(\"。\")])]),t._v(\" \"),s(\"p\",[t._v(\"接下来，在\"),s(\"code\",[t._v(\"onCreate\")]),t._v(\"里创建MethodChannel并设置一个\"),s(\"code\",[t._v(\"MethodCallHandler\")]),t._v(\"。确保使用与在Flutter客户端使用的通道名称相同。\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-kotlin extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-kotlin\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" android\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"os\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"Bundle\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" io\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"flutter\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"app\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"FlutterActivity\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" io\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"flutter\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"plugin\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"common\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"MethodChannel\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" io\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"flutter\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"plugins\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"GeneratedPluginRegistrant\\n\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MainActivity\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlutterActivity\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"private\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"val\")]),t._v(\" CHANNEL \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"samples.flutter.io/battery\"')]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"override\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"fun\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onCreate\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"savedInstanceState\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" Bundle\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onCreate\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"savedInstanceState\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    GeneratedPluginRegistrant\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"registerWith\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MethodChannel\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"flutterView\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" CHANNEL\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setMethodCallHandler\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" call\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" result \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"->\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// TODO\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"接下来，我们添加Kotlin代码，使用Android电池API来获取电池电量，这和原生开发是一样的。\")]),t._v(\" \"),s(\"p\",[t._v(\"首先，添加需要导入的依赖。\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-kotlin extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-kotlin\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" android\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"content\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"Context\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" android\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"content\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"ContextWrapper\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" android\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"content\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"Intent\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" android\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"content\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"IntentFilter\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" android\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"os\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"BatteryManager\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" android\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"os\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"Build\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"VERSION\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" android\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"os\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"Build\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"VERSION_CODES\\n\")])])]),s(\"p\",[t._v(\"然后，将下面的新方法添加到activity类中的，位于onCreate 方法下方：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-kotlin extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-kotlin\"}},[s(\"code\",[t._v(\"  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"private\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"fun\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getBatteryLevel\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" Int \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"val\")]),t._v(\" batteryLevel\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" Int\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"VERSION\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"SDK_INT \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">=\")]),t._v(\" VERSION_CODES\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"LOLLIPOP\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"val\")]),t._v(\" batteryManager \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getSystemService\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"BATTERY_SERVICE\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"as\")]),t._v(\" BatteryManager\\n      batteryLevel \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" batteryManager\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getIntProperty\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BatteryManager\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"BATTERY_PROPERTY_CAPACITY\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"val\")]),t._v(\" intent \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ContextWrapper\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"applicationContext\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"registerReceiver\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"IntentFilter\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Intent\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"ACTION_BATTERY_CHANGED\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      batteryLevel \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" intent\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!!\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getIntExtra\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BatteryManager\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"EXTRA_LEVEL\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" intent\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getIntExtra\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BatteryManager\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"EXTRA_SCALE\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" batteryLevel\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"最后，我们完成之前添加的\"),s(\"code\",[t._v(\"onMethodCall\")]),t._v(\"方法。我们需要处理平台方法名为\"),s(\"code\",[t._v(\"getBatteryLevel\")]),t._v(\"的调用消息，所以我们需要先在call参数判断调用的方法是否为\"),s(\"code\",[t._v(\"getBatteryLevel\")]),t._v(\"。 这个平台方法的实现只需调用我们在前一步中编写的Android代码，并通过result参数返回成功或错误情况的响应信息。如果调用了未定义的API，我们也会通知返回：\\n​\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-kotlin extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-kotlin\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MethodChannel\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"flutterView\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" CHANNEL\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setMethodCallHandler\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" call\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" result \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"->\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"call\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"method \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"getBatteryLevel\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n     \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"val\")]),t._v(\" batteryLevel \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getBatteryLevel\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n     \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"batteryLevel \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n       result\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"success\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"batteryLevel\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n     \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n       result\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"error\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"UNAVAILABLE\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Battery level not available.\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n     \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      result\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"notImplemented\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v('您现在就可以在Android上运行该应用程序。如果您使用的是Android模拟器，则可以通过工具栏中的\"...\"按钮访问Extended Controls面板中的电池电量。')])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/141.592ee9d6.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[141],{740:function(t,a,s){\"use strict\";s.r(a);var n=s(45),e=Object(n.a)({},(function(){var t=this,a=t.$createElement,s=t._self._c||a;return s(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[s(\"h1\",{attrs:{id:\"_12-3-开发flutter插件\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_12-3-开发flutter插件\"}},[t._v(\"#\")]),t._v(\" 12.3 开发Flutter插件\")]),t._v(\" \"),s(\"p\",[t._v(\"下面我们通过一个获取电池电量的插件来介绍一下Flutter插件的开发流程。该插件中我们在Dart中通过\"),s(\"code\",[t._v(\"getBatteryLevel\")]),t._v(\" 调用Android \"),s(\"code\",[t._v(\"BatteryManager\")]),t._v(\" API和iOS \"),s(\"code\",[t._v(\"device.batteryLevel\")]),t._v(\" API。\")]),t._v(\" \"),s(\"h3\",{attrs:{id:\"创建一个新的应用程序项目\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#创建一个新的应用程序项目\"}},[t._v(\"#\")]),t._v(\" 创建一个新的应用程序项目\")]),t._v(\" \"),s(\"p\",[t._v(\"首先创建一个新的应用程序:\")]),t._v(\" \"),s(\"ul\",[s(\"li\",[t._v(\"在终端中运行：\"),s(\"code\",[t._v(\"flutter create batterylevel\")])])]),t._v(\" \"),s(\"p\",[t._v(\"默认情况下，模板支持使用Java编写Android代码，或使用Objective-C编写iOS代码。要使用Kotlin或Swift，请使用-i和/或-a标志:\")]),t._v(\" \"),s(\"ul\",[s(\"li\",[t._v(\"在终端中运行: \"),s(\"code\",[t._v(\"flutter create -i swift -a kotlin batterylevel\")])])]),t._v(\" \"),s(\"h3\",{attrs:{id:\"创建flutter平台客户端\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#创建flutter平台客户端\"}},[t._v(\"#\")]),t._v(\" 创建Flutter平台客户端\")]),t._v(\" \"),s(\"p\",[t._v(\"该应用的\"),s(\"code\",[t._v(\"State\")]),t._v(\"类拥有当前的应用状态。我们需要延长这一点以保持当前的电量\")]),t._v(\" \"),s(\"p\",[t._v(\"首先，我们构建通道。我们使用\"),s(\"code\",[t._v(\"MethodChannel\")]),t._v(\"调用一个方法来返回电池电量。\")]),t._v(\" \"),s(\"p\",[t._v(\"通道的客户端和宿主通过通道构造函数中传递的通道名称进行连接。单个应用中使用的所有通道名称必须是唯一的; 我们建议在通道名称前加一个唯一的“域名前缀”，例如\"),s(\"code\",[t._v(\"samples.flutter.io/battery\")]),t._v(\"。\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'dart:async'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/services.dart'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_MyHomePageState\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"MyHomePage\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" platform \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MethodChannel\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'samples.flutter.io/battery'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// Get battery level.\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"接下来，我们调用通道上的方法，指定通过字符串标识符调用方法\"),s(\"code\",[t._v(\"getBatteryLevel\")]),t._v(\"。 该调用可能失败(平台不支持平台API，例如在模拟器中运行时)，所以我们将invokeMethod调用包装在try-catch语句中。\")]),t._v(\" \"),s(\"p\",[t._v(\"我们使用返回的结果，在\"),s(\"code\",[t._v(\"setState\")]),t._v(\"中来更新用户界面状态\"),s(\"code\",[t._v(\"batteryLevel\")]),t._v(\"。\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// Get battery level.\")]),t._v(\"\\n  String _batteryLevel \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Unknown battery level.'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  Future\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Null\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_getBatteryLevel\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    String batteryLevel\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" int result \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" platform\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"invokeMethod\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'getBatteryLevel'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      batteryLevel \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Battery level at $result % .'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"on\")]),t._v(\" PlatformException \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"catch\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      batteryLevel \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"\\\"Failed to get battery level: '${e.message}'.\\\"\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _batteryLevel \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" batteryLevel\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"最后，我们在build创建包含一个小字体显示电池状态和一个用于刷新值的按钮的用户界面。\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\nWidget \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Material\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Center\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Column\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        mainAxisAlignment\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"spaceEvenly\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        children\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"RaisedButton\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Get Battery Level'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onPressed\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _getBatteryLevel\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_batteryLevel\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"至此Flutter部分的测试代码写好了，接下来我们需要实现Android和iOS平台下的API，由于平台API实现部分篇幅较大，我们将在接下来的两节中，分别介绍Android和iOS端API的实现。\")])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/142.d7d8dbaf.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[142],{743:function(t,e,r){\"use strict\";r.r(e);var a=r(45),l=Object(a.a)({},(function(){var t=this,e=t.$createElement,r=t._self._c||e;return r(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[r(\"h1\",{attrs:{id:\"包与插件\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#包与插件\"}},[t._v(\"#\")]),t._v(\" 包与插件\")]),t._v(\" \"),r(\"ul\",[r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/chapter12/develop_package.html\"}},[t._v(\"12.1：开发package\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/chapter12/platform-channel.html\"}},[t._v(\"12.2：平台通道简介\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/chapter12/develop_plugin.html\"}},[t._v(\"12.3：开发Flutter插件\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/chapter12/android_implement.html\"}},[t._v(\"12.4：插件开发：实现Android端API\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/chapter12/ios_implement.html\"}},[t._v(\"12.5：插件开发：实现IOS端API\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/chapter12/texture_platformview.html\"}},[t._v(\"12.6：Texture和PlatformView\")])],1)])])}),[],!1,null,null,null);e.default=l.exports}}]);"
  },
  {
    "path": "docs/assets/js/143.77583b74.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[143],{768:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_12-5-插件开发-ios端api实现\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_12-5-插件开发-ios端api实现\"}},[t._v(\"#\")]),t._v(\" 12.5 插件开发：iOS端API实现\")]),t._v(\" \"),a(\"p\",[t._v('本节我们接着之前\"获取电池电量\"插件的示例，来完成iOS端API的实现。以下步骤使用Objective-C，如果您更喜欢Swift，可以直接跳到后面Swift部分。')]),t._v(\" \"),a(\"p\",[t._v(\"首先打开Xcode中Flutter应用程序的iOS部分:\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"启动 Xcode\")]),t._v(\" \"),a(\"li\",[t._v(\"选择 File > Open…\")]),t._v(\" \"),a(\"li\",[t._v(\"定位到您 Flutter app目录, 然后选择里面的 \"),a(\"code\",[t._v(\"iOS\")]),t._v(\"文件夹，点击 OK\")]),t._v(\" \"),a(\"li\",[t._v(\"确保Xcode项目的构建没有错误。\")]),t._v(\" \"),a(\"li\",[t._v(\"选择 Runner > Runner ，打开\"),a(\"code\",[t._v(\"AppDelegate.m\")])])]),t._v(\" \"),a(\"p\",[t._v(\"接下来，在\"),a(\"code\",[t._v(\"application didFinishLaunchingWithOptions:\")]),t._v(\"方法内部创建一个\"),a(\"code\",[t._v(\"FlutterMethodChannel\")]),t._v(\"，并添加一个处理方法。 确保与在Flutter客户端使用的通道名称相同。\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-objectivec extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-objectivec\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token macro property\"}},[a(\"span\",{pre:!0,attrs:{class:\"token directive-hash\"}},[t._v(\"#\")]),a(\"span\",{pre:!0,attrs:{class:\"token directive keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token expression\"}},[a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Flutter\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\"Flutter\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"h\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")])])]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"@implementation\")]),t._v(\" AppDelegate\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BOOL\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"application\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"UIApplication\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"application didFinishLaunchingWithOptions\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"NSDictionary\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"launchOptions \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  FlutterViewController\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" controller \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"FlutterViewController\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"self\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"window\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"rootViewController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  FlutterMethodChannel\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" batteryChannel \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"FlutterMethodChannel\\n                                          methodChannelWithName\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('@\"samples.flutter.io/battery\"')]),t._v(\"\\n                                          binaryMessenger\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"batteryChannel setMethodCallHandler\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"^\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"FlutterMethodCall\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" call\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" FlutterResult result\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// TODO\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),t._v(\" application\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"application didFinishLaunchingWithOptions\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"launchOptions\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"接下来，我们添加Objective-C代码，使用iOS电池API来获取电池电量，这和原生是相同的。\")]),t._v(\" \"),a(\"p\",[t._v(\"在\"),a(\"code\",[t._v(\"AppDelegate\")]),t._v(\"类中添加以下新的方法：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-objectivec extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-objectivec\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"int\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"getBatteryLevel \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  UIDevice\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" device \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" UIDevice\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"currentDevice\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  device\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"batteryMonitoringEnabled \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" YES\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"device\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"batteryState \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" UIDeviceBatteryStateUnknown\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"int\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"device\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"batteryLevel \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"最后，我们完成之前添加的\"),a(\"code\",[t._v(\"setMethodCallHandler\")]),t._v(\"方法。我们需要处理的平台方法名为\"),a(\"code\",[t._v(\"getBatteryLevel\")]),t._v(\"，所以我们在call参数中需要先判断是否为\"),a(\"code\",[t._v(\"getBatteryLevel\")]),t._v(\"。 这个平台方法的实现只需调用我们在前一步中编写的iOS代码，并使用result参数返回成功或错误的响应。如果调用了未定义的API，我们也会通知返回：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-objectivec extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-objectivec\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"batteryChannel setMethodCallHandler\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"^\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"FlutterMethodCall\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" call\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" FlutterResult result\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('@\"getBatteryLevel\"')]),t._v(\" isEqualToString\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"call\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"method\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"int\")]),t._v(\" batteryLevel \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"self\")]),t._v(\" getBatteryLevel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"batteryLevel \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"result\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"FlutterError errorWithCode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('@\"UNAVAILABLE\"')]),t._v(\"\\n                                 message\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('@\"电池信息不可用\"')]),t._v(\"\\n                                 details\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"nil\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"result\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"@\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"batteryLevel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"result\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"FlutterMethodNotImplemented\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"现在可以在iOS上运行该应用程序了，如果使用的是iOS模拟器，请注意，它不支持电池API，因此应用程序将显示“电池信息不可用”。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"使用swift实现ios-api\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#使用swift实现ios-api\"}},[t._v(\"#\")]),t._v(\" 使用Swift实现iOS API\")]),t._v(\" \"),a(\"p\",[t._v(\"以下步骤与上面使用Objective-C相似，首先打开Xcode中Flutter应用程序的iOS部分:\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"启动 Xcode\")]),t._v(\" \"),a(\"li\",[t._v(\"选择 File > Open…\")]),t._v(\" \"),a(\"li\",[t._v(\"定位到您 Flutter app目录, 然后选择里面的 \"),a(\"code\",[t._v(\"ios\")]),t._v(\"文件夹，点击 OK\")]),t._v(\" \"),a(\"li\",[t._v(\"确保Xcode项目的构建没有错误。\")]),t._v(\" \"),a(\"li\",[t._v(\"选择 Runner > Runner ，然后打开\"),a(\"code\",[t._v(\"AppDelegate.swift\")])])]),t._v(\" \"),a(\"p\",[t._v(\"接下来，覆盖application方法并创建一个\"),a(\"code\",[t._v(\"FlutterMethodChannel\")]),t._v(\"绑定通道名称\"),a(\"code\",[t._v(\"samples.flutter.io/battery\")]),t._v(\"：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-swift extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-swift\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token atrule\"}},[t._v(\"@UIApplicationMain\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token atrule\"}},[t._v(\"@objc\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AppDelegate\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"FlutterAppDelegate\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"override\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"func\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"application\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"_\")]),t._v(\" application\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"UIApplication\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    didFinishLaunchingWithOptions launchOptions\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"UIApplicationLaunchOptionsKey\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"Any\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"Bool\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"GeneratedPluginRegistrant\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"register\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"with\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"self\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"let\")]),t._v(\" controller \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"FlutterViewController\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" window\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"rootViewController \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"as\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"FlutterViewController\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"let\")]),t._v(\" batteryChannel \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"FlutterMethodChannel\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"init\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"name\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"samples.flutter.io/battery\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                                   binaryMessenger\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    batteryChannel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setMethodCallHandler\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"call\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"FlutterMethodCall\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" result\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"FlutterResult\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"Void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"in\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// Handle battery messages.\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"application\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"application\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" didFinishLaunchingWithOptions\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" launchOptions\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"接下来，我们添加Swift代码，使用iOS电池API来获取电池电量，这和原生开发是相同的。\")]),t._v(\" \"),a(\"p\",[t._v(\"将以下新方法添加到\"),a(\"code\",[t._v(\"AppDelegate.swift\")]),t._v(\"底部:\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-swift extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-swift\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"private\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"func\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"receiveBatteryLevel\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"result\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"FlutterResult\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"let\")]),t._v(\" device \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"UIDevice\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"current\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  device\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isBatteryMonitoringEnabled \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"device\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"batteryState \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"UIDeviceBatteryState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"unknown\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"result\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"FlutterError\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"init\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"code\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"UNAVAILABLE\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                             message\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"电池信息不可用\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                             details\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token constant\"}},[t._v(\"nil\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"result\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Int\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"device\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"batteryLevel \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"最后，我们完成之前添加的\"),a(\"code\",[t._v(\"setMethodCallHandler\")]),t._v(\"方法。我们需要处理的平台方法名为\"),a(\"code\",[t._v(\"getBatteryLevel\")]),t._v(\"，所以我们在call参数中需要先判断是否为\"),a(\"code\",[t._v(\"getBatteryLevel\")]),t._v(\"。 这个平台方法的实现只需调用我们在前一步中编写的iOS代码，并使用result参数返回成功或错误的响应。如果调用了未定义的API，我们也会通知返回：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-swift extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-swift\"}},[a(\"code\",[t._v(\"batteryChannel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setMethodCallHandler\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"call\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"FlutterMethodCall\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" result\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"FlutterResult\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"Void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"in\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"getBatteryLevel\"')]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" call\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"method\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"receiveBatteryLevel\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"result\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" result\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"result\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"FlutterMethodNotImplemented\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"现在可以在iOS上运行应用程序，如果使用的是iOS模拟器，请注意，它不支持电池API，因此应用程序将显示“电池信息不可用”。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/144.2ce6c614.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[144],{746:function(t,a,s){\"use strict\";s.r(a);var n=s(45),e=Object(n.a)({},(function(){var t=this,a=t.$createElement,s=t._self._c||a;return s(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[s(\"h1\",{attrs:{id:\"_13-4-国际化常见问题\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_13-4-国际化常见问题\"}},[t._v(\"#\")]),t._v(\" 13.4 国际化常见问题\")]),t._v(\" \"),s(\"p\",[t._v(\"本节主要解答一下在国际化中常见的问题。\")]),t._v(\" \"),s(\"h3\",{attrs:{id:\"默认语言区域不对\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#默认语言区域不对\"}},[t._v(\"#\")]),t._v(\" 默认语言区域不对\")]),t._v(\" \"),s(\"p\",[t._v(\"在一些非大陆行货渠道买的一些Android和iOS设备，会出现默认的Locale不是中文简体的情况。这属于正常现象，但是为了防止设备获取的Locale与实际的地区不一致，所有的支持多语言的APP都必须提供一个手动选择语言的入口。\")]),t._v(\" \"),s(\"h3\",{attrs:{id:\"如何对应用标题进行国际化\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#如何对应用标题进行国际化\"}},[t._v(\"#\")]),t._v(\" 如何对应用标题进行国际化\")]),t._v(\" \"),s(\"p\",[s(\"code\",[t._v(\"MaterialApp\")]),t._v(\"有一个\"),s(\"code\",[t._v(\"title\")]),t._v(\"属性，用于指定APP的标题。在Android系统中，APP的标题会出现在任务管理器中。所以也需要对\"),s(\"code\",[t._v(\"title\")]),t._v(\"进行国际化。但是问题是很多国际化的配置都是在\"),s(\"code\",[t._v(\"MaterialApp\")]),t._v(\"上设置的，我们无法在构建\"),s(\"code\",[t._v(\"MaterialApp\")]),t._v(\"时通过\"),s(\"code\",[t._v(\"Localizations.of\")]),t._v(\"来获取本地化资源，如：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MaterialApp\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  title\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" DemoLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"title\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//不能正常工作！\")]),t._v(\"\\n  localizationsDelegates\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 本地化的代理类\")]),t._v(\"\\n    GlobalMaterialLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"delegate\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    GlobalWidgetsLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"delegate\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DemoLocalizationsDelegate\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 设置Delegate\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"上面代码运行后，\"),s(\"code\",[t._v(\"DemoLocalizations.of(context).title\")]),t._v(\" 是会报错的，原因是\"),s(\"code\",[t._v(\"Localizations.of\")]),t._v(\"会从当前的context沿着widget树向顶部查找\"),s(\"code\",[t._v(\"DemoLocalizations\")]),t._v(\"，但是我们在\"),s(\"code\",[t._v(\"MaterialApp\")]),t._v(\"中设置完\"),s(\"code\",[t._v(\"DemoLocalizationsDelegate\")]),t._v(\"后，实际上\"),s(\"code\",[t._v(\"DemoLocalizations\")]),t._v(\"是在当前context的子树中的，所以\"),s(\"code\",[t._v(\"DemoLocalizations.of(context)\")]),t._v(\"会返回null，报错。那么我们该如何处理这种情况呢？其实很简单，我们只需要设置一个\"),s(\"code\",[t._v(\"onGenerateTitle\")]),t._v(\"回调即可：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MaterialApp\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  onGenerateTitle\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 此时context在Localizations的子树中\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" DemoLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"title\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  localizationsDelegates\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DemoLocalizationsDelegate\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"h3\",{attrs:{id:\"如何为英语系的国家指定同一个locale\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#如何为英语系的国家指定同一个locale\"}},[t._v(\"#\")]),t._v(\" 如何为英语系的国家指定同一个locale\")]),t._v(\" \"),s(\"p\",[t._v(\"英语系的国家非常多，如美国、英国、澳大利亚等，这些英语系国家虽然说的都是英语，但也会有一些区别。如果我们的APP只想提供一种英语（如美国英语）供所有英语系国家使用，我们可以在前面介绍的\"),s(\"code\",[t._v(\"localeListResolutionCallback\")]),t._v(\"中来做兼容：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"localeListResolutionCallback\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"List\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Locale\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" locales\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Iterable\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Locale\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" supportedLocales\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 判断当前locale是否为英语系国家，如果是直接返回Locale('en', 'US')     \")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/145.8fe852a4.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[145],{747:function(t,a,l){\"use strict\";l.r(a);var e=l(45),r=Object(e.a)({},(function(){var t=this,a=t.$createElement,l=t._self._c||a;return l(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[l(\"h2\",{attrs:{id:\"本章目录\"}},[l(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本章目录\"}},[t._v(\"#\")]),t._v(\" 本章目录\")]),t._v(\" \"),l(\"ul\",[l(\"li\",[l(\"RouterLink\",{attrs:{to:\"/chapter13/multi_languages_support.html\"}},[t._v(\"13.1：让App支持多语言\")])],1),t._v(\" \"),l(\"li\",[l(\"RouterLink\",{attrs:{to:\"/chapter13/locallization_implement.html\"}},[t._v(\"13.2：实现Localizations\")])],1),t._v(\" \"),l(\"li\",[l(\"RouterLink\",{attrs:{to:\"/chapter13/intl.html\"}},[t._v(\"13.3：使用Intl包\")])],1),t._v(\" \"),l(\"li\",[l(\"RouterLink\",{attrs:{to:\"/chapter13/faq.html\"}},[t._v(\"13.4：国际化常见问题\")])],1)])])}),[],!1,null,null,null);a.default=r.exports}}]);"
  },
  {
    "path": "docs/assets/js/146.86bb1e0f.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[146],{749:function(t,a,s){\"use strict\";s.r(a);var n=s(45),e=Object(n.a)({},(function(){var t=this,a=t.$createElement,s=t._self._c||a;return s(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[s(\"h1\",{attrs:{id:\"使用intl包\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#使用intl包\"}},[t._v(\"#\")]),t._v(\" 使用Intl包\")]),t._v(\" \"),s(\"p\",[t._v(\"使用\"),s(\"a\",{attrs:{href:\"https://pub.dartlang.org/packages/intl\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Intl\"),s(\"OutboundLink\")],1),t._v(\"包我们不仅可以非常轻松的实现国际化，而且也可以将字符串文本分离成单独的文件，方便开发人员和翻译人员分工协作。为了使用\"),s(\"a\",{attrs:{href:\"https://pub.dartlang.org/packages/intl\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Intl\"),s(\"OutboundLink\")],1),t._v(\"包我们需要添加两个依赖：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-yaml extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"dependencies\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"#...省略无关项\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"intl\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ^0.15.7 \\n\"),s(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"dev_dependencies\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"#...省略无关项\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"intl_translation\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ^0.17.2  \\n\")])])]),s(\"p\",[s(\"a\",{attrs:{href:\"https://pub.dartlang.org/packages/intl_translation\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"intl_translation\"),s(\"OutboundLink\")],1),t._v(\" 包主要包含了一些工具，它在开发阶段主要主要的作用是从代码中提取要国际化的字符串到单独的arb文件和根据arb文件生成对应语言的dart代码，而intl包主要是引用和加载intl_translation生成后的dart代码。下面我们将一步步来说明如何使用：\")]),t._v(\" \"),s(\"h3\",{attrs:{id:\"第一步-创建必要目录\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#第一步-创建必要目录\"}},[t._v(\"#\")]),t._v(\" 第一步：创建必要目录\")]),t._v(\" \"),s(\"p\",[t._v(\"首先，在项目根目录下创建一个l10n-arb目录，该目录保存我们接下来通过intl_translation命令生成的arb文件。一个简单的arb文件内容如下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-json extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-json\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"@@last_modified\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"2018-12-10T15:46:20.897228\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"@@locale\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"zh_CH\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"title\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Flutter应用\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"@title\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"description\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Title for the Demo application\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"type\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"text\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"placeholders\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v('我们根据\"@@locale\"字段可以看出这个arb对应的是中文简体的翻译，里面的'),s(\"code\",[t._v(\"title\")]),t._v(\"字段对应的正是我们应用标题的中文简体翻译。\"),s(\"code\",[t._v(\"@title\")]),t._v(\"字段是对\"),s(\"code\",[t._v(\"title\")]),t._v(\"的一些描述信息。\")]),t._v(\" \"),s(\"p\",[t._v(\"接下来，我们在lib目录下创建一个l10n的目录，该目录用于保存从arb文件生成的dart代码文件。\")]),t._v(\" \"),s(\"h3\",{attrs:{id:\"第二步-实现localizations和delegate类\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#第二步-实现localizations和delegate类\"}},[t._v(\"#\")]),t._v(\" 第二步：实现Localizations和Delegate类\")]),t._v(\" \"),s(\"p\",[t._v(\"和上一节中的步骤类似，我们仍然要实现\"),s(\"code\",[t._v(\"Localizations\")]),t._v(\"和Delegate类，不同的是，现在我们在实现时要使用intl包的一些方法（有些是动态生成的）。\")]),t._v(\" \"),s(\"p\",[t._v(\"下面我们在\"),s(\"code\",[t._v(\"lib/l10n\")]),t._v(\"目录下新建一个“localization_intl.dart”的文件，文件内容如下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:intl/intl.dart'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'messages_all.dart'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//1\")]),t._v(\"\\n\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"DemoLocalizations\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" Future\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"DemoLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"load\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Locale locale\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String name \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" locale\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"countryCode\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isEmpty \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" locale\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"languageCode \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" locale\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String localeName \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Intl\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"canonicalizedLocale\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"name\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//2\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initializeMessages\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"localeName\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"b\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      Intl\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"defaultLocale \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" localeName\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"DemoLocalizations\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" DemoLocalizations \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" Localizations\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"of\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"DemoLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" DemoLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  String \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" title \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" Intl\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"message\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Flutter APP'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      name\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'title'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      desc\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Title for the Demo application'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//Locale代理类\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"DemoLocalizationsDelegate\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"LocalizationsDelegate\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"DemoLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DemoLocalizationsDelegate\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//是否支持某个Local\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  bool \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"isSupported\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Locale locale\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'en'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'zh'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"contains\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"locale\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"languageCode\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// Flutter会调用此类加载相应的Locale资源类\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Future\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"DemoLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"load\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Locale locale\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//3\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\"  DemoLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"load\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"locale\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 当Localizations Widget重新build时，是否调用load重新加载Locale资源.\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  bool \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"shouldReload\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"DemoLocalizationsDelegate old\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"注意：\")]),t._v(\" \"),s(\"ul\",[s(\"li\",[t._v('注释1的\"messages_all.dart\"文件是通过'),s(\"a\",{attrs:{href:\"https://pub.dartlang.org/packages/intl_translation\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"intl_translation\"),s(\"OutboundLink\")],1),t._v(\"工具从arb文件生成的代码，所以在第一次运行生成命令之前，此文件不存在。注释2处的\"),s(\"code\",[t._v(\"initializeMessages()\")]),t._v('方法和\"messages_all.dart\"文件一样，是同时生成的。')]),t._v(\" \"),s(\"li\",[t._v(\"注释3处和上一节示例代码不同，这里我们直接调用\"),s(\"code\",[t._v(\"DemoLocalizations.load()\")]),t._v(\"即可。\")])]),t._v(\" \"),s(\"h3\",{attrs:{id:\"第三步-添加需要国际化的属性\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#第三步-添加需要国际化的属性\"}},[t._v(\"#\")]),t._v(\" 第三步：添加需要国际化的属性\")]),t._v(\" \"),s(\"p\",[t._v(\"现在我们可以在DemoLocalizations类中添加需要国际化的属性或方法，如上面示例代码中的\"),s(\"code\",[t._v(\"title\")]),t._v(\"属性，这时我们就要用到Intl库提供的一些方法，这些方法可以帮我们轻松实现不同语言的一些语法特性，如复数语境，举个例子，比如我们有一个电子邮件列表页，我们需要在顶部显示未读邮件的数量，在未读数量不同事，我们展示的文本可能会不同：\")]),t._v(\" \"),s(\"table\",[s(\"thead\",[s(\"tr\",[s(\"th\",[t._v(\"未读邮件数\")]),t._v(\" \"),s(\"th\",[t._v(\"提示语\")])])]),t._v(\" \"),s(\"tbody\",[s(\"tr\",[s(\"td\",[t._v(\"0\")]),t._v(\" \"),s(\"td\",[t._v(\"There are no emails left\")])]),t._v(\" \"),s(\"tr\",[s(\"td\",[t._v(\"1\")]),t._v(\" \"),s(\"td\",[t._v(\"There is 1 email left\")])]),t._v(\" \"),s(\"tr\",[s(\"td\",[t._v(\"n(n>1)\")]),t._v(\" \"),s(\"td\",[t._v(\"There are n emails left\")])])])]),t._v(\" \"),s(\"p\",[t._v(\"我们可以通过\"),s(\"code\",[t._v(\"Intl.plural(...)\")]),t._v(\"来实现：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"remainingEmailsMessage\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int howMany\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Intl\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"plural\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"howMany\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    zero\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'There are no emails left'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    one\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'There is $howMany email left'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    other\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'There are $howMany emails left'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    name\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"remainingEmailsMessage\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    args\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"howMany\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    desc\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"How many emails remain after archiving.\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    examples\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'howMany'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"42\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'userName'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Fred'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"可以看到通过\"),s(\"code\",[t._v(\"Intl.plural\")]),t._v(\"方法可以在\"),s(\"code\",[t._v(\"howMany\")]),t._v(\"值不同时输出不同的提示信息。\")]),t._v(\" \"),s(\"p\",[s(\"a\",{attrs:{href:\"https://pub.dartlang.org/packages/intl\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Intl\"),s(\"OutboundLink\")],1),t._v(\"包还有一些其他的方法，读者可以自行查看其文档，本书不在赘述。\")]),t._v(\" \"),s(\"h3\",{attrs:{id:\"第四步-生成arb文件\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#第四步-生成arb文件\"}},[t._v(\"#\")]),t._v(\" 第四步：生成arb文件\")]),t._v(\" \"),s(\"p\",[t._v(\"现在我们可以通\"),s(\"a\",{attrs:{href:\"https://pub.dartlang.org/packages/intl_translation\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"intl_translation\"),s(\"OutboundLink\")],1),t._v(\"包的工具来提取代码中的字符串到一个arb文件，运行如下命名：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-shell extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[s(\"code\",[t._v(\"flutter pub pub run intl_translation:extract_to_arb --output-dir\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"l10n-arb \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"\\\\\")]),t._v(\" lib/l10n/localization_intl.dart\\n\")])])]),s(\"p\",[t._v(\"运行此命令后，会将我们之前通过Intl API标识的属性和字符串提取到“l10n-arb/intl_messages.arb”文件中，我们看看其内容：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-json extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-json\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"@@last_modified\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"2018-12-10T17:37:28.505088\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"title\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Flutter APP\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"@title\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"description\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Title for the Demo application\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"type\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"text\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"placeholders\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"remainingEmailsMessage\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"{howMany,plural, =0{There are no emails left}=1{There is {howMany} email left}other{There are {howMany} emails left}}\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"@remainingEmailsMessage\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"description\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"How many emails remain after archiving.\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"type\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"text\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"placeholders\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"howMany\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"example\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"42\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v('这个是默认的Locale资源文件，如果我们现在要支持中文简体，只需要在该文件同级目录创建一个\"intl_zh_CN.arb\"文件，然后将\"intl_messages.arb\"的内容拷贝到\"intl_zh_CN.arb\"文件，接下来将英文翻译为中文即可，翻译后的\"intl_zh_CN.arb\"文件内容如下：')]),t._v(\" \"),s(\"div\",{staticClass:\"language-json extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-json\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"@@last_modified\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"2018-12-10T15:46:20.897228\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"@@locale\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"zh_CN\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"title\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Flutter应用\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"@title\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"description\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Title for the Demo application\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"type\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"text\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"placeholders\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"remainingEmailsMessage\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"{howMany,plural, =0{没有未读邮件}=1{有{howMany}封未读邮件}other{有{howMany}封未读邮件}}\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"@remainingEmailsMessage\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"description\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"How many emails remain after archiving.\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"type\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"text\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"placeholders\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"howMany\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"example\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"42\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"我们必须要翻译\"),s(\"code\",[t._v(\"title\")]),t._v(\"和\"),s(\"code\",[t._v(\"remainingEmailsMessage\")]),t._v(\"字段，\"),s(\"code\",[t._v(\"description\")]),t._v(\"是该字段的说明，通常给翻译人员看，代码中不会用到。\")]),t._v(\" \"),s(\"p\",[t._v(\"有两点需要说明：\")]),t._v(\" \"),s(\"ol\",[s(\"li\",[t._v(\"如果某个特定的arb中缺失某个属性，那么应用将会加载默认的arb文件(intl_messages.arb)中的相应属性，这是Intl的托底策略。\")]),t._v(\" \"),s(\"li\",[t._v(\"每次运行提取命令时，intl_messages.arb都会根据代码重新生成，但其他arb文件不会，所以当要添加新的字段或方法时，其他arb文件是增量的，不用担心会覆盖。\")]),t._v(\" \"),s(\"li\",[t._v(\"arb文件是标准的，其格式规范可以自行了解。通常会将arb文件交给翻译人员，当他们完成翻译后，我们再通过下面的步骤根据arb文件生成最终的dart代码。\")])]),t._v(\" \"),s(\"h3\",{attrs:{id:\"第五步-生成dart代码\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#第五步-生成dart代码\"}},[t._v(\"#\")]),t._v(\" 第五步：生成dart代码\")]),t._v(\" \"),s(\"p\",[t._v(\"最后一步就是根据arb生成dart文件：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-shell extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[s(\"code\",[t._v(\"flutter pub pub run intl_translation:generate_from_arb --output-dir\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"lib/l10n --no-use-deferred-loading lib/l10n/localization_intl.dart l10n-arb/intl_*.arb\\n\")])])]),s(\"p\",[t._v('这句命令在首次运行时会在\"lib/l10n\"目录下生成多个文件，对应多种Locale，这些代码便是最终要使用的dart代码。')]),t._v(\" \"),s(\"h3\",{attrs:{id:\"总结\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),s(\"p\",[t._v(\"至此，我们将使用\"),s(\"a\",{attrs:{href:\"https://pub.dartlang.org/packages/intl\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Intl\"),s(\"OutboundLink\")],1),t._v(\"包对APP进行国际化的流程介绍完了，我们可以发现，其中第一步和第二步只在第一次需要，而我们开发时的主要的工作都是在第三步。由于最后两步在第三步完成后每次也都需要，所以我们可以将最后两步放在一个shell脚本里，当我们完成第三步或完成arb文件翻译后只需要分别执行该脚本即可。我们在根目录下创建一个intl.sh的脚本，内容为：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-shell extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[s(\"code\",[t._v(\"flutter pub pub run intl_translation:extract_to_arb --output-dir\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"l10n-arb lib/l10n/localization_intl.dart\\nflutter pub pub run intl_translation:generate_from_arb --output-dir\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"lib/l10n --no-use-deferred-loading lib/l10n/localization_intl.dart l10n-arb/intl_*.arb\\n\")])])]),s(\"p\",[t._v(\"然后授予执行权限：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-shell extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"chmod\")]),t._v(\" +x intl.sh\\n\")])])]),s(\"p\",[t._v(\"执行intl.sh\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-shell extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[s(\"code\",[t._v(\"./intl.sh\\n\")])])])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/147.e9009fb7.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[147],{750:function(t,a,s){\"use strict\";s.r(a);var n=s(45),e=Object(n.a)({},(function(){var t=this,a=t.$createElement,s=t._self._c||a;return s(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[s(\"h1\",{attrs:{id:\"_13-2-实现localizations\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_13-2-实现localizations\"}},[t._v(\"#\")]),t._v(\" 13.2 实现Localizations\")]),t._v(\" \"),s(\"p\",[t._v(\"前面讲了Material组件库如何支持国际化，本节我们将介绍一下我们自己的UI中如何支持多语言。根据上节所述，我们需要实现两个类：一个\"),s(\"code\",[t._v(\"Delegate\")]),t._v(\"类一个\"),s(\"code\",[t._v(\"Localizations\")]),t._v(\"类，下面我们通过一个实例说明。\")]),t._v(\" \"),s(\"h3\",{attrs:{id:\"实现localizations类\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#实现localizations类\"}},[t._v(\"#\")]),t._v(\" 实现Localizations类\")]),t._v(\" \"),s(\"p\",[t._v(\"我们已经知道\"),s(\"code\",[t._v(\"Localizations\")]),t._v(\"类中主要实现提供了本地化值，如文本：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//Locale资源类\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"DemoLocalizations\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DemoLocalizations\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isZh\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//是否为中文\")]),t._v(\"\\n  bool isZh \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//为了使用方便，我们定义一个静态方法\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" DemoLocalizations \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" Localizations\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"of\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"DemoLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" DemoLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//Locale相关值，title为应用标题\")]),t._v(\"\\n  String \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" title \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" isZh \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Flutter应用\"')]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Flutter APP\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//... 其它的值  \")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[s(\"code\",[t._v(\"DemoLocalizations\")]),t._v(\"中会根据当前的语言来返回不同的文本，如\"),s(\"code\",[t._v(\"title\")]),t._v(\"，我们可以将所有需要支持多语言的文本都在此类中定义。\"),s(\"code\",[t._v(\"DemoLocalizations\")]),t._v(\"的实例将会在Delegate类的\"),s(\"code\",[t._v(\"load\")]),t._v(\"方法中创建。\")]),t._v(\" \"),s(\"h3\",{attrs:{id:\"实现delegate类\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#实现delegate类\"}},[t._v(\"#\")]),t._v(\" 实现Delegate类\")]),t._v(\" \"),s(\"p\",[t._v(\"Delegate类的职责是在Locale改变时加载新的Locale资源，所以它有一个\"),s(\"code\",[t._v(\"load\")]),t._v(\"方法，Delegate类需要继承自\"),s(\"code\",[t._v(\"LocalizationsDelegate\")]),t._v(\"类，实现相应的接口，示例如下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//Locale代理类\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"DemoLocalizationsDelegate\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"LocalizationsDelegate\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"DemoLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DemoLocalizationsDelegate\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//是否支持某个Local\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  bool \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"isSupported\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Locale locale\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'en'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'zh'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"contains\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"locale\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"languageCode\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// Flutter会调用此类加载相应的Locale资源类\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Future\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"DemoLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"load\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Locale locale\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$locale\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" SynchronousFuture\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"DemoLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DemoLocalizations\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"locale\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"languageCode \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"zh\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  bool \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"shouldReload\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"DemoLocalizationsDelegate old\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[s(\"code\",[t._v(\"shouldReload\")]),t._v(\"的返回值决定当Localizations组件重新build时，是否调用\"),s(\"code\",[t._v(\"load\")]),t._v(\"方法重新加载Locale资源。一般情况下，Locale资源只应该在Locale切换时加载一次，不需要每次在\"),s(\"code\",[t._v(\"Localizations\")]),t._v(\"重新build时都加载，所以返回\"),s(\"code\",[t._v(\"false\")]),t._v(\"即可。可能有些人会担心返回\"),s(\"code\",[t._v(\"false\")]),t._v(\"的话在APP启动后用户再改变系统语言时\"),s(\"code\",[t._v(\"load\")]),t._v(\"方法将不会被调用，所以Locale资源将不会被加载。事实上，每当Locale改变时Flutter都会再调用\"),s(\"code\",[t._v(\"load\")]),t._v(\"方法加载新的Locale，无论\"),s(\"code\",[t._v(\"shouldReload\")]),t._v(\"返回\"),s(\"code\",[t._v(\"true\")]),t._v(\"还是\"),s(\"code\",[t._v(\"false\")]),t._v(\"。\")]),t._v(\" \"),s(\"h3\",{attrs:{id:\"最后一步-添加多语言支持\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#最后一步-添加多语言支持\"}},[t._v(\"#\")]),t._v(\" 最后一步：添加多语言支持\")]),t._v(\" \"),s(\"p\",[t._v(\"和上一节中介绍的相同，我们现在需要先注册\"),s(\"code\",[t._v(\"DemoLocalizationsDelegate\")]),t._v(\"类，然后再通过\"),s(\"code\",[t._v(\"DemoLocalizations.of(context)\")]),t._v(\"来动态获取当前Locale文本。\")]),t._v(\" \"),s(\"p\",[t._v(\"只需要在MaterialApp或WidgetsApp的\"),s(\"code\",[t._v(\"localizationsDelegates\")]),t._v(\"列表中添加我们的Delegate实例即可完成注册：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"localizationsDelegates\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 本地化的代理类\")]),t._v(\"\\n GlobalMaterialLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"delegate\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n GlobalWidgetsLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"delegate\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 注册我们的Delegate\")]),t._v(\"\\n \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DemoLocalizationsDelegate\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"接下来我们可以在Widget中使用Locale值：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  appBar\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//使用Locale title  \")]),t._v(\"\\n    title\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"DemoLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"title\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n ） \\n\")])])]),s(\"p\",[t._v(\"这样，当在美国英语和中文简体之间切换系统语言时，APP的标题将会分别为“Flutter APP”和“Flutter应用”。\")]),t._v(\" \"),s(\"h3\",{attrs:{id:\"总结\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),s(\"p\",[t._v(\"本节我们通过一个简单的示例说明了Flutter应用国际化的基本过程及原理。但是上面的实例还有一个严重的不足就是我们需要在DemoLocalizations类中获取\"),s(\"code\",[t._v(\"title\")]),t._v(\"时手动的判断当前语言Locale，然后返回合适的文本。试想一下，当我们要支持的语言不是两种而是8种甚至20几种时，如果为每个文本属性都要分别去判断到底是哪种Locale从而获取相应语言的文本将会是一件非常复杂的事。还有，通常情况下翻译人员并不是开发人员，能不能像i18n或l10n标准那样可以将翻译单独保存为一个arb文件交由翻译人员去翻译，翻译好之后开发人员再通过工具将arb文件转为代码。答案是肯定的！我们将在下一节介绍如何通过Dart intl包来实现这些。\")])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/148.0c341180.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[148],{753:function(t,a,n){\"use strict\";n.r(a);var s=n(45),e=Object(s.a)({},(function(){var t=this,a=t.$createElement,n=t._self._c||a;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_14-4-flutter运行机制-从启动到显示\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_14-4-flutter运行机制-从启动到显示\"}},[t._v(\"#\")]),t._v(\" 14.4 Flutter运行机制-从启动到显示\")]),t._v(\" \"),n(\"p\",[t._v(\"本节我们主要介绍一下Flutter从启动到显示的过程。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"启动\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#启动\"}},[t._v(\"#\")]),t._v(\" 启动\")]),t._v(\" \"),n(\"p\",[t._v('Flutter的入口在\"lib/main.dart\"的'),n(\"code\",[t._v(\"main()\")]),t._v(\"函数中，它是Dart应用程序的起点。在Flutter应用中，\"),n(\"code\",[t._v(\"main()\")]),t._v(\"函数最简单的实现如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"main\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"runApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"可以看\"),n(\"code\",[t._v(\"main()\")]),t._v(\"函数只调用了一个\"),n(\"code\",[t._v(\"runApp()\")]),t._v(\"方法，我们看看\"),n(\"code\",[t._v(\"runApp()\")]),t._v(\"方法中都做了什么：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"runApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Widget app\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  WidgetsFlutterBinding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ensureInitialized\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"attachRootWidget\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"app\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"scheduleWarmUpFrame\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"参数\"),n(\"code\",[t._v(\"app\")]),t._v(\"是一个widget，它是Flutter应用启动后要展示的第一个Widget。而\"),n(\"code\",[t._v(\"WidgetsFlutterBinding\")]),t._v(\"正是绑定widget 框架和Flutter engine的桥梁，定义如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"WidgetsFlutterBinding\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"BindingBase\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"with\")]),t._v(\" GestureBinding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" ServicesBinding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" SchedulerBinding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" PaintingBinding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" SemanticsBinding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" RendererBinding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" WidgetsBinding \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" WidgetsBinding \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ensureInitialized\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"WidgetsBinding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"instance \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"WidgetsFlutterBinding\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" WidgetsBinding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"instance\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"可以看到\"),n(\"code\",[t._v(\"WidgetsFlutterBinding\")]),t._v(\"继承自\"),n(\"code\",[t._v(\"BindingBase\")]),t._v(\" 并混入了很多\"),n(\"code\",[t._v(\"Binding\")]),t._v(\"，在介绍这些\"),n(\"code\",[t._v(\"Binding\")]),t._v(\"之前我们先介绍一下\"),n(\"code\",[t._v(\"Window\")]),t._v(\"，下面是\"),n(\"code\",[t._v(\"Window\")]),t._v(\"的官方解释：\")]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[t._v(\"The most basic interface to the host operating system's user interface.\")])]),t._v(\" \"),n(\"p\",[t._v(\"很明显，\"),n(\"code\",[t._v(\"Window\")]),t._v(\"正是Flutter Framework连接宿主操作系统的接口。我们看一下\"),n(\"code\",[t._v(\"Window\")]),t._v(\"类的部分定义：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Window\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 当前设备的DPI，即一个逻辑像素显示多少物理像素，数字越大，显示效果就越精细保真。\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// DPI是设备屏幕的固件属性，如Nexus 6的屏幕DPI为3.5 \")]),t._v(\"\\n  double \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" devicePixelRatio \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _devicePixelRatio\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// Flutter UI绘制区域的大小\")]),t._v(\"\\n  Size \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" physicalSize \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _physicalSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 当前系统默认的语言Locale\")]),t._v(\"\\n  Locale \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" locale\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 当前系统字体缩放比例。  \")]),t._v(\"\\n  double \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" textScaleFactor \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _textScaleFactor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"  \\n    \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 当绘制区域大小改变回调\")]),t._v(\"\\n  VoidCallback \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" onMetricsChanged \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _onMetricsChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"  \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// Locale发生变化回调\")]),t._v(\"\\n  VoidCallback \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" onLocaleChanged \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _onLocaleChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 系统字体缩放变化回调\")]),t._v(\"\\n  VoidCallback \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" onTextScaleFactorChanged \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _onTextScaleFactorChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 绘制前回调，一般会受显示器的垂直同步信号VSync驱动，当屏幕刷新时就会被调用\")]),t._v(\"\\n  FrameCallback \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" onBeginFrame \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _onBeginFrame\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 绘制回调  \")]),t._v(\"\\n  VoidCallback \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" onDrawFrame \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _onDrawFrame\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 点击或指针事件回调\")]),t._v(\"\\n  PointerDataPacketCallback \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" onPointerDataPacket \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _onPointerDataPacket\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 调度Frame，该方法执行后，onBeginFrame和onDrawFrame将紧接着会在合适时机被调用，\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 此方法会直接调用Flutter engine的Window_scheduleFrame方法\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"scheduleFrame\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" native \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Window_scheduleFrame'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 更新应用在GPU上的渲染,此方法会直接调用Flutter engine的Window_render方法\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"render\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Scene scene\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" native \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Window_render'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 发送平台消息\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"sendPlatformMessage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String name\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                           ByteData data\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                           PlatformMessageResponseCallback callback\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 平台通道消息处理回调  \")]),t._v(\"\\n  PlatformMessageCallback \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" onPlatformMessage \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _onPlatformMessage\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//其它属性及回调\")]),t._v(\"\\n   \\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"可以看到\"),n(\"code\",[t._v(\"Window\")]),t._v(\"类包含了当前设备和系统的一些信息以及Flutter Engine的一些回调。现在我们再回来看看\"),n(\"code\",[t._v(\"WidgetsFlutterBinding\")]),t._v(\"混入的各种Binding。通过查看这些 Binding的源码，我们可以发现这些Binding中基本都是监听并处理\"),n(\"code\",[t._v(\"Window\")]),t._v(\"对象的一些事件，然后将这些事件按照Framework的模型包装、抽象然后分发。可以看到\"),n(\"code\",[t._v(\"WidgetsFlutterBinding\")]),t._v(\"正是粘连Flutter engine与上层Framework的“胶水”。\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"GestureBinding\")]),t._v(\"：提供了\"),n(\"code\",[t._v(\"window.onPointerDataPacket\")]),t._v(\" 回调，绑定Framework手势子系统，是Framework事件模型与底层事件的绑定入口。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"ServicesBinding\")]),t._v(\"：提供了\"),n(\"code\",[t._v(\"window.onPlatformMessage\")]),t._v(\" 回调， 用于绑定平台消息通道（message channel），主要处理原生和Flutter通信。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"SchedulerBinding\")]),t._v(\"：提供了\"),n(\"code\",[t._v(\"window.onBeginFrame\")]),t._v(\"和\"),n(\"code\",[t._v(\"window.onDrawFrame\")]),t._v(\"回调，监听刷新事件，绑定Framework绘制调度子系统。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"PaintingBinding\")]),t._v(\"：绑定绘制库，主要用于处理图片缓存。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"SemanticsBinding\")]),t._v(\"：语义化层与Flutter engine的桥梁，主要是辅助功能的底层支持。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"RendererBinding\")]),t._v(\": 提供了\"),n(\"code\",[t._v(\"window.onMetricsChanged\")]),t._v(\" 、\"),n(\"code\",[t._v(\"window.onTextScaleFactorChanged\")]),t._v(\" 等回调。它是渲染树与Flutter engine的桥梁。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"WidgetsBinding\")]),t._v(\"：提供了\"),n(\"code\",[t._v(\"window.onLocaleChanged\")]),t._v(\"、\"),n(\"code\",[t._v(\"onBuildScheduled\")]),t._v(\" 等回调。它是Flutter widget层与engine的桥梁。\")])]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"WidgetsFlutterBinding.ensureInitialized()\")]),t._v(\"负责初始化一个\"),n(\"code\",[t._v(\"WidgetsBinding\")]),t._v(\"的全局单例，紧接着会调用\"),n(\"code\",[t._v(\"WidgetsBinding\")]),t._v(\"的\"),n(\"code\",[t._v(\"attachRootWidget\")]),t._v(\"方法，该方法负责将根Widget添加到\"),n(\"code\",[t._v(\"RenderView\")]),t._v(\"上，代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"attachRootWidget\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Widget rootWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  _renderViewElement \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" RenderObjectToWidgetAdapter\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"RenderBox\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    container\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" renderView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n    debugShortDescription\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'[root]'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" rootWidget\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"attachToRenderTree\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"buildOwner\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" renderViewElement\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"注意，代码中的有\"),n(\"code\",[t._v(\"renderView\")]),t._v(\"和\"),n(\"code\",[t._v(\"renderViewElement\")]),t._v(\"两个变量，\"),n(\"code\",[t._v(\"renderView\")]),t._v(\"是一个\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"，它是渲染树的根，而\"),n(\"code\",[t._v(\"renderViewElement\")]),t._v(\"是\"),n(\"code\",[t._v(\"renderView\")]),t._v(\"对应的\"),n(\"code\",[t._v(\"Element\")]),t._v(\"对象，可见该方法主要完成了根widget到根 \"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"再到根\"),n(\"code\",[t._v(\"Element\")]),t._v(\"的整个关联过程。我们看看\"),n(\"code\",[t._v(\"attachToRenderTree\")]),t._v(\"的源码实现：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"RenderObjectToWidgetElement\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"attachToRenderTree\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildOwner owner\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"RenderObjectToWidgetElement\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" element\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"element \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    owner\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"lockState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      element \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createElement\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"element \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      element\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"assignOwner\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"owner\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    owner\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"buildScope\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"element\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      element\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"mount\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    element\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"_newWidget \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    element\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"markNeedsBuild\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" element\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"该方法负责创建根element，即\"),n(\"code\",[t._v(\"RenderObjectToWidgetElement\")]),t._v(\"，并且将element与widget 进行关联，即创建出 widget树对应的element树。如果element 已经创建过了，则将根element 中关联的widget 设为新的，由此可以看出element 只会创建一次，后面会进行复用。那么\"),n(\"code\",[t._v(\"BuildOwner\")]),t._v(\"是什么呢？其实他就是widget framework的管理类，它跟踪哪些widget需要重新构建。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"渲染\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#渲染\"}},[t._v(\"#\")]),t._v(\" 渲染\")]),t._v(\" \"),n(\"p\",[t._v(\"回到\"),n(\"code\",[t._v(\"runApp\")]),t._v(\"的实现中，当调用完\"),n(\"code\",[t._v(\"attachRootWidget\")]),t._v(\"后，最后一行会调用 \"),n(\"code\",[t._v(\"WidgetsFlutterBinding\")]),t._v(\" 实例的 \"),n(\"code\",[t._v(\"scheduleWarmUpFrame()\")]),t._v(\" 方法，该方法的实现在\"),n(\"code\",[t._v(\"SchedulerBinding\")]),t._v(' 中，它被调用后会立即进行一次绘制（而不是等待\"vsync\" 信号），在此次绘制结束前，该方法会锁定事件分发，也就是说在本次绘制结束完成之前Flutter将不会响应各种事件，这可以保证在绘制过程中不会再触发新的重绘。下面是'),n(\"code\",[t._v(\"scheduleWarmUpFrame()\")]),t._v(\" 方法的部分实现(省略了无关代码)：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"scheduleWarmUpFrame\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  Timer\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"run\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"handleBeginFrame\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  Timer\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"run\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"handleDrawFrame\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"  \\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"resetEpoch\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 锁定事件\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"lockEvents\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" endOfFrame\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    Timeline\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"finishSync\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"可以看到该方法中主要调用了\"),n(\"code\",[t._v(\"handleBeginFrame()\")]),t._v(\" 和 \"),n(\"code\",[t._v(\"handleDrawFrame()\")]),t._v(\" 两个方法，在看这两个方法之前我们首先了解一下Frame 和 FrameCallback 的概念：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"p\",[t._v('Frame: 一次绘制过程，我们称其为一帧。Flutter engine受显示器垂直同步信号\"VSync\"的驱使不断的触发绘制。我们之前说的Flutter可以实现60fps（Frame Per-Second），就是指一秒钟可以触发60次重绘，FPS值越大，界面就越流畅。')])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"FrameCallback：\"),n(\"code\",[t._v(\"SchedulerBinding\")]),t._v(\" 类中有三个FrameCallback回调队列， 在一次绘制过程中，这三个回调队列会放在不同时机被执行：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"code\",[t._v(\"transientCallbacks\")]),t._v(\"：用于存放一些临时回调，一般存放动画回调。可以通过\"),n(\"code\",[t._v(\"SchedulerBinding.instance.scheduleFrameCallback\")]),t._v(\" 添加回调。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"persistentCallbacks\")]),t._v(\"：用于存放一些持久的回调，不能在此类回调中再请求新的绘制帧，持久回调一经注册则不能移除。\"),n(\"code\",[t._v(\"SchedulerBinding.instance.addPersitentFrameCallback()\")]),t._v(\"，这个回调中处理了布局与绘制工作。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"postFrameCallbacks\")]),t._v(\"：在Frame结束时只会被调用一次，调用后会被系统移除，可由 \"),n(\"code\",[t._v(\"SchedulerBinding.instance.addPostFrameCallback()\")]),t._v(\" 注册，注意，不要在此类回调中再触发新的Frame，这可以会导致循环刷新。\")])])])]),t._v(\" \"),n(\"p\",[t._v(\"现在请读者自行查看\"),n(\"code\",[t._v(\"handleBeginFrame()\")]),t._v(\" 和 \"),n(\"code\",[t._v(\"handleDrawFrame()\")]),t._v(\" 两个方法的源码，可以发现前者主要是执行了\"),n(\"code\",[t._v(\"transientCallbacks\")]),t._v(\"队列，而后者执行了 \"),n(\"code\",[t._v(\"persistentCallbacks\")]),t._v(\" 和 \"),n(\"code\",[t._v(\"postFrameCallbacks\")]),t._v(\" 队列。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"绘制\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#绘制\"}},[t._v(\"#\")]),t._v(\" 绘制\")]),t._v(\" \"),n(\"p\",[t._v(\"渲染和绘制逻辑在\"),n(\"code\",[t._v(\"RendererBinding\")]),t._v(\"中实现，查看其源码，发现在其\"),n(\"code\",[t._v(\"initInstances()\")]),t._v(\"方法中有如下代码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initInstances\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n      \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//监听Window对象的事件  \")]),t._v(\"\\n  ui\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"window\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onMetricsChanged \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" handleMetricsChanged\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onTextScaleFactorChanged \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" handleTextScaleFactorChanged\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onSemanticsEnabledChanged \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _handleSemanticsEnabledChanged\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onSemanticsAction \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _handleSemanticsAction\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n   \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//添加PersistentFrameCallback    \")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addPersistentFrameCallback\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_handlePersistentFrameCallback\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们看最后一行，通过\"),n(\"code\",[t._v(\"addPersistentFrameCallback\")]),t._v(\" 向\"),n(\"code\",[t._v(\"persistentCallbacks\")]),t._v(\"队列添加了一个回调 \"),n(\"code\",[t._v(\"_handlePersistentFrameCallback\")]),t._v(\":\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_handlePersistentFrameCallback\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Duration timeStamp\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"drawFrame\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"该方法直接调用了\"),n(\"code\",[t._v(\"RendererBinding\")]),t._v(\"的\"),n(\"code\",[t._v(\"drawFrame()\")]),t._v(\"方法：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"drawFrame\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"renderView \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  pipelineOwner\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"flushLayout\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//布局\")]),t._v(\"\\n  pipelineOwner\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"flushCompositingBits\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//重绘之前的预处理操作，检查RenderObject是否需要重绘\")]),t._v(\"\\n  pipelineOwner\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"flushPaint\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 重绘\")]),t._v(\"\\n  renderView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"compositeFrame\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 将需要绘制的比特数据发给GPU\")]),t._v(\"\\n  pipelineOwner\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"flushSemantics\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// this also sends the semantics to the OS.\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们看看这些方法分别做了什么：\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"flushlayout\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#flushlayout\"}},[t._v(\"#\")]),t._v(\" flushLayout()\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"flushLayout\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"while\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_nodesNeedingLayout\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isNotEmpty\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" List\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"RenderObject\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" dirtyNodes \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _nodesNeedingLayout\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      _nodesNeedingLayout \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"RenderObject\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"for\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"RenderObject node \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"in\")]),t._v(\" \\n           dirtyNodes\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"sort\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"RenderObject a\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" RenderObject b\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" a\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"depth \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" b\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"depth\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"node\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"_needsLayout \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\" node\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"owner \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n          node\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_layoutWithoutResize\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"源码很简单，该方法主要任务是更新了所有被标记为“dirty”的\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"的布局信息。主要的动作发生在\"),n(\"code\",[t._v(\"node._layoutWithoutResize()\")]),t._v(\"方法中，该方法中会调用\"),n(\"code\",[t._v(\"performLayout()\")]),t._v(\"进行重新布局。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"flushcompositingbits\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#flushcompositingbits\"}},[t._v(\"#\")]),t._v(\" flushCompositingBits()\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"flushCompositingBits\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  _nodesNeedingCompositingBitsUpdate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"sort\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"RenderObject a\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" RenderObject b\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" a\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"depth \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" b\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"depth\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"for\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"RenderObject node \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"in\")]),t._v(\" _nodesNeedingCompositingBitsUpdate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"node\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"_needsCompositingBitsUpdate \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\" node\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"owner \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      node\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_updateCompositingBits\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//更新RenderObject.needsCompositing属性值\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  _nodesNeedingCompositingBitsUpdate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"clear\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"检查\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"是否需要重绘，然后更新\"),n(\"code\",[t._v(\"RenderObject.needsCompositing\")]),t._v(\"属性，如果该属性值被标记为\"),n(\"code\",[t._v(\"true\")]),t._v(\"则需要重绘。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"flushpaint\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#flushpaint\"}},[t._v(\"#\")]),t._v(\" flushPaint()\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"flushPaint\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" List\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"RenderObject\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" dirtyNodes \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _nodesNeedingPaint\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \\n    _nodesNeedingPaint \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"RenderObject\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 反向遍历需要重绘的RenderObject\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"for\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"RenderObject node \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"in\")]),t._v(\" \\n         dirtyNodes\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"sort\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"RenderObject a\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" RenderObject b\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" b\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"depth \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" a\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"depth\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"node\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"_needsPaint \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\" node\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"owner \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"node\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"_layer\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"attached\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 真正的绘制逻辑  \")]),t._v(\"\\n          PaintingContext\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"repaintCompositedChild\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"node\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          node\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_skippedPaintingOnLayer\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"该方法进行了最终的绘制，可以看出它不是重绘了所有 \"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"，而是只重绘了需要重绘的 \"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"。真正的绘制是通过\"),n(\"code\",[t._v(\"PaintingContext.repaintCompositedChild()\")]),t._v(\"来绘制的，该方法最终会调用Flutter engine提供的Canvas API来完成绘制。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"compositeframe\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#compositeframe\"}},[t._v(\"#\")]),t._v(\" compositeFrame()\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"compositeFrame\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" ui\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"SceneBuilder builder \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" ui\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SceneBuilder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" ui\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"Scene scene \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" layer\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"buildScene\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"automaticSystemUiAdjustment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_updateSystemChrome\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    ui\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"window\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"render\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"scene\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//调用Flutter engine的渲染API\")]),t._v(\"\\n    scene\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"finally\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Timeline\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"finishSync\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"这个方法中有一个\"),n(\"code\",[t._v(\"Scene\")]),t._v(\"对象，Scene对象是一个数据结构，保存最终渲染后的像素信息。这个方法将Canvas画好的\"),n(\"code\",[t._v(\"Scene\")]),t._v(\"传给\"),n(\"code\",[t._v(\"window.render()\")]),t._v(\"方法，该方法会直接将scene信息发送给Flutter engine，最终由engine将图像画在设备屏幕上。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"最后\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#最后\"}},[t._v(\"#\")]),t._v(\" 最后\")]),t._v(\" \"),n(\"p\",[t._v(\"需要注意的是：由于\"),n(\"code\",[t._v(\"RendererBinding\")]),t._v(\"只是一个mixin，而with它的是\"),n(\"code\",[t._v(\"WidgetsBinding\")]),t._v(\"，所以我们需要看看\"),n(\"code\",[t._v(\"WidgetsBinding\")]),t._v(\"中是否重写该方法，查看\"),n(\"code\",[t._v(\"WidgetsBinding\")]),t._v(\"的\"),n(\"code\",[t._v(\"drawFrame()\")]),t._v(\"方法源码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"drawFrame\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"renderViewElement \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      buildOwner\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"buildScope\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"renderViewElement\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"drawFrame\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//调用RendererBinding的drawFrame()方法\")]),t._v(\"\\n    buildOwner\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"finalizeTree\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们发现在调用\"),n(\"code\",[t._v(\"RendererBinding.drawFrame()\")]),t._v(\"方法前会调用 \"),n(\"code\",[t._v(\"buildOwner.buildScope()\")]),t._v(\" （非首次绘制），该方法会将被标记为“dirty” 的 element 进行 \"),n(\"code\",[t._v(\"rebuild()\")]),t._v(\" 。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"总结\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),n(\"p\",[t._v(\"本节介绍了Flutter APP从启动到显示到屏幕上的主流程，读者可以结合前面章节对Widget、Element以及RenderObject的介绍来加强细节理解。\")])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/149.46210223.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[149],{754:function(t,e,r){\"use strict\";r.r(e);var a=r(45),I=Object(a.a)({},(function(){var t=this,e=t.$createElement,r=t._self._c||e;return r(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[r(\"h1\",{attrs:{id:\"_14-1-flutter-ui系统\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_14-1-flutter-ui系统\"}},[t._v(\"#\")]),t._v(\" 14.1 Flutter UI系统\")]),t._v(\" \"),r(\"p\",[t._v('在本书的前面章节中，我们多次提到\"UI系统\"这个概念，本书中所指的UI系统特指：基于一个平台，在此平台上实现GUI的一个系统，这里的平台特指操作系统，如Android、iOS或者Windows、macOS。我们说过各个平台UI系统的原理是相通的，也就是说无论是Android还是iOS，他们将一个用户界面展示到屏幕的流程是相似的，所以，在介绍Flutter UI系统之前，我们先看看UI系统的基本原理，这样可以帮助读者对操作系统和系统底层UI逻辑有一个清晰的认识。')]),t._v(\" \"),r(\"h3\",{attrs:{id:\"硬件绘图基本原理\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#硬件绘图基本原理\"}},[t._v(\"#\")]),t._v(\" 硬件绘图基本原理\")]),t._v(\" \"),r(\"p\",[t._v(\"提到原理，我们要从屏幕显示图像的基本原理谈起。我们知道显示器（屏幕）是由一个个物理显示单元组成，每一个单元我们可以称之为一个物理像素点，而每一个像素点可以发出多种颜色，显示器成相的原理就是在不同的物理像素点上显示不同的颜色，最终构成完整的图像。\")]),t._v(\" \"),r(\"p\",[t._v(\"一个像素点能发出的所有颜色总数是显示器的一个重要指标，比如我们所说的1600万色的屏幕就是指一个像素点可以显示出1600万种颜色，而显示器颜色是有RGB三基色组成，所以1600万即2的24次方，即每个基本色（R、G、B）深度扩展至8 bit(位)，颜色深度越深，所能显示的色彩更加丰富靓丽。\")]),t._v(\" \"),r(\"p\",[t._v(\"为了更新显示画面，显示器是以固定的频率刷新（从GPU取数据），比如有一部手机屏幕的刷新频率是 60Hz。当一帧图像绘制完毕后准备绘制下一帧时，显示器会发出一个垂直同步信号（如VSync）， 60Hz的屏幕就会一秒内发出 60次这样的信号。而这个信号主要是用于同步CPU、GPU和显示器的。一般地来说，计算机系统中，CPU、GPU和显示器以一种特定的方式协作：CPU将计算好的显示内容提交给 GPU，GPU渲染后放入帧缓冲区，然后视频控制器按照同步信号从帧缓冲区取帧数据传递给显示器显示。\")]),t._v(\" \"),r(\"p\",[t._v(\"CPU和GPU的任务是各有偏重的，CPU主要用于基本数学和逻辑计算，而GPU主要执行和图形处理相关的复杂的数学，如矩阵变化和几何计算，GPU的主要作用就是确定最终输送给显示器的各个像素点的色值。\")]),t._v(\" \"),r(\"h3\",{attrs:{id:\"操作系统绘制api的封装\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#操作系统绘制api的封装\"}},[t._v(\"#\")]),t._v(\" 操作系统绘制API的封装\")]),t._v(\" \"),r(\"p\",[t._v(\"由于最终的图形计算和绘制都是由相应的硬件来完成，而直接操作硬件的指令通常都会有操作系统屏蔽，应用开发者通常不会直接面对硬件，操作系统屏蔽了这些底层硬件操作后会提供一些封装后的API供操作系统之上的应用调用，但是对于应用开发者来说，直接调用这些操作系统提供的API是比较复杂和低效的，因为操作系统提供的API往往比较基础，直接调用需要了解API的很多细节。正是因为这个原因，几乎所有用于开发GUI程序的编程语言都会在操作系统之上再封装一层，将操作系统原生API封装在一个编程框架和模型中，然后定义一种简单的开发规则来开发GUI应用程序，而这一层抽象，正是我们所说的“UI”系统，如Android SDK正是封装了Android操作系统API，提供了一个“UI描述文件XML+Java操作DOM”的UI系统，而iOS的UIKit 对View的抽象也是一样的，他们都将操作系统API抽象成一个基础对象（如用于2D图形绘制的Canvas），然后再定义一套规则来描述UI，如UI树结构，UI操作的单线程原则等。\")]),t._v(\" \"),r(\"h3\",{attrs:{id:\"flutter-ui系统\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#flutter-ui系统\"}},[t._v(\"#\")]),t._v(\" Flutter UI系统\")]),t._v(\" \"),r(\"p\",[t._v(\"我们可以看到，无论是Android SDK还是iOS的UIKit 的职责都是相同的，它们只是语言载体和底层的系统不同而已。那么可不可以实现这么一个UI系统：可以使用同一种编程语言开发，然后针对不同操作系统API抽象一个对上接口一致，对下适配不同操作系统的的中间层，然后在打包编译时再使用相应的中间层代码？如果可以做到，那么我们就可以使用同一套代码编写跨平台的应用了。而Flutter的原理正是如此，它提供了一套Dart API，然后在底层通过OpenGL这种跨平台的绘制库（内部会调用操作系统API）实现了一套代码跨多端。由于Dart API也是调用操作系统API，所以它的性能接近原生。\")]),t._v(\" \"),r(\"blockquote\",[r(\"p\",[t._v(\"注意，虽然Dart是先调用了OpenGL，OpenGL才会调用操作系统API，但是这仍然是原生渲染，因为OpenGL只是操作系统API的一个封装库，它并不像WebView渲染那样需要JavaScript运行环境和CSS渲染器，所以不会有性能损失。\")])]),t._v(\" \"),r(\"p\",[t._v(\"至此，我们已经介绍了Flutter UI系统和操作系统交互的这一部分原理，现在需要说一些它对应用开发者定义的开发标准。其实在前面的章节中，我们已经对这个标准非常熟悉了, 简单概括就是：组合和响应式。我们要开发一个UI界面，需要通过组合其它Widget来实现，Flutter中，一切都是Widget，当UI要发生变化时，我们不去直接修改DOM，而是通过更新状态，让Flutter UI系统来根据新的状态来重新构建UI。\")]),t._v(\" \"),r(\"p\",[t._v(\"讲到这里，读者可能发现Flutter UI系统和Flutter Framework的概念是差不多的，的确如此，之所以用“UI系统”，是因为其他平台中可能不这么叫，我们只是为了概念统一，便于描述，读者不必纠结于概念本身。\")]),t._v(\" \"),r(\"p\",[t._v(\"在接下来的小节中，我们先详细介绍一下\"),r(\"code\",[t._v(\"Element\")]),t._v(\"、\"),r(\"code\",[t._v(\"RenderObject\")]),t._v(\"，它们是组成Flutter UI系统的基石。最后我们再分析一下Flutter应用启动和运行的整体过程。\")])])}),[],!1,null,null,null);e.default=I.exports}}]);"
  },
  {
    "path": "docs/assets/js/15.7f957f4b.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[15],{466:function(t,a,s){t.exports=s.p+\"assets/img/5-18.f83914b2.png\"},467:function(t,a,s){t.exports=s.p+\"assets/img/5-19.a2dab018.png\"},468:function(t,a){t.exports=\"data:image/png;base64,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\"},469:function(t,a,s){t.exports=s.p+\"assets/img/5-21.946b0c47.png\"},470:function(t,a,s){t.exports=s.p+\"assets/img/5-22.e38796ed.png\"},471:function(t,a,s){t.exports=s.p+\"assets/img/5-23.9b16ea45.png\"},794:function(t,a,s){\"use strict\";s.r(a);var n=s(45),p=Object(n.a)({},(function(){var t=this,a=t.$createElement,n=t._self._c||a;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_5-6-scaffold、tabbar、底部导航\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_5-6-scaffold、tabbar、底部导航\"}},[t._v(\"#\")]),t._v(\" 5.6 Scaffold、TabBar、底部导航\")]),t._v(\" \"),n(\"p\",[t._v(\"Material组件库提供了丰富多样的组件，本节介绍一些常用的组件，其余的读者可以自行查看文档或Flutter Gallery中Material组件部分的示例。\")]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[t._v(\"Flutter Gallery是Flutter官方提供的Flutter Demo，源码位于flutter源码中的examples目录下，笔者强烈建议用户将Flutter Gallery示例跑起来，它是一个很全面的Flutter示例应用，是非常好的参考Demo，也是笔者学习Flutter的第一手资料。\")])]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_5-6-1-scaffold\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_5-6-1-scaffold\"}},[t._v(\"#\")]),t._v(\" 5.6.1 Scaffold\")]),t._v(\" \"),n(\"p\",[t._v(\"一个完整的路由页可能会包含导航栏、抽屉菜单(Drawer)以及底部Tab导航菜单等。如果每个路由页面都需要开发者自己手动去实现这些，这会是一件非常麻烦且无聊的事。幸运的是，Flutter Material组件库提供了一些现成的组件来减少我们的开发任务。\"),n(\"code\",[t._v(\"Scaffold\")]),t._v(\"是一个路由页的骨架，我们使用它可以很容易地拼装出一个完整的页面。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"示例\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),n(\"p\",[t._v(\"我们实现一个页面，它包含：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"一个导航栏\")]),t._v(\" \"),n(\"li\",[t._v(\"导航栏右边有一个分享按钮\")]),t._v(\" \"),n(\"li\",[t._v(\"有一个抽屉菜单\")]),t._v(\" \"),n(\"li\",[t._v(\"有一个底部导航\")]),t._v(\" \"),n(\"li\",[t._v(\"右下角有一个悬浮的动作按钮\")])]),t._v(\" \"),n(\"p\",[t._v(\"最终效果如图5-18、图5-19所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(466),alt:\"图5-18\"}}),t._v(\" \"),n(\"img\",{attrs:{src:s(467),alt:\"图5-19\"}})]),t._v(\" \"),n(\"p\",[t._v(\"实现代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ScaffoldRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _ScaffoldRouteState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_ScaffoldRouteState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_ScaffoldRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ScaffoldRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  int _selectedIndex \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      appBar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//导航栏\")]),t._v(\"\\n        title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"App Name\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n        actions\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//导航栏右侧菜单\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"IconButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"icon\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"share\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      drawer\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyDrawer\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//抽屉\")]),t._v(\"\\n      bottomNavigationBar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BottomNavigationBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 底部导航\")]),t._v(\"\\n        items\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"BottomNavigationBarItem\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BottomNavigationBarItem\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"icon\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"home\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Home'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BottomNavigationBarItem\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"icon\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"business\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Business'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BottomNavigationBarItem\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"icon\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"school\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'School'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        currentIndex\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _selectedIndex\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        fixedColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        onTap\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _onItemTapped\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      floatingActionButton\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FloatingActionButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//悬浮按钮\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"add\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"_onAdd\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_onItemTapped\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _selectedIndex \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_onAdd\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上面代码中我们用到了如下组件：\")]),t._v(\" \"),n(\"table\",[n(\"thead\",[n(\"tr\",[n(\"th\",[t._v(\"组件名称\")]),t._v(\" \"),n(\"th\",[t._v(\"解释\")])])]),t._v(\" \"),n(\"tbody\",[n(\"tr\",[n(\"td\",[t._v(\"AppBar\")]),t._v(\" \"),n(\"td\",[t._v(\"一个导航栏骨架\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"MyDrawer\")]),t._v(\" \"),n(\"td\",[t._v(\"抽屉菜单\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"BottomNavigationBar\")]),t._v(\" \"),n(\"td\",[t._v(\"底部导航栏\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"FloatingActionButton\")]),t._v(\" \"),n(\"td\",[t._v(\"漂浮按钮\")])])])]),t._v(\" \"),n(\"p\",[t._v(\"下面我们来分别介绍一下它们。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_5-6-2-appbar\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_5-6-2-appbar\"}},[t._v(\"#\")]),t._v(\" 5.6.2 AppBar\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"AppBar\")]),t._v(\"是一个Material风格的导航栏，通过它可以设置导航栏标题、导航栏菜单、导航栏底部的Tab标题等。下面我们看看AppBar的定义：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"leading\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//导航栏最左侧Widget，常见为抽屉菜单按钮或返回按钮。\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"automaticallyImplyLeading \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//如果leading为null，是否自动实现默认的leading按钮\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 页面标题\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"actions\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 导航栏右侧菜单\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"bottom\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 导航栏底部菜单，通常为Tab按钮组\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"elevation \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"4.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 导航栏阴影\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"centerTitle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//标题是否居中 \")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"backgroundColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"   \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//其它属性见源码注释\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"如果给\"),n(\"code\",[t._v(\"Scaffold\")]),t._v(\"添加了抽屉菜单，默认情况下\"),n(\"code\",[t._v(\"Scaffold\")]),t._v(\"会自动将\"),n(\"code\",[t._v(\"AppBar\")]),t._v(\"的\"),n(\"code\",[t._v(\"leading\")]),t._v(\"设置为菜单按钮（如上面截图所示），点击它便可打开抽屉菜单。如果我们想自定义菜单图标，可以手动来设置\"),n(\"code\",[t._v(\"leading\")]),t._v(\"，如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  appBar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"App Name\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    leading\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"IconButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        icon\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dashboard\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//自定义图标\")]),t._v(\"\\n        onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 打开抽屉菜单  \")]),t._v(\"\\n          Scaffold\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"openDrawer\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"  \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"  \\n\")])])]),n(\"p\",[t._v(\"代码运行效果如图5-20所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(468),alt:\"图5-20\"}})]),t._v(\" \"),n(\"p\",[t._v(\"可以看到左侧菜单已经替换成功。\")]),t._v(\" \"),n(\"p\",[t._v(\"代码中打开抽屉菜单的方法在\"),n(\"code\",[t._v(\"ScaffoldState\")]),t._v(\"中，通过\"),n(\"code\",[t._v(\"Scaffold.of(context)\")]),t._v(\"可以获取父级最近的\"),n(\"code\",[t._v(\"Scaffold\")]),t._v(\" 组件的\"),n(\"code\",[t._v(\"State\")]),t._v(\"对象。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"tabbar\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#tabbar\"}},[t._v(\"#\")]),t._v(\" TabBar\")]),t._v(\" \"),n(\"p\",[t._v(\"下面我们通过“bottom”属性来添加一个导航栏底部Tab按钮组，将要实现的效果如图5-21所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(469),alt:\"图5-21\"}})]),t._v(\" \"),n(\"p\",[t._v(\"Material组件库中提供了一个\"),n(\"code\",[t._v(\"TabBar\")]),t._v(\"组件，它可以快速生成\"),n(\"code\",[t._v(\"Tab\")]),t._v(\"菜单，下面是上图对应的源码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_ScaffoldRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ScaffoldRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"with\")]),t._v(\" SingleTickerProviderStateMixin \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\n  TabController _tabController\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//需要定义一个Controller\")]),t._v(\"\\n  List tabs \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"新闻\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"历史\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"图片\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 创建Controller  \")]),t._v(\"\\n    _tabController \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TabController\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"length\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" tabs\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"length\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" vsync\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      appBar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n        bottom\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TabBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"   \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//生成Tab菜单\")]),t._v(\"\\n          controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _tabController\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          tabs\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" tabs\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"map\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Tab\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toList\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上面代码首先创建了一个\"),n(\"code\",[t._v(\"TabController\")]),t._v(\" ，它是用于控制/监听\"),n(\"code\",[t._v(\"Tab\")]),t._v(\"菜单切换的。接下来通过TabBar生成了一个底部菜单栏，\"),n(\"code\",[t._v(\"TabBar\")]),t._v(\"的\"),n(\"code\",[t._v(\"tabs\")]),t._v(\"属性接受一个Widget数组，表示每一个Tab子菜单，我们可以自定义，也可以像示例中一样直接使用\"),n(\"code\",[t._v(\"Tab\")]),t._v(\" 组件，它是Material组件库提供的Material风格的Tab菜单。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"Tab\")]),t._v(\"组件有三个可选参数，除了可以指定文字外，还可以指定Tab菜单图标，或者直接自定义组件样式。\"),n(\"code\",[t._v(\"Tab\")]),t._v(\"组件定义如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Tab\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 菜单文本\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"icon\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 菜单图标\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 自定义组件样式\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"开发者可以根据实际需求来定制。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"tabbarview\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#tabbarview\"}},[t._v(\"#\")]),t._v(\" TabBarView\")]),t._v(\" \"),n(\"p\",[t._v(\"通过\"),n(\"code\",[t._v(\"TabBar\")]),t._v(\"我们只能生成一个静态的菜单，真正的Tab页还没有实现。由于\"),n(\"code\",[t._v(\"Tab\")]),t._v(\"菜单和Tab页的切换需要同步，我们需要通过\"),n(\"code\",[t._v(\"TabController\")]),t._v(\"去监听Tab菜单的切换去切换Tab页，代码如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"_tabController\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"  \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"switch\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_tabController\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"   \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"如果我们Tab页可以滑动切换的话，还需要在滑动过程中更新TabBar指示器的偏移！显然，要手动处理这些是很麻烦的，为此，Material库提供了一个\"),n(\"code\",[t._v(\"TabBarView\")]),t._v(\"组件，通过它不仅可以轻松的实现Tab页，而且可以非常容易的配合TabBar来实现同步切换和滑动状态同步，示例如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  appBar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n    bottom\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TabBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _tabController\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      tabs\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" tabs\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"map\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Tab\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toList\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  drawer\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyDrawer\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  body\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TabBarView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _tabController\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" tabs\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"map\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//创建3个Tab页\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" textScaleFactor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"5\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toList\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 省略无关代码  \")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"    \\n\")])])]),n(\"p\",[t._v(\"运行后效果如图5-22所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(470),alt:\"图5-22\"}})]),t._v(\" \"),n(\"p\",[t._v(\"现在，无论是点击导航栏Tab菜单还是在页面上左右滑动，Tab页面都会切换，并且Tab菜单的状态和Tab页面始终保持同步！那它们是如何实现同步的呢？细心的读者可能已经发现，上例中\"),n(\"code\",[t._v(\"TabBar\")]),t._v(\"和\"),n(\"code\",[t._v(\"TabBarView\")]),t._v(\"的\"),n(\"code\",[t._v(\"controller\")]),t._v(\"是同一个！正是如此，\"),n(\"code\",[t._v(\"TabBar\")]),t._v(\"和\"),n(\"code\",[t._v(\"TabBarView\")]),t._v(\"正是通过同一个\"),n(\"code\",[t._v(\"controller\")]),t._v(\"来实现菜单切换和滑动状态同步的，有关\"),n(\"code\",[t._v(\"TabController\")]),t._v(\"的详细信息，我们不在本书做过多介绍，使用时读者直接查看SDK即可。\")]),t._v(\" \"),n(\"p\",[t._v(\"另外，Material组件库也提供了一个\"),n(\"code\",[t._v(\"PageView\")]),t._v(\" 组件，它和\"),n(\"code\",[t._v(\"TabBarView\")]),t._v(\"功能相似，读者可以自行了解一下。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_5-6-3-抽屉菜单drawer\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_5-6-3-抽屉菜单drawer\"}},[t._v(\"#\")]),t._v(\" 5.6.3 抽屉菜单Drawer\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"Scaffold\")]),t._v(\"的\"),n(\"code\",[t._v(\"drawer\")]),t._v(\"和\"),n(\"code\",[t._v(\"endDrawer\")]),t._v(\"属性可以分别接受一个Widget来作为页面的左、右抽屉菜单。如果开发者提供了抽屉菜单，那么当用户手指从屏幕左（或右）侧向里滑动时便可打开抽屉菜单。本节开始部分的示例中实现了一个左抽屉菜单\"),n(\"code\",[t._v(\"MyDrawer\")]),t._v(\"，它的源码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyDrawer\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyDrawer\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Drawer\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MediaQuery\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"removePadding\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//移除抽屉菜单顶部默认留白\")]),t._v(\"\\n        removeTop\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          crossAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" CrossAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"only\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"top\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"38.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"symmetric\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"horizontal\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ClipOval\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"asset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                        \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"imgs/avatar.png\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Wendux\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"fontWeight\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" FontWeight\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"bold\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Expanded\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    leading\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"add\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Add account'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    leading\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"settings\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Manage accounts'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"抽屉菜单通常将\"),n(\"code\",[t._v(\"Drawer\")]),t._v(\"组件作为根节点，它实现了Material风格的菜单面板，\"),n(\"code\",[t._v(\"MediaQuery.removePadding\")]),t._v(\"可以移除Drawer默认的一些留白（比如Drawer默认顶部会留和手机状态栏等高的留白），读者可以尝试传递不同的参数来看看实际效果。抽屉菜单页由顶部和底部组成，顶部由用户头像和昵称组成，底部是一个菜单列表，用ListView实现，关于ListView我们将在后面“可滚动组件”一节详细介绍。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_5-6-4-floatingactionbutton\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_5-6-4-floatingactionbutton\"}},[t._v(\"#\")]),t._v(\" 5.6.4 FloatingActionButton\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"FloatingActionButton\")]),t._v('是Material设计规范中的一种特殊Button，通常悬浮在页面的某一个位置作为某种常用动作的快捷入口，如本节示例中页面右下角的\"➕\"号按钮。我们可以通过'),n(\"code\",[t._v(\"Scaffold\")]),t._v(\"的\"),n(\"code\",[t._v(\"floatingActionButton\")]),t._v(\"属性来设置一个\"),n(\"code\",[t._v(\"FloatingActionButton\")]),t._v(\"，同时通过\"),n(\"code\",[t._v(\"floatingActionButtonLocation\")]),t._v(\"属性来指定其在页面中悬浮的位置，这个比较简单，不再赘述。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_5-6-5-底部tab导航栏\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_5-6-5-底部tab导航栏\"}},[t._v(\"#\")]),t._v(\" 5.6.5  底部Tab导航栏\")]),t._v(\" \"),n(\"p\",[t._v(\"我们可以通过\"),n(\"code\",[t._v(\"Scaffold\")]),t._v(\"的\"),n(\"code\",[t._v(\"bottomNavigationBar\")]),t._v(\"属性来设置底部导航，如本节开始示例所示，我们通过Material组件库提供的\"),n(\"code\",[t._v(\"BottomNavigationBar\")]),t._v(\"和\"),n(\"code\",[t._v(\"BottomNavigationBarItem\")]),t._v(\"两种组件来实现Material风格的底部导航栏。可以看到上面的实现代码非常简单，所以不再赘述，但是如果我们想实现如图5-23所示效果的底部导航栏应该怎么做呢？\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(471),alt:\"图5-23\"}})]),t._v(\" \"),n(\"p\",[t._v(\"Material组件库中提供了一个\"),n(\"code\",[t._v(\"BottomAppBar\")]),t._v(\" 组件，它可以和\"),n(\"code\",[t._v(\"FloatingActionButton\")]),t._v(\"配合实现这种“打洞”效果，源码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"bottomNavigationBar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BottomAppBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  shape\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircularNotchedRectangle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 底部导航栏打一个圆形的洞\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"IconButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"icon\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"home\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//中间位置空出\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"IconButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"icon\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"business\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    mainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"spaceAround\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//均分底部导航栏横向空间\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"可以看到，上面代码中没有控制打洞位置的属性，实际上，打洞的位置取决于\"),n(\"code\",[t._v(\"FloatingActionButton\")]),t._v(\"的位置，上面\"),n(\"code\",[t._v(\"FloatingActionButton\")]),t._v(\"的位置为：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"floatingActionButtonLocation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" FloatingActionButtonLocation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"centerDocked\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"所以打洞位置在底部导航栏的正中间。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"BottomAppBar\")]),t._v(\"的\"),n(\"code\",[t._v(\"shape\")]),t._v(\"属性决定洞的外形，\"),n(\"code\",[t._v(\"CircularNotchedRectangle\")]),t._v(\"实现了一个圆形的外形，我们也可以自定义外形，比如，Flutter Gallery示例中就有一个“钻石”形状的示例，读者感兴趣可以自行查看。\")])])}),[],!1,null,null,null);a.default=p.exports}}]);"
  },
  {
    "path": "docs/assets/js/150.220fd8cf.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[150],{756:function(t,e,r){\"use strict\";r.r(e);var l=r(45),n=Object(l.a)({},(function(){var t=this,e=t.$createElement,r=t._self._c||e;return r(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[r(\"h1\",{attrs:{id:\"本章目录\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本章目录\"}},[t._v(\"#\")]),t._v(\" 本章目录\")]),t._v(\" \"),r(\"ul\",[r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/chapter14/flutter_ui_system.html\"}},[t._v(\"Flutter UI系统\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/chapter14/element_buildcontext.html\"}},[t._v(\"Element和BuildContext\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/chapter14/render_object.html\"}},[t._v(\"RenderObject和RenderBox\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/chapter14/flutter_app_startup.html\"}},[t._v(\"Flutter从启动到显示\")])],1)])])}),[],!1,null,null,null);e.default=n.exports}}]);"
  },
  {
    "path": "docs/assets/js/151.a1cacc6d.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[151],{758:function(t,s,n){\"use strict\";n.r(s);var a=n(45),e=Object(a.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_15-2-flutter-app代码结构\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_15-2-flutter-app代码结构\"}},[t._v(\"#\")]),t._v(\" 15.2 Flutter APP代码结构\")]),t._v(\" \"),n(\"p\",[t._v('我们先来创建一个全新的Flutter工程，命名为\"github_client_app\"；创建新工程的步骤视读者使用的编辑器而定，都比较简单，在此不再赘述。创建完成后，工程结构如下：')]),t._v(\" \"),n(\"div\",{staticClass:\"language-shell extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[n(\"code\",[t._v(\"github_client_app\\n├── android\\n├── ios\\n├── lib\\n└── \"),n(\"span\",{pre:!0,attrs:{class:\"token builtin class-name\"}},[t._v(\"test\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"由于我们需要使用外部图片和Icon资源，所以我们在项目根目录下分别创建“imgs”和“fonts”文件夹，前者用于保存图片，后者用于保存Icon文件。关于图片和Icon，读者可以参考第三章中相应的内容。\")]),t._v(\" \"),n(\"p\",[t._v(\"由于在网络数据传输和持久化时，我们需要通过Json来传输、保存数据；但是在应用开发时我们又需要将Json转成Dart Model类，现在我们使用在第十一章中“Json转Model”小节中介绍的方案，所以，我们需要在根目录下再创建一个用于保存Json文件的“jsons”文件夹。\")]),t._v(\" \"),n(\"p\",[t._v(\"多语言支持我们使用第十三章“国际化”中介绍的方案，所以还需要在根目录下创建一个“l10n”文件夹，用于保存各国语言对应的arb文件。\")]),t._v(\" \"),n(\"p\",[t._v(\"现在工程目录变为：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-shell extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[n(\"code\",[t._v(\"github_client_app\\n├── android\\n├── fonts\\n├── l10n-arb\\n├── imgs\\n├── ios\\n├── jsons\\n├── lib\\n└── \"),n(\"span\",{pre:!0,attrs:{class:\"token builtin class-name\"}},[t._v(\"test\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"由于我们的Dart代码都在“lib”文件夹下，笔者根据技术选型和经验在lib文件下创建了如下目录：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-shell extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[n(\"code\",[t._v(\"lib\\n├── common\\n├── l10n\\n├── models\\n├── states\\n├── routes\\n└── widgets \\n\")])])]),n(\"table\",[n(\"thead\",[n(\"tr\",[n(\"th\",[t._v(\"文件夹\")]),t._v(\" \"),n(\"th\",[t._v(\"作用\")])])]),t._v(\" \"),n(\"tbody\",[n(\"tr\",[n(\"td\",[t._v(\"common\")]),t._v(\" \"),n(\"td\",[t._v(\"一些工具类，如通用方法类、网络接口类、保存全局变量的静态类等\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"l10n\")]),t._v(\" \"),n(\"td\",[t._v(\"国际化相关的类都在此目录下\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"models\")]),t._v(\" \"),n(\"td\",[t._v(\"Json文件对应的Dart Model类会在此目录下\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"states\")]),t._v(\" \"),n(\"td\",[t._v(\"保存APP中需要跨组件共享的状态类\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"routes\")]),t._v(\" \"),n(\"td\",[t._v(\"存放所有路由页面类\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"widgets\")]),t._v(\" \"),n(\"td\",[t._v(\"APP内封装的一些Widget组件都在该目录下\")])])])]),t._v(\" \"),n(\"p\",[t._v(\"注意，使用不同的框架或技术选型会对代码有不同的组织方式，因此，本节介绍的代码组织结构并不是固定或者“最佳”的，在实战中，读者可以自己根据情况调整源码结构。但是无论采取何种源码组织结构，清晰和解耦都是一个通用原则，我们应该让自己的代码结构清晰，以便交流和维护。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/152.5323fd98.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[152],{759:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_15-4-全局变量及共享状态\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_15-4-全局变量及共享状态\"}},[t._v(\"#\")]),t._v(\" 15.4 全局变量及共享状态\")]),t._v(\" \"),a(\"p\",[t._v(\"应用程序中通常会包含一些贯穿APP生命周期的变量信息，这些信息在APP大多数地方可能都会被用到，比如当前用户信息、Local信息等。在Flutter中我们把需要全局共享的信息分为两类：全局变量和共享状态。全局变量就是单纯指会贯穿整个APP生命周期的变量，用于单纯的保存一些信息，或者封装一些全局工具和方法的对象。而共享状态则是指哪些需要跨组件或跨路由共享的信息，这些信息通常也是全局变量，而共享状态和全局变量的不同在于前者发生改变时需要通知所有使用该状态的组件，而后者不需要。为此，我们将全局变量和共享状态分开单独管理。\")]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_15-4-1-全局变量-global类\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_15-4-1-全局变量-global类\"}},[t._v(\"#\")]),t._v(\" 15.4.1 全局变量-Global类\")]),t._v(\" \"),a(\"p\",[t._v(\"我们在“lib/common”目录下创建一个\"),a(\"code\",[t._v(\"Global\")]),t._v(\"类，它主要管理APP的全局变量，定义如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 提供五套可选主题色\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" _themes \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"MaterialColor\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n  Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cyan\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"teal\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"green\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Global\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" SharedPreferences _prefs\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" Profile profile \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Profile\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 网络缓存对象\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" NetCache netCache \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"NetCache\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 可选的主题列表\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" List\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"MaterialColor\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" themes \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _themes\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 是否为release版\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" bool \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" isRelease \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" bool\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fromEnvironment\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"dart.vm.product\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//初始化全局信息，会在APP启动时执行\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" Future \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"init\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    _prefs \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" SharedPreferences\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getInstance\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" _profile \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _prefs\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getString\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"profile\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_profile \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        profile \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fromJson\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"jsonDecode\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"catch\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 如果没有缓存策略，设置默认缓存策略\")]),t._v(\"\\n    profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cache \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cache \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CacheConfig\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"enable \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"maxAge \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3600\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"maxCount \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//初始化网络请求相关配置\")]),t._v(\"\\n    Git\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"init\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 持久化Profile信息\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"saveProfile\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"\\n      _prefs\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setString\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"profile\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"jsonEncode\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toJson\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"Global类的各个字段的意义都有注释，在此不再赘述，需要注意的是\"),a(\"code\",[t._v(\"init()\")]),t._v(\"需要在App启动时就要执行，所以应用的\"),a(\"code\",[t._v(\"main\")]),t._v(\"方法如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"main\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Global\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"init\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"runApp\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyApp\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"在此，一定要确保\"),a(\"code\",[t._v(\"Global.init()\")]),t._v(\"方法不能抛出异常，否则 \"),a(\"code\",[t._v(\"runApp(MyApp())\")]),t._v(\"根本执行不到。\")]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_15-4-2-共享状态\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_15-4-2-共享状态\"}},[t._v(\"#\")]),t._v(\" 15.4.2 共享状态\")]),t._v(\" \"),a(\"p\",[t._v(\"有了全局变量，我们还需要考虑如何跨组件共享状态。当然，如果我们将要共享的状态全部用全局变量替代也是可以的，但是这在Flutter开发中并不是一个好主意，因为组件的状态是和UI相关，而在状态改变时我们会期望依赖该状态的UI组件会自动更新，如果使用全局变量，那么我们必须得去手动处理状态变动通知、接收机制以及变量和组件依赖关系。因此，本实例中，我们使用前面介绍过的Provider包来实现跨组件状态共享，因此我们需要定义相关的Provider。在本实例中，需要共享的状态有登录用户信息、APP主题信息、APP语言信息。由于这些信息改变后都要立即通知其它依赖的该信息的Widget更新，所以我们应该使用\"),a(\"code\",[t._v(\"ChangeNotifierProvider\")]),t._v(\"，另外，这些信息改变后都是需要更新Profile信息并进行持久化的。综上所述，我们可以定义一个\"),a(\"code\",[t._v(\"ProfileChangeNotifier\")]),t._v(\"基类，然后让需要共享的Model继承自该类即可，\"),a(\"code\",[t._v(\"ProfileChangeNotifier\")]),t._v(\"定义如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ProfileChangeNotifier\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ChangeNotifier\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  Profile \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" _profile \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Global\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"notifyListeners\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Global\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"saveProfile\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//保存Profile变更\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"notifyListeners\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//通知依赖的Widget更新\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"h3\",{attrs:{id:\"用户状态\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#用户状态\"}},[t._v(\"#\")]),t._v(\" 用户状态\")]),t._v(\" \"),a(\"p\",[t._v(\"用户状态在登录状态发生变化时更新、通知其依赖项，我们定义如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"UserModel\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ProfileChangeNotifier\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  User \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" user \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"user\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// APP是否登录(如果有用户信息，则证明登录过)\")]),t._v(\"\\n  bool \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" isLogin \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" user \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//用户信息发生变化，更新用户信息并通知依赖它的子孙Widgets更新\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"set\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"user\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"User user\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"user\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"login \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" _profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"user\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"login\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"lastLogin \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"user\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"login\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      _profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"user \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" user\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"notifyListeners\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"h3\",{attrs:{id:\"app主题状态\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#app主题状态\"}},[t._v(\"#\")]),t._v(\" APP主题状态\")]),t._v(\" \"),a(\"p\",[t._v(\"主题状态在用户更换APP主题时更新、通知其依赖项，定义如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ThemeModel\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ProfileChangeNotifier\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 获取当前主题，如果为设置主题，则默认使用蓝色主题\")]),t._v(\"\\n  ColorSwatch \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" theme \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Global\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"themes\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"firstWhere\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" _profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"theme\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" orElse\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 主题改变后，通知其依赖项，新主题会立即生效\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"set\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"theme\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ColorSwatch color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" theme\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"theme \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"500\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"notifyListeners\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"h3\",{attrs:{id:\"app语言状态\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#app语言状态\"}},[t._v(\"#\")]),t._v(\" APP语言状态\")]),t._v(\" \"),a(\"p\",[t._v(\"当APP语言选为跟随系统（Auto）时，在系通语言改变时，APP语言会更新；当用户在APP中选定了具体语言时（美国英语或中文简体），则APP便会一直使用用户选定的语言，不会再随系统语言而变。语言状态类定义如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"LocaleModel\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ProfileChangeNotifier\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 获取当前用户的APP语言配置Locale类，如果为null，则语言跟随系统语言\")]),t._v(\"\\n  Locale \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getLocale\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"locale \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" t \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"locale\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"split\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"_\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Locale\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"t\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" t\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 获取当前Locale的字符串表示\")]),t._v(\"\\n  String \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" locale \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"locale\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 用户改变APP语言后，通知依赖项更新，新语言会立即生效\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"set\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"locale\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String locale\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"locale \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" _profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"locale\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"locale \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" locale\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"notifyListeners\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/153.26e3ed31.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[153],{760:function(t,_,v){\"use strict\";v.r(_);var l=v(45),i=Object(l.a)({},(function(){var t=this,_=t.$createElement,v=t._self._c||_;return v(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[v(\"h1\",{attrs:{id:\"_15-1-github客户端示例\"}},[v(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_15-1-github客户端示例\"}},[t._v(\"#\")]),t._v(\" 15.1 Github客户端示例\")]),t._v(\" \"),v(\"p\",[t._v(\"本章新建一个Flutter工程，实现一个简单的Github客户端。这个实例的主要目标有两个：\")]),t._v(\" \"),v(\"ol\",[v(\"li\",[t._v(\"带领读者了解如何使用Flutter来开发一个完整APP，了解Flutter应用开发流程及工程结构等。\")]),t._v(\" \"),v(\"li\",[t._v(\"对前面章节所学内容的一个应用及总结。\")])]),t._v(\" \"),v(\"p\",[t._v(\"需要注意的是，由于Github本身功能非常多，我们的焦点并不是去实现Github的所有业务功能。因此，我们只需要实现一个APP的骨架，能达到上面这两点即可。下面对我们要实现的功能如下：\")]),t._v(\" \"),v(\"ol\",[v(\"li\",[t._v(\"实现Github账号登录、退出登录功能\")]),t._v(\" \"),v(\"li\",[t._v(\"登录后可以查看自己的项目主页\")]),t._v(\" \"),v(\"li\",[t._v(\"支持换肤\")]),t._v(\" \"),v(\"li\",[t._v(\"支持多语言\")]),t._v(\" \"),v(\"li\",[t._v(\"登录状态可以持久化；\")])]),t._v(\" \"),v(\"p\",[t._v(\"要实现上面这些功能会涉及到如下技术点：\")]),t._v(\" \"),v(\"ol\",[v(\"li\",[t._v(\"网络请求；需要请求Github API。\")]),t._v(\" \"),v(\"li\",[t._v(\"Json转Dart Model类；\")]),t._v(\" \"),v(\"li\",[t._v(\"全局状态管理；语言、主题、登录态等都需要全局共享。\")]),t._v(\" \"),v(\"li\",[t._v(\"持久化存储；保存登录信息，用户信息等。\")]),t._v(\" \"),v(\"li\",[t._v(\"支持国际化、Intl包的使用\")])]),t._v(\" \"),v(\"p\",[t._v(\"现在，目标已经确定，在接下来章节中，我们将分模块一步一步实现上述功能。\")])])}),[],!1,null,null,null);_.default=i.exports}}]);"
  },
  {
    "path": "docs/assets/js/154.40b97c4a.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[154],{763:function(t,s,a){\"use strict\";a.r(s);var r=a(45),n=Object(r.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_15-3-model类定义\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_15-3-model类定义\"}},[t._v(\"#\")]),t._v(\" 15.3 Model类定义\")]),t._v(\" \"),a(\"p\",[t._v(\"本节我们先梳理一下APP中将用到的数据，然后生成相应的Dart Model类。Json文件转Dart Model的方案采用前面介绍过的 json_model 包方案\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"github账号信息\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#github账号信息\"}},[t._v(\"#\")]),t._v(\" Github账号信息\")]),t._v(\" \"),a(\"p\",[t._v(\"登录Github后，我们需要获取当前登录者的Github账号信息，Github API接口返回Json结构如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-json extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-json\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"login\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"octocat\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//用户登录名\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"avatar_url\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"https://github.com/images/error/octocat_happy.gif\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//用户头像地址\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"type\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"User\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//用户类型，可能是组织\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"name\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"monalisa octocat\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//用户名字\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"company\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"GitHub\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//公司\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"blog\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"https://github.com/blog\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//博客地址\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"location\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"San Francisco\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 用户所处地理位置\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"email\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"octocat@github.com\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 邮箱\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"hireable\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"bio\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"There once was...\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 用户简介\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"public_repos\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 公开项目数\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"followers\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//关注该用户的人数\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"following\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 该用户关注的人数\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"created_at\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"2008-01-14T04:33:35Z\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 账号创建时间\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"updated_at\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"2008-01-14T04:33:35Z\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 账号信息更新时间\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"total_private_repos\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//该用户总的私有项目数(包括参与的其它组织的私有项目)\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"owned_private_repos\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//该用户自己的私有项目数\")]),t._v(\"\\n  ... \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略其它字段\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"我们在“jsons”目录下创建一个“user.json”文件保存上述信息。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"api缓存策略信息\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#api缓存策略信息\"}},[t._v(\"#\")]),t._v(\" API缓存策略信息\")]),t._v(\" \"),a(\"p\",[t._v(\"由于Github服务器在国内访问速度较慢，我们对Github API应用一些简单的缓存策略。我们在“jsons”目录下创建一个“cacheConfig.json”文件缓存策略信息，定义如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-json extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-json\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"enable\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 是否启用缓存\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"maxAge\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1000\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 缓存的最长时间，单位（秒）\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"maxCount\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 最大缓存数\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"h3\",{attrs:{id:\"用户信息\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#用户信息\"}},[t._v(\"#\")]),t._v(\" 用户信息\")]),t._v(\" \"),a(\"p\",[t._v(\"用户信息(Profile)应包括如下信息：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"Github账号信息；由于我们的APP可以切换账号登录，且登录后再次打开则不需要登录，所以我们需要对用户账号信息和登录状态进行持久化。\")]),t._v(\" \"),a(\"li\",[t._v(\"应用使用配置信息；每一个用户都应有自己的APP配置信息，如主题、语言、以及数据缓存策略等。\")]),t._v(\" \"),a(\"li\",[t._v(\"用户注销登录后，为了便于用户在退出APP前再次登录，我们需要记住上次登录的用户名。\")])]),t._v(\" \"),a(\"p\",[t._v(\"需要注意的是，目前Github有三种登录方式，分别是账号密码登录、oauth授权登录、二次认证登录；这三种登录方式的安全性依次加强，但是在本示例中，为了简单起见，我们使用账号密码登录，因此我们需要保存用户的密码。\")]),t._v(\" \"),a(\"blockquote\",[a(\"p\",[t._v(\"注意：在这里需要提醒读者，在登录场景中，保护用户账号安全是一个非常重要且永恒的话题，在实际开发中应严格杜绝直接明文存储用户账密的行为。\")])]),t._v(\" \"),a(\"p\",[t._v(\"我们在“jsons”目录下创建一个“profile.json”文件，结构如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-json extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-json\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"user\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$user\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v('//Github账号信息，结构见\"user.json\"')]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"token\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 登录用户的token(oauth)或密码\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"theme\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"5678\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//主题色值\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"cache\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$cacheConfig\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v('// 缓存策略信息，结构见\"cacheConfig.json\"')]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"lastLogin\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//最近一次的注销登录的用户名\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"locale\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"\"')]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// APP语言信息\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"h3\",{attrs:{id:\"项目信息\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#项目信息\"}},[t._v(\"#\")]),t._v(\" 项目信息\")]),t._v(\" \"),a(\"p\",[t._v(\"由于APP主页要显示其所有项目信息，我们在“jsons”目录下创建一个“repo.json”文件保存项目信息。通过参考Github 获取项目信息的API文档，定义出最终的“repo.json”文件结构，如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-json extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-json\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"id\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1296269\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"name\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hello-World\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//项目名称\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"full_name\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"octocat/Hello-World\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//项目完整名称\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"owner\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$user\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v('// 项目拥有者，结构见\"user.json\"')]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"parent\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$repo\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 如果是fork的项目，则此字段表示fork的父项目信息\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"private\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 是否私有项目\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"description\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"This your first repo!\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//项目描述\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"fork\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 该项目是否为fork的项目\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"language\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"JavaScript\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//该项目的主要编程语言\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"forks_count\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"9\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// fork了该项目的数量\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"stargazers_count\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//该项目的star数量\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"size\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"108\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 项目占用的存储大小\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"default_branch\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"master\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//项目的默认分支\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"open_issues_count\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//该项目当前打开的issue数量\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"pushed_at\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"2011-01-26T19:06:43Z\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"created_at\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"2011-01-26T19:01:12Z\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"updated_at\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"2011-01-26T19:14:43Z\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"subscribers_count\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"42\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//订阅（关注）该项目的人数\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"license\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 该项目的开源许可证\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"key\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"mit\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"name\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"MIT License\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"spdx_id\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"MIT\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"url\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"https://api.github.com/licenses/mit\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"node_id\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"MDc6TGljZW5zZW1pdA==\"')]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  ...\"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略其它字段\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"h3\",{attrs:{id:\"生成dart-model类\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#生成dart-model类\"}},[t._v(\"#\")]),t._v(\" 生成Dart Model类\")]),t._v(\" \"),a(\"p\",[t._v(\"现在，我们需要的Json数据已经定义完毕，现在只需要运行json_model package提供的命令来通过json文件生成相应的Dart类：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-shell extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[a(\"code\",[t._v(\"flutter packages pub run json_model\\n\")])])]),a(\"p\",[t._v(\"命令执行成功后，可以看到lib/models文件夹下会生成相应的Dart Model类：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language- extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[a(\"code\",[t._v(\"├── models\\n│   ├── cacheConfig.dart\\n│   ├── cacheConfig.g.dart\\n│   ├── index.dart\\n│   ├── profile.dart\\n│   ├── profile.g.dart\\n│   ├── repo.dart\\n│   ├── repo.g.dart\\n│   ├── user.dart\\n│   └── user.g.dart\\n\\n\")])])]),a(\"h3\",{attrs:{id:\"数据持久化\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#数据持久化\"}},[t._v(\"#\")]),t._v(\" 数据持久化\")]),t._v(\" \"),a(\"p\",[t._v(\"我们使用shared_preferences包来对登录用户的Profile信息进行持久化。shared_preferences是一个Flutter插件，它通过Android和iOS平台提供的机制来实现数据持久化。由于shared_preferences的使用非常简单，读者可以自行查看其文档，在此不再赘述。\")])])}),[],!1,null,null,null);s.default=n.exports}}]);"
  },
  {
    "path": "docs/assets/js/155.b55f4193.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[155],{764:function(t,s,a){\"use strict\";a.r(s);var n=a(45),p=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_15-5-网络请求封装\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_15-5-网络请求封装\"}},[t._v(\"#\")]),t._v(\" 15.5 网络请求封装\")]),t._v(\" \"),a(\"p\",[t._v(\"本节我们会基于前面介绍过的dio网络库封装APP中用到的网络请求接口，并同时应用一个简单的缓存策略。下面我们先介绍一下网络接口缓存原理，然后再封装APP的业务请求接口。\")]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_15-5-1-网络接口缓存\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_15-5-1-网络接口缓存\"}},[t._v(\"#\")]),t._v(\" 15.5.1 网络接口缓存\")]),t._v(\" \"),a(\"p\",[t._v(\"由于在国内访问Github服务器速度较慢，所以我们应用一些简单的缓存策略：将请求的url作为key，对请求的返回值在一个指定时间段类进行缓存，另外设置一个最大缓存数，当超过最大缓存数后移除最早的一条缓存。但是也得提供一种针对特定接口或请求决定是否启用缓存的机制，这种机制可以指定哪些接口或那次请求不应用缓存，这种机制是很有必要的，比如登录接口就不应该缓存，又比如用户在下拉刷新时就不应该再应用缓存。在实现缓存之前我们先定义保存缓存信息的\"),a(\"code\",[t._v(\"CacheObject\")]),t._v(\"类：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CacheObject\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CacheObject\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" timeStamp \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" DateTime\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"now\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"millisecondsSinceEpoch\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  Response response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  int timeStamp\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 缓存创建时间\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  bool \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"operator\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"other\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"hashCode \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" other\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"hashCode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//将请求uri作为缓存的key\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  int \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" hashCode \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"realUri\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"hashCode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"接下来我们需要实现具体的缓存策略，由于我们使用的是dio package，所以我们可以直接通过拦截器来实现缓存策略：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'dart:collection'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:dio/dio.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'../index.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CacheObject\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CacheObject\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" timeStamp \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" DateTime\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"now\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"millisecondsSinceEpoch\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  Response response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  int timeStamp\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  bool \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"operator\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"other\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"hashCode \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" other\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"hashCode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  int \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" hashCode \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"realUri\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"hashCode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"NetCache\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Interceptor\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 为确保迭代器顺序和对象插入时间一致顺序一致，我们使用LinkedHashMap\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" cache \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" LinkedHashMap\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" CacheObject\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onRequest\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"RequestOptions options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"Global\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cache\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"enable\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v('// refresh标记是否是\"下拉刷新\"')]),t._v(\"\\n    bool refresh \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"extra\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"refresh\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//如果是下拉刷新，先删除相关缓存\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"refresh\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"extra\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"list\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//若是列表，则只要url中包含当前path的缓存全部删除（简单实现，并不精准）\")]),t._v(\"\\n        cache\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"removeWhere\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" v\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"contains\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"path\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 如果不是列表，则只删除uri相同的缓存\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"delete\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"uri\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"extra\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"noCache\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\"\\n        options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"method\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toLowerCase\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'get'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      String key \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"extra\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"cacheKey\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"uri\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" ob \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" cache\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ob \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//若缓存未过期，则返回缓存内容\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"DateTime\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"now\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"millisecondsSinceEpoch \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" ob\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"timeStamp\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1000\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"\\n            Global\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cache\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"maxAge\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" cache\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//若已过期则删除缓存，继续向服务器请求\")]),t._v(\"\\n          cache\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"remove\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onError\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"DioError err\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 错误状态不缓存\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onResponse\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Response response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 如果启用缓存，将返回结果保存到缓存\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Global\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cache\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"enable\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_saveCache\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_saveCache\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Response object\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    RequestOptions options \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" object\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"request\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"extra\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"noCache\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\"\\n        options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"method\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toLowerCase\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"get\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 如果缓存数量超过最大数量限制，则先移除最早的一条记录\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"cache\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"length \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" Global\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cache\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"maxCount\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        cache\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"remove\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"cache\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"cache\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"keys\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"first\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n      String key \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"extra\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"cacheKey\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"uri\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      cache\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CacheObject\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"object\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"delete\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    cache\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"remove\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"关于代码的解释都在注释中了，在此需要说明的是dio包的\"),a(\"code\",[t._v(\"option.extra\")]),t._v(\"是专门用于扩展请求参数的，我们通过定义了“refresh”和“noCache”两个参数实现了“针对特定接口或请求决定是否启用缓存的机制”，这两个参数含义如下：\")]),t._v(\" \"),a(\"table\",[a(\"thead\",[a(\"tr\",[a(\"th\",[t._v(\"参数名\")]),t._v(\" \"),a(\"th\",[t._v(\"类型\")]),t._v(\" \"),a(\"th\",[t._v(\"解释\")])])]),t._v(\" \"),a(\"tbody\",[a(\"tr\",[a(\"td\",[t._v(\"refresh\")]),t._v(\" \"),a(\"td\",[t._v(\"bool\")]),t._v(\" \"),a(\"td\",[t._v(\"如果为true，则本次请求不使用缓存，但新的请求结果依然会被缓存\")])]),t._v(\" \"),a(\"tr\",[a(\"td\",[t._v(\"noCache\")]),t._v(\" \"),a(\"td\",[t._v(\"bool\")]),t._v(\" \"),a(\"td\",[t._v(\"本次请求禁用缓存，请求结果也不会被缓存。\")])])])]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_15-5-2-封装网络请求\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_15-5-2-封装网络请求\"}},[t._v(\"#\")]),t._v(\" 15.5.2 封装网络请求\")]),t._v(\" \"),a(\"p\",[t._v(\"一个完整的APP，可能会涉及很多网络请求，为了便于管理、收敛请求入口，工程上最好的作法就是将所有网络请求放到同一个源码文件中。由于我们的接口都是请求的Github 开发平台提供的API，所以我们定义一个Git类，专门用于Github API接口调用。另外，在调试过程中，我们通常需要一些工具来查看网络请求、响应报文，使用网络代理工具来调试网络数据问题是主流方式。配置代理需要在应用中指定代理服务器的地址和端口，另外Github API是HTTPS协议，所以在配置完代理后还应该禁用证书校验，这些配置我们在Git类初始化时执行（\"),a(\"code\",[t._v(\"init()方法\")]),t._v(\"）。下面是Git类的源码：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'dart:async'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'dart:convert'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'dart:io'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:dio/dio.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:dio/adapter.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'../index.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Git\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 在网络请求过程中可能会需要使用当前的context信息，比如在请求失败时\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 打开一个新路由，而打开新路由需要context信息。\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Git\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    _options \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Options\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"extra\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"context\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  Options _options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" Dio dio \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Dio\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BaseOptions\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    baseUrl\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'https://api.github.com/'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    headers\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      HttpHeaders\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"acceptHeader\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"application/vnd.github.squirrel-girl-preview,\"')]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"application/vnd.github.symmetra-preview+json\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"init\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 添加缓存插件\")]),t._v(\"\\n    dio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"interceptors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Global\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"netCache\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 设置用户token（可能为null，代表未登录）\")]),t._v(\"\\n    dio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"headers\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"HttpHeaders\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"authorizationHeader\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Global\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"token\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 在调试模式下需要抓包调试，所以我们使用代理，并禁用HTTPS证书校验\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"Global\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isRelease\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"dio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"httpClientAdapter \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"as\")]),t._v(\" DefaultHttpClientAdapter\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onHttpClientCreate \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"client\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        client\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"findProxy \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"uri\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"PROXY 10.1.10.250:8888\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//代理工具会提供一个抓包的自签名证书，会通不过证书校验，所以我们禁用证书校验\")]),t._v(\"\\n        client\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"badCertificateCallback \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"X509Certificate cert\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" String host\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int port\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 登录接口，登录成功后返回用户信息\")]),t._v(\"\\n  Future\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"User\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"login\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String login\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" String pwd\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    String basic \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Basic '\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" base64\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"encode\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"utf8\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"encode\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'$login:$pwd'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" r \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" dio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"/users/$login\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"merge\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"headers\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        HttpHeaders\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"authorizationHeader\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" basic\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" extra\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"noCache\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//本接口禁用缓存\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//登录成功后更新公共头（authorization），此后的所有请求都会带上用户身份信息\")]),t._v(\"\\n    dio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"headers\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"HttpHeaders\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"authorizationHeader\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" basic\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//清空所有缓存\")]),t._v(\"\\n    Global\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"netCache\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cache\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"clear\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//更新profile中的token信息\")]),t._v(\"\\n    Global\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"token \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" basic\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" User\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fromJson\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"r\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//获取用户项目列表\")]),t._v(\"\\n  Future\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"List\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Repo\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">>\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getRepos\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"Map\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" queryParameters\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//query参数，用于接收分页信息\")]),t._v(\"\\n      refresh \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"refresh\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 列表下拉刷新，需要删除缓存（拦截器中会读取这些信息）\")]),t._v(\"\\n      _options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"extra\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addAll\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"refresh\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"list\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" r \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" dio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"List\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"user/repos\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      queryParameters\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" queryParameters\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" r\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"map\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Repo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fromJson\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toList\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"可以看到我们在\"),a(\"code\",[t._v(\"init()\")]),t._v(\"方法中，我们判断了是否是调试环境，然后做了一些针对调试环境的网络配置（设置代理和禁用证书校验）。而\"),a(\"code\",[t._v(\"Git.init()\")]),t._v(\"方法是应用启动时被调用的（\"),a(\"code\",[t._v(\"Global.init()\")]),t._v(\"方法中会调用\"),a(\"code\",[t._v(\"Git.init()\")]),t._v(\"）。\")]),t._v(\" \"),a(\"p\",[t._v(\"另外需要注意，我们所有的网络请求是通过同一个\"),a(\"code\",[t._v(\"dio\")]),t._v(\"实例（静态变量）发出的，在创建该\"),a(\"code\",[t._v(\"dio\")]),t._v(\"实例时我们将Github API的基地址和API支持的Header进行了全局配置，这样所有通过该\"),a(\"code\",[t._v(\"dio\")]),t._v(\"实例发出的请求都会默认使用者些配置。\")]),t._v(\" \"),a(\"p\",[t._v(\"在本实例中，我们只用到了登录接口和获取用户项目的接口，所以在\"),a(\"code\",[t._v(\"Git\")]),t._v(\"类中只定义了\"),a(\"code\",[t._v(\"login(…)\")]),t._v(\"和\"),a(\"code\",[t._v(\"getRepos(…)\")]),t._v(\"方法，如果读者要在本实例的基础上扩充功能，读者可以将其它的接口请求方法添加到\"),a(\"code\",[t._v(\"Git\")]),t._v(\"类中，这样便实现了网络请求接口在代码层面的集中管理和维护。\")])])}),[],!1,null,null,null);s.default=p.exports}}]);"
  },
  {
    "path": "docs/assets/js/156.f4128841.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[156],{766:function(t,e,a){\"use strict\";a.r(e);var s=a(45),n=Object(s.a)({},(function(){var t=this,e=t.$createElement,a=t._self._c||e;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_2-5-调试flutter应用\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_2-5-调试flutter应用\"}},[t._v(\"#\")]),t._v(\" 2.5 调试Flutter应用\")]),t._v(\" \"),a(\"p\",[t._v(\"有各种各样的工具和功能来帮助调试Flutter应用程序。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"dart-分析器\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#dart-分析器\"}},[t._v(\"#\")]),t._v(\" Dart 分析器\")]),t._v(\" \"),a(\"p\",[t._v(\"在运行应用程序前，请运行\"),a(\"code\",[t._v(\"flutter analyze\")]),t._v(\"测试你的代码。这个工具是一个静态代码检查工具，它是\"),a(\"code\",[t._v(\"dartanalyzer\")]),t._v(\"工具的一个包装，主要用于分析代码并帮助开发者发现可能的错误，比如，Dart分析器大量使用了代码中的类型注释来帮助追踪问题，避免\"),a(\"code\",[t._v(\"var\")]),t._v(\"、无类型的参数、无类型的列表文字等。\")]),t._v(\" \"),a(\"p\",[t._v(\"如果你使用IntelliJ的Flutter插件，那么分析器在打开IDE时就已经自动启用了，如果读者使用的是其它IDE，强烈建议读者启用Dart 分析器，因为在大多数时候，Dart 分析器可以在代码运行前发现大多数问题。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"dart-observatory-语句级的单步调试和分析器\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#dart-observatory-语句级的单步调试和分析器\"}},[t._v(\"#\")]),t._v(\" Dart Observatory (语句级的单步调试和分析器)\")]),t._v(\" \"),a(\"p\",[t._v(\"如果我们使用\"),a(\"code\",[t._v(\"flutter run\")]),t._v(\"启动应用程序，那么当它运行时，我们可以打开Observatory工具的Web页面，例如Observatory默认监听\"),a(\"a\",{attrs:{href:\"http://127.0.0.1:8100/\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"http://127.0.0.1:8100/\"),a(\"OutboundLink\")],1),t._v(\"，可以在浏览器中直接打开该链接。直接使用语句级单步调试器连接到您的应用程序。如果您使用的是IntelliJ，则还可以使用其内置的调试器来调试您的应用程序。\")]),t._v(\" \"),a(\"p\",[t._v(\"Observatory 同时支持分析、检查堆等。有关Observatory的更多信息请参考\"),a(\"a\",{attrs:{href:\"https://dart-lang.github.io/observatory/\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Observatory 文档\"),a(\"OutboundLink\")],1),t._v(\"。\")]),t._v(\" \"),a(\"p\",[t._v(\"如果您使用Observatory进行分析，请确保通过\"),a(\"code\",[t._v(\"--profile\")]),t._v(\"选项来运行\"),a(\"code\",[t._v(\"flutter run\")]),t._v(\"命令来运行应用程序。 否则，配置文件中将出现的主要问题将是调试断言，以验证框架的各种不变量（请参阅下面的“调试模式断言”）。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"debugger-声明\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#debugger-声明\"}},[t._v(\"#\")]),t._v(\" \"),a(\"code\",[t._v(\"debugger()\")]),t._v(\" 声明\")]),t._v(\" \"),a(\"p\",[t._v(\"当使用Dart Observatory（或另一个Dart调试器，例如IntelliJ IDE中的调试器）时，可以使用该\"),a(\"code\",[t._v(\"debugger()\")]),t._v(\"语句插入编程式断点。要使用这个，你必须添加\"),a(\"code\",[t._v(\"import 'dart:developer';\")]),t._v(\"到相关文件顶部。\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"debugger()\")]),t._v(\"语句采用一个可选\"),a(\"code\",[t._v(\"when\")]),t._v(\"参数，您可以指定该参数仅在特定条件为真时中断，如下所示：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"someFunction\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"double offset\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"debugger\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"when\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" offset \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"30.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// ...\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"h3\",{attrs:{id:\"print、debugprint、flutter-logs\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#print、debugprint、flutter-logs\"}},[t._v(\"#\")]),t._v(\" \"),a(\"code\",[t._v(\"print\")]),t._v(\"、\"),a(\"code\",[t._v(\"debugPrint\")]),t._v(\"、\"),a(\"code\",[t._v(\"flutter logs\")])]),t._v(\" \"),a(\"p\",[t._v(\"Dart \"),a(\"code\",[t._v(\"print()\")]),t._v(\"功能将输出到系统控制台，您可以使用\"),a(\"code\",[t._v(\"flutter logs\")]),t._v(\"来查看它（基本上是一个包装\"),a(\"code\",[t._v(\"adb logcat\")]),t._v(\"）。\")]),t._v(\" \"),a(\"p\",[t._v(\"如果你一次输出太多，那么Android有时会丢弃一些日志行。为了避免这种情况，您可以使用Flutter的\"),a(\"code\",[t._v(\"foundation\")]),t._v(\"库中的\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/foundation/debugPrint.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"debugPrint()\")]),a(\"OutboundLink\")],1),t._v(\"。 这是一个封装print，它将输出限制在一个级别，避免被Android内核丢弃。\")]),t._v(\" \"),a(\"p\",[t._v(\"Flutter框架中的许多类都有\"),a(\"code\",[t._v(\"toString\")]),t._v(\"实现。按照惯例，这些输出通常包括对象的\"),a(\"code\",[t._v(\"runtimeType\")]),t._v(\"单行输出，通常在表单中ClassName(more information about this instance…)。 树中使用的一些类也具有\"),a(\"code\",[t._v(\"toStringDeep\")]),t._v(\"，从该点返回整个子树的多行描述。已一些具有详细信息\"),a(\"code\",[t._v(\"toString\")]),t._v(\"的类会实现一个\"),a(\"code\",[t._v(\"toStringShort\")]),t._v(\"，它只返回对象的类型或其他非常简短的（一个或两个单词）描述。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"调试模式断言\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#调试模式断言\"}},[t._v(\"#\")]),t._v(\" 调试模式断言\")]),t._v(\" \"),a(\"p\",[t._v(\"在Flutter应用调试过程中，Dart \"),a(\"code\",[t._v(\"assert\")]),t._v(\"语句被启用，并且Flutter框架使用它来执行许多运行时检查来验证是否违反一些不可变的规则。\")]),t._v(\" \"),a(\"p\",[t._v(\"当一个不可变的规则被违反时，它被报告给控制台，并带有一些上下文信息来帮助追踪问题的根源。\")]),t._v(\" \"),a(\"p\",[t._v(\"要关闭调试模式并使用发布模式，请使用\"),a(\"code\",[t._v(\"flutter run --release\")]),t._v(\"运行您的应用程序。 这也关闭了Observatory调试器。一个中间模式可以关闭除Observatory之外所有调试辅助工具的，称为“profile mode”，用\"),a(\"code\",[t._v(\"--profile\")]),t._v(\"替代\"),a(\"code\",[t._v(\"--release\")]),t._v(\"即可。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"调试应用程序层\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#调试应用程序层\"}},[t._v(\"#\")]),t._v(\" 调试应用程序层\")]),t._v(\" \"),a(\"p\",[t._v(\"Flutter框架的每一层都提供了将其当前状态或事件转储(dump)到控制台（使用\"),a(\"code\",[t._v(\"debugPrint\")]),t._v(\"）的功能。\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"widget-树\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#widget-树\"}},[t._v(\"#\")]),t._v(\" Widget 树\")]),t._v(\" \"),a(\"p\",[t._v(\"要转储Widgets树的状态，请调用\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/debugDumpApp.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"debugDumpApp()\")]),a(\"OutboundLink\")],1),t._v(\"。 只要应用程序已经构建了至少一次（即在调用\"),a(\"code\",[t._v(\"build()\")]),t._v(\"之后的任何时间），您可以在应用程序未处于构建阶段（即，不在\"),a(\"code\",[t._v(\"build()\")]),t._v(\"方法内调用 ）的任何时间调用此方法（在调用\"),a(\"code\",[t._v(\"runApp()\")]),t._v(\"之后）。\")]),t._v(\" \"),a(\"p\",[t._v(\"如, 这个应用程序:\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"main\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"runApp\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MaterialApp\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      home\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AppHome\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AppHome\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Material\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Center\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FlatButton\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"debugDumpApp\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Dump App'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"…会输出这样的内容（精确的细节会根据框架的版本、设备的大小等等而变化）：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-shell extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[a(\"code\",[t._v(\"I/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\": WidgetsFlutterBinding - CHECKED MODE\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\": RenderObjectToWidgetAdapter\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"RenderBox\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"GlobalObjectKey RenderView\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"497039273\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" renderObject: RenderView\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\": └MaterialApp\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"state: _MaterialAppState\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1009803148\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"))\")]),t._v(\"\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":  └ScrollConfiguration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":   └AnimatedTheme\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"duration: 200ms\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" state: _AnimatedThemeState\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"543295893\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" ticker inactive\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" ThemeDataTween\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ThemeData\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Brightness.light Color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"0xff2196f3\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" etc\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"..\")]),t._v(\".\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" → null\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"))\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":    └Theme\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ThemeData\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Brightness.light Color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"0xff2196f3\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" etc\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"..\")]),t._v(\".\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"))\")]),t._v(\"\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":     └WidgetsApp\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"GlobalObjectKey _MaterialAppState\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1009803148\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" state: _WidgetsAppState\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"552902158\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"))\")]),t._v(\"\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":      └CheckedModeBanner\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":       └Banner\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":        └CustomPaint\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"renderObject: RenderCustomPaint\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":         └DefaultTextStyle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"inherit: \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" color: Color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"0xd0ff0000\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" family: \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"monospace\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" size: \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"48.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" weight: \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"900\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" decoration: double Color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"0xffffff00\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" TextDecoration.underline\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":          └MediaQuery\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"MediaQueryData\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"size: Size\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"411.4\")]),t._v(\", \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"683.4\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\", devicePixelRatio: \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2.625\")]),t._v(\", textScaleFactor: \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.0\")]),t._v(\", padding: EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),t._v(\", \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"24.0\")]),t._v(\", \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),t._v(\", \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"))\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":           └LocaleQuery\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"null\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":            └Title\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color: Color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"0xff2196f3\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"))\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"..\")]),t._v(\". \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"#省略剩余内容\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"这是一个“扁平化”的树，显示了通过各种构建函数投影的所有widget（如果你在widget树的根中调用\"),a(\"code\",[t._v(\"toStringDeepwidget\")]),t._v(\"，这是你获得的树）。 你会看到很多在你的应用源代码中没有出现的widget，因为它们是被框架中widget的\"),a(\"code\",[t._v(\"build()\")]),t._v(\"函数插入的。例如，\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/material/InkFeature-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"InkFeature\")]),a(\"OutboundLink\")],1),t._v(\"是Material widget的一个实现细节 。\")]),t._v(\" \"),a(\"p\",[t._v(\"当按钮从被按下变为被释放时debugDumpApp()被调用，FlatButton对象同时调用\"),a(\"code\",[t._v(\"setState()\")]),t._v('，并将自己标记为\"dirty\"。 这就是为什么如果你看转储，你会看到特定的对象标记为“dirty”。您还可以查看已注册了哪些手势监听器; 在这种情况下，一个单一的GestureDetector被列出，并且监听“tap”手势（“tap”是'),a(\"code\",[t._v(\"TapGestureDetector\")]),t._v(\"的\"),a(\"code\",[t._v(\"toStringShort\")]),t._v(\"函数输出的）\")]),t._v(\" \"),a(\"p\",[t._v(\"如果您编写自己的widget，则可以通过覆盖\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/Widget/debugFillProperties.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"debugFillProperties()\")]),a(\"OutboundLink\")],1),t._v(\"来添加信息。 将\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/foundation/DiagnosticsProperty-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"DiagnosticsProperty\"),a(\"OutboundLink\")],1),t._v(\"对象作为方法参数，并调用父类方法。 该函数是该\"),a(\"code\",[t._v(\"toString\")]),t._v(\"方法用来填充小部件描述信息的。\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"渲染树\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#渲染树\"}},[t._v(\"#\")]),t._v(\" 渲染树\")]),t._v(\" \"),a(\"p\",[t._v(\"如果您尝试调试布局问题，那么Widget树可能不够详细。在这种情况下，您可以通过调用\"),a(\"code\",[t._v(\"debugDumpRenderTree()\")]),t._v(\"转储渲染树。 正如\"),a(\"code\",[t._v(\"debugDumpApp()\")]),t._v(\"，除布局或绘制阶段外，您可以随时调用此函数。作为一般规则，从\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/scheduler/SchedulerBinding/addPersistentFrameCallback.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"frame 回调\"),a(\"OutboundLink\")],1),t._v(\" 或事件处理器中调用它是最佳解决方案。\")]),t._v(\" \"),a(\"p\",[t._v(\"要调用\"),a(\"code\",[t._v(\"debugDumpRenderTree()\")]),t._v(\"，您需要添加\"),a(\"code\",[t._v(\"import'package:flutter/rendering.dart';\")]),t._v(\"到您的源文件。\")]),t._v(\" \"),a(\"p\",[t._v(\"上面这个小例子的输出结果如下所示：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-shell extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[a(\"code\",[t._v(\"I/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\": RenderView\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":  │ debug mode enabled - android\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":  │ window size: Size\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1080.0\")]),t._v(\", \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1794.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"in physical pixels\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":  │ device pixel ratio: \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2.625\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"physical pixels per logical pixel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":  │ configuration: Size\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"411.4\")]),t._v(\", \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"683.4\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" at \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),t._v(\".625x \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"in logical pixels\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":  │\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":  └─child: RenderCustomPaint\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":    │ creator: CustomPaint ← Banner ← CheckedModeBanner ←\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":    │   WidgetsApp-\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"GlobalObjectKey _MaterialAppState\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1009803148\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" ←\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":    │   Theme ← AnimatedTheme ← ScrollConfiguration ← MaterialApp ←\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":    │   \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"root\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\"\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":    │ parentData: \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"none\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":    │ constraints: BoxConstraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"w\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"411.4\")]),t._v(\", \"),a(\"span\",{pre:!0,attrs:{class:\"token assign-left variable\"}},[t._v(\"h\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"683.4\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":    │ size: Size\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"411.4\")]),t._v(\", \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"683.4\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"..\")]),t._v(\". \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"# 省略\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"这是根\"),a(\"code\",[t._v(\"RenderObject\")]),t._v(\"对象的\"),a(\"code\",[t._v(\"toStringDeep\")]),t._v(\"函数的输出。\")]),t._v(\" \"),a(\"p\",[t._v(\"当调试布局问题时，关键要看的是\"),a(\"code\",[t._v(\"size\")]),t._v(\"和\"),a(\"code\",[t._v(\"constraints\")]),t._v(\"字段。约束沿着树向下传递，尺寸向上传递。\")]),t._v(\" \"),a(\"p\",[t._v(\"如果您编写自己的渲染对象，则可以通过覆盖\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/rendering/Layer/debugFillProperties.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"debugFillProperties()\")]),a(\"OutboundLink\")],1),t._v(\"将信息添加到转储。 将\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/foundation/DiagnosticsProperty-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"DiagnosticsProperty\"),a(\"OutboundLink\")],1),t._v(\"对象作为方法的参数，并调用父类方法。\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"layer树\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#layer树\"}},[t._v(\"#\")]),t._v(\" Layer树\")]),t._v(\" \"),a(\"p\",[t._v(\"读者可以理解为渲染树是可以分层的，而最终绘制需要将不同的层合成起来，而Layer则是绘制时需要合成的层，如果您尝试调试合成问题，则可以使用\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/rendering/debugDumpLayerTree.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"debugDumpLayerTree()\")]),a(\"OutboundLink\")],1),t._v(\"。对于上面的例子，它会输出：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language- extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[a(\"code\",[t._v(\"I/flutter : TransformLayer\\nI/flutter :  │ creator: [root]\\nI/flutter :  │ offset: Offset(0.0, 0.0)\\nI/flutter :  │ transform:\\nI/flutter :  │   [0] 3.5,0.0,0.0,0.0\\nI/flutter :  │   [1] 0.0,3.5,0.0,0.0\\nI/flutter :  │   [2] 0.0,0.0,1.0,0.0\\nI/flutter :  │   [3] 0.0,0.0,0.0,1.0\\nI/flutter :  │\\nI/flutter :  ├─child 1: OffsetLayer\\nI/flutter :  │ │ creator: RepaintBoundary ← _FocusScope ← Semantics ← Focus-[GlobalObjectKey MaterialPageRoute(560156430)] ← _ModalScope-[GlobalKey 328026813] ← _OverlayEntry-[GlobalKey 388965355] ← Stack ← Overlay-[GlobalKey 625702218] ← Navigator-[GlobalObjectKey _MaterialAppState(859106034)] ← Title ← ⋯\\nI/flutter :  │ │ offset: Offset(0.0, 0.0)\\nI/flutter :  │ │\\nI/flutter :  │ └─child 1: PictureLayer\\nI/flutter :  │\\nI/flutter :  └─child 2: PictureLayer\\n\")])])]),a(\"p\",[t._v(\"这是根\"),a(\"code\",[t._v(\"Layer\")]),t._v(\"的\"),a(\"code\",[t._v(\"toStringDeep\")]),t._v(\"输出的。\")]),t._v(\" \"),a(\"p\",[t._v(\"根部的变换是应用设备像素比的变换; 在这种情况下，每个逻辑像素代表3.5个设备像素。\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"RepaintBoundary\")]),t._v(\" widget在渲染树的层中创建了一个\"),a(\"code\",[t._v(\"RenderRepaintBoundary\")]),t._v(\"。这用于减少需要重绘的需求量。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"语义\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#语义\"}},[t._v(\"#\")]),t._v(\" 语义\")]),t._v(\" \"),a(\"p\",[t._v(\"您还可以调用\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/rendering/debugDumpSemanticsTree.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"debugDumpSemanticsTree()\")]),a(\"OutboundLink\")],1),t._v(\"获取语义树（呈现给系统可访问性API的树）的转储。 要使用此功能，必须首先启用辅助功能，例如启用系统辅助工具或\"),a(\"code\",[t._v(\"SemanticsDebugger\")]),t._v(\" （下面讨论）。\")]),t._v(\" \"),a(\"p\",[t._v(\"对于上面的例子，它会输出:\")]),t._v(\" \"),a(\"div\",{staticClass:\"language- extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[a(\"code\",[t._v('I/flutter : SemanticsNode(0; Rect.fromLTRB(0.0, 0.0, 411.4, 683.4))\\nI/flutter :  ├SemanticsNode(1; Rect.fromLTRB(0.0, 0.0, 411.4, 683.4))\\nI/flutter :  │ └SemanticsNode(2; Rect.fromLTRB(0.0, 0.0, 411.4, 683.4); canBeTapped)\\nI/flutter :  └SemanticsNode(3; Rect.fromLTRB(0.0, 0.0, 411.4, 683.4))\\nI/flutter :    └SemanticsNode(4; Rect.fromLTRB(0.0, 0.0, 82.0, 36.0); canBeTapped; \"Dump App\")\\n')])])]),a(\"h3\",{attrs:{id:\"调度\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#调度\"}},[t._v(\"#\")]),t._v(\" 调度\")]),t._v(\" \"),a(\"p\",[t._v(\"要找出相对于帧的开始/结束事件发生的位置，可以切换\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/scheduler/debugPrintBeginFrameBanner.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"debugPrintBeginFrameBanner\")]),a(\"OutboundLink\")],1),t._v(\"和\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/scheduler/debugPrintEndFrameBanner.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"debugPrintEndFrameBanner\")]),a(\"OutboundLink\")],1),t._v(\"布尔值以将帧的开始和结束打印到控制台。\")]),t._v(\" \"),a(\"p\",[t._v(\"例如:\")]),t._v(\" \"),a(\"div\",{staticClass:\"language- extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[a(\"code\",[t._v(\"I/flutter : ▄▄▄▄▄▄▄▄ Frame 12         30s 437.086ms ▄▄▄▄▄▄▄▄\\nI/flutter : Debug print: Am I performing this work more than once per frame?\\nI/flutter : Debug print: Am I performing this work more than once per frame?\\nI/flutter : ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀\\n\")])])]),a(\"p\",[a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/scheduler/debugPrintScheduleFrameStacks.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"debugPrintScheduleFrameStacks\")]),a(\"OutboundLink\")],1),t._v(\"还可以用来打印导致当前帧被调度的调用堆栈。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"可视化调试\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#可视化调试\"}},[t._v(\"#\")]),t._v(\" 可视化调试\")]),t._v(\" \"),a(\"p\",[t._v(\"您也可以通过设置\"),a(\"code\",[t._v(\"debugPaintSizeEnabled\")]),t._v(\"为\"),a(\"code\",[t._v(\"true\")]),t._v(\"以可视方式调试布局问题。 这是来自\"),a(\"code\",[t._v(\"rendering\")]),t._v(\"库的布尔值。它可以在任何时候启用，并在为true时影响绘制。 设置它的最简单方法是在\"),a(\"code\",[t._v(\"void main()\")]),t._v(\"的顶部设置。\")]),t._v(\" \"),a(\"p\",[t._v(\"当它被启用时，所有的盒子都会得到一个明亮的深青色边框，padding（来自widget如Padding）显示为浅蓝色，子widget周围有一个深蓝色框， 对齐方式（来自widget如Center和Align）显示为黄色箭头. 空白（如没有任何子节点的Container）以灰色显示。\")]),t._v(\" \"),a(\"p\",[a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/rendering/debugPaintBaselinesEnabled.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"debugPaintBaselinesEnabled\")]),a(\"OutboundLink\")],1),t._v(\"做了类似的事情，但对于具有基线的对象，文字基线以绿色显示，表意(ideographic)基线以橙色显示。\")]),t._v(\" \"),a(\"p\",[a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/rendering/debugPaintPointersEnabled.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"debugPaintPointersEnabled\")]),a(\"OutboundLink\")],1),t._v(\"标志打开一个特殊模式，任何正在点击的对象都会以深青色突出显示。 这可以帮助您确定某个对象是否以某种不正确的方式进行hit测试（Flutter检测点击的位置是否有能响应用户操作的widget）,例如，如果它实际上超出了其父项的范围，首先不会考虑通过hit测试。\")]),t._v(\" \"),a(\"p\",[t._v(\"如果您尝试调试合成图层，例如以确定是否以及在何处添加\"),a(\"code\",[t._v(\"RepaintBoundary\")]),t._v(\" widget，则可以使用\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/rendering/debugPaintLayerBordersEnabled.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"debugPaintLayerBordersEnabled\")]),a(\"OutboundLink\")],1),t._v(\" 标志， 该标志用橙色或轮廓线标出每个层的边界，或者使用\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/rendering/debugRepaintRainbowEnabled.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"debugRepaintRainbowEnabled\")]),a(\"OutboundLink\")],1),t._v(\"标志， 只要他们重绘时，这会使该层被一组旋转色所覆盖。\")]),t._v(\" \"),a(\"p\",[t._v(\"所有这些标志只能在调试模式下工作。通常，Flutter框架中以“\"),a(\"code\",[t._v(\"debug...\")]),t._v(\"” 开头的任何内容都只能在调试模式下工作。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"调试动画\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#调试动画\"}},[t._v(\"#\")]),t._v(\" 调试动画\")]),t._v(\" \"),a(\"p\",[t._v(\"调试动画最简单的方法是减慢它们的速度。为此，请将\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/scheduler/timeDilation.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"timeDilation\")]),a(\"OutboundLink\")],1),t._v(\"变量（在scheduler库中）设置为大于1.0的数字，例如50.0。 最好在应用程序启动时只设置一次。如果您在运行中更改它，尤其是在动画运行时将其值改小，则在观察时可能会出现倒退，这可能会导致断言命中，并且这通常会干扰我们的开发工作。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"调试性能问题\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#调试性能问题\"}},[t._v(\"#\")]),t._v(\" 调试性能问题\")]),t._v(\" \"),a(\"p\",[t._v(\"要了解您的应用程序导致重新布局或重新绘制的原因，您可以分别设置\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/rendering/debugPrintMarkNeedsLayoutStacks.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"debugPrintMarkNeedsLayoutStacks\")]),a(\"OutboundLink\")],1),t._v(\"和 \"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/rendering/debugPrintMarkNeedsPaintStacks.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"debugPrintMarkNeedsPaintStacks\")]),a(\"OutboundLink\")],1),t._v(\"标志。 每当渲染盒被要求重新布局和重新绘制时，这些都会将堆栈跟踪记录到控制台。如果这种方法对您有用，您可以使用\"),a(\"code\",[t._v(\"services\")]),t._v(\"库中的\"),a(\"code\",[t._v(\"debugPrintStack()\")]),t._v(\"方法按需打印堆栈痕迹。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"统计应用启动时间\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#统计应用启动时间\"}},[t._v(\"#\")]),t._v(\" 统计应用启动时间\")]),t._v(\" \"),a(\"p\",[t._v(\"要收集有关Flutter应用程序启动所需时间的详细信息，可以在运行\"),a(\"code\",[t._v(\"flutter run\")]),t._v(\"时使用\"),a(\"code\",[t._v(\"trace-startup\")]),t._v(\"和\"),a(\"code\",[t._v(\"profile\")]),t._v(\"选项。\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-shell extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[a(\"code\",[t._v(\"$ flutter run --trace-startup --profile\\n\")])])]),a(\"p\",[t._v(\"跟踪输出保存为\"),a(\"code\",[t._v(\"start_up_info.json\")]),t._v(\"，在Flutter工程目录在build目录下。输出列出了从应用程序启动到这些跟踪事件（以微秒捕获）所用的时间：\")]),t._v(\" \"),a(\"ul\",[a(\"li\",[t._v(\"进入Flutter引擎时.\")]),t._v(\" \"),a(\"li\",[t._v(\"展示应用第一帧时.\")]),t._v(\" \"),a(\"li\",[t._v(\"初始化Flutter框架时.\")]),t._v(\" \"),a(\"li\",[t._v(\"完成Flutter框架初始化时.\")])]),t._v(\" \"),a(\"p\",[t._v(\"如 :\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-json extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-json\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"engineEnterTimestampMicros\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"96025565262\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"timeToFirstFrameMicros\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2171978\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"timeToFrameworkInitMicros\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"514585\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"timeAfterFrameworkInitMicros\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1657393\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"h3\",{attrs:{id:\"跟踪dart代码性能\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#跟踪dart代码性能\"}},[t._v(\"#\")]),t._v(\" 跟踪Dart代码性能\")]),t._v(\" \"),a(\"p\",[t._v(\"要执行自定义性能跟踪和测量Dart任意代码段的wall/CPU时间（类似于在Android上使用\"),a(\"a\",{attrs:{href:\"https://developer.android.com/studio/profile/systrace.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"systrace\"),a(\"OutboundLink\")],1),t._v(\"）。 使用\"),a(\"code\",[t._v(\"dart:developer\")]),t._v(\"的\"),a(\"a\",{attrs:{href:\"https://api.dartlang.org/stable/dart-developer/Timeline-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Timeline\"),a(\"OutboundLink\")],1),t._v(\"工具来包含你想测试的代码块，例如：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[t._v(\"Timeline\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"startSync\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'interesting function'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// iWonderHowLongThisTakes();\")]),t._v(\"\\nTimeline\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"finishSync\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"然后打开你应用程序的Observatory timeline页面，在“Recorded Streams”中选择‘Dart’复选框，并执行你想测量的功能。\")]),t._v(\" \"),a(\"p\",[t._v(\"刷新页面将在Chrome的\"),a(\"a\",{attrs:{href:\"https://www.chromium.org/developers/how-tos/trace-event-profiling-tool\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"跟踪工具\"),a(\"OutboundLink\")],1),t._v(\"中显示应用按时间顺序排列的timeline记录。\")]),t._v(\" \"),a(\"p\",[t._v(\"请确保运行\"),a(\"code\",[t._v(\"flutter run\")]),t._v(\"时带有\"),a(\"code\",[t._v(\"--profile\")]),t._v(\"标志，以确保运行时性能特征与您的最终产品差异最小。\")])])}),[],!1,null,null,null);e.default=n.exports}}]);"
  },
  {
    "path": "docs/assets/js/157.fb299d51.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[157],{770:function(t,r,e){\"use strict\";e.r(r);var a=e(45),l=Object(a.a)({},(function(){var t=this,r=t.$createElement,e=t._self._c||r;return e(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[e(\"h2\",{attrs:{id:\"简介\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#简介\"}},[t._v(\"#\")]),t._v(\" 简介\")]),t._v(\" \"),e(\"p\",[t._v(\"本章将通过一些简单的示例来一步步介绍Flutter的开发流程.\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"本章目录\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本章目录\"}},[t._v(\"#\")]),t._v(\" 本章目录\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter2/first_flutter_app.html\"}},[t._v(\"2.1：计数器示例\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter2/flutter_router.html\"}},[t._v(\"2.2：路由管理\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter2/flutter_package_mgr.html\"}},[t._v(\"2.3：包管理\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter2/flutter_assets_mgr.html\"}},[t._v(\"2.4：资源管理\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter2/flutter_app_debug.html\"}},[t._v(\"2.5：调试Flutter APP\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter2/thread_model_and_error_report.html\"}},[t._v(\"2.6：Dart线程模型及异常捕获\")])],1)])])}),[],!1,null,null,null);r.default=l.exports}}]);"
  },
  {
    "path": "docs/assets/js/158.49064f28.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[158],{775:function(t,e,r){\"use strict\";r.r(e);var a=r(45),i=Object(a.a)({},(function(){var t=this,e=t.$createElement,r=t._self._c||e;return r(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[r(\"h1\",{attrs:{id:\"基础widget\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#基础widget\"}},[t._v(\"#\")]),t._v(\" 基础Widget\")]),t._v(\" \"),r(\"p\",[t._v(\"本节介绍一下Flutter中常用的一些基础widget，由于大多数widget的属性都比较多，我们在介绍widget时会着重介绍常用的属性，而不会像API文档一样所有属性都介绍，关于属性详细的信息请参考Flutter SDK文档。\")]),t._v(\" \"),r(\"h2\",{attrs:{id:\"本章目录\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本章目录\"}},[t._v(\"#\")]),t._v(\" 本章目录\")]),t._v(\" \"),r(\"ul\",[r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/chapter3/flutter_widget_intro.html\"}},[t._v(\"3.1：Widget简介\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/chapter3/state_manage.html\"}},[t._v(\"3.2：状态管理\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/chapter3/text.html\"}},[t._v(\"3.3：文本、字体样式\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/chapter3/buttons.html\"}},[t._v(\"3.4：按钮\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/chapter3/img_and_icon.html\"}},[t._v(\"3.5：图片和Icon\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/chapter3/radio_and_checkbox.html\"}},[t._v(\"3.6：单选框和复选框\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/chapter3/input_and_form.html\"}},[t._v(\"3.7：输入框和表单\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/chapter3/progress.html\"}},[t._v(\"3.8：进度指示器\")])],1)])])}),[],!1,null,null,null);e.default=i.exports}}]);"
  },
  {
    "path": "docs/assets/js/159.277037a1.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[159],{783:function(t,r,a){\"use strict\";a.r(r);var e=a(45),l=Object(e.a)({},(function(){var t=this,r=t.$createElement,a=t._self._c||r;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"本章目录\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本章目录\"}},[t._v(\"#\")]),t._v(\" 本章目录\")]),t._v(\" \"),a(\"ul\",[a(\"li\",[a(\"RouterLink\",{attrs:{to:\"/chapter4/intro.html\"}},[t._v(\"4.1：布局类组件简介\")])],1),t._v(\" \"),a(\"li\",[a(\"RouterLink\",{attrs:{to:\"/chapter4/row_and_column.html\"}},[t._v(\"4.2：线性布局（Row、Column）\")])],1),t._v(\" \"),a(\"li\",[a(\"RouterLink\",{attrs:{to:\"/chapter4/flex.html\"}},[t._v(\"4.3：弹性布局（Flex）\")])],1),t._v(\" \"),a(\"li\",[a(\"RouterLink\",{attrs:{to:\"/chapter4/wrap_and_flow.html\"}},[t._v(\"4.4：流式布局（Wrap、Flow）\")])],1),t._v(\" \"),a(\"li\",[a(\"RouterLink\",{attrs:{to:\"/chapter4/stack.html\"}},[t._v(\"4.5：层叠布局（Stack、Positioned）\")])],1),t._v(\" \"),a(\"li\",[a(\"RouterLink\",{attrs:{to:\"/chapter4/alignment.html\"}},[t._v(\"4.6：对齐与相对定位（Align）\")])],1)])])}),[],!1,null,null,null);r.default=l.exports}}]);"
  },
  {
    "path": "docs/assets/js/16.2d42d5bc.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[16],{473:function(t,n,a){t.exports=a.p+\"assets/img/5-10.62f0e5d6.png\"},474:function(t,n){t.exports=\"data:image/png;base64,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\"},475:function(t,n){t.exports=\"data:image/png;base64,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\"},476:function(t,n){t.exports=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAANQAAABICAYAAACOetsgAAAMJWlDQ1BJQ0MgUHJvZmlsZQAASImVVwdUk8kWnr8kISGhBUKREnoTpUiXGloEAamCjZAEEkoMgSBiBVlUYC2oWLCiqyKKrgWQxYZdWRTsdUFERVlFXWyovEkC6LqvnHfPmZnv3Ln3znennRkAVCM5YnEGqgZApihHEhXsz5yckMgkPQIo0AbKgATGcrjZYr/IyDAAZbj9u7y7CRBZe81OFuuf/f9V1Hn8bC4ASCTEybxsbibEhwHAXbhiSQ4AhF6oN52VI4aYCFkCTQkkCLGZDKcqsJsMJytwmNwmJooFcRIASlQOR5IKgIqMFzOXmwrjqJRBbC/iCUUQN0HszRVweBB/hnh0ZuZMiFWtILZK/i5O6t9iJo/E5HBSR7AiF7koBQizxRmc2f/ndPxvycyQDo9hCgtVIAmJkuUsm7f0maEyTIX4gig5PAJiDYivC3lyexl+IpCGxA7Zf+Bms+CcAQYAKJXHCQiFWB9iE2l6rN8Q9uZI5L7QHk3MF8TEK+KjIsnMqKH4aL4oIzxsKE6ZgM8exlX87MDoYZsUYRAbYriGaIMwhx0zFPNCrjAuHGIViO9np0eHDvk+zxewwkfGkkbJOMM1x0Bm9nAumFmKJChKYY+5CITs8CF9WI4gJkThi03ncuQcdCBO42dPDhvmw+MHBCr4YIV8UewQT6xcnOMfNWS/Q5wROWSPNfEzgmV6E4hbs3Ojh337cuBmU+SCgzTOhEjFuLimOCcyRsENZ4IwwAIBgAmksCSDmSANCFt763vBcE8Q4AAJSAV8YDekGfaIl/eIYB0N8sGfEPFB9oifv7yXD3Kh/suIVlHbgRR5b67cIx08gTgT18O9cU88DNa+sDjibrj7sB9TdXhUYiAxgBhCDCJazxAWSn6IywRcmEEGLBIQCls+zErGQTTM/VscwhNCG+ER4Qahg3AHxIHH0E74jwy/RROO6CaCDhg1aCi75O+zwy0ga2fcH/eC/CF3nIHrATt8HMzED/eBuTlD7bdZ+3fcpcOsyfZklKxN9iVb/WinYqPiPOIjy+17ngpeySOZsEZ6fhyN9V1uPNiG/miJLcEOYeexU9hFrAmrB0zsBNaAtWDHZHhkbzyW743h0aLkfNJhHOGwjX2NfY/95x/G5gyNL5GvP8jh5+XIDg5rpni2RJgqyGH6wduaz2SLuGNGMx3tHeAtKrv7FVfLW4b8TkcYl77pCuYAMP7Q4OBg0zddGLxZjhgAQHn1TWf1Hh5newAuFHClklyFDpdVBEABqvCk6AJDeHdZwYwcgQvwBL4gEEwAESAGJIDpcJ4FIBOyngXmggJQDErBCrAGbABbwHawG+wDB0E9aAKnwDlwGVwFN8A9uFe6wQvQB96BAQRBSAgNoSO6iBFijtgijogb4o0EImFIFJKAJCGpiAiRInORRUgpUo5sQLYh1civyFHkFHIRaUPuIJ1ID/IG+YRiKBXVRA1QC3Qs6ob6oaFoDDoNTUWz0Hy0CF2GrkOr0L1oHXoKvYzeQDvQF2g/BjBljIEZY3aYG8bCIrBELAWTYPOxEqwCq8JqsUa40tewDqwX+4gTcTrOxO3gfg3BY3EunoXPx8vwDfhuvA4/g1/DO/E+/CuBRtAn2BI8CGzCZEIqYRahmFBB2Ek4QjgLz1Q34R2RSGQQLYmu8KwmENOIc4hlxE3E/cSTxDZiF7GfRCLpkmxJXqQIEoeUQyomrSftJZ0gtZO6SR+UlJWMlByVgpQSlURKhUoVSnuUjiu1Kz1VGiCrkc3JHuQIMo88m7ycvIPcSL5C7iYPUNQplhQvSgwljVJAWUeppZyl3Ke8VVZWNlF2V56kLFReqLxO+YDyBeVO5Y9UDaoNlUWdSpVSl1F3UU9S71Df0mg0C5ovLZGWQ1tGq6adpj2kfVChq4xRYavwVBaoVKrUqbSrvFQlq5qr+qlOV81XrVA9pHpFtVeNrGahxlLjqM1Xq1Q7qnZLrV+dru6gHqGeqV6mvkf9ovozDZKGhUagBk+jSGO7xmmNLjpGN6Wz6Fz6IvoO+ll6tyZR01KTrZmmWaq5T7NVs09LQ2ucVpxWnlal1jGtDgbGsGCwGRmM5YyDjJuMT9oG2n7afO2l2rXa7drvdUbp+OrwdUp09uvc0Pmky9QN1E3XXalbr/tAD9ez0ZukN0tvs95Zvd5RmqM8R3FHlYw6OOquPqpvox+lP0d/u36Lfr+BoUGwgdhgvcFpg15DhqGvYZrhasPjhj1GdCNvI6HRaqMTRs+ZWkw/ZgZzHfMMs89Y3zjEWGq8zbjVeMDE0iTWpNBkv8kDU4qpm2mK6WrTZtM+MyOziWZzzWrM7pqTzd3MBeZrzc+bv7ewtIi3WGxRb/HMUseSbZlvWWN534pm5WOVZVVldd2aaO1mnW69yfqqDWrjbCOwqbS5YovautgKbTfZto0mjHYfLRpdNfqWHdXOzy7XrsaucwxjTNiYwjH1Y16ONRubOHbl2PNjv9o722fY77C/56DhMMGh0KHR4Y2jjSPXsdLxuhPNKchpgVOD0+txtuP44zaPu+1Md57ovNi52fmLi6uLxKXWpcfVzDXJdaPrLTdNt0i3MrcL7gR3f/cF7k3uHz1cPHI8Dnq88rTzTPfc4/lsvOV4/vgd47u8TLw4Xtu8OryZ3kneW707fIx9OD5VPo98TX15vjt9n/pZ+6X57fV76W/vL/E/4v+e5cGaxzoZgAUEB5QEtAZqBMYGbgh8GGQSlBpUE9QX7Bw8J/hkCCEkNGRlyC22AZvLrmb3TXCdMG/CmVBqaHTohtBHYTZhkrDGiejECRNXTbwfbh4uCq+PABHsiFURDyItI7Mif5tEnBQ5qXLSkyiHqLlR56Pp0TOi90S/i/GPWR5zL9YqVhrbHKcaNzWuOu59fEB8eXzH5LGT502+nKCXIExoSCQlxiXuTOyfEjhlzZTuqc5Ti6fenGY5LW/axel60zOmH5uhOoMz41ASISk+aU/SZ04Ep4rTn8xO3pjcx2Vx13Jf8Hx5q3k9fC9+Of9pildKecqzVK/UVak9Ah9BhaBXyBJuEL5OC0nbkvY+PSJ9V/pgRnzG/kylzKTMoyINUbrozEzDmXkz28S24mJxR5ZH1pqsPkmoZGc2kj0tuyFHEz6yW6RW0p+knbneuZW5H2bFzTqUp54nymuZbTN76eyn+UH5v8zB53DnNM81nlswt3Oe37xt85H5yfObF5guKFrQvTB44e4CSkF6we+F9oXlhX8til/UWGRQtLCo66fgn2qKVYolxbcWey7esgRfIlzSutRp6fqlX0t4JZdK7UsrSj+Xccsu/ezw87qfB5elLGtd7rJ88wriCtGKmyt9Vu4uVy/PL+9aNXFV3Wrm6pLVf62ZseZixbiKLWspa6VrO9aFrWtYb7Z+xfrPGwQbblT6V+7fqL9x6cb3m3ib2jf7bq7dYrCldMunrcKtt7cFb6ursqiq2E7cnrv9yY64Hed/cfuleqfeztKdX3aJdnXsjtp9ptq1unqP/p7lNWiNtKZn79S9V/cF7Guotavdtp+xv/QAOCA98PzXpF9vHgw92HzI7VDtYfPDG4/Qj5TUIXWz6/rqBfUdDQkNbUcnHG1u9Gw88tuY33Y1GTdVHtM6tvw45XjR8cET+Sf6T4pP9p5KPdXVPKP53unJp6+fmXSm9Wzo2Qvngs6dPu93/sQFrwtNFz0uHr3kdqn+ssvluhbnliO/O/9+pNWlte6K65WGq+5XG9vGtx1v92k/dS3g2rnr7OuXb4TfaLsZe/P2ram3Om7zbj+7k3Hn9d3cuwP3Ft4n3C95oPag4qH+w6o/rP/Y3+HScawzoLPlUfSje13crhePsx9/7i56QntS8dToafUzx2dNPUE9V59Ped79QvxioLf4T/U/N760enn4le+rlr7Jfd2vJa8H35S91X27669xfzX3R/Y/fJf5buB9yQfdD7s/un08/yn+09OBWZ9Jn9d9sf7S+DX06/3BzMFBMUfCkT8FMFjQlBQA3uwCgJYAAP0qfD9MUfzN5IIo/pNyBP4TVvzf5OICQC1sZM9w1kkADsBi6QtjwyJ7jsf4AtTJaaQMSXaKk6MiFhX+cAgfBgffwncMqRGAL5LBwYFNg4NfdkCydwA4maX4E8pE9gfdKo/RzshbCH6QfwEOk3CtyyHjHgAAAAlwSFlzAAAWJQAAFiUBSVIk8AAADX9JREFUeAHtXAtQVdcV3YDy//tBwD+CmqhUidWgTeOvUdPEmqT172Tib9ROm1TbTCZqWjWZ2qkdRadK7Fid1Gm1M43ROtZm2hi1mUSixkQJioKiIKj8QeDx614H7uO+B+8+Ll6VN+49o+/ee/b5vHXPOnufffbDy2azNZKIICAIWIKAtyWtSCOCgCCgEBBCyUQQBCxEQAhlIZjSlCAghJI5IAhYiIAQykIwpSlBQAglc0AQsBABIZSFYEpTgoAQSuaAIGAhAkIoC8GUpgQBIZTMAUHAQgSEUBaCKU0JAkIomQOCgIUICKEsBFOaEgSEUDIHBAELERBCWQimNCUICKFkDggCFiIghLIQTGlKEBBCyRwQBCxEQAhlIZjSlCDQRSBoPwIHDhyg3Nxc8vf3p+XLlztU3LlzJ1VVVdHEiRMpMTHRoUxumhCoq6ujbdu2qZvRo0fT+PHjTUGjr7906VIKCgoyVf9hKHsEoZYsWUKNjY0KwK1bt7rFJScnh9avX6/0Nm/eTGFhYa3q1L76Y2osuNXqudGDjy5coS/LKinKrystOrTPQfWDtHS6W1tLPT54n4ZGdXMok5smBGoiImnnmUvqJiAgwDShGhoaCAsXZMGCBUIohUQH/jt58qQiVHh4eLtqV1RUEOpA+K86tauOKAkCViAgeygrUJQ2BIFmBIRQMhUEAQsREEJZCKY0JQh4RFDiQb6mOg52XL5XTTW84e3p25Vi/XwfZHdUz3+nN7uqmopr6yi0iw8NDPSnrl5eD7TPjjSOPyd8o7qGbttqyd/bm+J4nAH8aUbyamyUV1NL/f19qTtja0by8/MJwaXQ0FBKSEggb5N9m+nLSt3HllDVTKC3Ll2jk8XlhGtNevv70ZLYnjQzKpKsnOZV3Md7Wbn036JSKqur17qjIB9vSg4PpbUDYymiq/vXcbHiHs3+OlPV//A7g2kQT3RnQTn0FvH3eL1ftHMx/SXvDm26lkfRvHgcSxrq8D2xwPzxRgEdul1EBUwmTUD6pNBgWhsXS30ZI2f5551ieiszh3owcf6RmEDLv82mCzwGyMyekbR+UB/nKm3eX7lyhVatWkUZGRn2coTHp0yZQhs2bLA/66wX7t9gZx35fYyrsLCQXjqRRvnFpaqVoUGBPJl96AyHxG/yqvzO1Ru8strop3173UcvLVUL2Rot/OYK5XDbkCiedPFB/pR1r0b183FhCZ0rr6TdT8bRgIDWk7WlJaIhQQHUnYl3l9s8VVLeilC5PG6QCXK6tEJf1X79efNztKVfNO7VN9DS9Kt0vrypfpCPDyWGBNIdJlYmW/HPS8vplfOXKWXIABobFmxvT39xr76e5vN3vd78XfVl7q5BphkzZhDOmyCI6vbr148uXrxIBw8epPT0dHdNPPLyx5JQ8+fPp/yyMl6hu9KOoQOVO4M3AUu1PSef9vIKnnqzgJ7rHk7xbVgAs2/t9YxrikxwnbYM6a8mow+v+HCrvmIivcHld3nS/jwjm/bz6m7kWqFeckSosiBnyiro1ZgeDsM5xRZXk4uVVYoMsBqawFJ+xsSAvNgjQnusPn97LVeRqQv38U5cb3qey2GZMM4sdlN/kXFdfa5my35k1FAKY5fVWSqZlLYGG63qH0PP8jgD2AK3x1HE8cbChQsVmXx9fWnv3r00cuRI8uL+QbDjx4/TypUrnbvrdPceRajKykrCIa87wTmUKzl8+DBlZWWp4t1PDqLe7N9rggm/mifCJ0VligCp7Pr8fnA/rbhDnx8XlirSoDLaGhceYm8H1mFkSBBt5RV/wTeZvLeqUUSZ1au7XaetC0xUuGSwQLXsoun3YJ+wSwmBpUN7/+b+50W3tIdntQ2Nal80PqJlLFfYAn1YUKTqLu/Ti37EbpomGGdcgD+lPjGQnjuTTqXssr6XdZM2JbSNzcb4vjSdFyMzcvbsWYLngL3Svn37aMSIEfbqXbp0ocmTJ1NqaiotW7bM/rwzXngUoWo5E+HEiRP3heOWLVtU/fG8b9GTSd/oAl713+UJc4xdsY0NfdTk05ebud6ff1epY6/zPe6zLYFbBaLBhfuI9yLuCPVddre8eeWGi5ZeUaXcMrSLQAfcObiUb/SLoZ+xxfu0uMyBUGfZrYU8ERzg8L2O3S1Rz7uxO/larKPVUwX8Xy+26Et7R9FOtt6nm9vRyrRPWC2zZELd7du3qyaCg4MdyKS1i8/k5GT9bae89ihCBQYG0rp169wCmZeXRykpKW3qlZQ0TZwfdAtzCA7olQcFtuxjyusayN+3PU6LvoWWa20vMSkyjEnQ8tz5ajxbHRDqAu9fytkChLThTml1MGlHhwbRF0yeE0wYEBICMtWzxXqKCQfSwXKd44kP4gWy6wX5otndSw5rsU54rrmBiDrC5XMlExg3EKqQXVREABEZ1YuXw65MX+L6Gi5dWlqaUhgzZoxrRQ8o8ShCwbeeOXOmW1ixeXVFqOrqalV/HQce1vEG2p3YGlsigO50ncux97hja9pgx+pcS2c93GvBCNQp43EZEQr6yWzRQCgEMzRBBBHyfPcIFT2czJP/KFue/zFRp/A19oif8TVkmtP+6Vu2dJBQDkQYibZvwjgRBXQmlFFdV2X1uvcwatQoV2oe8bzjS69HfL3Wg9S/vNalrZ/wdqPDwsZCbejRAPZnRuKtW9mxx3Enz7BFg5xnQiEQACt0nPd+IOIYtk6Qyd2a9jEgFQT7Jxu33Z/3V311BEdvWo8IIhiJ3nZVcZ9WCBKfNYEX4sniURbKCqBh5WpqauivIxJoWEBLQMKKtp3bgIuHFR17mxL+ZyR3eH+oieaeafdtfcI1g9XLrbYpUoGDsEDjIsLIt9m3hNsHApzlaCDKtf3T8GDHSQsd7CevMeHgbhqJnkTtOTczaksr89FZRRzoerIYL0ee/M1cjB0/G4DgTOVhiJZ5cb75bMhVn2nsvkHCOSiAwIA7wYvTIoYIPPyn2d2bEtlklVA/nMn8LO/dcA6G8DxcRMgPndw9PEviPRkkn904I8ngUDzEjy0uLJ0VgihedHTTAfTRo0etaPKRtfHYESo+Pl6B/T5vrPWrrfMbcGdRnPVd3WvuF6Jo+swDvT7cNQQXIAiW4KypPTKBrREEZAShYJmeiXQMNmDvBPl7QaHaPwWzNXiKMx6cZQITD5J5r4ojh00Hu846CNGn3rytHoNM+nC9s66Ze5w1LV68WFW5deuWy5/clJY27RHNtP2wdR87Qm3atImwIoJMSJXBJHEWnO9MPZtBf+YD3vvdJbzG6T9w+5DS83bmDYc0J/QLV+zXHCCBFQGN5ke3HbJ2HiPuR7JVAYmQxQC3EkQJ0blP0Hm6eT+FIwDkKw7niKDmEqJcE2Q+9OGUIoxn9eWcVhFQoPQnJhPyEDHO1RyWt1KQIQGBO75o0SL1+zd9+/g19Jw5c/SPOuW1e9+iUw6744OKjY1VJ+4pa99Wq/orX11SLlB8YABVcLQJG3us9iBALU/A9tkK1+NBAuy6uD70y8vXVcj6J+cz6cWeEeqgFGlOh/jcSXOj3hwQa4/2uW6xpQR5gE9z+BsuH+T7zYGKFg1SSaljWUdzcdvSgT5cuD/wwfMczgNEUuwL5zLU4W4iHzwXMVn/dbfY7jK+FNWNxoa3tnL6fs1eh4SE0Jo1a2jjxo10+vRpmjZtGk2dOpViYmJUytGxY8eoqKjp4Nls2w9T/7EjFMBdsWIFhR48QL9LO8epNDWUwulGzoKMifmcYXC/hEK7cOO8EvrSb67eVCv81uu3HLoDMX7VP1Yl5DoUtONmHGc7aIRCmLwtmcpZCyAUvgvG4kqQ27d32CB6M/M65zTaaHduk3un6cPFW9S7J63gTIoHIfPmzaOCggLatWsXZWdn044dO+zd4MB3//79NGvWLPuzznjhxTlUrX2eTjbSPXv2KBfAz8+P5s6d63Z0SGE5dOiQ0ps9ezZpgQh9RfxNieLcG/QpW6QLfAZTxoeLgewuDeZJNSkyVGVN6/VxDbepgH+OAAK8zKu0XrBHgRuJlTuBrV1bgixzpAZ9zXsURNOwnxnGEbeJ3UI5gNCxtQ2u4hG2crCE+nQhff9wBw+zDpJdX+YsencC1xBZ+F9ydBDWCbmFyPSYxGSMcfHzFixMp9hSwtLN6uWIjdZfffee9LeJL6jbpKQklxkROEc8cuQIIeIHIg0fPpymT5+u3iPmAsTVe1WFj/A/jyDUI8RHuhYETCHw2AUlTKEjyoKASQSEUCYBE3VBwAgBIZQROlImCJhEQAhlEjBRFwSMEBBCGaEjZYKASQSEUCYBE3VBwAgBIZQROlImCJhEQAhlEjBRFwSMEBBCGaEjZYKASQSEUCYBE3VBwAgBIZQROlImCJhEQAhlEjBRFwSMEBBCGaEjZYKASQSEUCYBE3VBwAgBIZQROlImCJhEQAhlEjBRFwSMEBBCGaEjZYKASQSEUCYBE3VBwAgBIZQROlImCJhEQAhlEjBRFwSMEBBCGaEjZYKASQSEUCYBE3VBwAgBIZQROlImCJhEQAhlEjBRFwSMEBBCGaEjZYKASQSEUCYBE3VBwAgBIZQROlImCJhEQAhlEjBRFwSMEBBCGaEjZYKASQT+D17Ha5GaZh2cAAAAAElFTkSuQmCC\"},477:function(t,n){t.exports=\"data:image/png;base64,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\"},478:function(t,n){t.exports=\"data:image/png;base64,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\"},796:function(t,n,a){\"use strict\";a.r(n);var s=a(45),r=Object(s.a)({},(function(){var t=this,n=t.$createElement,s=t._self._c||n;return s(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[s(\"h1\",{attrs:{id:\"_5-4-变换-transform\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_5-4-变换-transform\"}},[t._v(\"#\")]),t._v(\" 5.4 变换（Transform）\")]),t._v(\" \"),s(\"p\",[s(\"code\",[t._v(\"Transform\")]),t._v(\"可以在其子组件绘制时对其应用一些矩阵变换来实现一些特效。\"),s(\"code\",[t._v(\"Matrix4\")]),t._v(\"是一个4D矩阵，通过它我们可以实现各种矩阵操作，下面是一个例子：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"black\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Transform\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    alignment\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"topRight\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//相对于坐标系原点的对齐方式\")]),t._v(\"\\n    transform\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Matrix4\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"skewY\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.3\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//沿Y轴倾斜0.3弧度\")]),t._v(\"\\n    child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Container\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      padding\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"8.0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"deepOrange\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Apartment for rent!'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"运行效果如图5-10所示：\")]),t._v(\" \"),s(\"p\",[s(\"img\",{attrs:{src:a(473),alt:\"图5-10\"}})]),t._v(\" \"),s(\"blockquote\",[s(\"p\",[t._v(\"关于矩阵变换的相关内容属于线性代数范畴，本书不做讨论，读者有兴趣可以自行了解。本书中，我们把焦点放在Flutter中一些常见的变换效果上。另外，由于矩阵变化时发生在绘制时，而无需重新布局和构建等过程，所以性能很好。\")])]),t._v(\" \"),s(\"h3\",{attrs:{id:\"平移\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#平移\"}},[t._v(\"#\")]),t._v(\" 平移\")]),t._v(\" \"),s(\"p\",[s(\"code\",[t._v(\"Transform.translate\")]),t._v(\"接收一个\"),s(\"code\",[t._v(\"offset\")]),t._v(\"参数，可以在绘制时沿\"),s(\"code\",[t._v(\"x\")]),t._v(\"、\"),s(\"code\",[t._v(\"y\")]),t._v(\"轴对子组件平移指定的距离。\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecoratedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//默认原点为左上角，左移20像素，向上平移5像素  \")]),t._v(\"\\n  child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Transform\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"translate\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    offset\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"5.0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hello world\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"效果如图5-11所示：\")]),t._v(\" \"),s(\"p\",[s(\"img\",{attrs:{src:a(474),alt:\"图5-11\"}})]),t._v(\" \"),s(\"h3\",{attrs:{id:\"旋转\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#旋转\"}},[t._v(\"#\")]),t._v(\" 旋转\")]),t._v(\" \"),s(\"p\",[s(\"code\",[t._v(\"Transform.rotate\")]),t._v(\"可以对子组件进行旋转变换，如：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecoratedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Transform\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"rotate\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//旋转90度\")]),t._v(\"\\n    angle\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"math\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"pi\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hello world\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"；\\n\")])])]),s(\"blockquote\",[s(\"p\",[t._v(\"注意：要使用\"),s(\"code\",[t._v(\"math.pi\")]),t._v(\"需先进行如下导包。\")])]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'dart:math'\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"as\")]),t._v(\" math\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"  \\n\")])])]),s(\"p\",[t._v(\"效果如图5-12所示：\")]),t._v(\" \"),s(\"p\",[s(\"img\",{attrs:{src:a(475),alt:\"图5-12\"}})]),t._v(\" \"),s(\"h3\",{attrs:{id:\"缩放\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#缩放\"}},[t._v(\"#\")]),t._v(\" 缩放\")]),t._v(\" \"),s(\"p\",[s(\"code\",[t._v(\"Transform.scale\")]),t._v(\"可以对子组件进行缩小或放大，如：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecoratedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Transform\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"scale\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      scale\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.5\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//放大到1.5倍\")]),t._v(\"\\n      child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hello world\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"效果如图5-13所示：\")]),t._v(\" \"),s(\"p\",[s(\"img\",{attrs:{src:a(476),alt:\"图5-13\"}})]),t._v(\" \"),s(\"h3\",{attrs:{id:\"注意\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#注意\"}},[t._v(\"#\")]),t._v(\" 注意\")]),t._v(\" \"),s(\"ul\",[s(\"li\",[s(\"p\",[s(\"code\",[t._v(\"Transform\")]),t._v(\"的变换是应用在绘制阶段，而并不是应用在布局(layout)阶段，所以无论对子组件应用何种变化，其占用空间的大小和在屏幕上的位置都是固定不变的，因为这些是在布局阶段就确定的。下面我们具体说明：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  mainAxisAlignment\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  children\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecoratedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Transform\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"scale\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"scale\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.5\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hello world\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"你好\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" style\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"green\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" fontSize\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"18.0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"运行效果如图5-14所示：\")]),t._v(\" \"),s(\"p\",[s(\"img\",{attrs:{src:a(477),alt:\"图5-14\"}})]),t._v(\" \"),s(\"p\",[t._v(\"由于第一个\"),s(\"code\",[t._v(\"Text\")]),t._v(\"应用变换(放大)后，其在绘制时会放大，但其占用的空间依然为红色部分，所以第二个\"),s(\"code\",[t._v(\"Text\")]),t._v(\"会紧挨着红色部分，最终就会出现文字重合。\")])]),t._v(\" \"),s(\"li\",[s(\"p\",[t._v(\"由于矩阵变化只会作用在绘制阶段，所以在某些场景下，在UI需要变化时，可以直接通过矩阵变化来达到视觉上的UI改变，而不需要去重新触发build流程，这样会节省layout的开销，所以性能会比较好。如之前介绍的\"),s(\"code\",[t._v(\"Flow\")]),t._v(\"组件，它内部就是用矩阵变换来更新UI，除此之外，Flutter的动画组件中也大量使用了\"),s(\"code\",[t._v(\"Transform\")]),t._v(\"以提高性能。\")])])]),t._v(\" \"),s(\"blockquote\",[s(\"p\",[t._v(\"思考题：使用\"),s(\"code\",[t._v(\"Transform\")]),t._v(\"对其子组件先进行平移然后再旋转和先旋转再平移，两者最终的效果一样吗？为什么？\")])]),t._v(\" \"),s(\"h3\",{attrs:{id:\"rotatedbox\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#rotatedbox\"}},[t._v(\"#\")]),t._v(\" RotatedBox\")]),t._v(\" \"),s(\"p\",[s(\"code\",[t._v(\"RotatedBox\")]),t._v(\"和\"),s(\"code\",[t._v(\"Transform.rotate\")]),t._v(\"功能相似，它们都可以对子组件进行旋转变换，但是有一点不同：\"),s(\"code\",[t._v(\"RotatedBox\")]),t._v(\"的变换是在layout阶段，会影响在子组件的位置和大小。我们将上面介绍\"),s(\"code\",[t._v(\"Transform.rotate\")]),t._v(\"时的示例改一下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  mainAxisAlignment\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  children\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecoratedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//将Transform.rotate换成RotatedBox  \")]),t._v(\"\\n      child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RotatedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        quarterTurns\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//旋转90度(1/4圈)\")]),t._v(\"\\n        child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hello world\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"你好\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" style\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"green\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" fontSize\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"18.0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"效果如图5-15所示：\")]),t._v(\" \"),s(\"p\",[s(\"img\",{attrs:{src:a(478),alt:\"图5-15\"}})]),t._v(\" \"),s(\"p\",[t._v(\"由于\"),s(\"code\",[t._v(\"RotatedBox\")]),t._v(\"是作用于layout阶段，所以子组件会旋转90度（而不只是绘制的内容），\"),s(\"code\",[t._v(\"decoration\")]),t._v(\"会作用到子组件所占用的实际空间上，所以最终就是上图的效果，读者可以和前面\"),s(\"code\",[t._v(\"Transform.rotate\")]),t._v(\"示例对比理解。\")])])}),[],!1,null,null,null);n.default=r.exports}}]);"
  },
  {
    "path": "docs/assets/js/160.0664832e.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[160],{784:function(e,t,d){\"use strict\";d.r(t);var _=d(45),v=Object(_.a)({},(function(){var e=this,t=e.$createElement,d=e._self._c||t;return d(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":e.$parent.slotKey}},[d(\"h1\",{attrs:{id:\"_4-1-布局类组件简介\"}},[d(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_4-1-布局类组件简介\"}},[e._v(\"#\")]),e._v(\" 4.1 布局类组件简介\")]),e._v(\" \"),d(\"p\",[e._v(\"布局类组件都会包含一个或多个子组件，不同的布局类组件对子组件排版(layout)方式不同。我们在前面说过\"),d(\"code\",[e._v(\"Element\")]),e._v(\"树才是最终的绘制树，\"),d(\"code\",[e._v(\"Element\")]),e._v(\"树是通过Widget树来创建的（通过\"),d(\"code\",[e._v(\"Widget.createElement()\")]),e._v(\"），Widget其实就是Element的配置数据。在Flutter中，根据Widget是否需要包含子节点将Widget分为了三类，分别对应三种Element，如下表：\")]),e._v(\" \"),d(\"table\",[d(\"thead\",[d(\"tr\",[d(\"th\",[e._v(\"Widget\")]),e._v(\" \"),d(\"th\",[e._v(\"对应的Element\")]),e._v(\" \"),d(\"th\",[e._v(\"用途\")])])]),e._v(\" \"),d(\"tbody\",[d(\"tr\",[d(\"td\",[e._v(\"LeafRenderObjectWidget\")]),e._v(\" \"),d(\"td\",[e._v(\"LeafRenderObjectElement\")]),e._v(\" \"),d(\"td\",[e._v(\"Widget树的叶子节点，用于没有子节点的widget，通常基础组件都属于这一类，如Image。\")])]),e._v(\" \"),d(\"tr\",[d(\"td\",[e._v(\"SingleChildRenderObjectWidget\")]),e._v(\" \"),d(\"td\",[e._v(\"SingleChildRenderObjectElement\")]),e._v(\" \"),d(\"td\",[e._v(\"包含一个子Widget，如：ConstrainedBox、DecoratedBox等\")])]),e._v(\" \"),d(\"tr\",[d(\"td\",[e._v(\"MultiChildRenderObjectWidget\")]),e._v(\" \"),d(\"td\",[e._v(\"MultiChildRenderObjectElement\")]),e._v(\" \"),d(\"td\",[e._v(\"包含多个子Widget，一般都有一个children参数，接受一个Widget数组。如Row、Column、Stack等\")])])])]),e._v(\" \"),d(\"blockquote\",[d(\"p\",[e._v(\"注意，Flutter中的很多Widget是直接继承自StatelessWidget或StatefulWidget，然后在\"),d(\"code\",[e._v(\"build()\")]),e._v(\"方法中构建真正的RenderObjectWidget，如Text，它其实是继承自StatelessWidget，然后在\"),d(\"code\",[e._v(\"build()\")]),e._v(\"方法中通过RichText来构建其子树，而RichText才是继承自MultiChildRenderObjectWidget。所以为了方便叙述，我们也可以直接说Text属于MultiChildRenderObjectWidget（其它widget也可以这么描述），这才是本质。读到这里我们也会发现，其实\"),d(\"strong\",[e._v(\"StatelessWidget和StatefulWidget就是两个用于组合Widget的基类，它们本身并不关联最终的渲染对象（RenderObjectWidget）\")]),e._v(\"。\")])]),e._v(\" \"),d(\"p\",[e._v(\"布局类组件就是指直接或间接继承(包含)\"),d(\"code\",[e._v(\"MultiChildRenderObjectWidget\")]),e._v(\"的Widget，它们一般都会有一个\"),d(\"code\",[e._v(\"children\")]),e._v(\"属性用于接收子Widget。我们看一下继承关系 Widget > RenderObjectWidget > (Leaf/SingleChild/MultiChild)RenderObjectWidget 。\")]),e._v(\" \"),d(\"p\",[d(\"code\",[e._v(\"RenderObjectWidget\")]),e._v(\"类中定义了创建、更新\"),d(\"code\",[e._v(\"RenderObject\")]),e._v(\"的方法，子类必须实现他们，关于\"),d(\"code\",[e._v(\"RenderObject\")]),e._v(\"我们现在只需要知道它是最终布局、渲染UI界面的对象即可，也就是说，对于布局类组件来说，其布局算法都是通过对应的\"),d(\"code\",[e._v(\"RenderObject\")]),e._v(\"对象来实现的，所以读者如果对接下来介绍的某个布局类组件的原理感兴趣，可以查看其对应的\"),d(\"code\",[e._v(\"RenderObject\")]),e._v(\"的实现，比如\"),d(\"code\",[e._v(\"Stack\")]),e._v(\"（层叠布局）对应的\"),d(\"code\",[e._v(\"RenderObject\")]),e._v(\"对象就是\"),d(\"code\",[e._v(\"RenderStack\")]),e._v(\"，而层叠布局的实现就在\"),d(\"code\",[e._v(\"RenderStack\")]),e._v(\"中。\")]),e._v(\" \"),d(\"p\",[e._v(\"在本章中，为了让读者对布局类Widget有个快速的认识，所以我们并不会深入到\"),d(\"code\",[e._v(\"RenderObject\")]),e._v(\"的细节中去。在学习本章时，读者的重点是掌握不同布局组件的布局特点，具体原理和细节等我们对Flutter整体入门后，感兴趣的话再去研究。\")])])}),[],!1,null,null,null);t.default=v.exports}}]);"
  },
  {
    "path": "docs/assets/js/161.38ea1a1a.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[161],{793:function(t,e,i){\"use strict\";i.r(e);var r=i(45),a=Object(r.a)({},(function(){var t=this,e=t.$createElement,i=t._self._c||e;return i(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[i(\"h1\",{attrs:{id:\"容器类widget\"}},[i(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#容器类widget\"}},[t._v(\"#\")]),t._v(\" 容器类Widget\")]),t._v(\" \"),i(\"p\",[t._v(\"容器类Widget和布局类Widget都作用于其子Widget，不同的是：\")]),t._v(\" \"),i(\"ul\",[i(\"li\",[t._v(\"布局类Widget一般都需要接收一个widget数组（children），他们直接或间接继承自（或包含）MultiChildRenderObjectWidget ；而容器类Widget一般只需要接收一个子Widget（child），他们直接或间接继承自（或包含）SingleChildRenderObjectWidget。\")]),t._v(\" \"),i(\"li\",[t._v(\"布局类Widget是按照一定的排列方式来对其子Widget进行排列；而容器类Widget一般只是包装其子Widget，对其添加一些修饰（补白或背景色等）、变换(旋转或剪裁等)、或限制(大小等)。\")])]),t._v(\" \"),i(\"p\",[t._v(\"注意，Flutter官方并没有对Widget进行官方分类，我们对其分类主要是为了方便讨论和对Widget功能区分的记忆。\")]),t._v(\" \"),i(\"h2\",{attrs:{id:\"本章目录\"}},[i(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本章目录\"}},[t._v(\"#\")]),t._v(\" 本章目录\")]),t._v(\" \"),i(\"ul\",[i(\"li\",[i(\"RouterLink\",{attrs:{to:\"/chapter5/padding.html\"}},[t._v(\"5.1：填充（Padding）\")])],1),t._v(\" \"),i(\"li\",[i(\"RouterLink\",{attrs:{to:\"/chapter5/constrainedbox_and_sizebox.html\"}},[t._v(\"5.2：尺寸限制类容器（ConstrainedBox等）\")])],1),t._v(\" \"),i(\"li\",[i(\"RouterLink\",{attrs:{to:\"/chapter5/decoratedbox.html\"}},[t._v(\"5.3：装饰容器（DecoratedBox）\")])],1),t._v(\" \"),i(\"li\",[i(\"RouterLink\",{attrs:{to:\"/chapter5/transform.html\"}},[t._v(\"5.4：变换（Transform）\")])],1),t._v(\" \"),i(\"li\",[i(\"RouterLink\",{attrs:{to:\"/chapter5/container.html\"}},[t._v(\"5.5：Container容器\")])],1),t._v(\" \"),i(\"li\",[i(\"RouterLink\",{attrs:{to:\"/chapter5/material_scaffold.html\"}},[t._v(\"5.6：Scaffold、TabBar、底部导航\")])],1),t._v(\" \"),i(\"li\",[i(\"RouterLink\",{attrs:{to:\"/chapter5/clip.html\"}},[t._v(\"5.7：剪裁（Clip）\")])],1)])])}),[],!1,null,null,null);e.default=a.exports}}]);"
  },
  {
    "path": "docs/assets/js/162.f96e9c80.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[162],{799:function(t,r,l){\"use strict\";l.r(r);var e=l(45),i=Object(e.a)({},(function(){var t=this,r=t.$createElement,l=t._self._c||r;return l(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[l(\"h1\",{attrs:{id:\"本章目录\"}},[l(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本章目录\"}},[t._v(\"#\")]),t._v(\" 本章目录\")]),t._v(\" \"),l(\"ul\",[l(\"li\",[l(\"RouterLink\",{attrs:{to:\"/chapter6/intro.html\"}},[t._v(\"6.1：可滚动组件简介\")])],1),t._v(\" \"),l(\"li\",[l(\"RouterLink\",{attrs:{to:\"/chapter6/single_child_scrollview.html\"}},[t._v(\"6.2：SingleChildScrollView\")])],1),t._v(\" \"),l(\"li\",[l(\"RouterLink\",{attrs:{to:\"/chapter6/listview.html\"}},[t._v(\"6.3：ListView\")])],1),t._v(\" \"),l(\"li\",[l(\"RouterLink\",{attrs:{to:\"/chapter6/gridview.html\"}},[t._v(\"6.4：GridView\")])],1),t._v(\" \"),l(\"li\",[l(\"RouterLink\",{attrs:{to:\"/chapter6/custom_scrollview.html\"}},[t._v(\"6.5：CustomScrollView\")])],1),t._v(\" \"),l(\"li\",[l(\"RouterLink\",{attrs:{to:\"/chapter6/scroll_controller.html\"}},[t._v(\"6.6：滚动监听及控制（ScrollController）\")])],1)])])}),[],!1,null,null,null);r.default=i.exports}}]);"
  },
  {
    "path": "docs/assets/js/163.cfb15292.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[163],{800:function(t,r,a){\"use strict\";a.r(r);var s=a(45),e=Object(s.a)({},(function(){var t=this,r=t.$createElement,a=t._self._c||r;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_6-1-可滚动组件简介\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_6-1-可滚动组件简介\"}},[t._v(\"#\")]),t._v(\" 6.1 可滚动组件简介\")]),t._v(\" \"),a(\"p\",[t._v(\"当组件内容超过当前显示视口(ViewPort)时，如果没有特殊处理，Flutter则会提示Overflow错误。为此，Flutter提供了多种可滚动组件（Scrollable Widget）用于显示列表和长布局。在本章中，我们先介绍一下常用的可滚动组件（如\"),a(\"code\",[t._v(\"ListView\")]),t._v(\"、\"),a(\"code\",[t._v(\"GridView\")]),t._v(\"等），然后介绍一下\"),a(\"code\",[t._v(\"ScrollController\")]),t._v(\"。可滚动组件都直接或间接包含一个\"),a(\"code\",[t._v(\"Scrollable\")]),t._v(\"组件，因此它们包括一些共同的属性，为了避免重复介绍，我们在此统一介绍一下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scrollable\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"axisDirection \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" AxisDirection\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"down\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"physics\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"viewportBuilder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//后面介绍\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"ul\",[a(\"li\",[a(\"code\",[t._v(\"axisDirection\")]),t._v(\"滚动方向。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"physics\")]),t._v(\"：此属性接受一个\"),a(\"code\",[t._v(\"ScrollPhysics\")]),t._v(\"类型的对象，它决定可滚动组件如何响应用户操作，比如用户滑动完抬起手指后，继续执行动画；或者滑动到边界时，如何显示。默认情况下，Flutter会根据具体平台分别使用不同的\"),a(\"code\",[t._v(\"ScrollPhysics\")]),t._v(\"对象，应用不同的显示效果，如当滑动到边界时，继续拖动的话，在iOS上会出现弹性效果，而在Android上会出现微光效果。如果你想在所有平台下使用同一种效果，可以显式指定一个固定的\"),a(\"code\",[t._v(\"ScrollPhysics\")]),t._v(\"，Flutter SDK中包含了两个\"),a(\"code\",[t._v(\"ScrollPhysics\")]),t._v(\"的子类，他们可以直接使用：\\n\"),a(\"ul\",[a(\"li\",[a(\"code\",[t._v(\"ClampingScrollPhysics\")]),t._v(\"：Android下微光效果。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"BouncingScrollPhysics\")]),t._v(\"：iOS下弹性效果。\")])])]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"controller\")]),t._v(\"：此属性接受一个\"),a(\"code\",[t._v(\"ScrollController\")]),t._v(\"对象。\"),a(\"code\",[t._v(\"ScrollController\")]),t._v(\"的主要作用是控制滚动位置和监听滚动事件。默认情况下，Widget树中会有一个默认的\"),a(\"code\",[t._v(\"PrimaryScrollController\")]),t._v(\"，如果子树中的可滚动组件没有显式的指定\"),a(\"code\",[t._v(\"controller\")]),t._v(\"，并且\"),a(\"code\",[t._v(\"primary\")]),t._v(\"属性值为\"),a(\"code\",[t._v(\"true\")]),t._v(\"时（默认就为\"),a(\"code\",[t._v(\"true\")]),t._v(\"），可滚动组件会使用这个默认的\"),a(\"code\",[t._v(\"PrimaryScrollController\")]),t._v(\"。这种机制带来的好处是父组件可以控制子树中可滚动组件的滚动行为，例如，\"),a(\"code\",[t._v(\"Scaffold\")]),t._v(\"正是使用这种机制在iOS中实现了点击导航栏回到顶部的功能。我们将在本章后面“滚动控制”一节详细介绍\"),a(\"code\",[t._v(\"ScrollController\")]),t._v(\"。\")])]),t._v(\" \"),a(\"h3\",{attrs:{id:\"scrollbar\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#scrollbar\"}},[t._v(\"#\")]),t._v(\" Scrollbar\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"Scrollbar\")]),t._v(\"是一个Material风格的滚动指示器（滚动条），如果要给可滚动组件添加滚动条，只需将\"),a(\"code\",[t._v(\"Scrollbar\")]),t._v(\"作为可滚动组件的任意一个父级组件即可，如：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scrollbar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SingleChildScrollView\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[a(\"code\",[t._v(\"Scrollbar\")]),t._v(\"和\"),a(\"code\",[t._v(\"CupertinoScrollbar\")]),t._v(\"都是通过监听滚动通知来确定滚动条位置的。关于的滚动通知的详细内容我们将在本章最后一节中专门介绍。\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"cupertinoscrollbar\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#cupertinoscrollbar\"}},[t._v(\"#\")]),t._v(\" CupertinoScrollbar\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"CupertinoScrollbar\")]),t._v(\"是iOS风格的滚动条，如果你使用的是\"),a(\"code\",[t._v(\"Scrollbar\")]),t._v(\"，那么在iOS平台它会自动切换为\"),a(\"code\",[t._v(\"CupertinoScrollbar\")]),t._v(\"。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"viewport视口\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#viewport视口\"}},[t._v(\"#\")]),t._v(\" ViewPort视口\")]),t._v(\" \"),a(\"p\",[t._v(\"在很多布局系统中都有ViewPort的概念，在Flutter中，术语ViewPort（视口），如无特别说明，则是指一个Widget的实际显示区域。例如，一个\"),a(\"code\",[t._v(\"ListView\")]),t._v(\"的显示区域高度是800像素，虽然其列表项总高度可能远远超过800像素，但是其ViewPort仍然是800像素。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"基于sliver的延迟构建\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#基于sliver的延迟构建\"}},[t._v(\"#\")]),t._v(\" 基于Sliver的延迟构建\")]),t._v(\" \"),a(\"p\",[t._v(\"通常可滚动组件的子组件可能会非常多、占用的总高度也会非常大；如果要一次性将子组件全部构建出将会非常昂贵！为此，Flutter中提出一个Sliver（中文为“薄片”的意思）概念，如果一个可滚动组件支持Sliver模型，那么该滚动可以将子组件分成好多个“薄片”（Sliver），只有当Sliver出现在视口中时才会去构建它，这种模型也称为“基于Sliver的延迟构建模型”。可滚动组件中有很多都支持基于Sliver的延迟构建模型，如\"),a(\"code\",[t._v(\"ListView\")]),t._v(\"、\"),a(\"code\",[t._v(\"GridView\")]),t._v(\"，但是也有不支持该模型的，如\"),a(\"code\",[t._v(\"SingleChildScrollView\")]),t._v(\"。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"主轴和纵轴\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#主轴和纵轴\"}},[t._v(\"#\")]),t._v(\" 主轴和纵轴\")]),t._v(\" \"),a(\"p\",[t._v(\"在可滚动组件的坐标描述中，通常将滚动方向称为主轴，非滚动方向称为纵轴。由于可滚动组件的默认方向一般都是沿垂直方向，所以默认情况下主轴就是指垂直方向，水平方向同理。\")])])}),[],!1,null,null,null);r.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/164.46dd9498.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[164],{806:function(t,e,r){\"use strict\";r.r(e);var i=r(45),a=Object(i.a)({},(function(){var t=this,e=t.$createElement,r=t._self._c||e;return r(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[r(\"h2\",{attrs:{id:\"功能型widget简介\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#功能型widget简介\"}},[t._v(\"#\")]),t._v(\" 功能型Widget简介\")]),t._v(\" \"),r(\"p\",[t._v(\"功能型Widget指的是不会影响UI布局及外观的Widget，它们通常具有一定的功能，如事件监听、数据存储等，我们之前介绍过的FocusScope（焦点控制）、PageStorage（数据存储）、NotificationListener（事件监听）都属于功能型Widget。由于Widget是Flutter的一等公民，功能型Widget非常多，我们不会去一一介绍，本章中主要介绍几种常用的功能型Widget。\")]),t._v(\" \"),r(\"h2\",{attrs:{id:\"本章目录\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本章目录\"}},[t._v(\"#\")]),t._v(\" 本章目录\")]),t._v(\" \"),r(\"ul\",[r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/chapter7/willpopscope.html\"}},[t._v(\"7.1：导航返回拦截（WillPopScope）\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/chapter7/inherited_widget.html\"}},[t._v(\"7.2：数据共享（InheritedWidget）\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/chapter7/provider.html\"}},[t._v(\"7.3： 跨组件状态共享（Provider）\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/chapter7/theme.html\"}},[t._v(\"7.4：颜色和主题（Theme）\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/chapter7/futurebuilder_and_streambuilder.html\"}},[t._v(\"7.5：异步UI更新（FutureBuilder、StreamBuilder）\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/chapter7/dailog.html\"}},[t._v(\"7.6：对话框详解\")])],1)])])}),[],!1,null,null,null);e.default=a.exports}}]);"
  },
  {
    "path": "docs/assets/js/165.86c8b8c1.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[165],{810:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_7-1-导航返回拦截-willpopscope\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_7-1-导航返回拦截-willpopscope\"}},[t._v(\"#\")]),t._v(\" 7.1 导航返回拦截（WillPopScope）\")]),t._v(\" \"),a(\"p\",[t._v(\"为了避免用户误触返回按钮而导致APP退出，在很多APP中都拦截了用户点击返回键的按钮，然后进行一些防误触判断，比如当用户在某一个时间段内点击两次时，才会认为用户是要退出（而非误触）。Flutter中可以通过\"),a(\"code\",[t._v(\"WillPopScope\")]),t._v(\"来实现返回按钮拦截，我们看看\"),a(\"code\",[t._v(\"WillPopScope\")]),t._v(\"的默认构造函数：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"WillPopScope\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" WillPopCallback onWillPop\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" Widget child\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[a(\"code\",[t._v(\"onWillPop\")]),t._v(\"是一个回调函数，当用户点击返回按钮时被调用（包括导航返回按钮及Android物理返回按钮）。该回调需要返回一个\"),a(\"code\",[t._v(\"Future\")]),t._v(\"对象，如果返回的\"),a(\"code\",[t._v(\"Future\")]),t._v(\"最终值为\"),a(\"code\",[t._v(\"false\")]),t._v(\"时，则当前路由不出栈(不会返回)；最终值为\"),a(\"code\",[t._v(\"true\")]),t._v(\"时，当前路由出栈退出。我们需要提供这个回调来决定是否退出。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"示例\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),a(\"p\",[t._v(\"为了防止用户误触返回键退出，我们拦截返回事件。当用户在1秒内点击两次返回按钮时，则退出；如果间隔超过1秒则不退出，并重新记时。代码如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"WillPopScopeTestRoute\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  WillPopScopeTestRouteState \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"WillPopScopeTestRouteState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"WillPopScopeTestRouteState\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"WillPopScopeTestRoute\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  DateTime _lastPressedAt\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//上次点击时间\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"WillPopScope\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        onWillPop\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_lastPressedAt \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"||\")]),t._v(\"\\n              DateTime\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"now\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"difference\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_lastPressedAt\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//两次点击间隔超过1秒则重新计时\")]),t._v(\"\\n            _lastPressedAt \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" DateTime\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"now\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"1秒内连续按两次返回键退出\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"读者可以运行示例看看效果。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/166.3938fc5f.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[166],{811:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_8-3-事件总线\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_8-3-事件总线\"}},[t._v(\"#\")]),t._v(\" 8.3 事件总线\")]),t._v(\" \"),a(\"p\",[t._v(\"在APP中，我们经常会需要一个广播机制，用以跨页面事件通知，比如一个需要登录的APP中，页面会关注用户登录或注销事件，来进行一些状态更新。这时候，一个事件总线便会非常有用，事件总线通常实现了订阅者模式，订阅者模式包含发布者和订阅者两种角色，可以通过事件总线来触发事件和监听事件，本节我们实现一个简单的全局事件总线，我们使用单例模式，代码如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//订阅者回调签名\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"typedef\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"EventCallback\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"arg\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"EventBus\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//私有构造函数\")]),t._v(\"\\n  EventBus\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_internal\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//保存单例\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" EventBus _singleton \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"EventBus\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"_internal\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//工厂构造函数\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"factory\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"EventBus\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _singleton\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//保存事件订阅者队列，key:事件名(id)，value: 对应事件的订阅者队列\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" _emap \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Map\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Object\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" List\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"EventCallback\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">>\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//添加订阅者\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"on\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"eventName\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" EventCallback f\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"eventName \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"||\")]),t._v(\" f \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    _emap\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"eventName\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"List\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"EventCallback\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    _emap\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"eventName\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"f\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//移除订阅者\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"off\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"eventName\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"EventCallback f\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" list \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _emap\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"eventName\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"eventName \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"||\")]),t._v(\" list \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"f \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _emap\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"eventName\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      list\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"remove\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"f\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//触发事件，事件触发后该事件所有订阅者会被调用\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"emit\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"eventName\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"arg\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" list \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _emap\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"eventName\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"list \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    int len \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" list\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"length \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//反向遍历，防止订阅者在回调中移除自身带来的下标错位 \")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"for\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" i \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" len\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" i \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"--\")]),t._v(\"i\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      list\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"i\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"arg\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//定义一个top-level（全局）变量，页面引入该文件后可以直接使用bus\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" bus \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"EventBus\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"使用示例：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//页面A中\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//监听登录事件\")]),t._v(\"\\nbus\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"on\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"login\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"arg\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// do something\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//登录页B中\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//登录成功后触发登录事件，页面A中订阅者会被调用\")]),t._v(\"\\nbus\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"emit\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"login\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" userInfo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\")])])]),a(\"blockquote\",[a(\"p\",[t._v(\"注意：Dart中实现单例模式的标准做法就是使用static变量+工厂构造函数的方式，这样就可以保证\"),a(\"code\",[t._v(\"new EventBus()\")]),t._v(\"始终返回都是同一个实例，读者应该理解并掌握这种方法。\")])]),t._v(\" \"),a(\"p\",[t._v(\"事件总线通常用于组件之间状态共享，但关于组件之间状态共享也有一些专门的包如redux、以及前面介绍过的Provider。对于一些简单的应用，事件总线是足以满足业务需求的，如果你决定使用状态管理包的话，一定要想清楚您的APP是否真的有必要使用它，防止“化简为繁”、过度设计。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/167.09cbb683.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[167],{813:function(t,e,r){\"use strict\";r.r(e);var a=r(45),i=Object(a.a)({},(function(){var t=this,e=t.$createElement,r=t._self._c||e;return r(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[r(\"h2\",{attrs:{id:\"事件处理与通知\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#事件处理与通知\"}},[t._v(\"#\")]),t._v(\" 事件处理与通知\")]),t._v(\" \"),r(\"p\",[t._v(\"Flutter中的手势系统有两个独立的层。第一层为原始指针(pointer)事件，它描述了屏幕上指针（例如，触摸、鼠标和触控笔）的位置和移动。 第二层为手势，描述由一个或多个指针移动组成的语义动作，如拖动、缩放、双击等。本章将先分别介绍如何处理这两种事件，最后再介绍一下Flutter中重要的Notification机制。\")]),t._v(\" \"),r(\"h2\",{attrs:{id:\"本章目录\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本章目录\"}},[t._v(\"#\")]),t._v(\" 本章目录\")]),t._v(\" \"),r(\"ul\",[r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/chapter8/listener.html\"}},[t._v(\"原始指针事件处理\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/chapter8/gesture.html\"}},[t._v(\"手势识别\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/chapter8/eventbus.html\"}},[t._v(\"全局事件总线\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/chapter8/notification.html\"}},[t._v(\"通知Notification\")])],1)])])}),[],!1,null,null,null);e.default=i.exports}}]);"
  },
  {
    "path": "docs/assets/js/168.aab68e4f.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[168],{818:function(t,a,n){\"use strict\";n.r(a);var s=n(45),e=Object(s.a)({},(function(){var t=this,a=t.$createElement,n=t._self._c||a;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_9-4-hero动画\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_9-4-hero动画\"}},[t._v(\"#\")]),t._v(\" 9.4 Hero动画\")]),t._v(\" \"),n(\"p\",[t._v(\"Hero指的是可以在路由(页面)之间“飞行”的widget，简单来说Hero动画就是在路由切换时，有一个共享的widget可以在新旧路由间切换。由于共享的widget在新旧路由页面上的位置、外观可能有所差异，所以在路由切换时会从旧路逐渐过渡到新路由中的指定位置，这样就会产生一个Hero动画。\")]),t._v(\" \"),n(\"p\",[t._v(\"你可能多次看到过 hero 动画。例如，一个路由中显示待售商品的缩略图列表，选择一个条目会将其跳转到一个新路由，新路由中包含该商品的详细信息和“购买”按钮。 在Flutter中将图片从一个路由“飞”到另一个路由称为\"),n(\"strong\",[t._v(\"hero动画\")]),t._v(\"，尽管相同的动作有时也称为 \"),n(\"strong\",[t._v(\"共享元素转换\")]),t._v(\"。下面我们通过一个示例来体验一下hero 动画。\")]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[t._v(\"为什么要将这种可飞行的共享组件称为hero（英雄），有一种说法是说美国文化中的超人是可以飞的，那是美国人心中的大英雄，还有漫威中的超级英雄基本上都是会飞的，所以Flutter开发人员就对这种“会飞的widget”就起了一个富有浪漫主义的名字hero。当然这种说法并非官方解释，但却很有意思。\")])]),t._v(\" \"),n(\"h4\",{attrs:{id:\"示例\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),n(\"p\",[t._v(\"假设有两个路由A和B，他们的内容交互如下：\")]),t._v(\" \"),n(\"p\",[t._v(\"A：包含一个用户头像，圆形，点击后跳到B路由，可以查看大图。\")]),t._v(\" \"),n(\"p\",[t._v(\"B：显示用户头像原图，矩形；\")]),t._v(\" \"),n(\"p\",[t._v(\"在AB两个路由之间跳转的时候，用户头像会逐渐过渡到目标路由页的头像上，接下来我们先看看代码，然后再解析：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 路由A\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"HeroAnimationRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"topCenter\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"InkWell\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Hero\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          tag\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"avatar\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//唯一标记，前后两个路由页Hero的tag必须相同\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ClipOval\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"asset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"images/avatar.png\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        onTap\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//打开B路由  \")]),t._v(\"\\n          Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"push\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"PageRouteBuilder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              pageBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Animation animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  Animation secondaryAnimation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FadeTransition\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  opacity\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    appBar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                      title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"原图\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    body\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"HeroAnimationRouteB\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"路由B:\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"HeroAnimationRouteB\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Hero\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          tag\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"avatar\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//唯一标记，前后两个路由页Hero的tag必须相同\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"asset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"images/avatar.png\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们可以看到，实现Hero动画只需要用\"),n(\"code\",[t._v(\"Hero\")]),t._v(\"组件将要共享的widget包装起来，并提供一个相同的tag即可，中间的过渡帧都是Flutter Framework自动完成的。必须要注意， 前后路由页的共享\"),n(\"code\",[t._v(\"Hero\")]),t._v(\"的tag必须是相同的，Flutter Framework内部正是通过tag来确定新旧路由页widget的对应关系的。\")]),t._v(\" \"),n(\"p\",[t._v(\"Hero动画的原理比较简单，Flutter Framework知道新旧路由页中共享元素的位置和大小，所以根据这两个端点，在动画执行过程中求出过渡时的插值（中间态）即可，而感到幸运的是，这些事情不需要我们自己动手，Flutter已经帮我们做了！\")])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/169.b01d210b.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[169],{819:function(t,r,e){\"use strict\";e.r(r);var a=e(45),i=Object(a.a)({},(function(){var t=this,r=t.$createElement,e=t._self._c||r;return e(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[e(\"h2\",{attrs:{id:\"简介\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#简介\"}},[t._v(\"#\")]),t._v(\" 简介\")]),t._v(\" \"),e(\"p\",[t._v(\"精心设计的动画会让用户界面感觉更直观、流畅，能改善用户体验。 Flutter可以轻松实现各种动画类型，对于许多widget，特别是\"),e(\"a\",{attrs:{href:\"https://flutter.io/docs/reference/widgets/material\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Material Design widgets\"),e(\"OutboundLink\")],1),t._v(\"，都带有在其设计规范中定义的标准动画效果(但也可以自定义这些效果)。本章将详细介绍Flutter的动画系统，并会通过几个小实例来演示，以帮助开发者迅速理解并掌握动画的开发流程与原理。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"本章目录\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本章目录\"}},[t._v(\"#\")]),t._v(\" 本章目录\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter9/intro.html\"}},[t._v(\"9.1：Flutter动画简介\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter9/animation_structure.html\"}},[t._v(\"9.2：动画结构\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter9/route_transition.html\"}},[t._v(\"9.3：自定义路由过渡动画\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter9/hero.html\"}},[t._v(\"9.4：Hero动画\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter9/stagger_animation.html\"}},[t._v(\"9.5：交织动画\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter9/animated_switcher.html\"}},[t._v(\"9.6：通用“动画切换”组件（AnimatedSwitcher）\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter9/animated_widgets.html\"}},[t._v(\"9.7：动画过渡组件\")])],1)])])}),[],!1,null,null,null);r.default=i.exports}}]);"
  },
  {
    "path": "docs/assets/js/17.59a459c4.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[17],{590:function(t,a,s){t.exports=s.p+\"assets/img/3-17.a063365a.png\"},591:function(t,a,s){t.exports=s.p+\"assets/img/3-18.cb8a4b0f.png\"},592:function(t,a,s){t.exports=s.p+\"assets/img/3-19.feb336de.png\"},593:function(t,a,s){t.exports=s.p+\"assets/img/3-20.9c7569a6.png\"},594:function(t,a){t.exports=\"data:image/png;base64,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\"},595:function(t,a){t.exports=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHYAAAA8CAYAAACzZE4bAAAMJWlDQ1BJQ0MgUHJvZmlsZQAASImVVwdUk8kWnr8kISGhBUKREnoTpUiXGloEAamCjZAEEkoMgSBiBVlUYC2oWLCiqyKKrgWQxYZdWRTsdUFERVlFXWyovEkC6LqvnHfPmZnv3Ln3znennRkAVCM5YnEGqgZApihHEhXsz5yckMgkPQIo0AbKgATGcrjZYr/IyDAAZbj9u7y7CRBZe81OFuuf/f9V1Hn8bC4ASCTEybxsbibEhwHAXbhiSQ4AhF6oN52VI4aYCFkCTQkkCLGZDKcqsJsMJytwmNwmJooFcRIASlQOR5IKgIqMFzOXmwrjqJRBbC/iCUUQN0HszRVweBB/hnh0ZuZMiFWtILZK/i5O6t9iJo/E5HBSR7AiF7koBQizxRmc2f/ndPxvycyQDo9hCgtVIAmJkuUsm7f0maEyTIX4gig5PAJiDYivC3lyexl+IpCGxA7Zf+Bms+CcAQYAKJXHCQiFWB9iE2l6rN8Q9uZI5L7QHk3MF8TEK+KjIsnMqKH4aL4oIzxsKE6ZgM8exlX87MDoYZsUYRAbYriGaIMwhx0zFPNCrjAuHGIViO9np0eHDvk+zxewwkfGkkbJOMM1x0Bm9nAumFmKJChKYY+5CITs8CF9WI4gJkThi03ncuQcdCBO42dPDhvmw+MHBCr4YIV8UewQT6xcnOMfNWS/Q5wROWSPNfEzgmV6E4hbs3Ojh337cuBmU+SCgzTOhEjFuLimOCcyRsENZ4IwwAIBgAmksCSDmSANCFt763vBcE8Q4AAJSAV8YDekGfaIl/eIYB0N8sGfEPFB9oifv7yXD3Kh/suIVlHbgRR5b67cIx08gTgT18O9cU88DNa+sDjibrj7sB9TdXhUYiAxgBhCDCJazxAWSn6IywRcmEEGLBIQCls+zErGQTTM/VscwhNCG+ER4Qahg3AHxIHH0E74jwy/RROO6CaCDhg1aCi75O+zwy0ga2fcH/eC/CF3nIHrATt8HMzED/eBuTlD7bdZ+3fcpcOsyfZklKxN9iVb/WinYqPiPOIjy+17ngpeySOZsEZ6fhyN9V1uPNiG/miJLcEOYeexU9hFrAmrB0zsBNaAtWDHZHhkbzyW743h0aLkfNJhHOGwjX2NfY/95x/G5gyNL5GvP8jh5+XIDg5rpni2RJgqyGH6wduaz2SLuGNGMx3tHeAtKrv7FVfLW4b8TkcYl77pCuYAMP7Q4OBg0zddGLxZjhgAQHn1TWf1Hh5newAuFHClklyFDpdVBEABqvCk6AJDeHdZwYwcgQvwBL4gEEwAESAGJIDpcJ4FIBOyngXmggJQDErBCrAGbABbwHawG+wDB0E9aAKnwDlwGVwFN8A9uFe6wQvQB96BAQRBSAgNoSO6iBFijtgijogb4o0EImFIFJKAJCGpiAiRInORRUgpUo5sQLYh1civyFHkFHIRaUPuIJ1ID/IG+YRiKBXVRA1QC3Qs6ob6oaFoDDoNTUWz0Hy0CF2GrkOr0L1oHXoKvYzeQDvQF2g/BjBljIEZY3aYG8bCIrBELAWTYPOxEqwCq8JqsUa40tewDqwX+4gTcTrOxO3gfg3BY3EunoXPx8vwDfhuvA4/g1/DO/E+/CuBRtAn2BI8CGzCZEIqYRahmFBB2Ek4QjgLz1Q34R2RSGQQLYmu8KwmENOIc4hlxE3E/cSTxDZiF7GfRCLpkmxJXqQIEoeUQyomrSftJZ0gtZO6SR+UlJWMlByVgpQSlURKhUoVSnuUjiu1Kz1VGiCrkc3JHuQIMo88m7ycvIPcSL5C7iYPUNQplhQvSgwljVJAWUeppZyl3Ke8VVZWNlF2V56kLFReqLxO+YDyBeVO5Y9UDaoNlUWdSpVSl1F3UU9S71Df0mg0C5ovLZGWQ1tGq6adpj2kfVChq4xRYavwVBaoVKrUqbSrvFQlq5qr+qlOV81XrVA9pHpFtVeNrGahxlLjqM1Xq1Q7qnZLrV+dru6gHqGeqV6mvkf9ovozDZKGhUagBk+jSGO7xmmNLjpGN6Wz6Fz6IvoO+ll6tyZR01KTrZmmWaq5T7NVs09LQ2ucVpxWnlal1jGtDgbGsGCwGRmM5YyDjJuMT9oG2n7afO2l2rXa7drvdUbp+OrwdUp09uvc0Pmky9QN1E3XXalbr/tAD9ez0ZukN0tvs95Zvd5RmqM8R3FHlYw6OOquPqpvox+lP0d/u36Lfr+BoUGwgdhgvcFpg15DhqGvYZrhasPjhj1GdCNvI6HRaqMTRs+ZWkw/ZgZzHfMMs89Y3zjEWGq8zbjVeMDE0iTWpNBkv8kDU4qpm2mK6WrTZtM+MyOziWZzzWrM7pqTzd3MBeZrzc+bv7ewtIi3WGxRb/HMUseSbZlvWWN534pm5WOVZVVldd2aaO1mnW69yfqqDWrjbCOwqbS5YovautgKbTfZto0mjHYfLRpdNfqWHdXOzy7XrsaucwxjTNiYwjH1Y16ONRubOHbl2PNjv9o722fY77C/56DhMMGh0KHR4Y2jjSPXsdLxuhPNKchpgVOD0+txtuP44zaPu+1Md57ovNi52fmLi6uLxKXWpcfVzDXJdaPrLTdNt0i3MrcL7gR3f/cF7k3uHz1cPHI8Dnq88rTzTPfc4/lsvOV4/vgd47u8TLw4Xtu8OryZ3kneW707fIx9OD5VPo98TX15vjt9n/pZ+6X57fV76W/vL/E/4v+e5cGaxzoZgAUEB5QEtAZqBMYGbgh8GGQSlBpUE9QX7Bw8J/hkCCEkNGRlyC22AZvLrmb3TXCdMG/CmVBqaHTohtBHYTZhkrDGiejECRNXTbwfbh4uCq+PABHsiFURDyItI7Mif5tEnBQ5qXLSkyiHqLlR56Pp0TOi90S/i/GPWR5zL9YqVhrbHKcaNzWuOu59fEB8eXzH5LGT502+nKCXIExoSCQlxiXuTOyfEjhlzZTuqc5Ti6fenGY5LW/axel60zOmH5uhOoMz41ASISk+aU/SZ04Ep4rTn8xO3pjcx2Vx13Jf8Hx5q3k9fC9+Of9pildKecqzVK/UVak9Ah9BhaBXyBJuEL5OC0nbkvY+PSJ9V/pgRnzG/kylzKTMoyINUbrozEzDmXkz28S24mJxR5ZH1pqsPkmoZGc2kj0tuyFHEz6yW6RW0p+knbneuZW5H2bFzTqUp54nymuZbTN76eyn+UH5v8zB53DnNM81nlswt3Oe37xt85H5yfObF5guKFrQvTB44e4CSkF6we+F9oXlhX8til/UWGRQtLCo66fgn2qKVYolxbcWey7esgRfIlzSutRp6fqlX0t4JZdK7UsrSj+Xccsu/ezw87qfB5elLGtd7rJ88wriCtGKmyt9Vu4uVy/PL+9aNXFV3Wrm6pLVf62ZseZixbiKLWspa6VrO9aFrWtYb7Z+xfrPGwQbblT6V+7fqL9x6cb3m3ib2jf7bq7dYrCldMunrcKtt7cFb6ursqiq2E7cnrv9yY64Hed/cfuleqfeztKdX3aJdnXsjtp9ptq1unqP/p7lNWiNtKZn79S9V/cF7Guotavdtp+xv/QAOCA98PzXpF9vHgw92HzI7VDtYfPDG4/Qj5TUIXWz6/rqBfUdDQkNbUcnHG1u9Gw88tuY33Y1GTdVHtM6tvw45XjR8cET+Sf6T4pP9p5KPdXVPKP53unJp6+fmXSm9Wzo2Qvngs6dPu93/sQFrwtNFz0uHr3kdqn+ssvluhbnliO/O/9+pNWlte6K65WGq+5XG9vGtx1v92k/dS3g2rnr7OuXb4TfaLsZe/P2ram3Om7zbj+7k3Hn9d3cuwP3Ft4n3C95oPag4qH+w6o/rP/Y3+HScawzoLPlUfSje13crhePsx9/7i56QntS8dToafUzx2dNPUE9V59Ped79QvxioLf4T/U/N760enn4le+rlr7Jfd2vJa8H35S91X27669xfzX3R/Y/fJf5buB9yQfdD7s/un08/yn+09OBWZ9Jn9d9sf7S+DX06/3BzMFBMUfCkT8FMFjQlBQA3uwCgJYAAP0qfD9MUfzN5IIo/pNyBP4TVvzf5OICQC1sZM9w1kkADsBi6QtjwyJ7jsf4AtTJaaQMSXaKk6MiFhX+cAgfBgffwncMqRGAL5LBwYFNg4NfdkCydwA4maX4E8pE9gfdKo/RzshbCH6QfwEOk3CtyyHjHgAAAAlwSFlzAAAWJQAAFiUBSVIk8AAAB6FJREFUeAHtWntQVFUY/+2LheUN8hREAUUEUkF8IaUlTv80vprKcsxxcjRHHdOkdMo/aprRzFJHzWnsNU7pTH+UZlNqaZb4QEUwwZCHSipvloewsM/OuQ64u3fZvbvAZffuPTM73POd7zv3nN+Pc853v+9ItFqtCWIRHAJSwc1InBCDgEisQP8RRGJFYgWKgECnJa5YkViBIiDQaYkrViRWoAgIdFriihWJFSgCAp2WuGJFYgWKgECnJa5YkViBIiDQackFOi/O0+rSdeGf5psoabqBqrZq1Hc1QKPXQCaVIUgRiLjAOKSHpSF9RBqSg5M49zvcihJvzceqe9T4qeo4TtX8jh5DDyceYgNi8VLyi5gVOxNSiXufYrwSq2nqwdl119Ba0e4QSKlShoS50ZiSnwq5nww3Dlbi9g810HXoHNrKiH7WplQkL4izqfvHf2dxqPQrzoRadzI6KAHrJ64F/euuhVdiz28pRvWJh05hEfdMJJTBClQdf+CUncJfjiWX5lnYGE1GhtDf7p2ykLtS8ZH5YMOkdZgePc0V8yG34fWM7VY/Xm0jcyOQtjzR7uQMWiPKj9zD/XMNjF54WjAy3kiCT5DCrp26ogNXtpdB16ln6X176zAGg1Tasdagxa6i3cjP2ojsqGzWu4ZbwCuxvZNVRfoiemp4b7Xfv5GZoTg64zRMRhOy8yeA1h0VqUJiU6Ww/gp+vvMLq21UYDzSw9NRTRynf9XlrHYqGBM0GqlhqbjdWoHK1so+HYPJgN3F+7Dn6V0Y4TeiT+4OD8NCLNeJK1RySOUSGMh9O0WA60PVGXX4svQb1mtj/KPxae5OxhHSGw1YeWY12nraLPTiAuKwK/djRtat78ay0yugNz7ZDagHvbfkAD6Yvs3Cbrgr7u3aDRI6l+ouo1HTyOrNX+7f593KpFL4yXxZOiq5X59MLpVDJpH11XsfylpKUdtZ11t1i7+uLwO3GD63QVysvWxTsbKtCruu70ZuTA5ukG/Zuq56lh7dfveW7CdO0lRcrS9iedIKqQI+5Hey5hSWpy5j2Q+XwCuINT8XrYEueHgB9Gev/Hn/HOiPFuoNPxWegZzYGYgn5zPdljt1naDn7c3mUoT5hiJKFU1W9vBuhl5BbJvW8XezPWJpG92Cc2Jm4NWUV1DWcgvHqk/gbvtdm2YqhQpz459FXvxzGBkw0qbOUAu9gljbfjJ3aFVyFdZNXMMYbC54Fx3aR3aNaZjyOCGeeuHPJ8zD0pTX4Cdnn992Oxlg4/DuFwMcPFfzcD/Hn1b99eVLCNmSnc+s0h3XPnFIqnk/JpMJv949iQ1/bUSTptm8acifvYLYlNAUl4CUQIIVE17HtYYim9/AXDtt1DQhv2ALHpGzmK/iFcTmxua4hGdq2HiEKcOYZIF1BzH+MQhRBluLmXo0+T4OUYZYtLX2tOLDwo8sZENZ8QpiJ0dMQkroOKdxXJS8kIktWxtmRk7G/tl78Pmc/Qj3DbNonkAiVAdm78XBOfsQ4Rdh0VZBolbFjSUWsqGqeAWxFLw1GavhK1NyxpGuOBqcsPVt2xvIkEokxClSWfRJz2RaaFovQOFv0UYr35cfZcmGQuAVXjEFLp4kzDdMXk8C959BZxYS7A9UGkOubrtjs7mg9iKaLjSjpVvNimgVNVwn5+lWtJNPrAaStLcuDRq2zFpnMOpes2IpWFNJFmZz1ttklT0JE/YHYrBPkE1ievXL1bdZpPa20YCILVJpexeJLfNRvIpYCuiUyEwS1N9hM+ZrDjj1iN39loT5eK2fvY5YCoBGp2FCgNZgmNfbdR2gnu9gl0Byj4qP4jVnrDmYhQ1XmaqSOFN0e56f+AJzmY2GClu6W5gkOg0uJIcM/uW1pJBE86EM2fOwEGvUGaF79CSnaWt2FNiOmi6SizUyzc2lbfCP9oVEaj9AqO8y2OrOQlZYdwUzSdx3VcZKBCoCmLbE4DFYmDTfQo9WkoITmduLrAYXBDQTtCZjlQuWzpvweuep4D1yxfPYfedH6YIFvSf18vk8liW90qIhCfNgZRCrzZaApu22Xngf9L7UQAo9sxcnLyBJhCUD6YazLa9n7NhF8ZD68PPKUXm2z0eaduNKKkVxXMhYLEtdyhnQ/hSnkXwuX6TSMfC6YukLDT1GdKt76KPd0lLWjr/fKYa++/HWSrfgrE3jkTAv2q4dbZT7yqAM8XGo54zCj1XHmOACzbs6UyQkiDGLhDTfmrTeGbMB6/JOrDMjrrvcjDNrrzLn7PRtaRi7eJQz5oOuW9lahU+L96DOiWsw9IzeOWv7oI/FUYduTSwdfHNZG/SdBkRlW8ZkHU1sKNup9/xd+RFyz6mWydj0Xm6jyfhAnwCEKkMZz/pB50PQpPvhvK9BVy6fxe2J5RMMV99Fo0nUKbPO9lDv+4vSQ3gzfRWyojJd7d4lO5FYl2DjbmSCCeXqCox3IbvE/S1sTZFYNiaCkPDz7SEIqDxrEiKxnsUX59GKxHKGyrMURWI9iy/OoxWJ5QyVZymKxHoWX5xHKxLLGSrPUhSJ9Sy+OI9WJJYzVJ6lKBLrWXxxHq1ILGeoPEtRJNaz+OI8WpFYzlB5lqJIrGfxxXm0/wO1bkxwBptteAAAAABJRU5ErkJggg==\"},886:function(t,a,s){\"use strict\";s.r(a);var n=s(45),e=Object(n.a)({},(function(){var t=this,a=t.$createElement,n=t._self._c||a;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h2\",{attrs:{id:\"_3-5-图片及icon\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-5-图片及icon\"}},[t._v(\"#\")]),t._v(\" 3.5 图片及ICON\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_3-5-1-图片\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-5-1-图片\"}},[t._v(\"#\")]),t._v(\" 3.5.1 图片\")]),t._v(\" \"),n(\"p\",[t._v(\"Flutter中，我们可以通过\"),n(\"code\",[t._v(\"Image\")]),t._v(\"组件来加载并显示图片，\"),n(\"code\",[t._v(\"Image\")]),t._v(\"的数据源可以是asset、文件、内存以及网络。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"imageprovider\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#imageprovider\"}},[t._v(\"#\")]),t._v(\" ImageProvider\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"ImageProvider\")]),t._v(\" 是一个抽象类，主要定义了图片数据获取的接口\"),n(\"code\",[t._v(\"load()\")]),t._v(\"，从不同的数据源获取图片需要实现不同的\"),n(\"code\",[t._v(\"ImageProvider\")]),t._v(\" ，如\"),n(\"code\",[t._v(\"AssetImage\")]),t._v(\"是实现了从Asset中加载图片的ImageProvider，而\"),n(\"code\",[t._v(\"NetworkImage\")]),t._v(\"实现了从网络加载图片的ImageProvider。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"image\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#image\"}},[t._v(\"#\")]),t._v(\" Image\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"Image\")]),t._v(\" widget有一个必选的\"),n(\"code\",[t._v(\"image\")]),t._v(\"参数，它对应一个ImageProvider。下面我们分别演示一下如何从asset和网络加载图片。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"从asset中加载图片\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#从asset中加载图片\"}},[t._v(\"#\")]),t._v(\" 从asset中加载图片\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"p\",[t._v(\"在工程根目录下创建一个\"),n(\"code\",[t._v(\"images目录\")]),t._v(\"，并将图片avatar.png拷贝到该目录。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"在\"),n(\"code\",[t._v(\"pubspec.yaml\")]),t._v(\"中的\"),n(\"code\",[t._v(\"flutter\")]),t._v(\"部分添加如下内容：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-yaml extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[n(\"code\",[t._v(\"  \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"assets\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\" images/avatar.png\\n\")])])])])]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[t._v(\"注意: 由于 yaml 文件对缩进严格，所以必须严格按照每一层两个空格的方式进行缩进，此处assets前面应有两个空格。\")])]),t._v(\" \"),n(\"ol\",{attrs:{start:\"3\"}},[n(\"li\",[n(\"p\",[t._v(\"加载该图片\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Image\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AssetImage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"images/avatar.png\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"Image也提供了一个快捷的构造函数\"),n(\"code\",[t._v(\"Image.asset\")]),t._v(\"用于从asset中加载、显示图片：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"asset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"images/avatar.png\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])])])]),t._v(\" \"),n(\"h4\",{attrs:{id:\"从网络加载图片\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#从网络加载图片\"}},[t._v(\"#\")]),t._v(\" 从网络加载图片\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Image\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"NetworkImage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"https://avatars2.githubusercontent.com/u/20411648?s=460&v=4\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"Image也提供了一个快捷的构造函数\"),n(\"code\",[t._v(\"Image.network\")]),t._v(\"用于从网络加载、显示图片：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"network\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"https://avatars2.githubusercontent.com/u/20411648?s=460&v=4\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行上面两个示例，图片加载成功后如图3-17所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(590),alt:\"图3-17\"}})]),t._v(\" \"),n(\"h4\",{attrs:{id:\"参数\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#参数\"}},[t._v(\"#\")]),t._v(\" 参数\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"Image\")]),t._v(\"在显示图片时定义了一系列参数，通过这些参数我们可以控制图片的显示外观、大小、混合效果等。我们看一下Image的主要参数：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Image\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//图片的宽\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//图片高度\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//图片的混合色值\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"colorBlendMode\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//混合模式\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"fit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//缩放模式\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"alignment \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//对齐方式\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"repeat \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" ImageRepeat\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"noRepeat\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//重复方式\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"ul\",[n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"width\")]),t._v(\"、\"),n(\"code\",[t._v(\"height\")]),t._v(\"：用于设置图片的宽、高，当不指定宽高时，图片会根据当前父容器的限制，尽可能的显示其原始大小，如果只设置\"),n(\"code\",[t._v(\"width\")]),t._v(\"、\"),n(\"code\",[t._v(\"height\")]),t._v(\"的其中一个，那么另一个属性默认会按比例缩放，但可以通过下面介绍的\"),n(\"code\",[t._v(\"fit\")]),t._v(\"属性来指定适应规则。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"fit\")]),t._v(\"：该属性用于在图片的显示空间和图片本身大小不同时指定图片的适应模式。适应模式是在\"),n(\"code\",[t._v(\"BoxFit\")]),t._v(\"中定义，它是一个枚举类型，有如下值：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"fill\")]),t._v(\"：会拉伸填充满显示空间，图片本身长宽比会发生变化，图片会变形。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"cover\")]),t._v(\"：会按图片的长宽比放大后居中填满显示空间，图片不会变形，超出显示空间部分会被剪裁。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"contain\")]),t._v(\"：这是图片的默认适应规则，图片会在保证图片本身长宽比不变的情况下缩放以适应当前显示空间，图片不会变形。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"fitWidth\")]),t._v(\"：图片的宽度会缩放到显示空间的宽度，高度会按比例缩放，然后居中显示，图片不会变形，超出显示空间部分会被剪裁。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"fitHeight\")]),t._v(\"：图片的高度会缩放到显示空间的高度，宽度会按比例缩放，然后居中显示，图片不会变形，超出显示空间部分会被剪裁。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"none\")]),t._v(\"：图片没有适应策略，会在显示空间内显示图片，如果图片比显示空间大，则显示空间只会显示图片中间部分。\")])]),t._v(\" \"),n(\"p\",[t._v(\"一图胜万言！ 我们对一个宽高相同的头像图片应用不同的\"),n(\"code\",[t._v(\"fit\")]),t._v(\"值，效果如图3-18所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(591),alt:\"图3-18\"}})])]),t._v(\" \"),n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"color\")]),t._v(\"和 \"),n(\"code\",[t._v(\"colorBlendMode\")]),t._v(\"：在图片绘制时可以对每一个像素进行颜色混合处理，\"),n(\"code\",[t._v(\"color\")]),t._v(\"指定混合色，而\"),n(\"code\",[t._v(\"colorBlendMode\")]),t._v(\"指定混合模式，下面是一个简单的示例：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Image\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AssetImage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"images/avatar.png\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  colorBlendMode\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BlendMode\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"difference\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])])])]),t._v(\" \"),n(\"p\",[t._v(\"运行效果如图3-19所示（彩色）:\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(592),alt:\"图3-19\"}})]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"repeat\")]),t._v(\"：当图片本身大小小于显示空间时，指定图片的重复规则。简单示例如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Image\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AssetImage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"images/avatar.png\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  repeat\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ImageRepeat\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"repeatY \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行后效果如图3-20所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(593),alt:\"图3-20\"}})])])]),t._v(\" \"),n(\"p\",[t._v(\"完整的示例代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ImageAndIconRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" img\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AssetImage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"imgs/avatar.png\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SingleChildScrollView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Image\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Image\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" img\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            fit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BoxFit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"fill\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Image\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" img\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            fit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BoxFit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"contain\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Image\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" img\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            fit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BoxFit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cover\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Image\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" img\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            fit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BoxFit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"fitWidth\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Image\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" img\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            fit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BoxFit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"fitHeight\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Image\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" img\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            fit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BoxFit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"scaleDown\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Image\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" img\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            fit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BoxFit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"none\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Image\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" img\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            colorBlendMode\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BlendMode\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"difference\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            fit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BoxFit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"fill\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Image\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" img\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            repeat\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ImageRepeat\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"repeatY \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"map\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"fit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toList\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"h3\",{attrs:{id:\"image缓存\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#image缓存\"}},[t._v(\"#\")]),t._v(\" Image缓存\")]),t._v(\" \"),n(\"p\",[t._v(\"Flutter框架对加载过的图片是有缓存的（内存），默认最大缓存数量是1000，最大缓存空间为100M。关于Image的详细内容及原理我们将会在后面进阶部分深入介绍。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_3-5-2-icon\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-5-2-icon\"}},[t._v(\"#\")]),t._v(\" 3.5.2 ICON\")]),t._v(\" \"),n(\"p\",[t._v(\"Flutter中，可以像Web开发一样使用iconfont，iconfont即“字体图标”，它是将图标做成字体文件，然后通过指定不同的字符而显示不同的图片。\")]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[t._v(\"在字体文件中，每一个字符都对应一个位码，而每一个位码对应一个显示字形，不同的字体就是指字形不同，即字符对应的字形是不同的。而在iconfont中，只是将位码对应的字形做成了图标，所以不同的字符最终就会渲染成不同的图标。\")])]),t._v(\" \"),n(\"p\",[t._v(\"在Flutter开发中，iconfont和图片相比有如下优势：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"体积小：可以减小安装包大小。\")]),t._v(\" \"),n(\"li\",[t._v(\"矢量的：iconfont都是矢量图标，放大不会影响其清晰度。\")]),t._v(\" \"),n(\"li\",[t._v(\"可以应用文本样式：可以像文本一样改变字体图标的颜色、大小对齐等。\")]),t._v(\" \"),n(\"li\",[t._v(\"可以通过TextSpan和文本混用。\")])]),t._v(\" \"),n(\"h5\",{attrs:{id:\"使用material-design字体图标\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#使用material-design字体图标\"}},[t._v(\"#\")]),t._v(\" 使用Material Design字体图标\")]),t._v(\" \"),n(\"p\",[t._v(\"Flutter默认包含了一套Material Design的字体图标，在\"),n(\"code\",[t._v(\"pubspec.yaml\")]),t._v(\"文件中的配置如下\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-yaml extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"flutter\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"uses-material-design\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean important\"}},[t._v(\"true\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"Material Design所有图标可以在其官网查看：https://material.io/tools/icons/\")]),t._v(\" \"),n(\"p\",[t._v(\"我们看一个简单的例子：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"String icons \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// accessible: &#xE914; or 0xE914 or E914\")]),t._v(\"\\nicons \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"\\\\uE914\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// error: &#xE000; or 0xE000 or E000\")]),t._v(\"\\nicons \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\" \\\\uE000\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// fingerprint: &#xE90D; or 0xE90D or E90D\")]),t._v(\"\\nicons \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\" \\\\uE90D\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      fontFamily\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"MaterialIcons\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      fontSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"24.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"green\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图3-21所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(594),alt:\"图3-21\"}})]),t._v(\" \"),n(\"p\",[t._v(\"通过这个示例可以看到，使用图标就像使用文本一样，但是这种方式需要我们提供每个图标的码点，这并对开发者不友好，所以，Flutter封装了\"),n(\"code\",[t._v(\"IconData\")]),t._v(\"和\"),n(\"code\",[t._v(\"Icon\")]),t._v(\"来专门显示字体图标，上面的例子也可以用如下方式实现：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  mainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"accessible\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"green\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"error\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"green\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"fingerprint\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"green\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"Icons\")]),t._v(\"类中包含了所有Material Design图标的\"),n(\"code\",[t._v(\"IconData\")]),t._v(\"静态变量定义。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"使用自定义字体图标\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#使用自定义字体图标\"}},[t._v(\"#\")]),t._v(\" 使用自定义字体图标\")]),t._v(\" \"),n(\"p\",[t._v(\"我们也可以使用自定义字体图标。iconfont.cn上有很多字体图标素材，我们可以选择自己需要的图标打包下载后，会生成一些不同格式的字体文件，在Flutter中，我们使用ttf格式即可。\")]),t._v(\" \"),n(\"p\",[t._v(\"假设我们项目中需要使用一个书籍图标和微信图标，我们打包下载后导入：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"p\",[t._v('导入字体图标文件；这一步和导入字体文件相同，假设我们的字体图标文件保存在项目根目录下，路径为\"fonts/iconfont.ttf\"：')]),t._v(\" \"),n(\"div\",{staticClass:\"language-yaml extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"fonts\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"family\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" myIcon  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"#指定一个字体名\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"fonts\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"asset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" fonts/iconfont.ttf\\n\")])])])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"为了使用方便，我们定义一个\"),n(\"code\",[t._v(\"MyIcons\")]),t._v(\"类，功能和\"),n(\"code\",[t._v(\"Icons\")]),t._v(\"类一样：将字体文件中的所有图标都定义成静态变量：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyIcons\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// book 图标\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" IconData book \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"IconData\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0xe614\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n      fontFamily\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'myIcon'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n      matchTextDirection\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 微信图标\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" IconData wechat \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"IconData\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0xec7d\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"  \\n      fontFamily\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'myIcon'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n      matchTextDirection\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"使用\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  mainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"MyIcons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"book\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"purple\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"MyIcons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"wechat\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"green\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行后效果如图3-22所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(595),alt:\"图3-22\"}})])])])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/170.e68bf3e7.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[170],{820:function(t,a,n){\"use strict\";n.r(a);var s=n(45),e=Object(s.a)({},(function(){var t=this,a=t.$createElement,n=t._self._c||a;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_9-1-flutter动画简介\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_9-1-flutter动画简介\"}},[t._v(\"#\")]),t._v(\" 9.1 Flutter动画简介\")]),t._v(\" \"),n(\"p\",[t._v(\"在任何系统的UI框架中，动画实现的原理都是相同的，即：在一段时间内，快速地多次改变UI外观；由于人眼会产生视觉暂留，所以最终看到的就是一个“连续”的动画，这和电影的原理是一样的。我们将UI的一次改变称为一个动画帧，对应一次屏幕刷新，而决定动画流畅度的一个重要指标就是帧率FPS（Frame Per Second），即每秒的动画帧数。很明显，帧率越高则动画就会越流畅！一般情况下，对于人眼来说，动画帧率超过16FPS，就比较流畅了，超过32FPS就会非常的细腻平滑，而超过32FPS，人眼基本上就感受不到差别了。由于动画的每一帧都是要改变UI输出，所以在一个时间段内连续的改变UI输出是比较耗资源的，对设备的软硬件系统要求都较高，所以在UI系统中，动画的平均帧率是重要的性能指标，而在Flutter中，理想情况下是可以实现60FPS的，这和原生应用能达到的帧率是基本是持平的。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"flutter中动画抽象\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#flutter中动画抽象\"}},[t._v(\"#\")]),t._v(\" Flutter中动画抽象\")]),t._v(\" \"),n(\"p\",[t._v(\"为了方便开发者创建动画，不同的UI系统对动画都进行了一些抽象，比如在Android中可以通过XML来描述一个动画然后设置给View。Flutter中也对动画进行了抽象，主要涉及Animation、Curve、Controller、Tween这四个角色，它们一起配合来完成一个完整动画，下面我们一一来介绍它们。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"animation\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#animation\"}},[t._v(\"#\")]),t._v(\" Animation\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"Animation\")]),t._v(\"是一个抽象类，它本身和UI渲染没有任何关系，而它主要的功能是保存动画的插值和状态；其中一个比较常用的\"),n(\"code\",[t._v(\"Animation\")]),t._v(\"类是\"),n(\"code\",[t._v(\"Animation<double>\")]),t._v(\"。\"),n(\"code\",[t._v(\"Animation\")]),t._v(\"对象是一个在一段时间内依次生成一个区间(Tween)之间值的类。\"),n(\"code\",[t._v(\"Animation\")]),t._v(\"对象在整个动画执行过程中输出的值可以是线性的、曲线的、一个步进函数或者任何其他曲线函数等等，这由\"),n(\"code\",[t._v(\"Curve\")]),t._v(\"来决定。 根据\"),n(\"code\",[t._v(\"Animation\")]),t._v(\"对象的控制方式，动画可以正向运行（从起始状态开始，到终止状态结束），也可以反向运行，甚至可以在中间切换方向。\"),n(\"code\",[t._v(\"Animation\")]),t._v(\"还可以生成除\"),n(\"code\",[t._v(\"double\")]),t._v(\"之外的其他类型值，如：\"),n(\"code\",[t._v(\"Animation<Color>\")]),t._v(\" 或\"),n(\"code\",[t._v(\"Animation<Size>\")]),t._v(\"。在动画的每一帧中，我们可以通过\"),n(\"code\",[t._v(\"Animation\")]),t._v(\"对象的\"),n(\"code\",[t._v(\"value\")]),t._v(\"属性获取动画的当前状态值。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"动画通知\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#动画通知\"}},[t._v(\"#\")]),t._v(\" 动画通知\")]),t._v(\" \"),n(\"p\",[t._v(\"我们可以通过\"),n(\"code\",[t._v(\"Animation\")]),t._v(\"来监听动画每一帧以及执行状态的变化，\"),n(\"code\",[t._v(\"Animation\")]),t._v(\"有如下两个方法：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"code\",[t._v(\"addListener()\")]),t._v(\"；它可以用于给\"),n(\"code\",[t._v(\"Animation\")]),t._v(\"添加帧监听器，在每一帧都会被调用。帧监听器中最常见的行为是改变状态后调用\"),n(\"code\",[t._v(\"setState()\")]),t._v(\"来触发UI重建。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"addStatusListener()\")]),t._v(\"；它可以给\"),n(\"code\",[t._v(\"Animation\")]),t._v(\"添加“动画状态改变”监听器；动画开始、结束、正向或反向（见\"),n(\"code\",[t._v(\"AnimationStatus\")]),t._v(\"定义）时会调用状态改变的监听器。\")])]),t._v(\" \"),n(\"p\",[t._v(\"读者在此只需要知道帧监听器和状态监听器的区别，在后面的章节中我们将会举例说明。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"curve\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#curve\"}},[t._v(\"#\")]),t._v(\" Curve\")]),t._v(\" \"),n(\"p\",[t._v(\"动画过程可以是匀速的、匀加速的或者先加速后减速等。Flutter中通过\"),n(\"code\",[t._v(\"Curve\")]),t._v(\"（曲线）来描述动画过程，我们把匀速动画称为线性的(Curves.linear)，而非匀速动画称为非线性的。\")]),t._v(\" \"),n(\"p\",[t._v(\"我们可以通过\"),n(\"code\",[t._v(\"CurvedAnimation\")]),t._v(\"来指定动画的曲线，如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" CurvedAnimation curve \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CurvedAnimation\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"parent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" curve\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Curves\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"easeIn\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"CurvedAnimation\")]),t._v(\"和\"),n(\"code\",[t._v(\"AnimationController\")]),t._v(\"（下面介绍）都是\"),n(\"code\",[t._v(\"Animation<double>\")]),t._v(\"类型。\"),n(\"code\",[t._v(\"CurvedAnimation\")]),t._v(\"可以通过包装\"),n(\"code\",[t._v(\"AnimationController\")]),t._v(\"和\"),n(\"code\",[t._v(\"Curve\")]),t._v(\"生成一个新的动画对象 ，我们正是通过这种方式来将动画和动画执行的曲线关联起来的。我们指定动画的曲线为\"),n(\"code\",[t._v(\"Curves.easeIn\")]),t._v(\"，它表示动画开始时比较慢，结束时比较快。 \"),n(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/animation/Curves-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Curves\"),n(\"OutboundLink\")],1),t._v(\" 类是一个预置的枚举类，定义了许多常用的曲线，下面列几种常用的：\")]),t._v(\" \"),n(\"table\",[n(\"thead\",[n(\"tr\",[n(\"th\",[t._v(\"Curves曲线\")]),t._v(\" \"),n(\"th\",[t._v(\"动画过程\")])])]),t._v(\" \"),n(\"tbody\",[n(\"tr\",[n(\"td\",[t._v(\"linear\")]),t._v(\" \"),n(\"td\",[t._v(\"匀速的\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"decelerate\")]),t._v(\" \"),n(\"td\",[t._v(\"匀减速\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"ease\")]),t._v(\" \"),n(\"td\",[t._v(\"开始加速，后面减速\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"easeIn\")]),t._v(\" \"),n(\"td\",[t._v(\"开始慢，后面快\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"easeOut\")]),t._v(\" \"),n(\"td\",[t._v(\"开始快，后面慢\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"easeInOut\")]),t._v(\" \"),n(\"td\",[t._v(\"开始慢，然后加速，最后再减速\")])])])]),t._v(\" \"),n(\"p\",[t._v(\"除了上面列举的， \"),n(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/animation/Curves-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Curves\"),n(\"OutboundLink\")],1),t._v(\" 类中还定义了许多其它的曲线，在此便不一一介绍，读者可以自行查看Curves类定义。\")]),t._v(\" \"),n(\"p\",[t._v(\"当然我们也可以创建自己Curve，例如我们定义一个正弦曲线：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ShakeCurve\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Curve\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  double \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"transform\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"double t\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" math\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"sin\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"t \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" math\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"PI \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"h3\",{attrs:{id:\"animationcontroller\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#animationcontroller\"}},[t._v(\"#\")]),t._v(\" AnimationController\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"AnimationController\")]),t._v(\"用于控制动画，它包含动画的启动\"),n(\"code\",[t._v(\"forward()\")]),t._v(\"、停止\"),n(\"code\",[t._v(\"stop()\")]),t._v(\" 、反向播放 \"),n(\"code\",[t._v(\"reverse()\")]),t._v(\"等方法。\"),n(\"code\",[t._v(\"AnimationController\")]),t._v(\"会在动画的每一帧，就会生成一个新的值。默认情况下，\"),n(\"code\",[t._v(\"AnimationController\")]),t._v(\"在给定的时间段内线性的生成从0.0到1.0（默认区间）的数字。 例如，下面代码创建一个\"),n(\"code\",[t._v(\"Animation\")]),t._v(\"对象（但不会启动动画）：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" AnimationController controller \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimationController\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    duration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"milliseconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2000\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" vsync\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"AnimationController\")]),t._v(\"生成数字的区间可以通过\"),n(\"code\",[t._v(\"lowerBound\")]),t._v(\"和\"),n(\"code\",[t._v(\"upperBound\")]),t._v(\"来指定，如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" AnimationController controller \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimationController\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \\n duration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"milliseconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2000\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n lowerBound\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"10.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n upperBound\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n vsync\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"AnimationController\")]),t._v(\"派生自\"),n(\"code\",[t._v(\"Animation<double>\")]),t._v(\"，因此可以在需要\"),n(\"code\",[t._v(\"Animation\")]),t._v(\"对象的任何地方使用。 但是，\"),n(\"code\",[t._v(\"AnimationController\")]),t._v(\"具有控制动画的其他方法，例如\"),n(\"code\",[t._v(\"forward()\")]),t._v(\"方法可以启动正向动画，\"),n(\"code\",[t._v(\"reverse()\")]),t._v(\"可以启动反向动画。在动画开始执行后开始生成动画帧，屏幕每刷新一次就是一个动画帧，在动画的每一帧，会随着根据动画的曲线来生成当前的动画值（\"),n(\"code\",[t._v(\"Animation.value\")]),t._v(\"），然后根据当前的动画值去构建UI，当所有动画帧依次触发时，动画值会依次改变，所以构建的UI也会依次变化，所以最终我们可以看到一个完成的动画。 另外在动画的每一帧，\"),n(\"code\",[t._v(\"Animation\")]),t._v(\"对象会调用其帧监听器，等动画状态发生改变时（如动画结束）会调用状态改变监听器。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"duration\")]),t._v(\"表示动画执行的时长，通过它我们可以控制动画的速度。\")]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[n(\"strong\",[t._v(\"注意\")]),t._v(\"： 在某些情况下，动画值可能会超出\"),n(\"code\",[t._v(\"AnimationController\")]),t._v(\"的[0.0，1.0]的范围，这取决于具体的曲线。例如，\"),n(\"code\",[t._v(\"fling()\")]),t._v(\"函数可以根据我们手指滑动（甩出）的速度(velocity)、力量(force)等来模拟一个手指甩出动画，因此它的动画值可以在[0.0，1.0]范围之外 。也就是说，根据选择的曲线，\"),n(\"code\",[t._v(\"CurvedAnimation\")]),t._v(\"的输出可以具有比输入更大的范围。例如，Curves.elasticIn等弹性曲线会生成大于或小于默认范围的值。\")])]),t._v(\" \"),n(\"h4\",{attrs:{id:\"ticker\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#ticker\"}},[t._v(\"#\")]),t._v(\" Ticker\")]),t._v(\" \"),n(\"p\",[t._v(\"当创建一个\"),n(\"code\",[t._v(\"AnimationController\")]),t._v(\"时，需要传递一个\"),n(\"code\",[t._v(\"vsync\")]),t._v(\"参数，它接收一个\"),n(\"code\",[t._v(\"TickerProvider\")]),t._v(\"类型的对象，它的主要职责是创建\"),n(\"code\",[t._v(\"Ticker\")]),t._v(\"，定义如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"abstract\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TickerProvider\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//通过一个回调创建一个Ticker\")]),t._v(\"\\n  Ticker \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createTicker\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"TickerCallback onTick\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"Flutter应用在启动时都会绑定一个\"),n(\"code\",[t._v(\"SchedulerBinding\")]),t._v(\"，通过\"),n(\"code\",[t._v(\"SchedulerBinding\")]),t._v(\"可以给每一次屏幕刷新添加回调，而\"),n(\"code\",[t._v(\"Ticker\")]),t._v(\"就是通过\"),n(\"code\",[t._v(\"SchedulerBinding\")]),t._v(\"来添加屏幕刷新回调，这样一来，每次屏幕刷新都会调用\"),n(\"code\",[t._v(\"TickerCallback\")]),t._v(\"。使用\"),n(\"code\",[t._v(\"Ticker\")]),t._v(\"(而不是\"),n(\"code\",[t._v(\"Timer\")]),t._v(\")来驱动动画会防止屏幕外动画（动画的UI不在当前屏幕时，如锁屏时）消耗不必要的资源，因为Flutter中屏幕刷新时会通知到绑定的\"),n(\"code\",[t._v(\"SchedulerBinding\")]),t._v(\"，而\"),n(\"code\",[t._v(\"Ticker\")]),t._v(\"是受\"),n(\"code\",[t._v(\"SchedulerBinding\")]),t._v(\"驱动的，由于锁屏后屏幕会停止刷新，所以\"),n(\"code\",[t._v(\"Ticker\")]),t._v(\"就不会再触发。\")]),t._v(\" \"),n(\"p\",[t._v(\"通常我们会将\"),n(\"code\",[t._v(\"SingleTickerProviderStateMixin\")]),t._v(\"添加到\"),n(\"code\",[t._v(\"State\")]),t._v(\"的定义中，然后将State对象作为\"),n(\"code\",[t._v(\"vsync\")]),t._v(\"的值，这在后面的例子中可以见到。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"tween\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#tween\"}},[t._v(\"#\")]),t._v(\" Tween\")]),t._v(\" \"),n(\"p\",[t._v(\"默认情况下，\"),n(\"code\",[t._v(\"AnimationController\")]),t._v(\"对象值的范围是[0.0，1.0]。如果我们需要构建UI的动画值在不同的范围或不同的数据类型，则可以使用\"),n(\"code\",[t._v(\"Tween\")]),t._v(\"来添加映射以生成不同的范围或数据类型的值。例如，像下面示例，\"),n(\"code\",[t._v(\"Tween\")]),t._v(\"生成[-200.0，0.0]的值：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Tween doubleTween \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Tween\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"begin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" end\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"Tween\")]),t._v(\"构造函数需要\"),n(\"code\",[t._v(\"begin\")]),t._v(\"和\"),n(\"code\",[t._v(\"end\")]),t._v(\"两个参数。\"),n(\"code\",[t._v(\"Tween\")]),t._v(\"的唯一职责就是定义从输入范围到输出范围的映射。输入范围通常为[0.0，1.0]，但这不是必须的，我们可以自定义需要的范围。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"Tween\")]),t._v(\"继承自\"),n(\"code\",[t._v(\"Animatable<T>\")]),t._v(\"，而不是继承自\"),n(\"code\",[t._v(\"Animation<T>\")]),t._v(\"，\"),n(\"code\",[t._v(\"Animatable\")]),t._v(\"中主要定义动画值的映射规则。\")]),t._v(\" \"),n(\"p\",[t._v(\"下面我们看一个ColorTween将动画输入范围映射为两种颜色值之间过渡输出的例子：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Tween colorTween \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ColorTween\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"begin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"transparent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" end\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"black54\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"Tween\")]),t._v(\"对象不存储任何状态，相反，它提供了\"),n(\"code\",[t._v(\"evaluate(Animation<double> animation)\")]),t._v(\"方法，它可以获取动画当前映射值。 \"),n(\"code\",[t._v(\"Animation\")]),t._v(\"对象的当前值可以通过\"),n(\"code\",[t._v(\"value()\")]),t._v(\"方法取到。\"),n(\"code\",[t._v(\"evaluate\")]),t._v(\"函数还执行一些其它处理，例如分别确保在动画值为0.0和1.0时返回开始和结束状态。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"tween-animate\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#tween-animate\"}},[t._v(\"#\")]),t._v(\" Tween.animate\")]),t._v(\" \"),n(\"p\",[t._v(\"要使用Tween对象，需要调用其\"),n(\"code\",[t._v(\"animate()\")]),t._v(\"方法，然后传入一个控制器对象。例如，以下代码在500毫秒内生成从0到255的整数值。\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" AnimationController controller \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimationController\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    duration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"milliseconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"500\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" vsync\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\nAnimation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"int\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" alpha \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"IntTween\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"begin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" end\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"255\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"animate\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"注意\"),n(\"code\",[t._v(\"animate()\")]),t._v(\"返回的是一个\"),n(\"code\",[t._v(\"Animation\")]),t._v(\"，而不是一个\"),n(\"code\",[t._v(\"Animatable\")]),t._v(\"。\")]),t._v(\" \"),n(\"p\",[t._v(\"以下示例构建了一个控制器、一条曲线和一个Tween：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" AnimationController controller \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimationController\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    duration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"milliseconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"500\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" vsync\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Animation curve \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CurvedAnimation\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"parent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" curve\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Curves\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"easeOut\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\nAnimation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"int\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" alpha \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"IntTween\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"begin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" end\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"255\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"animate\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"curve\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/171.4f0e7d6b.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[171],{821:function(t,a,s){\"use strict\";s.r(a);var n=s(45),e=Object(n.a)({},(function(){var t=this,a=t.$createElement,s=t._self._c||a;return s(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[s(\"h1\",{attrs:{id:\"_9-3-自定义路由切换动画\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_9-3-自定义路由切换动画\"}},[t._v(\"#\")]),t._v(\" 9.3 自定义路由切换动画\")]),t._v(\" \"),s(\"p\",[t._v(\"我们在第二章“路由管理”一节中讲过：Material组件库中提供了一个\"),s(\"code\",[t._v(\"MaterialPageRoute\")]),t._v(\"组件，它可以使用和平台风格一致的路由切换动画，如在iOS上会左右滑动切换，而在Android上会上下滑动切换。现在，我们如果在Android上也想使用左右切换风格，该怎么做？一个简单的作法是可以直接使用\"),s(\"code\",[t._v(\"CupertinoPageRoute\")]),t._v(\"，如：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\" Navigator\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"push\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CupertinoPageRoute\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"  \\n   builder\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"PageB\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"p\",[s(\"code\",[t._v(\"CupertinoPageRoute\")]),t._v(\"是Cupertino组件库提供的iOS风格的路由切换组件，它实现的就是左右滑动切换。那么我们如何来自定义路由切换动画呢？答案就是\"),s(\"code\",[t._v(\"PageRouteBuilder\")]),t._v(\"。下面我们来看看如何使用\"),s(\"code\",[t._v(\"PageRouteBuilder\")]),t._v(\"来自定义路由切换动画。例如我们想以渐隐渐入动画来实现路由过渡，实现代码如下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"Navigator\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"push\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"PageRouteBuilder\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    transitionDuration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"milliseconds\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"500\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//动画时间为500毫秒\")]),t._v(\"\\n    pageBuilder\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Animation animation\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        Animation secondaryAnimation\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FadeTransition\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//使用渐隐渐入过渡,\")]),t._v(\"\\n        opacity\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"PageB\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//路由B\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"我们可以看到\"),s(\"code\",[t._v(\"pageBuilder\")]),t._v(\" 有一个\"),s(\"code\",[t._v(\"animation\")]),t._v(\"参数，这是Flutter路由管理器提供的，在路由切换时\"),s(\"code\",[t._v(\"pageBuilder\")]),t._v(\"在每个动画帧都会被回调，因此我们可以通过\"),s(\"code\",[t._v(\"animation\")]),t._v(\"对象来自定义过渡动画。\")]),t._v(\" \"),s(\"p\",[t._v(\"无论是\"),s(\"code\",[t._v(\"MaterialPageRoute\")]),t._v(\"、\"),s(\"code\",[t._v(\"CupertinoPageRoute\")]),t._v(\"，还是\"),s(\"code\",[t._v(\"PageRouteBuilder\")]),t._v(\"，它们都继承自PageRoute类，而\"),s(\"code\",[t._v(\"PageRouteBuilder\")]),t._v(\"其实只是\"),s(\"code\",[t._v(\"PageRoute\")]),t._v(\"的一个包装，我们可以直接继承\"),s(\"code\",[t._v(\"PageRoute\")]),t._v(\"类来实现自定义路由，上面的例子可以通过如下方式实现：\")]),t._v(\" \"),s(\"ol\",[s(\"li\",[s(\"p\",[t._v(\"定义一个路由类\"),s(\"code\",[t._v(\"FadeRoute\")])]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FadeRoute\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"PageRoute\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FadeRoute\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"builder\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"transitionDuration \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"milliseconds\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"opaque \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"barrierDismissible \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"barrierColor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"barrierLabel\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"maintainState \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" WidgetBuilder builder\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Duration transitionDuration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" bool opaque\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" bool barrierDismissible\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Color barrierColor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String barrierLabel\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" bool maintainState\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"buildPage\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Animation\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" animation\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      Animation\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" secondaryAnimation\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"buildTransitions\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Animation\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" animation\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      Animation\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" secondaryAnimation\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Widget child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n     \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FadeTransition\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \\n       opacity\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n       child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n     \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])])]),t._v(\" \"),s(\"li\",[s(\"p\",[t._v(\"使用\"),s(\"code\",[t._v(\"FadeRoute\")])]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"Navigator\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"push\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FadeRoute\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"PageB\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])])])]),t._v(\" \"),s(\"p\",[t._v(\"虽然上面的两种方法都可以实现自定义切换动画，但实际使用时应优先考虑使用PageRouteBuilder，这样无需定义一个新的路由类，使用起来会比较方便。但是有些时候\"),s(\"code\",[t._v(\"PageRouteBuilder\")]),t._v(\"是不能满足需求的，例如在应用过渡动画时我们需要读取当前路由的一些属性，这时就只能通过继承\"),s(\"code\",[t._v(\"PageRoute\")]),t._v(\"的方式了，举个例子，假如我们只想在打开新路由时应用动画，而在返回时不使用动画，那么我们在构建过渡动画时就必须判断当前路由\"),s(\"code\",[t._v(\"isActive\")]),t._v(\"属性是否为\"),s(\"code\",[t._v(\"true\")]),t._v(\"，代码如下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\nWidget \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"buildTransitions\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Animation\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" animation\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    Animation\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" secondaryAnimation\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Widget child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//当前路由被激活，是打开新路由\")]),t._v(\"\\n \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"isActive\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FadeTransition\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n     opacity\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n     child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//是返回，则不应用过渡动画\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"padding\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"zero\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"关于路由参数的详细信息读者可以自行查阅API文档，比较简单，不再赘述。\")])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/172.ae542eee.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[172],{823:function(t,r,e){\"use strict\";e.r(r);var l=e(45),u=Object(l.a)({},(function(){var t=this,r=t.$createElement,e=t._self._c||r;return e(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[e(\"h1\",{attrs:{id:\"前言\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#前言\"}},[t._v(\"#\")]),t._v(\" 前言\")]),t._v(\" \"),e(\"p\",[t._v(\"本书是第一本系统介绍Flutter技术的中文书籍，它是Flutter中文社区发起的开源项目之一，旨在帮助开发者入门，系统地、循序渐进的了解Flutter。\")]),t._v(\" \"),e(\"blockquote\",[e(\"p\",[t._v(\"本书官网访问量较大，由于服务器配置有限，所以某些时段访问本站可能略有延迟，如果延迟较久，读者可以直接在\"),e(\"a\",{attrs:{href:\"https://github.com/flutterchina/flutter-in-action/blob/master/docs/SUMMARY.md\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Github上阅读本书\"),e(\"OutboundLink\")],1),t._v(\"。\")])]),t._v(\" \"),e(\"h2\",{attrs:{id:\"缘起\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#缘起\"}},[t._v(\"#\")]),t._v(\" 缘起\")]),t._v(\" \"),e(\"p\",[t._v(\"在全球，随着Flutter被越来越多的知名公司应用在自己的商业APP中，Flutter这门新技术也逐渐进入了移动开发者的视野，尤其是当Google在2018年IO大会上发布了第一个Preview版本后，国内刮起来一股学习Flutter的热潮。\")]),t._v(\" \"),e(\"p\",[t._v(\"在Flutter发布之初，当时，我在看完Flutter原理介绍后，就对它产生了浓厚的兴趣。当时笔者身边也一些人比较关注Flutter，我也被经常问到关于Flutter的一些问题，比如Flutter怎么样？和RN有什么区别？Flutter为什么要用Dart？当时也听到了一些批评的声音，比如有些人说Flutter只是重复造轮子，没啥亮点、Flutter最大的缺点就是使用了Dart语言等。在听到这些问题及论调后，我深知这是对Flutter的不了解而造成的，这和当时国内缺乏Flutter中文文档和教程有直接关系，很多人对Flutter的了解都只停留在Google的发布会介绍（有中文翻译）。\")]),t._v(\" \"),e(\"p\",[t._v(\"在笔者深入的了解Flutter后，深知Flutter必将是一个会改变移动开发格局的里程碑级作品，它从设计之初就对性能和开发效率兼顾，并且借鉴了React（一个Web开发框架）的响应式的UI框架设计思想等，总之，很难用一两句话说完Flutter的优点，同时我也很快成为了Flutter的路转粉。\")]),t._v(\" \"),e(\"p\",[t._v(\"为了更好的帮助中国开发者了解这门新技术，我在2018年初开始翻译Flutter官网文档，同年4月份上线了\"),e(\"a\",{attrs:{href:\"https://flutterchina.club/\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Flutter中文网\"),e(\"OutboundLink\")],1),t._v(\"，上线后反响很强烈，Flutter中文网也很快被传播开，百度搜索排名迅速蹿升到前三，截止目前，Flutter中文官网日PV在7万左右，每日独立访问人数近一万多。\")]),t._v(\" \"),e(\"p\",[t._v(\"虽然Flutter中文网给中国开发者提供了很好的第一手了解Flutter的资料，但是笔者还会经常遇到一些对Flutter技术处于围观而不愿尝试的开发者。这主要是因为当时Flutter在国内没有成功案例，再加上新技术都有学习成本，所以即使有文档，也会有一些开发者犹豫是否来学习。为了解决这部分开发者的疑虑，我就想如果能用Flutter开发一个完整的APP发布到应用商店，这样开发者就可以在犹豫的时候可以先实际感受一下Flutter应用，这样有个直观的了解后，就会容易做出判断，为此，我开发了\"),e(\"a\",{attrs:{href:\"https://flutterchina.club/app/gm.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Gitme\"),e(\"OutboundLink\")],1),t._v(\"，它是一个Github客户端，它支持了源码浏览、Issue、Label等Github的大多数功能，到目前为止，通过Gitme登录过Github账号的用户有8000多人，日活用户有1000人。更重要的是，有很多人正是用了Gitme后，才来学Flutter的。\")]),t._v(\" \"),e(\"p\",[t._v(\"无论是做Flutter中文网，还是写Gitme，主要目标都是帮助开发者学习Flutter，同时消除围观开发者疑虑。但当开发者们真正开始动手时，Flutter的生态问题就变得尤为突出。由于在2018年初Flutter刚起步时，很多基础的包和库都是空白，少数已有的一些库也大都是预览版（未到1.0），存在很多bug。这个状况不是一两个人花一两天能搞定的，这是需要聚整个Flutter开发者社群之力，耗费数年时间才可能有所改善。因此，在2018年4月份，我以Flutter中文网名义发起了Flutter开源计划，该计划主要是开发一些常用的包来丰富Flutter生态，帮助开发者提高开发效率。自在github建立\"),e(\"a\",{attrs:{href:\"https://github.com/flutterchina\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Flutter中文开发者社区账号\"),e(\"OutboundLink\")],1),t._v(\"以来，前后开源了dio、cookieJar、flukit等多个项目，而dio在开源两周后，就迅速成为Flutter第三方包中Star排名第一的开源库。\")]),t._v(\" \"),e(\"p\",[t._v(\"虽然做的事情已经够多了，但是仍有一些很有必要去做的事情，由于时间原因，一直被搁置。\")]),t._v(\" \"),e(\"p\",[t._v(\"随着学习Flutter的人越来越多，一部分开发者通过查看官网的文档就能入门，但也有很多开发者感觉学习时仍然有些吃力，主要原因有两个，首先官网的文档主要是为了引导开发者快速上手的，讲的并不是很细；其次是我们虽然翻译了官方文档，但是对于Flutter SDK文档并没有翻译，而在开发中遇到的一些具体问题通常都得去查看SDK文档。所以，要解决这两个问题，必须得有一个系统化的Flutter教程，它不仅可以快速引导开发者入门，而且也能触及到一些细节和原理，最好也能提供一些学习和研究Flutter的方法。因此，如果能有一本能系统地介绍Flutter的书籍便是便是非常棒的！但是，当时没有一本关于Flutter的中文书籍，因此，我便计划写一本能由浅入深、系统介绍Flutter的书。2018年12月，《Flutter实战》完成初稿，并在Github上开源，同时上线了\"),e(\"a\",{attrs:{href:\"https://book.flutterchina.club/\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"《Flutter实战》电子书官网\"),e(\"OutboundLink\")],1),t._v(\" ，至今每天有3000多人在线浏览本书。那为什么不直接出版？如果直接出版，不仅有稿费，而且也能保护知识产权，而直接开源，不就只能当雷锋了？其实，无论是做中文网、写Gitme、做Flutter开源项目，我的出发点都是为了能帮助中国开发者了解、学习Flutter，而这是一件非常有意义的事，只要坚持做对别人有价值的事，那么上帝迟早会奖赏你；当然我们也在网站中留了打赏的按钮，如果读者觉得有帮助，可以扫码打赏，请笔者喝一杯咖啡。另外由于成书比较仓促，当时书中也有很多错误，开源后，有很多读者提PR来纠正书中的错别字，时至今日，有78名开发者为本书提过PR，我在此，衷心的感谢你们。\")]),t._v(\" \"),e(\"p\",[t._v(\"起初，我没有出版实体书的打算，当时我以为开发者直接通过在线访问本书官网即方便又不用付费，何乐而不为。但在本书上线后，很多读者来添加微信好友，表示非常有收益，很期待出版纸质书，甚至有比较热心的读者想提前付定金预定！我理解，这是大家对我所做之事的认可和鼓励。考虑到，确实有一部分读者，尤其是还没毕业的同学，可能更喜欢通过书籍去学习，为此，我已经和机械工业出版社合作，目前本书纸质版正在出版中，敬请关注。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"本书组织结构\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本书组织结构\"}},[t._v(\"#\")]),t._v(\" 本书组织结构\")]),t._v(\" \"),e(\"p\",[t._v(\"本书采用由浅入深的方式介绍Flutter技术原理，分为三篇，总计15章，各篇的主要内容如下。\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"第一篇，入门篇（第1章~第7章），包括Flutter技术的出现背景和简介、Flutter的各种类型的Widget以及如何构建UI。通过学习本章，读者可以掌握如何使用Flutter来构建UI界面。\")]),t._v(\" \"),e(\"li\",[t._v(\"第二篇，进阶篇（第8章~第14章），包括Flutter中的事件机制、动画、自定义组件、文件和网络、插件、国际化以及Flutter核心原理等。通过本章内容，读者可以对Flutter整体构建及原理有一个深入的认识。\")]),t._v(\" \"),e(\"li\",[t._v(\"第三篇，实例篇（第15章），本章主要通过一个简版的Github APP来将前面介绍的内容串起来，让开发者对一个完整的Flutter APP开发流有个了解。\")])]),t._v(\" \"),e(\"h2\",{attrs:{id:\"本书特色\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本书特色\"}},[t._v(\"#\")]),t._v(\" 本书特色\")]),t._v(\" \"),e(\"p\",[t._v(\"笔者在大学时读过候捷（真名侯俊杰）写的一些C++相关书籍，在他的《深入浅出MFC》一书中，有一句话我印象像非常深 “唯有深入，方能浅出”。我非常认同这句话，对于一门技术，只有了解的深入，才能用最浅显、通俗的话语描述出。我在写作本书时，深入浅出就是一个主要目标。所以，本书的目标不仅是想告诉读者如何使用Flutter，而且也非常关注各个知识点的底层实现以及设计思想。从本书章节划分上来看，入门篇为“浅出”，进阶篇则是“深入”。另外由于PC客户端开发、移动开发、Web开发这些经验我都有，而Flutter本质上是一个UI系统，而UI系统的设计和实现在”大前端“下有很多相通之处，所以在本书中的一些知识点我也会对比一些其他UI系统（主要是Android或Web）相应的实现，便于有相关开发经验的读者对比理解。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"本书读者对象\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本书读者对象\"}},[t._v(\"#\")]),t._v(\" 本书读者对象\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"读者至少熟悉一种编程语言。\")]),t._v(\" \"),e(\"li\",[t._v(\"读者最好接触过PC客户端、移动开发或Web前端开发中的一种。\")]),t._v(\" \"),e(\"li\",[t._v(\"本书不适合做为编程的入门读物。\")])]),t._v(\" \"),e(\"h2\",{attrs:{id:\"关于随书源码\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#关于随书源码\"}},[t._v(\"#\")]),t._v(\" 关于随书源码\")]),t._v(\" \"),e(\"p\",[t._v(\"由于篇幅所限，本书中大多数示例代码都只是部分核心代码，读者可以去\"),e(\"a\",{attrs:{href:\"https://github.com/wendux/flutter_in_action_source_code\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"这里\"),e(\"OutboundLink\")],1),t._v(\"查看下载。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"致谢\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#致谢\"}},[t._v(\"#\")]),t._v(\" 致谢\")]),t._v(\" \"),e(\"p\",[t._v(\"感谢一直以来支持Flutter中文网、Flutter开源项目的人以及所有对本书提过PR的人，正是因为有你们，才有这本书。另外尤其感谢给本书打赏过的同学，你们的支持给了我很大的鼓励。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"权益\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#权益\"}},[t._v(\"#\")]),t._v(\" 权益\")]),t._v(\" \"),e(\"p\",[t._v(\"最后，知识是应该付费的，创作不易，开源不等于免费，如果您是本书读者并手头宽裕，可以点击下面打赏按钮打赏；当然，如果您囊中羞涩，您也可以阅读本书，但我对您有个小小的要求，希望您在阅读的过程中能积极参与到本书的纠错以及未完成内容的创作上来，也算是有所付出。\")]),t._v(\" \"),e(\"p\",[t._v(\"近来在网上发现很多\"),e(\"strong\",[t._v(\"原封不动复制本书\")]),t._v(\"的镜像网站和大量复制或\"),e(\"strong\",[t._v(\"引用了本书但未注明出处\")]),t._v(\"的博客、文章甚至书籍；对此，笔者在此声明，本书著作权归wendux所有，任何组织或个人在未经授权的情况下复制、拷贝、抄袭本书用于商业目的，笔者保留追究其法律责任的权利。如果是非商业目的的转载和引用，请注明出处并附上本书网址。另外如有出版机构愿意为本书出版实体书或者想转载本书内容，亦或是想合作，请加微信Demons-du.\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"勘误\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#勘误\"}},[t._v(\"#\")]),t._v(\" 勘误\")]),t._v(\" \"),e(\"p\",[t._v(\"由于Flutter SDK在不断更新，本书中的部分内容（如类的继承关系、参数等）可能会和新版本的Flutter不一致，读者以最新的Flutter SDK为准。另外，由于时间仓促，书中难免有错误之处，如果您发现本书中的错误，欢迎点击右上角的”编辑按钮“，提PR。如果您想一起参与本书创作，可以参考\"),e(\"a\",{attrs:{href:\"https://github.com/flutterchina/flutter-in-action#%E8%B4%A1%E7%8C%AE%E9%A1%BB%E7%9F%A5\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"《Flutter实战》贡献指南\"),e(\"OutboundLink\")],1),t._v(\"。\")])])}),[],!1,null,null,null);r.default=u.exports}}]);"
  },
  {
    "path": "docs/assets/js/173.c985ca5c.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[173],{731:function(t,r,e){\"use strict\";e.r(r);var l=e(45),u=Object(l.a)({},(function(){var t=this,r=t.$createElement,e=t._self._c||r;return e(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[e(\"h1\",{attrs:{id:\"前言\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#前言\"}},[t._v(\"#\")]),t._v(\" 前言\")]),t._v(\" \"),e(\"p\",[t._v(\"本书是第一本系统介绍Flutter技术的中文书籍，它是Flutter中文社区发起的开源项目之一，旨在帮助开发者入门，系统地、循序渐进的了解Flutter。\")]),t._v(\" \"),e(\"blockquote\",[e(\"p\",[t._v(\"本书官网访问量较大，由于服务器配置有限，所以某些时段访问本站可能略有延迟，如果延迟较久，读者可以直接在\"),e(\"a\",{attrs:{href:\"https://github.com/flutterchina/flutter-in-action/blob/master/docs/SUMMARY.md\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Github上阅读本书\"),e(\"OutboundLink\")],1),t._v(\"。\")])]),t._v(\" \"),e(\"h2\",{attrs:{id:\"缘起\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#缘起\"}},[t._v(\"#\")]),t._v(\" 缘起\")]),t._v(\" \"),e(\"p\",[t._v(\"在全球，随着Flutter被越来越多的知名公司应用在自己的商业APP中，Flutter这门新技术也逐渐进入了移动开发者的视野，尤其是当Google在2018年IO大会上发布了第一个Preview版本后，国内刮起来一股学习Flutter的热潮。\")]),t._v(\" \"),e(\"p\",[t._v(\"在Flutter发布之初，当时，我在看完Flutter原理介绍后，就对它产生了浓厚的兴趣。当时笔者身边也一些人比较关注Flutter，我也被经常问到关于Flutter的一些问题，比如Flutter怎么样？和RN有什么区别？Flutter为什么要用Dart？当时也听到了一些批评的声音，比如有些人说Flutter只是重复造轮子，没啥亮点、Flutter最大的缺点就是使用了Dart语言等。在听到这些问题及论调后，我深知这是对Flutter的不了解而造成的，这和当时国内缺乏Flutter中文文档和教程有直接关系，很多人对Flutter的了解都只停留在Google的发布会介绍（有中文翻译）。\")]),t._v(\" \"),e(\"p\",[t._v(\"在笔者深入的了解Flutter后，深知Flutter必将是一个会改变移动开发格局的里程碑级作品，它从设计之初就对性能和开发效率兼顾，并且借鉴了React（一个Web开发框架）的响应式的UI框架设计思想等，总之，很难用一两句话说完Flutter的优点，同时我也很快成为了Flutter的路转粉。\")]),t._v(\" \"),e(\"p\",[t._v(\"为了更好的帮助中国开发者了解这门新技术，我在2018年初开始翻译Flutter官网文档，同年4月份上线了\"),e(\"a\",{attrs:{href:\"https://flutterchina.club/\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Flutter中文网\"),e(\"OutboundLink\")],1),t._v(\"，上线后反响很强烈，Flutter中文网也很快被传播开，百度搜索排名迅速蹿升到前三，截止目前，Flutter中文官网日PV在7万左右，每日独立访问人数近一万多。\")]),t._v(\" \"),e(\"p\",[t._v(\"虽然Flutter中文网给中国开发者提供了很好的第一手了解Flutter的资料，但是笔者还会经常遇到一些对Flutter技术处于围观而不愿尝试的开发者。这主要是因为当时Flutter在国内没有成功案例，再加上新技术都有学习成本，所以即使有文档，也会有一些开发者犹豫是否来学习。为了解决这部分开发者的疑虑，我就想如果能用Flutter开发一个完整的APP发布到应用商店，这样开发者就可以在犹豫的时候可以先实际感受一下Flutter应用，这样有个直观的了解后，就会容易做出判断，为此，我开发了\"),e(\"a\",{attrs:{href:\"https://flutterchina.club/app/gm.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Gitme\"),e(\"OutboundLink\")],1),t._v(\"，它是一个Github客户端，它支持了源码浏览、Issue、Label等Github的大多数功能，到目前为止，通过Gitme登录过Github账号的用户有8000多人，日活用户有1000人。更重要的是，有很多人正是用了Gitme后，才来学Flutter的。\")]),t._v(\" \"),e(\"p\",[t._v(\"无论是做Flutter中文网，还是写Gitme，主要目标都是帮助开发者学习Flutter，同时消除围观开发者疑虑。但当开发者们真正开始动手时，Flutter的生态问题就变得尤为突出。由于在2018年初Flutter刚起步时，很多基础的包和库都是空白，少数已有的一些库也大都是预览版（未到1.0），存在很多bug。这个状况不是一两个人花一两天能搞定的，这是需要聚整个Flutter开发者社群之力，耗费数年时间才可能有所改善。因此，在2018年4月份，我以Flutter中文网名义发起了Flutter开源计划，该计划主要是开发一些常用的包来丰富Flutter生态，帮助开发者提高开发效率。自在github建立\"),e(\"a\",{attrs:{href:\"https://github.com/flutterchina\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Flutter中文开发者社区账号\"),e(\"OutboundLink\")],1),t._v(\"以来，前后开源了dio、cookieJar、flukit等多个项目，而dio在开源两周后，就迅速成为Flutter第三方包中Star排名第一的开源库。\")]),t._v(\" \"),e(\"p\",[t._v(\"虽然做的事情已经够多了，但是仍有一些很有必要去做的事情，由于时间原因，一直被搁置。\")]),t._v(\" \"),e(\"p\",[t._v(\"随着学习Flutter的人越来越多，一部分开发者通过查看官网的文档就能入门，但也有很多开发者感觉学习时仍然有些吃力，主要原因有两个，首先官网的文档主要是为了引导开发者快速上手的，讲的并不是很细；其次是我们虽然翻译了官方文档，但是对于Flutter SDK文档并没有翻译，而在开发中遇到的一些具体问题通常都得去查看SDK文档。所以，要解决这两个问题，必须得有一个系统化的Flutter教程，它不仅可以快速引导开发者入门，而且也能触及到一些细节和原理，最好也能提供一些学习和研究Flutter的方法。因此，如果能有一本能系统地介绍Flutter的书籍便是便是非常棒的！但是，当时没有一本关于Flutter的中文书籍，因此，我便计划写一本能由浅入深、系统介绍Flutter的书。2018年12月，《Flutter实战》完成初稿，并在Github上开源，同时上线了\"),e(\"a\",{attrs:{href:\"https://book.flutterchina.club/\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"《Flutter实战》电子书官网\"),e(\"OutboundLink\")],1),t._v(\" ，至今每天有3000多人在线浏览本书。那为什么不直接出版？如果直接出版，不仅有稿费，而且也能保护知识产权，而直接开源，不就只能当雷锋了？其实，无论是做中文网、写Gitme、做Flutter开源项目，我的出发点都是为了能帮助中国开发者了解、学习Flutter，而这是一件非常有意义的事，只要坚持做对别人有价值的事，那么上帝迟早会奖赏你；当然我们也在网站中留了打赏的按钮，如果读者觉得有帮助，可以扫码打赏，请笔者喝一杯咖啡。另外由于成书比较仓促，当时书中也有很多错误，开源后，有很多读者提PR来纠正书中的错别字，时至今日，有78名开发者为本书提过PR，我在此，衷心的感谢你们。\")]),t._v(\" \"),e(\"p\",[t._v(\"起初，我没有出版实体书的打算，当时我以为开发者直接通过在线访问本书官网即方便又不用付费，何乐而不为。但在本书上线后，很多读者来添加微信好友，表示非常有收益，很期待出版纸质书，甚至有比较热心的读者想提前付定金预定！我理解，这是大家对我所做之事的认可和鼓励。考虑到，确实有一部分读者，尤其是还没毕业的同学，可能更喜欢通过书籍去学习，为此，我已经和机械工业出版社合作，目前本书纸质版正在出版中，敬请关注。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"本书组织结构\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本书组织结构\"}},[t._v(\"#\")]),t._v(\" 本书组织结构\")]),t._v(\" \"),e(\"p\",[t._v(\"本书采用由浅入深的方式介绍Flutter技术原理，分为三篇，总计15章，各篇的主要内容如下。\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"第一篇，入门篇（第1章~第7章），包括Flutter技术的出现背景和简介、Flutter的各种类型的Widget以及如何构建UI。通过学习本章，读者可以掌握如何使用Flutter来构建UI界面。\")]),t._v(\" \"),e(\"li\",[t._v(\"第二篇，进阶篇（第8章~第14章），包括Flutter中的事件机制、动画、自定义组件、文件和网络、插件、国际化以及Flutter核心原理等。通过本章内容，读者可以对Flutter整体构建及原理有一个深入的认识。\")]),t._v(\" \"),e(\"li\",[t._v(\"第三篇，实例篇（第15章），本章主要通过一个简版的Github APP来将前面介绍的内容串起来，让开发者对一个完整的Flutter APP开发流有个了解。\")])]),t._v(\" \"),e(\"h2\",{attrs:{id:\"本书特色\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本书特色\"}},[t._v(\"#\")]),t._v(\" 本书特色\")]),t._v(\" \"),e(\"p\",[t._v(\"笔者在大学时读过候捷（真名侯俊杰）写的一些C++相关书籍，在他的《深入浅出MFC》一书中，有一句话我印象像非常深 “唯有深入，方能浅出”。我非常认同这句话，对于一门技术，只有了解的深入，才能用最浅显、通俗的话语描述出。我在写作本书时，深入浅出就是一个主要目标。所以，本书的目标不仅是想告诉读者如何使用Flutter，而且也非常关注各个知识点的底层实现以及设计思想。从本书章节划分上来看，入门篇为“浅出”，进阶篇则是“深入”。另外由于PC客户端开发、移动开发、Web开发这些经验我都有，而Flutter本质上是一个UI系统，而UI系统的设计和实现在”大前端“下有很多相通之处，所以在本书中的一些知识点我也会对比一些其他UI系统（主要是Android或Web）相应的实现，便于有相关开发经验的读者对比理解。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"本书读者对象\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本书读者对象\"}},[t._v(\"#\")]),t._v(\" 本书读者对象\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"读者至少熟悉一种编程语言。\")]),t._v(\" \"),e(\"li\",[t._v(\"读者最好接触过PC客户端、移动开发或Web前端开发中的一种。\")]),t._v(\" \"),e(\"li\",[t._v(\"本书不适合做为编程的入门读物。\")])]),t._v(\" \"),e(\"h2\",{attrs:{id:\"关于随书源码\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#关于随书源码\"}},[t._v(\"#\")]),t._v(\" 关于随书源码\")]),t._v(\" \"),e(\"p\",[t._v(\"由于篇幅所限，本书中大多数示例代码都只是部分核心代码，读者可以去\"),e(\"a\",{attrs:{href:\"https://github.com/wendux/flutter_in_action_source_code\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"这里\"),e(\"OutboundLink\")],1),t._v(\"查看下载。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"致谢\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#致谢\"}},[t._v(\"#\")]),t._v(\" 致谢\")]),t._v(\" \"),e(\"p\",[t._v(\"感谢一直以来支持Flutter中文网、Flutter开源项目的人以及所有对本书提过PR的人，正是因为有你们，才有这本书。另外尤其感谢给本书打赏过的同学，你们的支持给了我很大的鼓励。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"权益\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#权益\"}},[t._v(\"#\")]),t._v(\" 权益\")]),t._v(\" \"),e(\"p\",[t._v(\"最后，知识是应该付费的，创作不易，开源不等于免费，如果您是本书读者并手头宽裕，可以点击下面打赏按钮打赏；当然，如果您囊中羞涩，您也可以阅读本书，但我对您有个小小的要求，希望您在阅读的过程中能积极参与到本书的纠错以及未完成内容的创作上来，也算是有所付出。\")]),t._v(\" \"),e(\"p\",[t._v(\"近来在网上发现很多\"),e(\"strong\",[t._v(\"原封不动复制本书\")]),t._v(\"的镜像网站和大量复制或\"),e(\"strong\",[t._v(\"引用了本书但未注明出处\")]),t._v(\"的博客、文章甚至书籍；对此，笔者在此声明，本书著作权归wendux所有，任何组织或个人在未经授权的情况下复制、拷贝、抄袭本书用于商业目的，笔者保留追究其法律责任的权利。如果是非商业目的的转载和引用，请注明出处并附上本书网址。另外如有出版机构愿意为本书出版实体书或者想转载本书内容，亦或是想合作，请加微信Demons-du.\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"勘误\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#勘误\"}},[t._v(\"#\")]),t._v(\" 勘误\")]),t._v(\" \"),e(\"p\",[t._v(\"由于Flutter SDK在不断更新，本书中的部分内容（如类的继承关系、参数等）可能会和新版本的Flutter不一致，读者以最新的Flutter SDK为准。另外，由于时间仓促，书中难免有错误之处，如果您发现本书中的错误，欢迎点击右上角的”编辑按钮“，提PR。如果您想一起参与本书创作，可以参考\"),e(\"a\",{attrs:{href:\"https://github.com/flutterchina/flutter-in-action#%E8%B4%A1%E7%8C%AE%E9%A1%BB%E7%9F%A5\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"《Flutter实战》贡献指南\"),e(\"OutboundLink\")],1),t._v(\"。\")])])}),[],!1,null,null,null);r.default=u.exports}}]);"
  },
  {
    "path": "docs/assets/js/174.ece20a6a.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[174],{824:function(t,r,e){\"use strict\";e.r(r);var l=e(45),u=Object(l.a)({},(function(){var t=this,r=t.$createElement,e=t._self._c||r;return e(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[e(\"h1\",{attrs:{id:\"前言\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#前言\"}},[t._v(\"#\")]),t._v(\" 前言\")]),t._v(\" \"),e(\"p\",[t._v(\"本书是第一本系统介绍Flutter技术的中文书籍，它是Flutter中文社区发起的开源项目之一，旨在帮助开发者入门，系统地、循序渐进的了解Flutter。\")]),t._v(\" \"),e(\"blockquote\",[e(\"p\",[t._v(\"本书官网访问量较大，由于服务器配置有限，所以某些时段访问本站可能略有延迟，如果延迟较久，读者可以直接在\"),e(\"a\",{attrs:{href:\"https://github.com/flutterchina/flutter-in-action/blob/master/docs/SUMMARY.md\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Github上阅读本书\"),e(\"OutboundLink\")],1),t._v(\"。\")])]),t._v(\" \"),e(\"h2\",{attrs:{id:\"缘起\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#缘起\"}},[t._v(\"#\")]),t._v(\" 缘起\")]),t._v(\" \"),e(\"p\",[t._v(\"在全球，随着Flutter被越来越多的知名公司应用在自己的商业APP中，Flutter这门新技术也逐渐进入了移动开发者的视野，尤其是当Google在2018年IO大会上发布了第一个Preview版本后，国内刮起来一股学习Flutter的热潮。\")]),t._v(\" \"),e(\"p\",[t._v(\"在Flutter发布之初，当时，我在看完Flutter原理介绍后，就对它产生了浓厚的兴趣。当时笔者身边也一些人比较关注Flutter，我也被经常问到关于Flutter的一些问题，比如Flutter怎么样？和RN有什么区别？Flutter为什么要用Dart？当时也听到了一些批评的声音，比如有些人说Flutter只是重复造轮子，没啥亮点、Flutter最大的缺点就是使用了Dart语言等。在听到这些问题及论调后，我深知这是对Flutter的不了解而造成的，这和当时国内缺乏Flutter中文文档和教程有直接关系，很多人对Flutter的了解都只停留在Google的发布会介绍（有中文翻译）。\")]),t._v(\" \"),e(\"p\",[t._v(\"在笔者深入的了解Flutter后，深知Flutter必将是一个会改变移动开发格局的里程碑级作品，它从设计之初就对性能和开发效率兼顾，并且借鉴了React（一个Web开发框架）的响应式的UI框架设计思想等，总之，很难用一两句话说完Flutter的优点，同时我也很快成为了Flutter的路转粉。\")]),t._v(\" \"),e(\"p\",[t._v(\"为了更好的帮助中国开发者了解这门新技术，我在2018年初开始翻译Flutter官网文档，同年4月份上线了\"),e(\"a\",{attrs:{href:\"https://flutterchina.club/\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Flutter中文网\"),e(\"OutboundLink\")],1),t._v(\"，上线后反响很强烈，Flutter中文网也很快被传播开，百度搜索排名迅速蹿升到前三，截止目前，Flutter中文官网日PV在7万左右，每日独立访问人数近一万多。\")]),t._v(\" \"),e(\"p\",[t._v(\"虽然Flutter中文网给中国开发者提供了很好的第一手了解Flutter的资料，但是笔者还会经常遇到一些对Flutter技术处于围观而不愿尝试的开发者。这主要是因为当时Flutter在国内没有成功案例，再加上新技术都有学习成本，所以即使有文档，也会有一些开发者犹豫是否来学习。为了解决这部分开发者的疑虑，我就想如果能用Flutter开发一个完整的APP发布到应用商店，这样开发者就可以在犹豫的时候可以先实际感受一下Flutter应用，这样有个直观的了解后，就会容易做出判断，为此，我开发了\"),e(\"a\",{attrs:{href:\"https://flutterchina.club/app/gm.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Gitme\"),e(\"OutboundLink\")],1),t._v(\"，它是一个Github客户端，它支持了源码浏览、Issue、Label等Github的大多数功能，到目前为止，通过Gitme登录过Github账号的用户有8000多人，日活用户有1000人。更重要的是，有很多人正是用了Gitme后，才来学Flutter的。\")]),t._v(\" \"),e(\"p\",[t._v(\"无论是做Flutter中文网，还是写Gitme，主要目标都是帮助开发者学习Flutter，同时消除围观开发者疑虑。但当开发者们真正开始动手时，Flutter的生态问题就变得尤为突出。由于在2018年初Flutter刚起步时，很多基础的包和库都是空白，少数已有的一些库也大都是预览版（未到1.0），存在很多bug。这个状况不是一两个人花一两天能搞定的，这是需要聚整个Flutter开发者社群之力，耗费数年时间才可能有所改善。因此，在2018年4月份，我以Flutter中文网名义发起了Flutter开源计划，该计划主要是开发一些常用的包来丰富Flutter生态，帮助开发者提高开发效率。自在github建立\"),e(\"a\",{attrs:{href:\"https://github.com/flutterchina\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Flutter中文开发者社区账号\"),e(\"OutboundLink\")],1),t._v(\"以来，前后开源了dio、cookieJar、flukit等多个项目，而dio在开源两周后，就迅速成为Flutter第三方包中Star排名第一的开源库。\")]),t._v(\" \"),e(\"p\",[t._v(\"虽然做的事情已经够多了，但是仍有一些很有必要去做的事情，由于时间原因，一直被搁置。\")]),t._v(\" \"),e(\"p\",[t._v(\"随着学习Flutter的人越来越多，一部分开发者通过查看官网的文档就能入门，但也有很多开发者感觉学习时仍然有些吃力，主要原因有两个，首先官网的文档主要是为了引导开发者快速上手的，讲的并不是很细；其次是我们虽然翻译了官方文档，但是对于Flutter SDK文档并没有翻译，而在开发中遇到的一些具体问题通常都得去查看SDK文档。所以，要解决这两个问题，必须得有一个系统化的Flutter教程，它不仅可以快速引导开发者入门，而且也能触及到一些细节和原理，最好也能提供一些学习和研究Flutter的方法。因此，如果能有一本能系统地介绍Flutter的书籍便是便是非常棒的！但是，当时没有一本关于Flutter的中文书籍，因此，我便计划写一本能由浅入深、系统介绍Flutter的书。2018年12月，《Flutter实战》完成初稿，并在Github上开源，同时上线了\"),e(\"a\",{attrs:{href:\"https://book.flutterchina.club/\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"《Flutter实战》电子书官网\"),e(\"OutboundLink\")],1),t._v(\" ，至今每天有3000多人在线浏览本书。那为什么不直接出版？如果直接出版，不仅有稿费，而且也能保护知识产权，而直接开源，不就只能当雷锋了？其实，无论是做中文网、写Gitme、做Flutter开源项目，我的出发点都是为了能帮助中国开发者了解、学习Flutter，而这是一件非常有意义的事，只要坚持做对别人有价值的事，那么上帝迟早会奖赏你；当然我们也在网站中留了打赏的按钮，如果读者觉得有帮助，可以扫码打赏，请笔者喝一杯咖啡。另外由于成书比较仓促，当时书中也有很多错误，开源后，有很多读者提PR来纠正书中的错别字，时至今日，有78名开发者为本书提过PR，我在此，衷心的感谢你们。\")]),t._v(\" \"),e(\"p\",[t._v(\"起初，我没有出版实体书的打算，当时我以为开发者直接通过在线访问本书官网即方便又不用付费，何乐而不为。但在本书上线后，很多读者来添加微信好友，表示非常有收益，很期待出版纸质书，甚至有比较热心的读者想提前付定金预定！我理解，这是大家对我所做之事的认可和鼓励。考虑到，确实有一部分读者，尤其是还没毕业的同学，可能更喜欢通过书籍去学习，为此，我已经和机械工业出版社合作，目前本书纸质版正在出版中，敬请关注。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"本书组织结构\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本书组织结构\"}},[t._v(\"#\")]),t._v(\" 本书组织结构\")]),t._v(\" \"),e(\"p\",[t._v(\"本书采用由浅入深的方式介绍Flutter技术原理，分为三篇，总计15章，各篇的主要内容如下。\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"第一篇，入门篇（第1章~第7章），包括Flutter技术的出现背景和简介、Flutter的各种类型的Widget以及如何构建UI。通过学习本章，读者可以掌握如何使用Flutter来构建UI界面。\")]),t._v(\" \"),e(\"li\",[t._v(\"第二篇，进阶篇（第8章~第14章），包括Flutter中的事件机制、动画、自定义组件、文件和网络、插件、国际化以及Flutter核心原理等。通过本章内容，读者可以对Flutter整体构建及原理有一个深入的认识。\")]),t._v(\" \"),e(\"li\",[t._v(\"第三篇，实例篇（第15章），本章主要通过一个简版的Github APP来将前面介绍的内容串起来，让开发者对一个完整的Flutter APP开发流有个了解。\")])]),t._v(\" \"),e(\"h2\",{attrs:{id:\"本书特色\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本书特色\"}},[t._v(\"#\")]),t._v(\" 本书特色\")]),t._v(\" \"),e(\"p\",[t._v(\"笔者在大学时读过候捷（真名侯俊杰）写的一些C++相关书籍，在他的《深入浅出MFC》一书中，有一句话我印象像非常深 “唯有深入，方能浅出”。我非常认同这句话，对于一门技术，只有了解的深入，才能用最浅显、通俗的话语描述出。我在写作本书时，深入浅出就是一个主要目标。所以，本书的目标不仅是想告诉读者如何使用Flutter，而且也非常关注各个知识点的底层实现以及设计思想。从本书章节划分上来看，入门篇为“浅出”，进阶篇则是“深入”。另外由于PC客户端开发、移动开发、Web开发这些经验我都有，而Flutter本质上是一个UI系统，而UI系统的设计和实现在”大前端“下有很多相通之处，所以在本书中的一些知识点我也会对比一些其他UI系统（主要是Android或Web）相应的实现，便于有相关开发经验的读者对比理解。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"本书读者对象\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本书读者对象\"}},[t._v(\"#\")]),t._v(\" 本书读者对象\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"读者至少熟悉一种编程语言。\")]),t._v(\" \"),e(\"li\",[t._v(\"读者最好接触过PC客户端、移动开发或Web前端开发中的一种。\")]),t._v(\" \"),e(\"li\",[t._v(\"本书不适合做为编程的入门读物。\")])]),t._v(\" \"),e(\"h2\",{attrs:{id:\"关于随书源码\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#关于随书源码\"}},[t._v(\"#\")]),t._v(\" 关于随书源码\")]),t._v(\" \"),e(\"p\",[t._v(\"由于篇幅所限，本书中大多数示例代码都只是部分核心代码，读者可以去\"),e(\"a\",{attrs:{href:\"https://github.com/wendux/flutter_in_action_source_code\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"这里\"),e(\"OutboundLink\")],1),t._v(\"查看下载。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"致谢\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#致谢\"}},[t._v(\"#\")]),t._v(\" 致谢\")]),t._v(\" \"),e(\"p\",[t._v(\"感谢一直以来支持Flutter中文网、Flutter开源项目的人以及所有对本书提过PR的人，正是因为有你们，才有这本书。另外尤其感谢给本书打赏过的同学，你们的支持给了我很大的鼓励。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"权益\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#权益\"}},[t._v(\"#\")]),t._v(\" 权益\")]),t._v(\" \"),e(\"p\",[t._v(\"最后，知识是应该付费的，创作不易，开源不等于免费，如果您是本书读者并手头宽裕，可以点击下面打赏按钮打赏；当然，如果您囊中羞涩，您也可以阅读本书，但我对您有个小小的要求，希望您在阅读的过程中能积极参与到本书的纠错以及未完成内容的创作上来，也算是有所付出。\")]),t._v(\" \"),e(\"p\",[t._v(\"近来在网上发现很多\"),e(\"strong\",[t._v(\"原封不动复制本书\")]),t._v(\"的镜像网站和大量复制或\"),e(\"strong\",[t._v(\"引用了本书但未注明出处\")]),t._v(\"的博客、文章甚至书籍；对此，笔者在此声明，本书著作权归wendux所有，任何组织或个人在未经授权的情况下复制、拷贝、抄袭本书用于商业目的，笔者保留追究其法律责任的权利。如果是非商业目的的转载和引用，请注明出处并附上本书网址。另外如有出版机构愿意为本书出版实体书或者想转载本书内容，亦或是想合作，请加微信Demons-du.\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"勘误\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#勘误\"}},[t._v(\"#\")]),t._v(\" 勘误\")]),t._v(\" \"),e(\"p\",[t._v(\"由于Flutter SDK在不断更新，本书中的部分内容（如类的继承关系、参数等）可能会和新版本的Flutter不一致，读者以最新的Flutter SDK为准。另外，由于时间仓促，书中难免有错误之处，如果您发现本书中的错误，欢迎点击右上角的”编辑按钮“，提PR。如果您想一起参与本书创作，可以参考\"),e(\"a\",{attrs:{href:\"https://github.com/flutterchina/flutter-in-action#%E8%B4%A1%E7%8C%AE%E9%A1%BB%E7%9F%A5\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"《Flutter实战》贡献指南\"),e(\"OutboundLink\")],1),t._v(\"。\")])])}),[],!1,null,null,null);r.default=u.exports}}]);"
  },
  {
    "path": "docs/assets/js/175.891c0a2f.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[175],{825:function(v,_,t){\"use strict\";t.r(_);var a=t(45),r=Object(a.a)({},(function(){var v=this,_=v.$createElement,t=v._self._c||_;return t(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":v.$parent.slotKey}},[t(\"h1\",{attrs:{id:\"字节跳动-内推\"}},[t(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#字节跳动-内推\"}},[v._v(\"#\")]),v._v(\" 字节跳动-内推\")]),v._v(\" \"),t(\"p\",[v._v(\"欢迎来字节跳动，和作者（wendux） 一起做同事！我们有大量HC，社招、校招、实习，前端、客户端、后端都有，欢迎对技术有热情的同学来投递！风里雨里，我在字节跳动等你~\")]),v._v(\" \"),t(\"blockquote\",[t(\"p\",[v._v('投递方式：加微信（Demons-du），好友申请时请按 \"姓名+学校+职位+来自flutter社区\" 备注信息，微信请求通过后，字节跳动VIP通道就建立了（后续发简历、进度跟进、问题咨询都可以直接微信联系哦）。')])]),v._v(\" \"),t(\"h2\",{attrs:{id:\"前端团队介绍\"}},[t(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#前端团队介绍\"}},[v._v(\"#\")]),v._v(\" 前端团队介绍\")]),v._v(\" \"),t(\"p\",[v._v(\"我们是字节跳动-幸福里FE团队，诞生于2018年9月，从最初的4人组成长到今天的30多人，成员年龄跨度从90后到00后。技术栈覆盖当下前端主流全方向（Vue、React、Typescript、nodejs、webgl、flutter、小程序)，团队内大牛多多，技术氛围浓厚，有VR专家老吴、3D渲染一哥博哥、《Flutter实战》作者wendux、深谙 Web 框架及工程化的董老师、以及技能树满点的杰哥等等，还有，团队经常组织线下学习及娱乐活动，是一个开心且战斗力极强的team。只要你觉得自己够出色，或想让自己变得更出色，还等什么，放肆地加入我们吧。\")]),v._v(\" \"),t(\"h3\",{attrs:{id:\"业务线介绍\"}},[t(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#业务线介绍\"}},[v._v(\"#\")]),v._v(\" 业务线介绍\")]),v._v(\" \"),t(\"p\",[v._v(\"幸福里是字节跳动旗下集内容、社区、工具于一体的房产信息、服务、交易平台。产品基于个性化推荐引擎向用户推荐优质的房产内容和全面、真实的房源信息，致力于为用户提供全面、专业、可靠的购房决策支持。\")]),v._v(\" \"),t(\"p\",[v._v(\"幸福里始于2018年8月，是国内发展最快的，集内容、社区、工具于一体的房产信息与服务平台，业务覆盖一二线共23城，现累积注册用户千万，目前进入高速增长期。\")]),v._v(\" \"),t(\"h3\",{attrs:{id:\"团队福利\"}},[t(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#团队福利\"}},[v._v(\"#\")]),v._v(\" 团队福利\")]),v._v(\" \"),t(\"p\",[v._v(\"五险一金、补充医疗保险、定期体检、年终奖、股票期权、带薪年假、员工旅游、交通补助、包吃、节日福利、住房补贴，不限量零食下午茶、弹性工作制\")]),v._v(\" \"),t(\"h4\",{attrs:{id:\"实习生\"}},[t(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#实习生\"}},[v._v(\"#\")]),v._v(\" 实习生\")]),v._v(\" \"),t(\"ol\",[t(\"li\",[v._v(\"团队为每一位实习生提供专职mentor，手把手带入工作业务。\")]),v._v(\" \"),t(\"li\",[v._v(\"团队为没有基础的实习生提供“筑基计划”的课程学习，轻松进阶前端技能。\")])]),v._v(\" \"),t(\"h2\",{attrs:{id:\"职位介绍\"}},[t(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#职位介绍\"}},[v._v(\"#\")]),v._v(\" 职位介绍\")]),v._v(\" \"),t(\"h3\",{attrs:{id:\"职位一-前端开发工程师-校招-社招-急\"}},[t(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#职位一-前端开发工程师-校招-社招-急\"}},[v._v(\"#\")]),v._v(\" 职位一：前端开发工程师(校招/社招)急\")]),v._v(\" \"),t(\"p\",[t(\"strong\",[v._v(\"职位描述:\")])]),v._v(\" \"),t(\"ol\",[t(\"li\",[t(\"p\",[v._v(\"负责移动端 /PC 端业务系统、小程序、跨端页面开发；\")])]),v._v(\" \"),t(\"li\",[t(\"p\",[v._v(\"负责推动与优化业务线中前端基础架构、组件抽象、技术调研；\")])]),v._v(\" \"),t(\"li\",[t(\"p\",[v._v(\"积极推动改进产品，包括技术、用户体验、产品等各个维度的改进。\")])])]),v._v(\" \"),t(\"p\",[t(\"strong\",[v._v(\"职位要求:\")])]),v._v(\" \"),t(\"ol\",[t(\"li\",[t(\"p\",[v._v(\"本科及以上学历，计算机、通信等相关专业；\")])]),v._v(\" \"),t(\"li\",[t(\"p\",[v._v(\"熟练掌握 EcmaScript，CSS，HTML，DOM、绘图、动画、协议、安全、网络、性能优化等前端技术，对主流前端框架（ React \\\\ Vue 等）至少一种有深入应用并深入理解其设计原理；\")])]),v._v(\" \"),t(\"li\",[t(\"p\",[v._v(\"有安卓、iOS 开发经验或跨端技术flutter者优先；\")])]),v._v(\" \"),t(\"li\",[t(\"p\",[v._v(\"对用户体验、交互操作流程，及用户需求有一定了解；\")])]),v._v(\" \"),t(\"li\",[t(\"p\",[v._v(\"积极乐观，责任心强，工作认真细致，具备良好的服务意识，具有良好的团队沟通与协作能力；\")])]),v._v(\" \"),t(\"li\",[t(\"p\",[v._v(\"热爱前端技术，有较强的学习能力，有强烈的求知欲、好奇心和进取心 ，能及时关注和学习业界最新的前端技术。\")])])]),v._v(\" \"),t(\"h3\",{attrs:{id:\"职位二-前端开发实习生-可转正\"}},[t(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#职位二-前端开发实习生-可转正\"}},[v._v(\"#\")]),v._v(\" 职位二：前端开发实习生(可转正)\")]),v._v(\" \"),t(\"p\",[t(\"strong\",[v._v(\"职位描述：\")])]),v._v(\" \"),t(\"p\",[v._v(\"1、负责字节跳动-幸福里业务h5、小程序、中台系统的维护与开发；\")]),v._v(\" \"),t(\"p\",[v._v(\"2、负责根据已有前端项目的基础架构进行合理的技术优化；\")]),v._v(\" \"),t(\"p\",[v._v(\"3、积极推动改进产品，包括技术、用户体验、产品等各个维度。\")]),v._v(\" \"),t(\"p\",[t(\"strong\",[v._v(\"职位要求：\")])]),v._v(\" \"),t(\"p\",[v._v(\"1、计算机基础扎实：数据结构、算法、操作系统；\")]),v._v(\" \"),t(\"p\",[v._v(\"2、熟悉掌握javascript、ES6 语言特性，熟练掌握css中常见布局方式，以及CSS3动画技术；\")]),v._v(\" \"),t(\"p\",[v._v(\"3、熟练掌握VUE或React技术（非必须）；\")]),v._v(\" \"),t(\"p\",[v._v(\"4、有ACM竞赛且获奖者优先；\")]),v._v(\" \"),t(\"p\",[v._v(\"5、具较强的学习能力、主动、自驱、有责任心。\")]),v._v(\" \"),t(\"p\",[t(\"strong\",[v._v(\"实习生培养计划\")]),v._v(\"：\")]),v._v(\" \"),t(\"p\",[v._v(\"我们会为每一位实习生配备一名mentor进行日常决疑解惑和指导，同时我们针对不太熟悉前端的同学进行一个专门的【筑基培训】，旨在帮助快速补齐前端基础，以及明确后续学习和成长路线，有老司机带，不迷路！\")]),v._v(\" \"),t(\"h3\",{attrs:{id:\"职位三-web3d开发工程师-急\"}},[t(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#职位三-web3d开发工程师-急\"}},[v._v(\"#\")]),v._v(\" 职位三：web3D开发工程师（急）\")]),v._v(\" \"),t(\"p\",[t(\"strong\",[v._v(\"职位描述：\")])]),v._v(\" \"),t(\"ol\",[t(\"li\",[v._v(\"负责 VR 看房相关的业务开发，包括渲染SDK、标注平台等。\")]),v._v(\" \"),t(\"li\",[v._v(\"负责 VR 数据平台相关开发：包括数据预处理\")])]),v._v(\" \"),t(\"p\",[t(\"strong\",[v._v(\"职位要求：\")])]),v._v(\" \"),t(\"ol\",[t(\"li\",[t(\"p\",[v._v(\"熟练掌握 JavaScript，WebGL；\")])]),v._v(\" \"),t(\"li\",[t(\"p\",[v._v(\"熟悉计算机图形学，渲染管线/线性代数；\")])]),v._v(\" \"),t(\"li\",[t(\"p\",[v._v(\"熟悉常用 Shader 原理及编写；\")])]),v._v(\" \"),t(\"li\",[t(\"p\",[v._v(\"熟悉至少一款 H5 渲染引擎，如ThreeJS，Babylon等；\")])]),v._v(\" \"),t(\"li\",[t(\"p\",[v._v(\"热爱钻研新技术，有强烈的好奇心和求知欲，有良好的编码规范；\")])]),v._v(\" \"),t(\"li\",[t(\"p\",[v._v(\"加分项：\")]),v._v(\" \"),t(\"ul\",[t(\"li\",[v._v(\"熟悉VR看房相关业务\")]),v._v(\" \"),t(\"li\",[v._v(\"熟悉后端开发（Node）、对服务稳定性、并发了解同学优先（VR数据平台）。\")]),v._v(\" \"),t(\"li\",[v._v(\"熟悉 ThreeJS，Babylon, Unity3D 有相关 3D 作品或 DEMO。\")]),v._v(\" \"),t(\"li\",[v._v(\"各大前端技术社区活跃者、有自己的开源项目；\")])])])]),v._v(\" \"),t(\"h2\",{attrs:{id:\"其它职位-实习-校招-社招均可\"}},[t(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#其它职位-实习-校招-社招均可\"}},[v._v(\"#\")]),v._v(\" 其它职位（实习/校招/社招均可）\")]),v._v(\" \"),t(\"h3\",{attrs:{id:\"android开发工程师\"}},[t(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#android开发工程师\"}},[v._v(\"#\")]),v._v(\" Android开发工程师\")]),v._v(\" \"),t(\"p\",[v._v(\"职位描述\")]),v._v(\" \"),t(\"p\",[v._v(\"1、负责公司移动产品的研发, 编写高质量的代码；\")]),v._v(\" \"),t(\"p\",[v._v(\"2、和产品经理配合, 深度参与手机产品需求讨论, 功能定义等；\")]),v._v(\" \"),t(\"p\",[v._v(\"3、设计良好的代码结构, 不断迭代重构 。\")]),v._v(\" \"),t(\"p\",[v._v(\"职位要求\")]),v._v(\" \"),t(\"p\",[v._v(\"1、智能手机爱好者和使用者, 追求良好的用户体验；\")]),v._v(\" \"),t(\"p\",[v._v(\"2、热爱移动产品研发, 愿意在移动开发领域深入钻研, 并成为专家；\")]),v._v(\" \"),t(\"p\",[v._v(\"3、熟练掌握JAVA, 熟悉Android SDK；\")]),v._v(\" \"),t(\"p\",[v._v(\"4、一年以上Android开发经验, 能独立开发Android App； 5、对软件产品有强烈的责任心, 具备良好的沟通能力和优秀的团队协作能力。\")]),v._v(\" \"),t(\"p\",[v._v(\"5、有flutter开发经验者加分。\")]),v._v(\" \"),t(\"h3\",{attrs:{id:\"ios开发工程师\"}},[t(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#ios开发工程师\"}},[v._v(\"#\")]),v._v(\" iOS开发工程师\")]),v._v(\" \"),t(\"p\",[v._v(\"职位描述\")]),v._v(\" \"),t(\"p\",[v._v(\"1、负责公司移动产品的研发，编写高质量的代码；\")]),v._v(\" \"),t(\"p\",[v._v(\"2、和产品经理配合，深度参与手机产品需求讨论，功能定义等；\")]),v._v(\" \"),t(\"p\",[v._v(\"3、设计良好的代码结构，不断迭代重构 ；\")]),v._v(\" \"),t(\"p\",[v._v(\"4、导并带领初级工程师共同完成研发任务。\")]),v._v(\" \"),t(\"p\",[v._v(\"职位要求\")]),v._v(\" \"),t(\"p\",[v._v(\"1、有强烈的求知欲和进取心；\")]),v._v(\" \"),t(\"p\",[v._v(\"2、具有扎实的编程工底，良好的设计能力和编程习惯；\")]),v._v(\" \"),t(\"p\",[v._v(\"3、至少精通一门编程语言 ，熟练掌握Objective-C，熟悉Swift的优先 ；\")]),v._v(\" \"),t(\"p\",[v._v(\"4、一年以上iOS开发经验，能独立开发iPhoneApp者先。\")]),v._v(\" \"),t(\"p\",[v._v(\"5、有flutter开发经验者加分。\")]),v._v(\" \"),t(\"h3\",{attrs:{id:\"后端开发工程师\"}},[t(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#后端开发工程师\"}},[v._v(\"#\")]),v._v(\" 后端开发工程师\")]),v._v(\" \"),t(\"p\",[t(\"strong\",[v._v(\"职位描述\")])]),v._v(\" \"),t(\"p\",[v._v(\"1、主导或参与系统设计、研发、部署等相关工作；\")]),v._v(\" \"),t(\"p\",[v._v(\"2、研发基础服务组件，解决共性需求，减少重复开发与运维；\")]),v._v(\" \"),t(\"p\",[v._v(\"3、有较强的系统问题分析经验和能力，能够解决复杂的系统问题；\")]),v._v(\" \"),t(\"p\",[v._v(\"4、参与部分生产系统维护工作，解决生产系统问题及进行系统调优。\")]),v._v(\" \"),t(\"p\",[t(\"strong\",[v._v(\"职位要求\")])]),v._v(\" \"),t(\"p\",[v._v(\"1、本科及以上学历，计算机相关专业；\")]),v._v(\" \"),t(\"p\",[v._v(\"2、热爱计算机科学和互联网技术，精通至少一门编程语言，包括但不仅限于：Java、C、C++、PHP、 Python、Go；\")]),v._v(\" \"),t(\"p\",[v._v(\"3、掌握扎实的计算机基础知识，深入理解数据结构、算法和操作系统知识；\")]),v._v(\" \"),t(\"p\",[v._v(\"4、有优秀的逻辑分析能力，能够对业务逻辑进行合理的抽象和拆分；\")]),v._v(\" \"),t(\"p\",[v._v(\"5、有强烈的求知欲，优秀的学习和沟通能力。\")]),v._v(\" \"),t(\"h2\",{attrs:{id:\"返回书籍菜单列表\"}},[t(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#返回书籍菜单列表\"}},[v._v(\"#\")]),v._v(\" \"),t(\"a\",{attrs:{href:\"/index\"}},[v._v(\"返回书籍菜单列表\")])])])}),[],!1,null,null,null);_.default=r.exports}}]);"
  },
  {
    "path": "docs/assets/js/176.93a74e23.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[176],{826:function(t,a,e){\"use strict\";e.r(a);var r=e(45),s=Object(r.a)({},(function(){var t=this,a=t.$createElement,e=t._self._c||a;return e(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[e(\"h1\",{attrs:{id:\"下一步\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#下一步\"}},[t._v(\"#\")]),t._v(\" 下一步\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"其它平台\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#其它平台\"}},[t._v(\"#\")]),t._v(\" 其它平台\")]),t._v(\" \"),e(\"p\",[t._v(\"本书主要讲的是Flutter在移动端开发\")]),t._v(\" \"),e(\"ul\",[e(\"li\")])])}),[],!1,null,null,null);a.default=s.exports}}]);"
  },
  {
    "path": "docs/assets/js/177.20c492a7.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[177],{827:function(t,e,r){\"use strict\";r.r(e);var l=r(45),u=Object(l.a)({},(function(){var t=this,e=t.$createElement,r=t._self._c||e;return r(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[r(\"h1\",{attrs:{id:\"前言\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#前言\"}},[t._v(\"#\")]),t._v(\" 前言\")]),t._v(\" \"),r(\"h3\",{attrs:{id:\"缘起\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#缘起\"}},[t._v(\"#\")]),t._v(\" 缘起\")]),t._v(\" \"),r(\"p\",[t._v(\"在全球，随着Flutter被越来越多的知名公司应用在自己的商业APP中，Flutter这门新技术也逐渐进入了移动开发者的视野，尤其是当Google在2018年IO大会上发布了第一个Preview版本后，国内刮起来一股学习Flutter的热潮。\")]),t._v(\" \"),r(\"p\",[t._v(\"在Flutter发布之初，当时，我在看完Flutter原理介绍后，就对它产生了浓厚的兴趣。当时笔者身边也一些人比较关注Flutter，我也被经常问到关于Flutter的一些问题，比如Flutter怎么样？和RN有什么区别？Flutter为什么要用Dart？当时也听到了一些批评的声音，比如有些人说Flutter只是重复造轮子，没啥亮点、Flutter最大的缺点就是使用了Dart语言等。在听到这些问题及论调后，我深知这是对Flutter的不了解而造成的，这和当时国内缺乏Flutter中文文档和教程有直接关系，很多人对Flutter的了解都只停留在Google的发布会介绍（有中文翻译）。\")]),t._v(\" \"),r(\"p\",[t._v(\"在笔者深入的了解Flutter后，深知Flutter必将是一个会改变移动开发格局的里程碑级作品，它从设计之初就对性能和开发效率兼顾，并且借鉴了React（一个Web开发框架）的响应式的UI框架设计思想等，总之，很难用一两句话说完Flutter的优点，同时我也很快成为了Flutter的路转粉。\")]),t._v(\" \"),r(\"p\",[t._v(\"为了更好的帮助中国开发者了解这门新技术，我在2018年初开始翻译Flutter官网文档，同年4月份上线了\"),r(\"a\",{attrs:{href:\"https://flutterchina.club/\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Flutter中文网\"),r(\"OutboundLink\")],1),t._v(\"，上线后反响很强烈，Flutter中文网也很快被传播开，百度搜索排名迅速蹿升到前三，截止目前，Flutter中文官网日PV在7万左右，每日独立访问人数近一万多。\")]),t._v(\" \"),r(\"p\",[t._v(\"虽然Flutter中文网给中国开发者提供了很好的第一手了解Flutter的资料，但是笔者还会经常遇到一些对Flutter技术处于围观而不愿尝试的开发者。这主要是因为当时Flutter在国内没有成功案例，再加上新技术都有学习成本，所以即使有文档，也会有一些开发者犹豫是否来学习。为了解决这部分开发者的疑虑，我就想如果能用Flutter开发一个完整的APP发布到应用商店，这样开发者就可以在犹豫的时候可以先实际感受一下Flutter应用，这样有个直观的了解后，就会容易做出判断，为此，我开发了\"),r(\"a\",{attrs:{href:\"https://flutterchina.club/app/gm.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Gitme\"),r(\"OutboundLink\")],1),t._v(\"，它是一个Github客户端，它支持了源码浏览、Issue、Label等Github的大多数功能，到目前为止，通过Gitme登录过Github账号的用户有8000多人，日活用户有1000人。更重要的是，有很多人正是用了Gitme后，才来学Flutter的。\")]),t._v(\" \"),r(\"p\",[t._v(\"无论是做Flutter中文网，还是写Gitme，主要目标都是帮助开发者学习Flutter，同时消除围观开发者疑虑。但当开发者们真正开始动手时，Flutter的生态问题就变得尤为突出。由于在2018年初Flutter刚起步时，很多基础的包和库都是空白，少数已有的一些库也大都是预览版（未到1.0），存在很多bug。这个状况不是一两个人花一两天能搞定的，这是需要聚整个Flutter开发者社群之力，耗费数年时间才可能有所改善。因此，在2018年4月份，我以Flutter中文网名义发起了Flutter开源计划，该计划主要是开发一些常用的包来丰富Flutter生态，帮助开发者提高开发效率。自在github建立\"),r(\"a\",{attrs:{href:\"https://github.com/flutterchina\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Flutter中文开发者社区官方账号\"),r(\"OutboundLink\")],1),t._v(\"以来，前后开源了dio、cookieJar、flukit等多个项目，而dio在开源两周后，就迅速成为Flutter第三方包中Star排名第一的开源库。\")]),t._v(\" \"),r(\"p\",[t._v(\"虽然做的事情已经够多了，但是仍有一些很有必要去做的事情，由于时间原因，一直被搁置。\")]),t._v(\" \"),r(\"p\",[t._v(\"随着学习Flutter的人越来越多，一部分开发者通过查看官网的文档就能入门，但也有很多开发者感觉学习时仍然有些吃力，主要原因有两个，首先官网的文档主要是为了引导开发者快速上手的，讲的并不是很细；其次是我们虽然翻译了官方文档，但是对于Flutter SDK文档并没有翻译，而在开发中遇到的一些具体问题通常都得去查看SDK文档。所以，要解决这两个问题，必须得有一个系统化的Flutter教程，它不仅可以快速引导开发者入门，而且也能触及到一些细节和原理，最好也能提供一些学习和研究Flutter的方法。因此，如果能有一本能系统地介绍Flutter的书籍便是便是非常棒的！但是，当时没有一本关于Flutter的中文书籍，因此，我便计划写一本能由浅入深、系统介绍Flutter的书。2018年12月，《Flutter实战》完成初稿，并在Github上开源，同时上线了《Flutter实战》电子书官网（https://book.flutterchina.club/） ，至今每天有3000多人在线浏览本书。那为什么不直接出版？如果直接出版，不仅有稿费，而且也能保护知识产权，而直接开源，不就就只能当雷锋了？其实，无论是做中文网、写Gitme、做Flutter开源项目，我的出发点都是为了能帮助中国开发者了解、学习Flutter，而这是一件非常有意义的事，只要坚持做对别人有价值的事，那么上帝迟早会奖赏你；当然我们也在网站中留了打赏的按钮，如果读者觉得有帮助，可以扫码打赏，请笔者喝一杯咖啡。另外由于成书比较仓促，当时书中也有很多错误，开源后，有很多读者提PR来纠正书中的错别字，时至今日，有78名开发者为本书提过PR，我在此，衷心的感谢你们。\")]),t._v(\" \"),r(\"p\",[t._v(\"起初，我没有出版实体书的打算，当时我以为开发者直接通过在线访问本书官网即方便又不用付费，何乐而不为。但在本书上线后，很多读者来添加微信好友，表示非常有收益，很期待出版纸质书，甚至有比较热心的读者想提前付定金预定！我理解，这是大家对我所做之事的认可和鼓励。考虑到，确实有一部分读者，尤其是还没毕业的同学，可能更喜欢通过书籍去学习，为此，我已经和机械工业出版社合作，目前本书纸质版正在出版中，敬请关注。\")]),t._v(\" \"),r(\"h3\",{attrs:{id:\"本书组织结构\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本书组织结构\"}},[t._v(\"#\")]),t._v(\" 本书组织结构\")]),t._v(\" \"),r(\"p\",[t._v(\"本书采用由浅入深的方式介绍Flutter技术原理，分为三篇，总计15章，各篇的主要内容如下。\")]),t._v(\" \"),r(\"ul\",[r(\"li\",[t._v(\"第一篇，入门篇（第1章~第7章），包括Flutter技术的出现背景和简介、Flutter的各种类型的Widget以及如何构建UI。通过学习本章，读者可以掌握如何使用Flutter来构建UI界面。\")]),t._v(\" \"),r(\"li\",[t._v(\"第二篇，进阶篇（第8章~第14章），包括Flutter中的事件机制、动画、自定义组件、文件和网络、插件、国际化以及Flutter核心原理等。通过本章内容，读者可以对Flutter整体构建及原理有一个深入的认识。\")]),t._v(\" \"),r(\"li\",[t._v(\"第三篇，实例篇（第15张），本章主要通过一个简版的Github APP来将前面介绍的内容串起来，让开发者对一个完整的Flutter APP开发流有个了解。\")])]),t._v(\" \"),r(\"p\",[t._v(\"由于Flutter的很多知识点是相互交织的，很难将它们彻底划分开，所以本书中也难免会出现一些在前面章节会使用在后面章节的场景，比如我们在入门篇介绍进度指示器时会用到在进阶篇中才介绍的动画相关知识。本书中对于这种情况会在相应的章节进行说明。读者可以直接跳到后面相应知识点章节阅读后再返回，也可以先有个印象，待学习到后面相关章节后再回头来看。\")]),t._v(\" \"),r(\"h3\",{attrs:{id:\"本书特色\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本书特色\"}},[t._v(\"#\")]),t._v(\" 本书特色\")]),t._v(\" \"),r(\"p\",[t._v(\"笔者在大学时读过候捷（真名侯俊杰）写的一些C++相关书籍，在他的《深入浅出MFC》一书中，有一句话我印象非常深 “唯有深入，方能浅出”。我非常认同这句话，对于一门技术，只有了解的深入，才能用最浅显、通俗的话语描述出。我在写作本书时，深入浅出就是一个主要目标。所以，本书的目标不仅是想告诉读者如何使用Flutter，而且也非常关注各个知识点的底层实现以及设计思想。从本书章节划分上来看，入门篇为“浅出”，进阶篇则是“深入”。另外由于PC客户端开发、移动开发、Web开发这些经验我都有，而Flutter本质上是一个UI系统，而UI系统的设计和实现在”大前端“下有很多相通之处，所以在本书中的一些知识点我也会对比一些其他UI系统（主要是Android或Web）相应的实现，便于有相关开发经验的读者对比理解。\")]),t._v(\" \"),r(\"h3\",{attrs:{id:\"本书读者对象\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本书读者对象\"}},[t._v(\"#\")]),t._v(\" 本书读者对象\")]),t._v(\" \"),r(\"ul\",[r(\"li\",[t._v(\"读者至少熟悉一种编程语言。\")]),t._v(\" \"),r(\"li\",[t._v(\"读者最好接触过PC客户端、移动开发或Web前端开发中的一种。\")]),t._v(\" \"),r(\"li\",[t._v(\"本书不适合做为编程的入门读物。\")])]),t._v(\" \"),r(\"h3\",{attrs:{id:\"关于随书源码\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#关于随书源码\"}},[t._v(\"#\")]),t._v(\" 关于随书源码\")]),t._v(\" \"),r(\"p\",[t._v(\"由于篇幅所限，本书中大多数示例代码都只是部分核心代码，读者可以去https://github.com/wendux/flutter_in_action_source_code 查看\")]),t._v(\" \"),r(\"h3\",{attrs:{id:\"勘误\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#勘误\"}},[t._v(\"#\")]),t._v(\" 勘误\")]),t._v(\" \"),r(\"p\",[t._v(\"由于Flutter SDK在不断更新，本书中的部分内容（如类的继承关系、参数等）可能会和新版本的Flutter不一致，读者以最新的Flutter SDK为准。另外，由于时间仓促，书中难免有错误之处，如果发现错误，可以在本书Github项目issue列表中去反馈，地址是https://github.com/flutterchina/flutter-in-action/issues 。\")]),t._v(\" \"),r(\"h3\",{attrs:{id:\"致谢\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#致谢\"}},[t._v(\"#\")]),t._v(\" 致谢\")]),t._v(\" \"),r(\"p\",[t._v(\"感谢一直以来支持Flutter中文网、Flutter开源项目的人以及所有对本书提过PR的人，正是因为有你们，才有这本书。另外尤其感谢给本书打赏过的同学，你们的支持给了我很大的鼓励。\")])])}),[],!1,null,null,null);e.default=u.exports}}]);"
  },
  {
    "path": "docs/assets/js/178.a9c56f1f.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[178],{828:function(t,e,l){\"use strict\";l.r(e);var p=l(45),i=Object(p.a)({},(function(){var t=this,e=t.$createElement,l=t._self._c||e;return l(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[l(\"h1\",{attrs:{id:\"参考文献\"}},[l(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#参考文献\"}},[t._v(\"#\")]),t._v(\" 参考文献\")]),t._v(\" \"),l(\"ul\",[l(\"li\",[l(\"p\",[t._v(\"React Native官网：https://facebook.github.io/react-native/\")])]),t._v(\" \"),l(\"li\",[l(\"p\",[t._v(\"Weex：https://weex.apache.org/zh/guide/introduction.html\")])]),t._v(\" \"),l(\"li\",[l(\"p\",[t._v(\"快应用：https://www.quickapp.cn/\")])]),t._v(\" \"),l(\"li\",[l(\"p\",[t._v(\"QT for mobile：https://www.qt.io/mobile-app-development/\")])]),t._v(\" \"),l(\"li\",[l(\"p\",[t._v(\"Flutter官网：https://flutter.dev/\")])]),t._v(\" \"),l(\"li\",[l(\"p\",[t._v(\"Flutter中文网社区：https://flutterchina.club/docs/\")])]),t._v(\" \"),l(\"li\",[l(\"p\",[t._v(\"Dart Packages官网：https://pub.dev/\")])]),t._v(\" \"),l(\"li\",[l(\"p\",[t._v(\"Flutter中文开发者社区开源项目：https://github.com/flutterchina\")])]),t._v(\" \"),l(\"li\",[l(\"p\",[t._v(\"Material Design：https://material.io/\")])]),t._v(\" \"),l(\"li\",[l(\"p\",[t._v(\"Github 开发者中心官网：https://developer.github.com/v3/\")])]),t._v(\" \"),l(\"li\",[l(\"p\",[t._v(\"Android开发者中心官网：https://developer.android.google.cn/\")])]),t._v(\" \"),l(\"li\",[l(\"p\",[t._v(\"Apple开发者中心官网：https://developer.apple.com/\")])])])])}),[],!1,null,null,null);e.default=i.exports}}]);"
  },
  {
    "path": "docs/assets/js/179.b42bb139.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[179],{829:function(t,r,e){\"use strict\";e.r(r);var o=e(45),a=Object(o.a)({},(function(){var t=this,r=t.$createElement,e=t._self._c||r;return e(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[e(\"h1\",{attrs:{id:\"summary\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#summary\"}},[t._v(\"#\")]),t._v(\" Summary\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/\"}},[t._v(\"简介\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/intro.html\"}},[t._v(\"前言\")])],1)]),t._v(\" \"),e(\"h2\",{attrs:{id:\"入门篇\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#入门篇\"}},[t._v(\"#\")]),t._v(\" 入门篇\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"p\",[e(\"RouterLink\",{attrs:{to:\"/chapter1/\"}},[t._v(\"第一章：起步\")])],1),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter1/mobile_development_intro.html\"}},[t._v(\"1.1：移动开发技术简介\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter1/flutter_intro.html\"}},[t._v(\"1.2：初识Flutter\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter1/install_flutter.html\"}},[t._v(\"1.3：搭建Flutter开发环境\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter1/dart.html\"}},[t._v(\"1.4：Dart语言简介\")])],1)])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"RouterLink\",{attrs:{to:\"/chapter2/\"}},[t._v(\"第二章：第一个Flutter应用\")])],1),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter2/first_flutter_app.html\"}},[t._v(\"2.1：计数器示例\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter2/flutter_router.html\"}},[t._v(\"2.2：路由管理\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter2/flutter_package_mgr.html\"}},[t._v(\"2.3：包管理\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter2/flutter_assets_mgr.html\"}},[t._v(\"2.4：资源管理\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter2/flutter_app_debug.html\"}},[t._v(\"2.5：调试Flutter APP\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter2/thread_model_and_error_report.html\"}},[t._v(\"2.6：Dart线程模型及异常捕获\")])],1)])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"RouterLink\",{attrs:{to:\"/chapter3/\"}},[t._v(\"第三章：基础组件\")])],1),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter3/flutter_widget_intro.html\"}},[t._v(\"3.1：Widget简介\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter3/state_manage.html\"}},[t._v(\"3.2：状态管理\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter3/text.html\"}},[t._v(\"3.3：文本、字体样式\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter3/buttons.html\"}},[t._v(\"3.4：按钮\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter3/img_and_icon.html\"}},[t._v(\"3.5：图片和Icon\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter3/radio_and_checkbox.html\"}},[t._v(\"3.6：单选框和复选框\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter3/input_and_form.html\"}},[t._v(\"3.7：输入框和表单\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter3/progress.html\"}},[t._v(\"3.8：进度指示器\")])],1)])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"RouterLink\",{attrs:{to:\"/chapter4/\"}},[t._v(\"第四章：布局类组件\")])],1),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter4/intro.html\"}},[t._v(\"4.1：布局类组件简介\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter4/row_and_column.html\"}},[t._v(\"4.2：线性布局（Row、Column）\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter4/flex.html\"}},[t._v(\"4.3：弹性布局（Flex）\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter4/wrap_and_flow.html\"}},[t._v(\"4.4：流式布局（Wrap、Flow）\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter4/stack.html\"}},[t._v(\"4.5：层叠布局（Stack、Positioned）\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter4/alignment.html\"}},[t._v(\"4.6：对齐与相对定位（Align）\")])],1)])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"RouterLink\",{attrs:{to:\"/chapter5/\"}},[t._v(\"第五章：容器类组件\")])],1),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter5/padding.html\"}},[t._v(\"5.1：填充（Padding）\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter5/constrainedbox_and_sizebox.html\"}},[t._v(\"5.2：尺寸限制类容器（ConstrainedBox等）\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter5/decoratedbox.html\"}},[t._v(\"5.3：装饰容器（DecoratedBox）\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter5/transform.html\"}},[t._v(\"5.4：变换（Transform）\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter5/container.html\"}},[t._v(\"5.5：Container容器\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter5/material_scaffold.html\"}},[t._v(\"5.6：Scaffold、TabBar、底部导航\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter5/clip.html\"}},[t._v(\"5.7：剪裁（Clip）\")])],1)])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"RouterLink\",{attrs:{to:\"/chapter6/\"}},[t._v(\"第六章：可滚动组件\")])],1),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter6/intro.html\"}},[t._v(\"6.1：可滚动组件简介\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter6/single_child_scrollview.html\"}},[t._v(\"6.2：SingleChildScrollView\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter6/listview.html\"}},[t._v(\"6.3：ListView\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter6/gridview.html\"}},[t._v(\"6.4：GridView\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter6/custom_scrollview.html\"}},[t._v(\"6.5：CustomScrollView\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter6/scroll_controller.html\"}},[t._v(\"6.6：滚动监听及控制（ScrollController）\")])],1)])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"RouterLink\",{attrs:{to:\"/chapter7/\"}},[t._v(\"第七章：功能型组件\")])],1),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter7/willpopscope.html\"}},[t._v(\"7.1：导航返回拦截（WillPopScope）\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter7/inherited_widget.html\"}},[t._v(\"7.2：数据共享（InheritedWidget）\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter7/provider.html\"}},[t._v(\"7.3： 跨组件状态共享（Provider）\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter7/theme.html\"}},[t._v(\"7.4：颜色和主题（Theme）\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter7/futurebuilder_and_streambuilder.html\"}},[t._v(\"7.5：异步UI更新（FutureBuilder、StreamBuilder）\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter7/dailog.html\"}},[t._v(\"7.6：对话框详解\")])],1)])])]),t._v(\" \"),e(\"h2\",{attrs:{id:\"进阶篇\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#进阶篇\"}},[t._v(\"#\")]),t._v(\" 进阶篇\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"p\",[e(\"RouterLink\",{attrs:{to:\"/chapter8/\"}},[t._v(\"第八章：事件处理与通知\")])],1),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter8/listener.html\"}},[t._v(\"8.1：原始指针事件处理\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter8/gesture.html\"}},[t._v(\"8.2：手势识别\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter8/eventbus.html\"}},[t._v(\"8.3：全局事件总线\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter8/notification.html\"}},[t._v(\"8.4：通知(Notification)\")])],1)])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"RouterLink\",{attrs:{to:\"/chapter9/\"}},[t._v(\"第九章：动画\")])],1),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter9/intro.html\"}},[t._v(\"9.1：Flutter动画简介\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter9/animation_structure.html\"}},[t._v(\"9.2：动画结构\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter9/route_transition.html\"}},[t._v(\"9.3：自定义路由过渡动画\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter9/hero.html\"}},[t._v(\"9.4：Hero动画\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter9/stagger_animation.html\"}},[t._v(\"9.5：交织动画\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter9/animated_switcher.html\"}},[t._v(\"9.6：通用“动画切换”组件（AnimatedSwitcher）\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter9/animated_widgets.html\"}},[t._v(\"9.7：动画过渡组件\")])],1)])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"RouterLink\",{attrs:{to:\"/chapter10/\"}},[t._v(\"第十章：自定义组件\")])],1),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter10/intro.html\"}},[t._v(\"10.1：自定义组件方法简介\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter10/combine.html\"}},[t._v(\"10.2：组合现有组件\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter10/turn_box.html\"}},[t._v(\"10.3：组合实例：TurnBox\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter10/custom_paint.html\"}},[t._v(\"10.4：自绘组件（CustomPaint与Canvas）\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter10/gradient_circular_progress_demo.html\"}},[t._v(\"10.5：自绘实例：圆形渐变进度条(自绘)\")])],1)])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"RouterLink\",{attrs:{to:\"/chapter11/\"}},[t._v(\"第十一章：文件操作与网络请求\")])],1),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter11/file_operation.html\"}},[t._v(\"11.1：文件操作\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter11/http.html\"}},[t._v(\"11.2：Http请求-HttpClient\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter11/dio.html\"}},[t._v(\"11.3：Http请求-Dio package\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter11/download_with_chunks.html\"}},[t._v(\"11.4：实例：Http分块下载\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter11/websocket.html\"}},[t._v(\"11.5：WebSocket\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter11/socket.html\"}},[t._v(\"11.6：使用Socket API\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter11/json_model.html\"}},[t._v(\"11.7：Json转Dart Model类\")])],1)])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"RouterLink\",{attrs:{to:\"/chapter12/\"}},[t._v(\"第十二章：包与插件\")])],1),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter12/develop_package.html\"}},[t._v(\"12.1：开发package\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter12/platform-channel.html\"}},[t._v(\"12.2：平台通道简介\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter12/develop_plugin.html\"}},[t._v(\"12.3：开发Flutter插件\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter12/android_implement.html\"}},[t._v(\"12.4：插件开发：实现Android端API\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter12/ios_implement.html\"}},[t._v(\"12.5：插件开发：实现IOS端API\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter12/texture_platformview.html\"}},[t._v(\"12.6：Texture和PlatformView\")])],1)])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"RouterLink\",{attrs:{to:\"/chapter13/\"}},[t._v(\"第十三章：国际化\")])],1),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter13/multi_languages_support.html\"}},[t._v(\"13.1：让App支持多语言\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter13/locallization_implement.html\"}},[t._v(\"13.2：实现Localizations\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter13/intl.html\"}},[t._v(\"13.3：使用Intl包\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter13/faq.html\"}},[t._v(\"13.4：国际化常见问题\")])],1)])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"RouterLink\",{attrs:{to:\"/chapter14/\"}},[t._v(\"第十四章：Flutter核心原理\")])],1),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter14/flutter_ui_system.html\"}},[t._v(\"14.1：Flutter UI系统\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter14/element_buildcontext.html\"}},[t._v(\"14.2：Element和BuildContext\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter14/render_object.html\"}},[t._v(\"14.3：RenderObject与RenderBox\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter14/flutter_app_startup.html\"}},[t._v(\"14.4：Flutter从启动到显示\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter14/image_and_cache.html\"}},[t._v(\"14.5：Flutter图片加载与缓存\")])],1)])])]),t._v(\" \"),e(\"h2\",{attrs:{id:\"实例篇\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#实例篇\"}},[t._v(\"#\")]),t._v(\" 实例篇\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter15/intro.html\"}},[t._v(\"第十五章：一个完整的Flutter应用\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter15/intro.html\"}},[t._v(\"15.1：应用简介\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter15/code_structure.html\"}},[t._v(\"15.2：APP代码结构\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter15/models.html\"}},[t._v(\"15.3：Model类定义\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter15/globals.html\"}},[t._v(\"15.4：全局变量及共享状态\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter15/network.html\"}},[t._v(\"15.5：网络请求封装\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter15/entry.html\"}},[t._v(\"15.6：App入口及首页\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter15/login_page.html\"}},[t._v(\"15.7：登录页\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/chapter15/language_and_theme_setting.html\"}},[t._v(\"15.8：多语言和多主题\")])],1)])],1)])])}),[],!1,null,null,null);r.default=a.exports}}]);"
  },
  {
    "path": "docs/assets/js/18.318a79e1.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[18],{605:function(t,s,n){t.exports=n.p+\"assets/img/3-24.68d03561.png\"},606:function(t,s,n){t.exports=n.p+\"assets/img/3-25.18d09233.png\"},607:function(t,s){t.exports=\"data:image/png;base64,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\"},608:function(t,s,n){t.exports=n.p+\"assets/img/3-27.6ff2b58c.png\"},609:function(t,s,n){t.exports=n.p+\"assets/img/3-28.b6b9c9b2.png\"},610:function(t,s,n){t.exports=n.p+\"assets/img/3-29.ff4de036.png\"},889:function(t,s,n){\"use strict\";n.r(s);var a=n(45),e=Object(a.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_3-7-输入框及表单\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-7-输入框及表单\"}},[t._v(\"#\")]),t._v(\" 3.7 输入框及表单\")]),t._v(\" \"),a(\"p\",[t._v(\"Material组件库中提供了输入框组件\"),a(\"code\",[t._v(\"TextField\")]),t._v(\"和表单组件\"),a(\"code\",[t._v(\"Form\")]),t._v(\"。下面我们分别介绍一下。\")]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_3-7-1-textfield\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-7-1-textfield\"}},[t._v(\"#\")]),t._v(\" 3.7.1 TextField\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"TextField\")]),t._v(\"用于文本输入，它提供了很多属性，我们先简单介绍一下主要属性的作用，然后通过几个示例来演示一下关键属性的用法。\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  TextEditingController controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n  FocusNode focusNode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  InputDecoration decoration \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"InputDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  TextInputType keyboardType\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  TextInputAction textInputAction\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  TextStyle style\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  TextAlign textAlign \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" TextAlign\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  bool autofocus \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  bool obscureText \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  int maxLines \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  int maxLength\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  bool maxLengthEnforced \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  ValueChanged\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" onChanged\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  VoidCallback onEditingComplete\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  ValueChanged\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" onSubmitted\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  List\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"TextInputFormatter\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" inputFormatters\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  bool enabled\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cursorWidth \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cursorRadius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cursorColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"ul\",[a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"controller\")]),t._v(\"：编辑框的控制器，通过它可以设置/获取编辑框的内容、选择编辑内容、监听编辑文本改变事件。大多数情况下我们都需要显式提供一个\"),a(\"code\",[t._v(\"controller\")]),t._v(\"来与文本框交互。如果没有提供\"),a(\"code\",[t._v(\"controller\")]),t._v(\"，则\"),a(\"code\",[t._v(\"TextField\")]),t._v(\"内部会自动创建一个。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"focusNode\")]),t._v(\"：用于控制\"),a(\"code\",[t._v(\"TextField\")]),t._v(\"是否占有当前键盘的输入焦点。它是我们和键盘交互的一个句柄（handle）。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"InputDecoration\")]),t._v(\"：用于控制\"),a(\"code\",[t._v(\"TextField\")]),t._v(\"的外观显示，如提示文本、背景颜色、边框等。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"keyboardType\")]),t._v(\"：用于设置该输入框默认的键盘输入类型，取值如下：\")]),t._v(\" \"),a(\"table\",[a(\"thead\",[a(\"tr\",[a(\"th\",[t._v(\"TextInputType枚举值\")]),t._v(\" \"),a(\"th\",[t._v(\"含义\")])])]),t._v(\" \"),a(\"tbody\",[a(\"tr\",[a(\"td\",[t._v(\"text\")]),t._v(\" \"),a(\"td\",[t._v(\"文本输入键盘\")])]),t._v(\" \"),a(\"tr\",[a(\"td\",[t._v(\"multiline\")]),t._v(\" \"),a(\"td\",[t._v(\"多行文本，需和maxLines配合使用(设为null或大于1)\")])]),t._v(\" \"),a(\"tr\",[a(\"td\",[t._v(\"number\")]),t._v(\" \"),a(\"td\",[t._v(\"数字；会弹出数字键盘\")])]),t._v(\" \"),a(\"tr\",[a(\"td\",[t._v(\"phone\")]),t._v(\" \"),a(\"td\",[t._v(\"优化后的电话号码输入键盘；会弹出数字键盘并显示“* #”\")])]),t._v(\" \"),a(\"tr\",[a(\"td\",[t._v(\"datetime\")]),t._v(\" \"),a(\"td\",[t._v(\"优化后的日期输入键盘；Android上会显示“: -”\")])]),t._v(\" \"),a(\"tr\",[a(\"td\",[t._v(\"emailAddress\")]),t._v(\" \"),a(\"td\",[t._v(\"优化后的电子邮件地址；会显示“@ .”\")])]),t._v(\" \"),a(\"tr\",[a(\"td\",[t._v(\"url\")]),t._v(\" \"),a(\"td\",[t._v(\"优化后的url输入键盘； 会显示“/ .”\")])])])])]),t._v(\" \"),a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"textInputAction\")]),t._v(\"：键盘动作按钮图标(即回车键位图标)，它是一个枚举值，有多个可选值，全部的取值列表读者可以查看API文档，下面是当值为\"),a(\"code\",[t._v(\"TextInputAction.search\")]),t._v(\"时，原生Android系统下键盘样式如图3-24所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(605),alt:\"图3-24\"}})])]),t._v(\" \"),a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"style\")]),t._v(\"：正在编辑的文本样式。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"textAlign\")]),t._v(\": 输入框内编辑文本在水平方向的对齐方式。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"autofocus\")]),t._v(\": 是否自动获取焦点。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"obscureText\")]),t._v(\"：是否隐藏正在编辑的文本，如用于输入密码的场景等，文本内容会用“•”替换。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"maxLines\")]),t._v(\"：输入框的最大行数，默认为1；如果为\"),a(\"code\",[t._v(\"null\")]),t._v(\"，则无行数限制。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"maxLength\")]),t._v(\"和\"),a(\"code\",[t._v(\"maxLengthEnforced\")]),t._v(\" ：\"),a(\"code\",[t._v(\"maxLength\")]),t._v(\"代表输入框文本的最大长度，设置后输入框右下角会显示输入的文本计数。\"),a(\"code\",[t._v(\"maxLengthEnforced\")]),t._v(\"决定当输入文本长度超过\"),a(\"code\",[t._v(\"maxLength\")]),t._v(\"时是否阻止输入，为\"),a(\"code\",[t._v(\"true\")]),t._v(\"时会阻止输入，为\"),a(\"code\",[t._v(\"false\")]),t._v(\"时不会阻止输入但输入框会变红。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"onChange\")]),t._v(\"：输入框内容改变时的回调函数；注：内容改变事件也可以通过\"),a(\"code\",[t._v(\"controller\")]),t._v(\"来监听。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"onEditingComplete\")]),t._v(\"和\"),a(\"code\",[t._v(\"onSubmitted\")]),t._v(\"：这两个回调都是在输入框输入完成时触发，比如按了键盘的完成键（对号图标）或搜索键（🔍图标）。不同的是两个回调签名不同，\"),a(\"code\",[t._v(\"onSubmitted\")]),t._v(\"回调是\"),a(\"code\",[t._v(\"ValueChanged<String>\")]),t._v(\"类型，它接收当前输入内容做为参数，而\"),a(\"code\",[t._v(\"onEditingComplete\")]),t._v(\"不接收参数。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"inputFormatters\")]),t._v(\"：用于指定输入格式；当用户输入内容改变时，会根据指定的格式来校验。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"enable\")]),t._v(\"：如果为\"),a(\"code\",[t._v(\"false\")]),t._v(\"，则输入框会被禁用，禁用状态不接收输入和事件，同时显示禁用态样式（在其\"),a(\"code\",[t._v(\"decoration\")]),t._v(\"中定义）。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[a(\"code\",[t._v(\"cursorWidth\")]),t._v(\"、\"),a(\"code\",[t._v(\"cursorRadius\")]),t._v(\"和\"),a(\"code\",[t._v(\"cursorColor\")]),t._v(\"：这三个属性是用于自定义输入框光标宽度、圆角和颜色的。\")])])]),t._v(\" \"),a(\"h4\",{attrs:{id:\"示例-登录输入框\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例-登录输入框\"}},[t._v(\"#\")]),t._v(\" 示例：登录输入框\")]),t._v(\" \"),a(\"h5\",{attrs:{id:\"布局\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#布局\"}},[t._v(\"#\")]),t._v(\" 布局\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            autofocus\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"InputDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                labelText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"用户名\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                hintText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"用户名或邮箱\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                prefixIcon\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"person\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"InputDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                labelText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"密码\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                hintText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"您的登录密码\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                prefixIcon\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"lock\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            obscureText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"运行后，效果如图3-25所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(606),alt:\"图3-25\"}})]),t._v(\" \"),a(\"h5\",{attrs:{id:\"获取输入内容\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#获取输入内容\"}},[t._v(\"#\")]),t._v(\" 获取输入内容\")]),t._v(\" \"),a(\"p\",[t._v(\"获取输入内容有两种方式：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"定义两个变量，用于保存用户名和密码，然后在\"),a(\"code\",[t._v(\"onChange\")]),t._v(\"触发时，各自保存一下输入内容。\")]),t._v(\" \"),a(\"li\",[t._v(\"通过\"),a(\"code\",[t._v(\"controller\")]),t._v(\"直接获取。\")])]),t._v(\" \"),a(\"p\",[t._v(\"第一种方式比较简单，不在举例，我们来重点看一下第二种方式，我们以用户名输入框举例：\")]),t._v(\" \"),a(\"p\",[t._v(\"定义一个\"),a(\"code\",[t._v(\"controller\")]),t._v(\"：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//定义一个controller\")]),t._v(\"\\nTextEditingController _unameController \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextEditingController\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"然后设置输入框controller：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    autofocus\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _unameController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//设置controller\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"通过controller获取输入框内容\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_unameController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"text\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"h5\",{attrs:{id:\"监听文本变化\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#监听文本变化\"}},[t._v(\"#\")]),t._v(\" 监听文本变化\")]),t._v(\" \"),a(\"p\",[t._v(\"监听文本变化也有两种方式：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[a(\"p\",[t._v(\"设置\"),a(\"code\",[t._v(\"onChange\")]),t._v(\"回调，如：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    autofocus\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    onChanged\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"v\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"onChange: $v\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"通过\"),a(\"code\",[t._v(\"controller\")]),t._v(\"监听，如：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//监听输入改变  \")]),t._v(\"\\n  _unameController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addListener\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_unameController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"text\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])])])]),t._v(\" \"),a(\"p\",[t._v(\"两种方式相比，\"),a(\"code\",[t._v(\"onChanged\")]),t._v(\"是专门用于监听文本变化，而\"),a(\"code\",[t._v(\"controller\")]),t._v(\"的功能却多一些，除了能监听文本变化外，它还可以设置默认值、选择文本，下面我们看一个例子：\")]),t._v(\" \"),a(\"p\",[t._v(\"创建一个\"),a(\"code\",[t._v(\"controller\")]),t._v(\":\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[t._v(\"TextEditingController _selectionController \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextEditingController\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"设置默认值，并从第三个字符开始选中后面的字符\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[t._v(\"_selectionController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"text\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"hello world!\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n_selectionController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"selection\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextSelection\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    baseOffset\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    extentOffset\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _selectionController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"text\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"length\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"设置\"),a(\"code\",[t._v(\"controlle\")]),t._v(\"r:\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _selectionController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"运行效果如图3-26所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(607),alt:\"图3-26\"}})]),t._v(\" \"),a(\"h5\",{attrs:{id:\"控制焦点\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#控制焦点\"}},[t._v(\"#\")]),t._v(\" 控制焦点\")]),t._v(\" \"),a(\"p\",[t._v(\"焦点可以通过\"),a(\"code\",[t._v(\"FocusNode\")]),t._v(\"和\"),a(\"code\",[t._v(\"FocusScopeNode\")]),t._v(\"来控制，默认情况下，焦点由\"),a(\"code\",[t._v(\"FocusScope\")]),t._v(\"来管理，它代表焦点控制范围，可以在这个范围内可以通过\"),a(\"code\",[t._v(\"FocusScopeNode\")]),t._v(\"在输入框之间移动焦点、设置默认焦点等。我们可以通过\"),a(\"code\",[t._v(\"FocusScope.of(context)\")]),t._v(\" 来获取Widget树中默认的\"),a(\"code\",[t._v(\"FocusScopeNode\")]),t._v(\"。下面看一个示例，在此示例中创建两个\"),a(\"code\",[t._v(\"TextField\")]),t._v(\"，第一个自动获取焦点，然后创建两个按钮：\")]),t._v(\" \"),a(\"ul\",[a(\"li\",[t._v(\"点击第一个按钮可以将焦点从第一个\"),a(\"code\",[t._v(\"TextField\")]),t._v(\"挪到第二个\"),a(\"code\",[t._v(\"TextField\")]),t._v(\"。\")]),t._v(\" \"),a(\"li\",[t._v(\"点击第二个按钮可以关闭键盘。\")])]),t._v(\" \"),a(\"p\",[t._v(\"我们要实现的效果如图3-27所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(608),alt:\"图3-27\"}})]),t._v(\" \"),a(\"p\",[t._v(\"代码如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FocusTestRoute\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _FocusTestRouteState \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_FocusTestRouteState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_FocusTestRouteState\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"FocusTestRoute\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  FocusNode focusNode1 \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FocusNode\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  FocusNode focusNode2 \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FocusNode\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  FocusScopeNode focusScopeNode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            autofocus\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n            focusNode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" focusNode1\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//关联focusNode1\")]),t._v(\"\\n            decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"InputDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                labelText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"input1\"')]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            focusNode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" focusNode2\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//关联focusNode2\")]),t._v(\"\\n            decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"InputDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                labelText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"input2\"')]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ctx\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"移动焦点\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//将焦点从第一个TextField移到第二个TextField\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 这是一种写法 FocusScope.of(context).requestFocus(focusNode2);\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 这是第二种写法\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" focusScopeNode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                      focusScopeNode \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" FocusScope\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n                    focusScopeNode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"requestFocus\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"focusNode2\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"隐藏键盘\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 当所有编辑框都失去焦点时键盘就会收起  \")]),t._v(\"\\n                    focusNode1\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"unfocus\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                    focusNode2\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"unfocus\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[a(\"code\",[t._v(\"FocusNode\")]),t._v(\"和\"),a(\"code\",[t._v(\"FocusScopeNode\")]),t._v(\"还有一些其它的方法，详情可以查看API文档。\")]),t._v(\" \"),a(\"h5\",{attrs:{id:\"监听焦点状态改变事件\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#监听焦点状态改变事件\"}},[t._v(\"#\")]),t._v(\" 监听焦点状态改变事件\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"FocusNode\")]),t._v(\"继承自\"),a(\"code\",[t._v(\"ChangeNotifier\")]),t._v(\"，通过\"),a(\"code\",[t._v(\"FocusNode\")]),t._v(\"可以监听焦点的改变事件，如：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 创建 focusNode   \")]),t._v(\"\\nFocusNode focusNode \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FocusNode\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// focusNode绑定输入框   \")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"focusNode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" focusNode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 监听焦点变化    \")]),t._v(\"\\nfocusNode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addListener\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"focusNode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"hasFocus\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"获得焦点时\"),a(\"code\",[t._v(\"focusNode.hasFocus\")]),t._v(\"值为\"),a(\"code\",[t._v(\"true\")]),t._v(\"，失去焦点时为\"),a(\"code\",[t._v(\"false\")]),t._v(\"。\")]),t._v(\" \"),a(\"h5\",{attrs:{id:\"自定义样式\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#自定义样式\"}},[t._v(\"#\")]),t._v(\" 自定义样式\")]),t._v(\" \"),a(\"p\",[t._v(\"虽然我们可以通过\"),a(\"code\",[t._v(\"decoration\")]),t._v(\"属性来定义输入框样式，下面以自定义输入框下划线颜色为例来介绍一下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"InputDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    labelText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"请输入用户名\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    prefixIcon\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"person\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 未获得焦点下划线设为灰色\")]),t._v(\"\\n    enabledBorder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"UnderlineInputBorder\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      borderSide\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BorderSide\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//获得焦点下划线设为蓝色\")]),t._v(\"\\n    focusedBorder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"UnderlineInputBorder\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      borderSide\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BorderSide\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"上面代码我们直接通过InputDecoration的enabledBorder和focusedBorder来分别设置了输入框在未获取焦点和获得焦点后的下划线颜色。另外，我们也可以通过主题来自定义输入框的样式，下面我们探索一下如何在不使用enabledBorder和focusedBorder的情况下来自定义下滑线颜色。\")]),t._v(\" \"),a(\"p\",[t._v(\"由于\"),a(\"code\",[t._v(\"TextField\")]),t._v(\"在绘制下划线时使用的颜色是主题色里面的\"),a(\"code\",[t._v(\"hintColor\")]),t._v(\"，但提示文本颜色也是用的\"),a(\"code\",[t._v(\"hintColor\")]),t._v(\"， 如果我们直接修改\"),a(\"code\",[t._v(\"hintColor\")]),t._v(\"，那么下划线和提示文本的颜色都会变。值得高兴的是\"),a(\"code\",[t._v(\"decoration\")]),t._v(\"中可以设置\"),a(\"code\",[t._v(\"hintStyle\")]),t._v(\"，它可以覆盖\"),a(\"code\",[t._v(\"hintColor\")]),t._v(\"，并且主题中可以通过\"),a(\"code\",[t._v(\"inputDecorationTheme\")]),t._v(\"来设置输入框默认的\"),a(\"code\",[t._v(\"decoration\")]),t._v(\"。所以我们可以通过主题来自定义，代码如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Theme\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  data\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Theme\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"copyWith\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      hintColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//定义下划线颜色\")]),t._v(\"\\n      inputDecorationTheme\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"InputDecorationTheme\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          labelStyle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//定义label字体样式\")]),t._v(\"\\n          hintStyle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" fontSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"14.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//定义提示文本样式\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"InputDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            labelText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"用户名\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            hintText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"用户名或邮箱\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            prefixIcon\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"person\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"InputDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            prefixIcon\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"lock\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            labelText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"密码\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            hintText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"您的登录密码\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            hintStyle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" fontSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"13.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        obscureText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"运行效果如图3-28所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(609),alt:\"图3-28\"}})]),t._v(\" \"),a(\"p\",[t._v(\"我们成功的自定义了下划线颜色和提问文字样式，细心的读者可能已经发现，通过这种方式自定义后，输入框在获取焦点时，\"),a(\"code\",[t._v(\"labelText\")]),t._v('不会高亮显示了，正如上图中的\"用户名\"本应该显示蓝色，但现在却显示为灰色，并且我们还是无法定义下划线宽度。另一种灵活的方式是直接隐藏掉'),a(\"code\",[t._v(\"TextField\")]),t._v(\"本身的下划线，然后通过\"),a(\"code\",[t._v(\"Container\")]),t._v(\"去嵌套定义样式，如:\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    keyboardType\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" TextInputType\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"emailAddress\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"InputDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        labelText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Email\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        hintText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"电子邮件地址\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        prefixIcon\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"email\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        border\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" InputBorder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"none \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//隐藏下划线\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 下滑线浅灰色，宽度1像素\")]),t._v(\"\\n      border\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Border\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"bottom\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BorderSide\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"运行效果：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:\"https://cdn.jsdelivr.net/gh/flutterchina/flutter-in-action@1.0/docs/imgs/image-20180904150511545.png\",alt:\"image-20180904150511545\"}})]),t._v(\" \"),a(\"p\",[t._v(\"通过这种组件组合的方式，也可以定义背景圆角等。一般来说，优先通过\"),a(\"code\",[t._v(\"decoration\")]),t._v(\"来自定义样式，如果\"),a(\"code\",[t._v(\"decoration\")]),t._v(\"实现不了，再用widget组合的方式。\")]),t._v(\" \"),a(\"blockquote\",[a(\"p\",[t._v(\"思考题：在这个示例中，下划线颜色是固定的，所以获得焦点后颜色仍然为灰色，如何实现点击后下滑线也变色呢？\")])]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_3-7-2-表单form\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-7-2-表单form\"}},[t._v(\"#\")]),t._v(\" 3.7.2 表单Form\")]),t._v(\" \"),a(\"p\",[t._v(\"实际业务中，在正式向服务器提交数据前，都会对各个输入框数据进行合法性校验，但是对每一个\"),a(\"code\",[t._v(\"TextField\")]),t._v(\"都分别进行校验将会是一件很麻烦的事。还有，如果用户想清除一组\"),a(\"code\",[t._v(\"TextField\")]),t._v(\"的内容，除了一个一个清除有没有什么更好的办法呢？为此，Flutter提供了一个\"),a(\"code\",[t._v(\"Form\")]),t._v(\" 组件，它可以对输入框进行分组，然后进行一些统一操作，如输入内容校验、输入框重置以及输入内容保存。\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"form\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#form\"}},[t._v(\"#\")]),t._v(\" Form\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"Form\")]),t._v(\"继承自\"),a(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"对象，它对应的状态类为\"),a(\"code\",[t._v(\"FormState\")]),t._v(\"。我们先看看\"),a(\"code\",[t._v(\"Form\")]),t._v(\"类的定义：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Form\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" Widget child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  bool autovalidate \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  WillPopCallback onWillPop\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  VoidCallback onChanged\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"ul\",[a(\"li\",[a(\"code\",[t._v(\"autovalidate\")]),t._v(\"：是否自动校验输入内容；当为\"),a(\"code\",[t._v(\"true\")]),t._v(\"时，每一个子FormField内容发生变化时都会自动校验合法性，并直接显示错误信息。否则，需要通过调用\"),a(\"code\",[t._v(\"FormState.validate()\")]),t._v(\"来手动校验。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"onWillPop\")]),t._v(\"：决定\"),a(\"code\",[t._v(\"Form\")]),t._v(\"所在的路由是否可以直接返回（如点击返回按钮），该回调返回一个\"),a(\"code\",[t._v(\"Future\")]),t._v(\"对象，如果Future的最终结果是\"),a(\"code\",[t._v(\"false\")]),t._v(\"，则当前路由不会返回；如果为\"),a(\"code\",[t._v(\"true\")]),t._v(\"，则会返回到上一个路由。此属性通常用于拦截返回按钮。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"onChanged\")]),t._v(\"：\"),a(\"code\",[t._v(\"Form\")]),t._v(\"的任意一个子\"),a(\"code\",[t._v(\"FormField\")]),t._v(\"内容发生变化时会触发此回调。\")])]),t._v(\" \"),a(\"h4\",{attrs:{id:\"formfield\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#formfield\"}},[t._v(\"#\")]),t._v(\" FormField\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"Form\")]),t._v(\"的子孙元素必须是\"),a(\"code\",[t._v(\"FormField\")]),t._v(\"类型，\"),a(\"code\",[t._v(\"FormField\")]),t._v(\"是一个抽象类，定义几个属性，\"),a(\"code\",[t._v(\"FormState\")]),t._v(\"内部通过它们来完成操作，\"),a(\"code\",[t._v(\"FormField\")]),t._v(\"部分定义如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FormField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  FormFieldSetter\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" onSaved\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//保存回调\")]),t._v(\"\\n  FormFieldValidator\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"  validator\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//验证回调\")]),t._v(\"\\n  T initialValue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//初始值\")]),t._v(\"\\n  bool autovalidate \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//是否自动校验。\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"为了方便使用，Flutter提供了一个\"),a(\"code\",[t._v(\"TextFormField\")]),t._v(\"组件，它继承自\"),a(\"code\",[t._v(\"FormField\")]),t._v(\"类，也是\"),a(\"code\",[t._v(\"TextField\")]),t._v(\"的一个包装类，所以除了\"),a(\"code\",[t._v(\"FormField\")]),t._v(\"定义的属性之外，它还包括\"),a(\"code\",[t._v(\"TextField\")]),t._v(\"的属性。\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"formstate\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#formstate\"}},[t._v(\"#\")]),t._v(\" FormState\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"FormState\")]),t._v(\"为\"),a(\"code\",[t._v(\"Form\")]),t._v(\"的\"),a(\"code\",[t._v(\"State\")]),t._v(\"类，可以通过\"),a(\"code\",[t._v(\"Form.of()\")]),t._v(\"或\"),a(\"code\",[t._v(\"GlobalKey\")]),t._v(\"获得。我们可以通过它来对\"),a(\"code\",[t._v(\"Form\")]),t._v(\"的子孙\"),a(\"code\",[t._v(\"FormField\")]),t._v(\"进行统一操作。我们看看其常用的三个方法：\")]),t._v(\" \"),a(\"ul\",[a(\"li\",[a(\"code\",[t._v(\"FormState.validate()\")]),t._v(\"：调用此方法后，会调用\"),a(\"code\",[t._v(\"Form\")]),t._v(\"子孙\"),a(\"code\",[t._v(\"FormField的validate\")]),t._v(\"回调，如果有一个校验失败，则返回false，所有校验失败项都会返回用户返回的错误提示。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"FormState.save()\")]),t._v(\"：调用此方法后，会调用\"),a(\"code\",[t._v(\"Form\")]),t._v(\"子孙\"),a(\"code\",[t._v(\"FormField\")]),t._v(\"的\"),a(\"code\",[t._v(\"save\")]),t._v(\"回调，用于保存表单内容\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"FormState.reset()\")]),t._v(\"：调用此方法后，会将子孙\"),a(\"code\",[t._v(\"FormField\")]),t._v(\"的内容清空。\")])]),t._v(\" \"),a(\"h4\",{attrs:{id:\"示例\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),a(\"p\",[t._v(\"我们修改一下上面用户登录的示例，在提交之前校验：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"用户名不能为空，如果为空则提示“用户名不能为空”。\")]),t._v(\" \"),a(\"li\",[t._v(\"密码不能小于6位，如果小于6为则提示“密码不能少于6位”。\")])]),t._v(\" \"),a(\"p\",[t._v(\"完整代码：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FormTestRoute\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _FormTestRouteState \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_FormTestRouteState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_FormTestRouteState\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"FormTestRoute\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  TextEditingController _unameController \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TextEditingController\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  TextEditingController _pwdController \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TextEditingController\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  GlobalKey _formKey\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"GlobalKey\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"FormState\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      appBar\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        title\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Form Test\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      body\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"symmetric\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"vertical\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" horizontal\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"24.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Form\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _formKey\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//设置globalKey，用于后面获取FormState\")]),t._v(\"\\n          autovalidate\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//开启自动校验\")]),t._v(\"\\n          child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextFormField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  autofocus\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _unameController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"InputDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                      labelText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"用户名\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      hintText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"用户名或邮箱\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      icon\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"person\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 校验用户名\")]),t._v(\"\\n                  validator\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"v\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" v\\n                        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"trim\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"length \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"用户名不能为空\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextFormField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _pwdController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"InputDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                      labelText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"密码\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      hintText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"您的登录密码\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      icon\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"lock\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  obscureText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//校验密码\")]),t._v(\"\\n                  validator\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"v\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" v\\n                        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"trim\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"length \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"5\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"密码不能少于6位\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 登录按钮\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"only\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"top\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"28.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Expanded\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                        padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"15.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"登录\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Theme\\n                            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"primaryColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        textColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//在这里不能通过此方式获取FormState，context不对\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//print(Form.of(context));\")]),t._v(\"\\n                            \\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 通过_formKey.currentState 获取FormState后，\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 调用validate()方法校验用户名密码是否合法，校验\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 通过后再提交数据。 \")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_formKey\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"currentState \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"as\")]),t._v(\" FormState\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"validate\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                            \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//验证通过提交数据\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n                        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"运行后效果如图3-29所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(610),alt:\"图3-29\"}})]),t._v(\" \"),a(\"p\",[t._v(\"注意，登录按钮的\"),a(\"code\",[t._v(\"onPressed\")]),t._v(\"方法中不能通过\"),a(\"code\",[t._v(\"Form.of(context)\")]),t._v(\"来获取，原因是，此处的\"),a(\"code\",[t._v(\"context\")]),t._v(\"为\"),a(\"code\",[t._v(\"FormTestRoute\")]),t._v(\"的context，而\"),a(\"code\",[t._v(\"Form.of(context)\")]),t._v(\"是根据所指定\"),a(\"code\",[t._v(\"context\")]),t._v(\"向根去查找，而\"),a(\"code\",[t._v(\"FormState\")]),t._v(\"是在\"),a(\"code\",[t._v(\"FormTestRoute\")]),t._v(\"的子树中，所以不行。正确的做法是通过\"),a(\"code\",[t._v(\"Builder\")]),t._v(\"来构建登录按钮，\"),a(\"code\",[t._v(\"Builder\")]),t._v(\"会将\"),a(\"code\",[t._v(\"widget\")]),t._v(\"节点的\"),a(\"code\",[t._v(\"context\")]),t._v(\"作为回调参数：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Expanded\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 通过Builder来获取RaisedButton所在widget树的真正context(Element) \")]),t._v(\"\\n  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n      onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//由于本widget也是Form的子代widget，所以可以通过下面方式获取FormState  \")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Form\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"validate\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//验证通过提交数据\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"其实\"),a(\"code\",[t._v(\"context\")]),t._v(\"正是操作Widget所对应的\"),a(\"code\",[t._v(\"Element\")]),t._v(\"的一个接口，由于Widget树对应的\"),a(\"code\",[t._v(\"Element\")]),t._v(\"都是不同的，所以\"),a(\"code\",[t._v(\"context\")]),t._v(\"也都是不同的，有关\"),a(\"code\",[t._v(\"context\")]),t._v(\"的更多内容会在后面高级部分详细讨论。Flutter中有很多“of(context)”这种方法，读者在使用时一定要注意\"),a(\"code\",[t._v(\"context\")]),t._v(\"是否正确。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/180.d048122a.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[180],{941:function(t,a,s){\"use strict\";s.r(a);var n=s(45),r=Object(n.a)({},(function(){var t=this,a=t.$createElement,s=t._self._c||a;return s(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[s(\"h1\",{attrs:{id:\"_1-4-dart语言简介\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-4-dart语言简介\"}},[t._v(\"#\")]),t._v(\" 1.4 Dart语言简介\")]),t._v(\" \"),s(\"p\",[t._v(\"在之前我们已经介绍过Dart语言的相关特性，读者可以翻看一下，如果读者已经熟悉Dart语法，可以跳过本节，如果你还不了解Dart，也不用担心，按照笔者经验，如果你有过其他编程语言经验（尤其是Java和JavaScript）的话会非常容易上手Dart。当然，如果你是iOS开发者，也不用担心，Dart中也有一些与Swift比较相似的特性，如命名参数等，笔者当时学习Dart时，只是花了一个小时，看完Dart官网的Language Tour，就开始动手写Flutter了。\")]),t._v(\" \"),s(\"p\",[t._v(\"在笔者看来，Dart的设计目标应该是同时借鉴了Java和JavaScript。Dart在静态语法方面和Java非常相似，如类型定义、函数声明、泛型等，而在动态特性方面又和JavaScript很像，如函数式特性、异步支持等。除了融合Java和JavaScript语言之所长之外，Dart也具有一些其它具有表现力的语法，如可选命名参数、\"),s(\"code\",[t._v(\"..\")]),t._v(\"（级联运算符）和\"),s(\"code\",[t._v(\"?.\")]),t._v(\"（条件成员访问运算符）以及\"),s(\"code\",[t._v(\"??\")]),t._v(\"（判空赋值运算符）。其实，对编程语言了解比较多的读者会发现，在Dart中其实看到的不仅有Java和JavaScript的影子，它还具有其它编程语言中的身影，如命名参数在Objective-C和Swift中早就很普遍，而\"),s(\"code\",[t._v(\"??\")]),t._v(\"操作符在PHP 7.0语法中就已经存在了，因此我们可以看到Google对Dart语言给予厚望，是想把Dart打造成一门集百家之所长的编程语言。\")]),t._v(\" \"),s(\"p\",[t._v(\"接下来，我们先对Dart语法做一个简单的介绍，然后再将Dart与JavaScript和Java做一个简要的对比，方便读者更好的理解。\")]),t._v(\" \"),s(\"blockquote\",[s(\"p\",[t._v(\"注意：由于本书并非专门介绍Dart语言的书籍，所以本章主要会介绍一下在Flutter开发中常用的语法特性，如果想更多了解Dart，读者可以去Dart官网学习，现在互联网上Dart相关资料已经很多了。另外Dart 2.0已经正式发布，所以本书所有示例均采用Dart 2.0语法。\")])]),t._v(\" \"),s(\"h2\",{attrs:{id:\"_1-4-1-变量声明\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-4-1-变量声明\"}},[t._v(\"#\")]),t._v(\" 1.4.1 变量声明\")]),t._v(\" \"),s(\"ol\",[s(\"li\",[s(\"p\",[s(\"strong\",[t._v(\"var\")])]),t._v(\" \"),s(\"p\",[t._v(\"类似于JavaScript中的\"),s(\"code\",[t._v(\"var\")]),t._v(\"，它可以接收任何类型的变量，但最大的不同是Dart中var变量一旦赋值，类型便会确定，则不能再改变其类型，如：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" t\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\nt \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"hi world\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 下面代码在dart中会报错，因为变量t的类型已经确定为String，\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 类型一旦确定后则不能再更改其类型。\")]),t._v(\"\\nt \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1000\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"上面的代码在JavaScript是没有问题的，前端开发者需要注意一下，之所以有此差异是因为Dart本身是一个强类型语言，任何变量都是有确定类型的，在Dart中，当用\"),s(\"code\",[t._v(\"var\")]),t._v(\"声明一个变量后，Dart在编译时会根据第一次赋值数据的类型来推断其类型，编译结束后其类型就已经被确定，而JavaScript是纯粹的弱类型脚本语言，var只是变量的声明方式而已。\")])]),t._v(\" \"),s(\"li\",[s(\"p\",[s(\"strong\",[t._v(\"dynamic\")]),t._v(\"和\"),s(\"strong\",[t._v(\"Object\")])]),t._v(\" \"),s(\"p\",[s(\"code\",[t._v(\"Object\")]),t._v(\" 是Dart所有对象的根基类，也就是说所有类型都是\"),s(\"code\",[t._v(\"Object\")]),t._v(\"的子类(包括Function和Null)，所以任何类型的数据都可以赋值给\"),s(\"code\",[t._v(\"Object\")]),t._v(\"声明的对象.\\n\"),s(\"code\",[t._v(\"dynamic\")]),t._v(\"与\"),s(\"code\",[t._v(\"var\")]),t._v(\"一样都是关键词,声明的变量可以赋值任意对象。\\n而\"),s(\"code\",[t._v(\"dynamic\")]),t._v(\"与\"),s(\"code\",[t._v(\"Object\")]),t._v(\"相同之处在于,他们声明的变量可以在后期改变赋值类型。\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),t._v(\" t\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\nObject x\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\nt \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"hi world\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\nx \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Hello Object'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//下面代码没有问题\")]),t._v(\"\\nt \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1000\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\nx \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1000\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"p\",[s(\"code\",[t._v(\"dynamic\")]),t._v(\"与\"),s(\"code\",[t._v(\"Object\")]),t._v(\"不同的是,\"),s(\"code\",[t._v(\"dynamic\")]),t._v(\"声明的对象编译器会提供所有可能的组合,\\n而\"),s(\"code\",[t._v(\"Object\")]),t._v(\"声明的对象只能使用Object的属性与方法, 否则编译器会报错。如:\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),t._v(\" a\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n Object b\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"main\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n     a \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n     b \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n     \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"printLengths\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"   \\n\\n \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"printLengths\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n     \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// no warning\")]),t._v(\"\\n     \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"a\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"length\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n     \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// warning:\")]),t._v(\"\\n     \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// The getter 'length' is not defined for the class 'Object'\")]),t._v(\"\\n     \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"b\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"length\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"变量a不会报错, 变量b编译器会报错\")]),t._v(\" \"),s(\"p\",[s(\"code\",[t._v(\"dynamic\")]),t._v(\"的这个特性与\"),s(\"code\",[t._v(\"Objective-C\")]),t._v(\"中的\"),s(\"code\",[t._v(\"id\")]),t._v(\"作用很像.\\n\"),s(\"code\",[t._v(\"dynamic\")]),t._v(\"的这个特点使得我们在使用它时需要格外注意,这很容易引入一个运行时错误.\")])]),t._v(\" \"),s(\"li\",[s(\"p\",[s(\"strong\",[t._v(\"final\")]),t._v(\"和\"),s(\"strong\",[t._v(\"const\")])]),t._v(\" \"),s(\"p\",[t._v(\"如果您从未打算更改一个变量，那么使用 \"),s(\"code\",[t._v(\"final\")]),t._v(\" 或 \"),s(\"code\",[t._v(\"const\")]),t._v(\"，不是\"),s(\"code\",[t._v(\"var\")]),t._v(\"，也不是一个类型。 一个 \"),s(\"code\",[t._v(\"final\")]),t._v(\" 变量只能被设置一次，两者区别在于：\"),s(\"code\",[t._v(\"const\")]),t._v(\" 变量是一个编译时常量，\"),s(\"code\",[t._v(\"final\")]),t._v(\"变量在第一次使用时被初始化。被\"),s(\"code\",[t._v(\"final\")]),t._v(\"或者\"),s(\"code\",[t._v(\"const\")]),t._v(\"修饰的变量，变量类型可以省略，如：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//可以省略String这个类型声明\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" str \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"hi world\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v('//final String str = \"hi world\"; ')]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" str1 \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"hi world\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v('//const String str1 = \"hi world\";')]),t._v(\"\\n\")])])])])]),t._v(\" \"),s(\"h2\",{attrs:{id:\"_1-4-2-函数\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-4-2-函数\"}},[t._v(\"#\")]),t._v(\" 1.4.2 函数\")]),t._v(\" \"),s(\"p\",[t._v(\"Dart是一种真正的面向对象的语言，所以即使是函数也是对象，并且有一个类型\"),s(\"strong\",[t._v(\"Function\")]),t._v(\"。这意味着函数可以赋值给变量或作为参数传递给其他函数，这是函数式编程的典型特征。\")]),t._v(\" \"),s(\"ol\",[s(\"li\",[s(\"p\",[t._v(\"函数声明\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"bool \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"isNoble\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int atomicNumber\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" _nobleGases\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"atomicNumber\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"Dart函数声明如果没有显式声明返回值类型时会默认当做\"),s(\"code\",[t._v(\"dynamic\")]),t._v(\"处理，注意，函数返回值没有类型推断：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"typedef\")]),t._v(\" bool \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CALLBACK\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//不指定返回类型，此时默认为dynamic，不是bool\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"isNoble\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int atomicNumber\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" _nobleGases\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"atomicNumber\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"test\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"CALLBACK cb\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"cb\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//报错，isNoble不是bool类型\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"test\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"isNoble\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])])]),t._v(\" \"),s(\"li\",[s(\"p\",[t._v(\"对于只包含一个表达式的函数，可以使用简写语法\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"bool isNoble \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int atomicNumber\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _nobleGases \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\" atomicNumber \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" ！\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"   \\n\")])])])]),t._v(\" \"),s(\"li\",[s(\"p\",[t._v(\"函数作为变量\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" say \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"str\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"str\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"say\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"hi world\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])])]),t._v(\" \"),s(\"li\",[s(\"p\",[t._v(\"函数作为参数传递\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"execute\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" callback\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"callback\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"execute\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"xxx\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])])]),t._v(\" \"),s(\"li\",[s(\"p\",[t._v(\"可选的位置参数\")]),t._v(\" \"),s(\"p\",[t._v(\"包装一组函数参数，用[]标记为可选的位置参数，并放在参数列表的最后面：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"String \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"say\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String from\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" String msg\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"String device\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" result \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'$from says $msg'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"device \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    result \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'$result with a $device'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" result\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"下面是一个不带可选参数调用这个函数的例子：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"say\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Bob'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Howdy'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//结果是： Bob says Howdy\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"下面是用第三个参数调用这个函数的例子：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"say\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Bob'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Howdy'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'smoke signal'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//结果是：Bob says Howdy with a smoke signal\")]),t._v(\"\\n\")])])])]),t._v(\" \"),s(\"li\",[s(\"p\",[t._v(\"可选的命名参数\")]),t._v(\" \"),s(\"p\",[t._v(\"定义函数时，使用{param1, param2, …}，放在参数列表的最后面，用于指定命名参数。例如：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//设置[bold]和[hidden]标志\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"enableFlags\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"bool bold\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" bool hidden\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// ... \")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"调用函数时，可以使用指定命名参数。例如：\"),s(\"code\",[t._v(\"paramName: value\")])]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"enableFlags\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"bold\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" hidden\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"可选命名参数在Flutter中使用非常多。\")]),t._v(\" \"),s(\"p\",[s(\"strong\",[t._v(\"注意，不能同时使用可选的位置参数和可选的命名参数\")])])])]),t._v(\" \"),s(\"h2\",{attrs:{id:\"_1-4-3-异步支持\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-4-3-异步支持\"}},[t._v(\"#\")]),t._v(\" 1.4.3 异步支持\")]),t._v(\" \"),s(\"p\",[t._v(\"Dart类库有非常多的返回\"),s(\"code\",[t._v(\"Future\")]),t._v(\"或者\"),s(\"code\",[t._v(\"Stream\")]),t._v(\"对象的函数。 这些函数被称为\"),s(\"strong\",[t._v(\"异步函数\")]),t._v(\"：它们只会在设置好一些耗时操作之后返回，比如像 IO操作。而不是等到这个操作完成。\")]),t._v(\" \"),s(\"p\",[s(\"code\",[t._v(\"async\")]),t._v(\"和\"),s(\"code\",[t._v(\"await\")]),t._v(\"关键词支持了异步编程，允许您写出和同步代码很像的异步代码。\")]),t._v(\" \"),s(\"h3\",{attrs:{id:\"future\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#future\"}},[t._v(\"#\")]),t._v(\" Future\")]),t._v(\" \"),s(\"p\",[s(\"code\",[t._v(\"Future\")]),t._v(\"与JavaScript中的\"),s(\"code\",[t._v(\"Promise\")]),t._v(\"非常相似，表示一个异步操作的最终完成（或失败）及其结果值的表示。简单来说，它就是用于处理异步操作的，异步处理成功了就执行成功的操作，异步处理失败了就捕获错误或者停止后续操作。一个Future只会对应一个结果，要么成功，要么失败。\")]),t._v(\" \"),s(\"p\",[t._v(\"由于本身功能较多，这里我们只介绍其常用的API及特性。还有，请记住，\"),s(\"code\",[t._v(\"Future\")]),t._v(\" 的所有API的返回值仍然是一个\"),s(\"code\",[t._v(\"Future\")]),t._v(\"对象，所以可以很方便的进行链式调用。\")]),t._v(\" \"),s(\"h4\",{attrs:{id:\"future-then\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#future-then\"}},[t._v(\"#\")]),t._v(\" Future.then\")]),t._v(\" \"),s(\"p\",[t._v(\"为了方便示例，在本例中我们使用\"),s(\"code\",[t._v(\"Future.delayed\")]),t._v(' 创建了一个延时任务（实际场景会是一个真正的耗时任务，比如一次网络请求），即2秒后返回结果字符串\"hi world!\"，然后我们在'),s(\"code\",[t._v(\"then\")]),t._v(\"中接收异步结果并打印结果，代码如下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"Future\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"delayed\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Duration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"hi world!\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"data\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"data\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"h4\",{attrs:{id:\"future-catcherror\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#future-catcherror\"}},[t._v(\"#\")]),t._v(\" Future.catchError\")]),t._v(\" \"),s(\"p\",[t._v(\"如果异步任务发生错误，我们可以在\"),s(\"code\",[t._v(\"catchError\")]),t._v(\"中捕获错误，我们将上面示例改为：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"Future\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"delayed\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Duration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v('//return \"hi world!\";')]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"throw\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AssertionError\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Error\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"  \\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"data\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//执行成功会走到这里  \")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"success\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"catchError\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//执行失败会走到这里  \")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"在本示例中，我们在异步任务中抛出了一个异常，\"),s(\"code\",[t._v(\"then\")]),t._v(\"的回调函数将不会被执行，取而代之的是 \"),s(\"code\",[t._v(\"catchError\")]),t._v(\"回调函数将被调用；但是，并不是只有 \"),s(\"code\",[t._v(\"catchError\")]),t._v(\"回调才能捕获错误，\"),s(\"code\",[t._v(\"then\")]),t._v(\"方法还有一个可选参数\"),s(\"code\",[t._v(\"onError\")]),t._v(\"，我们也可以它来捕获异常：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"Future\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"delayed\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Duration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\t\"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v('//return \"hi world!\";')]),t._v(\"\\n\\t\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"throw\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AssertionError\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Error\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"data\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\t\"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"success\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" onError\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\t\"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"h4\",{attrs:{id:\"future-whencomplete\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#future-whencomplete\"}},[t._v(\"#\")]),t._v(\" Future.whenComplete\")]),t._v(\" \"),s(\"p\",[t._v(\"有些时候，我们会遇到无论异步任务执行成功或失败都需要做一些事的场景，比如在网络请求前弹出加载对话框，在请求结束后关闭对话框。这种场景，有两种方法，第一种是分别在\"),s(\"code\",[t._v(\"then\")]),t._v(\"或\"),s(\"code\",[t._v(\"catch\")]),t._v(\"中关闭一下对话框，第二种就是使用\"),s(\"code\",[t._v(\"Future\")]),t._v(\"的\"),s(\"code\",[t._v(\"whenComplete\")]),t._v(\"回调，我们将上面示例改一下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"Future\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"delayed\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Duration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v('//return \"hi world!\";')]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"throw\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AssertionError\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Error\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"data\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//执行成功会走到这里 \")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"data\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"catchError\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//执行失败会走到这里   \")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"whenComplete\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//无论成功或失败都会走到这里\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"h4\",{attrs:{id:\"future-wait\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#future-wait\"}},[t._v(\"#\")]),t._v(\" Future.wait\")]),t._v(\" \"),s(\"p\",[t._v(\"有些时候，我们需要等待多个异步任务都执行结束后才进行一些操作，比如我们有一个界面，需要先分别从两个网络接口获取数据，获取成功后，我们需要将两个接口数据进行特定的处理后再显示到UI界面上，应该怎么做？答案是\"),s(\"code\",[t._v(\"Future.wait\")]),t._v(\"，它接受一个\"),s(\"code\",[t._v(\"Future\")]),t._v(\"数组参数，只有数组中所有\"),s(\"code\",[t._v(\"Future\")]),t._v(\"都执行成功后，才会触发\"),s(\"code\",[t._v(\"then\")]),t._v(\"的成功回调，只要有一个\"),s(\"code\",[t._v(\"Future\")]),t._v(\"执行失败，就会触发错误回调。下面，我们通过模拟\"),s(\"code\",[t._v(\"Future.delayed\")]),t._v(\" 来模拟两个数据获取的异步任务，等两个异步任务都执行成功时，将两个异步任务的结果拼接打印出来，代码如下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"Future\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"wait\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 2秒后返回结果  \")]),t._v(\"\\n  Future\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"delayed\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Duration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"hello\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 4秒后返回结果  \")]),t._v(\"\\n  Future\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"delayed\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Duration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"4\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\" world\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"results\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"results\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\"results\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"catchError\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"执行上面代码，4秒后你会在控制台中看到“hello world”。\")]),t._v(\" \"),s(\"h3\",{attrs:{id:\"async-await\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#async-await\"}},[t._v(\"#\")]),t._v(\" Async/await\")]),t._v(\" \"),s(\"p\",[t._v(\"Dart中的\"),s(\"code\",[t._v(\"async/await\")]),t._v(\" 和JavaScript中的\"),s(\"code\",[t._v(\"async/await\")]),t._v(\"功能和用法是一模一样的，如果你已经了解JavaScript中的\"),s(\"code\",[t._v(\"async/await\")]),t._v(\"的用法，可以直接跳过本节。\")]),t._v(\" \"),s(\"h4\",{attrs:{id:\"回调地狱-callback-hell\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#回调地狱-callback-hell\"}},[t._v(\"#\")]),t._v(\" 回调地狱(Callback Hell)\")]),t._v(\" \"),s(\"p\",[t._v(\"如果代码中有大量异步逻辑，并且出现大量异步任务依赖其它异步任务的结果时，必然会出现\"),s(\"code\",[t._v(\"Future.then\")]),t._v(\"回调中套回调情况。举个例子，比如现在有个需求场景是用户先登录，登录成功后会获得用户ID，然后通过用户ID，再去请求用户个人信息，获取到用户个人信息后，为了使用方便，我们需要将其缓存在本地文件系统，代码如下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//先分别定义各个异步任务\")]),t._v(\"\\nFuture\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"login\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String userName\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" String pwd\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\t\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//用户登录\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\nFuture\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getUserInfo\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String id\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\t\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//获取用户信息 \")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\nFuture \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"saveUserInfo\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String userInfo\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\t\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\\t\"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 保存用户信息 \")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \\n\")])])]),s(\"p\",[t._v(\"接下来，执行整个任务流：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"login\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"alice\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"******\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"id\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//登录成功后通过，id获取用户信息    \")]),t._v(\"\\n \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getUserInfo\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"id\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"userInfo\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//获取用户信息后保存 \")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"saveUserInfo\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"userInfo\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n       \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//保存用户信息，接下来执行其它操作\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"可以感受一下，如果业务逻辑中有大量异步依赖的情况，将会出现上面这种在回调里面套回调的情况，过多的嵌套会导致的代码可读性下降以及出错率提高，并且非常难维护，这个问题被形象的称为\"),s(\"strong\",[t._v(\"回调地狱（Callback Hell）\")]),t._v(\"。回调地狱问题在之前JavaScript中非常突出，也是JavaScript被吐槽最多的点，但随着ECMAScript6和ECMAScript7标准发布后，这个问题得到了非常好的解决，而解决回调地狱的两大神器正是ECMAScript6引入了\"),s(\"code\",[t._v(\"Promise\")]),t._v(\"，以及ECMAScript7中引入的\"),s(\"code\",[t._v(\"async/await\")]),t._v(\"。 而在Dart中几乎是完全平移了JavaScript中的这两者：\"),s(\"code\",[t._v(\"Future\")]),t._v(\"相当于\"),s(\"code\",[t._v(\"Promise\")]),t._v(\"，而\"),s(\"code\",[t._v(\"async/await\")]),t._v(\"连名字都没改。接下来我们看看通过\"),s(\"code\",[t._v(\"Future\")]),t._v(\"和\"),s(\"code\",[t._v(\"async/await\")]),t._v(\"如何消除上面示例中的嵌套问题。\")]),t._v(\" \"),s(\"h5\",{attrs:{id:\"使用future消除callback-hell\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#使用future消除callback-hell\"}},[t._v(\"#\")]),t._v(\" 使用Future消除Callback Hell\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"login\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"alice\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"******\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"id\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \\t\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getUserInfo\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"id\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"userInfo\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"saveUserInfo\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"userInfo\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//执行接下来的操作 \")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"catchError\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//错误处理  \")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"正如上文所述， \"),s(\"em\",[t._v(\"“\"),s(\"code\",[t._v(\"Future\")]),t._v(\" 的所有API的返回值仍然是一个\"),s(\"code\",[t._v(\"Future\")]),t._v(\"对象，所以可以很方便的进行链式调用”\")]),t._v(\" ，如果在then中返回的是一个\"),s(\"code\",[t._v(\"Future\")]),t._v(\"的话，该\"),s(\"code\",[t._v(\"future\")]),t._v(\"会执行，执行结束后会触发后面的\"),s(\"code\",[t._v(\"then\")]),t._v(\"回调，这样依次向下，就避免了层层嵌套。\")]),t._v(\" \"),s(\"h5\",{attrs:{id:\"使用async-await消除callback-hell\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#使用async-await消除callback-hell\"}},[t._v(\"#\")]),t._v(\" 使用async/await消除callback hell\")]),t._v(\" \"),s(\"p\",[t._v(\"通过\"),s(\"code\",[t._v(\"Future\")]),t._v(\"回调中再返回\"),s(\"code\",[t._v(\"Future\")]),t._v(\"的方式虽然能避免层层嵌套，但是还是有一层回调，有没有一种方式能够让我们可以像写同步代码那样来执行异步任务而不使用回调的方式？答案是肯定的，这就要使用\"),s(\"code\",[t._v(\"async/await\")]),t._v(\"了，下面我们先直接看代码，然后再解释，代码如下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"task\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    String id \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"login\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"alice\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"******\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    String userInfo \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getUserInfo\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"id\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"saveUserInfo\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"userInfo\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//执行接下来的操作   \")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"catch\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//错误处理   \")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"   \\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"  \\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"ul\",[s(\"li\",[s(\"code\",[t._v(\"async\")]),t._v(\"用来表示函数是异步的，定义的函数会返回一个\"),s(\"code\",[t._v(\"Future\")]),t._v(\"对象，可以使用then方法添加回调函数。\")]),t._v(\" \"),s(\"li\",[s(\"code\",[t._v(\"await\")]),t._v(\" 后面是一个\"),s(\"code\",[t._v(\"Future\")]),t._v(\"，表示等待该异步任务完成，异步完成后才会往下走；\"),s(\"code\",[t._v(\"await\")]),t._v(\"必须出现在 \"),s(\"code\",[t._v(\"async\")]),t._v(\" 函数内部。\")])]),t._v(\" \"),s(\"p\",[t._v(\"可以看到，我们通过\"),s(\"code\",[t._v(\"async/await\")]),t._v(\"将一个异步流用同步的代码表示出来了。\")]),t._v(\" \"),s(\"blockquote\",[s(\"p\",[t._v(\"其实，无论是在JavaScript还是Dart中，\"),s(\"code\",[t._v(\"async/await\")]),t._v(\"都只是一个语法糖，编译器或解释器最终都会将其转化为一个Promise（Future）的调用链。\")])]),t._v(\" \"),s(\"h2\",{attrs:{id:\"_1-4-4-stream\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-4-4-stream\"}},[t._v(\"#\")]),t._v(\" 1.4.4 Stream\")]),t._v(\" \"),s(\"p\",[s(\"code\",[t._v(\"Stream\")]),t._v(\" 也是用于接收异步事件数据，和\"),s(\"code\",[t._v(\"Future\")]),t._v(\" 不同的是，它可以接收多个异步操作的结果（成功或失败）。 也就是说，在执行异步任务时，可以通过多次触发成功或失败事件来传递结果数据或错误异常。 \"),s(\"code\",[t._v(\"Stream\")]),t._v(\" 常用于会多次读取数据的异步任务场景，如网络内容下载、文件读写等。举个例子：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"Stream\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fromFutures\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 1秒后返回结果\")]),t._v(\"\\n  Future\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"delayed\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Duration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"hello 1\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 抛出一个异常\")]),t._v(\"\\n  Future\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"delayed\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Duration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"throw\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AssertionError\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Error\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 3秒后返回结果\")]),t._v(\"\\n  Future\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"delayed\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Duration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"hello 3\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"listen\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"data\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"data\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" onError\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"message\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"onDone\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"上面的代码依次会输出：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language- extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[s(\"code\",[t._v(\"I/flutter (17666): hello 1\\nI/flutter (17666): Error\\nI/flutter (17666): hello 3\\n\")])])]),s(\"p\",[t._v(\"代码很简单，就不赘述了。\")]),t._v(\" \"),s(\"blockquote\",[s(\"p\",[t._v(\"思考题：既然Stream可以接收多次事件，那能不能用Stream来实现一个订阅者模式的事件总线？\")])]),t._v(\" \"),s(\"h2\",{attrs:{id:\"_1-4-5-dart和java及javascript对比\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-4-5-dart和java及javascript对比\"}},[t._v(\"#\")]),t._v(\" 1.4.5 Dart和Java及JavaScript对比\")]),t._v(\" \"),s(\"p\",[t._v(\"通过上面介绍，相信你对Dart应该有了一个初步的印象，由于笔者平时也使用Java和JavaScript，下面笔者根据自己的经验，结合Java和JavaScript，谈一下自己的看法。\")]),t._v(\" \"),s(\"blockquote\",[s(\"p\",[t._v(\"之所以将Dart与Java和JavaScript对比，是因为，这两者分别是强类型语言和弱类型语言的典型代表，并且Dart 语法中很多地方也都借鉴了Java和JavaScript。\")])]),t._v(\" \"),s(\"h3\",{attrs:{id:\"dart-vs-java\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#dart-vs-java\"}},[t._v(\"#\")]),t._v(\" Dart vs Java\")]),t._v(\" \"),s(\"p\",[t._v(\"客观的来讲，Dart在语法层面确实比Java更有表现力；在VM层面，Dart VM在内存回收和吞吐量都进行了反复的优化，但具体的性能对比，笔者没有找到相关测试数据，但在笔者看来，只要Dart语言能流行，VM的性能就不用担心，毕竟Google在Go（没用VM但有GC）、JavaScript（v8）、Dalvik（Android上的Java VM）上已经有了很多技术积淀。值得注意的是Dart在Flutter中已经可以将GC做到10ms以内，所以Dart和Java相比，决胜因素并不会是在性能方面。而在语法层面，Dart要比Java更有表现力，最重要的是Dart对函数式编程支持要远强于Java(目前只停留在Lambda表达式)，而Dart目前真正的不足是\"),s(\"strong\",[t._v(\"生态\")]),t._v(\"，但笔者相信，随着Flutter的逐渐火热，会回过头来反推Dart生态加速发展，对于Dart来说，现在需要的是时间。\")]),t._v(\" \"),s(\"h3\",{attrs:{id:\"dart-vs-javascript\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#dart-vs-javascript\"}},[t._v(\"#\")]),t._v(\" Dart vs JavaScript\")]),t._v(\" \"),s(\"p\",[t._v(\"JavaScript的弱类型一直被抓短，所以TypeScript、CoffeeScript甚至是Facebook的flow（虽然并不能算JavaScript的一个超集，但也通过标注和打包工具提供了静态类型检查）才有市场。就笔者使用过的脚本语言中（笔者曾使用过Python、PHP），JavaScript无疑是\"),s(\"strong\",[t._v(\"动态化\")]),t._v(\"支持最好的脚本语言，比如在JavaScript中，可以给任何对象在任何时候动态扩展属性，对于精通JavaScript的高手来说，这无疑是一把利剑。但是，任何事物都有两面性，JavaScript的强大的动态化特性也是把双刃剑，你可经常听到另一个声音，认为JavaScript的这种动态性糟糕透了，太过灵活反而导致代码很难预期，无法限制不被期望的修改。毕竟有些人总是对自己或别人写的代码不放心，他们希望能够让代码变得可控，并期望有一套静态类型检查系统来帮助自己减少错误。正因如此，在Flutter中，Dart几乎放弃了脚本语言动态化的特性，如不支持反射、也不支持动态创建函数等。并且Dart在2.0强制开启了类型检查（Strong Mode），原先的检查模式（checked mode）和可选类型（optional type）将淡出，所以在类型安全这个层面来说，Dart和TypeScript、CoffeeScript是差不多的，所以单从这一点来看，Dart并不具备什么明显优势，但综合起来看，Dart既能进行服务端脚本、APP开发、web开发，这就有优势了！\")]),t._v(\" \"),s(\"p\",[t._v(\"综上所述，笔者还是很看好Dart语言的将来，之所以表这个态，是因为在新技术发展初期，很多人可能还有所摇摆，有所犹豫，所以有必要给大家打一剂强心针，当然，这是一个见仁见智的问题，大家可以各抒己见。\")])])}),[],!1,null,null,null);a.default=r.exports}}]);"
  },
  {
    "path": "docs/assets/js/181.e3633acb.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[181],{833:function(t,r,e){\"use strict\";e.r(r);var l=e(45),a=Object(l.a)({},(function(){var t=this,r=t.$createElement,e=t._self._c||r;return e(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[e(\"h2\",{attrs:{id:\"本章目录\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本章目录\"}},[t._v(\"#\")]),t._v(\" 本章目录\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter1/mobile_development_intro.html\"}},[t._v(\"移动开发技术简介\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter1/flutter_intro.html\"}},[t._v(\"Flutter简介\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter1/install_flutter.html\"}},[t._v(\"搭建Flutter开发环境\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter1/dart.html\"}},[t._v(\"Dart语言简介\")])],1)])])}),[],!1,null,null,null);r.default=a.exports}}]);"
  },
  {
    "path": "docs/assets/js/182.d30b9f69.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[182],{835:function(t,a,e){\"use strict\";e.r(a);var s=e(45),v=Object(s.a)({},(function(){var t=this,a=t.$createElement,e=t._self._c||a;return e(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[e(\"h1\",{attrs:{id:\"_1-1-移动开发技术简介\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-1-移动开发技术简介\"}},[t._v(\"#\")]),t._v(\" 1.1 移动开发技术简介\")]),t._v(\" \"),e(\"p\",[t._v(\"本节将主要介绍一下移动开发技术的进化历程，主要是想让读者知道Flutter技术出现的背景。笔者认为，了解一门新技术出现的背景是非常重要的，因为只有了解之前是什么样的，才能理解为什么会是现在这样。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"_1-1-1-原生开发与跨平台技术\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-1-1-原生开发与跨平台技术\"}},[t._v(\"#\")]),t._v(\" 1.1.1 原生开发与跨平台技术\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"原生开发\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#原生开发\"}},[t._v(\"#\")]),t._v(\" 原生开发\")]),t._v(\" \"),e(\"p\",[t._v(\"原生应用程序是指某一个移动平台（比如iOS或安卓）所特有的应用，使用相应平台支持的开发工具和语言，并直接调用系统提供的SDK API。比如Android原生应用就是指使用Java或Kotlin语言直接调用Android SDK开发的应用程序；而iOS原生应用就是指通过Objective-C或Swift语言直接调用iOS SDK开发的应用程序。原生开发有以下主要优势：\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"可访问平台全部功能（GPS、摄像头）；\")]),t._v(\" \"),e(\"li\",[t._v(\"速度快、性能高、可以实现复杂动画及绘制，整体用户体验好；\")])]),t._v(\" \"),e(\"p\",[t._v(\"主要缺点：\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"平台特定，开发成本高；不同平台必须维护不同代码，人力成本随之变大；\")]),t._v(\" \"),e(\"li\",[t._v(\"内容固定，动态化弱，大多数情况下，有新功能更新时只能发版；\")])]),t._v(\" \"),e(\"p\",[t._v(\"在移动互联网发展初期，业务场景并不复杂，原生开发还可以应对产品需求迭代。 但近几年，随着物联网时代到来、移动互联网高歌猛进，日新月异，在很多业务场景中，传统的纯原生开发已经不能满足日益增长的业务需求。主要表现在：\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"动态化内容需求增大；当需求发生变化时，纯原生应用需要通过版本升级来更新内容，但应用上架、审核是需要周期的，这对高速变化的互联网时代来说是很难接受的，所以，对应用动态化(不发版也可以更新应用内容)的需求就变的迫在眉睫。\")]),t._v(\" \"),e(\"li\",[t._v(\"业务需求变化快，开发成本变大；由于原生开发一般都要维护Android、iOS两个开发团队，版本迭代时，无论人力成本，还是测试成本都会变大。\")])]),t._v(\" \"),e(\"p\",[t._v(\"总结一下，纯原生开发主要面临动态化和开发成本两个问题，而针对这两个问题，诞生了一些跨平台的动态化框架。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"跨平台技术简介\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#跨平台技术简介\"}},[t._v(\"#\")]),t._v(\" 跨平台技术简介\")]),t._v(\" \"),e(\"p\",[t._v(\"针对原生开发面临问题，人们一直都在努力寻找好的解决方案，而时至今日，已经有很多跨平台框架(注意，本书中所指的“跨平台”若无特殊说明，即特指Android和iOS两个平台)，根据其原理，主要分为三类：\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"H5+原生（Cordova、Ionic、微信小程序）\")]),t._v(\" \"),e(\"li\",[t._v(\"JavaScript开发+原生渲染 （React Native、Weex、快应用）\")]),t._v(\" \"),e(\"li\",[t._v(\"自绘UI+原生(QT for mobile、Flutter)\")])]),t._v(\" \"),e(\"p\",[t._v(\"在接下来的章节中我们逐个来看看这三类框架的原理及优缺点。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"_1-1-2-hybrid技术简介\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-1-2-hybrid技术简介\"}},[t._v(\"#\")]),t._v(\" 1.1.2 Hybrid技术简介\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"h5-原生混合开发\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#h5-原生混合开发\"}},[t._v(\"#\")]),t._v(\" H5+原生混合开发\")]),t._v(\" \"),e(\"p\",[t._v(\"这类框架主要原理就是将APP的一部分需要动态变动的内容通过H5来实现，通过原生的网页加载控件WebView (Android)或WKWebView（iOS）来加载（以后若无特殊说明，我们用WebView来统一指代android和iOS中的网页加载控件）。这样以来，H5部分是可以随时改变而不用发版，动态化需求能满足；同时，由于h5代码只需要一次开发，就能同时在Android和iOS两个平台运行，这也可以减小开发成本，也就是说，H5部分功能越多，开发成本就越小。我们称这种h5+原生的开发模式为\"),e(\"strong\",[t._v(\"混合开发 ** ，采用混合模式开发的APP我们称之为\")]),t._v(\"混合应用\"),e(\"strong\",[t._v(\"或\")]),t._v(\"Hybrid APP**  ，如果一个应用的大多数功能都是H5实现的话，我们称其为\"),e(\"strong\",[t._v(\"Web APP\")]),t._v(\" 。\")]),t._v(\" \"),e(\"p\",[t._v(\"目前混合开发框架的典型代表有：Cordova、Ionic 和微信小程序，值得一提的是微信小程序目前是在webview中渲染的，并非原生渲染，但将来有可能会采用原生渲染。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"混合开发技术点\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#混合开发技术点\"}},[t._v(\"#\")]),t._v(\" 混合开发技术点\")]),t._v(\" \"),e(\"p\",[t._v(\"如之前所述，原生开发可以访问平台所有功能，而混合开发中，H5代码是运行在WebView中，而WebView实质上就是一个浏览器内核，其JavaScript依然运行在一个权限受限的沙箱中，所以对于大多数系统能力都没有访问权限，如无法访问文件系统、不能使用蓝牙等。所以，对于H5不能实现的功能，都需要原生去做。而混合框架一般都会在原生代码中预先实现一些访问系统能力的API， 然后暴露给WebView以供JavaScript调用，这样一来，WebView就成为了JavaScript与原生API之间通信的桥梁，主要负责JavaScript与原生之间传递调用消息，而消息的传递必须遵守一个标准的协议，它规定了消息的格式与含义，我们把依赖于WebView的用于在JavaScript与原生之间通信并实现了某种消息传输协议的工具称之为\"),e(\"strong\",[t._v(\"WebView JavaScript Bridge\")]),t._v(\", 简称 \"),e(\"strong\",[t._v(\"JsBridge\")]),t._v(\"，它也是混合开发框架的核心。\")]),t._v(\" \"),e(\"h4\",{attrs:{id:\"示例-javascript调用原生api获取手机型号\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例-javascript调用原生api获取手机型号\"}},[t._v(\"#\")]),t._v(\" 示例：JavaScript调用原生API获取手机型号\")]),t._v(\" \"),e(\"p\",[t._v(\"下面我们以Android为例，实现一个获取手机型号的原生API供JavaScript调用。在这个示例中将展示JavaScript调用原生API的流程，读者可以直观的感受一下调用流程。我们选用笔者在Github上开源的dsBridge作为JsBridge来进行通信。dsBridge是一个支持同步调用的跨平台的JsBridge，此示例中只使用其同步调用功能。\")]),t._v(\" \"),e(\"ol\",[e(\"li\",[e(\"p\",[t._v(\"首先在原生中实现获取手机型号的API \"),e(\"code\",[t._v(\"getPhoneModel\")])]),t._v(\" \"),e(\"div\",{staticClass:\"language-java extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-java\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" JSAPI \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n \"),e(\"span\",{pre:!0,attrs:{class:\"token annotation punctuation\"}},[t._v(\"@JavascriptInterface\")]),t._v(\"\\n \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"public\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Object\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getPhoneModel\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Object\")]),t._v(\" msg\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Build\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"MODEL\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"    \\n\")])])])]),t._v(\" \"),e(\"li\",[e(\"p\",[t._v(\"将原生API通过WebView注册到JsBridge中\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-java extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-java\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token namespace\"}},[t._v(\"wendu\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dsbridge\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")])]),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"DWebView\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//DWebView继承自WebView，由dsBridge提供  \")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"DWebView\")]),t._v(\" dwebView \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"DWebView\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"findViewById\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"R\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"id\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dwebview\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//注册原生API到JsBridge\")]),t._v(\"\\ndwebView\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addJavascriptObject\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"JsAPI\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])])]),t._v(\" \"),e(\"li\",[e(\"p\",[t._v(\"在JavaScript中调用原生API\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-javascript extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-javascript\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" dsBridge \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"require\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"dsbridge\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//直接调用原生API `getPhoneModel`\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" model \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dsBridge\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"call\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"getPhoneModel\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//打印机型\")]),t._v(\"\\nconsole\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"log\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"model\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])])])]),t._v(\" \"),e(\"p\",[t._v(\"上面示例演示了JavaScript调用原生API的过程，同样的，一般来说优秀的JsBridge也支持原生调用JavaScript，dsBridge也是支持的，如果您感兴趣，可以去github dsBridge项目主页查看。\")]),t._v(\" \"),e(\"p\",[t._v(\"现在，我们回头来看一下，混合应用无非就是在第一步中预先实现一系列API供JavaScript调用，让JavaScript有访问系统的能力，看到这里，我相信你也可以自己实现一个混合开发框架了。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"总结\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),e(\"p\",[t._v(\"混合应用的优点是动态内容是H5，web技术栈，社区及资源丰富，缺点是性能不好，对于复杂用户界面或动画，WebView不堪重任。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"_1-1-3-react-native、weex及快应用\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-1-3-react-native、weex及快应用\"}},[t._v(\"#\")]),t._v(\" 1.1.3 React Native、Weex及快应用\")]),t._v(\" \"),e(\"p\",[t._v(\"本篇主要介绍一下 \"),e(\"strong\",[t._v(\"JavaScript开发+原生渲染\")]),t._v(\"的跨平台框架原理。\")]),t._v(\" \"),e(\"p\",[t._v(\"React Native (简称RN)是Facebook于2015年4月开源的跨平台移动应用开发框架，是Facebook早先开源的JS框架 React 在原生移动应用平台的衍生产物，目前支持iOS和Android两个平台。RN使用Javascript语言，类似于HTML的JSX，以及CSS来开发移动应用，因此熟悉Web前端开发的技术人员只需很少的学习就可以进入移动应用开发领域。\")]),t._v(\" \"),e(\"p\",[t._v(\"由于RN和React原理相通，并且Flutter也是受React启发，很多思想也都是相通的，万丈高楼平地起，我们有必要深入了解一下React原理。React是一个响应式的Web框架，我们先了解一下两个重要的概念：DOM树与响应式编程。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"dom树与控件树\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#dom树与控件树\"}},[t._v(\"#\")]),t._v(\" DOM树与控件树\")]),t._v(\" \"),e(\"p\",[t._v(\"文档对象模型（Document Object Model，简称DOM），是W3C组织推荐的处理可扩展标志语言的标准编程接口，一种独立于平台和语言的方式访问和修改一个文档的内容和结构。换句话说，这是表示和处理一个HTML或XML文档的标准接口。简单来说，DOM就是文档树，与用户界面控件树对应，在前端开发中通常指HTML对应的渲染树，但广义的DOM也可以指Android中的XML布局文件对应的控件树，而术语\"),e(\"strong\",[t._v(\"DOM操作\")]),t._v(\"就是指直接来操作渲染树（或控件树）， 因此，可以看到其实DOM树和控件树是等价的概念，只不过前者常用于Web开发中，而后者常用于原生开发中。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"响应式编程\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#响应式编程\"}},[t._v(\"#\")]),t._v(\" 响应式编程\")]),t._v(\" \"),e(\"p\",[t._v(\"React中提出一个重要思想：状态改变则UI随之自动改变，而React框架本身就是响应用户状态改变的事件而执行重新构建用户界面的工作，这就是典型的\"),e(\"strong\",[t._v(\"响应式\")]),t._v(\"编程范式，下面我们总结一下React中响应式原理：\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"开发者只需关注状态转移（数据），当状态发生变化，React框架会自动根据新的状态重新构建UI。\")]),t._v(\" \"),e(\"li\",[t._v(\"React框架在接收到用户状态改变通知后，会根据当前渲染树，结合最新的状态改变，通过Diff算法，计算出树中变化的部分，然后只更新变化的部分（DOM操作），从而避免整棵树重构，提高性能。\")])]),t._v(\" \"),e(\"p\",[t._v(\"值得注意的是，在第二步中，状态变化后React框架并不会立即去计算并渲染DOM树的变化部分，相反，React会在DOM的基础上建立一个抽象层，即\"),e(\"strong\",[t._v(\"虚拟DOM\")]),t._v(\"树，对数据和状态所做的任何改动，都会被自动且高效的同步到虚拟DOM，最后再批量同步到真实DOM中，而不是每次改变都去操作一下DOM。为什么不能每次改变都直接去操作DOM树？这是因为在浏览器中每一次DOM操作都有可能引起浏览器的重绘或回流：\")]),t._v(\" \"),e(\"ol\",[e(\"li\",[t._v(\"如果DOM只是外观风格发生变化，如颜色变化，会导致浏览器重绘界面。\")]),t._v(\" \"),e(\"li\",[t._v(\"如果DOM树的结构发生变化，如尺寸、布局、节点隐藏等导致，浏览器就需要回流（及重新排版布局）。\")])]),t._v(\" \"),e(\"p\",[t._v(\"而浏览器的重绘和回流都是比较昂贵的操作，如果每一次改变都直接对DOM进行操作，这会带来性能问题，而批量操作只会触发一次DOM更新。\")]),t._v(\" \"),e(\"blockquote\",[e(\"p\",[t._v(\"思考题：Diff操作和DOM批量更新难道不应该是浏览器的职责吗？第三方框架中去做合不合适？\")])]),t._v(\" \"),e(\"blockquote\",[e(\"p\",[t._v(\"此处需要有一张插图\")])]),t._v(\" \"),e(\"h3\",{attrs:{id:\"react-native\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#react-native\"}},[t._v(\"#\")]),t._v(\" React Native\")]),t._v(\" \"),e(\"p\",[t._v(\"上文已经提到React Native 是React 在原生移动应用平台的衍生产物，那两者主要的区别是什么呢？其实，主要的区别在于虚拟DOM映射的对象是什么？React中虚拟DOM最终会映射为浏览器DOM树，而RN中虚拟DOM会通过 JavaScriptCore 映射为原生控件树。\")]),t._v(\" \"),e(\"p\",[t._v(\"JavaScriptCore 是一个JavaScript解释器，它在React Native中主要有两个作用：\")]),t._v(\" \"),e(\"ol\",[e(\"li\",[t._v(\"为JavaScript提供运行环境。\")]),t._v(\" \"),e(\"li\",[t._v(\"是JavaScript与原生应用之间通信的桥梁，作用和JsBridge一样，事实上，在iOS中，很多JsBridge的实现都是基于 JavaScriptCore 。\")])]),t._v(\" \"),e(\"p\",[t._v(\"而RN中将虚拟DOM映射为原生控件的过程中分两步：\")]),t._v(\" \"),e(\"ol\",[e(\"li\",[t._v(\"布局消息传递； 将虚拟DOM布局信息传递给原生；\")]),t._v(\" \"),e(\"li\",[t._v(\"原生根据布局信息通过对应的原生控件渲染控件树；\")])]),t._v(\" \"),e(\"p\",[t._v(\"至此，React Native 便实现了跨平台。 相对于混合应用，由于React Native是原生控件渲染，所以性能会比混合应用中H5好很多，同时React Native使用了Web开发技术栈，也只需维护一份代码，同样是跨平台框架。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"weex\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#weex\"}},[t._v(\"#\")]),t._v(\" Weex\")]),t._v(\" \"),e(\"p\",[t._v(\"Weex是阿里巴巴于2016年发布的跨平台移动端开发框架，思想及原理和React Native类似，最大的不同是语法层面，Weex支持Vue语法和Rax语法，Rax 的 DSL(Domain Specific Language) 语法是基于 React JSX 语法而创造。与 React 不同，在 Rax 中 JSX 是必选的，它不支持通过其它方式创建组件，所以学习 JSX 是使用 Rax 的必要基础。而React Native只支持JSX语法。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"快应用\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#快应用\"}},[t._v(\"#\")]),t._v(\" 快应用\")]),t._v(\" \"),e(\"p\",[t._v(\"快应用是华为、小米、OPPO、魅族等国内9大主流手机厂商共同制定的轻量级应用标准，目标直指微信小程序。它也是采用JavaScript语言开发，原生控件渲染，与React Native和Weex相比主要有两点不同：\")]),t._v(\" \"),e(\"ol\",[e(\"li\",[t._v(\"快应用自身不支持Vue或React语法，其采用原生JavaScript开发，其开发框架和微信小程序很像，值得一提的是小程序目前已经可以使用Vue语法开发（mpvue），从原理上来讲，Vue的语法也可以移植到快应用上。\")]),t._v(\" \"),e(\"li\",[t._v(\"React Native和Weex的渲染/排版引擎是集成到框架中的，每一个APP都需要打包一份，安装包体积较大；而快应用渲染/排版引擎是集成到ROM中的，应用中无需打包，安装包体积小，正因如此，快应用才能在保证性能的同时做到快速分发。\")])]),t._v(\" \"),e(\"h3\",{attrs:{id:\"总结-2\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结-2\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),e(\"p\",[t._v(\"JavaScript开发+原生渲染的方式主要优点如下：\")]),t._v(\" \"),e(\"ol\",[e(\"li\",[t._v(\"采用Web开发技术栈，社区庞大、上手快、开发成本相对较低。\")]),t._v(\" \"),e(\"li\",[t._v(\"原生渲染，性能相比H5提高很多。\")]),t._v(\" \"),e(\"li\",[t._v(\"动态化较好，支持热更新。\")])]),t._v(\" \"),e(\"p\",[t._v(\"不足：\")]),t._v(\" \"),e(\"ol\",[e(\"li\",[t._v(\"渲染时需要JavaScript和原生之间通信，在有些场景如拖动可能会因为通信频繁导致卡顿。\")]),t._v(\" \"),e(\"li\",[t._v(\"JavaScript为脚本语言，执行时需要JIT(Just In Time)，执行效率和AOT(Ahead Of Time)代码仍有差距。\")]),t._v(\" \"),e(\"li\",[t._v(\"由于渲染依赖原生控件，不同平台的控件需要单独维护，并且当系统更新时，社区控件可能会滞后；除此之外，其控件系统也会受到原生UI系统限制，例如，在Android中，手势冲突消歧规则是固定的，这在使用不同人写的控件嵌套时，手势冲突问题将会变得非常棘手。\")])]),t._v(\" \"),e(\"h2\",{attrs:{id:\"_1-1-4-qt-mobile\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-1-4-qt-mobile\"}},[t._v(\"#\")]),t._v(\" 1.1.4 QT Mobile\")]),t._v(\" \"),e(\"p\",[t._v(\"在本篇中，我们看看最后一种跨平台技术：自绘UI+原生。这种技术的思路是，通过在不同平台实现一个统一接口的渲染引擎来绘制UI，而不依赖系统原生控件，所以可以做到不同平台UI的一致性。注意，自绘引擎解决的是UI的跨平台问题，如果涉及其它系统能力调用，依然要涉及原生开发。这种平台技术的优点如下：\")]),t._v(\" \"),e(\"ol\",[e(\"li\",[e(\"p\",[t._v(\"性能高；由于自绘引擎是直接调用系统API来绘制UI，所以性能和原生控件接近。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[t._v(\"灵活、组件库易维护、UI外观保真度和一致性高；由于UI渲染不依赖原生控件，也就不需要根据不同平台的控件单独维护一套组件库，所以代码容易维护。由于组件库是同一套代码、同一个渲染引擎，所以在不同平台，组件显示外观可以做到高保真和高一致性；另外，由于不依赖原生控件，也就不会受原生布局系统的限制，这样布局系统会非常灵活。\")])])]),t._v(\" \"),e(\"p\",[t._v(\"不足：\")]),t._v(\" \"),e(\"ol\",[e(\"li\",[t._v(\"动态性不足；为了保证UI绘制性能，自绘UI系统一般都会采用AOT模式编译其发布包，所以应用发布后，不能像Hybrid和RN那些使用JavaScript（JIT）作为开发语言的框架那样动态下发代码。\")]),t._v(\" \"),e(\"li\",[t._v(\"开发效率低：QT使用C++作为其开发语言，而编程效率是直接会影响APP开发效率的，C++作为一门静态语言，在UI开发方面灵活性不及JavaScript这样的动态语言，另外，C++需要开发者手动去管理内存分配，没有JavaScript及Java中垃圾回收（GC）的机制。\")])]),t._v(\" \"),e(\"p\",[t._v(\"也许你已经猜到Flutter就属于这一类跨平台技术，没错，Flutter正是实现一套自绘引擎，并拥有一套自己的UI布局系统。不过，自绘制引擎的思路并不是什么新概念，Flutter并不是第一个尝试这么做的，在它之前有一个典型的代表，即大名鼎鼎的QT。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"qt简介\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#qt简介\"}},[t._v(\"#\")]),t._v(\" QT简介\")]),t._v(\" \"),e(\"p\",[t._v(\"Qt是一个1991年由Qt Company开发的跨平台C++图形用户界面应用程序开发框架。2008年，Qt Company科技被诺基亚公司收购，Qt也因此成为诺基亚旗下的编程语言工具。2012年，Qt被Digia收购。2014年4月，跨平台集成开发环境Qt Creator 3.1.0正式发布，实现了对于iOS的完全支持，新增WinRT、Beautifier等插件，废弃了无Python接口的GDB调试支持，集成了基于Clang的C/C++代码模块，并对Android支持做出了调整，至此实现了全面支持iOS、Android、WP，它提供给应用程序开发者构建图形用户界面所需的所有功能。但是，QT虽然在PC端获得了巨大成功，备受社区追捧，然而其在移动端却表现不佳，在近几年，虽然偶尔能听到QT的声音，但一直很弱，无论QT本身技术如何、设计思想如何，但事实上终究是败了，究其原因，笔者认为主要有四：\")]),t._v(\" \"),e(\"p\",[t._v(\"第一：QT移动开发社区太小，学习资料不足，生态不好。\")]),t._v(\" \"),e(\"p\",[t._v(\"第二：官方推广不利，支持不够。\")]),t._v(\" \"),e(\"p\",[t._v(\"第三：移动端发力较晚，市场已被其它动态化框架占领（Hybrid和RN)。\")]),t._v(\" \"),e(\"p\",[t._v(\"第四：在移动开发中，C++开发和Web开发栈相比有着先天的劣势，直接结果就是QT开发效率太低。\")]),t._v(\" \"),e(\"p\",[t._v(\"基于此四点，尽管QT是移动端开发跨平台自绘引擎的先驱，但却成为了烈士。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"_1-1-5-flutter出世\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-1-5-flutter出世\"}},[t._v(\"#\")]),t._v(\" 1.1.5 Flutter出世\")]),t._v(\" \"),e(\"p\",[t._v(\"“千呼万唤始出来”，铺垫这么久，现在终于等到本书的主角出场了！\")]),t._v(\" \"),e(\"p\",[t._v(\"Flutter是Google发布的一个用于创建跨平台、高性能移动应用的框架。Flutter和QT mobile一样，都没有使用原生控件，相反都实现了一个自绘引擎，使用自身的布局、绘制系统。那么，我们会担心，QT mobile面对的问题Flutter是否也一样，Flutter会不会步入QT mobile后尘，成为另一个烈士？要回到这个问题，我们先来看看Flutter诞生过程：\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"2017 年 Google I/O 大会上，Google 首次推出了一款新的用于创建跨平台、高性能的移动应用框架——Flutter。\")]),t._v(\" \"),e(\"li\",[t._v(\"2018年2月，Flutter发布了第一个Beta版本，同年五月， 在2018年Google I/O 大会上，Flutter 更新到了 beta 3 版本。\")]),t._v(\" \"),e(\"li\",[t._v(\"2018年6月，Flutter发布了首个预览版本，这意味着 Flutter 进入了正式版（1.0）发布前的最后阶段。\")])]),t._v(\" \"),e(\"p\",[t._v(\"观其发展，在2018年5月份，Flutter 进入了 GitHub stars 排行榜前 100 名，已有 27k  star。而今天(2019年5月29日)，已经有65K的Star。经历了短短2年多的时间，Flutter 生态系统得以快速增长，由此可见，Flutter在开发者中受到了热烈的欢迎，其未来发展值得期待！\")]),t._v(\" \"),e(\"p\",[t._v(\"现在，我们来和QT mobile做一个对比：\")]),t._v(\" \"),e(\"ol\",[e(\"li\",[t._v(\"生态：从Github上来看，目前Flutter活跃用户正在高速增长。从Stackoverflow上提问来看，Flutter社区现在已经很庞大。Flutter的文档、资源也越来越丰富，开发过程中遇到的很多问题都可以在Stackoverflow或其github issue中找到答案。\")]),t._v(\" \"),e(\"li\",[t._v(\"技术支持：现在Google正在大力推广Flutter，Flutter的作者中很多人都是来自Chromium团队，并且github上活跃度很高。另一个角度，从今年上半年Flutter频繁的版本发布也可以看出Google对Flutter的投入的资源不小，所以在官方技术支持这方面，大可不必担心。\")]),t._v(\" \"),e(\"li\",[t._v(\"开发效率：Flutter的热重载可帮助开发者快速地进行测试、构建UI、添加功能并更快地修复错误。在iOS和Android模拟器或真机上可以实现毫秒级热重载，并且不会丢失状态。这真的很棒，相信我，如果你是一名原生开发者，体验了Flutter开发流后，很可能就不想重新回去做原生了，毕竟很少有人不吐槽原生开发的编译速度。\")])]),t._v(\" \"),e(\"p\",[t._v(\"基于以上三点，相信读者和笔者一样，Flutter未来如何，心中自有定论。到现在为止，我们已经对移动端开发技术有了一个全面的了解，接下来我们便要进入本书的主题，你准备好了吗！\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"_1-1-6-小结\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-1-6-小结\"}},[t._v(\"#\")]),t._v(\" 1.1.6 小结\")]),t._v(\" \"),e(\"p\",[t._v(\"本章主要介绍了目前移动开发中三种跨平台技术，现在我们从框架角度对比一下它们，如表1-1所示：\")]),t._v(\" \"),e(\"table\",[e(\"thead\",[e(\"tr\",[e(\"th\",[t._v(\"技术类型\")]),t._v(\" \"),e(\"th\",[t._v(\"UI渲染方式\")]),t._v(\" \"),e(\"th\",[t._v(\"性能\")]),t._v(\" \"),e(\"th\",[t._v(\"开发效率\")]),t._v(\" \"),e(\"th\",[t._v(\"动态化\")]),t._v(\" \"),e(\"th\",[t._v(\"框架代表\")])])]),t._v(\" \"),e(\"tbody\",[e(\"tr\",[e(\"td\",[t._v(\"H5+原生\")]),t._v(\" \"),e(\"td\",[t._v(\"WebView渲染\")]),t._v(\" \"),e(\"td\",[t._v(\"一般\")]),t._v(\" \"),e(\"td\",[t._v(\"高\")]),t._v(\" \"),e(\"td\",[t._v(\"支持\")]),t._v(\" \"),e(\"td\",[t._v(\"Cordova、Ionic\")])]),t._v(\" \"),e(\"tr\",[e(\"td\",[t._v(\"JavaScript+原生渲染\")]),t._v(\" \"),e(\"td\",[t._v(\"原生控件渲染\")]),t._v(\" \"),e(\"td\",[t._v(\"好\")]),t._v(\" \"),e(\"td\",[t._v(\"中\")]),t._v(\" \"),e(\"td\",[t._v(\"支持\")]),t._v(\" \"),e(\"td\",[t._v(\"RN、Weex\")])]),t._v(\" \"),e(\"tr\",[e(\"td\",[t._v(\"自绘UI+原生\")]),t._v(\" \"),e(\"td\",[t._v(\"调用系统API渲染\")]),t._v(\" \"),e(\"td\",[t._v(\"好\")]),t._v(\" \"),e(\"td\",[t._v(\"Flutter高, QT低\")]),t._v(\" \"),e(\"td\",[t._v(\"默认不支持\")]),t._v(\" \"),e(\"td\",[t._v(\"QT、Flutter\")])])])]),t._v(\" \"),e(\"center\",[t._v(\"表1-1: 跨平台技术对比\")]),t._v(\"\\n上表中开发语言主要指UI的开发语言。而开发效率，是指整个开发周期的效率，包括编码时间、调试时间、以及排错、兼容时间。动态化主要指是否支持动态下发代码和是否支持热更新。值得注意的是Flutter的Release包默认是使用Dart AOT模式编译的，所以不支持动态化，但Dart还有JIT或snapshot运行方式，这些模式都是支持动态化的。\\n\")],1)}),[],!1,null,null,null);a.default=v.exports}}]);"
  },
  {
    "path": "docs/assets/js/183.802b20e7.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[183],{839:function(t,r,e){\"use strict\";e.r(r);var a=e(45),o=Object(a.a)({},(function(){var t=this,r=t.$createElement,e=t._self._c||r;return e(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[e(\"h1\",{attrs:{id:\"本章目录\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本章目录\"}},[t._v(\"#\")]),t._v(\" 本章目录\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter10/intro.html\"}},[t._v(\"10.1：自定义组件方法简介\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter10/combine.html\"}},[t._v(\"10.2：组合现有组件\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter10/turn_box.html\"}},[t._v(\"10.3：组合实例：TurnBox\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter10/custom_paint.html\"}},[t._v(\"10.4：自绘组件（CustomPaint与Canvas）\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter10/gradient_circular_progress_demo.html\"}},[t._v(\"10.5：自绘实例：圆形渐变进度条(自绘)\")])],1)])])}),[],!1,null,null,null);r.default=o.exports}}]);"
  },
  {
    "path": "docs/assets/js/184.90e5f242.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[184],{840:function(e,t,v){\"use strict\";v.r(t);var _=v(45),a=Object(_.a)({},(function(){var e=this,t=e.$createElement,v=e._self._c||t;return v(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":e.$parent.slotKey}},[v(\"h1\",{attrs:{id:\"_10-1-自定义组件方法简介\"}},[v(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_10-1-自定义组件方法简介\"}},[e._v(\"#\")]),e._v(\" 10.1 自定义组件方法简介\")]),e._v(\" \"),v(\"p\",[e._v(\"当Flutter提供的现有组件无法满足我们的需求，或者我们为了共享代码需要封装一些通用组件，这时我们就需要自定义组件。在Flutter中自定义组件有三种方式：通过组合其它组件、自绘和实现RenderObject。本节我们先分别介绍一下这三种方式的特点，后面章节中则详细介绍它们的细节。\")]),e._v(\" \"),v(\"h3\",{attrs:{id:\"组合其它widget\"}},[v(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#组合其它widget\"}},[e._v(\"#\")]),e._v(\" 组合其它Widget\")]),e._v(\" \"),v(\"p\",[e._v(\"这种方式是通过拼装其它组件来组合成一个新的组件。例如我们之前介绍的\"),v(\"code\",[e._v(\"Container\")]),e._v(\"就是一个组合组件，它是由\"),v(\"code\",[e._v(\"DecoratedBox\")]),e._v(\"、\"),v(\"code\",[e._v(\"ConstrainedBox\")]),e._v(\"、\"),v(\"code\",[e._v(\"Transform\")]),e._v(\"、\"),v(\"code\",[e._v(\"Padding\")]),e._v(\"、\"),v(\"code\",[e._v(\"Align\")]),e._v(\"等组件组成。\")]),e._v(\" \"),v(\"p\",[e._v(\"在Flutter中，组合的思想非常重要，Flutter提供了非常多的基础组件，而我们的界面开发其实就是按照需要组合这些组件来实现各种不同的布局而已。\")]),e._v(\" \"),v(\"h3\",{attrs:{id:\"自绘\"}},[v(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#自绘\"}},[e._v(\"#\")]),e._v(\" 自绘\")]),e._v(\" \"),v(\"p\",[e._v(\"如果遇到无法通过现有的组件来实现需要的UI时，我们可以通过自绘组件的方式来实现，例如我们需要一个颜色渐变的圆形进度条，而Flutter提供的\"),v(\"code\",[e._v(\"CircularProgressIndicator\")]),e._v(\"并不支持在显示精确进度时对进度条应用渐变色（其\"),v(\"code\",[e._v(\"valueColor\")]),e._v(\" 属性只支持执行旋转动画时变化Indicator的颜色），这时最好的方法就是通过自定义组件来绘制出我们期望的外观。我们可以通过Flutter中提供的\"),v(\"code\",[e._v(\"CustomPaint\")]),e._v(\"和\"),v(\"code\",[e._v(\"Canvas\")]),e._v(\"来实现UI自绘。\")]),e._v(\" \"),v(\"h3\",{attrs:{id:\"实现renderobject\"}},[v(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#实现renderobject\"}},[e._v(\"#\")]),e._v(\" 实现RenderObject\")]),e._v(\" \"),v(\"p\",[e._v(\"Flutter提供的自身具有UI外观的组件，如文本\"),v(\"code\",[e._v(\"Text\")]),e._v(\"、\"),v(\"code\",[e._v(\"Image\")]),e._v(\"都是通过相应的\"),v(\"code\",[e._v(\"RenderObject\")]),e._v(\"（我们将在“Flutter核心原理”一章中详细介绍\"),v(\"code\",[e._v(\"RenderObject\")]),e._v(\"）渲染出来的，如Text是由\"),v(\"code\",[e._v(\"RenderParagraph\")]),e._v(\"渲染；而\"),v(\"code\",[e._v(\"Image\")]),e._v(\"是由\"),v(\"code\",[e._v(\"RenderImage\")]),e._v(\"渲染。\"),v(\"code\",[e._v(\"RenderObject\")]),e._v(\"是一个抽象类，它定义了一个抽象方法\"),v(\"code\",[e._v(\"paint(...)\")]),e._v(\"：\")]),e._v(\" \"),v(\"div\",{staticClass:\"language-dart extra-class\"},[v(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[v(\"code\",[v(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[e._v(\"void\")]),e._v(\" \"),v(\"span\",{pre:!0,attrs:{class:\"token function\"}},[e._v(\"paint\")]),v(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[e._v(\"(\")]),e._v(\"PaintingContext context\"),v(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[e._v(\",\")]),e._v(\" Offset offset\"),v(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[e._v(\")\")]),e._v(\"\\n\")])])]),v(\"p\",[v(\"code\",[e._v(\"PaintingContext\")]),e._v(\"代表组件的绘制上下文，通过\"),v(\"code\",[e._v(\"PaintingContext.canvas\")]),e._v(\"可以获得\"),v(\"code\",[e._v(\"Canvas\")]),e._v(\"，而绘制逻辑主要是通过\"),v(\"code\",[e._v(\"Canvas\")]),e._v(\" API来实现。子类需要重写此方法以实现自身的绘制逻辑，如\"),v(\"code\",[e._v(\"RenderParagraph\")]),e._v(\"需要实现文本绘制逻辑，而\"),v(\"code\",[e._v(\"RenderImage\")]),e._v(\"需要实现图片绘制逻辑。\")]),e._v(\" \"),v(\"p\",[e._v(\"可以发现，\"),v(\"code\",[e._v(\"RenderObject\")]),e._v(\"中最终也是通过\"),v(\"code\",[e._v(\"Canvas\")]),e._v(\" API来绘制的，那么通过实现\"),v(\"code\",[e._v(\"RenderObject\")]),e._v(\"的方式和上面介绍的通过\"),v(\"code\",[e._v(\"CustomPaint\")]),e._v(\"和\"),v(\"code\",[e._v(\"Canvas\")]),e._v(\"自绘的方式有什么区别？其实答案很简单，\"),v(\"code\",[e._v(\"CustomPaint\")]),e._v(\"只是为了方便开发者封装的一个代理类，它直接继承自\"),v(\"code\",[e._v(\"SingleChildRenderObjectWidget\")]),e._v(\"，通过\"),v(\"code\",[e._v(\"RenderCustomPaint\")]),e._v(\"的\"),v(\"code\",[e._v(\"paint\")]),e._v(\"方法将\"),v(\"code\",[e._v(\"Canvas\")]),e._v(\"和画笔\"),v(\"code\",[e._v(\"Painter\")]),e._v(\"(需要开发者实现，后面章节介绍)连接起来实现了最终的绘制（绘制逻辑在\"),v(\"code\",[e._v(\"Painter\")]),e._v(\"中）。\")]),e._v(\" \"),v(\"h3\",{attrs:{id:\"总结\"}},[v(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[e._v(\"#\")]),e._v(\" 总结\")]),e._v(\" \"),v(\"p\",[e._v(\"“组合”是自定义组件最简单的方法，在任何需要自定义组件的场景下，我们都应该优先考虑是否能够通过组合来实现。而自绘和通过实现\"),v(\"code\",[e._v(\"RenderObject\")]),e._v(\"的方法本质上是一样的，都需要开发者调用\"),v(\"code\",[e._v(\"Canvas\")]),e._v(\" API手动去绘制UI，优点是强大灵活，理论上可以实现任何外观的UI，而缺点是必须了解\"),v(\"code\",[e._v(\"Canvas\")]),e._v(\" API细节，并且得自己去实现绘制逻辑。\")]),e._v(\" \"),v(\"p\",[e._v(\"在本章接下来的小节中，我们将通过一些实例来详细介绍自定义UI的过程，由于后两种方法本质是相同的，并且Flutter中很多基础组件都是通过\"),v(\"code\",[e._v(\"RenderObject\")]),e._v(\"的形式来实现的，所以后续我们只介绍\"),v(\"code\",[e._v(\"CustomPaint\")]),e._v(\"和\"),v(\"code\",[e._v(\"Canvas\")]),e._v(\"的方式，读者如果对自定义\"),v(\"code\",[e._v(\"RenderObject\")]),e._v(\"的方法好奇，可以查看Flutter中相关基础组件对应的\"),v(\"code\",[e._v(\"RenderObject\")]),e._v(\"的实现源码，如\"),v(\"code\",[e._v(\"RenderParagraph\")]),e._v(\"或\"),v(\"code\",[e._v(\"RenderImage\")]),e._v(\"。\")])])}),[],!1,null,null,null);t.default=a.exports}}]);"
  },
  {
    "path": "docs/assets/js/185.5cca4d69.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[185],{842:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_11-3-http请求-dio-http库\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_11-3-http请求-dio-http库\"}},[t._v(\"#\")]),t._v(\" 11.3 Http请求-Dio http库\")]),t._v(\" \"),a(\"p\",[t._v(\"通过上一节介绍，我们可以发现直接使用HttpClient发起网络请求是比较麻烦的，很多事情得我们手动处理，如果再涉及到文件上传/下载、Cookie管理等就会非常繁琐。幸运的是，Dart社区有一些第三方http请求库，用它们来发起http请求将会简单的多，本节我们介绍一下目前人气较高的\"),a(\"a\",{attrs:{href:\"https://github.com/flutterchina/dio\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"dio\"),a(\"OutboundLink\")],1),t._v(\"库。\")]),t._v(\" \"),a(\"blockquote\",[a(\"p\",[t._v(\"dio是一个强大的Dart Http请求库，支持Restful API、FormData、拦截器、请求取消、Cookie管理、文件上传/下载、超时等。dio的使用方式随着其版本升级可能会发生变化，如果本节所述内容和dio官方有差异，请以dio官方文档为准。\")])]),t._v(\" \"),a(\"h3\",{attrs:{id:\"引入\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#引入\"}},[t._v(\"#\")]),t._v(\" 引入\")]),t._v(\" \"),a(\"p\",[t._v(\"引入dio:\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-yaml extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"dependencies\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"dio\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ^x.x.x \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"#请使用pub上的最新版本\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"导入并创建dio实例：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:dio/dio.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\nDio dio \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Dio\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"接下来就可以通过 dio实例来发起网络请求了，注意，一个dio实例可以发起多个http请求，一般来说，APP只有一个http数据源时，dio应该使用单例模式。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"示例\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),a(\"p\",[t._v(\"发起 \"),a(\"code\",[t._v(\"GET\")]),t._v(\" 请求 :\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[t._v(\"Response response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\nresponse\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" dio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"/test?id=12&name=wendu\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"对于\"),a(\"code\",[t._v(\"GET\")]),t._v(\"请求我们可以将query参数通过对象来传递，上面的代码等同于：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[t._v(\"response\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" dio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"/test\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"queryParameters\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"id\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"12\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"name\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"wendu\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"发起一个 \"),a(\"code\",[t._v(\"POST\")]),t._v(\" 请求:\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[t._v(\"response\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" dio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"post\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"/test\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"data\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"id\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"12\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"name\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"wendu\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"发起多个并发请求:\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[t._v(\"response\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" Future\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"wait\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"dio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"post\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"/info\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"dio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"/token\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"下载文件:\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[t._v(\"response\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" dio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"download\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"https://www.google.com/\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"_savePath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"发送 FormData:\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[t._v(\"FormData formData \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FormData\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"from\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"name\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"wendux\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n   \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"age\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"25\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\nresponse \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" dio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"post\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"/info\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" data\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" formData\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"如果发送的数据是FormData，则dio会将请求header的\"),a(\"code\",[t._v(\"contentType\")]),t._v(\"设为“multipart/form-data”。\")]),t._v(\" \"),a(\"p\",[t._v(\"通过FormData上传多个文件:\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[t._v(\"FormData formData \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FormData\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"from\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"name\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"wendux\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n   \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"age\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"25\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n   \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"file1\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"UploadFileInfo\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"File\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"./upload.txt\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"upload1.txt\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n   \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"file2\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"UploadFileInfo\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"File\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"./upload.txt\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"upload2.txt\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n     \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 支持文件数组上传\")]),t._v(\"\\n   \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"files\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"UploadFileInfo\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"File\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"./example/upload.txt\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"upload.txt\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"UploadFileInfo\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"File\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"./example/upload.txt\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"upload.txt\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\nresponse \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" dio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"post\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"/info\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" data\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" formData\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"值得一提的是，dio内部仍然使用HttpClient发起的请求，所以代理、请求认证、证书校验等和HttpClient是相同的，我们可以在\"),a(\"code\",[t._v(\"onHttpClientCreate\")]),t._v(\"回调中设置，例如：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"dio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"httpClientAdapter \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"as\")]),t._v(\" DefaultHttpClientAdapter\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onHttpClientCreate \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"client\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//设置代理 \")]),t._v(\"\\n    client\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"findProxy \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"uri\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"PROXY 192.168.1.2:8888\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//校验证书\")]),t._v(\"\\n    httpClient\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"badCertificateCallback\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"X509Certificate cert\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" String host\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int port\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"cert\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"pem\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\"PEM\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//证书一致，则允许发送数据\")]),t._v(\"\\n     \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n     \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"   \\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"注意，\"),a(\"code\",[t._v(\"onHttpClientCreate\")]),t._v(\"会在当前dio实例内部需要创建HttpClient时调用，所以通过此回调配置HttpClient会对整个dio实例生效，如果你想针对某个应用请求单独的代理或证书校验策略，可以创建一个新的dio实例即可。\")]),t._v(\" \"),a(\"p\",[t._v(\"怎么样，是不是很简单，除了这些基本的用法，dio还支持请求配置、拦截器等，官方资料比较详细，故本书不再赘述，详情可以参考dio主页：https://github.com/flutterchina/dio 。 下一节我们将使用dio实现一个分块下载器。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"实例\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#实例\"}},[t._v(\"#\")]),t._v(\" 实例\")]),t._v(\" \"),a(\"p\",[t._v(\"我们通过Github开放的API来请求flutterchina组织下的所有公开的开源项目，实现：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"在请求阶段弹出loading\")]),t._v(\" \"),a(\"li\",[t._v(\"请求结束后，如果请求失败，则展示错误信息；如果成功，则将项目名称列表展示出来。\")])]),t._v(\" \"),a(\"p\",[t._v(\"代码如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_FutureBuilderRouteState\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"FutureBuilderRoute\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  Dio _dio \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Dio\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FutureBuilder\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          future\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _dio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"https://api.github.com/orgs/flutterchina/repos\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          builder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" AsyncSnapshot snapshot\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//请求完成\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"snapshot\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"connectionState \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" ConnectionState\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"done\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              Response response \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" snapshot\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//发生错误\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"snapshot\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"hasError\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"snapshot\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"error\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//请求成功，通过项目信息构建用于显示项目名称的ListView\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListView\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"map\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"full_name\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toList\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//请求未完成时弹出loading\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircularProgressIndicator\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/186.56dca3cc.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[186],{843:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_11-4-实例-http分块下载\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_11-4-实例-http分块下载\"}},[t._v(\"#\")]),t._v(\" 11.4 实例：Http分块下载\")]),t._v(\" \"),a(\"p\",[t._v(\"本节将通过一个“Http分块下载”的示例演示一下dio的具体用法。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"原理\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#原理\"}},[t._v(\"#\")]),t._v(\" 原理\")]),t._v(\" \"),a(\"p\",[t._v('Http协议定义了分块传输的响应header字段，但具体是否支持取决于Server的实现，我们可以指定请求头的\"range\"字段来验证服务器是否支持分块传输。例如，我们可以利用curl命令来验证：')]),t._v(\" \"),a(\"div\",{staticClass:\"language-shell extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[a(\"code\",[t._v(\"bogon:~ duwen$ \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"curl\")]),t._v(\" -H \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Range: bytes=0-10\"')]),t._v(\" http://download.dcloud.net.cn/HBuilder.9.0.2.macosx_64.dmg -v\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"# 请求头\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" GET /HBuilder.9.0.2.macosx_64.dmg HTTP/1.1\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Host: download.dcloud.net.cn\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" User-Agent: curl/7.54.0\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Accept: */*\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Range: \"),a(\"span\",{pre:!0,attrs:{class:\"token assign-left variable\"}},[t._v(\"bytes\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),t._v(\"-10\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"# 响应头\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\" HTTP/1.1 \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"206\")]),t._v(\" Partial Content\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\" Content-Type: application/octet-stream\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\" Content-Length: \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"11\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\" Connection: keep-alive\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\" Date: Thu, \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"21\")]),t._v(\" Feb \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2019\")]),t._v(\" 06:25:15 GMT\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\" Content-Range: bytes \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),t._v(\"-10/233295878\\n\\n\")])])]),a(\"p\",[t._v('我们在请求头中添加\"Range: bytes=0-10\"的作用是，告诉服务器本次请求我们只想获取文件0-10(包括10，共11字节)这块内容。如果服务器支持分块传输，则响应状态码为206，表示“部分内容”，并且同时响应头中包含“Content-Range”字段，如果不支持则不会包含。我们看看上面“Content-Range”的内容：')]),t._v(\" \"),a(\"div\",{staticClass:\"language- extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[a(\"code\",[t._v(\"Content-Range: bytes 0-10/233295878\\n\")])])]),a(\"p\",[t._v(\"0-10表示本次返回的区块，233295878代表文件的总长度，单位都是byte,  也就是该文件大概233M多一点。\")]),t._v(\" \"),a(\"p\",[t._v(\"基于此，我们可以设计一个简单的多线程的文件分块下载器，实现的思路是：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"先检测是否支持分块传输，如果不支持，则直接下载；若支持，则将剩余内容分块下载。\")]),t._v(\" \"),a(\"li\",[t._v(\"各个分块下载时保存到各自临时文件，等到所有分块下载完后合并临时文件。\")]),t._v(\" \"),a(\"li\",[t._v(\"删除临时文件。\")])]),t._v(\" \"),a(\"h3\",{attrs:{id:\"实现\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#实现\"}},[t._v(\"#\")]),t._v(\" 实现\")]),t._v(\" \"),a(\"p\",[t._v(\"下面是整体的流程：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 通过第一个分块请求检测服务器是否支持分块传输  \")]),t._v(\"\\nResponse response \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"downloadChunk\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"url\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" firstChunkSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"statusCode \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"206\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//如果支持\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//解析文件总长度，进而算出剩余长度\")]),t._v(\"\\n    total \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" int\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"parse\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"headers\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"value\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"HttpHeaders\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"contentRangeHeader\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"split\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"/\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"last\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    int reserved \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" total \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\"\\n        int\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"parse\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"headers\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"value\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"HttpHeaders\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"contentLengthHeader\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//文件的总块数(包括第一块)\")]),t._v(\"\\n    int chunk \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"reserved \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" firstChunkSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ceil\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"chunk \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        int chunkSize \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" firstChunkSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"chunk \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" maxChunk \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            chunk \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" maxChunk \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            chunkSize \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"reserved \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" maxChunk\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ceil\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" futures \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Future\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"for\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int i \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" i \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\" maxChunk\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"++\")]),t._v(\"i\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            int start \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" firstChunkSize \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" i \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" chunkSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//分块下载剩余文件  \")]),t._v(\"\\n            futures\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"downloadChunk\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"url\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" start\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" start \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" chunkSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" i \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//等待所有分块全部下载完成\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" Future\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"wait\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"futures\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//合并文件文件  \")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"mergeTempFiles\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"chunk\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"下面我们使用dio的\"),a(\"code\",[t._v(\"download\")]),t._v(\" API 实现\"),a(\"code\",[t._v(\"downloadChunk\")]),t._v(\"：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//start 代表当前块的起始位置，end代表结束位置\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//no 代表当前是第几块\")]),t._v(\"\\nFuture\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Response\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"downloadChunk\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"url\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" start\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" end\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" no\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  progress\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//progress记录每一块已接收数据的长度\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"--\")]),t._v(\"end\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" dio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"download\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    url\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    savePath \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"temp$no\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//临时文件按照块的序号命名，方便最后合并\")]),t._v(\"\\n    onReceiveProgress\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createCallback\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"no\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 创建进度回调，后面实现\")]),t._v(\"\\n    options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Options\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      headers\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"range\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"bytes=$start-$end\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//指定请求的内容区间\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"接下来实现\"),a(\"code\",[t._v(\"mergeTempFiles\")]),t._v(\":\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[t._v(\"Future \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"mergeTempFiles\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"chunk\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  File f \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"File\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"savePath \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"temp0\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  IOSink ioSink\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" f\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"openWrite\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"mode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" FileMode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"writeOnlyAppend\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//合并临时文件  \")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"for\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int i \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" i \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\" chunk\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"++\")]),t._v(\"i\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    File _f \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"File\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"savePath \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"temp$i\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" ioSink\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addStream\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_f\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"openRead\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" _f\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"delete\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//删除临时文件\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" ioSink\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"close\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" f\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"rename\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"savePath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//合并后的文件重命名为真正的名称\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"下面我们看一下完整实现：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// Downloading by spiting as file in chunks\")]),t._v(\"\\nFuture \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"downloadWithChunks\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  url\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  savePath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  ProgressCallback onReceiveProgress\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" firstChunkSize \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"102\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" maxChunk \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  int total \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" dio \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Dio\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" progress \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"int\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createCallback\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"no\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int received\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" _\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      progress\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"no\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" received\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"onReceiveProgress \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\" total \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onReceiveProgress\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"progress\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"reduce\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"a\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" b\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" a \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" b\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" total\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  Future\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Response\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"downloadChunk\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"url\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" start\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" end\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" no\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    progress\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"--\")]),t._v(\"end\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" dio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"download\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      url\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      savePath \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"temp$no\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      onReceiveProgress\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createCallback\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"no\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Options\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        headers\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"range\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"bytes=$start-$end\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  Future \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"mergeTempFiles\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"chunk\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    File f \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"File\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"savePath \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"temp0\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    IOSink ioSink\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" f\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"openWrite\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"mode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" FileMode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"writeOnlyAppend\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"for\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int i \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" i \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\" chunk\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"++\")]),t._v(\"i\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      File _f \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"File\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"savePath \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"temp$i\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" ioSink\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addStream\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_f\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"openRead\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" _f\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"delete\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" ioSink\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"close\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" f\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"rename\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"savePath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  Response response \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"downloadChunk\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"url\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" firstChunkSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"statusCode \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"206\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    total \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" int\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"parse\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"headers\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"value\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"HttpHeaders\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"contentRangeHeader\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"split\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"/\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"last\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    int reserved \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" total \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\"\\n        int\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"parse\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"headers\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"value\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"HttpHeaders\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"contentLengthHeader\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    int chunk \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"reserved \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" firstChunkSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ceil\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"chunk \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      int chunkSize \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" firstChunkSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"chunk \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" maxChunk \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        chunk \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" maxChunk \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        chunkSize \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"reserved \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" maxChunk\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ceil\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" futures \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Future\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"for\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int i \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" i \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\" maxChunk\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"++\")]),t._v(\"i\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        int start \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" firstChunkSize \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" i \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" chunkSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        futures\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"downloadChunk\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"url\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" start\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" start \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" chunkSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" i \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" Future\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"wait\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"futures\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"mergeTempFiles\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"chunk\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"现在可以进行分块下载了：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"main\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" url \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"http://download.dcloud.net.cn/HBuilder.9.0.2.macosx_64.dmg\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" savePath \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"./example/HBuilder.9.0.2.macosx_64.dmg\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"downloadWithChunks\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"url\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" savePath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" onReceiveProgress\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"received\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" total\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"total \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"${(received / total * 100).floor()}%\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"h3\",{attrs:{id:\"思考\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#思考\"}},[t._v(\"#\")]),t._v(\" 思考\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[a(\"p\",[t._v(\"分块下载真的能提高下载速度吗？\")]),t._v(\" \"),a(\"p\",[t._v(\"其实下载速度的主要瓶颈是取决于网络速度和服务器的出口速度，如果是同一个数据源，分块下载的意义并不大，因为服务器是同一个，出口速度确定的，主要取决于网速，而上面的例子正式同源分块下载，读者可以自己对比一下分块和不分块的的下载速度。如果有多个下载源，并且每个下载源的出口带宽都是有限制的，这时分块下载可能会更快一下，之所以说“可能”，是由于这并不是一定的，比如有三个源，三个源的出口带宽都为1G/s，而我们设备所连网络的峰值假设只有800M/s，那么瓶颈就在我们的网络。即使我们设备的带宽大于任意一个源，下载速度依然不一定就比单源单线下载快，试想一下，假设有两个源A和B，速度A源是B源的3倍，如果采用分块下载，两个源各下载一半的话，读者可以算一下所需的下载时间，然后再算一下只从A源下载所需的时间，看看哪个更快。\")]),t._v(\" \"),a(\"p\",[t._v(\"分块下载的最终速度受设备所在网络带宽、源出口速度、每个块大小、以及分块的数量等诸多因素影响，实际过程中很难保证速度最优。在实际开发中，读者可可以先测试对比后再决定是否使用。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"分块下载有什么实际的用处吗？\")]),t._v(\" \"),a(\"p\",[t._v(\"分块下载还有一个比较使用的场景是断点续传，可以将文件分为若干个块，然后维护一个下载状态文件用以记录每一个块的状态，这样即使在网络中断后，也可以恢复中断前的状态，具体实现读者可以自己尝试一下，还是有一些细节需要特别注意的，比如分块大小多少合适？下载到一半的块如何处理？要不要维护一个任务队列？\")])])])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/187.405078e0.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[187],{844:function(t,a,s){\"use strict\";s.r(a);var n=s(45),e=Object(n.a)({},(function(){var t=this,a=t.$createElement,s=t._self._c||a;return s(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[s(\"h1\",{attrs:{id:\"_11-1-文件操作\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_11-1-文件操作\"}},[t._v(\"#\")]),t._v(\" 11.1 文件操作\")]),t._v(\" \"),s(\"p\",[t._v(\"Dart的IO库包含了文件读写的相关类，它属于Dart语法标准的一部分，所以通过Dart IO库，无论是Dart VM下的脚本还是Flutter，都是通过Dart IO库来操作文件的，不过和Dart VM相比，Flutter有一个重要差异是文件系统路径不同，这是因为Dart VM是运行在PC或服务器操作系统下，而Flutter是运行在移动操作系统中，他们的文件系统会有一些差异。\")]),t._v(\" \"),s(\"h4\",{attrs:{id:\"app目录\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#app目录\"}},[t._v(\"#\")]),t._v(\" APP目录\")]),t._v(\" \"),s(\"p\",[t._v(\"Android和iOS的应用存储目录不同，\"),s(\"a\",{attrs:{href:\"https://pub.dartlang.org/packages/path_provider\",target:\"_blank\",rel:\"noopener noreferrer\"}},[s(\"code\",[t._v(\"PathProvider\")]),s(\"OutboundLink\")],1),t._v(\" 插件提供了一种平台透明的方式来访问设备文件系统上的常用位置。该类当前支持访问两个文件系统位置：\")]),t._v(\" \"),s(\"ul\",[s(\"li\",[s(\"strong\",[t._v(\"临时目录:\")]),t._v(\"  可以使用 \"),s(\"code\",[t._v(\"getTemporaryDirectory()\")]),t._v(\" 来获取临时目录； 系统可随时清除的临时目录（缓存）。在iOS上，这对应于\"),s(\"a\",{attrs:{href:\"https://developer.apple.com/reference/foundation/1409211-nstemporarydirectory\",target:\"_blank\",rel:\"noopener noreferrer\"}},[s(\"code\",[t._v(\"NSTemporaryDirectory()\")]),s(\"OutboundLink\")],1),t._v(\" 返回的值。在Android上，这是\"),s(\"a\",{attrs:{href:\"https://developer.android.com/reference/android/content/Context.html#getCacheDir()\",target:\"_blank\",rel:\"noopener noreferrer\"}},[s(\"code\",[t._v(\"getCacheDir()\")]),s(\"OutboundLink\")],1),t._v(\"返回的值。\")]),t._v(\" \"),s(\"li\",[s(\"strong\",[t._v(\"文档目录:\")]),t._v(\" 可以使用\"),s(\"code\",[t._v(\"getApplicationDocumentsDirectory()\")]),t._v(\"来获取应用程序的文档目录，该目录用于存储只有自己可以访问的文件。只有当应用程序被卸载时，系统才会清除该目录。在iOS上，这对应于\"),s(\"code\",[t._v(\"NSDocumentDirectory\")]),t._v(\"。在Android上，这是\"),s(\"code\",[t._v(\"AppData\")]),t._v(\"目录。\")]),t._v(\" \"),s(\"li\",[s(\"strong\",[t._v(\"外部存储目录\")]),t._v(\"：可以使用\"),s(\"code\",[t._v(\"getExternalStorageDirectory()\")]),t._v(\"来获取外部存储目录，如SD卡；由于iOS不支持外部目录，所以在iOS下调用该方法会抛出\"),s(\"code\",[t._v(\"UnsupportedError\")]),t._v(\"异常，而在Android下结果是android SDK中\"),s(\"code\",[t._v(\"getExternalStorageDirectory\")]),t._v(\"的返回值。\")])]),t._v(\" \"),s(\"p\",[t._v(\"一旦你的Flutter应用程序有一个文件位置的引用，你可以使用\"),s(\"a\",{attrs:{href:\"https://api.dartlang.org/stable/dart-io/dart-io-library.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"dart:io\"),s(\"OutboundLink\")],1),t._v(\"API来执行对文件系统的读/写操作。有关使用Dart处理文件和目录的详细内容可以参考Dart语言文档，下面我们看一个简单的例子。\")]),t._v(\" \"),s(\"h4\",{attrs:{id:\"示例\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),s(\"p\",[t._v(\"我们还是以计数器为例，实现在应用退出重启后可以恢复点击次数。 这里，我们使用文件来保存数据：\")]),t._v(\" \"),s(\"ol\",[s(\"li\",[s(\"p\",[t._v(\"引入PathProvider插件；在\"),s(\"code\",[t._v(\"pubspec.yaml\")]),t._v(\"文件中添加如下声明：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-yaml extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"path_provider\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ^0.4.1\\n\")])])]),s(\"p\",[t._v(\"添加后，执行\"),s(\"code\",[t._v(\"flutter packages get\")]),t._v(\" 获取一下, 版本号可能随着时间推移会发生变化，读者可以使用最新版。\")])]),t._v(\" \"),s(\"li\",[s(\"p\",[t._v(\"实现：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'dart:io'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'dart:async'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:path_provider/path_provider.dart'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FileOperationRoute\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FileOperationRoute\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"Key key\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _FileOperationRouteState \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_FileOperationRouteState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_FileOperationRouteState\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"FileOperationRoute\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  int _counter\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//从文件读取点击次数\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_readCounter\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int value\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        _counter \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" value\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  Future\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"File\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_getLocalFile\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 获取应用目录\")]),t._v(\"\\n    String dir \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getApplicationDocumentsDirectory\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"path\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"File\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'$dir/counter.txt'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  Future\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"int\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_readCounter\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      File file \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_getLocalFile\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 读取点击次数（以字符串）\")]),t._v(\"\\n      String contents \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" file\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"readAsString\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" int\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"parse\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"contents\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"on\")]),t._v(\" FileSystemException \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  Future\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Null\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_incrementCounter\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _counter\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"++\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 将点击次数以字符串类型写到文件中\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_getLocalFile\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"writeAsString\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'$_counter'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Scaffold\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      appBar\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AppBar\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'文件操作'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      body\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Center\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'点击了 $_counter 次'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      floatingActionButton\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FloatingActionButton\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        onPressed\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _incrementCounter\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        tooltip\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Increment'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Icon\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"add\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"上面代码比较简单，不再赘述，需要说明的是，本示例只是为了演示文件读写，而在实际开发中，如果要存储一些简单的数据，使用shared_preferences插件会比较简单。\")]),t._v(\" \"),s(\"blockquote\",[s(\"p\",[t._v(\"注意，Dart IO库操作文件的API非常丰富，但本书不是介绍Dart语言的，故不详细说明，读者需要的话可以自行学习。\")])])])])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/188.766e2a5e.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[188],{846:function(t,e,o){\"use strict\";o.r(e);var r=o(45),a=Object(r.a)({},(function(){var t=this,e=t.$createElement,o=t._self._c||e;return o(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[o(\"h1\",{attrs:{id:\"本章目录\"}},[o(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本章目录\"}},[t._v(\"#\")]),t._v(\" 本章目录\")]),t._v(\" \"),o(\"ul\",[o(\"li\",[o(\"RouterLink\",{attrs:{to:\"/v2/chapter11/file_operation.html\"}},[t._v(\"11.1：文件操作\")])],1),t._v(\" \"),o(\"li\",[o(\"RouterLink\",{attrs:{to:\"/v2/chapter11/http.html\"}},[t._v(\"11.2：Http请求-HttpClient\")])],1),t._v(\" \"),o(\"li\",[o(\"RouterLink\",{attrs:{to:\"/v2/chapter11/dio.html\"}},[t._v(\"11.3：Http请求-Dio package\")])],1),t._v(\" \"),o(\"li\",[o(\"RouterLink\",{attrs:{to:\"/v2/chapter11/download_with_chunks.html\"}},[t._v(\"11.4：实例：Http分块下载\")])],1),t._v(\" \"),o(\"li\",[o(\"RouterLink\",{attrs:{to:\"/v2/chapter11/websocket.html\"}},[t._v(\"11.5：WebSocket\")])],1),t._v(\" \"),o(\"li\",[o(\"RouterLink\",{attrs:{to:\"/v2/chapter11/socket.html\"}},[t._v(\"11.6：使用Socket API\")])],1),t._v(\" \"),o(\"li\",[o(\"RouterLink\",{attrs:{to:\"/v2/chapter11/json_model.html\"}},[t._v(\"11.7：Json转Dart Model类\")])],1)])])}),[],!1,null,null,null);e.default=a.exports}}]);"
  },
  {
    "path": "docs/assets/js/189.c927c79a.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[189],{849:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"使用websockets\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#使用websockets\"}},[t._v(\"#\")]),t._v(\" 使用WebSockets\")]),t._v(\" \"),a(\"p\",[t._v(\"Http协议是无状态的，只能由客户端主动发起，服务端再被动响应，服务端无法向客户端主动推送内容，并且一旦服务器响应结束，链接就会断开(见注解部分)，所以无法进行实时通信。WebSocket协议正是为解决客户端与服务端实时通信而产生的技术，现在已经被主流浏览器支持，所以对于Web开发者来说应该比较熟悉了，Flutter也提供了专门的包来支持WebSocket协议。\")]),t._v(\" \"),a(\"blockquote\",[a(\"p\",[t._v(\"注意：Http协议中虽然可以通过keep-alive机制使服务器在响应结束后链接会保持一段时间，但最终还是会断开，keep-alive机制主要是用于避免在同一台服务器请求多个资源时频繁创建链接，它本质上是支持链接复用的技术，而并非用于实时通信，读者需要知道这两者的区别。\")])]),t._v(\" \"),a(\"p\",[t._v(\"WebSocket协议本质上是一个基于tcp的协议，它是先通过HTTP协议发起一条特殊的http请求进行握手后，如果服务端支持WebSocket协议，则会进行协议升级。WebSocket会使用http协议握手后创建的tcp链接，和http协议不同的是，WebSocket的tcp链接是个长链接（不会断开），所以服务端与客户端就可以通过此TCP连接进行实时通信。有关WebSocket协议细节，读者可以看RFC文档，下面我们重点看看Flutter中如何使用WebSocket。\")]),t._v(\" \"),a(\"p\",[t._v(\"在接下来例子中，我们将连接到由\"),a(\"a\",{attrs:{href:\"http://www.websocket.org/echo.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"websocket.org提供的测试服务器\"),a(\"OutboundLink\")],1),t._v(\"。服务器将简单地返回我们发送给它的相同消息！\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"步骤\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#步骤\"}},[t._v(\"#\")]),t._v(\" 步骤\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"连接到WebSocket服务器。\")]),t._v(\" \"),a(\"li\",[t._v(\"监听来自服务器的消息。\")]),t._v(\" \"),a(\"li\",[t._v(\"将数据发送到服务器。\")]),t._v(\" \"),a(\"li\",[t._v(\"关闭WebSocket连接。\")])]),t._v(\" \"),a(\"h3\",{attrs:{id:\"_1-连接到websocket服务器\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-连接到websocket服务器\"}},[t._v(\"#\")]),t._v(\" 1. 连接到WebSocket服务器\")]),t._v(\" \"),a(\"p\",[a(\"a\",{attrs:{href:\"https://pub.dartlang.org/packages/web_socket_channel\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"web_socket_channel\"),a(\"OutboundLink\")],1),t._v(\" package 提供了我们需要连接到WebSocket服务器的工具。该package提供了一个\"),a(\"code\",[t._v(\"WebSocketChannel\")]),t._v(\"允许我们既可以监听来自服务器的消息，又可以将消息发送到服务器的方法。\")]),t._v(\" \"),a(\"p\",[t._v(\"在Flutter中，我们可以创建一个\"),a(\"code\",[t._v(\"WebSocketChannel\")]),t._v(\"连接到一台服务器：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" channel \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" IOWebSocketChannel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"connect\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'ws://echo.websocket.org'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"h3\",{attrs:{id:\"_2-监听来自服务器的消息\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_2-监听来自服务器的消息\"}},[t._v(\"#\")]),t._v(\" 2. 监听来自服务器的消息\")]),t._v(\" \"),a(\"p\",[t._v(\"现在我们建立了连接，我们可以监听来自服务器的消息，在我们发送消息给测试服务器之后，它会返回相同的消息。\")]),t._v(\" \"),a(\"p\",[t._v(\"我们如何收取消息并显示它们？在这个例子中，我们将使用一个\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/StreamBuilder-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"StreamBuilder\")]),a(\"OutboundLink\")],1),t._v(\" 来监听新消息， 并用一个Text来显示它们。\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StreamBuilder\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  stream\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" widget\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"channel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"stream\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  builder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" snapshot\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"snapshot\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"hasData \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'${snapshot.data}'\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"''\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"h4\",{attrs:{id:\"工作原理\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#工作原理\"}},[t._v(\"#\")]),t._v(\" 工作原理\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"WebSocketChannel\")]),t._v(\"提供了一个来自服务器的消息\"),a(\"code\",[t._v(\"Stream\")]),t._v(\" 。该\"),a(\"code\",[t._v(\"Stream\")]),t._v(\"类是\"),a(\"code\",[t._v(\"dart:async\")]),t._v(\"包中的一个基础类。它提供了一种方法来监听来自数据源的异步事件。与\"),a(\"code\",[t._v(\"Future\")]),t._v(\"返回单个异步响应不同，\"),a(\"code\",[t._v(\"Stream\")]),t._v(\"类可以随着时间推移传递很多事件。该\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/StreamBuilder-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"StreamBuilder\")]),a(\"OutboundLink\")],1),t._v(\" 组件将连接到一个\"),a(\"code\",[t._v(\"Stream\")]),t._v(\"， 并在每次收到消息时通知Flutter重新构建界面。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"_3-将数据发送到服务器\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-将数据发送到服务器\"}},[t._v(\"#\")]),t._v(\" 3. 将数据发送到服务器\")]),t._v(\" \"),a(\"p\",[t._v(\"为了将数据发送到服务器，我们会\"),a(\"code\",[t._v(\"add\")]),t._v(\"消息给\"),a(\"code\",[t._v(\"WebSocketChannel\")]),t._v(\"提供的sink。\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[t._v(\"channel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"sink\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Hello!'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"h4\",{attrs:{id:\"工作原理-2\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#工作原理-2\"}},[t._v(\"#\")]),t._v(\" 工作原理\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"WebSocketChannel\")]),t._v(\"提供了一个\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/dart-async/StreamSink-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"StreamSink\")]),a(\"OutboundLink\")],1),t._v(\"，它将消息发给服务器。\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"StreamSink\")]),t._v(\"类提供了给数据源同步或异步添加事件的一般方法。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"_4-关闭websocket连接\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_4-关闭websocket连接\"}},[t._v(\"#\")]),t._v(\" 4. 关闭WebSocket连接\")]),t._v(\" \"),a(\"p\",[t._v(\"在我们使用\"),a(\"code\",[t._v(\"WebSocket\")]),t._v(\"后，要关闭连接：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[t._v(\"channel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"sink\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"close\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"h3\",{attrs:{id:\"完整的例子\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#完整的例子\"}},[t._v(\"#\")]),t._v(\" 完整的例子\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:web_socket_channel/io.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"WebSocketRoute\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _WebSocketRouteState \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_WebSocketRouteState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_WebSocketRouteState\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"WebSocketRoute\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  TextEditingController _controller \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TextEditingController\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  IOWebSocketChannel channel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  String _text \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//创建websocket连接\")]),t._v(\"\\n    channel \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"IOWebSocketChannel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"connect\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'ws://echo.websocket.org'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Scaffold\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      appBar\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AppBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        title\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"WebSocket(内容回显)\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      body\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          crossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" CrossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Form\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TextFormField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"InputDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"labelText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Send a message'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StreamBuilder\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              stream\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" channel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"stream\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              builder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" snapshot\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//网络不通会走到这\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"snapshot\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"hasError\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                  _text \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"网络不通...\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"snapshot\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"hasData\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                  _text \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"echo: \"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\"snapshot\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"symmetric\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"vertical\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"24.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_text\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      floatingActionButton\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FloatingActionButton\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _sendMessage\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        tooltip\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Send message'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"send\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_sendMessage\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"text\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isNotEmpty\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      channel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"sink\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"text\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    channel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"sink\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"close\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"上面的例子比较简单，不再赘述。我们现在思考一个问题，假如我们想通过WebSocket传输二进制数据应该怎么做（比如要从服务器接收一张图片）？我们发现\"),a(\"code\",[t._v(\"StreamBuilder\")]),t._v(\"和\"),a(\"code\",[t._v(\"Stream\")]),t._v(\"都没有指定接收类型的参数，并且在创建WebSocket链接时也没有相应的配置，貌似没有什么办法……其实很简单，要接收二进制数据仍然使用\"),a(\"code\",[t._v(\"StreamBuilder\")]),t._v(\"，因为WebSocket中所有发送的数据使用帧的形式发送，而帧是有固定格式，每一个帧的数据类型都可以通过Opcode字段指定，它可以指定当前帧是文本类型还是二进制类型（还有其它类型），所以客户端在收到帧时就已经知道了其数据类型，所以flutter完全可以在收到数据后解析出正确的类型，所以就无需开发者去关心，当服务器传输的数据是指定为二进制时，\"),a(\"code\",[t._v(\"StreamBuilder\")]),t._v(\"的\"),a(\"code\",[t._v(\"snapshot.data\")]),t._v(\"的类型就是\"),a(\"code\",[t._v(\"List<int>\")]),t._v(\"，是文本时，则为\"),a(\"code\",[t._v(\"String\")]),t._v(\"。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/19.175563ad.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[19],{645:function(t,a,s){t.exports=s.p+\"assets/img/5-18.f83914b2.png\"},646:function(t,a,s){t.exports=s.p+\"assets/img/5-19.a2dab018.png\"},647:function(t,a){t.exports=\"data:image/png;base64,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\"},648:function(t,a,s){t.exports=s.p+\"assets/img/5-21.946b0c47.png\"},649:function(t,a,s){t.exports=s.p+\"assets/img/5-22.e38796ed.png\"},650:function(t,a,s){t.exports=s.p+\"assets/img/5-23.9b16ea45.png\"},906:function(t,a,s){\"use strict\";s.r(a);var n=s(45),p=Object(n.a)({},(function(){var t=this,a=t.$createElement,n=t._self._c||a;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_5-6-scaffold、tabbar、底部导航\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_5-6-scaffold、tabbar、底部导航\"}},[t._v(\"#\")]),t._v(\" 5.6 Scaffold、TabBar、底部导航\")]),t._v(\" \"),n(\"p\",[t._v(\"Material组件库提供了丰富多样的组件，本节介绍一些常用的组件，其余的读者可以自行查看文档或Flutter Gallery中Material组件部分的示例。\")]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[t._v(\"Flutter Gallery是Flutter官方提供的Flutter Demo，源码位于flutter源码中的examples目录下，笔者强烈建议用户将Flutter Gallery示例跑起来，它是一个很全面的Flutter示例应用，是非常好的参考Demo，也是笔者学习Flutter的第一手资料。\")])]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_5-6-1-scaffold\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_5-6-1-scaffold\"}},[t._v(\"#\")]),t._v(\" 5.6.1 Scaffold\")]),t._v(\" \"),n(\"p\",[t._v(\"一个完整的路由页可能会包含导航栏、抽屉菜单(Drawer)以及底部Tab导航菜单等。如果每个路由页面都需要开发者自己手动去实现这些，这会是一件非常麻烦且无聊的事。幸运的是，Flutter Material组件库提供了一些现成的组件来减少我们的开发任务。\"),n(\"code\",[t._v(\"Scaffold\")]),t._v(\"是一个路由页的骨架，我们使用它可以很容易地拼装出一个完整的页面。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"示例\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),n(\"p\",[t._v(\"我们实现一个页面，它包含：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"一个导航栏\")]),t._v(\" \"),n(\"li\",[t._v(\"导航栏右边有一个分享按钮\")]),t._v(\" \"),n(\"li\",[t._v(\"有一个抽屉菜单\")]),t._v(\" \"),n(\"li\",[t._v(\"有一个底部导航\")]),t._v(\" \"),n(\"li\",[t._v(\"右下角有一个悬浮的动作按钮\")])]),t._v(\" \"),n(\"p\",[t._v(\"最终效果如图5-18、图5-19所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(645),alt:\"图5-18\"}}),t._v(\" \"),n(\"img\",{attrs:{src:s(646),alt:\"图5-19\"}})]),t._v(\" \"),n(\"p\",[t._v(\"实现代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ScaffoldRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _ScaffoldRouteState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_ScaffoldRouteState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_ScaffoldRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ScaffoldRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  int _selectedIndex \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      appBar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//导航栏\")]),t._v(\"\\n        title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"App Name\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n        actions\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//导航栏右侧菜单\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"IconButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"icon\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"share\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      drawer\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyDrawer\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//抽屉\")]),t._v(\"\\n      bottomNavigationBar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BottomNavigationBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 底部导航\")]),t._v(\"\\n        items\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"BottomNavigationBarItem\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BottomNavigationBarItem\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"icon\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"home\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Home'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BottomNavigationBarItem\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"icon\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"business\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Business'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BottomNavigationBarItem\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"icon\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"school\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'School'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        currentIndex\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _selectedIndex\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        fixedColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        onTap\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _onItemTapped\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      floatingActionButton\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FloatingActionButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//悬浮按钮\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"add\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"_onAdd\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_onItemTapped\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _selectedIndex \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_onAdd\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上面代码中我们用到了如下组件：\")]),t._v(\" \"),n(\"table\",[n(\"thead\",[n(\"tr\",[n(\"th\",[t._v(\"组件名称\")]),t._v(\" \"),n(\"th\",[t._v(\"解释\")])])]),t._v(\" \"),n(\"tbody\",[n(\"tr\",[n(\"td\",[t._v(\"AppBar\")]),t._v(\" \"),n(\"td\",[t._v(\"一个导航栏骨架\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"MyDrawer\")]),t._v(\" \"),n(\"td\",[t._v(\"抽屉菜单\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"BottomNavigationBar\")]),t._v(\" \"),n(\"td\",[t._v(\"底部导航栏\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"FloatingActionButton\")]),t._v(\" \"),n(\"td\",[t._v(\"漂浮按钮\")])])])]),t._v(\" \"),n(\"p\",[t._v(\"下面我们来分别介绍一下它们。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_5-6-2-appbar\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_5-6-2-appbar\"}},[t._v(\"#\")]),t._v(\" 5.6.2 AppBar\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"AppBar\")]),t._v(\"是一个Material风格的导航栏，通过它可以设置导航栏标题、导航栏菜单、导航栏底部的Tab标题等。下面我们看看AppBar的定义：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"leading\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//导航栏最左侧Widget，常见为抽屉菜单按钮或返回按钮。\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"automaticallyImplyLeading \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//如果leading为null，是否自动实现默认的leading按钮\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 页面标题\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"actions\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 导航栏右侧菜单\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"bottom\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 导航栏底部菜单，通常为Tab按钮组\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"elevation \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"4.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 导航栏阴影\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"centerTitle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//标题是否居中 \")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"backgroundColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"   \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//其它属性见源码注释\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"如果给\"),n(\"code\",[t._v(\"Scaffold\")]),t._v(\"添加了抽屉菜单，默认情况下\"),n(\"code\",[t._v(\"Scaffold\")]),t._v(\"会自动将\"),n(\"code\",[t._v(\"AppBar\")]),t._v(\"的\"),n(\"code\",[t._v(\"leading\")]),t._v(\"设置为菜单按钮（如上面截图所示），点击它便可打开抽屉菜单。如果我们想自定义菜单图标，可以手动来设置\"),n(\"code\",[t._v(\"leading\")]),t._v(\"，如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  appBar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"App Name\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    leading\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"IconButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        icon\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dashboard\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//自定义图标\")]),t._v(\"\\n        onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 打开抽屉菜单  \")]),t._v(\"\\n          Scaffold\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"openDrawer\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"  \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"  \\n\")])])]),n(\"p\",[t._v(\"代码运行效果如图5-20所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(647),alt:\"图5-20\"}})]),t._v(\" \"),n(\"p\",[t._v(\"可以看到左侧菜单已经替换成功。\")]),t._v(\" \"),n(\"p\",[t._v(\"代码中打开抽屉菜单的方法在\"),n(\"code\",[t._v(\"ScaffoldState\")]),t._v(\"中，通过\"),n(\"code\",[t._v(\"Scaffold.of(context)\")]),t._v(\"可以获取父级最近的\"),n(\"code\",[t._v(\"Scaffold\")]),t._v(\" 组件的\"),n(\"code\",[t._v(\"State\")]),t._v(\"对象。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"tabbar\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#tabbar\"}},[t._v(\"#\")]),t._v(\" TabBar\")]),t._v(\" \"),n(\"p\",[t._v(\"下面我们通过“bottom”属性来添加一个导航栏底部Tab按钮组，将要实现的效果如图5-21所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(648),alt:\"图5-21\"}})]),t._v(\" \"),n(\"p\",[t._v(\"Material组件库中提供了一个\"),n(\"code\",[t._v(\"TabBar\")]),t._v(\"组件，它可以快速生成\"),n(\"code\",[t._v(\"Tab\")]),t._v(\"菜单，下面是上图对应的源码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_ScaffoldRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ScaffoldRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"with\")]),t._v(\" SingleTickerProviderStateMixin \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\n  TabController _tabController\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//需要定义一个Controller\")]),t._v(\"\\n  List tabs \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"新闻\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"历史\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"图片\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 创建Controller  \")]),t._v(\"\\n    _tabController \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TabController\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"length\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" tabs\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"length\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" vsync\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      appBar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n        bottom\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TabBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"   \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//生成Tab菜单\")]),t._v(\"\\n          controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _tabController\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          tabs\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" tabs\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"map\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Tab\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toList\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上面代码首先创建了一个\"),n(\"code\",[t._v(\"TabController\")]),t._v(\" ，它是用于控制/监听\"),n(\"code\",[t._v(\"Tab\")]),t._v(\"菜单切换的。接下来通过TabBar生成了一个底部菜单栏，\"),n(\"code\",[t._v(\"TabBar\")]),t._v(\"的\"),n(\"code\",[t._v(\"tabs\")]),t._v(\"属性接受一个Widget数组，表示每一个Tab子菜单，我们可以自定义，也可以像示例中一样直接使用\"),n(\"code\",[t._v(\"Tab\")]),t._v(\" 组件，它是Material组件库提供的Material风格的Tab菜单。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"Tab\")]),t._v(\"组件有三个可选参数，除了可以指定文字外，还可以指定Tab菜单图标，或者直接自定义组件样式。\"),n(\"code\",[t._v(\"Tab\")]),t._v(\"组件定义如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Tab\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 菜单文本\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"icon\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 菜单图标\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 自定义组件样式\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"开发者可以根据实际需求来定制。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"tabbarview\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#tabbarview\"}},[t._v(\"#\")]),t._v(\" TabBarView\")]),t._v(\" \"),n(\"p\",[t._v(\"通过\"),n(\"code\",[t._v(\"TabBar\")]),t._v(\"我们只能生成一个静态的菜单，真正的Tab页还没有实现。由于\"),n(\"code\",[t._v(\"Tab\")]),t._v(\"菜单和Tab页的切换需要同步，我们需要通过\"),n(\"code\",[t._v(\"TabController\")]),t._v(\"去监听Tab菜单的切换去切换Tab页，代码如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"_tabController\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"  \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"switch\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_tabController\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"   \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"如果我们Tab页可以滑动切换的话，还需要在滑动过程中更新TabBar指示器的偏移！显然，要手动处理这些是很麻烦的，为此，Material库提供了一个\"),n(\"code\",[t._v(\"TabBarView\")]),t._v(\"组件，通过它不仅可以轻松的实现Tab页，而且可以非常容易的配合TabBar来实现同步切换和滑动状态同步，示例如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  appBar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n    bottom\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TabBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _tabController\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      tabs\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" tabs\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"map\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Tab\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toList\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  drawer\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyDrawer\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  body\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TabBarView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _tabController\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" tabs\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"map\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//创建3个Tab页\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" textScaleFactor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"5\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toList\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 省略无关代码  \")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"    \\n\")])])]),n(\"p\",[t._v(\"运行后效果如图5-22所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(649),alt:\"图5-22\"}})]),t._v(\" \"),n(\"p\",[t._v(\"现在，无论是点击导航栏Tab菜单还是在页面上左右滑动，Tab页面都会切换，并且Tab菜单的状态和Tab页面始终保持同步！那它们是如何实现同步的呢？细心的读者可能已经发现，上例中\"),n(\"code\",[t._v(\"TabBar\")]),t._v(\"和\"),n(\"code\",[t._v(\"TabBarView\")]),t._v(\"的\"),n(\"code\",[t._v(\"controller\")]),t._v(\"是同一个！正是如此，\"),n(\"code\",[t._v(\"TabBar\")]),t._v(\"和\"),n(\"code\",[t._v(\"TabBarView\")]),t._v(\"正是通过同一个\"),n(\"code\",[t._v(\"controller\")]),t._v(\"来实现菜单切换和滑动状态同步的，有关\"),n(\"code\",[t._v(\"TabController\")]),t._v(\"的详细信息，我们不在本书做过多介绍，使用时读者直接查看SDK即可。\")]),t._v(\" \"),n(\"p\",[t._v(\"另外，Material组件库也提供了一个\"),n(\"code\",[t._v(\"PageView\")]),t._v(\" 组件，它和\"),n(\"code\",[t._v(\"TabBarView\")]),t._v(\"功能相似，读者可以自行了解一下。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_5-6-3-抽屉菜单drawer\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_5-6-3-抽屉菜单drawer\"}},[t._v(\"#\")]),t._v(\" 5.6.3 抽屉菜单Drawer\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"Scaffold\")]),t._v(\"的\"),n(\"code\",[t._v(\"drawer\")]),t._v(\"和\"),n(\"code\",[t._v(\"endDrawer\")]),t._v(\"属性可以分别接受一个Widget来作为页面的左、右抽屉菜单。如果开发者提供了抽屉菜单，那么当用户手指从屏幕左（或右）侧向里滑动时便可打开抽屉菜单。本节开始部分的示例中实现了一个左抽屉菜单\"),n(\"code\",[t._v(\"MyDrawer\")]),t._v(\"，它的源码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyDrawer\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyDrawer\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Drawer\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MediaQuery\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"removePadding\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//移除抽屉菜单顶部默认留白\")]),t._v(\"\\n        removeTop\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          crossAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" CrossAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"only\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"top\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"38.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"symmetric\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"horizontal\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ClipOval\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"asset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                        \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"imgs/avatar.png\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Wendux\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"fontWeight\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" FontWeight\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"bold\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Expanded\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    leading\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"add\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Add account'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    leading\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"settings\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Manage accounts'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"抽屉菜单通常将\"),n(\"code\",[t._v(\"Drawer\")]),t._v(\"组件作为根节点，它实现了Material风格的菜单面板，\"),n(\"code\",[t._v(\"MediaQuery.removePadding\")]),t._v(\"可以移除Drawer默认的一些留白（比如Drawer默认顶部会留和手机状态栏等高的留白），读者可以尝试传递不同的参数来看看实际效果。抽屉菜单页由顶部和底部组成，顶部由用户头像和昵称组成，底部是一个菜单列表，用ListView实现，关于ListView我们将在后面“可滚动组件”一节详细介绍。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_5-6-4-floatingactionbutton\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_5-6-4-floatingactionbutton\"}},[t._v(\"#\")]),t._v(\" 5.6.4 FloatingActionButton\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"FloatingActionButton\")]),t._v('是Material设计规范中的一种特殊Button，通常悬浮在页面的某一个位置作为某种常用动作的快捷入口，如本节示例中页面右下角的\"➕\"号按钮。我们可以通过'),n(\"code\",[t._v(\"Scaffold\")]),t._v(\"的\"),n(\"code\",[t._v(\"floatingActionButton\")]),t._v(\"属性来设置一个\"),n(\"code\",[t._v(\"FloatingActionButton\")]),t._v(\"，同时通过\"),n(\"code\",[t._v(\"floatingActionButtonLocation\")]),t._v(\"属性来指定其在页面中悬浮的位置，这个比较简单，不再赘述。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_5-6-5-底部tab导航栏\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_5-6-5-底部tab导航栏\"}},[t._v(\"#\")]),t._v(\" 5.6.5  底部Tab导航栏\")]),t._v(\" \"),n(\"p\",[t._v(\"我们可以通过\"),n(\"code\",[t._v(\"Scaffold\")]),t._v(\"的\"),n(\"code\",[t._v(\"bottomNavigationBar\")]),t._v(\"属性来设置底部导航，如本节开始示例所示，我们通过Material组件库提供的\"),n(\"code\",[t._v(\"BottomNavigationBar\")]),t._v(\"和\"),n(\"code\",[t._v(\"BottomNavigationBarItem\")]),t._v(\"两种组件来实现Material风格的底部导航栏。可以看到上面的实现代码非常简单，所以不再赘述，但是如果我们想实现如图5-23所示效果的底部导航栏应该怎么做呢？\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(650),alt:\"图5-23\"}})]),t._v(\" \"),n(\"p\",[t._v(\"Material组件库中提供了一个\"),n(\"code\",[t._v(\"BottomAppBar\")]),t._v(\" 组件，它可以和\"),n(\"code\",[t._v(\"FloatingActionButton\")]),t._v(\"配合实现这种“打洞”效果，源码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"bottomNavigationBar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BottomAppBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  shape\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircularNotchedRectangle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 底部导航栏打一个圆形的洞\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"IconButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"icon\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"home\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//中间位置空出\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"IconButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"icon\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"business\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    mainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"spaceAround\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//均分底部导航栏横向空间\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"可以看到，上面代码中没有控制打洞位置的属性，实际上，打洞的位置取决于\"),n(\"code\",[t._v(\"FloatingActionButton\")]),t._v(\"的位置，上面\"),n(\"code\",[t._v(\"FloatingActionButton\")]),t._v(\"的位置为：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"floatingActionButtonLocation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" FloatingActionButtonLocation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"centerDocked\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"所以打洞位置在底部导航栏的正中间。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"BottomAppBar\")]),t._v(\"的\"),n(\"code\",[t._v(\"shape\")]),t._v(\"属性决定洞的外形，\"),n(\"code\",[t._v(\"CircularNotchedRectangle\")]),t._v(\"实现了一个圆形的外形，我们也可以自定义外形，比如，Flutter Gallery示例中就有一个“钻石”形状的示例，读者感兴趣可以自行查看。\")])])}),[],!1,null,null,null);a.default=p.exports}}]);"
  },
  {
    "path": "docs/assets/js/190.92378aeb.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[190],{850:function(t,a,s){\"use strict\";s.r(a);var n=s(45),e=Object(n.a)({},(function(){var t=this,a=t.$createElement,s=t._self._c||a;return s(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[s(\"h1\",{attrs:{id:\"_12-4-插件开发-android端api实现\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_12-4-插件开发-android端api实现\"}},[t._v(\"#\")]),t._v(\" 12.4 插件开发：Android端API实现\")]),t._v(\" \"),s(\"p\",[t._v('本节我们接着上一节\"获取电池电量\"插件的示例，来完成Android端API的实现。以下步骤是使用Java的示例，如果您更喜欢Kotlin，可以直接跳到后面Kotlin部分。')]),t._v(\" \"),s(\"p\",[t._v(\"首先在Android Studio中打开您的Flutter应用的Android部分：\")]),t._v(\" \"),s(\"ol\",[s(\"li\",[t._v(\"启动 Android Studio\")]),t._v(\" \"),s(\"li\",[t._v(\"选择 File > Open…\")]),t._v(\" \"),s(\"li\",[t._v(\"定位到您 Flutter app目录, 然后选择里面的 \"),s(\"code\",[t._v(\"android\")]),t._v(\"文件夹，点击 OK\")]),t._v(\" \"),s(\"li\",[t._v(\"在\"),s(\"code\",[t._v(\"java\")]),t._v(\"目录下打开 \"),s(\"code\",[t._v(\"MainActivity.java\")])])]),t._v(\" \"),s(\"p\",[t._v(\"接下来，在\"),s(\"code\",[t._v(\"onCreate\")]),t._v(\"里创建MethodChannel并设置一个\"),s(\"code\",[t._v(\"MethodCallHandler\")]),t._v(\"。确保使用和Flutter客户端中使用的通道名称相同的名称。\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" io\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"flutter\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"app\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"FlutterActivity\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" io\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"flutter\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"plugin\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"common\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"MethodCall\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" io\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"flutter\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"plugin\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"common\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"MethodChannel\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" io\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"flutter\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"plugin\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"common\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"MethodChannel\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"MethodCallHandler\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" io\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"flutter\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"plugin\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"common\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"MethodChannel\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"Result\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\npublic \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MainActivity\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FlutterActivity\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    private \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String CHANNEL \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"samples.flutter.io/battery\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@Override\")]),t._v(\"\\n    public \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onCreate\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Bundle savedInstanceState\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onCreate\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"savedInstanceState\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MethodChannel\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getFlutterView\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" CHANNEL\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setMethodCallHandler\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MethodCallHandler\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n             \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@Override\")]),t._v(\"\\n             public \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onMethodCall\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"MethodCall call\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Result result\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                 \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// TODO\")]),t._v(\"\\n             \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"接下来，我们添加Java代码，使用Android电池API来获取电池电量。此代码和在原生Android应用中编写的代码完全相同。\")]),t._v(\" \"),s(\"p\",[t._v(\"首先，添加需要导入的依赖。\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" android\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"content\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"ContextWrapper\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" android\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"content\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"Intent\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" android\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"content\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"IntentFilter\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" android\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"os\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"BatteryManager\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" android\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"os\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"Build\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"VERSION\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" android\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"os\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"Build\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"VERSION_CODES\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" android\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"os\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"Bundle\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"然后，将下面的新方法添加到activity类中的，位于onCreate 方法下方：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-java extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-java\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"private\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"int\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getBatteryLevel\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"int\")]),t._v(\" batteryLevel \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"VERSION\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"SDK_INT \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">=\")]),t._v(\" VERSION_CODES\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"LOLLIPOP\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"BatteryManager\")]),t._v(\" batteryManager \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"BatteryManager\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getSystemService\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BATTERY_SERVICE\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    batteryLevel \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" batteryManager\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getIntProperty\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"BatteryManager\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"BATTERY_PROPERTY_CAPACITY\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Intent\")]),t._v(\" intent \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ContextWrapper\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getApplicationContext\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"registerReceiver\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"IntentFilter\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Intent\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"ACTION_BATTERY_CHANGED\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    batteryLevel \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"intent\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getIntExtra\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"BatteryManager\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"EXTRA_LEVEL\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\"\\n        intent\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getIntExtra\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"BatteryManager\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"EXTRA_SCALE\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" batteryLevel\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"最后，我们完成之前添加的\"),s(\"code\",[t._v(\"onMethodCall\")]),t._v(\"方法。我们需要处理平台方法名为\"),s(\"code\",[t._v(\"getBatteryLevel\")]),t._v(\"的调用消息，所以我们需要先在call参数判断调用的方法是否为\"),s(\"code\",[t._v(\"getBatteryLevel\")]),t._v(\"。 这个平台方法的实现只需调用我们在前一步中编写的Android代码，并通过result参数返回成功或错误情况的响应信息。如果调用了未定义的API，我们也会通知返回：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-java extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-java\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token annotation punctuation\"}},[t._v(\"@Override\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"public\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onMethodCall\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MethodCall\")]),t._v(\" call\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Result\")]),t._v(\" result\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"call\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"method\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"equals\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"getBatteryLevel\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"int\")]),t._v(\" batteryLevel \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getBatteryLevel\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"batteryLevel \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            result\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"success\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"batteryLevel\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            result\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"error\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"UNAVAILABLE\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Battery level not available.\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        result\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"notImplemented\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"  \\n\")])])]),s(\"p\",[t._v('现在就可以在Android上运行该应用程序了，如果使用的是Android模拟器，则可以通过工具栏中的\"...\"按钮访问Extended Controls面板中的电池电量。')]),t._v(\" \"),s(\"h3\",{attrs:{id:\"使用kotlin添加android平台特定的实现\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#使用kotlin添加android平台特定的实现\"}},[t._v(\"#\")]),t._v(\" 使用Kotlin添加Android平台特定的实现\")]),t._v(\" \"),s(\"p\",[t._v(\"使用Kotlin和使用Java的步骤类似，首先在Android Studio中打开您的Flutter应用的Android部分：\")]),t._v(\" \"),s(\"ol\",[s(\"li\",[t._v(\"启动 Android Studio。\")]),t._v(\" \"),s(\"li\",[t._v('选择 the menu item \"File > Open…\"。')]),t._v(\" \"),s(\"li\",[t._v(\"定位到 Flutter app目录, 然后选择里面的 \"),s(\"code\",[t._v(\"android\")]),t._v(\"文件夹，点击 OK。\")]),t._v(\" \"),s(\"li\",[t._v(\"在\"),s(\"code\",[t._v(\"kotlin\")]),t._v(\"目录中打开\"),s(\"code\",[t._v(\"MainActivity.kt\")]),t._v(\"。\")])]),t._v(\" \"),s(\"p\",[t._v(\"接下来，在\"),s(\"code\",[t._v(\"onCreate\")]),t._v(\"里创建MethodChannel并设置一个\"),s(\"code\",[t._v(\"MethodCallHandler\")]),t._v(\"。确保使用与在Flutter客户端使用的通道名称相同。\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-kotlin extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-kotlin\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" android\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"os\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"Bundle\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" io\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"flutter\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"app\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"FlutterActivity\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" io\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"flutter\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"plugin\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"common\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"MethodChannel\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" io\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"flutter\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"plugins\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"GeneratedPluginRegistrant\\n\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MainActivity\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlutterActivity\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"private\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"val\")]),t._v(\" CHANNEL \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"samples.flutter.io/battery\"')]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"override\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"fun\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onCreate\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"savedInstanceState\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" Bundle\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onCreate\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"savedInstanceState\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    GeneratedPluginRegistrant\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"registerWith\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MethodChannel\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"flutterView\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" CHANNEL\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setMethodCallHandler\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" call\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" result \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"->\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// TODO\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"接下来，我们添加Kotlin代码，使用Android电池API来获取电池电量，这和原生开发是一样的。\")]),t._v(\" \"),s(\"p\",[t._v(\"首先，添加需要导入的依赖。\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-kotlin extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-kotlin\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" android\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"content\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"Context\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" android\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"content\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"ContextWrapper\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" android\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"content\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"Intent\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" android\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"content\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"IntentFilter\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" android\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"os\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"BatteryManager\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" android\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"os\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"Build\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"VERSION\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" android\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"os\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"Build\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"VERSION_CODES\\n\")])])]),s(\"p\",[t._v(\"然后，将下面的新方法添加到activity类中的，位于onCreate 方法下方：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-kotlin extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-kotlin\"}},[s(\"code\",[t._v(\"  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"private\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"fun\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getBatteryLevel\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" Int \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"val\")]),t._v(\" batteryLevel\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" Int\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"VERSION\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"SDK_INT \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">=\")]),t._v(\" VERSION_CODES\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"LOLLIPOP\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"val\")]),t._v(\" batteryManager \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getSystemService\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"BATTERY_SERVICE\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"as\")]),t._v(\" BatteryManager\\n      batteryLevel \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" batteryManager\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getIntProperty\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BatteryManager\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"BATTERY_PROPERTY_CAPACITY\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"val\")]),t._v(\" intent \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ContextWrapper\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"applicationContext\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"registerReceiver\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"IntentFilter\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Intent\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"ACTION_BATTERY_CHANGED\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      batteryLevel \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" intent\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!!\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getIntExtra\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BatteryManager\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"EXTRA_LEVEL\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" intent\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getIntExtra\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BatteryManager\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"EXTRA_SCALE\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" batteryLevel\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"最后，我们完成之前添加的\"),s(\"code\",[t._v(\"onMethodCall\")]),t._v(\"方法。我们需要处理平台方法名为\"),s(\"code\",[t._v(\"getBatteryLevel\")]),t._v(\"的调用消息，所以我们需要先在call参数判断调用的方法是否为\"),s(\"code\",[t._v(\"getBatteryLevel\")]),t._v(\"。 这个平台方法的实现只需调用我们在前一步中编写的Android代码，并通过result参数返回成功或错误情况的响应信息。如果调用了未定义的API，我们也会通知返回：\\n​\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-kotlin extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-kotlin\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MethodChannel\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"flutterView\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" CHANNEL\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setMethodCallHandler\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" call\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" result \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"->\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"call\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"method \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"getBatteryLevel\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n     \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"val\")]),t._v(\" batteryLevel \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getBatteryLevel\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n     \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"batteryLevel \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n       result\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"success\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"batteryLevel\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n     \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n       result\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"error\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"UNAVAILABLE\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Battery level not available.\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n     \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      result\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"notImplemented\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v('您现在就可以在Android上运行该应用程序。如果您使用的是Android模拟器，则可以通过工具栏中的\"...\"按钮访问Extended Controls面板中的电池电量。')])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/191.167ff3f3.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[191],{852:function(t,a,s){\"use strict\";s.r(a);var n=s(45),e=Object(n.a)({},(function(){var t=this,a=t.$createElement,s=t._self._c||a;return s(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[s(\"h1\",{attrs:{id:\"_12-3-开发flutter插件\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_12-3-开发flutter插件\"}},[t._v(\"#\")]),t._v(\" 12.3 开发Flutter插件\")]),t._v(\" \"),s(\"p\",[t._v(\"下面我们通过一个获取电池电量的插件来介绍一下Flutter插件的开发流程。该插件中我们在Dart中通过\"),s(\"code\",[t._v(\"getBatteryLevel\")]),t._v(\" 调用Android \"),s(\"code\",[t._v(\"BatteryManager\")]),t._v(\" API和iOS \"),s(\"code\",[t._v(\"device.batteryLevel\")]),t._v(\" API。\")]),t._v(\" \"),s(\"h3\",{attrs:{id:\"创建一个新的应用程序项目\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#创建一个新的应用程序项目\"}},[t._v(\"#\")]),t._v(\" 创建一个新的应用程序项目\")]),t._v(\" \"),s(\"p\",[t._v(\"首先创建一个新的应用程序:\")]),t._v(\" \"),s(\"ul\",[s(\"li\",[t._v(\"在终端中运行：\"),s(\"code\",[t._v(\"flutter create batterylevel\")])])]),t._v(\" \"),s(\"p\",[t._v(\"默认情况下，模板支持使用Java编写Android代码，或使用Objective-C编写iOS代码。要使用Kotlin或Swift，请使用-i和/或-a标志:\")]),t._v(\" \"),s(\"ul\",[s(\"li\",[t._v(\"在终端中运行: \"),s(\"code\",[t._v(\"flutter create -i swift -a kotlin batterylevel\")])])]),t._v(\" \"),s(\"h3\",{attrs:{id:\"创建flutter平台客户端\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#创建flutter平台客户端\"}},[t._v(\"#\")]),t._v(\" 创建Flutter平台客户端\")]),t._v(\" \"),s(\"p\",[t._v(\"该应用的\"),s(\"code\",[t._v(\"State\")]),t._v(\"类拥有当前的应用状态。我们需要延长这一点以保持当前的电量\")]),t._v(\" \"),s(\"p\",[t._v(\"首先，我们构建通道。我们使用\"),s(\"code\",[t._v(\"MethodChannel\")]),t._v(\"调用一个方法来返回电池电量。\")]),t._v(\" \"),s(\"p\",[t._v(\"通道的客户端和宿主通过通道构造函数中传递的通道名称进行连接。单个应用中使用的所有通道名称必须是唯一的; 我们建议在通道名称前加一个唯一的“域名前缀”，例如\"),s(\"code\",[t._v(\"samples.flutter.io/battery\")]),t._v(\"。\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'dart:async'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/services.dart'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_MyHomePageState\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"MyHomePage\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" platform \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MethodChannel\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'samples.flutter.io/battery'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// Get battery level.\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"接下来，我们调用通道上的方法，指定通过字符串标识符调用方法\"),s(\"code\",[t._v(\"getBatteryLevel\")]),t._v(\"。 该调用可能失败(平台不支持平台API，例如在模拟器中运行时)，所以我们将invokeMethod调用包装在try-catch语句中。\")]),t._v(\" \"),s(\"p\",[t._v(\"我们使用返回的结果，在\"),s(\"code\",[t._v(\"setState\")]),t._v(\"中来更新用户界面状态\"),s(\"code\",[t._v(\"batteryLevel\")]),t._v(\"。\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// Get battery level.\")]),t._v(\"\\n  String _batteryLevel \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Unknown battery level.'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  Future\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Null\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_getBatteryLevel\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    String batteryLevel\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" int result \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" platform\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"invokeMethod\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'getBatteryLevel'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      batteryLevel \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Battery level at $result % .'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"on\")]),t._v(\" PlatformException \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"catch\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      batteryLevel \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"\\\"Failed to get battery level: '${e.message}'.\\\"\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _batteryLevel \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" batteryLevel\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"最后，我们在build创建包含一个小字体显示电池状态和一个用于刷新值的按钮的用户界面。\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\nWidget \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Material\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Center\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Column\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        mainAxisAlignment\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"spaceEvenly\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        children\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"RaisedButton\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Get Battery Level'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onPressed\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _getBatteryLevel\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_batteryLevel\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"至此Flutter部分的测试代码写好了，接下来我们需要实现Android和iOS平台下的API，由于平台API实现部分篇幅较大，我们将在接下来的两节中，分别介绍Android和iOS端API的实现。\")])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/192.53c8ab5d.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[192],{853:function(t,e,r){\"use strict\";r.r(e);var a=r(45),l=Object(a.a)({},(function(){var t=this,e=t.$createElement,r=t._self._c||e;return r(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[r(\"h1\",{attrs:{id:\"包与插件\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#包与插件\"}},[t._v(\"#\")]),t._v(\" 包与插件\")]),t._v(\" \"),r(\"ul\",[r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/v2/chapter12/develop_package.html\"}},[t._v(\"12.1：开发package\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/v2/chapter12/platform-channel.html\"}},[t._v(\"12.2：平台通道简介\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/v2/chapter12/develop_plugin.html\"}},[t._v(\"12.3：开发Flutter插件\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/v2/chapter12/android_implement.html\"}},[t._v(\"12.4：插件开发：实现Android端API\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/v2/chapter12/ios_implement.html\"}},[t._v(\"12.5：插件开发：实现IOS端API\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/v2/chapter12/texture_platformview.html\"}},[t._v(\"12.6：Texture和PlatformView\")])],1)])])}),[],!1,null,null,null);e.default=l.exports}}]);"
  },
  {
    "path": "docs/assets/js/193.05fa90e3.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[193],{854:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_12-5-插件开发-ios端api实现\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_12-5-插件开发-ios端api实现\"}},[t._v(\"#\")]),t._v(\" 12.5 插件开发：iOS端API实现\")]),t._v(\" \"),a(\"p\",[t._v('本节我们接着之前\"获取电池电量\"插件的示例，来完成iOS端API的实现。以下步骤使用Objective-C，如果您更喜欢Swift，可以直接跳到后面Swift部分。')]),t._v(\" \"),a(\"p\",[t._v(\"首先打开Xcode中Flutter应用程序的iOS部分:\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"启动 Xcode\")]),t._v(\" \"),a(\"li\",[t._v(\"选择 File > Open…\")]),t._v(\" \"),a(\"li\",[t._v(\"定位到您 Flutter app目录, 然后选择里面的 \"),a(\"code\",[t._v(\"iOS\")]),t._v(\"文件夹，点击 OK\")]),t._v(\" \"),a(\"li\",[t._v(\"确保Xcode项目的构建没有错误。\")]),t._v(\" \"),a(\"li\",[t._v(\"选择 Runner > Runner ，打开\"),a(\"code\",[t._v(\"AppDelegate.m\")])])]),t._v(\" \"),a(\"p\",[t._v(\"接下来，在\"),a(\"code\",[t._v(\"application didFinishLaunchingWithOptions:\")]),t._v(\"方法内部创建一个\"),a(\"code\",[t._v(\"FlutterMethodChannel\")]),t._v(\"，并添加一个处理方法。 确保与在Flutter客户端使用的通道名称相同。\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-objectivec extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-objectivec\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token macro property\"}},[a(\"span\",{pre:!0,attrs:{class:\"token directive-hash\"}},[t._v(\"#\")]),a(\"span\",{pre:!0,attrs:{class:\"token directive keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token expression\"}},[a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Flutter\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\"Flutter\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"h\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")])])]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"@implementation\")]),t._v(\" AppDelegate\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BOOL\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"application\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"UIApplication\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"application didFinishLaunchingWithOptions\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"NSDictionary\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"launchOptions \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  FlutterViewController\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" controller \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"FlutterViewController\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"self\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"window\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"rootViewController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  FlutterMethodChannel\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" batteryChannel \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"FlutterMethodChannel\\n                                          methodChannelWithName\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('@\"samples.flutter.io/battery\"')]),t._v(\"\\n                                          binaryMessenger\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"batteryChannel setMethodCallHandler\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"^\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"FlutterMethodCall\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" call\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" FlutterResult result\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// TODO\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),t._v(\" application\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"application didFinishLaunchingWithOptions\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"launchOptions\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"接下来，我们添加Objective-C代码，使用iOS电池API来获取电池电量，这和原生是相同的。\")]),t._v(\" \"),a(\"p\",[t._v(\"在\"),a(\"code\",[t._v(\"AppDelegate\")]),t._v(\"类中添加以下新的方法：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-objectivec extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-objectivec\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"int\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"getBatteryLevel \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  UIDevice\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" device \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" UIDevice\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"currentDevice\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  device\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"batteryMonitoringEnabled \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" YES\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"device\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"batteryState \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" UIDeviceBatteryStateUnknown\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"int\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"device\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"batteryLevel \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"最后，我们完成之前添加的\"),a(\"code\",[t._v(\"setMethodCallHandler\")]),t._v(\"方法。我们需要处理的平台方法名为\"),a(\"code\",[t._v(\"getBatteryLevel\")]),t._v(\"，所以我们在call参数中需要先判断是否为\"),a(\"code\",[t._v(\"getBatteryLevel\")]),t._v(\"。 这个平台方法的实现只需调用我们在前一步中编写的iOS代码，并使用result参数返回成功或错误的响应。如果调用了未定义的API，我们也会通知返回：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-objectivec extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-objectivec\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"batteryChannel setMethodCallHandler\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"^\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"FlutterMethodCall\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" call\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" FlutterResult result\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('@\"getBatteryLevel\"')]),t._v(\" isEqualToString\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"call\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"method\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"int\")]),t._v(\" batteryLevel \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"self\")]),t._v(\" getBatteryLevel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"batteryLevel \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"result\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"FlutterError errorWithCode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('@\"UNAVAILABLE\"')]),t._v(\"\\n                                 message\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('@\"电池信息不可用\"')]),t._v(\"\\n                                 details\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"nil\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"result\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"@\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"batteryLevel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"result\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"FlutterMethodNotImplemented\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"现在可以在iOS上运行该应用程序了，如果使用的是iOS模拟器，请注意，它不支持电池API，因此应用程序将显示“电池信息不可用”。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"使用swift实现ios-api\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#使用swift实现ios-api\"}},[t._v(\"#\")]),t._v(\" 使用Swift实现iOS API\")]),t._v(\" \"),a(\"p\",[t._v(\"以下步骤与上面使用Objective-C相似，首先打开Xcode中Flutter应用程序的iOS部分:\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"启动 Xcode\")]),t._v(\" \"),a(\"li\",[t._v(\"选择 File > Open…\")]),t._v(\" \"),a(\"li\",[t._v(\"定位到您 Flutter app目录, 然后选择里面的 \"),a(\"code\",[t._v(\"ios\")]),t._v(\"文件夹，点击 OK\")]),t._v(\" \"),a(\"li\",[t._v(\"确保Xcode项目的构建没有错误。\")]),t._v(\" \"),a(\"li\",[t._v(\"选择 Runner > Runner ，然后打开\"),a(\"code\",[t._v(\"AppDelegate.swift\")])])]),t._v(\" \"),a(\"p\",[t._v(\"接下来，覆盖application方法并创建一个\"),a(\"code\",[t._v(\"FlutterMethodChannel\")]),t._v(\"绑定通道名称\"),a(\"code\",[t._v(\"samples.flutter.io/battery\")]),t._v(\"：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-swift extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-swift\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token atrule\"}},[t._v(\"@UIApplicationMain\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token atrule\"}},[t._v(\"@objc\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AppDelegate\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"FlutterAppDelegate\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"override\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"func\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"application\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"_\")]),t._v(\" application\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"UIApplication\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    didFinishLaunchingWithOptions launchOptions\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"UIApplicationLaunchOptionsKey\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"Any\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"Bool\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"GeneratedPluginRegistrant\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"register\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"with\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"self\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"let\")]),t._v(\" controller \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"FlutterViewController\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" window\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"rootViewController \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"as\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"FlutterViewController\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"let\")]),t._v(\" batteryChannel \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"FlutterMethodChannel\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"init\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"name\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"samples.flutter.io/battery\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                                   binaryMessenger\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    batteryChannel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setMethodCallHandler\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"call\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"FlutterMethodCall\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" result\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"FlutterResult\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"Void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"in\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// Handle battery messages.\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"application\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"application\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" didFinishLaunchingWithOptions\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" launchOptions\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"接下来，我们添加Swift代码，使用iOS电池API来获取电池电量，这和原生开发是相同的。\")]),t._v(\" \"),a(\"p\",[t._v(\"将以下新方法添加到\"),a(\"code\",[t._v(\"AppDelegate.swift\")]),t._v(\"底部:\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-swift extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-swift\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"private\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"func\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"receiveBatteryLevel\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"result\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"FlutterResult\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"let\")]),t._v(\" device \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"UIDevice\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"current\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  device\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isBatteryMonitoringEnabled \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"device\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"batteryState \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"UIDeviceBatteryState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"unknown\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"result\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"FlutterError\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"init\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"code\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"UNAVAILABLE\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                             message\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"电池信息不可用\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                             details\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token constant\"}},[t._v(\"nil\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"result\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Int\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"device\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"batteryLevel \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"最后，我们完成之前添加的\"),a(\"code\",[t._v(\"setMethodCallHandler\")]),t._v(\"方法。我们需要处理的平台方法名为\"),a(\"code\",[t._v(\"getBatteryLevel\")]),t._v(\"，所以我们在call参数中需要先判断是否为\"),a(\"code\",[t._v(\"getBatteryLevel\")]),t._v(\"。 这个平台方法的实现只需调用我们在前一步中编写的iOS代码，并使用result参数返回成功或错误的响应。如果调用了未定义的API，我们也会通知返回：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-swift extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-swift\"}},[a(\"code\",[t._v(\"batteryChannel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setMethodCallHandler\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"call\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"FlutterMethodCall\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" result\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"FlutterResult\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"Void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"in\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"getBatteryLevel\"')]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" call\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"method\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"receiveBatteryLevel\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"result\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" result\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"result\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token builtin\"}},[t._v(\"FlutterMethodNotImplemented\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"现在可以在iOS上运行应用程序，如果使用的是iOS模拟器，请注意，它不支持电池API，因此应用程序将显示“电池信息不可用”。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/194.df254afb.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[194],{857:function(t,a,s){\"use strict\";s.r(a);var n=s(45),e=Object(n.a)({},(function(){var t=this,a=t.$createElement,s=t._self._c||a;return s(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[s(\"h1\",{attrs:{id:\"_13-4-国际化常见问题\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_13-4-国际化常见问题\"}},[t._v(\"#\")]),t._v(\" 13.4 国际化常见问题\")]),t._v(\" \"),s(\"p\",[t._v(\"本节主要解答一下在国际化中常见的问题。\")]),t._v(\" \"),s(\"h3\",{attrs:{id:\"默认语言区域不对\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#默认语言区域不对\"}},[t._v(\"#\")]),t._v(\" 默认语言区域不对\")]),t._v(\" \"),s(\"p\",[t._v(\"在一些非大陆行货渠道买的一些Android和iOS设备，会出现默认的Locale不是中文简体的情况。这属于正常现象，但是为了防止设备获取的Locale与实际的地区不一致，所有的支持多语言的APP都必须提供一个手动选择语言的入口。\")]),t._v(\" \"),s(\"h3\",{attrs:{id:\"如何对应用标题进行国际化\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#如何对应用标题进行国际化\"}},[t._v(\"#\")]),t._v(\" 如何对应用标题进行国际化\")]),t._v(\" \"),s(\"p\",[s(\"code\",[t._v(\"MaterialApp\")]),t._v(\"有一个\"),s(\"code\",[t._v(\"title\")]),t._v(\"属性，用于指定APP的标题。在Android系统中，APP的标题会出现在任务管理器中。所以也需要对\"),s(\"code\",[t._v(\"title\")]),t._v(\"进行国际化。但是问题是很多国际化的配置都是在\"),s(\"code\",[t._v(\"MaterialApp\")]),t._v(\"上设置的，我们无法在构建\"),s(\"code\",[t._v(\"MaterialApp\")]),t._v(\"时通过\"),s(\"code\",[t._v(\"Localizations.of\")]),t._v(\"来获取本地化资源，如：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MaterialApp\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  title\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" DemoLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"title\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//不能正常工作！\")]),t._v(\"\\n  localizationsDelegates\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 本地化的代理类\")]),t._v(\"\\n    GlobalMaterialLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"delegate\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    GlobalWidgetsLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"delegate\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DemoLocalizationsDelegate\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 设置Delegate\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"上面代码运行后，\"),s(\"code\",[t._v(\"DemoLocalizations.of(context).title\")]),t._v(\" 是会报错的，原因是\"),s(\"code\",[t._v(\"Localizations.of\")]),t._v(\"会从当前的context沿着widget树向顶部查找\"),s(\"code\",[t._v(\"DemoLocalizations\")]),t._v(\"，但是我们在\"),s(\"code\",[t._v(\"MaterialApp\")]),t._v(\"中设置完\"),s(\"code\",[t._v(\"DemoLocalizationsDelegate\")]),t._v(\"后，实际上\"),s(\"code\",[t._v(\"DemoLocalizations\")]),t._v(\"是在当前context的子树中的，所以\"),s(\"code\",[t._v(\"DemoLocalizations.of(context)\")]),t._v(\"会返回null，报错。那么我们该如何处理这种情况呢？其实很简单，我们只需要设置一个\"),s(\"code\",[t._v(\"onGenerateTitle\")]),t._v(\"回调即可：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MaterialApp\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  onGenerateTitle\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 此时context在Localizations的子树中\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" DemoLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"title\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  localizationsDelegates\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DemoLocalizationsDelegate\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"h3\",{attrs:{id:\"如何为英语系的国家指定同一个locale\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#如何为英语系的国家指定同一个locale\"}},[t._v(\"#\")]),t._v(\" 如何为英语系的国家指定同一个locale\")]),t._v(\" \"),s(\"p\",[t._v(\"英语系的国家非常多，如美国、英国、澳大利亚等，这些英语系国家虽然说的都是英语，但也会有一些区别。如果我们的APP只想提供一种英语（如美国英语）供所有英语系国家使用，我们可以在前面介绍的\"),s(\"code\",[t._v(\"localeListResolutionCallback\")]),t._v(\"中来做兼容：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"localeListResolutionCallback\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"List\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Locale\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" locales\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Iterable\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Locale\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" supportedLocales\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 判断当前locale是否为英语系国家，如果是直接返回Locale('en', 'US')     \")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/195.1c40c74b.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[195],{858:function(t,a,l){\"use strict\";l.r(a);var e=l(45),r=Object(e.a)({},(function(){var t=this,a=t.$createElement,l=t._self._c||a;return l(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[l(\"h2\",{attrs:{id:\"本章目录\"}},[l(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本章目录\"}},[t._v(\"#\")]),t._v(\" 本章目录\")]),t._v(\" \"),l(\"ul\",[l(\"li\",[l(\"RouterLink\",{attrs:{to:\"/v2/chapter13/multi_languages_support.html\"}},[t._v(\"13.1：让App支持多语言\")])],1),t._v(\" \"),l(\"li\",[l(\"RouterLink\",{attrs:{to:\"/v2/chapter13/locallization_implement.html\"}},[t._v(\"13.2：实现Localizations\")])],1),t._v(\" \"),l(\"li\",[l(\"RouterLink\",{attrs:{to:\"/v2/chapter13/intl.html\"}},[t._v(\"13.3：使用Intl包\")])],1),t._v(\" \"),l(\"li\",[l(\"RouterLink\",{attrs:{to:\"/v2/chapter13/faq.html\"}},[t._v(\"13.4：国际化常见问题\")])],1)])])}),[],!1,null,null,null);a.default=r.exports}}]);"
  },
  {
    "path": "docs/assets/js/196.3dbd36a7.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[196],{859:function(t,a,s){\"use strict\";s.r(a);var n=s(45),e=Object(n.a)({},(function(){var t=this,a=t.$createElement,s=t._self._c||a;return s(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[s(\"h1\",{attrs:{id:\"使用intl包\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#使用intl包\"}},[t._v(\"#\")]),t._v(\" 使用Intl包\")]),t._v(\" \"),s(\"p\",[t._v(\"使用\"),s(\"a\",{attrs:{href:\"https://pub.dartlang.org/packages/intl\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Intl\"),s(\"OutboundLink\")],1),t._v(\"包我们不仅可以非常轻松的实现国际化，而且也可以将字符串文本分离成单独的文件，方便开发人员和翻译人员分工协作。为了使用\"),s(\"a\",{attrs:{href:\"https://pub.dartlang.org/packages/intl\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Intl\"),s(\"OutboundLink\")],1),t._v(\"包我们需要添加两个依赖：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-yaml extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"dependencies\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"#...省略无关项\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"intl\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ^0.15.7 \\n\"),s(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"dev_dependencies\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"#...省略无关项\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"intl_translation\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ^0.17.2  \\n\")])])]),s(\"p\",[s(\"a\",{attrs:{href:\"https://pub.dartlang.org/packages/intl_translation\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"intl_translation\"),s(\"OutboundLink\")],1),t._v(\" 包主要包含了一些工具，它在开发阶段主要主要的作用是从代码中提取要国际化的字符串到单独的arb文件和根据arb文件生成对应语言的dart代码，而intl包主要是引用和加载intl_translation生成后的dart代码。下面我们将一步步来说明如何使用：\")]),t._v(\" \"),s(\"h3\",{attrs:{id:\"第一步-创建必要目录\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#第一步-创建必要目录\"}},[t._v(\"#\")]),t._v(\" 第一步：创建必要目录\")]),t._v(\" \"),s(\"p\",[t._v(\"首先，在项目根目录下创建一个l10n-arb目录，该目录保存我们接下来通过intl_translation命令生成的arb文件。一个简单的arb文件内容如下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-json extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-json\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"@@last_modified\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"2018-12-10T15:46:20.897228\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"@@locale\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"zh_CH\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"title\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Flutter应用\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"@title\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"description\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Title for the Demo application\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"type\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"text\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"placeholders\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v('我们根据\"@@locale\"字段可以看出这个arb对应的是中文简体的翻译，里面的'),s(\"code\",[t._v(\"title\")]),t._v(\"字段对应的正是我们应用标题的中文简体翻译。\"),s(\"code\",[t._v(\"@title\")]),t._v(\"字段是对\"),s(\"code\",[t._v(\"title\")]),t._v(\"的一些描述信息。\")]),t._v(\" \"),s(\"p\",[t._v(\"接下来，我们在lib目录下创建一个l10n的目录，该目录用于保存从arb文件生成的dart代码文件。\")]),t._v(\" \"),s(\"h3\",{attrs:{id:\"第二步-实现localizations和delegate类\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#第二步-实现localizations和delegate类\"}},[t._v(\"#\")]),t._v(\" 第二步：实现Localizations和Delegate类\")]),t._v(\" \"),s(\"p\",[t._v(\"和上一节中的步骤类似，我们仍然要实现\"),s(\"code\",[t._v(\"Localizations\")]),t._v(\"和Delegate类，不同的是，现在我们在实现时要使用intl包的一些方法（有些是动态生成的）。\")]),t._v(\" \"),s(\"p\",[t._v(\"下面我们在\"),s(\"code\",[t._v(\"lib/l10n\")]),t._v(\"目录下新建一个“localization_intl.dart”的文件，文件内容如下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:intl/intl.dart'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'messages_all.dart'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//1\")]),t._v(\"\\n\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"DemoLocalizations\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" Future\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"DemoLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"load\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Locale locale\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String name \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" locale\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"countryCode\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isEmpty \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" locale\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"languageCode \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" locale\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String localeName \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Intl\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"canonicalizedLocale\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"name\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//2\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initializeMessages\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"localeName\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"b\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      Intl\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"defaultLocale \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" localeName\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"DemoLocalizations\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" DemoLocalizations \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" Localizations\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"of\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"DemoLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" DemoLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  String \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" title \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" Intl\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"message\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Flutter APP'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      name\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'title'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      desc\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Title for the Demo application'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//Locale代理类\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"DemoLocalizationsDelegate\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"LocalizationsDelegate\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"DemoLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DemoLocalizationsDelegate\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//是否支持某个Local\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  bool \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"isSupported\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Locale locale\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'en'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'zh'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"contains\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"locale\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"languageCode\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// Flutter会调用此类加载相应的Locale资源类\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Future\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"DemoLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"load\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Locale locale\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//3\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\"  DemoLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"load\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"locale\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 当Localizations Widget重新build时，是否调用load重新加载Locale资源.\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  bool \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"shouldReload\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"DemoLocalizationsDelegate old\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"注意：\")]),t._v(\" \"),s(\"ul\",[s(\"li\",[t._v('注释1的\"messages_all.dart\"文件是通过'),s(\"a\",{attrs:{href:\"https://pub.dartlang.org/packages/intl_translation\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"intl_translation\"),s(\"OutboundLink\")],1),t._v(\"工具从arb文件生成的代码，所以在第一次运行生成命令之前，此文件不存在。注释2处的\"),s(\"code\",[t._v(\"initializeMessages()\")]),t._v('方法和\"messages_all.dart\"文件一样，是同时生成的。')]),t._v(\" \"),s(\"li\",[t._v(\"注释3处和上一节示例代码不同，这里我们直接调用\"),s(\"code\",[t._v(\"DemoLocalizations.load()\")]),t._v(\"即可。\")])]),t._v(\" \"),s(\"h3\",{attrs:{id:\"第三步-添加需要国际化的属性\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#第三步-添加需要国际化的属性\"}},[t._v(\"#\")]),t._v(\" 第三步：添加需要国际化的属性\")]),t._v(\" \"),s(\"p\",[t._v(\"现在我们可以在DemoLocalizations类中添加需要国际化的属性或方法，如上面示例代码中的\"),s(\"code\",[t._v(\"title\")]),t._v(\"属性，这时我们就要用到Intl库提供的一些方法，这些方法可以帮我们轻松实现不同语言的一些语法特性，如复数语境，举个例子，比如我们有一个电子邮件列表页，我们需要在顶部显示未读邮件的数量，在未读数量不同事，我们展示的文本可能会不同：\")]),t._v(\" \"),s(\"table\",[s(\"thead\",[s(\"tr\",[s(\"th\",[t._v(\"未读邮件数\")]),t._v(\" \"),s(\"th\",[t._v(\"提示语\")])])]),t._v(\" \"),s(\"tbody\",[s(\"tr\",[s(\"td\",[t._v(\"0\")]),t._v(\" \"),s(\"td\",[t._v(\"There are no emails left\")])]),t._v(\" \"),s(\"tr\",[s(\"td\",[t._v(\"1\")]),t._v(\" \"),s(\"td\",[t._v(\"There is 1 email left\")])]),t._v(\" \"),s(\"tr\",[s(\"td\",[t._v(\"n(n>1)\")]),t._v(\" \"),s(\"td\",[t._v(\"There are n emails left\")])])])]),t._v(\" \"),s(\"p\",[t._v(\"我们可以通过\"),s(\"code\",[t._v(\"Intl.plural(...)\")]),t._v(\"来实现：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"remainingEmailsMessage\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int howMany\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Intl\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"plural\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"howMany\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    zero\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'There are no emails left'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    one\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'There is $howMany email left'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    other\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'There are $howMany emails left'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    name\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"remainingEmailsMessage\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    args\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"howMany\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    desc\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"How many emails remain after archiving.\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    examples\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'howMany'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"42\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'userName'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Fred'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"可以看到通过\"),s(\"code\",[t._v(\"Intl.plural\")]),t._v(\"方法可以在\"),s(\"code\",[t._v(\"howMany\")]),t._v(\"值不同时输出不同的提示信息。\")]),t._v(\" \"),s(\"p\",[s(\"a\",{attrs:{href:\"https://pub.dartlang.org/packages/intl\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Intl\"),s(\"OutboundLink\")],1),t._v(\"包还有一些其他的方法，读者可以自行查看其文档，本书不在赘述。\")]),t._v(\" \"),s(\"h3\",{attrs:{id:\"第四步-生成arb文件\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#第四步-生成arb文件\"}},[t._v(\"#\")]),t._v(\" 第四步：生成arb文件\")]),t._v(\" \"),s(\"p\",[t._v(\"现在我们可以通\"),s(\"a\",{attrs:{href:\"https://pub.dartlang.org/packages/intl_translation\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"intl_translation\"),s(\"OutboundLink\")],1),t._v(\"包的工具来提取代码中的字符串到一个arb文件，运行如下命名：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-shell extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[s(\"code\",[t._v(\"flutter pub pub run intl_translation:extract_to_arb --output-dir\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"l10n-arb \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"\\\\\")]),t._v(\" lib/l10n/localization_intl.dart\\n\")])])]),s(\"p\",[t._v(\"运行此命令后，会将我们之前通过Intl API标识的属性和字符串提取到“l10n-arb/intl_messages.arb”文件中，我们看看其内容：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-json extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-json\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"@@last_modified\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"2018-12-10T17:37:28.505088\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"title\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Flutter APP\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"@title\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"description\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Title for the Demo application\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"type\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"text\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"placeholders\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"remainingEmailsMessage\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"{howMany,plural, =0{There are no emails left}=1{There is {howMany} email left}other{There are {howMany} emails left}}\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"@remainingEmailsMessage\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"description\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"How many emails remain after archiving.\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"type\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"text\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"placeholders\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"howMany\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"example\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"42\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v('这个是默认的Locale资源文件，如果我们现在要支持中文简体，只需要在该文件同级目录创建一个\"intl_zh_CN.arb\"文件，然后将\"intl_messages.arb\"的内容拷贝到\"intl_zh_CN.arb\"文件，接下来将英文翻译为中文即可，翻译后的\"intl_zh_CN.arb\"文件内容如下：')]),t._v(\" \"),s(\"div\",{staticClass:\"language-json extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-json\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"@@last_modified\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"2018-12-10T15:46:20.897228\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"@@locale\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"zh_CN\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"title\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Flutter应用\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"@title\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"description\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Title for the Demo application\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"type\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"text\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"placeholders\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"remainingEmailsMessage\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"{howMany,plural, =0{没有未读邮件}=1{有{howMany}封未读邮件}other{有{howMany}封未读邮件}}\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"@remainingEmailsMessage\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"description\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"How many emails remain after archiving.\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"type\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"text\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"placeholders\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"howMany\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"example\"')]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"42\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"我们必须要翻译\"),s(\"code\",[t._v(\"title\")]),t._v(\"和\"),s(\"code\",[t._v(\"remainingEmailsMessage\")]),t._v(\"字段，\"),s(\"code\",[t._v(\"description\")]),t._v(\"是该字段的说明，通常给翻译人员看，代码中不会用到。\")]),t._v(\" \"),s(\"p\",[t._v(\"有两点需要说明：\")]),t._v(\" \"),s(\"ol\",[s(\"li\",[t._v(\"如果某个特定的arb中缺失某个属性，那么应用将会加载默认的arb文件(intl_messages.arb)中的相应属性，这是Intl的托底策略。\")]),t._v(\" \"),s(\"li\",[t._v(\"每次运行提取命令时，intl_messages.arb都会根据代码重新生成，但其他arb文件不会，所以当要添加新的字段或方法时，其他arb文件是增量的，不用担心会覆盖。\")]),t._v(\" \"),s(\"li\",[t._v(\"arb文件是标准的，其格式规范可以自行了解。通常会将arb文件交给翻译人员，当他们完成翻译后，我们再通过下面的步骤根据arb文件生成最终的dart代码。\")])]),t._v(\" \"),s(\"h3\",{attrs:{id:\"第五步-生成dart代码\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#第五步-生成dart代码\"}},[t._v(\"#\")]),t._v(\" 第五步：生成dart代码\")]),t._v(\" \"),s(\"p\",[t._v(\"最后一步就是根据arb生成dart文件：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-shell extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[s(\"code\",[t._v(\"flutter pub pub run intl_translation:generate_from_arb --output-dir\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"lib/l10n --no-use-deferred-loading lib/l10n/localization_intl.dart l10n-arb/intl_*.arb\\n\")])])]),s(\"p\",[t._v('这句命令在首次运行时会在\"lib/l10n\"目录下生成多个文件，对应多种Locale，这些代码便是最终要使用的dart代码。')]),t._v(\" \"),s(\"h3\",{attrs:{id:\"总结\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),s(\"p\",[t._v(\"至此，我们将使用\"),s(\"a\",{attrs:{href:\"https://pub.dartlang.org/packages/intl\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Intl\"),s(\"OutboundLink\")],1),t._v(\"包对APP进行国际化的流程介绍完了，我们可以发现，其中第一步和第二步只在第一次需要，而我们开发时的主要的工作都是在第三步。由于最后两步在第三步完成后每次也都需要，所以我们可以将最后两步放在一个shell脚本里，当我们完成第三步或完成arb文件翻译后只需要分别执行该脚本即可。我们在根目录下创建一个intl.sh的脚本，内容为：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-shell extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[s(\"code\",[t._v(\"flutter pub pub run intl_translation:extract_to_arb --output-dir\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"l10n-arb lib/l10n/localization_intl.dart\\nflutter pub pub run intl_translation:generate_from_arb --output-dir\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"lib/l10n --no-use-deferred-loading lib/l10n/localization_intl.dart l10n-arb/intl_*.arb\\n\")])])]),s(\"p\",[t._v(\"然后授予执行权限：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-shell extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"chmod\")]),t._v(\" +x intl.sh\\n\")])])]),s(\"p\",[t._v(\"执行intl.sh\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-shell extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[s(\"code\",[t._v(\"./intl.sh\\n\")])])])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/197.7fd8856f.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[197],{860:function(t,a,s){\"use strict\";s.r(a);var n=s(45),e=Object(n.a)({},(function(){var t=this,a=t.$createElement,s=t._self._c||a;return s(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[s(\"h1\",{attrs:{id:\"_13-2-实现localizations\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_13-2-实现localizations\"}},[t._v(\"#\")]),t._v(\" 13.2 实现Localizations\")]),t._v(\" \"),s(\"p\",[t._v(\"前面讲了Material组件库如何支持国际化，本节我们将介绍一下我们自己的UI中如何支持多语言。根据上节所述，我们需要实现两个类：一个\"),s(\"code\",[t._v(\"Delegate\")]),t._v(\"类一个\"),s(\"code\",[t._v(\"Localizations\")]),t._v(\"类，下面我们通过一个实例说明。\")]),t._v(\" \"),s(\"h3\",{attrs:{id:\"实现localizations类\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#实现localizations类\"}},[t._v(\"#\")]),t._v(\" 实现Localizations类\")]),t._v(\" \"),s(\"p\",[t._v(\"我们已经知道\"),s(\"code\",[t._v(\"Localizations\")]),t._v(\"类中主要实现提供了本地化值，如文本：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//Locale资源类\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"DemoLocalizations\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DemoLocalizations\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isZh\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//是否为中文\")]),t._v(\"\\n  bool isZh \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//为了使用方便，我们定义一个静态方法\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" DemoLocalizations \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" Localizations\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"of\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"DemoLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" DemoLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//Locale相关值，title为应用标题\")]),t._v(\"\\n  String \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" title \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" isZh \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Flutter应用\"')]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Flutter APP\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//... 其它的值  \")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[s(\"code\",[t._v(\"DemoLocalizations\")]),t._v(\"中会根据当前的语言来返回不同的文本，如\"),s(\"code\",[t._v(\"title\")]),t._v(\"，我们可以将所有需要支持多语言的文本都在此类中定义。\"),s(\"code\",[t._v(\"DemoLocalizations\")]),t._v(\"的实例将会在Delegate类的\"),s(\"code\",[t._v(\"load\")]),t._v(\"方法中创建。\")]),t._v(\" \"),s(\"h3\",{attrs:{id:\"实现delegate类\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#实现delegate类\"}},[t._v(\"#\")]),t._v(\" 实现Delegate类\")]),t._v(\" \"),s(\"p\",[t._v(\"Delegate类的职责是在Locale改变时加载新的Locale资源，所以它有一个\"),s(\"code\",[t._v(\"load\")]),t._v(\"方法，Delegate类需要继承自\"),s(\"code\",[t._v(\"LocalizationsDelegate\")]),t._v(\"类，实现相应的接口，示例如下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//Locale代理类\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"DemoLocalizationsDelegate\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"LocalizationsDelegate\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"DemoLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DemoLocalizationsDelegate\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//是否支持某个Local\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  bool \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"isSupported\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Locale locale\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'en'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'zh'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"contains\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"locale\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"languageCode\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// Flutter会调用此类加载相应的Locale资源类\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Future\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"DemoLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"load\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Locale locale\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$locale\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" SynchronousFuture\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"DemoLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DemoLocalizations\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"locale\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"languageCode \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"zh\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  bool \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"shouldReload\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"DemoLocalizationsDelegate old\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[s(\"code\",[t._v(\"shouldReload\")]),t._v(\"的返回值决定当Localizations组件重新build时，是否调用\"),s(\"code\",[t._v(\"load\")]),t._v(\"方法重新加载Locale资源。一般情况下，Locale资源只应该在Locale切换时加载一次，不需要每次在\"),s(\"code\",[t._v(\"Localizations\")]),t._v(\"重新build时都加载，所以返回\"),s(\"code\",[t._v(\"false\")]),t._v(\"即可。可能有些人会担心返回\"),s(\"code\",[t._v(\"false\")]),t._v(\"的话在APP启动后用户再改变系统语言时\"),s(\"code\",[t._v(\"load\")]),t._v(\"方法将不会被调用，所以Locale资源将不会被加载。事实上，每当Locale改变时Flutter都会再调用\"),s(\"code\",[t._v(\"load\")]),t._v(\"方法加载新的Locale，无论\"),s(\"code\",[t._v(\"shouldReload\")]),t._v(\"返回\"),s(\"code\",[t._v(\"true\")]),t._v(\"还是\"),s(\"code\",[t._v(\"false\")]),t._v(\"。\")]),t._v(\" \"),s(\"h3\",{attrs:{id:\"最后一步-添加多语言支持\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#最后一步-添加多语言支持\"}},[t._v(\"#\")]),t._v(\" 最后一步：添加多语言支持\")]),t._v(\" \"),s(\"p\",[t._v(\"和上一节中介绍的相同，我们现在需要先注册\"),s(\"code\",[t._v(\"DemoLocalizationsDelegate\")]),t._v(\"类，然后再通过\"),s(\"code\",[t._v(\"DemoLocalizations.of(context)\")]),t._v(\"来动态获取当前Locale文本。\")]),t._v(\" \"),s(\"p\",[t._v(\"只需要在MaterialApp或WidgetsApp的\"),s(\"code\",[t._v(\"localizationsDelegates\")]),t._v(\"列表中添加我们的Delegate实例即可完成注册：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"localizationsDelegates\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 本地化的代理类\")]),t._v(\"\\n GlobalMaterialLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"delegate\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n GlobalWidgetsLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"delegate\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 注册我们的Delegate\")]),t._v(\"\\n \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DemoLocalizationsDelegate\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"接下来我们可以在Widget中使用Locale值：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  appBar\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//使用Locale title  \")]),t._v(\"\\n    title\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"DemoLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"title\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n ） \\n\")])])]),s(\"p\",[t._v(\"这样，当在美国英语和中文简体之间切换系统语言时，APP的标题将会分别为“Flutter APP”和“Flutter应用”。\")]),t._v(\" \"),s(\"h3\",{attrs:{id:\"总结\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),s(\"p\",[t._v(\"本节我们通过一个简单的示例说明了Flutter应用国际化的基本过程及原理。但是上面的实例还有一个严重的不足就是我们需要在DemoLocalizations类中获取\"),s(\"code\",[t._v(\"title\")]),t._v(\"时手动的判断当前语言Locale，然后返回合适的文本。试想一下，当我们要支持的语言不是两种而是8种甚至20几种时，如果为每个文本属性都要分别去判断到底是哪种Locale从而获取相应语言的文本将会是一件非常复杂的事。还有，通常情况下翻译人员并不是开发人员，能不能像i18n或l10n标准那样可以将翻译单独保存为一个arb文件交由翻译人员去翻译，翻译好之后开发人员再通过工具将arb文件转为代码。答案是肯定的！我们将在下一节介绍如何通过Dart intl包来实现这些。\")])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/198.2a531f59.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[198],{863:function(t,a,n){\"use strict\";n.r(a);var s=n(45),e=Object(s.a)({},(function(){var t=this,a=t.$createElement,n=t._self._c||a;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_14-4-flutter运行机制-从启动到显示\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_14-4-flutter运行机制-从启动到显示\"}},[t._v(\"#\")]),t._v(\" 14.4 Flutter运行机制-从启动到显示\")]),t._v(\" \"),n(\"p\",[t._v(\"本节我们主要介绍一下Flutter从启动到显示的过程。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"启动\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#启动\"}},[t._v(\"#\")]),t._v(\" 启动\")]),t._v(\" \"),n(\"p\",[t._v('Flutter的入口在\"lib/main.dart\"的'),n(\"code\",[t._v(\"main()\")]),t._v(\"函数中，它是Dart应用程序的起点。在Flutter应用中，\"),n(\"code\",[t._v(\"main()\")]),t._v(\"函数最简单的实现如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"main\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"runApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"可以看\"),n(\"code\",[t._v(\"main()\")]),t._v(\"函数只调用了一个\"),n(\"code\",[t._v(\"runApp()\")]),t._v(\"方法，我们看看\"),n(\"code\",[t._v(\"runApp()\")]),t._v(\"方法中都做了什么：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"runApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Widget app\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  WidgetsFlutterBinding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ensureInitialized\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"attachRootWidget\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"app\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"scheduleWarmUpFrame\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"参数\"),n(\"code\",[t._v(\"app\")]),t._v(\"是一个widget，它是Flutter应用启动后要展示的第一个Widget。而\"),n(\"code\",[t._v(\"WidgetsFlutterBinding\")]),t._v(\"正是绑定widget 框架和Flutter engine的桥梁，定义如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"WidgetsFlutterBinding\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"BindingBase\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"with\")]),t._v(\" GestureBinding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" ServicesBinding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" SchedulerBinding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" PaintingBinding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" SemanticsBinding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" RendererBinding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" WidgetsBinding \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" WidgetsBinding \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ensureInitialized\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"WidgetsBinding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"instance \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"WidgetsFlutterBinding\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" WidgetsBinding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"instance\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"可以看到\"),n(\"code\",[t._v(\"WidgetsFlutterBinding\")]),t._v(\"继承自\"),n(\"code\",[t._v(\"BindingBase\")]),t._v(\" 并混入了很多\"),n(\"code\",[t._v(\"Binding\")]),t._v(\"，在介绍这些\"),n(\"code\",[t._v(\"Binding\")]),t._v(\"之前我们先介绍一下\"),n(\"code\",[t._v(\"Window\")]),t._v(\"，下面是\"),n(\"code\",[t._v(\"Window\")]),t._v(\"的官方解释：\")]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[t._v(\"The most basic interface to the host operating system's user interface.\")])]),t._v(\" \"),n(\"p\",[t._v(\"很明显，\"),n(\"code\",[t._v(\"Window\")]),t._v(\"正是Flutter Framework连接宿主操作系统的接口。我们看一下\"),n(\"code\",[t._v(\"Window\")]),t._v(\"类的部分定义：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Window\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 当前设备的DPI，即一个逻辑像素显示多少物理像素，数字越大，显示效果就越精细保真。\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// DPI是设备屏幕的固件属性，如Nexus 6的屏幕DPI为3.5 \")]),t._v(\"\\n  double \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" devicePixelRatio \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _devicePixelRatio\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// Flutter UI绘制区域的大小\")]),t._v(\"\\n  Size \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" physicalSize \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _physicalSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 当前系统默认的语言Locale\")]),t._v(\"\\n  Locale \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" locale\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 当前系统字体缩放比例。  \")]),t._v(\"\\n  double \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" textScaleFactor \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _textScaleFactor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"  \\n    \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 当绘制区域大小改变回调\")]),t._v(\"\\n  VoidCallback \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" onMetricsChanged \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _onMetricsChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"  \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// Locale发生变化回调\")]),t._v(\"\\n  VoidCallback \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" onLocaleChanged \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _onLocaleChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 系统字体缩放变化回调\")]),t._v(\"\\n  VoidCallback \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" onTextScaleFactorChanged \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _onTextScaleFactorChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 绘制前回调，一般会受显示器的垂直同步信号VSync驱动，当屏幕刷新时就会被调用\")]),t._v(\"\\n  FrameCallback \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" onBeginFrame \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _onBeginFrame\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 绘制回调  \")]),t._v(\"\\n  VoidCallback \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" onDrawFrame \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _onDrawFrame\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 点击或指针事件回调\")]),t._v(\"\\n  PointerDataPacketCallback \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" onPointerDataPacket \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _onPointerDataPacket\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 调度Frame，该方法执行后，onBeginFrame和onDrawFrame将紧接着会在合适时机被调用，\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 此方法会直接调用Flutter engine的Window_scheduleFrame方法\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"scheduleFrame\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" native \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Window_scheduleFrame'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 更新应用在GPU上的渲染,此方法会直接调用Flutter engine的Window_render方法\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"render\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Scene scene\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" native \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Window_render'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 发送平台消息\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"sendPlatformMessage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String name\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                           ByteData data\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                           PlatformMessageResponseCallback callback\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 平台通道消息处理回调  \")]),t._v(\"\\n  PlatformMessageCallback \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" onPlatformMessage \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _onPlatformMessage\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//其它属性及回调\")]),t._v(\"\\n   \\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"可以看到\"),n(\"code\",[t._v(\"Window\")]),t._v(\"类包含了当前设备和系统的一些信息以及Flutter Engine的一些回调。现在我们再回来看看\"),n(\"code\",[t._v(\"WidgetsFlutterBinding\")]),t._v(\"混入的各种Binding。通过查看这些 Binding的源码，我们可以发现这些Binding中基本都是监听并处理\"),n(\"code\",[t._v(\"Window\")]),t._v(\"对象的一些事件，然后将这些事件按照Framework的模型包装、抽象然后分发。可以看到\"),n(\"code\",[t._v(\"WidgetsFlutterBinding\")]),t._v(\"正是粘连Flutter engine与上层Framework的“胶水”。\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"GestureBinding\")]),t._v(\"：提供了\"),n(\"code\",[t._v(\"window.onPointerDataPacket\")]),t._v(\" 回调，绑定Framework手势子系统，是Framework事件模型与底层事件的绑定入口。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"ServicesBinding\")]),t._v(\"：提供了\"),n(\"code\",[t._v(\"window.onPlatformMessage\")]),t._v(\" 回调， 用于绑定平台消息通道（message channel），主要处理原生和Flutter通信。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"SchedulerBinding\")]),t._v(\"：提供了\"),n(\"code\",[t._v(\"window.onBeginFrame\")]),t._v(\"和\"),n(\"code\",[t._v(\"window.onDrawFrame\")]),t._v(\"回调，监听刷新事件，绑定Framework绘制调度子系统。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"PaintingBinding\")]),t._v(\"：绑定绘制库，主要用于处理图片缓存。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"SemanticsBinding\")]),t._v(\"：语义化层与Flutter engine的桥梁，主要是辅助功能的底层支持。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"RendererBinding\")]),t._v(\": 提供了\"),n(\"code\",[t._v(\"window.onMetricsChanged\")]),t._v(\" 、\"),n(\"code\",[t._v(\"window.onTextScaleFactorChanged\")]),t._v(\" 等回调。它是渲染树与Flutter engine的桥梁。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"WidgetsBinding\")]),t._v(\"：提供了\"),n(\"code\",[t._v(\"window.onLocaleChanged\")]),t._v(\"、\"),n(\"code\",[t._v(\"onBuildScheduled\")]),t._v(\" 等回调。它是Flutter widget层与engine的桥梁。\")])]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"WidgetsFlutterBinding.ensureInitialized()\")]),t._v(\"负责初始化一个\"),n(\"code\",[t._v(\"WidgetsBinding\")]),t._v(\"的全局单例，紧接着会调用\"),n(\"code\",[t._v(\"WidgetsBinding\")]),t._v(\"的\"),n(\"code\",[t._v(\"attachRootWidget\")]),t._v(\"方法，该方法负责将根Widget添加到\"),n(\"code\",[t._v(\"RenderView\")]),t._v(\"上，代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"attachRootWidget\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Widget rootWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  _renderViewElement \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" RenderObjectToWidgetAdapter\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"RenderBox\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    container\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" renderView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n    debugShortDescription\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'[root]'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" rootWidget\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"attachToRenderTree\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"buildOwner\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" renderViewElement\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"注意，代码中的有\"),n(\"code\",[t._v(\"renderView\")]),t._v(\"和\"),n(\"code\",[t._v(\"renderViewElement\")]),t._v(\"两个变量，\"),n(\"code\",[t._v(\"renderView\")]),t._v(\"是一个\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"，它是渲染树的根，而\"),n(\"code\",[t._v(\"renderViewElement\")]),t._v(\"是\"),n(\"code\",[t._v(\"renderView\")]),t._v(\"对应的\"),n(\"code\",[t._v(\"Element\")]),t._v(\"对象，可见该方法主要完成了根widget到根 \"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"再到根\"),n(\"code\",[t._v(\"Element\")]),t._v(\"的整个关联过程。我们看看\"),n(\"code\",[t._v(\"attachToRenderTree\")]),t._v(\"的源码实现：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"RenderObjectToWidgetElement\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"attachToRenderTree\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildOwner owner\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"RenderObjectToWidgetElement\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" element\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"element \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    owner\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"lockState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      element \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createElement\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"element \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      element\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"assignOwner\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"owner\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    owner\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"buildScope\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"element\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      element\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"mount\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    element\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"_newWidget \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    element\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"markNeedsBuild\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" element\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"该方法负责创建根element，即\"),n(\"code\",[t._v(\"RenderObjectToWidgetElement\")]),t._v(\"，并且将element与widget 进行关联，即创建出 widget树对应的element树。如果element 已经创建过了，则将根element 中关联的widget 设为新的，由此可以看出element 只会创建一次，后面会进行复用。那么\"),n(\"code\",[t._v(\"BuildOwner\")]),t._v(\"是什么呢？其实他就是widget framework的管理类，它跟踪哪些widget需要重新构建。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"渲染\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#渲染\"}},[t._v(\"#\")]),t._v(\" 渲染\")]),t._v(\" \"),n(\"p\",[t._v(\"回到\"),n(\"code\",[t._v(\"runApp\")]),t._v(\"的实现中，当调用完\"),n(\"code\",[t._v(\"attachRootWidget\")]),t._v(\"后，最后一行会调用 \"),n(\"code\",[t._v(\"WidgetsFlutterBinding\")]),t._v(\" 实例的 \"),n(\"code\",[t._v(\"scheduleWarmUpFrame()\")]),t._v(\" 方法，该方法的实现在\"),n(\"code\",[t._v(\"SchedulerBinding\")]),t._v(' 中，它被调用后会立即进行一次绘制（而不是等待\"vsync\" 信号），在此次绘制结束前，该方法会锁定事件分发，也就是说在本次绘制结束完成之前Flutter将不会响应各种事件，这可以保证在绘制过程中不会再触发新的重绘。下面是'),n(\"code\",[t._v(\"scheduleWarmUpFrame()\")]),t._v(\" 方法的部分实现(省略了无关代码)：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"scheduleWarmUpFrame\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  Timer\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"run\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"handleBeginFrame\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  Timer\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"run\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"handleDrawFrame\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"  \\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"resetEpoch\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 锁定事件\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"lockEvents\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" endOfFrame\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    Timeline\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"finishSync\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"可以看到该方法中主要调用了\"),n(\"code\",[t._v(\"handleBeginFrame()\")]),t._v(\" 和 \"),n(\"code\",[t._v(\"handleDrawFrame()\")]),t._v(\" 两个方法，在看这两个方法之前我们首先了解一下Frame 和 FrameCallback 的概念：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"p\",[t._v('Frame: 一次绘制过程，我们称其为一帧。Flutter engine受显示器垂直同步信号\"VSync\"的驱使不断的触发绘制。我们之前说的Flutter可以实现60fps（Frame Per-Second），就是指一秒钟可以触发60次重绘，FPS值越大，界面就越流畅。')])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"FrameCallback：\"),n(\"code\",[t._v(\"SchedulerBinding\")]),t._v(\" 类中有三个FrameCallback回调队列， 在一次绘制过程中，这三个回调队列会放在不同时机被执行：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"code\",[t._v(\"transientCallbacks\")]),t._v(\"：用于存放一些临时回调，一般存放动画回调。可以通过\"),n(\"code\",[t._v(\"SchedulerBinding.instance.scheduleFrameCallback\")]),t._v(\" 添加回调。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"persistentCallbacks\")]),t._v(\"：用于存放一些持久的回调，不能在此类回调中再请求新的绘制帧，持久回调一经注册则不能移除。\"),n(\"code\",[t._v(\"SchedulerBinding.instance.addPersitentFrameCallback()\")]),t._v(\"，这个回调中处理了布局与绘制工作。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"postFrameCallbacks\")]),t._v(\"：在Frame结束时只会被调用一次，调用后会被系统移除，可由 \"),n(\"code\",[t._v(\"SchedulerBinding.instance.addPostFrameCallback()\")]),t._v(\" 注册，注意，不要在此类回调中再触发新的Frame，这可以会导致循环刷新。\")])])])]),t._v(\" \"),n(\"p\",[t._v(\"现在请读者自行查看\"),n(\"code\",[t._v(\"handleBeginFrame()\")]),t._v(\" 和 \"),n(\"code\",[t._v(\"handleDrawFrame()\")]),t._v(\" 两个方法的源码，可以发现前者主要是执行了\"),n(\"code\",[t._v(\"transientCallbacks\")]),t._v(\"队列，而后者执行了 \"),n(\"code\",[t._v(\"persistentCallbacks\")]),t._v(\" 和 \"),n(\"code\",[t._v(\"postFrameCallbacks\")]),t._v(\" 队列。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"绘制\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#绘制\"}},[t._v(\"#\")]),t._v(\" 绘制\")]),t._v(\" \"),n(\"p\",[t._v(\"渲染和绘制逻辑在\"),n(\"code\",[t._v(\"RendererBinding\")]),t._v(\"中实现，查看其源码，发现在其\"),n(\"code\",[t._v(\"initInstances()\")]),t._v(\"方法中有如下代码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initInstances\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n      \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//监听Window对象的事件  \")]),t._v(\"\\n  ui\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"window\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onMetricsChanged \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" handleMetricsChanged\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onTextScaleFactorChanged \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" handleTextScaleFactorChanged\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onSemanticsEnabledChanged \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _handleSemanticsEnabledChanged\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onSemanticsAction \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _handleSemanticsAction\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n   \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//添加PersistentFrameCallback    \")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addPersistentFrameCallback\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_handlePersistentFrameCallback\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们看最后一行，通过\"),n(\"code\",[t._v(\"addPersistentFrameCallback\")]),t._v(\" 向\"),n(\"code\",[t._v(\"persistentCallbacks\")]),t._v(\"队列添加了一个回调 \"),n(\"code\",[t._v(\"_handlePersistentFrameCallback\")]),t._v(\":\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_handlePersistentFrameCallback\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Duration timeStamp\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"drawFrame\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"该方法直接调用了\"),n(\"code\",[t._v(\"RendererBinding\")]),t._v(\"的\"),n(\"code\",[t._v(\"drawFrame()\")]),t._v(\"方法：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"drawFrame\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"renderView \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  pipelineOwner\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"flushLayout\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//布局\")]),t._v(\"\\n  pipelineOwner\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"flushCompositingBits\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//重绘之前的预处理操作，检查RenderObject是否需要重绘\")]),t._v(\"\\n  pipelineOwner\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"flushPaint\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 重绘\")]),t._v(\"\\n  renderView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"compositeFrame\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 将需要绘制的比特数据发给GPU\")]),t._v(\"\\n  pipelineOwner\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"flushSemantics\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// this also sends the semantics to the OS.\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们看看这些方法分别做了什么：\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"flushlayout\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#flushlayout\"}},[t._v(\"#\")]),t._v(\" flushLayout()\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"flushLayout\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"while\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_nodesNeedingLayout\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isNotEmpty\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" List\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"RenderObject\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" dirtyNodes \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _nodesNeedingLayout\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      _nodesNeedingLayout \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"RenderObject\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"for\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"RenderObject node \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"in\")]),t._v(\" \\n           dirtyNodes\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"sort\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"RenderObject a\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" RenderObject b\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" a\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"depth \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" b\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"depth\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"node\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"_needsLayout \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\" node\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"owner \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n          node\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_layoutWithoutResize\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"源码很简单，该方法主要任务是更新了所有被标记为“dirty”的\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"的布局信息。主要的动作发生在\"),n(\"code\",[t._v(\"node._layoutWithoutResize()\")]),t._v(\"方法中，该方法中会调用\"),n(\"code\",[t._v(\"performLayout()\")]),t._v(\"进行重新布局。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"flushcompositingbits\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#flushcompositingbits\"}},[t._v(\"#\")]),t._v(\" flushCompositingBits()\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"flushCompositingBits\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  _nodesNeedingCompositingBitsUpdate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"sort\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"RenderObject a\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" RenderObject b\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" a\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"depth \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" b\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"depth\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"for\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"RenderObject node \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"in\")]),t._v(\" _nodesNeedingCompositingBitsUpdate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"node\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"_needsCompositingBitsUpdate \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\" node\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"owner \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      node\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_updateCompositingBits\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//更新RenderObject.needsCompositing属性值\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  _nodesNeedingCompositingBitsUpdate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"clear\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"检查\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"是否需要重绘，然后更新\"),n(\"code\",[t._v(\"RenderObject.needsCompositing\")]),t._v(\"属性，如果该属性值被标记为\"),n(\"code\",[t._v(\"true\")]),t._v(\"则需要重绘。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"flushpaint\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#flushpaint\"}},[t._v(\"#\")]),t._v(\" flushPaint()\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"flushPaint\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" List\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"RenderObject\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" dirtyNodes \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _nodesNeedingPaint\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \\n    _nodesNeedingPaint \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"RenderObject\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 反向遍历需要重绘的RenderObject\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"for\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"RenderObject node \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"in\")]),t._v(\" \\n         dirtyNodes\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"sort\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"RenderObject a\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" RenderObject b\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" b\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"depth \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" a\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"depth\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"node\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"_needsPaint \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\" node\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"owner \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"node\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"_layer\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"attached\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 真正的绘制逻辑  \")]),t._v(\"\\n          PaintingContext\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"repaintCompositedChild\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"node\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          node\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_skippedPaintingOnLayer\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"该方法进行了最终的绘制，可以看出它不是重绘了所有 \"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"，而是只重绘了需要重绘的 \"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"。真正的绘制是通过\"),n(\"code\",[t._v(\"PaintingContext.repaintCompositedChild()\")]),t._v(\"来绘制的，该方法最终会调用Flutter engine提供的Canvas API来完成绘制。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"compositeframe\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#compositeframe\"}},[t._v(\"#\")]),t._v(\" compositeFrame()\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"compositeFrame\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" ui\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"SceneBuilder builder \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" ui\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SceneBuilder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" ui\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"Scene scene \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" layer\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"buildScene\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"automaticSystemUiAdjustment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_updateSystemChrome\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    ui\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"window\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"render\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"scene\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//调用Flutter engine的渲染API\")]),t._v(\"\\n    scene\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"finally\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Timeline\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"finishSync\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"这个方法中有一个\"),n(\"code\",[t._v(\"Scene\")]),t._v(\"对象，Scene对象是一个数据结构，保存最终渲染后的像素信息。这个方法将Canvas画好的\"),n(\"code\",[t._v(\"Scene\")]),t._v(\"传给\"),n(\"code\",[t._v(\"window.render()\")]),t._v(\"方法，该方法会直接将scene信息发送给Flutter engine，最终由engine将图像画在设备屏幕上。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"最后\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#最后\"}},[t._v(\"#\")]),t._v(\" 最后\")]),t._v(\" \"),n(\"p\",[t._v(\"需要注意的是：由于\"),n(\"code\",[t._v(\"RendererBinding\")]),t._v(\"只是一个mixin，而with它的是\"),n(\"code\",[t._v(\"WidgetsBinding\")]),t._v(\"，所以我们需要看看\"),n(\"code\",[t._v(\"WidgetsBinding\")]),t._v(\"中是否重写该方法，查看\"),n(\"code\",[t._v(\"WidgetsBinding\")]),t._v(\"的\"),n(\"code\",[t._v(\"drawFrame()\")]),t._v(\"方法源码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"drawFrame\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"renderViewElement \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      buildOwner\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"buildScope\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"renderViewElement\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"drawFrame\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//调用RendererBinding的drawFrame()方法\")]),t._v(\"\\n    buildOwner\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"finalizeTree\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们发现在调用\"),n(\"code\",[t._v(\"RendererBinding.drawFrame()\")]),t._v(\"方法前会调用 \"),n(\"code\",[t._v(\"buildOwner.buildScope()\")]),t._v(\" （非首次绘制），该方法会将被标记为“dirty” 的 element 进行 \"),n(\"code\",[t._v(\"rebuild()\")]),t._v(\" 。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"总结\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),n(\"p\",[t._v(\"本节介绍了Flutter APP从启动到显示到屏幕上的主流程，读者可以结合前面章节对Widget、Element以及RenderObject的介绍来加强细节理解。\")])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/199.7dc1f153.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[199],{864:function(t,e,r){\"use strict\";r.r(e);var a=r(45),I=Object(a.a)({},(function(){var t=this,e=t.$createElement,r=t._self._c||e;return r(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[r(\"h1\",{attrs:{id:\"_14-1-flutter-ui系统\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_14-1-flutter-ui系统\"}},[t._v(\"#\")]),t._v(\" 14.1 Flutter UI系统\")]),t._v(\" \"),r(\"p\",[t._v('在本书的前面章节中，我们多次提到\"UI系统\"这个概念，本书中所指的UI系统特指：基于一个平台，在此平台上实现GUI的一个系统，这里的平台特指操作系统，如Android、iOS或者Windows、macOS。我们说过各个平台UI系统的原理是相通的，也就是说无论是Android还是iOS，他们将一个用户界面展示到屏幕的流程是相似的，所以，在介绍Flutter UI系统之前，我们先看看UI系统的基本原理，这样可以帮助读者对操作系统和系统底层UI逻辑有一个清晰的认识。')]),t._v(\" \"),r(\"h3\",{attrs:{id:\"硬件绘图基本原理\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#硬件绘图基本原理\"}},[t._v(\"#\")]),t._v(\" 硬件绘图基本原理\")]),t._v(\" \"),r(\"p\",[t._v(\"提到原理，我们要从屏幕显示图像的基本原理谈起。我们知道显示器（屏幕）是由一个个物理显示单元组成，每一个单元我们可以称之为一个物理像素点，而每一个像素点可以发出多种颜色，显示器成相的原理就是在不同的物理像素点上显示不同的颜色，最终构成完整的图像。\")]),t._v(\" \"),r(\"p\",[t._v(\"一个像素点能发出的所有颜色总数是显示器的一个重要指标，比如我们所说的1600万色的屏幕就是指一个像素点可以显示出1600万种颜色，而显示器颜色是有RGB三基色组成，所以1600万即2的24次方，即每个基本色（R、G、B）深度扩展至8 bit(位)，颜色深度越深，所能显示的色彩更加丰富靓丽。\")]),t._v(\" \"),r(\"p\",[t._v(\"为了更新显示画面，显示器是以固定的频率刷新（从GPU取数据），比如有一部手机屏幕的刷新频率是 60Hz。当一帧图像绘制完毕后准备绘制下一帧时，显示器会发出一个垂直同步信号（如VSync）， 60Hz的屏幕就会一秒内发出 60次这样的信号。而这个信号主要是用于同步CPU、GPU和显示器的。一般地来说，计算机系统中，CPU、GPU和显示器以一种特定的方式协作：CPU将计算好的显示内容提交给 GPU，GPU渲染后放入帧缓冲区，然后视频控制器按照同步信号从帧缓冲区取帧数据传递给显示器显示。\")]),t._v(\" \"),r(\"p\",[t._v(\"CPU和GPU的任务是各有偏重的，CPU主要用于基本数学和逻辑计算，而GPU主要执行和图形处理相关的复杂的数学，如矩阵变化和几何计算，GPU的主要作用就是确定最终输送给显示器的各个像素点的色值。\")]),t._v(\" \"),r(\"h3\",{attrs:{id:\"操作系统绘制api的封装\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#操作系统绘制api的封装\"}},[t._v(\"#\")]),t._v(\" 操作系统绘制API的封装\")]),t._v(\" \"),r(\"p\",[t._v(\"由于最终的图形计算和绘制都是由相应的硬件来完成，而直接操作硬件的指令通常都会有操作系统屏蔽，应用开发者通常不会直接面对硬件，操作系统屏蔽了这些底层硬件操作后会提供一些封装后的API供操作系统之上的应用调用，但是对于应用开发者来说，直接调用这些操作系统提供的API是比较复杂和低效的，因为操作系统提供的API往往比较基础，直接调用需要了解API的很多细节。正是因为这个原因，几乎所有用于开发GUI程序的编程语言都会在操作系统之上再封装一层，将操作系统原生API封装在一个编程框架和模型中，然后定义一种简单的开发规则来开发GUI应用程序，而这一层抽象，正是我们所说的“UI”系统，如Android SDK正是封装了Android操作系统API，提供了一个“UI描述文件XML+Java操作DOM”的UI系统，而iOS的UIKit 对View的抽象也是一样的，他们都将操作系统API抽象成一个基础对象（如用于2D图形绘制的Canvas），然后再定义一套规则来描述UI，如UI树结构，UI操作的单线程原则等。\")]),t._v(\" \"),r(\"h3\",{attrs:{id:\"flutter-ui系统\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#flutter-ui系统\"}},[t._v(\"#\")]),t._v(\" Flutter UI系统\")]),t._v(\" \"),r(\"p\",[t._v(\"我们可以看到，无论是Android SDK还是iOS的UIKit 的职责都是相同的，它们只是语言载体和底层的系统不同而已。那么可不可以实现这么一个UI系统：可以使用同一种编程语言开发，然后针对不同操作系统API抽象一个对上接口一致，对下适配不同操作系统的的中间层，然后在打包编译时再使用相应的中间层代码？如果可以做到，那么我们就可以使用同一套代码编写跨平台的应用了。而Flutter的原理正是如此，它提供了一套Dart API，然后在底层通过OpenGL这种跨平台的绘制库（内部会调用操作系统API）实现了一套代码跨多端。由于Dart API也是调用操作系统API，所以它的性能接近原生。\")]),t._v(\" \"),r(\"blockquote\",[r(\"p\",[t._v(\"注意，虽然Dart是先调用了OpenGL，OpenGL才会调用操作系统API，但是这仍然是原生渲染，因为OpenGL只是操作系统API的一个封装库，它并不像WebView渲染那样需要JavaScript运行环境和CSS渲染器，所以不会有性能损失。\")])]),t._v(\" \"),r(\"p\",[t._v(\"至此，我们已经介绍了Flutter UI系统和操作系统交互的这一部分原理，现在需要说一些它对应用开发者定义的开发标准。其实在前面的章节中，我们已经对这个标准非常熟悉了, 简单概括就是：组合和响应式。我们要开发一个UI界面，需要通过组合其它Widget来实现，Flutter中，一切都是Widget，当UI要发生变化时，我们不去直接修改DOM，而是通过更新状态，让Flutter UI系统来根据新的状态来重新构建UI。\")]),t._v(\" \"),r(\"p\",[t._v(\"讲到这里，读者可能发现Flutter UI系统和Flutter Framework的概念是差不多的，的确如此，之所以用“UI系统”，是因为其他平台中可能不这么叫，我们只是为了概念统一，便于描述，读者不必纠结于概念本身。\")]),t._v(\" \"),r(\"p\",[t._v(\"在接下来的小节中，我们先详细介绍一下\"),r(\"code\",[t._v(\"Element\")]),t._v(\"、\"),r(\"code\",[t._v(\"RenderObject\")]),t._v(\"，它们是组成Flutter UI系统的基石。最后我们再分析一下Flutter应用启动和运行的整体过程。\")])])}),[],!1,null,null,null);e.default=I.exports}}]);"
  },
  {
    "path": "docs/assets/js/2.f4fe4405.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[2],{305:function(t,e,n){\"use strict\";n.d(e,\"d\",(function(){return i})),n.d(e,\"a\",(function(){return a})),n.d(e,\"i\",(function(){return s})),n.d(e,\"f\",(function(){return u})),n.d(e,\"g\",(function(){return l})),n.d(e,\"h\",(function(){return c})),n.d(e,\"b\",(function(){return f})),n.d(e,\"e\",(function(){return p})),n.d(e,\"k\",(function(){return h})),n.d(e,\"l\",(function(){return d})),n.d(e,\"c\",(function(){return v})),n.d(e,\"j\",(function(){return m}));n(66),n(46),n(306),n(309),n(170),n(65),n(94),n(95),n(27),n(96),n(164);var i=/#.*$/,r=/\\.(md|html)$/,a=/\\/$/,s=/^[a-z]+:/i;function o(t){return decodeURI(t).replace(i,\"\").replace(r,\"\")}function u(t){return s.test(t)}function l(t){return/^mailto:/.test(t)}function c(t){return/^tel:/.test(t)}function f(t){if(u(t))return t;var e=t.match(i),n=e?e[0]:\"\",r=o(t);return a.test(r)?t:r+\".html\"+n}function p(t,e){var n=decodeURIComponent(t.hash),r=function(t){var e=t.match(i);if(e)return e[0]}(e);return(!r||n===r)&&o(t.path)===o(e)}function h(t,e,n){if(u(e))return{type:\"external\",path:e};n&&(e=function(t,e,n){var i=t.charAt(0);if(\"/\"===i)return t;if(\"?\"===i||\"#\"===i)return e+t;var r=e.split(\"/\");n&&r[r.length-1]||r.pop();for(var a=t.replace(/^\\//,\"\").split(\"/\"),s=0;s<a.length;s++){var o=a[s];\"..\"===o?r.pop():\".\"!==o&&r.push(o)}\"\"!==r[0]&&r.unshift(\"\");return r.join(\"/\")}(e,n));for(var i=o(e),r=0;r<t.length;r++)if(o(t[r].regularPath)===i)return Object.assign({},t[r],{type:\"page\",path:f(t[r].path)});return console.error('[vuepress] No matching page found for sidebar item \"'.concat(e,'\"')),{}}function d(t,e,n,i){var r=n.pages,a=n.themeConfig,s=i&&a.locales&&a.locales[i]||a;if(\"auto\"===(t.frontmatter.sidebar||s.sidebar||a.sidebar))return g(t);var o=s.sidebar||a.sidebar;if(o){var u=function(t,e){if(Array.isArray(e))return{base:\"/\",config:e};for(var n in e)if(0===(i=t,/(\\.html|\\/)$/.test(i)?i:i+\"/\").indexOf(encodeURI(n)))return{base:n,config:e[n]};var i;return{}}(e,o),l=u.base,c=u.config;return\"auto\"===c?g(t):c?c.map((function(t){return function t(e,n,i){var r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:1;if(\"string\"==typeof e)return h(n,e,i);if(Array.isArray(e))return Object.assign(h(n,e[0],i),{title:e[1]});var a=e.children||[];return 0===a.length&&e.path?Object.assign(h(n,e.path,i),{title:e.title}):{type:\"group\",path:e.path,title:e.title,sidebarDepth:e.sidebarDepth,initialOpenGroupIndex:e.initialOpenGroupIndex,children:a.map((function(e){return t(e,n,i,r+1)})),collapsable:!1!==e.collapsable}}(t,r,l)})):[]}return[]}function g(t){var e=v(t.headers||[]);return[{type:\"group\",collapsable:!1,title:t.title,path:null,children:e.map((function(e){return{type:\"auto\",title:e.title,basePath:t.path,path:t.path+\"#\"+e.slug,children:e.children||[]}}))}]}function v(t){var e;return(t=t.map((function(t){return Object.assign({},t)}))).forEach((function(t){2===t.level?e=t:e&&(e.children||(e.children=[])).push(t)})),t.filter((function(t){return 2===t.level}))}function m(t){return Object.assign(t,{type:t.items&&t.items.length?\"links\":\"link\"})}},306:function(t,e,n){\"use strict\";var i=n(166),r=n(5),a=n(13),s=n(23),o=n(168),u=n(169);i(\"match\",1,(function(t,e,n){return[function(e){var n=s(this),i=null==e?void 0:e[t];return void 0!==i?i.call(e,n):new RegExp(e)[t](String(n))},function(t){var i=n(e,t,this);if(i.done)return i.value;var s=r(t),l=String(this);if(!s.global)return u(s,l);var c=s.unicode;s.lastIndex=0;for(var f,p=[],h=0;null!==(f=u(s,l));){var d=String(f[0]);p[h]=d,\"\"===d&&(s.lastIndex=o(l,a(s.lastIndex),c)),h++}return 0===h?null:p}]}))},307:function(t,e){t.exports=\"\\t\\n\\v\\f\\r                　\\u2028\\u2029\\ufeff\"},308:function(t,e,n){\"use strict\";n(336),n(92),n(93);var i=n(305),r={name:\"NavLink\",props:{item:{required:!0}},computed:{link:function(){return Object(i.b)(this.item.link)},exact:function(){var t=this;return this.$site.locales?Object.keys(this.$site.locales).some((function(e){return e===t.link})):\"/\"===this.link},isNonHttpURI:function(){return Object(i.g)(this.link)||Object(i.h)(this.link)},isBlankTarget:function(){return\"_blank\"===this.target},isInternal:function(){return!Object(i.f)(this.link)&&!this.isBlankTarget},target:function(){return this.isNonHttpURI?null:this.item.target?this.item.target:Object(i.f)(this.link)?\"_blank\":\"\"},rel:function(){return this.isNonHttpURI||!1===this.item.rel?null:this.item.rel?this.item.rel:this.isBlankTarget?\"noopener noreferrer\":null}},methods:{focusoutAction:function(){this.$emit(\"focusout\")}}},a=n(45),s=Object(a.a)(r,(function(){var t=this,e=t.$createElement,n=t._self._c||e;return t.isInternal?n(\"RouterLink\",{staticClass:\"nav-link\",attrs:{to:t.link,exact:t.exact},nativeOn:{focusout:function(e){return t.focusoutAction(e)}}},[t._v(\"\\n  \"+t._s(t.item.text)+\"\\n\")]):n(\"a\",{staticClass:\"nav-link external\",attrs:{href:t.link,target:t.target,rel:t.rel},on:{focusout:t.focusoutAction}},[t._v(\"\\n  \"+t._s(t.item.text)+\"\\n  \"),t.isBlankTarget?n(\"OutboundLink\"):t._e()],1)}),[],!1,null,null,null);e.a=s.exports},309:function(t,e,n){\"use strict\";var i=n(166),r=n(165),a=n(5),s=n(23),o=n(100),u=n(168),l=n(13),c=n(169),f=n(68),p=n(2),h=[].push,d=Math.min,g=!p((function(){return!RegExp(4294967295,\"y\")}));i(\"split\",2,(function(t,e,n){var i;return i=\"c\"==\"abbc\".split(/(b)*/)[1]||4!=\"test\".split(/(?:)/,-1).length||2!=\"ab\".split(/(?:ab)*/).length||4!=\".\".split(/(.?)(.?)/).length||\".\".split(/()()/).length>1||\"\".split(/.?/).length?function(t,n){var i=String(s(this)),a=void 0===n?4294967295:n>>>0;if(0===a)return[];if(void 0===t)return[i];if(!r(t))return e.call(i,t,a);for(var o,u,l,c=[],p=(t.ignoreCase?\"i\":\"\")+(t.multiline?\"m\":\"\")+(t.unicode?\"u\":\"\")+(t.sticky?\"y\":\"\"),d=0,g=new RegExp(t.source,p+\"g\");(o=f.call(g,i))&&!((u=g.lastIndex)>d&&(c.push(i.slice(d,o.index)),o.length>1&&o.index<i.length&&h.apply(c,o.slice(1)),l=o[0].length,d=u,c.length>=a));)g.lastIndex===o.index&&g.lastIndex++;return d===i.length?!l&&g.test(\"\")||c.push(\"\"):c.push(i.slice(d)),c.length>a?c.slice(0,a):c}:\"0\".split(void 0,0).length?function(t,n){return void 0===t&&0===n?[]:e.call(this,t,n)}:e,[function(e,n){var r=s(this),a=null==e?void 0:e[t];return void 0!==a?a.call(e,r,n):i.call(String(r),e,n)},function(t,r){var s=n(i,t,this,r,i!==e);if(s.done)return s.value;var f=a(t),p=String(this),h=o(f,RegExp),v=f.unicode,m=(f.ignoreCase?\"i\":\"\")+(f.multiline?\"m\":\"\")+(f.unicode?\"u\":\"\")+(g?\"y\":\"g\"),b=new h(g?f:\"^(?:\"+f.source+\")\",m),k=void 0===r?4294967295:r>>>0;if(0===k)return[];if(0===p.length)return null===c(b,p)?[p]:[];for(var _=0,x=0,C=[];x<p.length;){b.lastIndex=g?x:0;var L,y=c(b,g?p:p.slice(x));if(null===y||(L=d(l(b.lastIndex+(g?0:x)),p.length))===_)x=u(p,x,v);else{if(C.push(p.slice(_,x)),C.length===k)return C;for(var $=1;$<=y.length-1;$++)if(C.push(y[$]),C.length===k)return C;x=_=L}}return C.push(p.slice(_)),C}]}),!g)},310:function(t,e,n){},311:function(t,e,n){var i=n(23),r=\"[\"+n(307)+\"]\",a=RegExp(\"^\"+r+r+\"*\"),s=RegExp(r+r+\"*$\"),o=function(t){return function(e){var n=String(i(e));return 1&t&&(n=n.replace(a,\"\")),2&t&&(n=n.replace(s,\"\")),n}};t.exports={start:o(1),end:o(2),trim:o(3)}},312:function(t,e,n){\"use strict\";var i=n(0),r=n(311).trim;i({target:\"String\",proto:!0,forced:n(342)(\"trim\")},{trim:function(){return r(this)}})},313:function(t,e,n){var i=n(6),r=n(3),a=n(98),s=n(343),o=n(7).f,u=n(67).f,l=n(165),c=n(167),f=n(172),p=n(18),h=n(2),d=n(28).set,g=n(171),v=n(1)(\"match\"),m=r.RegExp,b=m.prototype,k=/a/g,_=/a/g,x=new m(k)!==k,C=f.UNSUPPORTED_Y;if(i&&a(\"RegExp\",!x||C||h((function(){return _[v]=!1,m(k)!=k||m(_)==_||\"/a/i\"!=m(k,\"i\")})))){for(var L=function(t,e){var n,i=this instanceof L,r=l(t),a=void 0===e;if(!i&&r&&t.constructor===L&&a)return t;x?r&&!a&&(t=t.source):t instanceof L&&(a&&(e=c.call(t)),t=t.source),C&&(n=!!e&&e.indexOf(\"y\")>-1)&&(e=e.replace(/y/g,\"\"));var o=s(x?new m(t,e):m(t,e),i?this:b,L);return C&&n&&d(o,{sticky:n}),o},y=function(t){t in L||o(L,t,{configurable:!0,get:function(){return m[t]},set:function(e){m[t]=e}})},$=u(m),w=0;$.length>w;)y($[w++]);b.constructor=L,L.prototype=b,p(r,\"RegExp\",L)}g(\"RegExp\")},314:function(t,e,n){\"use strict\";var i=n(18),r=n(5),a=n(2),s=n(167),o=RegExp.prototype,u=o.toString,l=a((function(){return\"/a/b\"!=u.call({source:\"a\",flags:\"b\"})})),c=\"toString\"!=u.name;(l||c)&&i(RegExp.prototype,\"toString\",(function(){var t=r(this),e=String(t.source),n=t.flags;return\"/\"+e+\"/\"+String(void 0===n&&t instanceof RegExp&&!(\"flags\"in o)?s.call(t):n)}),{unsafe:!0})},315:function(t,e,n){},316:function(t,e,n){},317:function(t,e,n){},318:function(t,e,n){},319:function(t,e,n){},320:function(t,e,n){},321:function(t,e){t.exports=function(t){return null==t}},322:function(t,e,n){},323:function(t,e,n){},324:function(t,e,n){},325:function(t,e,n){},326:function(t,e,n){},327:function(t,e,n){},332:function(t,e,n){\"use strict\";n.r(e);n(92);var i=n(305),r={name:\"SidebarGroup\",components:{DropdownTransition:n(334).a},props:[\"item\",\"open\",\"collapsable\",\"depth\"],beforeCreate:function(){this.$options.components.SidebarLinks=n(332).default},methods:{isActive:i.e}},a=(n(355),n(45)),s=Object(a.a)(r,(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n(\"section\",{staticClass:\"sidebar-group\",class:[{collapsable:t.collapsable,\"is-sub-group\":0!==t.depth},\"depth-\"+t.depth]},[t.item.path?n(\"RouterLink\",{staticClass:\"sidebar-heading clickable\",class:{open:t.open,active:t.isActive(t.$route,t.item.path)},attrs:{to:t.item.path},nativeOn:{click:function(e){return t.$emit(\"toggle\")}}},[n(\"span\",[t._v(t._s(t.item.title))]),t._v(\" \"),t.collapsable?n(\"span\",{staticClass:\"arrow\",class:t.open?\"down\":\"right\"}):t._e()]):n(\"p\",{staticClass:\"sidebar-heading\",class:{open:t.open},on:{click:function(e){return t.$emit(\"toggle\")}}},[n(\"span\",[t._v(t._s(t.item.title))]),t._v(\" \"),t.collapsable?n(\"span\",{staticClass:\"arrow\",class:t.open?\"down\":\"right\"}):t._e()]),t._v(\" \"),n(\"DropdownTransition\",[t.open||!t.collapsable?n(\"SidebarLinks\",{staticClass:\"sidebar-group-items\",attrs:{items:t.item.children,\"sidebar-depth\":t.item.sidebarDepth,\"initial-open-group-index\":t.item.initialOpenGroupIndex,depth:t.depth+1}}):t._e()],1)],1)}),[],!1,null,null,null).exports;n(356),n(65);function o(t,e,n,i,r){var a={props:{to:e,activeClass:\"\",exactActiveClass:\"\"},class:{active:i,\"sidebar-link\":!0}};return r>2&&(a.style={\"padding-left\":r+\"rem\"}),t(\"RouterLink\",a,n)}function u(t,e,n,r,a){var s=arguments.length>5&&void 0!==arguments[5]?arguments[5]:1;return!e||s>a?null:t(\"ul\",{class:\"sidebar-sub-headers\"},e.map((function(e){var l=Object(i.e)(r,n+\"#\"+e.slug);return t(\"li\",{class:\"sidebar-sub-header\"},[o(t,n+\"#\"+e.slug,e.title,l,e.level-1),u(t,e.children,n,r,a,s+1)])})))}var l={functional:!0,props:[\"item\",\"sidebarDepth\"],render:function(t,e){var n=e.parent,r=n.$page,a=(n.$site,n.$route),s=n.$themeConfig,l=n.$themeLocaleConfig,c=e.props,f=c.item,p=c.sidebarDepth,h=Object(i.e)(a,f.path),d=\"auto\"===f.type?h||f.children.some((function(t){return Object(i.e)(a,f.basePath+\"#\"+t.slug)})):h,g=\"external\"===f.type?function(t,e,n){return t(\"a\",{attrs:{href:e,target:\"_blank\",rel:\"noopener noreferrer\"},class:{\"sidebar-link\":!0}},[n,t(\"OutboundLink\")])}(t,f.path,f.title||f.path):o(t,f.path,f.title||f.path,d),v=[r.frontmatter.sidebarDepth,p,l.sidebarDepth,s.sidebarDepth,1].find((function(t){return void 0!==t})),m=l.displayAllHeaders||s.displayAllHeaders;return\"auto\"===f.type?[g,u(t,f.children,f.basePath,a,v)]:(d||m)&&f.headers&&!i.d.test(f.path)?[g,u(t,Object(i.c)(f.headers),f.path,a,v)]:g}};n(357);function c(t,e){if(\"group\"===e.type){var n=e.path&&Object(i.e)(t,e.path),r=e.children.some((function(e){return\"group\"===e.type?c(t,e):\"page\"===e.type&&Object(i.e)(t,e.path)}));return n||r}return!1}var f={name:\"SidebarLinks\",components:{SidebarGroup:s,SidebarLink:Object(a.a)(l,void 0,void 0,!1,null,null,null).exports},props:[\"items\",\"depth\",\"sidebarDepth\",\"initialOpenGroupIndex\"],data:function(){return{openGroupIndex:this.initialOpenGroupIndex||0}},watch:{$route:function(){this.refreshIndex()}},created:function(){this.refreshIndex()},methods:{refreshIndex:function(){var t=function(t,e){for(var n=0;n<e.length;n++){var i=e[n];if(c(t,i))return n}return-1}(this.$route,this.items);t>-1&&(this.openGroupIndex=t)},toggleGroup:function(t){this.openGroupIndex=t===this.openGroupIndex?-1:t},isActive:function(t){return Object(i.e)(this.$route,t.regularPath)}}},p=Object(a.a)(f,(function(){var t=this,e=t.$createElement,n=t._self._c||e;return t.items.length?n(\"ul\",{staticClass:\"sidebar-links\"},t._l(t.items,(function(e,i){return n(\"li\",{key:i},[\"group\"===e.type?n(\"SidebarGroup\",{attrs:{item:e,open:i===t.openGroupIndex,collapsable:e.collapsable||e.collapsible,depth:t.depth},on:{toggle:function(e){return t.toggleGroup(i)}}}):n(\"SidebarLink\",{attrs:{\"sidebar-depth\":t.sidebarDepth,item:e}})],1)})),0):t._e()}),[],!1,null,null,null);e.default=p.exports},333:function(t,e,n){\"use strict\";var i=n(43),r=(n(93),n(65),n(66),n(46),n(92),n(175),n(306),n(313),n(314),n(308)),a=n(334),s=n(177),o=n.n(s),u={name:\"DropdownLink\",components:{NavLink:r.a,DropdownTransition:a.a},props:{item:{required:!0}},data:function(){return{open:!1}},computed:{dropdownAriaLabel:function(){return this.item.ariaLabel||this.item.text}},watch:{$route:function(){this.open=!1}},methods:{setOpen:function(t){this.open=t},isLastItemOfArray:function(t,e){return o()(e)===t},handleDropdown:function(){0===event.detail&&this.setOpen(!this.open)}}},l=(n(348),n(45)),c=Object(l.a)(u,(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n(\"div\",{staticClass:\"dropdown-wrapper\",class:{open:t.open}},[n(\"button\",{staticClass:\"dropdown-title\",attrs:{type:\"button\",\"aria-label\":t.dropdownAriaLabel},on:{click:t.handleDropdown}},[n(\"span\",{staticClass:\"title\"},[t._v(t._s(t.item.text))]),t._v(\" \"),n(\"span\",{staticClass:\"arrow down\"})]),t._v(\" \"),n(\"button\",{staticClass:\"mobile-dropdown-title\",attrs:{type:\"button\",\"aria-label\":t.dropdownAriaLabel},on:{click:function(e){return t.setOpen(!t.open)}}},[n(\"span\",{staticClass:\"title\"},[t._v(t._s(t.item.text))]),t._v(\" \"),n(\"span\",{staticClass:\"arrow\",class:t.open?\"down\":\"right\"})]),t._v(\" \"),n(\"DropdownTransition\",[n(\"ul\",{directives:[{name:\"show\",rawName:\"v-show\",value:t.open,expression:\"open\"}],staticClass:\"nav-dropdown\"},t._l(t.item.items,(function(e,i){return n(\"li\",{key:e.link||i,staticClass:\"dropdown-item\"},[\"links\"===e.type?n(\"h4\",[t._v(\"\\n          \"+t._s(e.text)+\"\\n        \")]):t._e(),t._v(\" \"),\"links\"===e.type?n(\"ul\",{staticClass:\"dropdown-subitem-wrapper\"},t._l(e.items,(function(i){return n(\"li\",{key:i.link,staticClass:\"dropdown-subitem\"},[n(\"NavLink\",{attrs:{item:i},on:{focusout:function(n){t.isLastItemOfArray(i,e.items)&&t.isLastItemOfArray(e,t.item.items)&&t.setOpen(!1)}}})],1)})),0):n(\"NavLink\",{attrs:{item:e},on:{focusout:function(n){t.isLastItemOfArray(e,t.item.items)&&t.setOpen(!1)}}})],1)})),0)])],1)}),[],!1,null,null,null).exports,f=n(305),p={name:\"NavLinks\",components:{NavLink:r.a,DropdownLink:c},computed:{userNav:function(){return this.$themeLocaleConfig.nav||this.$site.themeConfig.nav||[]},nav:function(){var t=this,e=this.$site.locales;if(e&&Object.keys(e).length>1){var n=this.$page.path,r=this.$router.options.routes,a=this.$site.themeConfig.locales||{},s={text:this.$themeLocaleConfig.selectText||\"Languages\",ariaLabel:this.$themeLocaleConfig.ariaLabel||\"Select language\",items:Object.keys(e).map((function(i){var s,o=e[i],u=a[i]&&a[i].label||o.lang;return o.lang===t.$lang?s=n:(s=n.replace(t.$localeConfig.path,i),r.some((function(t){return t.path===s}))||(s=i)),{text:u,link:s}}))};return[].concat(Object(i.a)(this.userNav),[s])}return this.userNav},userLinks:function(){return(this.nav||[]).map((function(t){return Object.assign(Object(f.j)(t),{items:(t.items||[]).map(f.j)})}))},repoLink:function(){var t=this.$site.themeConfig.repo;return t?/^https?:/.test(t)?t:\"https://github.com/\".concat(t):null},repoLabel:function(){if(this.repoLink){if(this.$site.themeConfig.repoLabel)return this.$site.themeConfig.repoLabel;for(var t=this.repoLink.match(/^https?:\\/\\/[^/]+/)[0],e=[\"GitHub\",\"GitLab\",\"Bitbucket\"],n=0;n<e.length;n++){var i=e[n];if(new RegExp(i,\"i\").test(t))return i}return\"Source\"}}}},h=(n(349),Object(l.a)(p,(function(){var t=this,e=t.$createElement,n=t._self._c||e;return t.userLinks.length||t.repoLink?n(\"nav\",{staticClass:\"nav-links\"},[t._l(t.userLinks,(function(t){return n(\"div\",{key:t.link,staticClass:\"nav-item\"},[\"links\"===t.type?n(\"DropdownLink\",{attrs:{item:t}}):n(\"NavLink\",{attrs:{item:t}})],1)})),t._v(\" \"),t.repoLink?n(\"a\",{staticClass:\"repo-link\",attrs:{href:t.repoLink,target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"\\n    \"+t._s(t.repoLabel)+\"\\n    \"),n(\"OutboundLink\")],1):t._e()],2):t._e()}),[],!1,null,null,null));e.a=h.exports},334:function(t,e,n){\"use strict\";var i={name:\"DropdownTransition\",methods:{setHeight:function(t){t.style.height=t.scrollHeight+\"px\"},unsetHeight:function(t){t.style.height=\"\"}}},r=(n(347),n(45)),a=Object(r.a)(i,(function(){var t=this.$createElement;return(this._self._c||t)(\"transition\",{attrs:{name:\"dropdown\"},on:{enter:this.setHeight,\"after-enter\":this.unsetHeight,\"before-leave\":this.setHeight}},[this._t(\"default\")],2)}),[],!1,null,null,null);e.a=a.exports},336:function(t,e,n){\"use strict\";var i=n(0),r=n(337);i({target:\"String\",proto:!0,forced:n(338)(\"link\")},{link:function(t){return r(this,\"a\",\"href\",t)}})},337:function(t,e,n){var i=n(23),r=/\"/g;t.exports=function(t,e,n,a){var s=String(i(t)),o=\"<\"+e;return\"\"!==n&&(o+=\" \"+n+'=\"'+String(a).replace(r,\"&quot;\")+'\"'),o+\">\"+s+\"</\"+e+\">\"}},338:function(t,e,n){var i=n(2);t.exports=function(t){return i((function(){var e=\"\"[t]('\"');return e!==e.toLowerCase()||e.split('\"').length>3}))}},339:function(t,e,n){\"use strict\";n(310)},340:function(t,e,n){var i=n(0),r=n(341);i({global:!0,forced:parseInt!=r},{parseInt:r})},341:function(t,e,n){var i=n(3),r=n(311).trim,a=n(307),s=i.parseInt,o=/^[+-]?0[Xx]/,u=8!==s(a+\"08\")||22!==s(a+\"0x16\");t.exports=u?function(t,e){var n=r(String(t));return s(n,e>>>0||(o.test(n)?16:10))}:s},342:function(t,e,n){var i=n(2),r=n(307);t.exports=function(t){return i((function(){return!!r[t]()||\"​᠎\"!=\"​᠎\"[t]()||r[t].name!==t}))}},343:function(t,e,n){var i=n(4),r=n(99);t.exports=function(t,e,n){var a,s;return r&&\"function\"==typeof(a=e.constructor)&&a!==n&&i(s=a.prototype)&&s!==n.prototype&&r(t,s),t}},344:function(t,e,n){\"use strict\";var i,r=n(0),a=n(24).f,s=n(13),o=n(101),u=n(23),l=n(102),c=n(20),f=\"\".endsWith,p=Math.min,h=l(\"endsWith\");r({target:\"String\",proto:!0,forced:!!(c||h||(i=a(String.prototype,\"endsWith\"),!i||i.writable))&&!h},{endsWith:function(t){var e=String(u(this));o(t);var n=arguments.length>1?arguments[1]:void 0,i=s(e.length),r=void 0===n?i:p(s(n),i),a=String(t);return f?f.call(e,a,r):e.slice(r-a.length,r)===a}})},345:function(t,e,n){\"use strict\";n(315)},346:function(t,e,n){\"use strict\";n(316)},347:function(t,e,n){\"use strict\";n(317)},348:function(t,e,n){\"use strict\";n(318)},349:function(t,e,n){\"use strict\";n(319)},350:function(t,e,n){\"use strict\";n(320)},351:function(t,e,n){\"use strict\";n(322)},352:function(t,e,n){var i=n(30),r=n(14),a=n(25);t.exports=function(t){return\"string\"==typeof t||!r(t)&&a(t)&&\"[object String]\"==i(t)}},353:function(t,e,n){\"use strict\";n(323)},354:function(t,e,n){\"use strict\";n(324)},355:function(t,e,n){\"use strict\";n(325)},356:function(t,e,n){\"use strict\";var i=n(0),r=n(29).find,a=n(97),s=!0;\"find\"in[]&&Array(1).find((function(){s=!1})),i({target:\"Array\",proto:!0,forced:s},{find:function(t){return r(this,t,arguments.length>1?arguments[1]:void 0)}}),a(\"find\")},357:function(t,e,n){\"use strict\";n(326)},358:function(t,e,n){\"use strict\";n(327)},713:function(t,e,n){\"use strict\";n(340),n(312),n(164),n(96),n(27),n(306),n(46),n(173),n(174),n(170),n(66),n(313),n(314),n(65),n(309),n(344),n(92);var i=n(176),r=n.n(i),a=function(t,e){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,i=r()(e,\"title\",\"\");return r()(e,\"frontmatter.tags\")&&(i+=\" \".concat(e.frontmatter.tags.join(\" \"))),n&&(i+=\" \".concat(n)),s(t,i)},s=function(t,e){var n=function(t){return t.replace(/[-/\\\\^$*+?.()|[\\]{}]/g,\"\\\\$&\")},i=new RegExp(\"[^\\0-]\"),r=t.split(/\\s+/g).map((function(t){return t.trim()})).filter((function(t){return!!t}));if(i.test(t))return r.some((function(t){return e.toLowerCase().indexOf(t)>-1}));var a=t.endsWith(\" \");return new RegExp(r.map((function(t,e){return r.length!==e+1||a?\"(?=.*\\\\b\".concat(n(t),\"\\\\b)\"):\"(?=.*\\\\b\".concat(n(t),\")\")})).join(\"\")+\".+\",\"gi\").test(e)},o={name:\"SearchBox\",data:function(){return{query:\"\",focused:!1,focusIndex:0,placeholder:void 0}},computed:{showSuggestions:function(){return this.focused&&this.suggestions&&this.suggestions.length},suggestions:function(){var t=this.query.trim().toLowerCase();if(t){for(var e=this.$site.pages,n=this.$site.themeConfig.searchMaxSuggestions||5,i=this.$localePath,r=[],s=0;s<e.length&&!(r.length>=n);s++){var o=e[s];if(this.getPageLocalePath(o)===i&&this.isSearchable(o))if(a(t,o))r.push(o);else if(o.headers)for(var u=0;u<o.headers.length&&!(r.length>=n);u++){var l=o.headers[u];l.title&&a(t,o,l.title)&&r.push(Object.assign({},o,{path:o.path+\"#\"+l.slug,header:l}))}}return r}},alignRight:function(){return(this.$site.themeConfig.nav||[]).length+(this.$site.repo?1:0)<=2}},mounted:function(){this.placeholder=this.$site.themeConfig.searchPlaceholder||\"\",document.addEventListener(\"keydown\",this.onHotkey)},beforeDestroy:function(){document.removeEventListener(\"keydown\",this.onHotkey)},methods:{getPageLocalePath:function(t){for(var e in this.$site.locales||{})if(\"/\"!==e&&0===t.path.indexOf(e))return e;return\"/\"},isSearchable:function(t){var e=null;return null===e||(e=Array.isArray(e)?e:new Array(e)).filter((function(e){return t.path.match(e)})).length>0},onHotkey:function(t){t.srcElement===document.body&&[\"s\",\"/\"].includes(t.key)&&(this.$refs.input.focus(),t.preventDefault())},onUp:function(){this.showSuggestions&&(this.focusIndex>0?this.focusIndex--:this.focusIndex=this.suggestions.length-1)},onDown:function(){this.showSuggestions&&(this.focusIndex<this.suggestions.length-1?this.focusIndex++:this.focusIndex=0)},go:function(t){this.showSuggestions&&(this.$router.push(this.suggestions[t].path),this.query=\"\",this.focusIndex=0)},focus:function(t){this.focusIndex=t},unfocus:function(){this.focusIndex=-1}}},u=(n(345),n(45)),l=Object(u.a)(o,(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n(\"div\",{staticClass:\"search-box\"},[n(\"input\",{ref:\"input\",class:{focused:t.focused},attrs:{\"aria-label\":\"Search\",placeholder:t.placeholder,autocomplete:\"off\",spellcheck:\"false\"},domProps:{value:t.query},on:{input:function(e){t.query=e.target.value},focus:function(e){t.focused=!0},blur:function(e){t.focused=!1},keyup:[function(e){return!e.type.indexOf(\"key\")&&t._k(e.keyCode,\"enter\",13,e.key,\"Enter\")?null:t.go(t.focusIndex)},function(e){return!e.type.indexOf(\"key\")&&t._k(e.keyCode,\"up\",38,e.key,[\"Up\",\"ArrowUp\"])?null:t.onUp(e)},function(e){return!e.type.indexOf(\"key\")&&t._k(e.keyCode,\"down\",40,e.key,[\"Down\",\"ArrowDown\"])?null:t.onDown(e)}]}}),t._v(\" \"),t.showSuggestions?n(\"ul\",{staticClass:\"suggestions\",class:{\"align-right\":t.alignRight},on:{mouseleave:t.unfocus}},t._l(t.suggestions,(function(e,i){return n(\"li\",{key:i,staticClass:\"suggestion\",class:{focused:i===t.focusIndex},on:{mousedown:function(e){return t.go(i)},mouseenter:function(e){return t.focus(i)}}},[n(\"a\",{attrs:{href:e.path},on:{click:function(t){t.preventDefault()}}},[n(\"span\",{staticClass:\"page-title\"},[t._v(t._s(e.title||e.path))]),t._v(\" \"),e.header?n(\"span\",{staticClass:\"header\"},[t._v(\"> \"+t._s(e.header.title))]):t._e()])])})),0):t._e()])}),[],!1,null,null,null).exports;n(346);function c(t,e){return t.ownerDocument.defaultView.getComputedStyle(t,null)[e]}var f={name:\"Navbar\",components:{SidebarButton:Object(u.a)({},(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n(\"div\",{staticClass:\"sidebar-button\",on:{click:function(e){return t.$emit(\"toggle-sidebar\")}}},[n(\"svg\",{staticClass:\"icon\",attrs:{xmlns:\"http://www.w3.org/2000/svg\",\"aria-hidden\":\"true\",role:\"img\",viewBox:\"0 0 448 512\"}},[n(\"path\",{attrs:{fill:\"currentColor\",d:\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"}})])])}),[],!1,null,null,null).exports,NavLinks:n(333).a,SearchBox:l,AlgoliaSearchBox:{}},data:function(){return{linksWrapMaxWidth:null}},computed:{algolia:function(){return this.$themeLocaleConfig.algolia||this.$site.themeConfig.algolia||{}},isAlgoliaSearch:function(){return this.algolia&&this.algolia.apiKey&&this.algolia.indexName}},mounted:function(){var t=this,e=parseInt(c(this.$el,\"paddingLeft\"))+parseInt(c(this.$el,\"paddingRight\")),n=function(){document.documentElement.clientWidth<719?t.linksWrapMaxWidth=null:t.linksWrapMaxWidth=t.$el.offsetWidth-e-(t.$refs.siteName&&t.$refs.siteName.offsetWidth||0)};n(),window.addEventListener(\"resize\",n,!1)}},p=(n(350),Object(u.a)(f,(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n(\"header\",{staticClass:\"navbar\"},[n(\"SidebarButton\",{on:{\"toggle-sidebar\":function(e){return t.$emit(\"toggle-sidebar\")}}}),t._v(\" \"),n(\"RouterLink\",{staticClass:\"home-link\",attrs:{to:t.$localePath}},[t.$site.themeConfig.logo?n(\"img\",{staticClass:\"logo\",attrs:{src:t.$withBase(t.$site.themeConfig.logo),alt:t.$siteTitle}}):t._e(),t._v(\" \"),t.$siteTitle?n(\"span\",{ref:\"siteName\",staticClass:\"site-name\",class:{\"can-hide\":t.$site.themeConfig.logo}},[t._v(t._s(t.$siteTitle))]):t._e()]),t._v(\" \"),n(\"div\",{staticClass:\"links\",style:t.linksWrapMaxWidth?{\"max-width\":t.linksWrapMaxWidth+\"px\"}:{}},[t.isAlgoliaSearch?n(\"AlgoliaSearchBox\",{attrs:{options:t.algolia}}):!1!==t.$site.themeConfig.search&&!1!==t.$page.frontmatter.search?n(\"SearchBox\"):t._e(),t._v(\" \"),n(\"NavLinks\",{staticClass:\"can-hide\"})],1)],1)}),[],!1,null,null,null));e.a=p.exports},714:function(t,e,n){\"use strict\";n(66),n(46);var i=n(321),r=n.n(i),a=n(305),s={name:\"PageEdit\",computed:{lastUpdated:function(){return this.$page.lastUpdated},lastUpdatedText:function(){return\"string\"==typeof this.$themeLocaleConfig.lastUpdated?this.$themeLocaleConfig.lastUpdated:\"string\"==typeof this.$site.themeConfig.lastUpdated?this.$site.themeConfig.lastUpdated:\"Last Updated\"},editLink:function(){var t=r()(this.$page.frontmatter.editLink)?this.$site.themeConfig.editLinks:this.$page.frontmatter.editLink,e=this.$site.themeConfig,n=e.repo,i=e.docsDir,a=void 0===i?\"\":i,s=e.docsBranch,o=void 0===s?\"master\":s,u=e.docsRepo,l=void 0===u?n:u;return t&&l&&this.$page.relativePath?this.createEditLink(n,l,a,o,this.$page.relativePath):null},editLinkText:function(){return this.$themeLocaleConfig.editLinkText||this.$site.themeConfig.editLinkText||\"Edit this page\"}},methods:{createEditLink:function(t,e,n,i,r){if(/bitbucket.org/.test(e))return e.replace(a.a,\"\")+\"/src\"+\"/\".concat(i,\"/\")+(n?n.replace(a.a,\"\")+\"/\":\"\")+r+\"?mode=edit&spa=0&at=\".concat(i,\"&fileviewer=file-view-default\");return/gitlab.com/.test(e)?e.replace(a.a,\"\")+\"/-/edit\"+\"/\".concat(i,\"/\")+(n?n.replace(a.a,\"\")+\"/\":\"\")+r:(a.i.test(e)?e:\"https://github.com/\".concat(e)).replace(a.a,\"\")+\"/edit\"+\"/\".concat(i,\"/\")+(n?n.replace(a.a,\"\")+\"/\":\"\")+r}}},o=(n(351),n(45)),u=Object(o.a)(s,(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n(\"footer\",{staticClass:\"page-edit\"},[t.editLink?n(\"div\",{staticClass:\"edit-link\"},[n(\"a\",{attrs:{href:t.editLink,target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(t._s(t.editLinkText))]),t._v(\" \"),n(\"OutboundLink\")],1):t._e(),t._v(\" \"),t.lastUpdated?n(\"div\",{staticClass:\"last-updated\"},[n(\"span\",{staticClass:\"prefix\"},[t._v(t._s(t.lastUpdatedText)+\":\")]),t._v(\" \"),n(\"span\",{staticClass:\"time\"},[t._v(t._s(t.lastUpdated))])]):t._e()])}),[],!1,null,null,null).exports,l=n(352),c=n.n(l),f={name:\"PageNav\",props:[\"sidebarItems\"],computed:{prev:function(){return h(p.PREV,this)},next:function(){return h(p.NEXT,this)}}};var p={NEXT:{resolveLink:function(t,e){return d(t,e,1)},getThemeLinkConfig:function(t){return t.nextLinks},getPageLinkConfig:function(t){return t.frontmatter.next}},PREV:{resolveLink:function(t,e){return d(t,e,-1)},getThemeLinkConfig:function(t){return t.prevLinks},getPageLinkConfig:function(t){return t.frontmatter.prev}}};function h(t,e){var n=e.$themeConfig,i=e.$page,s=e.$route,o=e.$site,u=e.sidebarItems,l=t.resolveLink,f=t.getThemeLinkConfig,p=t.getPageLinkConfig,h=f(n),d=p(i),g=r()(d)?h:d;return!1===g?void 0:c()(g)?Object(a.k)(o.pages,g,s.path):l(i,u)}function d(t,e,n){var i=[];!function t(e,n){for(var i=0,r=e.length;i<r;i++)\"group\"===e[i].type?t(e[i].children||[],n):n.push(e[i])}(e,i);for(var r=0;r<i.length;r++){var a=i[r];if(\"page\"===a.type&&a.path===decodeURIComponent(t.path))return i[r+n]}}var g=f,v=(n(353),{components:{PageEdit:u,PageNav:Object(o.a)(g,(function(){var t=this,e=t.$createElement,n=t._self._c||e;return t.prev||t.next?n(\"div\",{staticClass:\"page-nav\"},[n(\"p\",{staticClass:\"inner\"},[t.prev?n(\"span\",{staticClass:\"prev\"},[t._v(\"\\n      ←\\n      \"),\"external\"===t.prev.type?n(\"a\",{staticClass:\"prev\",attrs:{href:t.prev.path,target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"\\n        \"+t._s(t.prev.title||t.prev.path)+\"\\n\\n        \"),n(\"OutboundLink\")],1):n(\"RouterLink\",{staticClass:\"prev\",attrs:{to:t.prev.path}},[t._v(\"\\n        \"+t._s(t.prev.title||t.prev.path)+\"\\n      \")])],1):t._e(),t._v(\" \"),t.next?n(\"span\",{staticClass:\"next\"},[\"external\"===t.next.type?n(\"a\",{attrs:{href:t.next.path,target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"\\n        \"+t._s(t.next.title||t.next.path)+\"\\n\\n        \"),n(\"OutboundLink\")],1):n(\"RouterLink\",{attrs:{to:t.next.path}},[t._v(\"\\n        \"+t._s(t.next.title||t.next.path)+\"\\n      \")]),t._v(\"\\n      →\\n    \")],1):t._e()])]):t._e()}),[],!1,null,null,null).exports},props:[\"sidebarItems\"]}),m=(n(354),Object(o.a)(v,(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n(\"main\",{staticClass:\"page\"},[t._t(\"top\"),t._v(\" \"),n(\"Content\",{staticClass:\"theme-default-content\"}),t._v(\" \"),n(\"PageEdit\"),t._v(\" \"),n(\"PageNav\",t._b({},\"PageNav\",{sidebarItems:t.sidebarItems},!1)),t._v(\" \"),t._t(\"bottom\")],2)}),[],!1,null,null,null));e.a=m.exports},715:function(t,e,n){\"use strict\";var i={name:\"Home\",components:{NavLink:n(308).a},computed:{data:function(){return this.$page.frontmatter},actionLink:function(){return{link:this.data.actionLink,text:this.data.actionText}}}},r=(n(339),n(45)),a=Object(r.a)(i,(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n(\"main\",{staticClass:\"home\",attrs:{\"aria-labelledby\":null!==t.data.heroText?\"main-title\":null}},[n(\"header\",{staticClass:\"hero\"},[t.data.heroImage?n(\"img\",{attrs:{src:t.$withBase(t.data.heroImage),alt:t.data.heroAlt||\"hero\"}}):t._e(),t._v(\" \"),null!==t.data.heroText?n(\"h1\",{attrs:{id:\"main-title\"}},[t._v(\"\\n      \"+t._s(t.data.heroText||t.$title||\"Hello\")+\"\\n    \")]):t._e(),t._v(\" \"),null!==t.data.tagline?n(\"p\",{staticClass:\"description\"},[t._v(\"\\n      \"+t._s(t.data.tagline||t.$description||\"Welcome to your VuePress site\")+\"\\n    \")]):t._e(),t._v(\" \"),t.data.actionText&&t.data.actionLink?n(\"p\",{staticClass:\"action\"},[n(\"NavLink\",{staticClass:\"action-button\",attrs:{item:t.actionLink}})],1):t._e()]),t._v(\" \"),t.data.features&&t.data.features.length?n(\"div\",{staticClass:\"features\"},t._l(t.data.features,(function(e,i){return n(\"div\",{key:i,staticClass:\"feature\"},[n(\"h2\",[t._v(t._s(e.title))]),t._v(\" \"),n(\"p\",[t._v(t._s(e.details))])])})),0):t._e(),t._v(\" \"),n(\"Content\",{staticClass:\"theme-default-content custom\"}),t._v(\" \"),t.data.footer?n(\"div\",{staticClass:\"footer\"},[t._v(\"\\n    \"+t._s(t.data.footer)+\"\\n  \")]):t._e()],1)}),[],!1,null,null,null);e.a=a.exports},716:function(t,e,n){\"use strict\";var i=n(332),r=n(333),a={name:\"Sidebar\",components:{SidebarLinks:i.default,NavLinks:r.a},props:[\"items\"]},s=(n(358),n(45)),o=Object(s.a)(a,(function(){var t=this.$createElement,e=this._self._c||t;return e(\"aside\",{staticClass:\"sidebar\"},[e(\"NavLinks\"),this._v(\" \"),this._t(\"top\"),this._v(\" \"),e(\"SidebarLinks\",{attrs:{depth:0,items:this.items}}),this._v(\" \"),this._t(\"bottom\")],2)}),[],!1,null,null,null);e.a=o.exports}}]);"
  },
  {
    "path": "docs/assets/js/20.6c5c8986.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[20],{651:function(t,n,a){t.exports=a.p+\"assets/img/5-10.62f0e5d6.png\"},652:function(t,n){t.exports=\"data:image/png;base64,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\"},653:function(t,n){t.exports=\"data:image/png;base64,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\"},654:function(t,n){t.exports=\"data:image/png;base64,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\"},655:function(t,n){t.exports=\"data:image/png;base64,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\"},656:function(t,n){t.exports=\"data:image/png;base64,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\"},907:function(t,n,a){\"use strict\";a.r(n);var s=a(45),r=Object(s.a)({},(function(){var t=this,n=t.$createElement,s=t._self._c||n;return s(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[s(\"h1\",{attrs:{id:\"_5-4-变换-transform\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_5-4-变换-transform\"}},[t._v(\"#\")]),t._v(\" 5.4 变换（Transform）\")]),t._v(\" \"),s(\"p\",[s(\"code\",[t._v(\"Transform\")]),t._v(\"可以在其子组件绘制时对其应用一些矩阵变换来实现一些特效。\"),s(\"code\",[t._v(\"Matrix4\")]),t._v(\"是一个4D矩阵，通过它我们可以实现各种矩阵操作，下面是一个例子：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"black\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Transform\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    alignment\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"topRight\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//相对于坐标系原点的对齐方式\")]),t._v(\"\\n    transform\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Matrix4\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"skewY\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.3\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//沿Y轴倾斜0.3弧度\")]),t._v(\"\\n    child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Container\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      padding\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"8.0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"deepOrange\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Apartment for rent!'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"运行效果如图5-10所示：\")]),t._v(\" \"),s(\"p\",[s(\"img\",{attrs:{src:a(651),alt:\"图5-10\"}})]),t._v(\" \"),s(\"blockquote\",[s(\"p\",[t._v(\"关于矩阵变换的相关内容属于线性代数范畴，本书不做讨论，读者有兴趣可以自行了解。本书中，我们把焦点放在Flutter中一些常见的变换效果上。另外，由于矩阵变化时发生在绘制时，而无需重新布局和构建等过程，所以性能很好。\")])]),t._v(\" \"),s(\"h3\",{attrs:{id:\"平移\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#平移\"}},[t._v(\"#\")]),t._v(\" 平移\")]),t._v(\" \"),s(\"p\",[s(\"code\",[t._v(\"Transform.translate\")]),t._v(\"接收一个\"),s(\"code\",[t._v(\"offset\")]),t._v(\"参数，可以在绘制时沿\"),s(\"code\",[t._v(\"x\")]),t._v(\"、\"),s(\"code\",[t._v(\"y\")]),t._v(\"轴对子组件平移指定的距离。\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecoratedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//默认原点为左上角，左移20像素，向上平移5像素  \")]),t._v(\"\\n  child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Transform\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"translate\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    offset\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"5.0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hello world\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"效果如图5-11所示：\")]),t._v(\" \"),s(\"p\",[s(\"img\",{attrs:{src:a(652),alt:\"图5-11\"}})]),t._v(\" \"),s(\"h3\",{attrs:{id:\"旋转\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#旋转\"}},[t._v(\"#\")]),t._v(\" 旋转\")]),t._v(\" \"),s(\"p\",[s(\"code\",[t._v(\"Transform.rotate\")]),t._v(\"可以对子组件进行旋转变换，如：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecoratedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Transform\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"rotate\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//旋转90度\")]),t._v(\"\\n    angle\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"math\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"pi\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hello world\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"；\\n\")])])]),s(\"blockquote\",[s(\"p\",[t._v(\"注意：要使用\"),s(\"code\",[t._v(\"math.pi\")]),t._v(\"需先进行如下导包。\")])]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'dart:math'\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"as\")]),t._v(\" math\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"  \\n\")])])]),s(\"p\",[t._v(\"效果如图5-12所示：\")]),t._v(\" \"),s(\"p\",[s(\"img\",{attrs:{src:a(653),alt:\"图5-12\"}})]),t._v(\" \"),s(\"h3\",{attrs:{id:\"缩放\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#缩放\"}},[t._v(\"#\")]),t._v(\" 缩放\")]),t._v(\" \"),s(\"p\",[s(\"code\",[t._v(\"Transform.scale\")]),t._v(\"可以对子组件进行缩小或放大，如：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecoratedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Transform\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"scale\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      scale\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.5\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//放大到1.5倍\")]),t._v(\"\\n      child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hello world\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"效果如图5-13所示：\")]),t._v(\" \"),s(\"p\",[s(\"img\",{attrs:{src:a(654),alt:\"图5-13\"}})]),t._v(\" \"),s(\"h3\",{attrs:{id:\"注意\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#注意\"}},[t._v(\"#\")]),t._v(\" 注意\")]),t._v(\" \"),s(\"ul\",[s(\"li\",[s(\"p\",[s(\"code\",[t._v(\"Transform\")]),t._v(\"的变换是应用在绘制阶段，而并不是应用在布局(layout)阶段，所以无论对子组件应用何种变化，其占用空间的大小和在屏幕上的位置都是固定不变的，因为这些是在布局阶段就确定的。下面我们具体说明：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  mainAxisAlignment\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  children\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecoratedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Transform\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"scale\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"scale\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.5\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hello world\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"你好\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" style\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"green\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" fontSize\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"18.0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"运行效果如图5-14所示：\")]),t._v(\" \"),s(\"p\",[s(\"img\",{attrs:{src:a(655),alt:\"图5-14\"}})]),t._v(\" \"),s(\"p\",[t._v(\"由于第一个\"),s(\"code\",[t._v(\"Text\")]),t._v(\"应用变换(放大)后，其在绘制时会放大，但其占用的空间依然为红色部分，所以第二个\"),s(\"code\",[t._v(\"Text\")]),t._v(\"会紧挨着红色部分，最终就会出现文字重合。\")])]),t._v(\" \"),s(\"li\",[s(\"p\",[t._v(\"由于矩阵变化只会作用在绘制阶段，所以在某些场景下，在UI需要变化时，可以直接通过矩阵变化来达到视觉上的UI改变，而不需要去重新触发build流程，这样会节省layout的开销，所以性能会比较好。如之前介绍的\"),s(\"code\",[t._v(\"Flow\")]),t._v(\"组件，它内部就是用矩阵变换来更新UI，除此之外，Flutter的动画组件中也大量使用了\"),s(\"code\",[t._v(\"Transform\")]),t._v(\"以提高性能。\")])])]),t._v(\" \"),s(\"blockquote\",[s(\"p\",[t._v(\"思考题：使用\"),s(\"code\",[t._v(\"Transform\")]),t._v(\"对其子组件先进行平移然后再旋转和先旋转再平移，两者最终的效果一样吗？为什么？\")])]),t._v(\" \"),s(\"h3\",{attrs:{id:\"rotatedbox\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#rotatedbox\"}},[t._v(\"#\")]),t._v(\" RotatedBox\")]),t._v(\" \"),s(\"p\",[s(\"code\",[t._v(\"RotatedBox\")]),t._v(\"和\"),s(\"code\",[t._v(\"Transform.rotate\")]),t._v(\"功能相似，它们都可以对子组件进行旋转变换，但是有一点不同：\"),s(\"code\",[t._v(\"RotatedBox\")]),t._v(\"的变换是在layout阶段，会影响在子组件的位置和大小。我们将上面介绍\"),s(\"code\",[t._v(\"Transform.rotate\")]),t._v(\"时的示例改一下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  mainAxisAlignment\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  children\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecoratedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//将Transform.rotate换成RotatedBox  \")]),t._v(\"\\n      child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RotatedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        quarterTurns\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//旋转90度(1/4圈)\")]),t._v(\"\\n        child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hello world\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"你好\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" style\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"green\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" fontSize\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"18.0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"效果如图5-15所示：\")]),t._v(\" \"),s(\"p\",[s(\"img\",{attrs:{src:a(656),alt:\"图5-15\"}})]),t._v(\" \"),s(\"p\",[t._v(\"由于\"),s(\"code\",[t._v(\"RotatedBox\")]),t._v(\"是作用于layout阶段，所以子组件会旋转90度（而不只是绘制的内容），\"),s(\"code\",[t._v(\"decoration\")]),t._v(\"会作用到子组件所占用的实际空间上，所以最终就是上图的效果，读者可以和前面\"),s(\"code\",[t._v(\"Transform.rotate\")]),t._v(\"示例对比理解。\")])])}),[],!1,null,null,null);n.default=r.exports}}]);"
  },
  {
    "path": "docs/assets/js/200.92165c5b.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[200],{866:function(t,e,r){\"use strict\";r.r(e);var l=r(45),n=Object(l.a)({},(function(){var t=this,e=t.$createElement,r=t._self._c||e;return r(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[r(\"h1\",{attrs:{id:\"本章目录\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本章目录\"}},[t._v(\"#\")]),t._v(\" 本章目录\")]),t._v(\" \"),r(\"ul\",[r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/v2/chapter14/flutter_ui_system.html\"}},[t._v(\"Flutter UI系统\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/v2/chapter14/element_buildcontext.html\"}},[t._v(\"Element和BuildContext\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/v2/chapter14/render_object.html\"}},[t._v(\"RenderObject和RenderBox\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/v2/chapter14/flutter_app_startup.html\"}},[t._v(\"Flutter从启动到显示\")])],1)])])}),[],!1,null,null,null);e.default=n.exports}}]);"
  },
  {
    "path": "docs/assets/js/201.2953b2e7.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[201],{868:function(t,s,n){\"use strict\";n.r(s);var a=n(45),e=Object(a.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_15-2-flutter-app代码结构\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_15-2-flutter-app代码结构\"}},[t._v(\"#\")]),t._v(\" 15.2 Flutter APP代码结构\")]),t._v(\" \"),n(\"p\",[t._v('我们先来创建一个全新的Flutter工程，命名为\"github_client_app\"；创建新工程的步骤视读者使用的编辑器而定，都比较简单，在此不再赘述。创建完成后，工程结构如下：')]),t._v(\" \"),n(\"div\",{staticClass:\"language-shell extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[n(\"code\",[t._v(\"github_client_app\\n├── android\\n├── ios\\n├── lib\\n└── \"),n(\"span\",{pre:!0,attrs:{class:\"token builtin class-name\"}},[t._v(\"test\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"由于我们需要使用外部图片和Icon资源，所以我们在项目根目录下分别创建“imgs”和“fonts”文件夹，前者用于保存图片，后者用于保存Icon文件。关于图片和Icon，读者可以参考第三章中相应的内容。\")]),t._v(\" \"),n(\"p\",[t._v(\"由于在网络数据传输和持久化时，我们需要通过Json来传输、保存数据；但是在应用开发时我们又需要将Json转成Dart Model类，现在我们使用在第十一章中“Json转Model”小节中介绍的方案，所以，我们需要在根目录下再创建一个用于保存Json文件的“jsons”文件夹。\")]),t._v(\" \"),n(\"p\",[t._v(\"多语言支持我们使用第十三章“国际化”中介绍的方案，所以还需要在根目录下创建一个“l10n”文件夹，用于保存各国语言对应的arb文件。\")]),t._v(\" \"),n(\"p\",[t._v(\"现在工程目录变为：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-shell extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[n(\"code\",[t._v(\"github_client_app\\n├── android\\n├── fonts\\n├── l10n-arb\\n├── imgs\\n├── ios\\n├── jsons\\n├── lib\\n└── \"),n(\"span\",{pre:!0,attrs:{class:\"token builtin class-name\"}},[t._v(\"test\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"由于我们的Dart代码都在“lib”文件夹下，笔者根据技术选型和经验在lib文件下创建了如下目录：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-shell extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[n(\"code\",[t._v(\"lib\\n├── common\\n├── l10n\\n├── models\\n├── states\\n├── routes\\n└── widgets \\n\")])])]),n(\"table\",[n(\"thead\",[n(\"tr\",[n(\"th\",[t._v(\"文件夹\")]),t._v(\" \"),n(\"th\",[t._v(\"作用\")])])]),t._v(\" \"),n(\"tbody\",[n(\"tr\",[n(\"td\",[t._v(\"common\")]),t._v(\" \"),n(\"td\",[t._v(\"一些工具类，如通用方法类、网络接口类、保存全局变量的静态类等\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"l10n\")]),t._v(\" \"),n(\"td\",[t._v(\"国际化相关的类都在此目录下\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"models\")]),t._v(\" \"),n(\"td\",[t._v(\"Json文件对应的Dart Model类会在此目录下\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"states\")]),t._v(\" \"),n(\"td\",[t._v(\"保存APP中需要跨组件共享的状态类\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"routes\")]),t._v(\" \"),n(\"td\",[t._v(\"存放所有路由页面类\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"widgets\")]),t._v(\" \"),n(\"td\",[t._v(\"APP内封装的一些Widget组件都在该目录下\")])])])]),t._v(\" \"),n(\"p\",[t._v(\"注意，使用不同的框架或技术选型会对代码有不同的组织方式，因此，本节介绍的代码组织结构并不是固定或者“最佳”的，在实战中，读者可以自己根据情况调整源码结构。但是无论采取何种源码组织结构，清晰和解耦都是一个通用原则，我们应该让自己的代码结构清晰，以便交流和维护。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/202.de6ee6b8.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[202],{870:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_15-4-全局变量及共享状态\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_15-4-全局变量及共享状态\"}},[t._v(\"#\")]),t._v(\" 15.4 全局变量及共享状态\")]),t._v(\" \"),a(\"p\",[t._v(\"应用程序中通常会包含一些贯穿APP生命周期的变量信息，这些信息在APP大多数地方可能都会被用到，比如当前用户信息、Local信息等。在Flutter中我们把需要全局共享的信息分为两类：全局变量和共享状态。全局变量就是单纯指会贯穿整个APP生命周期的变量，用于单纯的保存一些信息，或者封装一些全局工具和方法的对象。而共享状态则是指哪些需要跨组件或跨路由共享的信息，这些信息通常也是全局变量，而共享状态和全局变量的不同在于前者发生改变时需要通知所有使用该状态的组件，而后者不需要。为此，我们将全局变量和共享状态分开单独管理。\")]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_15-4-1-全局变量-global类\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_15-4-1-全局变量-global类\"}},[t._v(\"#\")]),t._v(\" 15.4.1 全局变量-Global类\")]),t._v(\" \"),a(\"p\",[t._v(\"我们在“lib/common”目录下创建一个\"),a(\"code\",[t._v(\"Global\")]),t._v(\"类，它主要管理APP的全局变量，定义如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 提供五套可选主题色\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" _themes \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"MaterialColor\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n  Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cyan\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"teal\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"green\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Global\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" SharedPreferences _prefs\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" Profile profile \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Profile\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 网络缓存对象\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" NetCache netCache \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"NetCache\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 可选的主题列表\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" List\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"MaterialColor\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" themes \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _themes\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 是否为release版\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" bool \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" isRelease \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" bool\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fromEnvironment\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"dart.vm.product\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//初始化全局信息，会在APP启动时执行\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" Future \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"init\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    _prefs \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" SharedPreferences\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getInstance\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" _profile \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _prefs\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getString\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"profile\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_profile \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        profile \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fromJson\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"jsonDecode\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"catch\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 如果没有缓存策略，设置默认缓存策略\")]),t._v(\"\\n    profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cache \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cache \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CacheConfig\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"enable \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"maxAge \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3600\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"maxCount \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//初始化网络请求相关配置\")]),t._v(\"\\n    Git\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"init\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 持久化Profile信息\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"saveProfile\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"\\n      _prefs\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setString\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"profile\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"jsonEncode\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toJson\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"Global类的各个字段的意义都有注释，在此不再赘述，需要注意的是\"),a(\"code\",[t._v(\"init()\")]),t._v(\"需要在App启动时就要执行，所以应用的\"),a(\"code\",[t._v(\"main\")]),t._v(\"方法如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"main\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Global\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"init\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"runApp\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyApp\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"在此，一定要确保\"),a(\"code\",[t._v(\"Global.init()\")]),t._v(\"方法不能抛出异常，否则 \"),a(\"code\",[t._v(\"runApp(MyApp())\")]),t._v(\"根本执行不到。\")]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_15-4-2-共享状态\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_15-4-2-共享状态\"}},[t._v(\"#\")]),t._v(\" 15.4.2 共享状态\")]),t._v(\" \"),a(\"p\",[t._v(\"有了全局变量，我们还需要考虑如何跨组件共享状态。当然，如果我们将要共享的状态全部用全局变量替代也是可以的，但是这在Flutter开发中并不是一个好主意，因为组件的状态是和UI相关，而在状态改变时我们会期望依赖该状态的UI组件会自动更新，如果使用全局变量，那么我们必须得去手动处理状态变动通知、接收机制以及变量和组件依赖关系。因此，本实例中，我们使用前面介绍过的Provider包来实现跨组件状态共享，因此我们需要定义相关的Provider。在本实例中，需要共享的状态有登录用户信息、APP主题信息、APP语言信息。由于这些信息改变后都要立即通知其它依赖的该信息的Widget更新，所以我们应该使用\"),a(\"code\",[t._v(\"ChangeNotifierProvider\")]),t._v(\"，另外，这些信息改变后都是需要更新Profile信息并进行持久化的。综上所述，我们可以定义一个\"),a(\"code\",[t._v(\"ProfileChangeNotifier\")]),t._v(\"基类，然后让需要共享的Model继承自该类即可，\"),a(\"code\",[t._v(\"ProfileChangeNotifier\")]),t._v(\"定义如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ProfileChangeNotifier\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ChangeNotifier\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  Profile \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" _profile \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Global\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"notifyListeners\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Global\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"saveProfile\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//保存Profile变更\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"notifyListeners\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//通知依赖的Widget更新\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"h3\",{attrs:{id:\"用户状态\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#用户状态\"}},[t._v(\"#\")]),t._v(\" 用户状态\")]),t._v(\" \"),a(\"p\",[t._v(\"用户状态在登录状态发生变化时更新、通知其依赖项，我们定义如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"UserModel\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ProfileChangeNotifier\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  User \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" user \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"user\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// APP是否登录(如果有用户信息，则证明登录过)\")]),t._v(\"\\n  bool \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" isLogin \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" user \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//用户信息发生变化，更新用户信息并通知依赖它的子孙Widgets更新\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"set\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"user\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"User user\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"user\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"login \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" _profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"user\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"login\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"lastLogin \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"user\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"login\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      _profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"user \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" user\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"notifyListeners\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"h3\",{attrs:{id:\"app主题状态\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#app主题状态\"}},[t._v(\"#\")]),t._v(\" APP主题状态\")]),t._v(\" \"),a(\"p\",[t._v(\"主题状态在用户更换APP主题时更新、通知其依赖项，定义如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ThemeModel\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ProfileChangeNotifier\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 获取当前主题，如果为设置主题，则默认使用蓝色主题\")]),t._v(\"\\n  ColorSwatch \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" theme \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Global\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"themes\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"firstWhere\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" _profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"theme\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" orElse\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 主题改变后，通知其依赖项，新主题会立即生效\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"set\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"theme\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ColorSwatch color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" theme\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"theme \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"500\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"notifyListeners\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"h3\",{attrs:{id:\"app语言状态\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#app语言状态\"}},[t._v(\"#\")]),t._v(\" APP语言状态\")]),t._v(\" \"),a(\"p\",[t._v(\"当APP语言选为跟随系统（Auto）时，在系通语言改变时，APP语言会更新；当用户在APP中选定了具体语言时（美国英语或中文简体），则APP便会一直使用用户选定的语言，不会再随系统语言而变。语言状态类定义如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"LocaleModel\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ProfileChangeNotifier\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 获取当前用户的APP语言配置Locale类，如果为null，则语言跟随系统语言\")]),t._v(\"\\n  Locale \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getLocale\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"locale \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" t \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"locale\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"split\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"_\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Locale\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"t\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" t\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 获取当前Locale的字符串表示\")]),t._v(\"\\n  String \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" locale \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"locale\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 用户改变APP语言后，通知依赖项更新，新语言会立即生效\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"set\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"locale\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String locale\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"locale \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" _profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"locale\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"locale \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" locale\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"notifyListeners\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/203.ac627436.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[203],{871:function(t,_,v){\"use strict\";v.r(_);var l=v(45),i=Object(l.a)({},(function(){var t=this,_=t.$createElement,v=t._self._c||_;return v(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[v(\"h1\",{attrs:{id:\"_15-1-github客户端示例\"}},[v(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_15-1-github客户端示例\"}},[t._v(\"#\")]),t._v(\" 15.1 Github客户端示例\")]),t._v(\" \"),v(\"p\",[t._v(\"本章新建一个Flutter工程，实现一个简单的Github客户端。这个实例的主要目标有两个：\")]),t._v(\" \"),v(\"ol\",[v(\"li\",[t._v(\"带领读者了解如何使用Flutter来开发一个完整APP，了解Flutter应用开发流程及工程结构等。\")]),t._v(\" \"),v(\"li\",[t._v(\"对前面章节所学内容的一个应用及总结。\")])]),t._v(\" \"),v(\"p\",[t._v(\"需要注意的是，由于Github本身功能非常多，我们的焦点并不是去实现Github的所有业务功能。因此，我们只需要实现一个APP的骨架，能达到上面这两点即可。下面对我们要实现的功能如下：\")]),t._v(\" \"),v(\"ol\",[v(\"li\",[t._v(\"实现Github账号登录、退出登录功能\")]),t._v(\" \"),v(\"li\",[t._v(\"登录后可以查看自己的项目主页\")]),t._v(\" \"),v(\"li\",[t._v(\"支持换肤\")]),t._v(\" \"),v(\"li\",[t._v(\"支持多语言\")]),t._v(\" \"),v(\"li\",[t._v(\"登录状态可以持久化；\")])]),t._v(\" \"),v(\"p\",[t._v(\"要实现上面这些功能会涉及到如下技术点：\")]),t._v(\" \"),v(\"ol\",[v(\"li\",[t._v(\"网络请求；需要请求Github API。\")]),t._v(\" \"),v(\"li\",[t._v(\"Json转Dart Model类；\")]),t._v(\" \"),v(\"li\",[t._v(\"全局状态管理；语言、主题、登录态等都需要全局共享。\")]),t._v(\" \"),v(\"li\",[t._v(\"持久化存储；保存登录信息，用户信息等。\")]),t._v(\" \"),v(\"li\",[t._v(\"支持国际化、Intl包的使用\")])]),t._v(\" \"),v(\"p\",[t._v(\"现在，目标已经确定，在接下来章节中，我们将分模块一步一步实现上述功能。\")])])}),[],!1,null,null,null);_.default=i.exports}}]);"
  },
  {
    "path": "docs/assets/js/204.fb9289cb.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[204],{874:function(t,s,a){\"use strict\";a.r(s);var r=a(45),n=Object(r.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_15-3-model类定义\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_15-3-model类定义\"}},[t._v(\"#\")]),t._v(\" 15.3 Model类定义\")]),t._v(\" \"),a(\"p\",[t._v(\"本节我们先梳理一下APP中将用到的数据，然后生成相应的Dart Model类。Json文件转Dart Model的方案采用前面介绍过的 json_model 包方案\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"github账号信息\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#github账号信息\"}},[t._v(\"#\")]),t._v(\" Github账号信息\")]),t._v(\" \"),a(\"p\",[t._v(\"登录Github后，我们需要获取当前登录者的Github账号信息，Github API接口返回Json结构如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-json extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-json\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"login\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"octocat\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//用户登录名\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"avatar_url\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"https://github.com/images/error/octocat_happy.gif\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//用户头像地址\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"type\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"User\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//用户类型，可能是组织\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"name\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"monalisa octocat\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//用户名字\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"company\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"GitHub\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//公司\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"blog\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"https://github.com/blog\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//博客地址\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"location\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"San Francisco\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 用户所处地理位置\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"email\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"octocat@github.com\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 邮箱\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"hireable\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"bio\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"There once was...\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 用户简介\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"public_repos\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 公开项目数\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"followers\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//关注该用户的人数\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"following\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 该用户关注的人数\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"created_at\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"2008-01-14T04:33:35Z\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 账号创建时间\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"updated_at\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"2008-01-14T04:33:35Z\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 账号信息更新时间\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"total_private_repos\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//该用户总的私有项目数(包括参与的其它组织的私有项目)\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"owned_private_repos\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//该用户自己的私有项目数\")]),t._v(\"\\n  ... \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略其它字段\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"我们在“jsons”目录下创建一个“user.json”文件保存上述信息。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"api缓存策略信息\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#api缓存策略信息\"}},[t._v(\"#\")]),t._v(\" API缓存策略信息\")]),t._v(\" \"),a(\"p\",[t._v(\"由于Github服务器在国内访问速度较慢，我们对Github API应用一些简单的缓存策略。我们在“jsons”目录下创建一个“cacheConfig.json”文件缓存策略信息，定义如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-json extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-json\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"enable\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 是否启用缓存\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"maxAge\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1000\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 缓存的最长时间，单位（秒）\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"maxCount\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 最大缓存数\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"h3\",{attrs:{id:\"用户信息\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#用户信息\"}},[t._v(\"#\")]),t._v(\" 用户信息\")]),t._v(\" \"),a(\"p\",[t._v(\"用户信息(Profile)应包括如下信息：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"Github账号信息；由于我们的APP可以切换账号登录，且登录后再次打开则不需要登录，所以我们需要对用户账号信息和登录状态进行持久化。\")]),t._v(\" \"),a(\"li\",[t._v(\"应用使用配置信息；每一个用户都应有自己的APP配置信息，如主题、语言、以及数据缓存策略等。\")]),t._v(\" \"),a(\"li\",[t._v(\"用户注销登录后，为了便于用户在退出APP前再次登录，我们需要记住上次登录的用户名。\")])]),t._v(\" \"),a(\"p\",[t._v(\"需要注意的是，目前Github有三种登录方式，分别是账号密码登录、oauth授权登录、二次认证登录；这三种登录方式的安全性依次加强，但是在本示例中，为了简单起见，我们使用账号密码登录，因此我们需要保存用户的密码。\")]),t._v(\" \"),a(\"blockquote\",[a(\"p\",[t._v(\"注意：在这里需要提醒读者，在登录场景中，保护用户账号安全是一个非常重要且永恒的话题，在实际开发中应严格杜绝直接明文存储用户账密的行为。\")])]),t._v(\" \"),a(\"p\",[t._v(\"我们在“jsons”目录下创建一个“profile.json”文件，结构如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-json extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-json\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"user\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$user\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v('//Github账号信息，结构见\"user.json\"')]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"token\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 登录用户的token(oauth)或密码\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"theme\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"5678\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//主题色值\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"cache\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$cacheConfig\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v('// 缓存策略信息，结构见\"cacheConfig.json\"')]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"lastLogin\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//最近一次的注销登录的用户名\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"locale\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"\"')]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// APP语言信息\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"h3\",{attrs:{id:\"项目信息\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#项目信息\"}},[t._v(\"#\")]),t._v(\" 项目信息\")]),t._v(\" \"),a(\"p\",[t._v(\"由于APP主页要显示其所有项目信息，我们在“jsons”目录下创建一个“repo.json”文件保存项目信息。通过参考Github 获取项目信息的API文档，定义出最终的“repo.json”文件结构，如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-json extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-json\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"id\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1296269\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"name\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hello-World\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//项目名称\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"full_name\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"octocat/Hello-World\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//项目完整名称\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"owner\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$user\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v('// 项目拥有者，结构见\"user.json\"')]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"parent\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$repo\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 如果是fork的项目，则此字段表示fork的父项目信息\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"private\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 是否私有项目\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"description\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"This your first repo!\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//项目描述\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"fork\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 该项目是否为fork的项目\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"language\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"JavaScript\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//该项目的主要编程语言\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"forks_count\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"9\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// fork了该项目的数量\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"stargazers_count\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//该项目的star数量\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"size\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"108\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 项目占用的存储大小\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"default_branch\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"master\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//项目的默认分支\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"open_issues_count\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//该项目当前打开的issue数量\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"pushed_at\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"2011-01-26T19:06:43Z\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"created_at\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"2011-01-26T19:01:12Z\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"updated_at\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"2011-01-26T19:14:43Z\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"subscribers_count\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"42\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//订阅（关注）该项目的人数\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"license\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 该项目的开源许可证\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"key\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"mit\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"name\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"MIT License\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"spdx_id\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"MIT\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"url\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"https://api.github.com/licenses/mit\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"node_id\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"MDc6TGljZW5zZW1pdA==\"')]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  ...\"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略其它字段\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"h3\",{attrs:{id:\"生成dart-model类\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#生成dart-model类\"}},[t._v(\"#\")]),t._v(\" 生成Dart Model类\")]),t._v(\" \"),a(\"p\",[t._v(\"现在，我们需要的Json数据已经定义完毕，现在只需要运行json_model package提供的命令来通过json文件生成相应的Dart类：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-shell extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[a(\"code\",[t._v(\"flutter packages pub run json_model\\n\")])])]),a(\"p\",[t._v(\"命令执行成功后，可以看到lib/models文件夹下会生成相应的Dart Model类：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language- extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[a(\"code\",[t._v(\"├── models\\n│   ├── cacheConfig.dart\\n│   ├── cacheConfig.g.dart\\n│   ├── index.dart\\n│   ├── profile.dart\\n│   ├── profile.g.dart\\n│   ├── repo.dart\\n│   ├── repo.g.dart\\n│   ├── user.dart\\n│   └── user.g.dart\\n\\n\")])])]),a(\"h3\",{attrs:{id:\"数据持久化\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#数据持久化\"}},[t._v(\"#\")]),t._v(\" 数据持久化\")]),t._v(\" \"),a(\"p\",[t._v(\"我们使用shared_preferences包来对登录用户的Profile信息进行持久化。shared_preferences是一个Flutter插件，它通过Android和iOS平台提供的机制来实现数据持久化。由于shared_preferences的使用非常简单，读者可以自行查看其文档，在此不再赘述。\")])])}),[],!1,null,null,null);s.default=n.exports}}]);"
  },
  {
    "path": "docs/assets/js/205.90d0284c.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[205],{875:function(t,s,a){\"use strict\";a.r(s);var n=a(45),p=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_15-5-网络请求封装\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_15-5-网络请求封装\"}},[t._v(\"#\")]),t._v(\" 15.5 网络请求封装\")]),t._v(\" \"),a(\"p\",[t._v(\"本节我们会基于前面介绍过的dio网络库封装APP中用到的网络请求接口，并同时应用一个简单的缓存策略。下面我们先介绍一下网络接口缓存原理，然后再封装APP的业务请求接口。\")]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_15-5-1-网络接口缓存\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_15-5-1-网络接口缓存\"}},[t._v(\"#\")]),t._v(\" 15.5.1 网络接口缓存\")]),t._v(\" \"),a(\"p\",[t._v(\"由于在国内访问Github服务器速度较慢，所以我们应用一些简单的缓存策略：将请求的url作为key，对请求的返回值在一个指定时间段类进行缓存，另外设置一个最大缓存数，当超过最大缓存数后移除最早的一条缓存。但是也得提供一种针对特定接口或请求决定是否启用缓存的机制，这种机制可以指定哪些接口或那次请求不应用缓存，这种机制是很有必要的，比如登录接口就不应该缓存，又比如用户在下拉刷新时就不应该再应用缓存。在实现缓存之前我们先定义保存缓存信息的\"),a(\"code\",[t._v(\"CacheObject\")]),t._v(\"类：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CacheObject\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CacheObject\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" timeStamp \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" DateTime\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"now\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"millisecondsSinceEpoch\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  Response response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  int timeStamp\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 缓存创建时间\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  bool \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"operator\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"other\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"hashCode \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" other\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"hashCode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//将请求uri作为缓存的key\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  int \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" hashCode \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"realUri\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"hashCode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"接下来我们需要实现具体的缓存策略，由于我们使用的是dio package，所以我们可以直接通过拦截器来实现缓存策略：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'dart:collection'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:dio/dio.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'../index.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CacheObject\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CacheObject\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" timeStamp \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" DateTime\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"now\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"millisecondsSinceEpoch\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  Response response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  int timeStamp\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  bool \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"operator\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"other\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"hashCode \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" other\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"hashCode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  int \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" hashCode \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"realUri\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"hashCode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"NetCache\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Interceptor\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 为确保迭代器顺序和对象插入时间一致顺序一致，我们使用LinkedHashMap\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" cache \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" LinkedHashMap\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" CacheObject\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onRequest\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"RequestOptions options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"Global\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cache\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"enable\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v('// refresh标记是否是\"下拉刷新\"')]),t._v(\"\\n    bool refresh \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"extra\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"refresh\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//如果是下拉刷新，先删除相关缓存\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"refresh\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"extra\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"list\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//若是列表，则只要url中包含当前path的缓存全部删除（简单实现，并不精准）\")]),t._v(\"\\n        cache\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"removeWhere\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" v\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"contains\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"path\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 如果不是列表，则只删除uri相同的缓存\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"delete\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"uri\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"extra\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"noCache\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\"\\n        options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"method\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toLowerCase\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'get'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      String key \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"extra\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"cacheKey\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"uri\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" ob \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" cache\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ob \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//若缓存未过期，则返回缓存内容\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"DateTime\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"now\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"millisecondsSinceEpoch \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" ob\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"timeStamp\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1000\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"\\n            Global\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cache\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"maxAge\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" cache\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//若已过期则删除缓存，继续向服务器请求\")]),t._v(\"\\n          cache\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"remove\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onError\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"DioError err\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 错误状态不缓存\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onResponse\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Response response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 如果启用缓存，将返回结果保存到缓存\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Global\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cache\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"enable\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_saveCache\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"response\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_saveCache\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Response object\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    RequestOptions options \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" object\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"request\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"extra\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"noCache\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\"\\n        options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"method\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toLowerCase\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"get\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 如果缓存数量超过最大数量限制，则先移除最早的一条记录\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"cache\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"length \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" Global\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cache\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"maxCount\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        cache\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"remove\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"cache\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"cache\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"keys\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"first\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n      String key \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"extra\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"cacheKey\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"uri\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      cache\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CacheObject\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"object\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"delete\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    cache\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"remove\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"关于代码的解释都在注释中了，在此需要说明的是dio包的\"),a(\"code\",[t._v(\"option.extra\")]),t._v(\"是专门用于扩展请求参数的，我们通过定义了“refresh”和“noCache”两个参数实现了“针对特定接口或请求决定是否启用缓存的机制”，这两个参数含义如下：\")]),t._v(\" \"),a(\"table\",[a(\"thead\",[a(\"tr\",[a(\"th\",[t._v(\"参数名\")]),t._v(\" \"),a(\"th\",[t._v(\"类型\")]),t._v(\" \"),a(\"th\",[t._v(\"解释\")])])]),t._v(\" \"),a(\"tbody\",[a(\"tr\",[a(\"td\",[t._v(\"refresh\")]),t._v(\" \"),a(\"td\",[t._v(\"bool\")]),t._v(\" \"),a(\"td\",[t._v(\"如果为true，则本次请求不使用缓存，但新的请求结果依然会被缓存\")])]),t._v(\" \"),a(\"tr\",[a(\"td\",[t._v(\"noCache\")]),t._v(\" \"),a(\"td\",[t._v(\"bool\")]),t._v(\" \"),a(\"td\",[t._v(\"本次请求禁用缓存，请求结果也不会被缓存。\")])])])]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_15-5-2-封装网络请求\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_15-5-2-封装网络请求\"}},[t._v(\"#\")]),t._v(\" 15.5.2 封装网络请求\")]),t._v(\" \"),a(\"p\",[t._v(\"一个完整的APP，可能会涉及很多网络请求，为了便于管理、收敛请求入口，工程上最好的作法就是将所有网络请求放到同一个源码文件中。由于我们的接口都是请求的Github 开发平台提供的API，所以我们定义一个Git类，专门用于Github API接口调用。另外，在调试过程中，我们通常需要一些工具来查看网络请求、响应报文，使用网络代理工具来调试网络数据问题是主流方式。配置代理需要在应用中指定代理服务器的地址和端口，另外Github API是HTTPS协议，所以在配置完代理后还应该禁用证书校验，这些配置我们在Git类初始化时执行（\"),a(\"code\",[t._v(\"init()方法\")]),t._v(\"）。下面是Git类的源码：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'dart:async'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'dart:convert'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'dart:io'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:dio/dio.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:dio/adapter.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'../index.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Git\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 在网络请求过程中可能会需要使用当前的context信息，比如在请求失败时\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 打开一个新路由，而打开新路由需要context信息。\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Git\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    _options \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Options\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"extra\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"context\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  Options _options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" Dio dio \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Dio\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BaseOptions\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    baseUrl\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'https://api.github.com/'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    headers\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      HttpHeaders\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"acceptHeader\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"application/vnd.github.squirrel-girl-preview,\"')]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"application/vnd.github.symmetra-preview+json\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"init\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 添加缓存插件\")]),t._v(\"\\n    dio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"interceptors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Global\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"netCache\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 设置用户token（可能为null，代表未登录）\")]),t._v(\"\\n    dio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"headers\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"HttpHeaders\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"authorizationHeader\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Global\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"token\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 在调试模式下需要抓包调试，所以我们使用代理，并禁用HTTPS证书校验\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"Global\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isRelease\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"dio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"httpClientAdapter \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"as\")]),t._v(\" DefaultHttpClientAdapter\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onHttpClientCreate \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"client\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        client\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"findProxy \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"uri\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"PROXY 10.1.10.250:8888\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//代理工具会提供一个抓包的自签名证书，会通不过证书校验，所以我们禁用证书校验\")]),t._v(\"\\n        client\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"badCertificateCallback \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"X509Certificate cert\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" String host\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int port\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 登录接口，登录成功后返回用户信息\")]),t._v(\"\\n  Future\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"User\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"login\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String login\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" String pwd\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    String basic \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Basic '\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" base64\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"encode\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"utf8\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"encode\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'$login:$pwd'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" r \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" dio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"/users/$login\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"merge\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"headers\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        HttpHeaders\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"authorizationHeader\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" basic\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" extra\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"noCache\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//本接口禁用缓存\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//登录成功后更新公共头（authorization），此后的所有请求都会带上用户身份信息\")]),t._v(\"\\n    dio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"headers\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"HttpHeaders\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"authorizationHeader\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" basic\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//清空所有缓存\")]),t._v(\"\\n    Global\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"netCache\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cache\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"clear\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//更新profile中的token信息\")]),t._v(\"\\n    Global\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"token \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" basic\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" User\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fromJson\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"r\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//获取用户项目列表\")]),t._v(\"\\n  Future\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"List\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Repo\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">>\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getRepos\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"Map\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" queryParameters\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//query参数，用于接收分页信息\")]),t._v(\"\\n      refresh \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"refresh\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 列表下拉刷新，需要删除缓存（拦截器中会读取这些信息）\")]),t._v(\"\\n      _options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"extra\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addAll\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"refresh\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"list\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" r \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" dio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"List\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"user/repos\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      queryParameters\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" queryParameters\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _options\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" r\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"map\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Repo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fromJson\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toList\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"可以看到我们在\"),a(\"code\",[t._v(\"init()\")]),t._v(\"方法中，我们判断了是否是调试环境，然后做了一些针对调试环境的网络配置（设置代理和禁用证书校验）。而\"),a(\"code\",[t._v(\"Git.init()\")]),t._v(\"方法是应用启动时被调用的（\"),a(\"code\",[t._v(\"Global.init()\")]),t._v(\"方法中会调用\"),a(\"code\",[t._v(\"Git.init()\")]),t._v(\"）。\")]),t._v(\" \"),a(\"p\",[t._v(\"另外需要注意，我们所有的网络请求是通过同一个\"),a(\"code\",[t._v(\"dio\")]),t._v(\"实例（静态变量）发出的，在创建该\"),a(\"code\",[t._v(\"dio\")]),t._v(\"实例时我们将Github API的基地址和API支持的Header进行了全局配置，这样所有通过该\"),a(\"code\",[t._v(\"dio\")]),t._v(\"实例发出的请求都会默认使用者些配置。\")]),t._v(\" \"),a(\"p\",[t._v(\"在本实例中，我们只用到了登录接口和获取用户项目的接口，所以在\"),a(\"code\",[t._v(\"Git\")]),t._v(\"类中只定义了\"),a(\"code\",[t._v(\"login(…)\")]),t._v(\"和\"),a(\"code\",[t._v(\"getRepos(…)\")]),t._v(\"方法，如果读者要在本实例的基础上扩充功能，读者可以将其它的接口请求方法添加到\"),a(\"code\",[t._v(\"Git\")]),t._v(\"类中，这样便实现了网络请求接口在代码层面的集中管理和维护。\")])])}),[],!1,null,null,null);s.default=p.exports}}]);"
  },
  {
    "path": "docs/assets/js/206.2dbac1ab.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[206],{877:function(t,e,a){\"use strict\";a.r(e);var s=a(45),n=Object(s.a)({},(function(){var t=this,e=t.$createElement,a=t._self._c||e;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_2-5-调试flutter应用\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_2-5-调试flutter应用\"}},[t._v(\"#\")]),t._v(\" 2.5 调试Flutter应用\")]),t._v(\" \"),a(\"p\",[t._v(\"有各种各样的工具和功能来帮助调试Flutter应用程序。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"dart-分析器\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#dart-分析器\"}},[t._v(\"#\")]),t._v(\" Dart 分析器\")]),t._v(\" \"),a(\"p\",[t._v(\"在运行应用程序前，请运行\"),a(\"code\",[t._v(\"flutter analyze\")]),t._v(\"测试你的代码。这个工具是一个静态代码检查工具，它是\"),a(\"code\",[t._v(\"dartanalyzer\")]),t._v(\"工具的一个包装，主要用于分析代码并帮助开发者发现可能的错误，比如，Dart分析器大量使用了代码中的类型注释来帮助追踪问题，避免\"),a(\"code\",[t._v(\"var\")]),t._v(\"、无类型的参数、无类型的列表文字等。\")]),t._v(\" \"),a(\"p\",[t._v(\"如果你使用IntelliJ的Flutter插件，那么分析器在打开IDE时就已经自动启用了，如果读者使用的是其它IDE，强烈建议读者启用Dart 分析器，因为在大多数时候，Dart 分析器可以在代码运行前发现大多数问题。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"dart-observatory-语句级的单步调试和分析器\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#dart-observatory-语句级的单步调试和分析器\"}},[t._v(\"#\")]),t._v(\" Dart Observatory (语句级的单步调试和分析器)\")]),t._v(\" \"),a(\"p\",[t._v(\"如果我们使用\"),a(\"code\",[t._v(\"flutter run\")]),t._v(\"启动应用程序，那么当它运行时，我们可以打开Observatory工具的Web页面，例如Observatory默认监听\"),a(\"a\",{attrs:{href:\"http://127.0.0.1:8100/\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"http://127.0.0.1:8100/\"),a(\"OutboundLink\")],1),t._v(\"，可以在浏览器中直接打开该链接。直接使用语句级单步调试器连接到您的应用程序。如果您使用的是IntelliJ，则还可以使用其内置的调试器来调试您的应用程序。\")]),t._v(\" \"),a(\"p\",[t._v(\"Observatory 同时支持分析、检查堆等。有关Observatory的更多信息请参考\"),a(\"a\",{attrs:{href:\"https://dart-lang.github.io/observatory/\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Observatory 文档\"),a(\"OutboundLink\")],1),t._v(\"。\")]),t._v(\" \"),a(\"p\",[t._v(\"如果您使用Observatory进行分析，请确保通过\"),a(\"code\",[t._v(\"--profile\")]),t._v(\"选项来运行\"),a(\"code\",[t._v(\"flutter run\")]),t._v(\"命令来运行应用程序。 否则，配置文件中将出现的主要问题将是调试断言，以验证框架的各种不变量（请参阅下面的“调试模式断言”）。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"debugger-声明\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#debugger-声明\"}},[t._v(\"#\")]),t._v(\" \"),a(\"code\",[t._v(\"debugger()\")]),t._v(\" 声明\")]),t._v(\" \"),a(\"p\",[t._v(\"当使用Dart Observatory（或另一个Dart调试器，例如IntelliJ IDE中的调试器）时，可以使用该\"),a(\"code\",[t._v(\"debugger()\")]),t._v(\"语句插入编程式断点。要使用这个，你必须添加\"),a(\"code\",[t._v(\"import 'dart:developer';\")]),t._v(\"到相关文件顶部。\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"debugger()\")]),t._v(\"语句采用一个可选\"),a(\"code\",[t._v(\"when\")]),t._v(\"参数，您可以指定该参数仅在特定条件为真时中断，如下所示：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"someFunction\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"double offset\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"debugger\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"when\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" offset \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"30.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// ...\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"h3\",{attrs:{id:\"print、debugprint、flutter-logs\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#print、debugprint、flutter-logs\"}},[t._v(\"#\")]),t._v(\" \"),a(\"code\",[t._v(\"print\")]),t._v(\"、\"),a(\"code\",[t._v(\"debugPrint\")]),t._v(\"、\"),a(\"code\",[t._v(\"flutter logs\")])]),t._v(\" \"),a(\"p\",[t._v(\"Dart \"),a(\"code\",[t._v(\"print()\")]),t._v(\"功能将输出到系统控制台，您可以使用\"),a(\"code\",[t._v(\"flutter logs\")]),t._v(\"来查看它（基本上是一个包装\"),a(\"code\",[t._v(\"adb logcat\")]),t._v(\"）。\")]),t._v(\" \"),a(\"p\",[t._v(\"如果你一次输出太多，那么Android有时会丢弃一些日志行。为了避免这种情况，您可以使用Flutter的\"),a(\"code\",[t._v(\"foundation\")]),t._v(\"库中的\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/foundation/debugPrint.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"debugPrint()\")]),a(\"OutboundLink\")],1),t._v(\"。 这是一个封装print，它将输出限制在一个级别，避免被Android内核丢弃。\")]),t._v(\" \"),a(\"p\",[t._v(\"Flutter框架中的许多类都有\"),a(\"code\",[t._v(\"toString\")]),t._v(\"实现。按照惯例，这些输出通常包括对象的\"),a(\"code\",[t._v(\"runtimeType\")]),t._v(\"单行输出，通常在表单中ClassName(more information about this instance…)。 树中使用的一些类也具有\"),a(\"code\",[t._v(\"toStringDeep\")]),t._v(\"，从该点返回整个子树的多行描述。已一些具有详细信息\"),a(\"code\",[t._v(\"toString\")]),t._v(\"的类会实现一个\"),a(\"code\",[t._v(\"toStringShort\")]),t._v(\"，它只返回对象的类型或其他非常简短的（一个或两个单词）描述。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"调试模式断言\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#调试模式断言\"}},[t._v(\"#\")]),t._v(\" 调试模式断言\")]),t._v(\" \"),a(\"p\",[t._v(\"在Flutter应用调试过程中，Dart \"),a(\"code\",[t._v(\"assert\")]),t._v(\"语句被启用，并且Flutter框架使用它来执行许多运行时检查来验证是否违反一些不可变的规则。\")]),t._v(\" \"),a(\"p\",[t._v(\"当一个不可变的规则被违反时，它被报告给控制台，并带有一些上下文信息来帮助追踪问题的根源。\")]),t._v(\" \"),a(\"p\",[t._v(\"要关闭调试模式并使用发布模式，请使用\"),a(\"code\",[t._v(\"flutter run --release\")]),t._v(\"运行您的应用程序。 这也关闭了Observatory调试器。一个中间模式可以关闭除Observatory之外所有调试辅助工具的，称为“profile mode”，用\"),a(\"code\",[t._v(\"--profile\")]),t._v(\"替代\"),a(\"code\",[t._v(\"--release\")]),t._v(\"即可。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"调试应用程序层\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#调试应用程序层\"}},[t._v(\"#\")]),t._v(\" 调试应用程序层\")]),t._v(\" \"),a(\"p\",[t._v(\"Flutter框架的每一层都提供了将其当前状态或事件转储(dump)到控制台（使用\"),a(\"code\",[t._v(\"debugPrint\")]),t._v(\"）的功能。\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"widget-树\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#widget-树\"}},[t._v(\"#\")]),t._v(\" Widget 树\")]),t._v(\" \"),a(\"p\",[t._v(\"要转储Widgets树的状态，请调用\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/debugDumpApp.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"debugDumpApp()\")]),a(\"OutboundLink\")],1),t._v(\"。 只要应用程序已经构建了至少一次（即在调用\"),a(\"code\",[t._v(\"build()\")]),t._v(\"之后的任何时间），您可以在应用程序未处于构建阶段（即，不在\"),a(\"code\",[t._v(\"build()\")]),t._v(\"方法内调用 ）的任何时间调用此方法（在调用\"),a(\"code\",[t._v(\"runApp()\")]),t._v(\"之后）。\")]),t._v(\" \"),a(\"p\",[t._v(\"如, 这个应用程序:\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"main\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"runApp\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MaterialApp\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      home\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AppHome\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AppHome\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Material\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Center\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FlatButton\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"debugDumpApp\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Dump App'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"…会输出这样的内容（精确的细节会根据框架的版本、设备的大小等等而变化）：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-shell extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[a(\"code\",[t._v(\"I/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\": WidgetsFlutterBinding - CHECKED MODE\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\": RenderObjectToWidgetAdapter\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"RenderBox\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"GlobalObjectKey RenderView\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"497039273\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" renderObject: RenderView\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\": └MaterialApp\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"state: _MaterialAppState\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1009803148\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"))\")]),t._v(\"\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":  └ScrollConfiguration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":   └AnimatedTheme\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"duration: 200ms\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" state: _AnimatedThemeState\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"543295893\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" ticker inactive\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" ThemeDataTween\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ThemeData\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Brightness.light Color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"0xff2196f3\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" etc\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"..\")]),t._v(\".\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" → null\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"))\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":    └Theme\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ThemeData\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Brightness.light Color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"0xff2196f3\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" etc\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"..\")]),t._v(\".\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"))\")]),t._v(\"\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":     └WidgetsApp\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"GlobalObjectKey _MaterialAppState\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1009803148\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" state: _WidgetsAppState\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"552902158\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"))\")]),t._v(\"\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":      └CheckedModeBanner\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":       └Banner\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":        └CustomPaint\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"renderObject: RenderCustomPaint\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":         └DefaultTextStyle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"inherit: \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" color: Color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"0xd0ff0000\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" family: \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"monospace\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" size: \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"48.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" weight: \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"900\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" decoration: double Color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"0xffffff00\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" TextDecoration.underline\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":          └MediaQuery\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"MediaQueryData\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"size: Size\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"411.4\")]),t._v(\", \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"683.4\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\", devicePixelRatio: \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2.625\")]),t._v(\", textScaleFactor: \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.0\")]),t._v(\", padding: EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),t._v(\", \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"24.0\")]),t._v(\", \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),t._v(\", \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"))\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":           └LocaleQuery\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"null\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":            └Title\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color: Color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"0xff2196f3\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"))\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"..\")]),t._v(\". \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"#省略剩余内容\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"这是一个“扁平化”的树，显示了通过各种构建函数投影的所有widget（如果你在widget树的根中调用\"),a(\"code\",[t._v(\"toStringDeepwidget\")]),t._v(\"，这是你获得的树）。 你会看到很多在你的应用源代码中没有出现的widget，因为它们是被框架中widget的\"),a(\"code\",[t._v(\"build()\")]),t._v(\"函数插入的。例如，\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/material/InkFeature-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"InkFeature\")]),a(\"OutboundLink\")],1),t._v(\"是Material widget的一个实现细节 。\")]),t._v(\" \"),a(\"p\",[t._v(\"当按钮从被按下变为被释放时debugDumpApp()被调用，FlatButton对象同时调用\"),a(\"code\",[t._v(\"setState()\")]),t._v('，并将自己标记为\"dirty\"。 这就是为什么如果你看转储，你会看到特定的对象标记为“dirty”。您还可以查看已注册了哪些手势监听器; 在这种情况下，一个单一的GestureDetector被列出，并且监听“tap”手势（“tap”是'),a(\"code\",[t._v(\"TapGestureDetector\")]),t._v(\"的\"),a(\"code\",[t._v(\"toStringShort\")]),t._v(\"函数输出的）\")]),t._v(\" \"),a(\"p\",[t._v(\"如果您编写自己的widget，则可以通过覆盖\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/Widget/debugFillProperties.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"debugFillProperties()\")]),a(\"OutboundLink\")],1),t._v(\"来添加信息。 将\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/foundation/DiagnosticsProperty-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"DiagnosticsProperty\"),a(\"OutboundLink\")],1),t._v(\"对象作为方法参数，并调用父类方法。 该函数是该\"),a(\"code\",[t._v(\"toString\")]),t._v(\"方法用来填充小部件描述信息的。\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"渲染树\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#渲染树\"}},[t._v(\"#\")]),t._v(\" 渲染树\")]),t._v(\" \"),a(\"p\",[t._v(\"如果您尝试调试布局问题，那么Widget树可能不够详细。在这种情况下，您可以通过调用\"),a(\"code\",[t._v(\"debugDumpRenderTree()\")]),t._v(\"转储渲染树。 正如\"),a(\"code\",[t._v(\"debugDumpApp()\")]),t._v(\"，除布局或绘制阶段外，您可以随时调用此函数。作为一般规则，从\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/scheduler/SchedulerBinding/addPersistentFrameCallback.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"frame 回调\"),a(\"OutboundLink\")],1),t._v(\" 或事件处理器中调用它是最佳解决方案。\")]),t._v(\" \"),a(\"p\",[t._v(\"要调用\"),a(\"code\",[t._v(\"debugDumpRenderTree()\")]),t._v(\"，您需要添加\"),a(\"code\",[t._v(\"import'package:flutter/rendering.dart';\")]),t._v(\"到您的源文件。\")]),t._v(\" \"),a(\"p\",[t._v(\"上面这个小例子的输出结果如下所示：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-shell extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[a(\"code\",[t._v(\"I/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\": RenderView\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":  │ debug mode enabled - android\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":  │ window size: Size\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1080.0\")]),t._v(\", \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1794.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"in physical pixels\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":  │ device pixel ratio: \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2.625\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"physical pixels per logical pixel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":  │ configuration: Size\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"411.4\")]),t._v(\", \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"683.4\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" at \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),t._v(\".625x \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"in logical pixels\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":  │\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":  └─child: RenderCustomPaint\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":    │ creator: CustomPaint ← Banner ← CheckedModeBanner ←\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":    │   WidgetsApp-\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"GlobalObjectKey _MaterialAppState\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1009803148\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" ←\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":    │   Theme ← AnimatedTheme ← ScrollConfiguration ← MaterialApp ←\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":    │   \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"root\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\"\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":    │ parentData: \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"none\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":    │ constraints: BoxConstraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"w\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"411.4\")]),t._v(\", \"),a(\"span\",{pre:!0,attrs:{class:\"token assign-left variable\"}},[t._v(\"h\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"683.4\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\nI/flutter \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6559\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\":    │ size: Size\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"411.4\")]),t._v(\", \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"683.4\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"..\")]),t._v(\". \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"# 省略\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"这是根\"),a(\"code\",[t._v(\"RenderObject\")]),t._v(\"对象的\"),a(\"code\",[t._v(\"toStringDeep\")]),t._v(\"函数的输出。\")]),t._v(\" \"),a(\"p\",[t._v(\"当调试布局问题时，关键要看的是\"),a(\"code\",[t._v(\"size\")]),t._v(\"和\"),a(\"code\",[t._v(\"constraints\")]),t._v(\"字段。约束沿着树向下传递，尺寸向上传递。\")]),t._v(\" \"),a(\"p\",[t._v(\"如果您编写自己的渲染对象，则可以通过覆盖\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/rendering/Layer/debugFillProperties.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"debugFillProperties()\")]),a(\"OutboundLink\")],1),t._v(\"将信息添加到转储。 将\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/foundation/DiagnosticsProperty-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"DiagnosticsProperty\"),a(\"OutboundLink\")],1),t._v(\"对象作为方法的参数，并调用父类方法。\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"layer树\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#layer树\"}},[t._v(\"#\")]),t._v(\" Layer树\")]),t._v(\" \"),a(\"p\",[t._v(\"读者可以理解为渲染树是可以分层的，而最终绘制需要将不同的层合成起来，而Layer则是绘制时需要合成的层，如果您尝试调试合成问题，则可以使用\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/rendering/debugDumpLayerTree.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"debugDumpLayerTree()\")]),a(\"OutboundLink\")],1),t._v(\"。对于上面的例子，它会输出：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language- extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[a(\"code\",[t._v(\"I/flutter : TransformLayer\\nI/flutter :  │ creator: [root]\\nI/flutter :  │ offset: Offset(0.0, 0.0)\\nI/flutter :  │ transform:\\nI/flutter :  │   [0] 3.5,0.0,0.0,0.0\\nI/flutter :  │   [1] 0.0,3.5,0.0,0.0\\nI/flutter :  │   [2] 0.0,0.0,1.0,0.0\\nI/flutter :  │   [3] 0.0,0.0,0.0,1.0\\nI/flutter :  │\\nI/flutter :  ├─child 1: OffsetLayer\\nI/flutter :  │ │ creator: RepaintBoundary ← _FocusScope ← Semantics ← Focus-[GlobalObjectKey MaterialPageRoute(560156430)] ← _ModalScope-[GlobalKey 328026813] ← _OverlayEntry-[GlobalKey 388965355] ← Stack ← Overlay-[GlobalKey 625702218] ← Navigator-[GlobalObjectKey _MaterialAppState(859106034)] ← Title ← ⋯\\nI/flutter :  │ │ offset: Offset(0.0, 0.0)\\nI/flutter :  │ │\\nI/flutter :  │ └─child 1: PictureLayer\\nI/flutter :  │\\nI/flutter :  └─child 2: PictureLayer\\n\")])])]),a(\"p\",[t._v(\"这是根\"),a(\"code\",[t._v(\"Layer\")]),t._v(\"的\"),a(\"code\",[t._v(\"toStringDeep\")]),t._v(\"输出的。\")]),t._v(\" \"),a(\"p\",[t._v(\"根部的变换是应用设备像素比的变换; 在这种情况下，每个逻辑像素代表3.5个设备像素。\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"RepaintBoundary\")]),t._v(\" widget在渲染树的层中创建了一个\"),a(\"code\",[t._v(\"RenderRepaintBoundary\")]),t._v(\"。这用于减少需要重绘的需求量。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"语义\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#语义\"}},[t._v(\"#\")]),t._v(\" 语义\")]),t._v(\" \"),a(\"p\",[t._v(\"您还可以调用\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/rendering/debugDumpSemanticsTree.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"debugDumpSemanticsTree()\")]),a(\"OutboundLink\")],1),t._v(\"获取语义树（呈现给系统可访问性API的树）的转储。 要使用此功能，必须首先启用辅助功能，例如启用系统辅助工具或\"),a(\"code\",[t._v(\"SemanticsDebugger\")]),t._v(\" （下面讨论）。\")]),t._v(\" \"),a(\"p\",[t._v(\"对于上面的例子，它会输出:\")]),t._v(\" \"),a(\"div\",{staticClass:\"language- extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[a(\"code\",[t._v('I/flutter : SemanticsNode(0; Rect.fromLTRB(0.0, 0.0, 411.4, 683.4))\\nI/flutter :  ├SemanticsNode(1; Rect.fromLTRB(0.0, 0.0, 411.4, 683.4))\\nI/flutter :  │ └SemanticsNode(2; Rect.fromLTRB(0.0, 0.0, 411.4, 683.4); canBeTapped)\\nI/flutter :  └SemanticsNode(3; Rect.fromLTRB(0.0, 0.0, 411.4, 683.4))\\nI/flutter :    └SemanticsNode(4; Rect.fromLTRB(0.0, 0.0, 82.0, 36.0); canBeTapped; \"Dump App\")\\n')])])]),a(\"h3\",{attrs:{id:\"调度\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#调度\"}},[t._v(\"#\")]),t._v(\" 调度\")]),t._v(\" \"),a(\"p\",[t._v(\"要找出相对于帧的开始/结束事件发生的位置，可以切换\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/scheduler/debugPrintBeginFrameBanner.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"debugPrintBeginFrameBanner\")]),a(\"OutboundLink\")],1),t._v(\"和\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/scheduler/debugPrintEndFrameBanner.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"debugPrintEndFrameBanner\")]),a(\"OutboundLink\")],1),t._v(\"布尔值以将帧的开始和结束打印到控制台。\")]),t._v(\" \"),a(\"p\",[t._v(\"例如:\")]),t._v(\" \"),a(\"div\",{staticClass:\"language- extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[a(\"code\",[t._v(\"I/flutter : ▄▄▄▄▄▄▄▄ Frame 12         30s 437.086ms ▄▄▄▄▄▄▄▄\\nI/flutter : Debug print: Am I performing this work more than once per frame?\\nI/flutter : Debug print: Am I performing this work more than once per frame?\\nI/flutter : ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀\\n\")])])]),a(\"p\",[a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/scheduler/debugPrintScheduleFrameStacks.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"debugPrintScheduleFrameStacks\")]),a(\"OutboundLink\")],1),t._v(\"还可以用来打印导致当前帧被调度的调用堆栈。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"可视化调试\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#可视化调试\"}},[t._v(\"#\")]),t._v(\" 可视化调试\")]),t._v(\" \"),a(\"p\",[t._v(\"您也可以通过设置\"),a(\"code\",[t._v(\"debugPaintSizeEnabled\")]),t._v(\"为\"),a(\"code\",[t._v(\"true\")]),t._v(\"以可视方式调试布局问题。 这是来自\"),a(\"code\",[t._v(\"rendering\")]),t._v(\"库的布尔值。它可以在任何时候启用，并在为true时影响绘制。 设置它的最简单方法是在\"),a(\"code\",[t._v(\"void main()\")]),t._v(\"的顶部设置。\")]),t._v(\" \"),a(\"p\",[t._v(\"当它被启用时，所有的盒子都会得到一个明亮的深青色边框，padding（来自widget如Padding）显示为浅蓝色，子widget周围有一个深蓝色框， 对齐方式（来自widget如Center和Align）显示为黄色箭头. 空白（如没有任何子节点的Container）以灰色显示。\")]),t._v(\" \"),a(\"p\",[a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/rendering/debugPaintBaselinesEnabled.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"debugPaintBaselinesEnabled\")]),a(\"OutboundLink\")],1),t._v(\"做了类似的事情，但对于具有基线的对象，文字基线以绿色显示，表意(ideographic)基线以橙色显示。\")]),t._v(\" \"),a(\"p\",[a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/rendering/debugPaintPointersEnabled.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"debugPaintPointersEnabled\")]),a(\"OutboundLink\")],1),t._v(\"标志打开一个特殊模式，任何正在点击的对象都会以深青色突出显示。 这可以帮助您确定某个对象是否以某种不正确的方式进行hit测试（Flutter检测点击的位置是否有能响应用户操作的widget）,例如，如果它实际上超出了其父项的范围，首先不会考虑通过hit测试。\")]),t._v(\" \"),a(\"p\",[t._v(\"如果您尝试调试合成图层，例如以确定是否以及在何处添加\"),a(\"code\",[t._v(\"RepaintBoundary\")]),t._v(\" widget，则可以使用\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/rendering/debugPaintLayerBordersEnabled.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"debugPaintLayerBordersEnabled\")]),a(\"OutboundLink\")],1),t._v(\" 标志， 该标志用橙色或轮廓线标出每个层的边界，或者使用\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/rendering/debugRepaintRainbowEnabled.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"debugRepaintRainbowEnabled\")]),a(\"OutboundLink\")],1),t._v(\"标志， 只要他们重绘时，这会使该层被一组旋转色所覆盖。\")]),t._v(\" \"),a(\"p\",[t._v(\"所有这些标志只能在调试模式下工作。通常，Flutter框架中以“\"),a(\"code\",[t._v(\"debug...\")]),t._v(\"” 开头的任何内容都只能在调试模式下工作。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"调试动画\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#调试动画\"}},[t._v(\"#\")]),t._v(\" 调试动画\")]),t._v(\" \"),a(\"p\",[t._v(\"调试动画最简单的方法是减慢它们的速度。为此，请将\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/scheduler/timeDilation.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"timeDilation\")]),a(\"OutboundLink\")],1),t._v(\"变量（在scheduler库中）设置为大于1.0的数字，例如50.0。 最好在应用程序启动时只设置一次。如果您在运行中更改它，尤其是在动画运行时将其值改小，则在观察时可能会出现倒退，这可能会导致断言命中，并且这通常会干扰我们的开发工作。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"调试性能问题\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#调试性能问题\"}},[t._v(\"#\")]),t._v(\" 调试性能问题\")]),t._v(\" \"),a(\"p\",[t._v(\"要了解您的应用程序导致重新布局或重新绘制的原因，您可以分别设置\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/rendering/debugPrintMarkNeedsLayoutStacks.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"debugPrintMarkNeedsLayoutStacks\")]),a(\"OutboundLink\")],1),t._v(\"和 \"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/rendering/debugPrintMarkNeedsPaintStacks.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[a(\"code\",[t._v(\"debugPrintMarkNeedsPaintStacks\")]),a(\"OutboundLink\")],1),t._v(\"标志。 每当渲染盒被要求重新布局和重新绘制时，这些都会将堆栈跟踪记录到控制台。如果这种方法对您有用，您可以使用\"),a(\"code\",[t._v(\"services\")]),t._v(\"库中的\"),a(\"code\",[t._v(\"debugPrintStack()\")]),t._v(\"方法按需打印堆栈痕迹。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"统计应用启动时间\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#统计应用启动时间\"}},[t._v(\"#\")]),t._v(\" 统计应用启动时间\")]),t._v(\" \"),a(\"p\",[t._v(\"要收集有关Flutter应用程序启动所需时间的详细信息，可以在运行\"),a(\"code\",[t._v(\"flutter run\")]),t._v(\"时使用\"),a(\"code\",[t._v(\"trace-startup\")]),t._v(\"和\"),a(\"code\",[t._v(\"profile\")]),t._v(\"选项。\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-shell extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[a(\"code\",[t._v(\"$ flutter run --trace-startup --profile\\n\")])])]),a(\"p\",[t._v(\"跟踪输出保存为\"),a(\"code\",[t._v(\"start_up_info.json\")]),t._v(\"，在Flutter工程目录在build目录下。输出列出了从应用程序启动到这些跟踪事件（以微秒捕获）所用的时间：\")]),t._v(\" \"),a(\"ul\",[a(\"li\",[t._v(\"进入Flutter引擎时.\")]),t._v(\" \"),a(\"li\",[t._v(\"展示应用第一帧时.\")]),t._v(\" \"),a(\"li\",[t._v(\"初始化Flutter框架时.\")]),t._v(\" \"),a(\"li\",[t._v(\"完成Flutter框架初始化时.\")])]),t._v(\" \"),a(\"p\",[t._v(\"如 :\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-json extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-json\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"engineEnterTimestampMicros\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"96025565262\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"timeToFirstFrameMicros\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2171978\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"timeToFrameworkInitMicros\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"514585\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"timeAfterFrameworkInitMicros\"')]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1657393\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"h3\",{attrs:{id:\"跟踪dart代码性能\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#跟踪dart代码性能\"}},[t._v(\"#\")]),t._v(\" 跟踪Dart代码性能\")]),t._v(\" \"),a(\"p\",[t._v(\"要执行自定义性能跟踪和测量Dart任意代码段的wall/CPU时间（类似于在Android上使用\"),a(\"a\",{attrs:{href:\"https://developer.android.com/studio/profile/systrace.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"systrace\"),a(\"OutboundLink\")],1),t._v(\"）。 使用\"),a(\"code\",[t._v(\"dart:developer\")]),t._v(\"的\"),a(\"a\",{attrs:{href:\"https://api.dartlang.org/stable/dart-developer/Timeline-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Timeline\"),a(\"OutboundLink\")],1),t._v(\"工具来包含你想测试的代码块，例如：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[t._v(\"Timeline\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"startSync\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'interesting function'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// iWonderHowLongThisTakes();\")]),t._v(\"\\nTimeline\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"finishSync\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"然后打开你应用程序的Observatory timeline页面，在“Recorded Streams”中选择‘Dart’复选框，并执行你想测量的功能。\")]),t._v(\" \"),a(\"p\",[t._v(\"刷新页面将在Chrome的\"),a(\"a\",{attrs:{href:\"https://www.chromium.org/developers/how-tos/trace-event-profiling-tool\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"跟踪工具\"),a(\"OutboundLink\")],1),t._v(\"中显示应用按时间顺序排列的timeline记录。\")]),t._v(\" \"),a(\"p\",[t._v(\"请确保运行\"),a(\"code\",[t._v(\"flutter run\")]),t._v(\"时带有\"),a(\"code\",[t._v(\"--profile\")]),t._v(\"标志，以确保运行时性能特征与您的最终产品差异最小。\")])])}),[],!1,null,null,null);e.default=n.exports}}]);"
  },
  {
    "path": "docs/assets/js/207.d052f90b.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[207],{881:function(t,r,e){\"use strict\";e.r(r);var a=e(45),l=Object(a.a)({},(function(){var t=this,r=t.$createElement,e=t._self._c||r;return e(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[e(\"h2\",{attrs:{id:\"简介\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#简介\"}},[t._v(\"#\")]),t._v(\" 简介\")]),t._v(\" \"),e(\"p\",[t._v(\"本章将通过一些简单的示例来一步步介绍Flutter的开发流程.\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"本章目录\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本章目录\"}},[t._v(\"#\")]),t._v(\" 本章目录\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter2/first_flutter_app.html\"}},[t._v(\"2.1：计数器示例\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter2/flutter_router.html\"}},[t._v(\"2.2：路由管理\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter2/flutter_package_mgr.html\"}},[t._v(\"2.3：包管理\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter2/flutter_assets_mgr.html\"}},[t._v(\"2.4：资源管理\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter2/flutter_app_debug.html\"}},[t._v(\"2.5：调试Flutter APP\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter2/thread_model_and_error_report.html\"}},[t._v(\"2.6：Dart线程模型及异常捕获\")])],1)])])}),[],!1,null,null,null);r.default=l.exports}}]);"
  },
  {
    "path": "docs/assets/js/208.0818aeb7.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[208],{885:function(t,e,r){\"use strict\";r.r(e);var a=r(45),i=Object(a.a)({},(function(){var t=this,e=t.$createElement,r=t._self._c||e;return r(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[r(\"h1\",{attrs:{id:\"基础widget\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#基础widget\"}},[t._v(\"#\")]),t._v(\" 基础Widget\")]),t._v(\" \"),r(\"p\",[t._v(\"本节介绍一下Flutter中常用的一些基础widget，由于大多数widget的属性都比较多，我们在介绍widget时会着重介绍常用的属性，而不会像API文档一样所有属性都介绍，关于属性详细的信息请参考Flutter SDK文档。\")]),t._v(\" \"),r(\"h2\",{attrs:{id:\"本章目录\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本章目录\"}},[t._v(\"#\")]),t._v(\" 本章目录\")]),t._v(\" \"),r(\"ul\",[r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/v2/chapter3/flutter_widget_intro.html\"}},[t._v(\"3.1：Widget简介\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/v2/chapter3/state_manage.html\"}},[t._v(\"3.2：状态管理\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/v2/chapter3/text.html\"}},[t._v(\"3.3：文本、字体样式\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/v2/chapter3/buttons.html\"}},[t._v(\"3.4：按钮\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/v2/chapter3/img_and_icon.html\"}},[t._v(\"3.5：图片和Icon\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/v2/chapter3/radio_and_checkbox.html\"}},[t._v(\"3.6：单选框和复选框\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/v2/chapter3/input_and_form.html\"}},[t._v(\"3.7：输入框和表单\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/v2/chapter3/progress.html\"}},[t._v(\"3.8：进度指示器\")])],1)])])}),[],!1,null,null,null);e.default=i.exports}}]);"
  },
  {
    "path": "docs/assets/js/209.95753ceb.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[209],{893:function(t,r,a){\"use strict\";a.r(r);var e=a(45),l=Object(e.a)({},(function(){var t=this,r=t.$createElement,a=t._self._c||r;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"本章目录\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本章目录\"}},[t._v(\"#\")]),t._v(\" 本章目录\")]),t._v(\" \"),a(\"ul\",[a(\"li\",[a(\"RouterLink\",{attrs:{to:\"/v2/chapter4/intro.html\"}},[t._v(\"4.1：布局类组件简介\")])],1),t._v(\" \"),a(\"li\",[a(\"RouterLink\",{attrs:{to:\"/v2/chapter4/row_and_column.html\"}},[t._v(\"4.2：线性布局（Row、Column）\")])],1),t._v(\" \"),a(\"li\",[a(\"RouterLink\",{attrs:{to:\"/v2/chapter4/flex.html\"}},[t._v(\"4.3：弹性布局（Flex）\")])],1),t._v(\" \"),a(\"li\",[a(\"RouterLink\",{attrs:{to:\"/v2/chapter4/wrap_and_flow.html\"}},[t._v(\"4.4：流式布局（Wrap、Flow）\")])],1),t._v(\" \"),a(\"li\",[a(\"RouterLink\",{attrs:{to:\"/v2/chapter4/stack.html\"}},[t._v(\"4.5：层叠布局（Stack、Positioned）\")])],1),t._v(\" \"),a(\"li\",[a(\"RouterLink\",{attrs:{to:\"/v2/chapter4/alignment.html\"}},[t._v(\"4.6：对齐与相对定位（Align）\")])],1)])])}),[],!1,null,null,null);r.default=l.exports}}]);"
  },
  {
    "path": "docs/assets/js/21.c0363ec5.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[21],{411:function(t,a,s){t.exports=s.p+\"assets/img/3-1.587e85ad.png\"},412:function(t,a,s){t.exports=s.p+\"assets/img/3-1-1.63daca67.png\"},413:function(t,a,s){t.exports=s.p+\"assets/img/3-2.a59bef97.jpg\"},414:function(t,a,s){t.exports=s.p+\"assets/img/3-1-2.70506d6d.png\"},415:function(t,a,s){t.exports=s.p+\"assets/img/3-3.3b5b2dd3.png\"},773:function(t,a,s){\"use strict\";s.r(a);var e=s(45),n=Object(e.a)({},(function(){var t=this,a=t.$createElement,e=t._self._c||a;return e(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[e(\"h1\",{attrs:{id:\"_3-1-widget简介\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-1-widget简介\"}},[t._v(\"#\")]),t._v(\" 3.1 Widget简介\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"_3-1-1-概念\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-1-1-概念\"}},[t._v(\"#\")]),t._v(\" 3.1.1 概念\")]),t._v(\" \"),e(\"p\",[t._v(\"在前面的介绍中，我们知道在Flutter中几乎所有的对象都是一个Widget。与原生开发中“控件”不同的是，Flutter中的Widget的概念更广泛，它不仅可以表示UI元素，也可以表示一些功能性的组件如：用于手势检测的 \"),e(\"code\",[t._v(\"GestureDetector\")]),t._v(\" widget、用于APP主题数据传递的\"),e(\"code\",[t._v(\"Theme\")]),t._v(\"等等，而原生开发中的控件通常只是指UI元素。在后面的内容中，我们在描述UI元素时可能会用到“控件”、“组件”这样的概念，读者心里需要知道他们就是widget，只是在不同场景的不同表述而已。由于Flutter主要就是用于构建用户界面的，所以，在大多数时候，读者可以认为widget就是一个控件，不必纠结于概念。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"_3-1-2-widget与element\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-1-2-widget与element\"}},[t._v(\"#\")]),t._v(\" 3.1.2 Widget与Element\")]),t._v(\" \"),e(\"p\",[t._v(\"在Flutter中，Widget的功能是“描述一个UI元素的配置数据”，它就是说，Widget其实并不是表示最终绘制在设备屏幕上的显示元素，而它只是描述显示元素的一个配置数据。\")]),t._v(\" \"),e(\"p\",[t._v(\"实际上，Flutter中真正代表屏幕上显示元素的类是\"),e(\"code\",[t._v(\"Element\")]),t._v(\"，也就是说Widget只是描述\"),e(\"code\",[t._v(\"Element\")]),t._v(\"的配置数据！有关\"),e(\"code\",[t._v(\"Element\")]),t._v(\"的详细介绍我们将在本书后面的高级部分深入介绍，现在，读者只需要知道：\"),e(\"strong\",[t._v(\"Widget只是UI元素的一个配置数据，并且一个Widget可以对应多个\"),e(\"code\",[t._v(\"Element\")])]),t._v(\"。这是因为同一个Widget对象可以被添加到UI树的不同部分，而真正渲染时，UI树的每一个\"),e(\"code\",[t._v(\"Element\")]),t._v(\"节点都会对应一个Widget对象。总结一下：\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"Widget实际上就是\"),e(\"code\",[t._v(\"Element\")]),t._v(\"的配置数据，Widget树实际上是一个配置树，而真正的UI渲染树是由\"),e(\"code\",[t._v(\"Element\")]),t._v(\"构成；不过，由于\"),e(\"code\",[t._v(\"Element\")]),t._v(\"是通过Widget生成的，所以它们之间有对应关系，在大多数场景，我们可以宽泛地认为Widget树就是指UI控件树或UI渲染树。\")]),t._v(\" \"),e(\"li\",[t._v(\"一个Widget对象可以对应多个\"),e(\"code\",[t._v(\"Element\")]),t._v(\"对象。这很好理解，根据同一份配置（Widget），可以创建多个实例（Element）。\")])]),t._v(\" \"),e(\"p\",[t._v(\"读者应该将这两点牢记在心中。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"_3-1-3-widget主要接口\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-1-3-widget主要接口\"}},[t._v(\"#\")]),t._v(\" 3.1.3 Widget主要接口\")]),t._v(\" \"),e(\"p\",[t._v(\"我们先来看一下Widget类的声明：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@immutable\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"abstract\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Widget\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"DiagnosticableTree\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Widget\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"key \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Key key\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@protected\")]),t._v(\"\\n  Element \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createElement\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  String \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toStringShort\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" key \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'$runtimeType'\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'$runtimeType-$key'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"debugFillProperties\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"DiagnosticPropertiesBuilder properties\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"debugFillProperties\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"properties\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    properties\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"defaultDiagnosticsTreeStyle \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" DiagnosticsTreeStyle\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dense\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" bool \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"canUpdate\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Widget oldWidget\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Widget newWidget\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" oldWidget\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"runtimeType \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" newWidget\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"runtimeType\\n        \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\" oldWidget\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"key \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" newWidget\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"key\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"ul\",[e(\"li\",[e(\"code\",[t._v(\"Widget\")]),t._v(\"类继承自\"),e(\"code\",[t._v(\"DiagnosticableTree\")]),t._v(\"，\"),e(\"code\",[t._v(\"DiagnosticableTree\")]),t._v(\"即“诊断树”，主要作用是提供调试信息。\")]),t._v(\" \"),e(\"li\",[e(\"code\",[t._v(\"Key\")]),t._v(\": 这个\"),e(\"code\",[t._v(\"key\")]),t._v(\"属性类似于React/Vue中的\"),e(\"code\",[t._v(\"key\")]),t._v(\"，主要的作用是决定是否在下一次\"),e(\"code\",[t._v(\"build\")]),t._v(\"时复用旧的widget，决定的条件在\"),e(\"code\",[t._v(\"canUpdate()\")]),t._v(\"方法中。\")]),t._v(\" \"),e(\"li\",[e(\"code\",[t._v(\"createElement()\")]),t._v(\"：正如前文所述“一个Widget可以对应多个\"),e(\"code\",[t._v(\"Element\")]),t._v(\"”；Flutter Framework在构建UI树时，会先调用此方法生成对应节点的\"),e(\"code\",[t._v(\"Element\")]),t._v(\"对象。此方法是Flutter Framework隐式调用的，在我们开发过程中基本不会调用到。\")]),t._v(\" \"),e(\"li\",[e(\"code\",[t._v(\"debugFillProperties(...)\")]),t._v(\" 复写父类的方法，主要是设置诊断树的一些特性。\")]),t._v(\" \"),e(\"li\",[e(\"code\",[t._v(\"canUpdate(...)\")]),t._v(\"是一个静态方法，它主要用于在Widget树重新\"),e(\"code\",[t._v(\"build\")]),t._v(\"时复用旧的widget，其实具体来说，应该是：是否用新的Widget对象去更新旧UI树上所对应的\"),e(\"code\",[t._v(\"Element\")]),t._v(\"对象的配置；通过其源码我们可以看到，只要\"),e(\"code\",[t._v(\"newWidget\")]),t._v(\"与\"),e(\"code\",[t._v(\"oldWidget\")]),t._v(\"的\"),e(\"code\",[t._v(\"runtimeType\")]),t._v(\"和\"),e(\"code\",[t._v(\"key\")]),t._v(\"同时相等时就会用\"),e(\"code\",[t._v(\"newWidget\")]),t._v(\"去更新\"),e(\"code\",[t._v(\"Element\")]),t._v(\"对象的配置，否则就会创建新的\"),e(\"code\",[t._v(\"Element\")]),t._v(\"。\")])]),t._v(\" \"),e(\"p\",[t._v(\"有关Key和Widget复用的细节将会在本书后面高级部分深入讨论，读者现在只需知道，为Widget显式添加key的话可能（但不一定）会使UI在重新构建时变的高效，读者目前可以先忽略此参数。本书后面的示例中，只会在构建列表项UI时会显式指定Key。\")]),t._v(\" \"),e(\"p\",[t._v(\"另外\"),e(\"code\",[t._v(\"Widget\")]),t._v(\"类本身是一个抽象类，其中最核心的就是定义了\"),e(\"code\",[t._v(\"createElement()\")]),t._v(\"接口，在Flutter开发中，我们一般都不用直接继承\"),e(\"code\",[t._v(\"Widget\")]),t._v(\"类来实现一个新组件，相反，我们通常会通过继承\"),e(\"code\",[t._v(\"StatelessWidget\")]),t._v(\"或\"),e(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"来间接继承\"),e(\"code\",[t._v(\"Widget\")]),t._v(\"类来实现。\"),e(\"code\",[t._v(\"StatelessWidget\")]),t._v(\"和\"),e(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"都是直接继承自\"),e(\"code\",[t._v(\"Widget\")]),t._v(\"类，而这两个类也正是Flutter中非常重要的两个抽象类，它们引入了两种Widget模型，接下来我们将重点介绍一下这两个类。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"_3-1-4-statelesswidget\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-1-4-statelesswidget\"}},[t._v(\"#\")]),t._v(\" 3.1.4 StatelessWidget\")]),t._v(\" \"),e(\"p\",[t._v(\"在之前的章节中，我们已经简单介绍过\"),e(\"code\",[t._v(\"StatelessWidget\")]),t._v(\"，\"),e(\"code\",[t._v(\"StatelessWidget\")]),t._v(\"相对比较简单，它继承自\"),e(\"code\",[t._v(\"Widget\")]),t._v(\"类，重写了\"),e(\"code\",[t._v(\"createElement()\")]),t._v(\"方法：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\nStatelessElement \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createElement\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessElement\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),e(\"p\",[e(\"code\",[t._v(\"StatelessElement\")]),t._v(\" 间接继承自\"),e(\"code\",[t._v(\"Element\")]),t._v(\"类，与\"),e(\"code\",[t._v(\"StatelessWidget\")]),t._v(\"相对应（作为其配置数据）。\")]),t._v(\" \"),e(\"p\",[e(\"code\",[t._v(\"StatelessWidget\")]),t._v(\"用于不需要维护状态的场景，它通常在\"),e(\"code\",[t._v(\"build\")]),t._v(\"方法中通过嵌套其它Widget来构建UI，在构建过程中会递归的构建其嵌套的Widget。我们看一个简单的例子：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Echo\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Echo\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"  \\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"text\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"backgroundColor\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"Colors\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"key\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String text\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Color backgroundColor\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        color\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" backgroundColor\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"text\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"上面的代码，实现了一个回显字符串的\"),e(\"code\",[t._v(\"Echo\")]),t._v(\" widget。\")]),t._v(\" \"),e(\"blockquote\",[e(\"p\",[t._v(\"按照惯例，\"),e(\"code\",[t._v(\"widget\")]),t._v(\"的构造函数参数应使用命名参数，命名参数中的必要参数要添加\"),e(\"code\",[t._v(\"@required\")]),t._v(\"标注，这样有利于静态代码分析器进行检查。另外，在继承\"),e(\"code\",[t._v(\"widget\")]),t._v(\"时，第一个参数通常应该是\"),e(\"code\",[t._v(\"Key\")]),t._v(\"，另外，如果Widget需要接收子Widget，那么\"),e(\"code\",[t._v(\"child\")]),t._v(\"或\"),e(\"code\",[t._v(\"children\")]),t._v(\"参数通常应被放在参数列表的最后。同样是按照惯例，Widget的属性应尽可能的被声明为\"),e(\"code\",[t._v(\"final\")]),t._v(\"，防止被意外改变。\")])]),t._v(\" \"),e(\"p\",[t._v(\"然后我们可以通过如下方式使用它：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[t._v(\"Widget \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Echo\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"text\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"hello world\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"运行后效果如图3-1所示：\")]),t._v(\" \"),e(\"p\",[e(\"img\",{attrs:{src:s(411),alt:\"图3-1\"}})]),t._v(\" \"),e(\"h3\",{attrs:{id:\"context\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#context\"}},[t._v(\"#\")]),t._v(\" Context\")]),t._v(\" \"),e(\"p\",[e(\"code\",[t._v(\"build\")]),t._v(\"方法有一个\"),e(\"code\",[t._v(\"context\")]),t._v(\"参数，它是\"),e(\"code\",[t._v(\"BuildContext\")]),t._v(\"类的一个实例，表示当前widget在widget树中的上下文，每一个widget都会对应一个context对象（因为每一个widget都是widget树上的一个节点）。实际上，\"),e(\"code\",[t._v(\"context\")]),t._v(\"是当前widget在widget树中位置中执行”相关操作“的一个句柄，比如它提供了从当前widget开始向上遍历widget树以及按照widget类型查找父级widget的方法。下面是在子树中获取父级widget的一个示例：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ContextRoute\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      appBar\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        title\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Context测试\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      body\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 在Widget树中向上查找最近的父级`Scaffold` widget\")]),t._v(\"\\n          Scaffold scaffold \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"findAncestorWidgetOfExactType\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Scaffold\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v('// 直接返回 AppBar的title， 此处实际上是Text(\"Context测试\")')]),t._v(\"\\n          \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"scaffold\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"appBar \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"as\")]),t._v(\" AppBar\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"title\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"运行后效果如图3-1-1所示：\")]),t._v(\" \"),e(\"p\",[e(\"img\",{attrs:{src:s(412),alt:\"图3-1-1\"}})]),t._v(\" \"),e(\"blockquote\",[e(\"p\",[e(\"strong\",[t._v(\"注意\")]),t._v(\"：对于\"),e(\"code\",[t._v(\"BuildContext\")]),t._v(\"读者现在可以先作了解，随着本书后面内容的展开，也会用到Context的一些方法，读者可以通过具体的场景对其有个直观的认识。关于\"),e(\"code\",[t._v(\"BuildContext\")]),t._v(\"更多的内容，我们也将在后面高级部分再深入介绍。\")])]),t._v(\" \"),e(\"h2\",{attrs:{id:\"_3-1-5-statefulwidget\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-1-5-statefulwidget\"}},[t._v(\"#\")]),t._v(\" 3.1.5 StatefulWidget\")]),t._v(\" \"),e(\"p\",[t._v(\"和\"),e(\"code\",[t._v(\"StatelessWidget\")]),t._v(\"一样，\"),e(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"也是继承自\"),e(\"code\",[t._v(\"Widget\")]),t._v(\"类，并重写了\"),e(\"code\",[t._v(\"createElement()\")]),t._v(\"方法，不同的是返回的\"),e(\"code\",[t._v(\"Element\")]),t._v(\" 对象并不相同；另外\"),e(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"类中添加了一个新的接口\"),e(\"code\",[t._v(\"createState()\")]),t._v(\"。\")]),t._v(\" \"),e(\"p\",[t._v(\"下面我们看看\"),e(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"的类定义：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"abstract\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Widget\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"StatefulWidget\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" Key key \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  StatefulElement \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createElement\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulElement\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@protected\")]),t._v(\"\\n  State \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"ul\",[e(\"li\",[e(\"p\",[e(\"code\",[t._v(\"StatefulElement\")]),t._v(\" 间接继承自\"),e(\"code\",[t._v(\"Element\")]),t._v(\"类，与StatefulWidget相对应（作为其配置数据）。\"),e(\"code\",[t._v(\"StatefulElement\")]),t._v(\"中可能会多次调用\"),e(\"code\",[t._v(\"createState()\")]),t._v(\"来创建状态(State)对象。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"code\",[t._v(\"createState()\")]),t._v(\" 用于创建和Stateful widget相关的状态，它在Stateful widget的生命周期中可能会被多次调用。例如，当一个Stateful widget同时插入到widget树的多个位置时，Flutter framework就会调用该方法为每一个位置生成一个独立的State实例，其实，本质上就是一个\"),e(\"code\",[t._v(\"StatefulElement\")]),t._v(\"对应一个State实例。\")]),t._v(\" \"),e(\"blockquote\",[e(\"p\",[t._v(\"在本书中经常会出现“树”的概念，在不同的场景可能指不同的意思，在说“widget树”时它可以指widget结构树，但由于widget与Element有对应关系（一可能对多），在有些场景（Flutter的SDK文档中）也代指“UI树”的意思。而在stateful widget中，State对象也和\"),e(\"code\",[t._v(\"StatefulElement\")]),t._v(\"具有对应关系（一对一），所以在Flutter的SDK文档中，可以经常看到“从树中移除State对象”或“插入State对象到树中”这样的描述。其实，无论哪种描述，其意思都是在描述“一棵构成用户界面的节点元素的树”，读者不必纠结于这些概念，还是那句话“得其神，忘其形”，因此，本书中出现的各种“树”，如果没有特别说明，读者都可抽象的认为它是“一棵构成用户界面的节点元素的树”。\")])])])]),t._v(\" \"),e(\"h2\",{attrs:{id:\"_3-1-6-state\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-1-6-state\"}},[t._v(\"#\")]),t._v(\" 3.1.6 State\")]),t._v(\" \"),e(\"p\",[t._v(\"一个StatefulWidget类会对应一个State类，State表示与其对应的StatefulWidget要维护的状态，State中的保存的状态信息可以：\")]),t._v(\" \"),e(\"ol\",[e(\"li\",[t._v(\"在widget 构建时可以被同步读取。\")]),t._v(\" \"),e(\"li\",[t._v(\"在widget生命周期中可以被改变，当State被改变时，可以手动调用其\"),e(\"code\",[t._v(\"setState()\")]),t._v(\"方法通知Flutter framework状态发生改变，Flutter framework在收到消息后，会重新调用其\"),e(\"code\",[t._v(\"build\")]),t._v(\"方法重新构建widget树，从而达到更新UI的目的。\")])]),t._v(\" \"),e(\"p\",[t._v(\"State中有两个常用属性：\")]),t._v(\" \"),e(\"ol\",[e(\"li\",[e(\"p\",[e(\"code\",[t._v(\"widget\")]),t._v(\"，它表示与该State实例关联的widget实例，由Flutter framework动态设置。注意，这种关联并非永久的，因为在应用生命周期中，UI树上的某一个节点的widget实例在重新构建时可能会变化，但State实例只会在第一次插入到树中时被创建，当在重新构建时，如果widget被修改了，Flutter framework会动态设置State.widget为新的widget实例。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"code\",[t._v(\"context\")]),t._v(\"。StatefulWidget对应的BuildContext，作用同StatelessWidget的BuildContext。\")])])]),t._v(\" \"),e(\"h4\",{attrs:{id:\"state生命周期\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#state生命周期\"}},[t._v(\"#\")]),t._v(\" State生命周期\")]),t._v(\" \"),e(\"p\",[t._v(\"理解State的生命周期对flutter开发非常重要，为了加深读者印象，本节我们通过一个实例来演示一下State的生命周期。在接下来的示例中，我们实现一个计数器widget，点击它可以使计数器加1，由于要保存计数器的数值状态，所以我们应继承StatefulWidget，代码如下：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CounterWidget\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CounterWidget\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"initValue\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" int initValue\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _CounterWidgetState \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_CounterWidgetState\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[e(\"code\",[t._v(\"CounterWidget\")]),t._v(\"接收一个\"),e(\"code\",[t._v(\"initValue\")]),t._v(\"整型参数，它表示计数器的初始值。下面我们看一下State的代码：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_CounterWidgetState\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"CounterWidget\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"  \\n  int _counter\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//初始化状态  \")]),t._v(\"\\n    _counter\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"widget\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"initValue\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"initState\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"build\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      body\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          child\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'$_counter'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//点击后计数器自增\")]),t._v(\"\\n          onPressed\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"++\")]),t._v(\"_counter\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didUpdateWidget\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"CounterWidget oldWidget\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didUpdateWidget\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"oldWidget\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"didUpdateWidget\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"deactivate\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"deactivate\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"deactive\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"dispose\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"reassemble\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"reassemble\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"reassemble\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didChangeDependencies\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didChangeDependencies\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"didChangeDependencies\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"接下来，我们创建一个新路由，在新路由中，我们只显示一个\"),e(\"code\",[t._v(\"CounterWidget\")]),t._v(\"：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[t._v(\"Widget \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CounterWidget\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"我们运行应用并打开该路由页面，在新路由页打开后，屏幕中央就会出现一个数字0，然后控制台日志输出：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language- extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[e(\"code\",[t._v(\"I/flutter ( 5436): initState\\nI/flutter ( 5436): didChangeDependencies\\nI/flutter ( 5436): build\\n\")])])]),e(\"p\",[t._v(\"可以看到，在StatefulWidget插入到Widget树时首先\"),e(\"code\",[t._v(\"initState\")]),t._v(\"方法会被调用。\")]),t._v(\" \"),e(\"p\",[t._v(\"然后我们点击⚡️按钮热重载，控制台输出日志如下：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language- extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[e(\"code\",[t._v(\"I/flutter ( 5436): reassemble\\nI/flutter ( 5436): didUpdateWidget\\nI/flutter ( 5436): build\\n\")])])]),e(\"p\",[t._v(\"可以看到此时\"),e(\"code\",[t._v(\"initState\")]),t._v(\" 和\"),e(\"code\",[t._v(\"didChangeDependencies\")]),t._v(\"都没有被调用，而此时\"),e(\"code\",[t._v(\"didUpdateWidget\")]),t._v(\"被调用。\")]),t._v(\" \"),e(\"p\",[t._v(\"接下来，我们在widget树中移除\"),e(\"code\",[t._v(\"CounterWidget\")]),t._v(\"，将路由\"),e(\"code\",[t._v(\"build\")]),t._v(\"方法改为：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[t._v(\"Widget \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//移除计数器 \")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//return CounterWidget();\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//随便返回一个Text()\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"xxx\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"然后热重载，日志如下：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language- extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[e(\"code\",[t._v(\"I/flutter ( 5436): reassemble\\nI/flutter ( 5436): deactive\\nI/flutter ( 5436): dispose\\n\")])])]),e(\"p\",[t._v(\"我们可以看到，在\"),e(\"code\",[t._v(\"CounterWidget\")]),t._v(\"从widget树中移除时，\"),e(\"code\",[t._v(\"deactive\")]),t._v(\"和\"),e(\"code\",[t._v(\"dispose\")]),t._v(\"会依次被调用。\")]),t._v(\" \"),e(\"p\",[t._v(\"下面我们来看看各个回调函数：\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"p\",[e(\"code\",[t._v(\"initState\")]),t._v(\"：当Widget第一次插入到Widget树时会被调用，对于每一个State对象，Flutter framework只会调用一次该回调，所以，通常在该回调中做一些一次性的操作，如状态初始化、订阅子树的事件通知等。不能在该回调中调用\"),e(\"code\",[t._v(\"BuildContext.dependOnInheritedWidgetOfExactType\")]),t._v(\"（该方法用于在Widget树上获取离当前widget最近的一个父级\"),e(\"code\",[t._v(\"InheritFromWidget\")]),t._v(\"，关于\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"我们将在后面章节介绍），原因是在初始化完成后，Widget树中的\"),e(\"code\",[t._v(\"InheritFromWidget\")]),t._v(\"也可能会发生变化，所以正确的做法应该在在\"),e(\"code\",[t._v(\"build（）\")]),t._v(\"方法或\"),e(\"code\",[t._v(\"didChangeDependencies()\")]),t._v(\"中调用它。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"code\",[t._v(\"didChangeDependencies()\")]),t._v(\"：当State对象的依赖发生变化时会被调用；例如：在之前\"),e(\"code\",[t._v(\"build()\")]),t._v(\" 中包含了一个\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"，然后在之后的\"),e(\"code\",[t._v(\"build()\")]),t._v(\" 中\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"发生了变化，那么此时\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"的子widget的\"),e(\"code\",[t._v(\"didChangeDependencies()\")]),t._v(\"回调都会被调用。典型的场景是当系统语言Locale或应用主题改变时，Flutter framework会通知widget调用此回调。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"code\",[t._v(\"build()\")]),t._v(\"：此回调读者现在应该已经相当熟悉了，它主要是用于构建Widget子树的，会在如下场景被调用：\")]),t._v(\" \"),e(\"ol\",[e(\"li\",[t._v(\"在调用\"),e(\"code\",[t._v(\"initState()\")]),t._v(\"之后。\")]),t._v(\" \"),e(\"li\",[t._v(\"在调用\"),e(\"code\",[t._v(\"didUpdateWidget()\")]),t._v(\"之后。\")]),t._v(\" \"),e(\"li\",[t._v(\"在调用\"),e(\"code\",[t._v(\"setState()\")]),t._v(\"之后。\")]),t._v(\" \"),e(\"li\",[t._v(\"在调用\"),e(\"code\",[t._v(\"didChangeDependencies()\")]),t._v(\"之后。\")]),t._v(\" \"),e(\"li\",[t._v(\"在State对象从树中一个位置移除后（会调用deactivate）又重新插入到树的其它位置之后。\")])])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"code\",[t._v(\"reassemble()\")]),t._v(\"：此回调是专门为了开发调试而提供的，在热重载(hot reload)时会被调用，此回调在Release模式下永远不会被调用。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"code\",[t._v(\"didUpdateWidget()\")]),t._v(\"：在widget重新构建时，Flutter framework会调用\"),e(\"code\",[t._v(\"Widget.canUpdate\")]),t._v(\"来检测Widget树中同一位置的新旧节点，然后决定是否需要更新，如果\"),e(\"code\",[t._v(\"Widget.canUpdate\")]),t._v(\"返回\"),e(\"code\",[t._v(\"true\")]),t._v(\"则会调用此回调。正如之前所述，\"),e(\"code\",[t._v(\"Widget.canUpdate\")]),t._v(\"会在新旧widget的key和runtimeType同时相等时会返回true，也就是说在在新旧widget的key和runtimeType同时相等时\"),e(\"code\",[t._v(\"didUpdateWidget()\")]),t._v(\"就会被调用。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"code\",[t._v(\"deactivate()\")]),t._v(\"：当State对象从树中被移除时，会调用此回调。在一些场景下，Flutter framework会将State对象重新插到树中，如包含此State对象的子树在树的一个位置移动到另一个位置时（可以通过GlobalKey来实现）。如果移除后没有重新插入到树中则紧接着会调用\"),e(\"code\",[t._v(\"dispose()\")]),t._v(\"方法。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"code\",[t._v(\"dispose()\")]),t._v(\"：当State对象从树中被永久移除时调用；通常在此回调中释放资源。\")])])]),t._v(\" \"),e(\"p\",[t._v(\"StatefulWidget生命周期如图3-2所示：\")]),t._v(\" \"),e(\"p\",[e(\"img\",{attrs:{src:s(413),alt:\"图3-2\"}})]),t._v(\" \"),e(\"blockquote\",[e(\"p\",[e(\"strong\",[t._v(\"注意\")]),t._v(\"：在继承\"),e(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"重写其方法时，对于包含\"),e(\"code\",[t._v(\"@mustCallSuper\")]),t._v(\"标注的父类方法，都要在子类方法中先调用父类方法。\")])]),t._v(\" \"),e(\"h3\",{attrs:{id:\"为什么要将build方法放在state中-而不是放在statefulwidget中\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#为什么要将build方法放在state中-而不是放在statefulwidget中\"}},[t._v(\"#\")]),t._v(\" 为什么要将build方法放在State中，而不是放在StatefulWidget中？\")]),t._v(\" \"),e(\"p\",[t._v(\"现在，我们回答之前提出的问题，为什么\"),e(\"code\",[t._v(\"build()\")]),t._v(\"方法放在State（而不是\"),e(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"）中 ？这主要是为了提高开发的灵活性。如果将\"),e(\"code\",[t._v(\"build()\")]),t._v(\"方法在\"),e(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"中则会有两个问题：\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"p\",[t._v(\"状态访问不便。\")]),t._v(\" \"),e(\"p\",[t._v(\"试想一下，如果我们的\"),e(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"有很多状态，而每次状态改变都要调用\"),e(\"code\",[t._v(\"build\")]),t._v(\"方法，由于状态是保存在State中的，如果\"),e(\"code\",[t._v(\"build\")]),t._v(\"方法在\"),e(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"中，那么\"),e(\"code\",[t._v(\"build\")]),t._v(\"方法和状态分别在两个类中，那么构建时读取状态将会很不方便！试想一下，如果真的将\"),e(\"code\",[t._v(\"build\")]),t._v(\"方法放在StatefulWidget中的话，由于构建用户界面过程需要依赖State，所以\"),e(\"code\",[t._v(\"build\")]),t._v(\"方法将必须加一个\"),e(\"code\",[t._v(\"State\")]),t._v(\"参数，大概是下面这样：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[t._v(\"  Widget \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" State state\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//state.counter\")]),t._v(\"\\n      \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"这样的话就只能将State的所有状态声明为公开的状态，这样才能在State类外部访问状态！但是，将状态设置为公开后，状态将不再具有私密性，这就会导致对状态的修改将会变的不可控。但如果将\"),e(\"code\",[t._v(\"build()\")]),t._v(\"方法放在State中的话，构建过程不仅可以直接访问状态，而且也无需公开私有状态，这会非常方便。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[t._v(\"继承\"),e(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"不便。\")]),t._v(\" \"),e(\"p\",[t._v(\"例如，Flutter中有一个动画widget的基类\"),e(\"code\",[t._v(\"AnimatedWidget\")]),t._v(\"，它继承自\"),e(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"类。\"),e(\"code\",[t._v(\"AnimatedWidget\")]),t._v(\"中引入了一个抽象方法\"),e(\"code\",[t._v(\"build(BuildContext context)\")]),t._v(\"，继承自\"),e(\"code\",[t._v(\"AnimatedWidget\")]),t._v(\"的动画widget都要实现这个\"),e(\"code\",[t._v(\"build\")]),t._v(\"方法。现在设想一下，如果\"),e(\"code\",[t._v(\"StatefulWidget\")]),t._v(\" 类中已经有了一个\"),e(\"code\",[t._v(\"build\")]),t._v(\"方法，正如上面所述，此时\"),e(\"code\",[t._v(\"build\")]),t._v(\"方法需要接收一个state对象，这就意味着\"),e(\"code\",[t._v(\"AnimatedWidget\")]),t._v(\"必须将自己的State对象(记为_animatedWidgetState)提供给其子类，因为子类需要在其\"),e(\"code\",[t._v(\"build\")]),t._v(\"方法中调用父类的\"),e(\"code\",[t._v(\"build\")]),t._v(\"方法，代码可能如下：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyAnimationWidget\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimatedWidget\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n    Widget \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" State state\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//由于子类要用到AnimatedWidget的状态对象_animatedWidgetState，\")]),t._v(\"\\n      \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//所以AnimatedWidget必须通过某种方式将其状态对象_animatedWidgetState\")]),t._v(\"\\n      \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//暴露给其子类   \")]),t._v(\"\\n      \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" _animatedWidgetState\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"这样很显然是不合理的，因为\")]),t._v(\" \"),e(\"ol\",[e(\"li\",[e(\"code\",[t._v(\"AnimatedWidget\")]),t._v(\"的状态对象是\"),e(\"code\",[t._v(\"AnimatedWidget\")]),t._v(\"内部实现细节，不应该暴露给外部。\")]),t._v(\" \"),e(\"li\",[t._v(\"如果要将父类状态暴露给子类，那么必须得有一种传递机制，而做这一套传递机制是无意义的，因为父子类之间状态的传递和子类本身逻辑是无关的。\")])])])]),t._v(\" \"),e(\"p\",[t._v(\"综上所述，可以发现，对于\"),e(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"，将\"),e(\"code\",[t._v(\"build\")]),t._v(\"方法放在State中，可以给开发带来很大的灵活性。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"_3-1-7-在widget树中获取state对象\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-1-7-在widget树中获取state对象\"}},[t._v(\"#\")]),t._v(\" 3.1.7 在Widget树中获取State对象\")]),t._v(\" \"),e(\"p\",[t._v(\"由于StatefulWidget的的具体逻辑都在其State中，所以很多时候，我们需要获取StatefulWidget对应的State对象来调用一些方法，比如\"),e(\"code\",[t._v(\"Scaffold\")]),t._v(\"组件对应的状态类\"),e(\"code\",[t._v(\"ScaffoldState\")]),t._v(\"中就定义了打开SnackBar(路由页底部提示条)的方法。我们有两种方法在子widget树中获取父级StatefulWidget的State对象。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"通过context获取\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#通过context获取\"}},[t._v(\"#\")]),t._v(\" 通过Context获取\")]),t._v(\" \"),e(\"p\",[e(\"code\",[t._v(\"context\")]),t._v(\"对象有一个\"),e(\"code\",[t._v(\"findAncestorStateOfType()\")]),t._v(\"方法，该方法可以从当前节点沿着widget树向上查找指定类型的StatefulWidget对应的State对象。下面是实现打开SnackBar的示例：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  appBar\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    title\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"子树中获取State对象\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  body\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    child\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        onPressed\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 查找父级最近的Scaffold对应的ScaffoldState对象\")]),t._v(\"\\n          ScaffoldState _state \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"findAncestorStateOfType\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ScaffoldState\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//调用ScaffoldState的showSnackBar来弹出SnackBar\")]),t._v(\"\\n          _state\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showSnackBar\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SnackBar\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              content\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"我是SnackBar\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"显示SnackBar\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"上面示例运行后，点击”显示SnackBar“，效果如图3-1-2所示：\")]),t._v(\" \"),e(\"p\",[e(\"img\",{attrs:{src:s(414),alt:\"图3-1-2\"}})]),t._v(\" \"),e(\"p\",[t._v(\"一般来说，如果StatefulWidget的状态是私有的（不应该向外部暴露），那么我们代码中就不应该去直接获取其State对象；如果StatefulWidget的状态是希望暴露出的（通常还有一些组件的操作方法），我们则可以去直接获取其State对象。但是通过\"),e(\"code\",[t._v(\"context.findAncestorStateOfType\")]),t._v(\"获取StatefulWidget的状态的方法是通用的，我们并不能在语法层面指定StatefulWidget的状态是否私有，所以在Flutter开发中便有了一个默认的约定：如果StatefulWidget的状态是希望暴露出的，应当在StatefulWidget中提供一个\"),e(\"code\",[t._v(\"of\")]),t._v(\"静态方法来获取其State对象，开发者便可直接通过该方法来获取；如果State不希望暴露，则不提供\"),e(\"code\",[t._v(\"of\")]),t._v(\"方法。这个约定在Flutter SDK里随处可见。所以，上面示例中的\"),e(\"code\",[t._v(\"Scaffold\")]),t._v(\"也提供了一个\"),e(\"code\",[t._v(\"of\")]),t._v(\"方法，我们其实是可以直接调用它的：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 直接通过of静态方法来获取ScaffoldState \")]),t._v(\"\\nScaffoldState _state\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"Scaffold\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \\n_state\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showSnackBar\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SnackBar\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    content\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"我是SnackBar\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),e(\"h3\",{attrs:{id:\"通过globalkey\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#通过globalkey\"}},[t._v(\"#\")]),t._v(\" 通过GlobalKey\")]),t._v(\" \"),e(\"p\",[t._v(\"Flutter还有一种通用的获取\"),e(\"code\",[t._v(\"State\")]),t._v(\"对象的方法——通过GlobalKey来获取！ 步骤分两步：\")]),t._v(\" \"),e(\"ol\",[e(\"li\",[e(\"p\",[t._v(\"给目标\"),e(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"添加\"),e(\"code\",[t._v(\"GlobalKey\")]),t._v(\"。\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//定义一个globalKey, 由于GlobalKey要保持全局唯一性，我们使用静态变量存储\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" GlobalKey\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ScaffoldState\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _globalKey\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GlobalKey\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    key\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _globalKey \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//设置key\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"  \\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])])]),t._v(\" \"),e(\"li\",[e(\"p\",[t._v(\"通过\"),e(\"code\",[t._v(\"GlobalKey\")]),t._v(\"来获取\"),e(\"code\",[t._v(\"State\")]),t._v(\"对象\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[t._v(\"_globalKey\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"currentState\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"openDrawer\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])])])]),t._v(\" \"),e(\"p\",[t._v(\"GlobalKey是Flutter提供的一种在整个APP中引用element的机制。如果一个widget设置了\"),e(\"code\",[t._v(\"GlobalKey\")]),t._v(\"，那么我们便可以通过\"),e(\"code\",[t._v(\"globalKey.currentWidget\")]),t._v(\"获得该widget对象、\"),e(\"code\",[t._v(\"globalKey.currentElement\")]),t._v(\"来获得widget对应的element对象，如果当前widget是\"),e(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"，则可以通过\"),e(\"code\",[t._v(\"globalKey.currentState\")]),t._v(\"来获得该widget对应的state对象。\")]),t._v(\" \"),e(\"blockquote\",[e(\"p\",[t._v(\"注意：使用GlobalKey开销较大，如果有其他可选方案，应尽量避免使用它。另外同一个GlobalKey在整个widget树中必须是唯一的，不能重复。\")])]),t._v(\" \"),e(\"h2\",{attrs:{id:\"_3-1-8-flutter-sdk内置组件库介绍\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-1-8-flutter-sdk内置组件库介绍\"}},[t._v(\"#\")]),t._v(\" 3.1.8 Flutter SDK内置组件库介绍\")]),t._v(\" \"),e(\"p\",[t._v(\"Flutter提供了一套丰富、强大的基础组件，在基础组件库之上Flutter又提供了一套Material风格（Android默认的视觉风格）和一套Cupertino风格（iOS视觉风格）的组件库。要使用基础组件库，需要先导入：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/widgets.dart'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"下面我们介绍一下常用的组件。\")]),t._v(\" \"),e(\"h4\",{attrs:{id:\"基础组件\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#基础组件\"}},[t._v(\"#\")]),t._v(\" 基础组件\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/Text-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"Text\")]),e(\"OutboundLink\")],1),t._v(\"：该组件可让您创建一个带格式的文本。\")]),t._v(\" \"),e(\"li\",[e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/Row-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"Row\")]),e(\"OutboundLink\")],1),t._v(\"、 \"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/Column-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"Column\")]),e(\"OutboundLink\")],1),t._v(\"： 这些具有弹性空间的布局类Widget可让您在水平（Row）和垂直（Column）方向上创建灵活的布局。其设计是基于Web开发中的Flexbox布局模型。\")]),t._v(\" \"),e(\"li\",[e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/Stack-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"Stack\")]),e(\"OutboundLink\")],1),t._v(\"： 取代线性布局 (译者语：和Android中的\"),e(\"code\",[t._v(\"FrameLayout\")]),t._v(\"相似)，\"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/Stack-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"Stack\")]),e(\"OutboundLink\")],1),t._v(\"允许子 widget 堆叠， 你可以使用 \"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/Positioned-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"Positioned\")]),e(\"OutboundLink\")],1),t._v(\" 来定位他们相对于\"),e(\"code\",[t._v(\"Stack\")]),t._v(\"的上下左右四条边的位置。Stacks是基于Web开发中的绝对定位（absolute positioning )布局模型设计的。\")]),t._v(\" \"),e(\"li\",[e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/Container-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"Container\")]),e(\"OutboundLink\")],1),t._v(\"： \"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/Container-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"Container\")]),e(\"OutboundLink\")],1),t._v(\" 可让您创建矩形视觉元素。container 可以装饰一个\"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/painting/BoxDecoration-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"BoxDecoration\")]),e(\"OutboundLink\")],1),t._v(\", 如 background、一个边框、或者一个阴影。 \"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/Container-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"Container\")]),e(\"OutboundLink\")],1),t._v(\" 也可以具有边距（margins）、填充(padding)和应用于其大小的约束(constraints)。另外， \"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/Container-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"Container\")]),e(\"OutboundLink\")],1),t._v(\"可以使用矩阵在三维空间中对其进行变换。\")])]),t._v(\" \"),e(\"h4\",{attrs:{id:\"material组件\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#material组件\"}},[t._v(\"#\")]),t._v(\" Material组件\")]),t._v(\" \"),e(\"p\",[t._v(\"Flutter提供了一套丰富的Material组件，它可以帮助我们构建遵循Material Design设计规范的应用程序。Material应用程序以\"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/material/MaterialApp-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"MaterialApp\")]),e(\"OutboundLink\")],1),t._v(\" 组件开始， 该组件在应用程序的根部创建了一些必要的组件，比如\"),e(\"code\",[t._v(\"Theme\")]),t._v(\"组件，它用于配置应用的主题。 是否使用\"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/material/MaterialApp-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"MaterialApp\")]),e(\"OutboundLink\")],1),t._v(\"完全是可选的，但是使用它是一个很好的做法。在之前的示例中，我们已经使用过多个Material 组件了，如：\"),e(\"code\",[t._v(\"Scaffold\")]),t._v(\"、\"),e(\"code\",[t._v(\"AppBar\")]),t._v(\"、\"),e(\"code\",[t._v(\"FlatButton\")]),t._v(\"等。要使用Material 组件，需要先引入它：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),e(\"h4\",{attrs:{id:\"cupertino组件\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#cupertino组件\"}},[t._v(\"#\")]),t._v(\" Cupertino组件\")]),t._v(\" \"),e(\"p\",[t._v(\"Flutter也提供了一套丰富的Cupertino风格的组件，尽管目前还没有Material 组件那么丰富，但是它仍在不断的完善中。值得一提的是在Material 组件库中有一些组件可以根据实际运行平台来切换表现风格，比如\"),e(\"code\",[t._v(\"MaterialPageRoute\")]),t._v(\"，在路由切换时，如果是Android系统，它将会使用Android系统默认的页面切换动画(从底向上)；如果是iOS系统，它会使用iOS系统默认的页面切换动画（从右向左）。由于在前面的示例中还没有Cupertino组件的示例，下面我们实现一个简单的Cupertino组件风格的页面：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//导入cupertino widget库\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/cupertino.dart'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CupertinoTestRoute\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CupertinoPageScaffold\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      navigationBar\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CupertinoNavigationBar\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        middle\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Cupertino Demo\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CupertinoButton\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            color\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" CupertinoColors\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"activeBlue\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Press\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onPressed\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n        \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"下面（图3-3）是在iPhoneX上页面效果截图：\")]),t._v(\" \"),e(\"p\",[e(\"img\",{attrs:{src:s(415),alt:\"图3-3\"}})]),t._v(\" \"),e(\"h3\",{attrs:{id:\"关于示例\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#关于示例\"}},[t._v(\"#\")]),t._v(\" 关于示例\")]),t._v(\" \"),e(\"p\",[t._v(\"本章后面章节的示例中会使用一些布局类组件，如\"),e(\"code\",[t._v(\"Scaffold\")]),t._v(\"、\"),e(\"code\",[t._v(\"Row\")]),t._v(\"、\"),e(\"code\",[t._v(\"Column\")]),t._v(\"等，这些组件将在后面“布局类组件”一章中详细介绍，读者可以先不用关注。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"总结\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),e(\"p\",[t._v(\"Flutter提供了丰富的组件，在实际的开发中你可以根据需要随意使用它们，而不必担心引入过多组件库会让你的应用安装包变大，这不是web开发，dart在编译时只会编译你使用了的代码。由于Material和Cupertino都是在基础组件库之上的，所以如果我们的应用中引入了这两者之一，则不需要再引入\"),e(\"code\",[t._v(\"flutter/widgets.dart\")]),t._v(\"了，因为它们内部已经引入过了。\")])])}),[],!1,null,null,null);a.default=n.exports}}]);"
  },
  {
    "path": "docs/assets/js/210.0c9a3892.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[210],{896:function(e,t,d){\"use strict\";d.r(t);var _=d(45),v=Object(_.a)({},(function(){var e=this,t=e.$createElement,d=e._self._c||t;return d(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":e.$parent.slotKey}},[d(\"h1\",{attrs:{id:\"_4-1-布局类组件简介\"}},[d(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_4-1-布局类组件简介\"}},[e._v(\"#\")]),e._v(\" 4.1 布局类组件简介\")]),e._v(\" \"),d(\"p\",[e._v(\"布局类组件都会包含一个或多个子组件，不同的布局类组件对子组件排版(layout)方式不同。我们在前面说过\"),d(\"code\",[e._v(\"Element\")]),e._v(\"树才是最终的绘制树，\"),d(\"code\",[e._v(\"Element\")]),e._v(\"树是通过Widget树来创建的（通过\"),d(\"code\",[e._v(\"Widget.createElement()\")]),e._v(\"），Widget其实就是Element的配置数据。在Flutter中，根据Widget是否需要包含子节点将Widget分为了三类，分别对应三种Element，如下表：\")]),e._v(\" \"),d(\"table\",[d(\"thead\",[d(\"tr\",[d(\"th\",[e._v(\"Widget\")]),e._v(\" \"),d(\"th\",[e._v(\"对应的Element\")]),e._v(\" \"),d(\"th\",[e._v(\"用途\")])])]),e._v(\" \"),d(\"tbody\",[d(\"tr\",[d(\"td\",[e._v(\"LeafRenderObjectWidget\")]),e._v(\" \"),d(\"td\",[e._v(\"LeafRenderObjectElement\")]),e._v(\" \"),d(\"td\",[e._v(\"Widget树的叶子节点，用于没有子节点的widget，通常基础组件都属于这一类，如Image。\")])]),e._v(\" \"),d(\"tr\",[d(\"td\",[e._v(\"SingleChildRenderObjectWidget\")]),e._v(\" \"),d(\"td\",[e._v(\"SingleChildRenderObjectElement\")]),e._v(\" \"),d(\"td\",[e._v(\"包含一个子Widget，如：ConstrainedBox、DecoratedBox等\")])]),e._v(\" \"),d(\"tr\",[d(\"td\",[e._v(\"MultiChildRenderObjectWidget\")]),e._v(\" \"),d(\"td\",[e._v(\"MultiChildRenderObjectElement\")]),e._v(\" \"),d(\"td\",[e._v(\"包含多个子Widget，一般都有一个children参数，接受一个Widget数组。如Row、Column、Stack等\")])])])]),e._v(\" \"),d(\"blockquote\",[d(\"p\",[e._v(\"注意，Flutter中的很多Widget是直接继承自StatelessWidget或StatefulWidget，然后在\"),d(\"code\",[e._v(\"build()\")]),e._v(\"方法中构建真正的RenderObjectWidget，如Text，它其实是继承自StatelessWidget，然后在\"),d(\"code\",[e._v(\"build()\")]),e._v(\"方法中通过RichText来构建其子树，而RichText才是继承自MultiChildRenderObjectWidget。所以为了方便叙述，我们也可以直接说Text属于MultiChildRenderObjectWidget（其它widget也可以这么描述），这才是本质。读到这里我们也会发现，其实\"),d(\"strong\",[e._v(\"StatelessWidget和StatefulWidget就是两个用于组合Widget的基类，它们本身并不关联最终的渲染对象（RenderObjectWidget）\")]),e._v(\"。\")])]),e._v(\" \"),d(\"p\",[e._v(\"布局类组件就是指直接或间接继承(包含)\"),d(\"code\",[e._v(\"MultiChildRenderObjectWidget\")]),e._v(\"的Widget，它们一般都会有一个\"),d(\"code\",[e._v(\"children\")]),e._v(\"属性用于接收子Widget。我们看一下继承关系 Widget > RenderObjectWidget > (Leaf/SingleChild/MultiChild)RenderObjectWidget 。\")]),e._v(\" \"),d(\"p\",[d(\"code\",[e._v(\"RenderObjectWidget\")]),e._v(\"类中定义了创建、更新\"),d(\"code\",[e._v(\"RenderObject\")]),e._v(\"的方法，子类必须实现他们，关于\"),d(\"code\",[e._v(\"RenderObject\")]),e._v(\"我们现在只需要知道它是最终布局、渲染UI界面的对象即可，也就是说，对于布局类组件来说，其布局算法都是通过对应的\"),d(\"code\",[e._v(\"RenderObject\")]),e._v(\"对象来实现的，所以读者如果对接下来介绍的某个布局类组件的原理感兴趣，可以查看其对应的\"),d(\"code\",[e._v(\"RenderObject\")]),e._v(\"的实现，比如\"),d(\"code\",[e._v(\"Stack\")]),e._v(\"（层叠布局）对应的\"),d(\"code\",[e._v(\"RenderObject\")]),e._v(\"对象就是\"),d(\"code\",[e._v(\"RenderStack\")]),e._v(\"，而层叠布局的实现就在\"),d(\"code\",[e._v(\"RenderStack\")]),e._v(\"中。\")]),e._v(\" \"),d(\"p\",[e._v(\"在本章中，为了让读者对布局类Widget有个快速的认识，所以我们并不会深入到\"),d(\"code\",[e._v(\"RenderObject\")]),e._v(\"的细节中去。在学习本章时，读者的重点是掌握不同布局组件的布局特点，具体原理和细节等我们对Flutter整体入门后，感兴趣的话再去研究。\")])])}),[],!1,null,null,null);t.default=v.exports}}]);"
  },
  {
    "path": "docs/assets/js/211.43a19344.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[211],{904:function(t,e,i){\"use strict\";i.r(e);var r=i(45),a=Object(r.a)({},(function(){var t=this,e=t.$createElement,i=t._self._c||e;return i(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[i(\"h1\",{attrs:{id:\"容器类widget\"}},[i(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#容器类widget\"}},[t._v(\"#\")]),t._v(\" 容器类Widget\")]),t._v(\" \"),i(\"p\",[t._v(\"容器类Widget和布局类Widget都作用于其子Widget，不同的是：\")]),t._v(\" \"),i(\"ul\",[i(\"li\",[t._v(\"布局类Widget一般都需要接收一个widget数组（children），他们直接或间接继承自（或包含）MultiChildRenderObjectWidget ；而容器类Widget一般只需要接收一个子Widget（child），他们直接或间接继承自（或包含）SingleChildRenderObjectWidget。\")]),t._v(\" \"),i(\"li\",[t._v(\"布局类Widget是按照一定的排列方式来对其子Widget进行排列；而容器类Widget一般只是包装其子Widget，对其添加一些修饰（补白或背景色等）、变换(旋转或剪裁等)、或限制(大小等)。\")])]),t._v(\" \"),i(\"p\",[t._v(\"注意，Flutter官方并没有对Widget进行官方分类，我们对其分类主要是为了方便讨论和对Widget功能区分的记忆。\")]),t._v(\" \"),i(\"h2\",{attrs:{id:\"本章目录\"}},[i(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本章目录\"}},[t._v(\"#\")]),t._v(\" 本章目录\")]),t._v(\" \"),i(\"ul\",[i(\"li\",[i(\"RouterLink\",{attrs:{to:\"/v2/chapter5/padding.html\"}},[t._v(\"5.1：填充（Padding）\")])],1),t._v(\" \"),i(\"li\",[i(\"RouterLink\",{attrs:{to:\"/v2/chapter5/constrainedbox_and_sizebox.html\"}},[t._v(\"5.2：尺寸限制类容器（ConstrainedBox等）\")])],1),t._v(\" \"),i(\"li\",[i(\"RouterLink\",{attrs:{to:\"/v2/chapter5/decoratedbox.html\"}},[t._v(\"5.3：装饰容器（DecoratedBox）\")])],1),t._v(\" \"),i(\"li\",[i(\"RouterLink\",{attrs:{to:\"/v2/chapter5/transform.html\"}},[t._v(\"5.4：变换（Transform）\")])],1),t._v(\" \"),i(\"li\",[i(\"RouterLink\",{attrs:{to:\"/v2/chapter5/container.html\"}},[t._v(\"5.5：Container容器\")])],1),t._v(\" \"),i(\"li\",[i(\"RouterLink\",{attrs:{to:\"/v2/chapter5/material_scaffold.html\"}},[t._v(\"5.6：Scaffold、TabBar、底部导航\")])],1),t._v(\" \"),i(\"li\",[i(\"RouterLink\",{attrs:{to:\"/v2/chapter5/clip.html\"}},[t._v(\"5.7：剪裁（Clip）\")])],1)])])}),[],!1,null,null,null);e.default=a.exports}}]);"
  },
  {
    "path": "docs/assets/js/212.f9dee082.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[212],{909:function(t,r,l){\"use strict\";l.r(r);var e=l(45),i=Object(e.a)({},(function(){var t=this,r=t.$createElement,l=t._self._c||r;return l(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[l(\"h1\",{attrs:{id:\"本章目录\"}},[l(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本章目录\"}},[t._v(\"#\")]),t._v(\" 本章目录\")]),t._v(\" \"),l(\"ul\",[l(\"li\",[l(\"RouterLink\",{attrs:{to:\"/v2/chapter6/intro.html\"}},[t._v(\"6.1：可滚动组件简介\")])],1),t._v(\" \"),l(\"li\",[l(\"RouterLink\",{attrs:{to:\"/v2/chapter6/single_child_scrollview.html\"}},[t._v(\"6.2：SingleChildScrollView\")])],1),t._v(\" \"),l(\"li\",[l(\"RouterLink\",{attrs:{to:\"/v2/chapter6/listview.html\"}},[t._v(\"6.3：ListView\")])],1),t._v(\" \"),l(\"li\",[l(\"RouterLink\",{attrs:{to:\"/v2/chapter6/gridview.html\"}},[t._v(\"6.4：GridView\")])],1),t._v(\" \"),l(\"li\",[l(\"RouterLink\",{attrs:{to:\"/v2/chapter6/custom_scrollview.html\"}},[t._v(\"6.5：CustomScrollView\")])],1),t._v(\" \"),l(\"li\",[l(\"RouterLink\",{attrs:{to:\"/v2/chapter6/scroll_controller.html\"}},[t._v(\"6.6：滚动监听及控制（ScrollController）\")])],1)])])}),[],!1,null,null,null);r.default=i.exports}}]);"
  },
  {
    "path": "docs/assets/js/213.f55d441e.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[213],{911:function(t,r,a){\"use strict\";a.r(r);var s=a(45),e=Object(s.a)({},(function(){var t=this,r=t.$createElement,a=t._self._c||r;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_6-1-可滚动组件简介\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_6-1-可滚动组件简介\"}},[t._v(\"#\")]),t._v(\" 6.1 可滚动组件简介\")]),t._v(\" \"),a(\"p\",[t._v(\"当组件内容超过当前显示视口(ViewPort)时，如果没有特殊处理，Flutter则会提示Overflow错误。为此，Flutter提供了多种可滚动组件（Scrollable Widget）用于显示列表和长布局。在本章中，我们先介绍一下常用的可滚动组件（如\"),a(\"code\",[t._v(\"ListView\")]),t._v(\"、\"),a(\"code\",[t._v(\"GridView\")]),t._v(\"等），然后介绍一下\"),a(\"code\",[t._v(\"ScrollController\")]),t._v(\"。可滚动组件都直接或间接包含一个\"),a(\"code\",[t._v(\"Scrollable\")]),t._v(\"组件，因此它们包括一些共同的属性，为了避免重复介绍，我们在此统一介绍一下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scrollable\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"axisDirection \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" AxisDirection\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"down\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"physics\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"viewportBuilder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//后面介绍\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"ul\",[a(\"li\",[a(\"code\",[t._v(\"axisDirection\")]),t._v(\"滚动方向。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"physics\")]),t._v(\"：此属性接受一个\"),a(\"code\",[t._v(\"ScrollPhysics\")]),t._v(\"类型的对象，它决定可滚动组件如何响应用户操作，比如用户滑动完抬起手指后，继续执行动画；或者滑动到边界时，如何显示。默认情况下，Flutter会根据具体平台分别使用不同的\"),a(\"code\",[t._v(\"ScrollPhysics\")]),t._v(\"对象，应用不同的显示效果，如当滑动到边界时，继续拖动的话，在iOS上会出现弹性效果，而在Android上会出现微光效果。如果你想在所有平台下使用同一种效果，可以显式指定一个固定的\"),a(\"code\",[t._v(\"ScrollPhysics\")]),t._v(\"，Flutter SDK中包含了两个\"),a(\"code\",[t._v(\"ScrollPhysics\")]),t._v(\"的子类，他们可以直接使用：\\n\"),a(\"ul\",[a(\"li\",[a(\"code\",[t._v(\"ClampingScrollPhysics\")]),t._v(\"：Android下微光效果。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"BouncingScrollPhysics\")]),t._v(\"：iOS下弹性效果。\")])])]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"controller\")]),t._v(\"：此属性接受一个\"),a(\"code\",[t._v(\"ScrollController\")]),t._v(\"对象。\"),a(\"code\",[t._v(\"ScrollController\")]),t._v(\"的主要作用是控制滚动位置和监听滚动事件。默认情况下，Widget树中会有一个默认的\"),a(\"code\",[t._v(\"PrimaryScrollController\")]),t._v(\"，如果子树中的可滚动组件没有显式的指定\"),a(\"code\",[t._v(\"controller\")]),t._v(\"，并且\"),a(\"code\",[t._v(\"primary\")]),t._v(\"属性值为\"),a(\"code\",[t._v(\"true\")]),t._v(\"时（默认就为\"),a(\"code\",[t._v(\"true\")]),t._v(\"），可滚动组件会使用这个默认的\"),a(\"code\",[t._v(\"PrimaryScrollController\")]),t._v(\"。这种机制带来的好处是父组件可以控制子树中可滚动组件的滚动行为，例如，\"),a(\"code\",[t._v(\"Scaffold\")]),t._v(\"正是使用这种机制在iOS中实现了点击导航栏回到顶部的功能。我们将在本章后面“滚动控制”一节详细介绍\"),a(\"code\",[t._v(\"ScrollController\")]),t._v(\"。\")])]),t._v(\" \"),a(\"h3\",{attrs:{id:\"scrollbar\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#scrollbar\"}},[t._v(\"#\")]),t._v(\" Scrollbar\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"Scrollbar\")]),t._v(\"是一个Material风格的滚动指示器（滚动条），如果要给可滚动组件添加滚动条，只需将\"),a(\"code\",[t._v(\"Scrollbar\")]),t._v(\"作为可滚动组件的任意一个父级组件即可，如：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scrollbar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SingleChildScrollView\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[a(\"code\",[t._v(\"Scrollbar\")]),t._v(\"和\"),a(\"code\",[t._v(\"CupertinoScrollbar\")]),t._v(\"都是通过监听滚动通知来确定滚动条位置的。关于的滚动通知的详细内容我们将在本章最后一节中专门介绍。\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"cupertinoscrollbar\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#cupertinoscrollbar\"}},[t._v(\"#\")]),t._v(\" CupertinoScrollbar\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"CupertinoScrollbar\")]),t._v(\"是iOS风格的滚动条，如果你使用的是\"),a(\"code\",[t._v(\"Scrollbar\")]),t._v(\"，那么在iOS平台它会自动切换为\"),a(\"code\",[t._v(\"CupertinoScrollbar\")]),t._v(\"。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"viewport视口\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#viewport视口\"}},[t._v(\"#\")]),t._v(\" ViewPort视口\")]),t._v(\" \"),a(\"p\",[t._v(\"在很多布局系统中都有ViewPort的概念，在Flutter中，术语ViewPort（视口），如无特别说明，则是指一个Widget的实际显示区域。例如，一个\"),a(\"code\",[t._v(\"ListView\")]),t._v(\"的显示区域高度是800像素，虽然其列表项总高度可能远远超过800像素，但是其ViewPort仍然是800像素。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"基于sliver的延迟构建\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#基于sliver的延迟构建\"}},[t._v(\"#\")]),t._v(\" 基于Sliver的延迟构建\")]),t._v(\" \"),a(\"p\",[t._v(\"通常可滚动组件的子组件可能会非常多、占用的总高度也会非常大；如果要一次性将子组件全部构建出将会非常昂贵！为此，Flutter中提出一个Sliver（中文为“薄片”的意思）概念，如果一个可滚动组件支持Sliver模型，那么该滚动可以将子组件分成好多个“薄片”（Sliver），只有当Sliver出现在视口中时才会去构建它，这种模型也称为“基于Sliver的延迟构建模型”。可滚动组件中有很多都支持基于Sliver的延迟构建模型，如\"),a(\"code\",[t._v(\"ListView\")]),t._v(\"、\"),a(\"code\",[t._v(\"GridView\")]),t._v(\"，但是也有不支持该模型的，如\"),a(\"code\",[t._v(\"SingleChildScrollView\")]),t._v(\"。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"主轴和纵轴\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#主轴和纵轴\"}},[t._v(\"#\")]),t._v(\" 主轴和纵轴\")]),t._v(\" \"),a(\"p\",[t._v(\"在可滚动组件的坐标描述中，通常将滚动方向称为主轴，非滚动方向称为纵轴。由于可滚动组件的默认方向一般都是沿垂直方向，所以默认情况下主轴就是指垂直方向，水平方向同理。\")])])}),[],!1,null,null,null);r.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/214.0836f39e.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[214],{916:function(t,e,r){\"use strict\";r.r(e);var i=r(45),a=Object(i.a)({},(function(){var t=this,e=t.$createElement,r=t._self._c||e;return r(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[r(\"h2\",{attrs:{id:\"功能型widget简介\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#功能型widget简介\"}},[t._v(\"#\")]),t._v(\" 功能型Widget简介\")]),t._v(\" \"),r(\"p\",[t._v(\"功能型Widget指的是不会影响UI布局及外观的Widget，它们通常具有一定的功能，如事件监听、数据存储等，我们之前介绍过的FocusScope（焦点控制）、PageStorage（数据存储）、NotificationListener（事件监听）都属于功能型Widget。由于Widget是Flutter的一等公民，功能型Widget非常多，我们不会去一一介绍，本章中主要介绍几种常用的功能型Widget。\")]),t._v(\" \"),r(\"h2\",{attrs:{id:\"本章目录\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本章目录\"}},[t._v(\"#\")]),t._v(\" 本章目录\")]),t._v(\" \"),r(\"ul\",[r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/v2/chapter7/willpopscope.html\"}},[t._v(\"7.1：导航返回拦截（WillPopScope）\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/v2/chapter7/inherited_widget.html\"}},[t._v(\"7.2：数据共享（InheritedWidget）\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/v2/chapter7/provider.html\"}},[t._v(\"7.3： 跨组件状态共享（Provider）\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/v2/chapter7/theme.html\"}},[t._v(\"7.4：颜色和主题（Theme）\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/v2/chapter7/futurebuilder_and_streambuilder.html\"}},[t._v(\"7.5：异步UI更新（FutureBuilder、StreamBuilder）\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/v2/chapter7/dailog.html\"}},[t._v(\"7.6：对话框详解\")])],1)])])}),[],!1,null,null,null);e.default=a.exports}}]);"
  },
  {
    "path": "docs/assets/js/215.807d3ade.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[215],{920:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_7-1-导航返回拦截-willpopscope\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_7-1-导航返回拦截-willpopscope\"}},[t._v(\"#\")]),t._v(\" 7.1 导航返回拦截（WillPopScope）\")]),t._v(\" \"),a(\"p\",[t._v(\"为了避免用户误触返回按钮而导致APP退出，在很多APP中都拦截了用户点击返回键的按钮，然后进行一些防误触判断，比如当用户在某一个时间段内点击两次时，才会认为用户是要退出（而非误触）。Flutter中可以通过\"),a(\"code\",[t._v(\"WillPopScope\")]),t._v(\"来实现返回按钮拦截，我们看看\"),a(\"code\",[t._v(\"WillPopScope\")]),t._v(\"的默认构造函数：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"WillPopScope\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" WillPopCallback onWillPop\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" Widget child\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[a(\"code\",[t._v(\"onWillPop\")]),t._v(\"是一个回调函数，当用户点击返回按钮时被调用（包括导航返回按钮及Android物理返回按钮）。该回调需要返回一个\"),a(\"code\",[t._v(\"Future\")]),t._v(\"对象，如果返回的\"),a(\"code\",[t._v(\"Future\")]),t._v(\"最终值为\"),a(\"code\",[t._v(\"false\")]),t._v(\"时，则当前路由不出栈(不会返回)；最终值为\"),a(\"code\",[t._v(\"true\")]),t._v(\"时，当前路由出栈退出。我们需要提供这个回调来决定是否退出。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"示例\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),a(\"p\",[t._v(\"为了防止用户误触返回键退出，我们拦截返回事件。当用户在1秒内点击两次返回按钮时，则退出；如果间隔超过1秒则不退出，并重新记时。代码如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"WillPopScopeTestRoute\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  WillPopScopeTestRouteState \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"WillPopScopeTestRouteState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"WillPopScopeTestRouteState\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"WillPopScopeTestRoute\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  DateTime _lastPressedAt\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//上次点击时间\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"WillPopScope\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        onWillPop\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_lastPressedAt \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"||\")]),t._v(\"\\n              DateTime\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"now\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"difference\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_lastPressedAt\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//两次点击间隔超过1秒则重新计时\")]),t._v(\"\\n            _lastPressedAt \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" DateTime\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"now\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"1秒内连续按两次返回键退出\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"读者可以运行示例看看效果。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/216.a7c3d830.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[216],{921:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_8-3-事件总线\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_8-3-事件总线\"}},[t._v(\"#\")]),t._v(\" 8.3 事件总线\")]),t._v(\" \"),a(\"p\",[t._v(\"在APP中，我们经常会需要一个广播机制，用以跨页面事件通知，比如一个需要登录的APP中，页面会关注用户登录或注销事件，来进行一些状态更新。这时候，一个事件总线便会非常有用，事件总线通常实现了订阅者模式，订阅者模式包含发布者和订阅者两种角色，可以通过事件总线来触发事件和监听事件，本节我们实现一个简单的全局事件总线，我们使用单例模式，代码如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//订阅者回调签名\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"typedef\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"EventCallback\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"arg\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"EventBus\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//私有构造函数\")]),t._v(\"\\n  EventBus\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_internal\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//保存单例\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" EventBus _singleton \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"EventBus\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"_internal\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//工厂构造函数\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"factory\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"EventBus\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _singleton\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//保存事件订阅者队列，key:事件名(id)，value: 对应事件的订阅者队列\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" _emap \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Map\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Object\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" List\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"EventCallback\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">>\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//添加订阅者\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"on\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"eventName\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" EventCallback f\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"eventName \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"||\")]),t._v(\" f \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    _emap\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"eventName\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"List\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"EventCallback\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    _emap\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"eventName\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"f\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//移除订阅者\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"off\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"eventName\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"EventCallback f\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" list \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _emap\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"eventName\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"eventName \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"||\")]),t._v(\" list \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"f \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _emap\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"eventName\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      list\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"remove\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"f\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//触发事件，事件触发后该事件所有订阅者会被调用\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"emit\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"eventName\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"arg\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" list \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _emap\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"eventName\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"list \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    int len \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" list\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"length \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//反向遍历，防止订阅者在回调中移除自身带来的下标错位 \")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"for\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" i \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" len\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" i \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"--\")]),t._v(\"i\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      list\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"i\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"arg\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//定义一个top-level（全局）变量，页面引入该文件后可以直接使用bus\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" bus \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"EventBus\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"使用示例：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//页面A中\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//监听登录事件\")]),t._v(\"\\nbus\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"on\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"login\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"arg\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// do something\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//登录页B中\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//登录成功后触发登录事件，页面A中订阅者会被调用\")]),t._v(\"\\nbus\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"emit\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"login\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" userInfo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\")])])]),a(\"blockquote\",[a(\"p\",[t._v(\"注意：Dart中实现单例模式的标准做法就是使用static变量+工厂构造函数的方式，这样就可以保证\"),a(\"code\",[t._v(\"new EventBus()\")]),t._v(\"始终返回都是同一个实例，读者应该理解并掌握这种方法。\")])]),t._v(\" \"),a(\"p\",[t._v(\"事件总线通常用于组件之间状态共享，但关于组件之间状态共享也有一些专门的包如redux、以及前面介绍过的Provider。对于一些简单的应用，事件总线是足以满足业务需求的，如果你决定使用状态管理包的话，一定要想清楚您的APP是否真的有必要使用它，防止“化简为繁”、过度设计。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/217.ad95582b.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[217],{923:function(t,e,r){\"use strict\";r.r(e);var a=r(45),i=Object(a.a)({},(function(){var t=this,e=t.$createElement,r=t._self._c||e;return r(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[r(\"h2\",{attrs:{id:\"事件处理与通知\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#事件处理与通知\"}},[t._v(\"#\")]),t._v(\" 事件处理与通知\")]),t._v(\" \"),r(\"p\",[t._v(\"Flutter中的手势系统有两个独立的层。第一层为原始指针(pointer)事件，它描述了屏幕上指针（例如，触摸、鼠标和触控笔）的位置和移动。 第二层为手势，描述由一个或多个指针移动组成的语义动作，如拖动、缩放、双击等。本章将先分别介绍如何处理这两种事件，最后再介绍一下Flutter中重要的Notification机制。\")]),t._v(\" \"),r(\"h2\",{attrs:{id:\"本章目录\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本章目录\"}},[t._v(\"#\")]),t._v(\" 本章目录\")]),t._v(\" \"),r(\"ul\",[r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/v2/chapter8/listener.html\"}},[t._v(\"原始指针事件处理\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/v2/chapter8/gesture.html\"}},[t._v(\"手势识别\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/v2/chapter8/eventbus.html\"}},[t._v(\"全局事件总线\")])],1),t._v(\" \"),r(\"li\",[r(\"RouterLink\",{attrs:{to:\"/v2/chapter8/notification.html\"}},[t._v(\"通知Notification\")])],1)])])}),[],!1,null,null,null);e.default=i.exports}}]);"
  },
  {
    "path": "docs/assets/js/218.059e8703.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[218],{929:function(t,a,n){\"use strict\";n.r(a);var s=n(45),e=Object(s.a)({},(function(){var t=this,a=t.$createElement,n=t._self._c||a;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_9-4-hero动画\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_9-4-hero动画\"}},[t._v(\"#\")]),t._v(\" 9.4 Hero动画\")]),t._v(\" \"),n(\"p\",[t._v(\"Hero指的是可以在路由(页面)之间“飞行”的widget，简单来说Hero动画就是在路由切换时，有一个共享的widget可以在新旧路由间切换。由于共享的widget在新旧路由页面上的位置、外观可能有所差异，所以在路由切换时会从旧路逐渐过渡到新路由中的指定位置，这样就会产生一个Hero动画。\")]),t._v(\" \"),n(\"p\",[t._v(\"你可能多次看到过 hero 动画。例如，一个路由中显示待售商品的缩略图列表，选择一个条目会将其跳转到一个新路由，新路由中包含该商品的详细信息和“购买”按钮。 在Flutter中将图片从一个路由“飞”到另一个路由称为\"),n(\"strong\",[t._v(\"hero动画\")]),t._v(\"，尽管相同的动作有时也称为 \"),n(\"strong\",[t._v(\"共享元素转换\")]),t._v(\"。下面我们通过一个示例来体验一下hero 动画。\")]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[t._v(\"为什么要将这种可飞行的共享组件称为hero（英雄），有一种说法是说美国文化中的超人是可以飞的，那是美国人心中的大英雄，还有漫威中的超级英雄基本上都是会飞的，所以Flutter开发人员就对这种“会飞的widget”就起了一个富有浪漫主义的名字hero。当然这种说法并非官方解释，但却很有意思。\")])]),t._v(\" \"),n(\"h4\",{attrs:{id:\"示例\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),n(\"p\",[t._v(\"假设有两个路由A和B，他们的内容交互如下：\")]),t._v(\" \"),n(\"p\",[t._v(\"A：包含一个用户头像，圆形，点击后跳到B路由，可以查看大图。\")]),t._v(\" \"),n(\"p\",[t._v(\"B：显示用户头像原图，矩形；\")]),t._v(\" \"),n(\"p\",[t._v(\"在AB两个路由之间跳转的时候，用户头像会逐渐过渡到目标路由页的头像上，接下来我们先看看代码，然后再解析：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 路由A\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"HeroAnimationRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"topCenter\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"InkWell\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Hero\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          tag\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"avatar\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//唯一标记，前后两个路由页Hero的tag必须相同\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ClipOval\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"asset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"images/avatar.png\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        onTap\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//打开B路由  \")]),t._v(\"\\n          Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"push\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"PageRouteBuilder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              pageBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Animation animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  Animation secondaryAnimation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FadeTransition\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  opacity\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    appBar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                      title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"原图\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    body\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"HeroAnimationRouteB\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"路由B:\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"HeroAnimationRouteB\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Hero\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          tag\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"avatar\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//唯一标记，前后两个路由页Hero的tag必须相同\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"asset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"images/avatar.png\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们可以看到，实现Hero动画只需要用\"),n(\"code\",[t._v(\"Hero\")]),t._v(\"组件将要共享的widget包装起来，并提供一个相同的tag即可，中间的过渡帧都是Flutter Framework自动完成的。必须要注意， 前后路由页的共享\"),n(\"code\",[t._v(\"Hero\")]),t._v(\"的tag必须是相同的，Flutter Framework内部正是通过tag来确定新旧路由页widget的对应关系的。\")]),t._v(\" \"),n(\"p\",[t._v(\"Hero动画的原理比较简单，Flutter Framework知道新旧路由页中共享元素的位置和大小，所以根据这两个端点，在动画执行过程中求出过渡时的插值（中间态）即可，而感到幸运的是，这些事情不需要我们自己动手，Flutter已经帮我们做了！\")])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/219.70d1d263.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[219],{930:function(t,r,e){\"use strict\";e.r(r);var a=e(45),i=Object(a.a)({},(function(){var t=this,r=t.$createElement,e=t._self._c||r;return e(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[e(\"h2\",{attrs:{id:\"简介\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#简介\"}},[t._v(\"#\")]),t._v(\" 简介\")]),t._v(\" \"),e(\"p\",[t._v(\"精心设计的动画会让用户界面感觉更直观、流畅，能改善用户体验。 Flutter可以轻松实现各种动画类型，对于许多widget，特别是\"),e(\"a\",{attrs:{href:\"https://flutter.io/docs/reference/widgets/material\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Material Design widgets\"),e(\"OutboundLink\")],1),t._v(\"，都带有在其设计规范中定义的标准动画效果(但也可以自定义这些效果)。本章将详细介绍Flutter的动画系统，并会通过几个小实例来演示，以帮助开发者迅速理解并掌握动画的开发流程与原理。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"本章目录\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本章目录\"}},[t._v(\"#\")]),t._v(\" 本章目录\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter9/intro.html\"}},[t._v(\"9.1：Flutter动画简介\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter9/animation_structure.html\"}},[t._v(\"9.2：动画结构\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter9/route_transition.html\"}},[t._v(\"9.3：自定义路由过渡动画\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter9/hero.html\"}},[t._v(\"9.4：Hero动画\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter9/stagger_animation.html\"}},[t._v(\"9.5：交织动画\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter9/animated_switcher.html\"}},[t._v(\"9.6：通用“动画切换”组件（AnimatedSwitcher）\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter9/animated_widgets.html\"}},[t._v(\"9.7：动画过渡组件\")])],1)])])}),[],!1,null,null,null);r.default=i.exports}}]);"
  },
  {
    "path": "docs/assets/js/22.4fdaa56f.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[22],{434:function(t,a,s){t.exports=s.p+\"assets/img/3-5.e95eb747.png\"},435:function(t,a){t.exports=\"data:image/png;base64,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\"},436:function(t,a){t.exports=\"data:image/png;base64,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\"},437:function(t,a,s){t.exports=s.p+\"assets/img/3-8.cc415da2.png\"},438:function(t,a,s){t.exports=s.p+\"assets/img/3-9.bb214e70.png\"},780:function(t,a,s){\"use strict\";s.r(a);var n=s(45),e=Object(n.a)({},(function(){var t=this,a=t.$createElement,n=t._self._c||a;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h2\",{attrs:{id:\"_3-3-文本及样式\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-3-文本及样式\"}},[t._v(\"#\")]),t._v(\" 3.3 文本及样式\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"_3-3-1-text\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-3-1-text\"}},[t._v(\"#\")]),t._v(\" 3.3.1 Text\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"Text\")]),t._v(\"用于显示简单样式文本，它包含一些控制文本显示样式的一些属性，一个简单的例子如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hello world\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  textAlign\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" TextAlign\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"left\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hello world! I\\'m Jack. \"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"4\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  maxLines\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  overflow\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" TextOverflow\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"ellipsis\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hello world\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  textScaleFactor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.5\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图3-5所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(434),alt:\"image-20180829103242552\"}})]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"textAlign\")]),t._v(\"：文本的对齐方式；可以选择左对齐、右对齐还是居中。注意，对齐的参考系是Text widget本身。本例中虽然是指定了居中对齐，但因为Text文本内容宽度不足一行，Text的宽度和文本内容长度相等，那么这时指定对齐方式是没有意义的，只有Text宽度大于文本内容长度时指定此属性才有意义。下面我们指定一个较长的字符串：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hello world \"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//字符串重复六次\")]),t._v(\"\\n  textAlign\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" TextAlign\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"；\\n\")])])]),n(\"p\",[t._v(\"运行效果如图3-6所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(435),alt:\"image-20180829104807535\"}})])])]),t._v(\" \"),n(\"p\",[t._v(\"​      字符串内容超过一行，Text宽度等于屏幕宽度，第二行文本便会居中显示。\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"maxLines\")]),t._v(\"、\"),n(\"code\",[t._v(\"overflow\")]),t._v(\"：指定文本显示的最大行数，默认情况下，文本是自动折行的，如果指定此参数，则文本最多不会超过指定的行。如果有多余的文本，可以通过\"),n(\"code\",[t._v(\"overflow\")]),t._v(\"来指定截断方式，默认是直接截断，本例中指定的截断方式\"),n(\"code\",[t._v(\"TextOverflow.ellipsis\")]),t._v(\"，它会将多余文本截断后以省略符“...”表示；TextOverflow的其它截断方式请参考SDK文档。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"textScaleFactor\")]),t._v(\"：代表文本相对于当前字体大小的缩放因子，相对于去设置文本的样式\"),n(\"code\",[t._v(\"style\")]),t._v(\"属性的\"),n(\"code\",[t._v(\"fontSize\")]),t._v(\"，它是调整字体大小的一个快捷方式。该属性的默认值可以通过\"),n(\"code\",[t._v(\"MediaQueryData.textScaleFactor\")]),t._v(\"获得，如果没有\"),n(\"code\",[t._v(\"MediaQuery\")]),t._v(\"，那么会默认值将为1.0。\")])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"_3-3-2-textstyle\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-3-2-textstyle\"}},[t._v(\"#\")]),t._v(\" 3.3.2 TextStyle\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"TextStyle\")]),t._v(\"用于指定文本显示的样式如颜色、字体、粗细、背景等。我们看一个示例：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hello world\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    fontSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"18.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"  \\n    fontFamily\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Courier\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    background\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Paint\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"yellow\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    decoration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"TextDecoration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"underline\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    decorationStyle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" TextDecorationStyle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dashed\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"效果如图3-7所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(436),alt:\"3-7\"}})]),t._v(\" \"),n(\"p\",[t._v(\"此示例只展示了TextStyle的部分属性，它还有一些其它属性，属性名基本都是自解释的，在此不再赘述，读者可以查阅SDK文档。值得注意的是：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"height\")]),t._v(\"：该属性用于指定行高，但它并不是一个绝对值，而是一个因子，具体的行高等于\"),n(\"code\",[t._v(\"fontSize\")]),t._v(\"*\"),n(\"code\",[t._v(\"height\")]),t._v(\"。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"fontFamily\")]),t._v(\" ：由于不同平台默认支持的字体集不同，所以在手动指定字体时一定要先在不同平台测试一下。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"fontSize\")]),t._v(\"：该属性和Text的\"),n(\"code\",[t._v(\"textScaleFactor\")]),t._v(\"都用于控制字体大小。但是有两个主要区别：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"fontSize\")]),t._v(\"可以精确指定字体大小，而\"),n(\"code\",[t._v(\"textScaleFactor\")]),t._v(\"只能通过缩放比例来控制。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"textScaleFactor\")]),t._v(\"主要是用于系统字体大小设置改变时对Flutter应用字体进行全局调整，而\"),n(\"code\",[t._v(\"fontSize\")]),t._v(\"通常用于单个文本，字体大小不会跟随系统字体大小变化。\")])])])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"_3-3-3-textspan\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-3-3-textspan\"}},[t._v(\"#\")]),t._v(\" 3.3.3 TextSpan\")]),t._v(\" \"),n(\"p\",[t._v(\"在上面的例子中，Text的所有文本内容只能按同一种样式，如果我们需要对一个Text内容的不同部分按照不同的样式显示，这时就可以使用\"),n(\"code\",[t._v(\"TextSpan\")]),t._v(\"，它代表文本的一个“片段”。我们看看TextSpan的定义:\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextSpan\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  TextStyle style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n  Sting text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  List\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"TextSpan\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  GestureRecognizer recognizer\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"其中\"),n(\"code\",[t._v(\"style\")]),t._v(\" 和 \"),n(\"code\",[t._v(\"text\")]),t._v(\"属性代表该文本片段的样式和内容。  \"),n(\"code\",[t._v(\"children\")]),t._v(\"是一个\"),n(\"code\",[t._v(\"TextSpan\")]),t._v(\"的数组，也就是说\"),n(\"code\",[t._v(\"TextSpan\")]),t._v(\"可以包括其他\"),n(\"code\",[t._v(\"TextSpan\")]),t._v(\"。而\"),n(\"code\",[t._v(\"recognizer\")]),t._v(\"用于对该文本片段上用于手势进行识别处理。下面我们看一个效果（图3-8），然后用\"),n(\"code\",[t._v(\"TextSpan\")]),t._v(\"实现它。\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(437),alt:\"3-8\"}})]),t._v(\" \"),n(\"p\",[t._v(\"源码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"rich\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextSpan\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextSpan\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n       text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Home: \"')]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextSpan\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n       text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"https://flutterchina.club\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n       style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n         color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\\n       \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"  \\n       recognizer\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _tapRecognizer\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"ul\",[n(\"li\",[t._v(\"上面代码中，我们通过TextSpan实现了一个基础文本片段和一个链接片段，然后通过\"),n(\"code\",[t._v(\"Text.rich\")]),t._v(\" 方法将\"),n(\"code\",[t._v(\"TextSpan\")]),t._v(\" 添加到Text中，之所以可以这样做，是因为Text其实就是RichText的一个包装，而RichText是可以显示多种样式(富文本)的widget。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"_tapRecognizer\")]),t._v(\"，它是点击链接后的一个处理器（代码已省略），关于手势识别的更多内容我们将在后面单独介绍。\")])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"_3-3-4-defaulttextstyle\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-3-4-defaulttextstyle\"}},[t._v(\"#\")]),t._v(\" 3.3.4 DefaultTextStyle\")]),t._v(\" \"),n(\"p\",[t._v(\"在Widget树中，文本的样式默认是可以被继承的（子类文本类组件未指定具体样式时可以使用Widget树中父级设置的默认样式），因此，如果在Widget树的某一个节点处设置一个默认的文本样式，那么该节点的子树中所有文本都会默认使用这个样式，而\"),n(\"code\",[t._v(\"DefaultTextStyle\")]),t._v(\"正是用于设置默认文本样式的。下面我们看一个例子：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DefaultTextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//1.设置文本默认样式  \")]),t._v(\"\\n  style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    fontSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  textAlign\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" TextAlign\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    crossAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" CrossAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"hello world\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"I am Jack\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"I am Jack\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          inherit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//2.不继承默认样式\")]),t._v(\"\\n          color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上面代码中，我们首先设置了一个默认的文本样式，即字体为20像素(逻辑像素)、颜色为红色。然后通过\"),n(\"code\",[t._v(\"DefaultTextStyle\")]),t._v(\" 设置给了子树Column节点处，这样一来Column的所有子孙Text默认都会继承该样式，除非Text显示指定不继承样式，如代码中注释2。示例运行效果如图3-9：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(438),alt:\"3-9\"}})]),t._v(\" \"),n(\"h3\",{attrs:{id:\"_3-3-5-字体\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-3-5-字体\"}},[t._v(\"#\")]),t._v(\" 3.3.5 字体\")]),t._v(\" \"),n(\"p\",[t._v(\"可以在Flutter应用程序中使用不同的字体。例如，我们可能会使用设计人员创建的自定义字体，或者其它第三方的字体，如\"),n(\"a\",{attrs:{href:\"https://fonts.google.com/\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Google Fonts\"),n(\"OutboundLink\")],1),t._v(\"中的字体。本节将介绍如何为Flutter应用配置字体，并在渲染文本时使用它们。\")]),t._v(\" \"),n(\"p\",[t._v(\"在Flutter中使用字体分两步完成。首先在\"),n(\"code\",[t._v(\"pubspec.yaml\")]),t._v(\"中声明它们，以确保它们会打包到应用程序中。然后通过\"),n(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/painting/TextStyle-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[n(\"code\",[t._v(\"TextStyle\")]),n(\"OutboundLink\")],1),t._v(\"属性使用字体。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"在asset中声明\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#在asset中声明\"}},[t._v(\"#\")]),t._v(\" 在asset中声明\")]),t._v(\" \"),n(\"p\",[t._v(\"要将字体文件打包到应用中，和使用其它资源一样，要先在\"),n(\"code\",[t._v(\"pubspec.yaml\")]),t._v(\"中声明它。然后将字体文件复制到在\"),n(\"code\",[t._v(\"pubspec.yaml\")]),t._v(\"中指定的位置。如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-yaml extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"flutter\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"fonts\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"family\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Raleway\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"fonts\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"asset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" assets/fonts/Raleway\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\"Regular.ttf\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"asset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" assets/fonts/Raleway\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\"Medium.ttf\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"weight\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"500\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"asset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" assets/fonts/Raleway\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\"SemiBold.ttf\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"weight\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"600\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"family\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" AbrilFatface\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"fonts\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"asset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" assets/fonts/abrilfatface/AbrilFatface\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\"Regular.ttf\\n\")])])]),n(\"h4\",{attrs:{id:\"使用字体\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#使用字体\"}},[t._v(\"#\")]),t._v(\" 使用字体\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 声明文本样式\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" textStyle \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  fontFamily\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Raleway'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 使用文本样式\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" buttonText \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Use the font for this text\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" textStyle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"h4\",{attrs:{id:\"package中的字体\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#package中的字体\"}},[t._v(\"#\")]),t._v(\" Package中的字体\")]),t._v(\" \"),n(\"p\",[t._v(\"要使用Package中定义的字体，\"),n(\"strong\",[t._v(\"必须提供\"),n(\"code\",[t._v(\"package\")]),t._v(\"参数\")]),t._v(\"。例如，假设上面的字体声明位于\"),n(\"code\",[t._v(\"my_package\")]),t._v(\"包中。然后创建TextStyle的过程如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" textStyle \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  fontFamily\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Raleway'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  package\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'my_package'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//指定包名\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"如果在package包内部使用它自己定义的字体，也应该在创建文本样式时指定\"),n(\"code\",[t._v(\"package\")]),t._v(\"参数，如上例所示。\")]),t._v(\" \"),n(\"p\",[t._v(\"一个包也可以只提供字体文件而不需要在pubspec.yaml中声明。 这些文件应该存放在包的\"),n(\"code\",[t._v(\"lib/\")]),t._v(\"文件夹中。字体文件不会自动绑定到应用程序中，应用程序可以在声明字体时有选择地使用这些字体。假设一个名为my_package的包中有一个字体文件：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language- extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[n(\"code\",[t._v(\"lib/fonts/Raleway-Medium.ttf\\n\")])])]),n(\"p\",[t._v(\"然后，应用程序可以声明一个字体，如下面的示例所示：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-yaml extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[n(\"code\",[t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"flutter\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"fonts\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"family\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Raleway\\n       \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"fonts\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n         \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"asset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" assets/fonts/Raleway\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\"Regular.ttf\\n         \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"asset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" packages/my_package/fonts/Raleway\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\"Medium.ttf\\n           \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"weight\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"500\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"lib/\")]),t._v(\"是隐含的，所以它不应该包含在asset路径中。\")]),t._v(\" \"),n(\"p\",[t._v(\"在这种情况下，由于应用程序本地定义了字体，所以在创建TextStyle时可以不指定\"),n(\"code\",[t._v(\"package\")]),t._v(\"参数：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" textStyle \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  fontFamily\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Raleway'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/220.69e2bb54.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[220],{931:function(t,a,n){\"use strict\";n.r(a);var s=n(45),e=Object(s.a)({},(function(){var t=this,a=t.$createElement,n=t._self._c||a;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_9-1-flutter动画简介\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_9-1-flutter动画简介\"}},[t._v(\"#\")]),t._v(\" 9.1 Flutter动画简介\")]),t._v(\" \"),n(\"p\",[t._v(\"在任何系统的UI框架中，动画实现的原理都是相同的，即：在一段时间内，快速地多次改变UI外观；由于人眼会产生视觉暂留，所以最终看到的就是一个“连续”的动画，这和电影的原理是一样的。我们将UI的一次改变称为一个动画帧，对应一次屏幕刷新，而决定动画流畅度的一个重要指标就是帧率FPS（Frame Per Second），即每秒的动画帧数。很明显，帧率越高则动画就会越流畅！一般情况下，对于人眼来说，动画帧率超过16FPS，就比较流畅了，超过32FPS就会非常的细腻平滑，而超过32FPS，人眼基本上就感受不到差别了。由于动画的每一帧都是要改变UI输出，所以在一个时间段内连续的改变UI输出是比较耗资源的，对设备的软硬件系统要求都较高，所以在UI系统中，动画的平均帧率是重要的性能指标，而在Flutter中，理想情况下是可以实现60FPS的，这和原生应用能达到的帧率是基本是持平的。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"flutter中动画抽象\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#flutter中动画抽象\"}},[t._v(\"#\")]),t._v(\" Flutter中动画抽象\")]),t._v(\" \"),n(\"p\",[t._v(\"为了方便开发者创建动画，不同的UI系统对动画都进行了一些抽象，比如在Android中可以通过XML来描述一个动画然后设置给View。Flutter中也对动画进行了抽象，主要涉及Animation、Curve、Controller、Tween这四个角色，它们一起配合来完成一个完整动画，下面我们一一来介绍它们。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"animation\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#animation\"}},[t._v(\"#\")]),t._v(\" Animation\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"Animation\")]),t._v(\"是一个抽象类，它本身和UI渲染没有任何关系，而它主要的功能是保存动画的插值和状态；其中一个比较常用的\"),n(\"code\",[t._v(\"Animation\")]),t._v(\"类是\"),n(\"code\",[t._v(\"Animation<double>\")]),t._v(\"。\"),n(\"code\",[t._v(\"Animation\")]),t._v(\"对象是一个在一段时间内依次生成一个区间(Tween)之间值的类。\"),n(\"code\",[t._v(\"Animation\")]),t._v(\"对象在整个动画执行过程中输出的值可以是线性的、曲线的、一个步进函数或者任何其他曲线函数等等，这由\"),n(\"code\",[t._v(\"Curve\")]),t._v(\"来决定。 根据\"),n(\"code\",[t._v(\"Animation\")]),t._v(\"对象的控制方式，动画可以正向运行（从起始状态开始，到终止状态结束），也可以反向运行，甚至可以在中间切换方向。\"),n(\"code\",[t._v(\"Animation\")]),t._v(\"还可以生成除\"),n(\"code\",[t._v(\"double\")]),t._v(\"之外的其他类型值，如：\"),n(\"code\",[t._v(\"Animation<Color>\")]),t._v(\" 或\"),n(\"code\",[t._v(\"Animation<Size>\")]),t._v(\"。在动画的每一帧中，我们可以通过\"),n(\"code\",[t._v(\"Animation\")]),t._v(\"对象的\"),n(\"code\",[t._v(\"value\")]),t._v(\"属性获取动画的当前状态值。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"动画通知\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#动画通知\"}},[t._v(\"#\")]),t._v(\" 动画通知\")]),t._v(\" \"),n(\"p\",[t._v(\"我们可以通过\"),n(\"code\",[t._v(\"Animation\")]),t._v(\"来监听动画每一帧以及执行状态的变化，\"),n(\"code\",[t._v(\"Animation\")]),t._v(\"有如下两个方法：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"code\",[t._v(\"addListener()\")]),t._v(\"；它可以用于给\"),n(\"code\",[t._v(\"Animation\")]),t._v(\"添加帧监听器，在每一帧都会被调用。帧监听器中最常见的行为是改变状态后调用\"),n(\"code\",[t._v(\"setState()\")]),t._v(\"来触发UI重建。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"addStatusListener()\")]),t._v(\"；它可以给\"),n(\"code\",[t._v(\"Animation\")]),t._v(\"添加“动画状态改变”监听器；动画开始、结束、正向或反向（见\"),n(\"code\",[t._v(\"AnimationStatus\")]),t._v(\"定义）时会调用状态改变的监听器。\")])]),t._v(\" \"),n(\"p\",[t._v(\"读者在此只需要知道帧监听器和状态监听器的区别，在后面的章节中我们将会举例说明。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"curve\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#curve\"}},[t._v(\"#\")]),t._v(\" Curve\")]),t._v(\" \"),n(\"p\",[t._v(\"动画过程可以是匀速的、匀加速的或者先加速后减速等。Flutter中通过\"),n(\"code\",[t._v(\"Curve\")]),t._v(\"（曲线）来描述动画过程，我们把匀速动画称为线性的(Curves.linear)，而非匀速动画称为非线性的。\")]),t._v(\" \"),n(\"p\",[t._v(\"我们可以通过\"),n(\"code\",[t._v(\"CurvedAnimation\")]),t._v(\"来指定动画的曲线，如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" CurvedAnimation curve \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CurvedAnimation\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"parent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" curve\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Curves\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"easeIn\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"CurvedAnimation\")]),t._v(\"和\"),n(\"code\",[t._v(\"AnimationController\")]),t._v(\"（下面介绍）都是\"),n(\"code\",[t._v(\"Animation<double>\")]),t._v(\"类型。\"),n(\"code\",[t._v(\"CurvedAnimation\")]),t._v(\"可以通过包装\"),n(\"code\",[t._v(\"AnimationController\")]),t._v(\"和\"),n(\"code\",[t._v(\"Curve\")]),t._v(\"生成一个新的动画对象 ，我们正是通过这种方式来将动画和动画执行的曲线关联起来的。我们指定动画的曲线为\"),n(\"code\",[t._v(\"Curves.easeIn\")]),t._v(\"，它表示动画开始时比较慢，结束时比较快。 \"),n(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/animation/Curves-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Curves\"),n(\"OutboundLink\")],1),t._v(\" 类是一个预置的枚举类，定义了许多常用的曲线，下面列几种常用的：\")]),t._v(\" \"),n(\"table\",[n(\"thead\",[n(\"tr\",[n(\"th\",[t._v(\"Curves曲线\")]),t._v(\" \"),n(\"th\",[t._v(\"动画过程\")])])]),t._v(\" \"),n(\"tbody\",[n(\"tr\",[n(\"td\",[t._v(\"linear\")]),t._v(\" \"),n(\"td\",[t._v(\"匀速的\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"decelerate\")]),t._v(\" \"),n(\"td\",[t._v(\"匀减速\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"ease\")]),t._v(\" \"),n(\"td\",[t._v(\"开始加速，后面减速\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"easeIn\")]),t._v(\" \"),n(\"td\",[t._v(\"开始慢，后面快\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"easeOut\")]),t._v(\" \"),n(\"td\",[t._v(\"开始快，后面慢\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"easeInOut\")]),t._v(\" \"),n(\"td\",[t._v(\"开始慢，然后加速，最后再减速\")])])])]),t._v(\" \"),n(\"p\",[t._v(\"除了上面列举的， \"),n(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/animation/Curves-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Curves\"),n(\"OutboundLink\")],1),t._v(\" 类中还定义了许多其它的曲线，在此便不一一介绍，读者可以自行查看Curves类定义。\")]),t._v(\" \"),n(\"p\",[t._v(\"当然我们也可以创建自己Curve，例如我们定义一个正弦曲线：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ShakeCurve\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Curve\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  double \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"transform\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"double t\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" math\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"sin\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"t \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" math\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"PI \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"h3\",{attrs:{id:\"animationcontroller\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#animationcontroller\"}},[t._v(\"#\")]),t._v(\" AnimationController\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"AnimationController\")]),t._v(\"用于控制动画，它包含动画的启动\"),n(\"code\",[t._v(\"forward()\")]),t._v(\"、停止\"),n(\"code\",[t._v(\"stop()\")]),t._v(\" 、反向播放 \"),n(\"code\",[t._v(\"reverse()\")]),t._v(\"等方法。\"),n(\"code\",[t._v(\"AnimationController\")]),t._v(\"会在动画的每一帧，就会生成一个新的值。默认情况下，\"),n(\"code\",[t._v(\"AnimationController\")]),t._v(\"在给定的时间段内线性的生成从0.0到1.0（默认区间）的数字。 例如，下面代码创建一个\"),n(\"code\",[t._v(\"Animation\")]),t._v(\"对象（但不会启动动画）：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" AnimationController controller \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimationController\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    duration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"milliseconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2000\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" vsync\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"AnimationController\")]),t._v(\"生成数字的区间可以通过\"),n(\"code\",[t._v(\"lowerBound\")]),t._v(\"和\"),n(\"code\",[t._v(\"upperBound\")]),t._v(\"来指定，如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" AnimationController controller \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimationController\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \\n duration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"milliseconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2000\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n lowerBound\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"10.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n upperBound\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n vsync\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"AnimationController\")]),t._v(\"派生自\"),n(\"code\",[t._v(\"Animation<double>\")]),t._v(\"，因此可以在需要\"),n(\"code\",[t._v(\"Animation\")]),t._v(\"对象的任何地方使用。 但是，\"),n(\"code\",[t._v(\"AnimationController\")]),t._v(\"具有控制动画的其他方法，例如\"),n(\"code\",[t._v(\"forward()\")]),t._v(\"方法可以启动正向动画，\"),n(\"code\",[t._v(\"reverse()\")]),t._v(\"可以启动反向动画。在动画开始执行后开始生成动画帧，屏幕每刷新一次就是一个动画帧，在动画的每一帧，会随着根据动画的曲线来生成当前的动画值（\"),n(\"code\",[t._v(\"Animation.value\")]),t._v(\"），然后根据当前的动画值去构建UI，当所有动画帧依次触发时，动画值会依次改变，所以构建的UI也会依次变化，所以最终我们可以看到一个完成的动画。 另外在动画的每一帧，\"),n(\"code\",[t._v(\"Animation\")]),t._v(\"对象会调用其帧监听器，等动画状态发生改变时（如动画结束）会调用状态改变监听器。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"duration\")]),t._v(\"表示动画执行的时长，通过它我们可以控制动画的速度。\")]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[n(\"strong\",[t._v(\"注意\")]),t._v(\"： 在某些情况下，动画值可能会超出\"),n(\"code\",[t._v(\"AnimationController\")]),t._v(\"的[0.0，1.0]的范围，这取决于具体的曲线。例如，\"),n(\"code\",[t._v(\"fling()\")]),t._v(\"函数可以根据我们手指滑动（甩出）的速度(velocity)、力量(force)等来模拟一个手指甩出动画，因此它的动画值可以在[0.0，1.0]范围之外 。也就是说，根据选择的曲线，\"),n(\"code\",[t._v(\"CurvedAnimation\")]),t._v(\"的输出可以具有比输入更大的范围。例如，Curves.elasticIn等弹性曲线会生成大于或小于默认范围的值。\")])]),t._v(\" \"),n(\"h4\",{attrs:{id:\"ticker\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#ticker\"}},[t._v(\"#\")]),t._v(\" Ticker\")]),t._v(\" \"),n(\"p\",[t._v(\"当创建一个\"),n(\"code\",[t._v(\"AnimationController\")]),t._v(\"时，需要传递一个\"),n(\"code\",[t._v(\"vsync\")]),t._v(\"参数，它接收一个\"),n(\"code\",[t._v(\"TickerProvider\")]),t._v(\"类型的对象，它的主要职责是创建\"),n(\"code\",[t._v(\"Ticker\")]),t._v(\"，定义如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"abstract\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TickerProvider\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//通过一个回调创建一个Ticker\")]),t._v(\"\\n  Ticker \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createTicker\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"TickerCallback onTick\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"Flutter应用在启动时都会绑定一个\"),n(\"code\",[t._v(\"SchedulerBinding\")]),t._v(\"，通过\"),n(\"code\",[t._v(\"SchedulerBinding\")]),t._v(\"可以给每一次屏幕刷新添加回调，而\"),n(\"code\",[t._v(\"Ticker\")]),t._v(\"就是通过\"),n(\"code\",[t._v(\"SchedulerBinding\")]),t._v(\"来添加屏幕刷新回调，这样一来，每次屏幕刷新都会调用\"),n(\"code\",[t._v(\"TickerCallback\")]),t._v(\"。使用\"),n(\"code\",[t._v(\"Ticker\")]),t._v(\"(而不是\"),n(\"code\",[t._v(\"Timer\")]),t._v(\")来驱动动画会防止屏幕外动画（动画的UI不在当前屏幕时，如锁屏时）消耗不必要的资源，因为Flutter中屏幕刷新时会通知到绑定的\"),n(\"code\",[t._v(\"SchedulerBinding\")]),t._v(\"，而\"),n(\"code\",[t._v(\"Ticker\")]),t._v(\"是受\"),n(\"code\",[t._v(\"SchedulerBinding\")]),t._v(\"驱动的，由于锁屏后屏幕会停止刷新，所以\"),n(\"code\",[t._v(\"Ticker\")]),t._v(\"就不会再触发。\")]),t._v(\" \"),n(\"p\",[t._v(\"通常我们会将\"),n(\"code\",[t._v(\"SingleTickerProviderStateMixin\")]),t._v(\"添加到\"),n(\"code\",[t._v(\"State\")]),t._v(\"的定义中，然后将State对象作为\"),n(\"code\",[t._v(\"vsync\")]),t._v(\"的值，这在后面的例子中可以见到。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"tween\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#tween\"}},[t._v(\"#\")]),t._v(\" Tween\")]),t._v(\" \"),n(\"p\",[t._v(\"默认情况下，\"),n(\"code\",[t._v(\"AnimationController\")]),t._v(\"对象值的范围是[0.0，1.0]。如果我们需要构建UI的动画值在不同的范围或不同的数据类型，则可以使用\"),n(\"code\",[t._v(\"Tween\")]),t._v(\"来添加映射以生成不同的范围或数据类型的值。例如，像下面示例，\"),n(\"code\",[t._v(\"Tween\")]),t._v(\"生成[-200.0，0.0]的值：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Tween doubleTween \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Tween\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"begin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" end\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"Tween\")]),t._v(\"构造函数需要\"),n(\"code\",[t._v(\"begin\")]),t._v(\"和\"),n(\"code\",[t._v(\"end\")]),t._v(\"两个参数。\"),n(\"code\",[t._v(\"Tween\")]),t._v(\"的唯一职责就是定义从输入范围到输出范围的映射。输入范围通常为[0.0，1.0]，但这不是必须的，我们可以自定义需要的范围。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"Tween\")]),t._v(\"继承自\"),n(\"code\",[t._v(\"Animatable<T>\")]),t._v(\"，而不是继承自\"),n(\"code\",[t._v(\"Animation<T>\")]),t._v(\"，\"),n(\"code\",[t._v(\"Animatable\")]),t._v(\"中主要定义动画值的映射规则。\")]),t._v(\" \"),n(\"p\",[t._v(\"下面我们看一个ColorTween将动画输入范围映射为两种颜色值之间过渡输出的例子：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Tween colorTween \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ColorTween\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"begin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"transparent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" end\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"black54\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"Tween\")]),t._v(\"对象不存储任何状态，相反，它提供了\"),n(\"code\",[t._v(\"evaluate(Animation<double> animation)\")]),t._v(\"方法，它可以获取动画当前映射值。 \"),n(\"code\",[t._v(\"Animation\")]),t._v(\"对象的当前值可以通过\"),n(\"code\",[t._v(\"value()\")]),t._v(\"方法取到。\"),n(\"code\",[t._v(\"evaluate\")]),t._v(\"函数还执行一些其它处理，例如分别确保在动画值为0.0和1.0时返回开始和结束状态。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"tween-animate\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#tween-animate\"}},[t._v(\"#\")]),t._v(\" Tween.animate\")]),t._v(\" \"),n(\"p\",[t._v(\"要使用Tween对象，需要调用其\"),n(\"code\",[t._v(\"animate()\")]),t._v(\"方法，然后传入一个控制器对象。例如，以下代码在500毫秒内生成从0到255的整数值。\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" AnimationController controller \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimationController\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    duration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"milliseconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"500\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" vsync\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\nAnimation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"int\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" alpha \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"IntTween\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"begin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" end\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"255\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"animate\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"注意\"),n(\"code\",[t._v(\"animate()\")]),t._v(\"返回的是一个\"),n(\"code\",[t._v(\"Animation\")]),t._v(\"，而不是一个\"),n(\"code\",[t._v(\"Animatable\")]),t._v(\"。\")]),t._v(\" \"),n(\"p\",[t._v(\"以下示例构建了一个控制器、一条曲线和一个Tween：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" AnimationController controller \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimationController\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    duration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"milliseconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"500\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" vsync\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Animation curve \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CurvedAnimation\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"parent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" curve\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Curves\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"easeOut\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\nAnimation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"int\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" alpha \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"IntTween\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"begin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" end\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"255\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"animate\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"curve\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/221.51aa50d8.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[221],{932:function(t,a,s){\"use strict\";s.r(a);var n=s(45),e=Object(n.a)({},(function(){var t=this,a=t.$createElement,s=t._self._c||a;return s(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[s(\"h1\",{attrs:{id:\"_9-3-自定义路由切换动画\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_9-3-自定义路由切换动画\"}},[t._v(\"#\")]),t._v(\" 9.3 自定义路由切换动画\")]),t._v(\" \"),s(\"p\",[t._v(\"我们在第二章“路由管理”一节中讲过：Material组件库中提供了一个\"),s(\"code\",[t._v(\"MaterialPageRoute\")]),t._v(\"组件，它可以使用和平台风格一致的路由切换动画，如在iOS上会左右滑动切换，而在Android上会上下滑动切换。现在，我们如果在Android上也想使用左右切换风格，该怎么做？一个简单的作法是可以直接使用\"),s(\"code\",[t._v(\"CupertinoPageRoute\")]),t._v(\"，如：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\" Navigator\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"push\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CupertinoPageRoute\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"  \\n   builder\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"PageB\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"p\",[s(\"code\",[t._v(\"CupertinoPageRoute\")]),t._v(\"是Cupertino组件库提供的iOS风格的路由切换组件，它实现的就是左右滑动切换。那么我们如何来自定义路由切换动画呢？答案就是\"),s(\"code\",[t._v(\"PageRouteBuilder\")]),t._v(\"。下面我们来看看如何使用\"),s(\"code\",[t._v(\"PageRouteBuilder\")]),t._v(\"来自定义路由切换动画。例如我们想以渐隐渐入动画来实现路由过渡，实现代码如下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"Navigator\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"push\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"PageRouteBuilder\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    transitionDuration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"milliseconds\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"500\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//动画时间为500毫秒\")]),t._v(\"\\n    pageBuilder\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Animation animation\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        Animation secondaryAnimation\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FadeTransition\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//使用渐隐渐入过渡,\")]),t._v(\"\\n        opacity\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"PageB\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//路由B\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"我们可以看到\"),s(\"code\",[t._v(\"pageBuilder\")]),t._v(\" 有一个\"),s(\"code\",[t._v(\"animation\")]),t._v(\"参数，这是Flutter路由管理器提供的，在路由切换时\"),s(\"code\",[t._v(\"pageBuilder\")]),t._v(\"在每个动画帧都会被回调，因此我们可以通过\"),s(\"code\",[t._v(\"animation\")]),t._v(\"对象来自定义过渡动画。\")]),t._v(\" \"),s(\"p\",[t._v(\"无论是\"),s(\"code\",[t._v(\"MaterialPageRoute\")]),t._v(\"、\"),s(\"code\",[t._v(\"CupertinoPageRoute\")]),t._v(\"，还是\"),s(\"code\",[t._v(\"PageRouteBuilder\")]),t._v(\"，它们都继承自PageRoute类，而\"),s(\"code\",[t._v(\"PageRouteBuilder\")]),t._v(\"其实只是\"),s(\"code\",[t._v(\"PageRoute\")]),t._v(\"的一个包装，我们可以直接继承\"),s(\"code\",[t._v(\"PageRoute\")]),t._v(\"类来实现自定义路由，上面的例子可以通过如下方式实现：\")]),t._v(\" \"),s(\"ol\",[s(\"li\",[s(\"p\",[t._v(\"定义一个路由类\"),s(\"code\",[t._v(\"FadeRoute\")])]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FadeRoute\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"PageRoute\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FadeRoute\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"builder\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"transitionDuration \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"milliseconds\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"opaque \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"barrierDismissible \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"barrierColor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"barrierLabel\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"maintainState \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" WidgetBuilder builder\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Duration transitionDuration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" bool opaque\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" bool barrierDismissible\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Color barrierColor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String barrierLabel\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" bool maintainState\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"buildPage\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Animation\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" animation\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      Animation\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" secondaryAnimation\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"buildTransitions\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Animation\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" animation\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      Animation\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" secondaryAnimation\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Widget child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n     \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FadeTransition\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \\n       opacity\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n       child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n     \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])])]),t._v(\" \"),s(\"li\",[s(\"p\",[t._v(\"使用\"),s(\"code\",[t._v(\"FadeRoute\")])]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"Navigator\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"push\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FadeRoute\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"PageB\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])])])]),t._v(\" \"),s(\"p\",[t._v(\"虽然上面的两种方法都可以实现自定义切换动画，但实际使用时应优先考虑使用PageRouteBuilder，这样无需定义一个新的路由类，使用起来会比较方便。但是有些时候\"),s(\"code\",[t._v(\"PageRouteBuilder\")]),t._v(\"是不能满足需求的，例如在应用过渡动画时我们需要读取当前路由的一些属性，这时就只能通过继承\"),s(\"code\",[t._v(\"PageRoute\")]),t._v(\"的方式了，举个例子，假如我们只想在打开新路由时应用动画，而在返回时不使用动画，那么我们在构建过渡动画时就必须判断当前路由\"),s(\"code\",[t._v(\"isActive\")]),t._v(\"属性是否为\"),s(\"code\",[t._v(\"true\")]),t._v(\"，代码如下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\nWidget \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"buildTransitions\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Animation\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" animation\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    Animation\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" secondaryAnimation\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Widget child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//当前路由被激活，是打开新路由\")]),t._v(\"\\n \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"isActive\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FadeTransition\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n     opacity\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n     child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//是返回，则不应用过渡动画\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"padding\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"zero\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"关于路由参数的详细信息读者可以自行查阅API文档，比较简单，不再赘述。\")])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/222.8f955066.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[222],{934:function(t,r,e){\"use strict\";e.r(r);var l=e(45),u=Object(l.a)({},(function(){var t=this,r=t.$createElement,e=t._self._c||r;return e(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[e(\"h1\",{attrs:{id:\"前言\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#前言\"}},[t._v(\"#\")]),t._v(\" 前言\")]),t._v(\" \"),e(\"p\",[t._v(\"本书是第一本系统介绍Flutter技术的中文书籍，它是Flutter中文社区发起的开源项目之一，旨在帮助开发者入门，系统地、循序渐进的了解Flutter。\")]),t._v(\" \"),e(\"blockquote\",[e(\"p\",[t._v(\"本书官网访问量较大，由于服务器配置有限，所以某些时段访问本站可能略有延迟，如果延迟较久，读者可以直接在\"),e(\"a\",{attrs:{href:\"https://github.com/flutterchina/flutter-in-action/blob/master/docs/SUMMARY.md\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Github上阅读本书\"),e(\"OutboundLink\")],1),t._v(\"。\")])]),t._v(\" \"),e(\"h2\",{attrs:{id:\"缘起\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#缘起\"}},[t._v(\"#\")]),t._v(\" 缘起\")]),t._v(\" \"),e(\"p\",[t._v(\"在全球，随着Flutter被越来越多的知名公司应用在自己的商业APP中，Flutter这门新技术也逐渐进入了移动开发者的视野，尤其是当Google在2018年IO大会上发布了第一个Preview版本后，国内刮起来一股学习Flutter的热潮。\")]),t._v(\" \"),e(\"p\",[t._v(\"在Flutter发布之初，当时，我在看完Flutter原理介绍后，就对它产生了浓厚的兴趣。当时笔者身边也一些人比较关注Flutter，我也被经常问到关于Flutter的一些问题，比如Flutter怎么样？和RN有什么区别？Flutter为什么要用Dart？当时也听到了一些批评的声音，比如有些人说Flutter只是重复造轮子，没啥亮点、Flutter最大的缺点就是使用了Dart语言等。在听到这些问题及论调后，我深知这是对Flutter的不了解而造成的，这和当时国内缺乏Flutter中文文档和教程有直接关系，很多人对Flutter的了解都只停留在Google的发布会介绍（有中文翻译）。\")]),t._v(\" \"),e(\"p\",[t._v(\"在笔者深入的了解Flutter后，深知Flutter必将是一个会改变移动开发格局的里程碑级作品，它从设计之初就对性能和开发效率兼顾，并且借鉴了React（一个Web开发框架）的响应式的UI框架设计思想等，总之，很难用一两句话说完Flutter的优点，同时我也很快成为了Flutter的路转粉。\")]),t._v(\" \"),e(\"p\",[t._v(\"为了更好的帮助中国开发者了解这门新技术，我在2018年初开始翻译Flutter官网文档，同年4月份上线了\"),e(\"a\",{attrs:{href:\"https://flutterchina.club/\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Flutter中文网\"),e(\"OutboundLink\")],1),t._v(\"，上线后反响很强烈，Flutter中文网也很快被传播开，百度搜索排名迅速蹿升到前三，截止目前，Flutter中文官网日PV在7万左右，每日独立访问人数近一万多。\")]),t._v(\" \"),e(\"p\",[t._v(\"虽然Flutter中文网给中国开发者提供了很好的第一手了解Flutter的资料，但是笔者还会经常遇到一些对Flutter技术处于围观而不愿尝试的开发者。这主要是因为当时Flutter在国内没有成功案例，再加上新技术都有学习成本，所以即使有文档，也会有一些开发者犹豫是否来学习。为了解决这部分开发者的疑虑，我就想如果能用Flutter开发一个完整的APP发布到应用商店，这样开发者就可以在犹豫的时候可以先实际感受一下Flutter应用，这样有个直观的了解后，就会容易做出判断，为此，我开发了\"),e(\"a\",{attrs:{href:\"https://flutterchina.club/app/gm.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Gitme\"),e(\"OutboundLink\")],1),t._v(\"，它是一个Github客户端，它支持了源码浏览、Issue、Label等Github的大多数功能，到目前为止，通过Gitme登录过Github账号的用户有8000多人，日活用户有1000人。更重要的是，有很多人正是用了Gitme后，才来学Flutter的。\")]),t._v(\" \"),e(\"p\",[t._v(\"无论是做Flutter中文网，还是写Gitme，主要目标都是帮助开发者学习Flutter，同时消除围观开发者疑虑。但当开发者们真正开始动手时，Flutter的生态问题就变得尤为突出。由于在2018年初Flutter刚起步时，很多基础的包和库都是空白，少数已有的一些库也大都是预览版（未到1.0），存在很多bug。这个状况不是一两个人花一两天能搞定的，这是需要聚整个Flutter开发者社群之力，耗费数年时间才可能有所改善。因此，在2018年4月份，我以Flutter中文网名义发起了Flutter开源计划，该计划主要是开发一些常用的包来丰富Flutter生态，帮助开发者提高开发效率。自在github建立\"),e(\"a\",{attrs:{href:\"https://github.com/flutterchina\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Flutter中文开发者社区账号\"),e(\"OutboundLink\")],1),t._v(\"以来，前后开源了dio、cookieJar、flukit等多个项目，而dio在开源两周后，就迅速成为Flutter第三方包中Star排名第一的开源库。\")]),t._v(\" \"),e(\"p\",[t._v(\"虽然做的事情已经够多了，但是仍有一些很有必要去做的事情，由于时间原因，一直被搁置。\")]),t._v(\" \"),e(\"p\",[t._v(\"随着学习Flutter的人越来越多，一部分开发者通过查看官网的文档就能入门，但也有很多开发者感觉学习时仍然有些吃力，主要原因有两个，首先官网的文档主要是为了引导开发者快速上手的，讲的并不是很细；其次是我们虽然翻译了官方文档，但是对于Flutter SDK文档并没有翻译，而在开发中遇到的一些具体问题通常都得去查看SDK文档。所以，要解决这两个问题，必须得有一个系统化的Flutter教程，它不仅可以快速引导开发者入门，而且也能触及到一些细节和原理，最好也能提供一些学习和研究Flutter的方法。因此，如果能有一本能系统地介绍Flutter的书籍便是便是非常棒的！但是，当时没有一本关于Flutter的中文书籍，因此，我便计划写一本能由浅入深、系统介绍Flutter的书。2018年12月，《Flutter实战》完成初稿，并在Github上开源，同时上线了\"),e(\"a\",{attrs:{href:\"https://book.flutterchina.club/\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"《Flutter实战》电子书官网\"),e(\"OutboundLink\")],1),t._v(\" ，至今每天有3000多人在线浏览本书。那为什么不直接出版？如果直接出版，不仅有稿费，而且也能保护知识产权，而直接开源，不就只能当雷锋了？其实，无论是做中文网、写Gitme、做Flutter开源项目，我的出发点都是为了能帮助中国开发者了解、学习Flutter，而这是一件非常有意义的事，只要坚持做对别人有价值的事，那么上帝迟早会奖赏你；当然我们也在网站中留了打赏的按钮，如果读者觉得有帮助，可以扫码打赏，请笔者喝一杯咖啡。另外由于成书比较仓促，当时书中也有很多错误，开源后，有很多读者提PR来纠正书中的错别字，时至今日，有78名开发者为本书提过PR，我在此，衷心的感谢你们。\")]),t._v(\" \"),e(\"p\",[t._v(\"起初，我没有出版实体书的打算，当时我以为开发者直接通过在线访问本书官网即方便又不用付费，何乐而不为。但在本书上线后，很多读者来添加微信好友，表示非常有收益，很期待出版纸质书，甚至有比较热心的读者想提前付定金预定！我理解，这是大家对我所做之事的认可和鼓励。考虑到，确实有一部分读者，尤其是还没毕业的同学，可能更喜欢通过书籍去学习，为此，我已经和机械工业出版社合作，目前本书纸质版正在出版中，敬请关注。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"本书组织结构\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本书组织结构\"}},[t._v(\"#\")]),t._v(\" 本书组织结构\")]),t._v(\" \"),e(\"p\",[t._v(\"本书采用由浅入深的方式介绍Flutter技术原理，分为三篇，总计15章，各篇的主要内容如下。\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"第一篇，入门篇（第1章~第7章），包括Flutter技术的出现背景和简介、Flutter的各种类型的Widget以及如何构建UI。通过学习本章，读者可以掌握如何使用Flutter来构建UI界面。\")]),t._v(\" \"),e(\"li\",[t._v(\"第二篇，进阶篇（第8章~第14章），包括Flutter中的事件机制、动画、自定义组件、文件和网络、插件、国际化以及Flutter核心原理等。通过本章内容，读者可以对Flutter整体构建及原理有一个深入的认识。\")]),t._v(\" \"),e(\"li\",[t._v(\"第三篇，实例篇（第15章），本章主要通过一个简版的Github APP来将前面介绍的内容串起来，让开发者对一个完整的Flutter APP开发流有个了解。\")])]),t._v(\" \"),e(\"h2\",{attrs:{id:\"本书特色\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本书特色\"}},[t._v(\"#\")]),t._v(\" 本书特色\")]),t._v(\" \"),e(\"p\",[t._v(\"笔者在大学时读过候捷（真名侯俊杰）写的一些C++相关书籍，在他的《深入浅出MFC》一书中，有一句话我印象像非常深 “唯有深入，方能浅出”。我非常认同这句话，对于一门技术，只有了解的深入，才能用最浅显、通俗的话语描述出。我在写作本书时，深入浅出就是一个主要目标。所以，本书的目标不仅是想告诉读者如何使用Flutter，而且也非常关注各个知识点的底层实现以及设计思想。从本书章节划分上来看，入门篇为“浅出”，进阶篇则是“深入”。另外由于PC客户端开发、移动开发、Web开发这些经验我都有，而Flutter本质上是一个UI系统，而UI系统的设计和实现在”大前端“下有很多相通之处，所以在本书中的一些知识点我也会对比一些其他UI系统（主要是Android或Web）相应的实现，便于有相关开发经验的读者对比理解。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"本书读者对象\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本书读者对象\"}},[t._v(\"#\")]),t._v(\" 本书读者对象\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"读者至少熟悉一种编程语言。\")]),t._v(\" \"),e(\"li\",[t._v(\"读者最好接触过PC客户端、移动开发或Web前端开发中的一种。\")]),t._v(\" \"),e(\"li\",[t._v(\"本书不适合做为编程的入门读物。\")])]),t._v(\" \"),e(\"h2\",{attrs:{id:\"关于随书源码\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#关于随书源码\"}},[t._v(\"#\")]),t._v(\" 关于随书源码\")]),t._v(\" \"),e(\"p\",[t._v(\"由于篇幅所限，本书中大多数示例代码都只是部分核心代码，读者可以去\"),e(\"a\",{attrs:{href:\"https://github.com/wendux/flutter_in_action_source_code\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"这里\"),e(\"OutboundLink\")],1),t._v(\"查看下载。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"致谢\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#致谢\"}},[t._v(\"#\")]),t._v(\" 致谢\")]),t._v(\" \"),e(\"p\",[t._v(\"感谢一直以来支持Flutter中文网、Flutter开源项目的人以及所有对本书提过PR的人，正是因为有你们，才有这本书。另外尤其感谢给本书打赏过的同学，你们的支持给了我很大的鼓励。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"权益\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#权益\"}},[t._v(\"#\")]),t._v(\" 权益\")]),t._v(\" \"),e(\"p\",[t._v(\"最后，知识是应该付费的，创作不易，开源不等于免费，如果您是本书读者并手头宽裕，可以点击下面打赏按钮打赏；当然，如果您囊中羞涩，您也可以阅读本书，但我对您有个小小的要求，希望您在阅读的过程中能积极参与到本书的纠错以及未完成内容的创作上来，也算是有所付出。\")]),t._v(\" \"),e(\"p\",[t._v(\"近来在网上发现很多\"),e(\"strong\",[t._v(\"原封不动复制本书\")]),t._v(\"的镜像网站和大量复制或\"),e(\"strong\",[t._v(\"引用了本书但未注明出处\")]),t._v(\"的博客、文章甚至书籍；对此，笔者在此声明，本书著作权归wendux所有，任何组织或个人在未经授权的情况下复制、拷贝、抄袭本书用于商业目的，笔者保留追究其法律责任的权利。如果是非商业目的的转载和引用，请注明出处并附上本书网址。另外如有出版机构愿意为本书出版实体书或者想转载本书内容，亦或是想合作，请加微信Demons-du.\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"勘误\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#勘误\"}},[t._v(\"#\")]),t._v(\" 勘误\")]),t._v(\" \"),e(\"p\",[t._v(\"由于Flutter SDK在不断更新，本书中的部分内容（如类的继承关系、参数等）可能会和新版本的Flutter不一致，读者以最新的Flutter SDK为准。另外，由于时间仓促，书中难免有错误之处，如果您发现本书中的错误，欢迎点击右上角的”编辑按钮“，提PR。如果您想一起参与本书创作，可以参考\"),e(\"a\",{attrs:{href:\"https://github.com/flutterchina/flutter-in-action#%E8%B4%A1%E7%8C%AE%E9%A1%BB%E7%9F%A5\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"《Flutter实战》贡献指南\"),e(\"OutboundLink\")],1),t._v(\"。\")])])}),[],!1,null,null,null);r.default=u.exports}}]);"
  },
  {
    "path": "docs/assets/js/223.3230e791.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[223],{831:function(t,r,e){\"use strict\";e.r(r);var l=e(45),u=Object(l.a)({},(function(){var t=this,r=t.$createElement,e=t._self._c||r;return e(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[e(\"h1\",{attrs:{id:\"前言\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#前言\"}},[t._v(\"#\")]),t._v(\" 前言\")]),t._v(\" \"),e(\"p\",[t._v(\"本书是第一本系统介绍Flutter技术的中文书籍，它是Flutter中文社区发起的开源项目之一，旨在帮助开发者入门，系统地、循序渐进的了解Flutter。\")]),t._v(\" \"),e(\"blockquote\",[e(\"p\",[t._v(\"本书官网访问量较大，由于服务器配置有限，所以某些时段访问本站可能略有延迟，如果延迟较久，读者可以直接在\"),e(\"a\",{attrs:{href:\"https://github.com/flutterchina/flutter-in-action/blob/master/docs/SUMMARY.md\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Github上阅读本书\"),e(\"OutboundLink\")],1),t._v(\"。\")])]),t._v(\" \"),e(\"h2\",{attrs:{id:\"缘起\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#缘起\"}},[t._v(\"#\")]),t._v(\" 缘起\")]),t._v(\" \"),e(\"p\",[t._v(\"在全球，随着Flutter被越来越多的知名公司应用在自己的商业APP中，Flutter这门新技术也逐渐进入了移动开发者的视野，尤其是当Google在2018年IO大会上发布了第一个Preview版本后，国内刮起来一股学习Flutter的热潮。\")]),t._v(\" \"),e(\"p\",[t._v(\"在Flutter发布之初，当时，我在看完Flutter原理介绍后，就对它产生了浓厚的兴趣。当时笔者身边也一些人比较关注Flutter，我也被经常问到关于Flutter的一些问题，比如Flutter怎么样？和RN有什么区别？Flutter为什么要用Dart？当时也听到了一些批评的声音，比如有些人说Flutter只是重复造轮子，没啥亮点、Flutter最大的缺点就是使用了Dart语言等。在听到这些问题及论调后，我深知这是对Flutter的不了解而造成的，这和当时国内缺乏Flutter中文文档和教程有直接关系，很多人对Flutter的了解都只停留在Google的发布会介绍（有中文翻译）。\")]),t._v(\" \"),e(\"p\",[t._v(\"在笔者深入的了解Flutter后，深知Flutter必将是一个会改变移动开发格局的里程碑级作品，它从设计之初就对性能和开发效率兼顾，并且借鉴了React（一个Web开发框架）的响应式的UI框架设计思想等，总之，很难用一两句话说完Flutter的优点，同时我也很快成为了Flutter的路转粉。\")]),t._v(\" \"),e(\"p\",[t._v(\"为了更好的帮助中国开发者了解这门新技术，我在2018年初开始翻译Flutter官网文档，同年4月份上线了\"),e(\"a\",{attrs:{href:\"https://flutterchina.club/\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Flutter中文网\"),e(\"OutboundLink\")],1),t._v(\"，上线后反响很强烈，Flutter中文网也很快被传播开，百度搜索排名迅速蹿升到前三，截止目前，Flutter中文官网日PV在7万左右，每日独立访问人数近一万多。\")]),t._v(\" \"),e(\"p\",[t._v(\"虽然Flutter中文网给中国开发者提供了很好的第一手了解Flutter的资料，但是笔者还会经常遇到一些对Flutter技术处于围观而不愿尝试的开发者。这主要是因为当时Flutter在国内没有成功案例，再加上新技术都有学习成本，所以即使有文档，也会有一些开发者犹豫是否来学习。为了解决这部分开发者的疑虑，我就想如果能用Flutter开发一个完整的APP发布到应用商店，这样开发者就可以在犹豫的时候可以先实际感受一下Flutter应用，这样有个直观的了解后，就会容易做出判断，为此，我开发了\"),e(\"a\",{attrs:{href:\"https://flutterchina.club/app/gm.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Gitme\"),e(\"OutboundLink\")],1),t._v(\"，它是一个Github客户端，它支持了源码浏览、Issue、Label等Github的大多数功能，到目前为止，通过Gitme登录过Github账号的用户有8000多人，日活用户有1000人。更重要的是，有很多人正是用了Gitme后，才来学Flutter的。\")]),t._v(\" \"),e(\"p\",[t._v(\"无论是做Flutter中文网，还是写Gitme，主要目标都是帮助开发者学习Flutter，同时消除围观开发者疑虑。但当开发者们真正开始动手时，Flutter的生态问题就变得尤为突出。由于在2018年初Flutter刚起步时，很多基础的包和库都是空白，少数已有的一些库也大都是预览版（未到1.0），存在很多bug。这个状况不是一两个人花一两天能搞定的，这是需要聚整个Flutter开发者社群之力，耗费数年时间才可能有所改善。因此，在2018年4月份，我以Flutter中文网名义发起了Flutter开源计划，该计划主要是开发一些常用的包来丰富Flutter生态，帮助开发者提高开发效率。自在github建立\"),e(\"a\",{attrs:{href:\"https://github.com/flutterchina\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Flutter中文开发者社区账号\"),e(\"OutboundLink\")],1),t._v(\"以来，前后开源了dio、cookieJar、flukit等多个项目，而dio在开源两周后，就迅速成为Flutter第三方包中Star排名第一的开源库。\")]),t._v(\" \"),e(\"p\",[t._v(\"虽然做的事情已经够多了，但是仍有一些很有必要去做的事情，由于时间原因，一直被搁置。\")]),t._v(\" \"),e(\"p\",[t._v(\"随着学习Flutter的人越来越多，一部分开发者通过查看官网的文档就能入门，但也有很多开发者感觉学习时仍然有些吃力，主要原因有两个，首先官网的文档主要是为了引导开发者快速上手的，讲的并不是很细；其次是我们虽然翻译了官方文档，但是对于Flutter SDK文档并没有翻译，而在开发中遇到的一些具体问题通常都得去查看SDK文档。所以，要解决这两个问题，必须得有一个系统化的Flutter教程，它不仅可以快速引导开发者入门，而且也能触及到一些细节和原理，最好也能提供一些学习和研究Flutter的方法。因此，如果能有一本能系统地介绍Flutter的书籍便是便是非常棒的！但是，当时没有一本关于Flutter的中文书籍，因此，我便计划写一本能由浅入深、系统介绍Flutter的书。2018年12月，《Flutter实战》完成初稿，并在Github上开源，同时上线了\"),e(\"a\",{attrs:{href:\"https://book.flutterchina.club/\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"《Flutter实战》电子书官网\"),e(\"OutboundLink\")],1),t._v(\" ，至今每天有3000多人在线浏览本书。那为什么不直接出版？如果直接出版，不仅有稿费，而且也能保护知识产权，而直接开源，不就只能当雷锋了？其实，无论是做中文网、写Gitme、做Flutter开源项目，我的出发点都是为了能帮助中国开发者了解、学习Flutter，而这是一件非常有意义的事，只要坚持做对别人有价值的事，那么上帝迟早会奖赏你；当然我们也在网站中留了打赏的按钮，如果读者觉得有帮助，可以扫码打赏，请笔者喝一杯咖啡。另外由于成书比较仓促，当时书中也有很多错误，开源后，有很多读者提PR来纠正书中的错别字，时至今日，有78名开发者为本书提过PR，我在此，衷心的感谢你们。\")]),t._v(\" \"),e(\"p\",[t._v(\"起初，我没有出版实体书的打算，当时我以为开发者直接通过在线访问本书官网即方便又不用付费，何乐而不为。但在本书上线后，很多读者来添加微信好友，表示非常有收益，很期待出版纸质书，甚至有比较热心的读者想提前付定金预定！我理解，这是大家对我所做之事的认可和鼓励。考虑到，确实有一部分读者，尤其是还没毕业的同学，可能更喜欢通过书籍去学习，为此，我已经和机械工业出版社合作，目前本书纸质版正在出版中，敬请关注。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"本书组织结构\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本书组织结构\"}},[t._v(\"#\")]),t._v(\" 本书组织结构\")]),t._v(\" \"),e(\"p\",[t._v(\"本书采用由浅入深的方式介绍Flutter技术原理，分为三篇，总计15章，各篇的主要内容如下。\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"第一篇，入门篇（第1章~第7章），包括Flutter技术的出现背景和简介、Flutter的各种类型的Widget以及如何构建UI。通过学习本章，读者可以掌握如何使用Flutter来构建UI界面。\")]),t._v(\" \"),e(\"li\",[t._v(\"第二篇，进阶篇（第8章~第14章），包括Flutter中的事件机制、动画、自定义组件、文件和网络、插件、国际化以及Flutter核心原理等。通过本章内容，读者可以对Flutter整体构建及原理有一个深入的认识。\")]),t._v(\" \"),e(\"li\",[t._v(\"第三篇，实例篇（第15章），本章主要通过一个简版的Github APP来将前面介绍的内容串起来，让开发者对一个完整的Flutter APP开发流有个了解。\")])]),t._v(\" \"),e(\"h2\",{attrs:{id:\"本书特色\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本书特色\"}},[t._v(\"#\")]),t._v(\" 本书特色\")]),t._v(\" \"),e(\"p\",[t._v(\"笔者在大学时读过候捷（真名侯俊杰）写的一些C++相关书籍，在他的《深入浅出MFC》一书中，有一句话我印象像非常深 “唯有深入，方能浅出”。我非常认同这句话，对于一门技术，只有了解的深入，才能用最浅显、通俗的话语描述出。我在写作本书时，深入浅出就是一个主要目标。所以，本书的目标不仅是想告诉读者如何使用Flutter，而且也非常关注各个知识点的底层实现以及设计思想。从本书章节划分上来看，入门篇为“浅出”，进阶篇则是“深入”。另外由于PC客户端开发、移动开发、Web开发这些经验我都有，而Flutter本质上是一个UI系统，而UI系统的设计和实现在”大前端“下有很多相通之处，所以在本书中的一些知识点我也会对比一些其他UI系统（主要是Android或Web）相应的实现，便于有相关开发经验的读者对比理解。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"本书读者对象\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本书读者对象\"}},[t._v(\"#\")]),t._v(\" 本书读者对象\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"读者至少熟悉一种编程语言。\")]),t._v(\" \"),e(\"li\",[t._v(\"读者最好接触过PC客户端、移动开发或Web前端开发中的一种。\")]),t._v(\" \"),e(\"li\",[t._v(\"本书不适合做为编程的入门读物。\")])]),t._v(\" \"),e(\"h2\",{attrs:{id:\"关于随书源码\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#关于随书源码\"}},[t._v(\"#\")]),t._v(\" 关于随书源码\")]),t._v(\" \"),e(\"p\",[t._v(\"由于篇幅所限，本书中大多数示例代码都只是部分核心代码，读者可以去\"),e(\"a\",{attrs:{href:\"https://github.com/wendux/flutter_in_action_source_code\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"这里\"),e(\"OutboundLink\")],1),t._v(\"查看下载。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"致谢\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#致谢\"}},[t._v(\"#\")]),t._v(\" 致谢\")]),t._v(\" \"),e(\"p\",[t._v(\"感谢一直以来支持Flutter中文网、Flutter开源项目的人以及所有对本书提过PR的人，正是因为有你们，才有这本书。另外尤其感谢给本书打赏过的同学，你们的支持给了我很大的鼓励。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"权益\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#权益\"}},[t._v(\"#\")]),t._v(\" 权益\")]),t._v(\" \"),e(\"p\",[t._v(\"最后，知识是应该付费的，创作不易，开源不等于免费，如果您是本书读者并手头宽裕，可以点击下面打赏按钮打赏；当然，如果您囊中羞涩，您也可以阅读本书，但我对您有个小小的要求，希望您在阅读的过程中能积极参与到本书的纠错以及未完成内容的创作上来，也算是有所付出。\")]),t._v(\" \"),e(\"p\",[t._v(\"近来在网上发现很多\"),e(\"strong\",[t._v(\"原封不动复制本书\")]),t._v(\"的镜像网站和大量复制或\"),e(\"strong\",[t._v(\"引用了本书但未注明出处\")]),t._v(\"的博客、文章甚至书籍；对此，笔者在此声明，本书著作权归wendux所有，任何组织或个人在未经授权的情况下复制、拷贝、抄袭本书用于商业目的，笔者保留追究其法律责任的权利。如果是非商业目的的转载和引用，请注明出处并附上本书网址。另外如有出版机构愿意为本书出版实体书或者想转载本书内容，亦或是想合作，请加微信Demons-du.\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"勘误\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#勘误\"}},[t._v(\"#\")]),t._v(\" 勘误\")]),t._v(\" \"),e(\"p\",[t._v(\"由于Flutter SDK在不断更新，本书中的部分内容（如类的继承关系、参数等）可能会和新版本的Flutter不一致，读者以最新的Flutter SDK为准。另外，由于时间仓促，书中难免有错误之处，如果您发现本书中的错误，欢迎点击右上角的”编辑按钮“，提PR。如果您想一起参与本书创作，可以参考\"),e(\"a\",{attrs:{href:\"https://github.com/flutterchina/flutter-in-action#%E8%B4%A1%E7%8C%AE%E9%A1%BB%E7%9F%A5\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"《Flutter实战》贡献指南\"),e(\"OutboundLink\")],1),t._v(\"。\")])])}),[],!1,null,null,null);r.default=u.exports}}]);"
  },
  {
    "path": "docs/assets/js/224.35c9623f.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[224],{935:function(t,r,e){\"use strict\";e.r(r);var l=e(45),u=Object(l.a)({},(function(){var t=this,r=t.$createElement,e=t._self._c||r;return e(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[e(\"h1\",{attrs:{id:\"前言\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#前言\"}},[t._v(\"#\")]),t._v(\" 前言\")]),t._v(\" \"),e(\"p\",[t._v(\"本书是第一本系统介绍Flutter技术的中文书籍，它是Flutter中文社区发起的开源项目之一，旨在帮助开发者入门，系统地、循序渐进的了解Flutter。\")]),t._v(\" \"),e(\"blockquote\",[e(\"p\",[t._v(\"本书官网访问量较大，由于服务器配置有限，所以某些时段访问本站可能略有延迟，如果延迟较久，读者可以直接在\"),e(\"a\",{attrs:{href:\"https://github.com/flutterchina/flutter-in-action/blob/master/docs/SUMMARY.md\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Github上阅读本书\"),e(\"OutboundLink\")],1),t._v(\"。\")])]),t._v(\" \"),e(\"h2\",{attrs:{id:\"缘起\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#缘起\"}},[t._v(\"#\")]),t._v(\" 缘起\")]),t._v(\" \"),e(\"p\",[t._v(\"在全球，随着Flutter被越来越多的知名公司应用在自己的商业APP中，Flutter这门新技术也逐渐进入了移动开发者的视野，尤其是当Google在2018年IO大会上发布了第一个Preview版本后，国内刮起来一股学习Flutter的热潮。\")]),t._v(\" \"),e(\"p\",[t._v(\"在Flutter发布之初，当时，我在看完Flutter原理介绍后，就对它产生了浓厚的兴趣。当时笔者身边也一些人比较关注Flutter，我也被经常问到关于Flutter的一些问题，比如Flutter怎么样？和RN有什么区别？Flutter为什么要用Dart？当时也听到了一些批评的声音，比如有些人说Flutter只是重复造轮子，没啥亮点、Flutter最大的缺点就是使用了Dart语言等。在听到这些问题及论调后，我深知这是对Flutter的不了解而造成的，这和当时国内缺乏Flutter中文文档和教程有直接关系，很多人对Flutter的了解都只停留在Google的发布会介绍（有中文翻译）。\")]),t._v(\" \"),e(\"p\",[t._v(\"在笔者深入的了解Flutter后，深知Flutter必将是一个会改变移动开发格局的里程碑级作品，它从设计之初就对性能和开发效率兼顾，并且借鉴了React（一个Web开发框架）的响应式的UI框架设计思想等，总之，很难用一两句话说完Flutter的优点，同时我也很快成为了Flutter的路转粉。\")]),t._v(\" \"),e(\"p\",[t._v(\"为了更好的帮助中国开发者了解这门新技术，我在2018年初开始翻译Flutter官网文档，同年4月份上线了\"),e(\"a\",{attrs:{href:\"https://flutterchina.club/\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Flutter中文网\"),e(\"OutboundLink\")],1),t._v(\"，上线后反响很强烈，Flutter中文网也很快被传播开，百度搜索排名迅速蹿升到前三，截止目前，Flutter中文官网日PV在7万左右，每日独立访问人数近一万多。\")]),t._v(\" \"),e(\"p\",[t._v(\"虽然Flutter中文网给中国开发者提供了很好的第一手了解Flutter的资料，但是笔者还会经常遇到一些对Flutter技术处于围观而不愿尝试的开发者。这主要是因为当时Flutter在国内没有成功案例，再加上新技术都有学习成本，所以即使有文档，也会有一些开发者犹豫是否来学习。为了解决这部分开发者的疑虑，我就想如果能用Flutter开发一个完整的APP发布到应用商店，这样开发者就可以在犹豫的时候可以先实际感受一下Flutter应用，这样有个直观的了解后，就会容易做出判断，为此，我开发了\"),e(\"a\",{attrs:{href:\"https://flutterchina.club/app/gm.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Gitme\"),e(\"OutboundLink\")],1),t._v(\"，它是一个Github客户端，它支持了源码浏览、Issue、Label等Github的大多数功能，到目前为止，通过Gitme登录过Github账号的用户有8000多人，日活用户有1000人。更重要的是，有很多人正是用了Gitme后，才来学Flutter的。\")]),t._v(\" \"),e(\"p\",[t._v(\"无论是做Flutter中文网，还是写Gitme，主要目标都是帮助开发者学习Flutter，同时消除围观开发者疑虑。但当开发者们真正开始动手时，Flutter的生态问题就变得尤为突出。由于在2018年初Flutter刚起步时，很多基础的包和库都是空白，少数已有的一些库也大都是预览版（未到1.0），存在很多bug。这个状况不是一两个人花一两天能搞定的，这是需要聚整个Flutter开发者社群之力，耗费数年时间才可能有所改善。因此，在2018年4月份，我以Flutter中文网名义发起了Flutter开源计划，该计划主要是开发一些常用的包来丰富Flutter生态，帮助开发者提高开发效率。自在github建立\"),e(\"a\",{attrs:{href:\"https://github.com/flutterchina\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Flutter中文开发者社区账号\"),e(\"OutboundLink\")],1),t._v(\"以来，前后开源了dio、cookieJar、flukit等多个项目，而dio在开源两周后，就迅速成为Flutter第三方包中Star排名第一的开源库。\")]),t._v(\" \"),e(\"p\",[t._v(\"虽然做的事情已经够多了，但是仍有一些很有必要去做的事情，由于时间原因，一直被搁置。\")]),t._v(\" \"),e(\"p\",[t._v(\"随着学习Flutter的人越来越多，一部分开发者通过查看官网的文档就能入门，但也有很多开发者感觉学习时仍然有些吃力，主要原因有两个，首先官网的文档主要是为了引导开发者快速上手的，讲的并不是很细；其次是我们虽然翻译了官方文档，但是对于Flutter SDK文档并没有翻译，而在开发中遇到的一些具体问题通常都得去查看SDK文档。所以，要解决这两个问题，必须得有一个系统化的Flutter教程，它不仅可以快速引导开发者入门，而且也能触及到一些细节和原理，最好也能提供一些学习和研究Flutter的方法。因此，如果能有一本能系统地介绍Flutter的书籍便是便是非常棒的！但是，当时没有一本关于Flutter的中文书籍，因此，我便计划写一本能由浅入深、系统介绍Flutter的书。2018年12月，《Flutter实战》完成初稿，并在Github上开源，同时上线了\"),e(\"a\",{attrs:{href:\"https://book.flutterchina.club/\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"《Flutter实战》电子书官网\"),e(\"OutboundLink\")],1),t._v(\" ，至今每天有3000多人在线浏览本书。那为什么不直接出版？如果直接出版，不仅有稿费，而且也能保护知识产权，而直接开源，不就只能当雷锋了？其实，无论是做中文网、写Gitme、做Flutter开源项目，我的出发点都是为了能帮助中国开发者了解、学习Flutter，而这是一件非常有意义的事，只要坚持做对别人有价值的事，那么上帝迟早会奖赏你；当然我们也在网站中留了打赏的按钮，如果读者觉得有帮助，可以扫码打赏，请笔者喝一杯咖啡。另外由于成书比较仓促，当时书中也有很多错误，开源后，有很多读者提PR来纠正书中的错别字，时至今日，有78名开发者为本书提过PR，我在此，衷心的感谢你们。\")]),t._v(\" \"),e(\"p\",[t._v(\"起初，我没有出版实体书的打算，当时我以为开发者直接通过在线访问本书官网即方便又不用付费，何乐而不为。但在本书上线后，很多读者来添加微信好友，表示非常有收益，很期待出版纸质书，甚至有比较热心的读者想提前付定金预定！我理解，这是大家对我所做之事的认可和鼓励。考虑到，确实有一部分读者，尤其是还没毕业的同学，可能更喜欢通过书籍去学习，为此，我已经和机械工业出版社合作，目前本书纸质版正在出版中，敬请关注。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"本书组织结构\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本书组织结构\"}},[t._v(\"#\")]),t._v(\" 本书组织结构\")]),t._v(\" \"),e(\"p\",[t._v(\"本书采用由浅入深的方式介绍Flutter技术原理，分为三篇，总计15章，各篇的主要内容如下。\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"第一篇，入门篇（第1章~第7章），包括Flutter技术的出现背景和简介、Flutter的各种类型的Widget以及如何构建UI。通过学习本章，读者可以掌握如何使用Flutter来构建UI界面。\")]),t._v(\" \"),e(\"li\",[t._v(\"第二篇，进阶篇（第8章~第14章），包括Flutter中的事件机制、动画、自定义组件、文件和网络、插件、国际化以及Flutter核心原理等。通过本章内容，读者可以对Flutter整体构建及原理有一个深入的认识。\")]),t._v(\" \"),e(\"li\",[t._v(\"第三篇，实例篇（第15章），本章主要通过一个简版的Github APP来将前面介绍的内容串起来，让开发者对一个完整的Flutter APP开发流有个了解。\")])]),t._v(\" \"),e(\"h2\",{attrs:{id:\"本书特色\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本书特色\"}},[t._v(\"#\")]),t._v(\" 本书特色\")]),t._v(\" \"),e(\"p\",[t._v(\"笔者在大学时读过候捷（真名侯俊杰）写的一些C++相关书籍，在他的《深入浅出MFC》一书中，有一句话我印象像非常深 “唯有深入，方能浅出”。我非常认同这句话，对于一门技术，只有了解的深入，才能用最浅显、通俗的话语描述出。我在写作本书时，深入浅出就是一个主要目标。所以，本书的目标不仅是想告诉读者如何使用Flutter，而且也非常关注各个知识点的底层实现以及设计思想。从本书章节划分上来看，入门篇为“浅出”，进阶篇则是“深入”。另外由于PC客户端开发、移动开发、Web开发这些经验我都有，而Flutter本质上是一个UI系统，而UI系统的设计和实现在”大前端“下有很多相通之处，所以在本书中的一些知识点我也会对比一些其他UI系统（主要是Android或Web）相应的实现，便于有相关开发经验的读者对比理解。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"本书读者对象\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本书读者对象\"}},[t._v(\"#\")]),t._v(\" 本书读者对象\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"读者至少熟悉一种编程语言。\")]),t._v(\" \"),e(\"li\",[t._v(\"读者最好接触过PC客户端、移动开发或Web前端开发中的一种。\")]),t._v(\" \"),e(\"li\",[t._v(\"本书不适合做为编程的入门读物。\")])]),t._v(\" \"),e(\"h2\",{attrs:{id:\"关于随书源码\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#关于随书源码\"}},[t._v(\"#\")]),t._v(\" 关于随书源码\")]),t._v(\" \"),e(\"p\",[t._v(\"由于篇幅所限，本书中大多数示例代码都只是部分核心代码，读者可以去\"),e(\"a\",{attrs:{href:\"https://github.com/wendux/flutter_in_action_source_code\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"这里\"),e(\"OutboundLink\")],1),t._v(\"查看下载。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"致谢\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#致谢\"}},[t._v(\"#\")]),t._v(\" 致谢\")]),t._v(\" \"),e(\"p\",[t._v(\"感谢一直以来支持Flutter中文网、Flutter开源项目的人以及所有对本书提过PR的人，正是因为有你们，才有这本书。另外尤其感谢给本书打赏过的同学，你们的支持给了我很大的鼓励。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"权益\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#权益\"}},[t._v(\"#\")]),t._v(\" 权益\")]),t._v(\" \"),e(\"p\",[t._v(\"最后，知识是应该付费的，创作不易，开源不等于免费，如果您是本书读者并手头宽裕，可以点击下面打赏按钮打赏；当然，如果您囊中羞涩，您也可以阅读本书，但我对您有个小小的要求，希望您在阅读的过程中能积极参与到本书的纠错以及未完成内容的创作上来，也算是有所付出。\")]),t._v(\" \"),e(\"p\",[t._v(\"近来在网上发现很多\"),e(\"strong\",[t._v(\"原封不动复制本书\")]),t._v(\"的镜像网站和大量复制或\"),e(\"strong\",[t._v(\"引用了本书但未注明出处\")]),t._v(\"的博客、文章甚至书籍；对此，笔者在此声明，本书著作权归wendux所有，任何组织或个人在未经授权的情况下复制、拷贝、抄袭本书用于商业目的，笔者保留追究其法律责任的权利。如果是非商业目的的转载和引用，请注明出处并附上本书网址。另外如有出版机构愿意为本书出版实体书或者想转载本书内容，亦或是想合作，请加微信Demons-du.\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"勘误\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#勘误\"}},[t._v(\"#\")]),t._v(\" 勘误\")]),t._v(\" \"),e(\"p\",[t._v(\"由于Flutter SDK在不断更新，本书中的部分内容（如类的继承关系、参数等）可能会和新版本的Flutter不一致，读者以最新的Flutter SDK为准。另外，由于时间仓促，书中难免有错误之处，如果您发现本书中的错误，欢迎点击右上角的”编辑按钮“，提PR。如果您想一起参与本书创作，可以参考\"),e(\"a\",{attrs:{href:\"https://github.com/flutterchina/flutter-in-action#%E8%B4%A1%E7%8C%AE%E9%A1%BB%E7%9F%A5\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"《Flutter实战》贡献指南\"),e(\"OutboundLink\")],1),t._v(\"。\")])])}),[],!1,null,null,null);r.default=u.exports}}]);"
  },
  {
    "path": "docs/assets/js/225.8687c2ec.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[225],{936:function(v,_,t){\"use strict\";t.r(_);var a=t(45),r=Object(a.a)({},(function(){var v=this,_=v.$createElement,t=v._self._c||_;return t(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":v.$parent.slotKey}},[t(\"h1\",{attrs:{id:\"字节跳动-内推\"}},[t(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#字节跳动-内推\"}},[v._v(\"#\")]),v._v(\" 字节跳动-内推\")]),v._v(\" \"),t(\"p\",[v._v(\"欢迎来字节跳动，和作者（wendux） 一起做同事！我们有大量HC，社招、校招、实习，前端、客户端、后端都有，欢迎对技术有热情的同学来投递！风里雨里，我在字节跳动等你~\")]),v._v(\" \"),t(\"blockquote\",[t(\"p\",[v._v('投递方式：加微信（Demons-du），好友申请时请按 \"姓名+学校+职位+来自flutter社区\" 备注信息，微信请求通过后，字节跳动VIP通道就建立了（后续发简历、进度跟进、问题咨询都可以直接微信联系哦）。')])]),v._v(\" \"),t(\"h2\",{attrs:{id:\"前端团队介绍\"}},[t(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#前端团队介绍\"}},[v._v(\"#\")]),v._v(\" 前端团队介绍\")]),v._v(\" \"),t(\"p\",[v._v(\"我们是字节跳动-幸福里FE团队，诞生于2018年9月，从最初的4人组成长到今天的30多人，成员年龄跨度从90后到00后。技术栈覆盖当下前端主流全方向（Vue/React/Typescript/nodejs/webgl/flutter/Taro)，团队内大牛多多，技术氛围浓厚，有VR专家老吴、3D渲染一哥博哥、《Flutter实战》作者wendux、深谙 Web 框架及工程化的董老师、以及技能树满点的杰哥等等，还有，团队经常组织线下学习及娱乐活动，是一个开心且战斗力极强的team。只要你觉得自己够出色，或想让自己变得更出色，还等什么，放肆地加入我们吧。\")]),v._v(\" \"),t(\"h3\",{attrs:{id:\"业务线介绍\"}},[t(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#业务线介绍\"}},[v._v(\"#\")]),v._v(\" 业务线介绍\")]),v._v(\" \"),t(\"p\",[v._v(\"幸福里是字节跳动旗下集内容、社区、工具于一体的房产信息、服务、交易平台。产品基于个性化推荐引擎向用户推荐优质的房产内容和全面、真实的房源信息，致力于为用户提供全面、专业、可靠的购房决策支持。\")]),v._v(\" \"),t(\"p\",[v._v(\"幸福里始于2018年8月，是国内发展最快的，集内容、社区、工具于一体的房产信息与服务平台，业务覆盖一二线共23城，现累积注册用户千万，目前进入高速增长期。\")]),v._v(\" \"),t(\"h3\",{attrs:{id:\"团队福利\"}},[t(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#团队福利\"}},[v._v(\"#\")]),v._v(\" 团队福利\")]),v._v(\" \"),t(\"p\",[v._v(\"五险一金、补充医疗保险、定期体检、年终奖、股票期权、带薪年假、员工旅游、交通补助、包吃、节日福利、住房补贴，不限量零食下午茶、弹性工作制\")]),v._v(\" \"),t(\"h4\",{attrs:{id:\"实习生\"}},[t(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#实习生\"}},[v._v(\"#\")]),v._v(\" 实习生\")]),v._v(\" \"),t(\"ol\",[t(\"li\",[v._v(\"团队为每一位实习生提供专职mentor，手把手带入工作业务。\")]),v._v(\" \"),t(\"li\",[v._v(\"团队为没有基础的实习生提供“筑基计划”的课程学习，轻松进阶前端技能。\")])]),v._v(\" \"),t(\"h2\",{attrs:{id:\"职位介绍\"}},[t(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#职位介绍\"}},[v._v(\"#\")]),v._v(\" 职位介绍\")]),v._v(\" \"),t(\"h3\",{attrs:{id:\"职位-前端开发工程师-校招-社招-急\"}},[t(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#职位-前端开发工程师-校招-社招-急\"}},[v._v(\"#\")]),v._v(\" 职位—：前端开发工程师（校招/社招）急\")]),v._v(\" \"),t(\"p\",[t(\"strong\",[v._v(\"职位描述:\")])]),v._v(\" \"),t(\"ol\",[t(\"li\",[t(\"p\",[v._v(\"负责移动端 /PC 端业务系统、小程序、跨端页面开发；\")])]),v._v(\" \"),t(\"li\",[t(\"p\",[v._v(\"负责推动与优化业务线中前端基础架构、组件抽象、技术调研；\")])]),v._v(\" \"),t(\"li\",[t(\"p\",[v._v(\"积极推动改进产品，包括技术、用户体验、产品等各个维度的改进。\")])])]),v._v(\" \"),t(\"p\",[t(\"strong\",[v._v(\"职位要求:\")])]),v._v(\" \"),t(\"ol\",[t(\"li\",[t(\"p\",[v._v(\"本科及以上学历，计算机、通信等相关专业；\")])]),v._v(\" \"),t(\"li\",[t(\"p\",[v._v(\"熟练掌握 EcmaScript，CSS，HTML，DOM、绘图、动画、协议、安全、网络、性能优化等前端技术，对主流前端框架（ React \\\\ Vue 等）至少一种有深入应用并深入理解其设计原理；\")])]),v._v(\" \"),t(\"li\",[t(\"p\",[v._v(\"有安卓、iOS 开发经验或跨端技术flutter者优先；\")])]),v._v(\" \"),t(\"li\",[t(\"p\",[v._v(\"对用户体验、交互操作流程，及用户需求有一定了解；\")])]),v._v(\" \"),t(\"li\",[t(\"p\",[v._v(\"积极乐观，责任心强，工作认真细致，具备良好的服务意识，具有良好的团队沟通与协作能力；\")])]),v._v(\" \"),t(\"li\",[t(\"p\",[v._v(\"热爱前端技术，有较强的学习能力，有强烈的求知欲、好奇心和进取心 ，能及时关注和学习业界最新的前端技术。\")])])]),v._v(\" \"),t(\"h3\",{attrs:{id:\"职位二-校招前端开发实习生\"}},[t(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#职位二-校招前端开发实习生\"}},[v._v(\"#\")]),v._v(\" 职位二：校招前端开发实习生\")]),v._v(\" \"),t(\"p\",[t(\"strong\",[v._v(\"职位描述：\")])]),v._v(\" \"),t(\"p\",[v._v(\"1、负责字节跳动-幸福里业务h5、小程序、中台系统的维护与开发；\")]),v._v(\" \"),t(\"p\",[v._v(\"2、负责根据已有前端项目的基础架构进行合理的技术优化；\")]),v._v(\" \"),t(\"p\",[v._v(\"3、积极推动改进产品，包括技术、用户体验、产品等各个维度。\")]),v._v(\" \"),t(\"p\",[t(\"strong\",[v._v(\"职位要求：\")])]),v._v(\" \"),t(\"p\",[v._v(\"1、计算机基础扎实：数据结构、算法、操作系统；\")]),v._v(\" \"),t(\"p\",[v._v(\"2、熟悉掌握javascript、ES6 语言特性，熟练掌握css中常见布局方式，以及CSS3动画技术；\")]),v._v(\" \"),t(\"p\",[v._v(\"3、熟练掌握VUE或React技术（非必须）；\")]),v._v(\" \"),t(\"p\",[v._v(\"4、有ACM竞赛且获奖者优先；\")]),v._v(\" \"),t(\"p\",[v._v(\"5、具较强的学习能力、主动、自驱、有责任心。\")]),v._v(\" \"),t(\"p\",[t(\"strong\",[v._v(\"实习生培养计划\")]),v._v(\"：\")]),v._v(\" \"),t(\"p\",[v._v(\"我们会为每一位实习生配备一名mentor进行日常决疑解惑和指导，同时我们针对不太熟悉前端的同学进行一个专门的【筑基培训】，旨在帮助快速补齐前端基础，以及明确后续学习和成长路线，有老司机带，不迷路！\")]),v._v(\" \"),t(\"h3\",{attrs:{id:\"职位三-web3d开发工程师-急\"}},[t(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#职位三-web3d开发工程师-急\"}},[v._v(\"#\")]),v._v(\" 职位三：web3D开发工程师（急）\")]),v._v(\" \"),t(\"p\",[t(\"strong\",[v._v(\"职位描述：\")])]),v._v(\" \"),t(\"ol\",[t(\"li\",[v._v(\"负责 VR 看房相关的业务开发，包括渲染SDK、标注平台等。\")]),v._v(\" \"),t(\"li\",[v._v(\"负责 VR 数据平台相关开发：包括数据预处理\")])]),v._v(\" \"),t(\"p\",[t(\"strong\",[v._v(\"职位要求：\")])]),v._v(\" \"),t(\"ol\",[t(\"li\",[t(\"p\",[v._v(\"熟练掌握 JavaScript，WebGL；\")])]),v._v(\" \"),t(\"li\",[t(\"p\",[v._v(\"熟悉计算机图形学，渲染管线/线性代数；\")])]),v._v(\" \"),t(\"li\",[t(\"p\",[v._v(\"熟悉常用 Shader 原理及编写；\")])]),v._v(\" \"),t(\"li\",[t(\"p\",[v._v(\"熟悉至少一款 H5 渲染引擎，如ThreeJS，Babylon等；\")])]),v._v(\" \"),t(\"li\",[t(\"p\",[v._v(\"热爱钻研新技术，有强烈的好奇心和求知欲，有良好的编码规范；\")])]),v._v(\" \"),t(\"li\",[t(\"p\",[v._v(\"加分项：\")])])]),v._v(\" \"),t(\"ul\",[t(\"li\",[v._v(\"熟悉VR看房相关业务\")]),v._v(\" \"),t(\"li\",[v._v(\"熟悉后端开发（Node）、对服务稳定性、并发了解同学优先（VR数据平台）。\")]),v._v(\" \"),t(\"li\",[v._v(\"熟悉 ThreeJS，Babylon, Unity3D 有相关 3D 作品或 DEMO。\")]),v._v(\" \"),t(\"li\",[v._v(\"各大前端技术社区活跃者、有自己的开源项目；\")])]),v._v(\" \"),t(\"h2\",{attrs:{id:\"其它职位-实习-校招-社招均可\"}},[t(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#其它职位-实习-校招-社招均可\"}},[v._v(\"#\")]),v._v(\" 其它职位（实习/校招/社招均可）\")]),v._v(\" \"),t(\"h3\",{attrs:{id:\"android开发工程师\"}},[t(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#android开发工程师\"}},[v._v(\"#\")]),v._v(\" Android开发工程师\")]),v._v(\" \"),t(\"p\",[v._v(\"职位描述\")]),v._v(\" \"),t(\"p\",[v._v(\"1、负责公司移动产品的研发, 编写高质量的代码；\")]),v._v(\" \"),t(\"p\",[v._v(\"2、和产品经理配合, 深度参与手机产品需求讨论, 功能定义等；\")]),v._v(\" \"),t(\"p\",[v._v(\"3、设计良好的代码结构, 不断迭代重构 。\")]),v._v(\" \"),t(\"p\",[v._v(\"职位要求\")]),v._v(\" \"),t(\"p\",[v._v(\"1、智能手机爱好者和使用者, 追求良好的用户体验；\")]),v._v(\" \"),t(\"p\",[v._v(\"2、热爱移动产品研发, 愿意在移动开发领域深入钻研, 并成为专家；\")]),v._v(\" \"),t(\"p\",[v._v(\"3、熟练掌握JAVA, 熟悉Android SDK；\")]),v._v(\" \"),t(\"p\",[v._v(\"4、一年以上Android开发经验, 能独立开发Android App； 5、对软件产品有强烈的责任心, 具备良好的沟通能力和优秀的团队协作能力。\")]),v._v(\" \"),t(\"p\",[v._v(\"5、有flutter开发经验者加分。\")]),v._v(\" \"),t(\"h3\",{attrs:{id:\"ios开发工程师\"}},[t(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#ios开发工程师\"}},[v._v(\"#\")]),v._v(\" iOS开发工程师\")]),v._v(\" \"),t(\"p\",[v._v(\"职位描述\")]),v._v(\" \"),t(\"p\",[v._v(\"1、负责公司移动产品的研发，编写高质量的代码；\")]),v._v(\" \"),t(\"p\",[v._v(\"2、和产品经理配合，深度参与手机产品需求讨论，功能定义等；\")]),v._v(\" \"),t(\"p\",[v._v(\"3、设计良好的代码结构，不断迭代重构 ；\")]),v._v(\" \"),t(\"p\",[v._v(\"4、导并带领初级工程师共同完成研发任务。\")]),v._v(\" \"),t(\"p\",[v._v(\"职位要求\")]),v._v(\" \"),t(\"p\",[v._v(\"1、有强烈的求知欲和进取心；\")]),v._v(\" \"),t(\"p\",[v._v(\"2、具有扎实的编程工底，良好的设计能力和编程习惯；\")]),v._v(\" \"),t(\"p\",[v._v(\"3、至少精通一门编程语言 ，熟练掌握Objective-C，熟悉Swift的优先 ；\")]),v._v(\" \"),t(\"p\",[v._v(\"4、一年以上iOS开发经验，能独立开发iPhoneApp者先。\")]),v._v(\" \"),t(\"p\",[v._v(\"5、有flutter开发经验者加分。\")]),v._v(\" \"),t(\"h3\",{attrs:{id:\"后端开发工程师\"}},[t(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#后端开发工程师\"}},[v._v(\"#\")]),v._v(\" 后端开发工程师\")]),v._v(\" \"),t(\"p\",[t(\"strong\",[v._v(\"职位描述\")])]),v._v(\" \"),t(\"p\",[v._v(\"1、主导或参与系统设计、研发、部署等相关工作；\")]),v._v(\" \"),t(\"p\",[v._v(\"2、研发基础服务组件，解决共性需求，减少重复开发与运维；\")]),v._v(\" \"),t(\"p\",[v._v(\"3、有较强的系统问题分析经验和能力，能够解决复杂的系统问题；\")]),v._v(\" \"),t(\"p\",[v._v(\"4、参与部分生产系统维护工作，解决生产系统问题及进行系统调优。\")]),v._v(\" \"),t(\"p\",[t(\"strong\",[v._v(\"职位要求\")])]),v._v(\" \"),t(\"p\",[v._v(\"1、本科及以上学历，计算机相关专业；\")]),v._v(\" \"),t(\"p\",[v._v(\"2、热爱计算机科学和互联网技术，精通至少一门编程语言，包括但不仅限于：Java、C、C++、PHP、 Python、Go；\")]),v._v(\" \"),t(\"p\",[v._v(\"3、掌握扎实的计算机基础知识，深入理解数据结构、算法和操作系统知识；\")]),v._v(\" \"),t(\"p\",[v._v(\"4、有优秀的逻辑分析能力，能够对业务逻辑进行合理的抽象和拆分；\")]),v._v(\" \"),t(\"p\",[v._v(\"5、有强烈的求知欲，优秀的学习和沟通能力。\")]),v._v(\" \"),t(\"h2\",{attrs:{id:\"返回书籍菜单列表\"}},[t(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#返回书籍菜单列表\"}},[v._v(\"#\")]),v._v(\" \"),t(\"a\",{attrs:{href:\"/index\"}},[v._v(\"返回书籍菜单列表\")])])])}),[],!1,null,null,null);_.default=r.exports}}]);"
  },
  {
    "path": "docs/assets/js/226.d982aa8d.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[226],{937:function(t,a,e){\"use strict\";e.r(a);var r=e(45),s=Object(r.a)({},(function(){var t=this,a=t.$createElement,e=t._self._c||a;return e(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[e(\"h1\",{attrs:{id:\"下一步\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#下一步\"}},[t._v(\"#\")]),t._v(\" 下一步\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"其它平台\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#其它平台\"}},[t._v(\"#\")]),t._v(\" 其它平台\")]),t._v(\" \"),e(\"p\",[t._v(\"本书主要讲的是Flutter在移动端开发\")]),t._v(\" \"),e(\"ul\",[e(\"li\")])])}),[],!1,null,null,null);a.default=s.exports}}]);"
  },
  {
    "path": "docs/assets/js/227.c297d2cb.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[227],{938:function(t,e,r){\"use strict\";r.r(e);var l=r(45),u=Object(l.a)({},(function(){var t=this,e=t.$createElement,r=t._self._c||e;return r(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[r(\"h1\",{attrs:{id:\"前言\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#前言\"}},[t._v(\"#\")]),t._v(\" 前言\")]),t._v(\" \"),r(\"h3\",{attrs:{id:\"缘起\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#缘起\"}},[t._v(\"#\")]),t._v(\" 缘起\")]),t._v(\" \"),r(\"p\",[t._v(\"在全球，随着Flutter被越来越多的知名公司应用在自己的商业APP中，Flutter这门新技术也逐渐进入了移动开发者的视野，尤其是当Google在2018年IO大会上发布了第一个Preview版本后，国内刮起来一股学习Flutter的热潮。\")]),t._v(\" \"),r(\"p\",[t._v(\"在Flutter发布之初，当时，我在看完Flutter原理介绍后，就对它产生了浓厚的兴趣。当时笔者身边也一些人比较关注Flutter，我也被经常问到关于Flutter的一些问题，比如Flutter怎么样？和RN有什么区别？Flutter为什么要用Dart？当时也听到了一些批评的声音，比如有些人说Flutter只是重复造轮子，没啥亮点、Flutter最大的缺点就是使用了Dart语言等。在听到这些问题及论调后，我深知这是对Flutter的不了解而造成的，这和当时国内缺乏Flutter中文文档和教程有直接关系，很多人对Flutter的了解都只停留在Google的发布会介绍（有中文翻译）。\")]),t._v(\" \"),r(\"p\",[t._v(\"在笔者深入的了解Flutter后，深知Flutter必将是一个会改变移动开发格局的里程碑级作品，它从设计之初就对性能和开发效率兼顾，并且借鉴了React（一个Web开发框架）的响应式的UI框架设计思想等，总之，很难用一两句话说完Flutter的优点，同时我也很快成为了Flutter的路转粉。\")]),t._v(\" \"),r(\"p\",[t._v(\"为了更好的帮助中国开发者了解这门新技术，我在2018年初开始翻译Flutter官网文档，同年4月份上线了\"),r(\"a\",{attrs:{href:\"https://flutterchina.club/\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Flutter中文网\"),r(\"OutboundLink\")],1),t._v(\"，上线后反响很强烈，Flutter中文网也很快被传播开，百度搜索排名迅速蹿升到前三，截止目前，Flutter中文官网日PV在7万左右，每日独立访问人数近一万多。\")]),t._v(\" \"),r(\"p\",[t._v(\"虽然Flutter中文网给中国开发者提供了很好的第一手了解Flutter的资料，但是笔者还会经常遇到一些对Flutter技术处于围观而不愿尝试的开发者。这主要是因为当时Flutter在国内没有成功案例，再加上新技术都有学习成本，所以即使有文档，也会有一些开发者犹豫是否来学习。为了解决这部分开发者的疑虑，我就想如果能用Flutter开发一个完整的APP发布到应用商店，这样开发者就可以在犹豫的时候可以先实际感受一下Flutter应用，这样有个直观的了解后，就会容易做出判断，为此，我开发了\"),r(\"a\",{attrs:{href:\"https://flutterchina.club/app/gm.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Gitme\"),r(\"OutboundLink\")],1),t._v(\"，它是一个Github客户端，它支持了源码浏览、Issue、Label等Github的大多数功能，到目前为止，通过Gitme登录过Github账号的用户有8000多人，日活用户有1000人。更重要的是，有很多人正是用了Gitme后，才来学Flutter的。\")]),t._v(\" \"),r(\"p\",[t._v(\"无论是做Flutter中文网，还是写Gitme，主要目标都是帮助开发者学习Flutter，同时消除围观开发者疑虑。但当开发者们真正开始动手时，Flutter的生态问题就变得尤为突出。由于在2018年初Flutter刚起步时，很多基础的包和库都是空白，少数已有的一些库也大都是预览版（未到1.0），存在很多bug。这个状况不是一两个人花一两天能搞定的，这是需要聚整个Flutter开发者社群之力，耗费数年时间才可能有所改善。因此，在2018年4月份，我以Flutter中文网名义发起了Flutter开源计划，该计划主要是开发一些常用的包来丰富Flutter生态，帮助开发者提高开发效率。自在github建立\"),r(\"a\",{attrs:{href:\"https://github.com/flutterchina\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Flutter中文开发者社区官方账号\"),r(\"OutboundLink\")],1),t._v(\"以来，前后开源了dio、cookieJar、flukit等多个项目，而dio在开源两周后，就迅速成为Flutter第三方包中Star排名第一的开源库。\")]),t._v(\" \"),r(\"p\",[t._v(\"虽然做的事情已经够多了，但是仍有一些很有必要去做的事情，由于时间原因，一直被搁置。\")]),t._v(\" \"),r(\"p\",[t._v(\"随着学习Flutter的人越来越多，一部分开发者通过查看官网的文档就能入门，但也有很多开发者感觉学习时仍然有些吃力，主要原因有两个，首先官网的文档主要是为了引导开发者快速上手的，讲的并不是很细；其次是我们虽然翻译了官方文档，但是对于Flutter SDK文档并没有翻译，而在开发中遇到的一些具体问题通常都得去查看SDK文档。所以，要解决这两个问题，必须得有一个系统化的Flutter教程，它不仅可以快速引导开发者入门，而且也能触及到一些细节和原理，最好也能提供一些学习和研究Flutter的方法。因此，如果能有一本能系统地介绍Flutter的书籍便是便是非常棒的！但是，当时没有一本关于Flutter的中文书籍，因此，我便计划写一本能由浅入深、系统介绍Flutter的书。2018年12月，《Flutter实战》完成初稿，并在Github上开源，同时上线了《Flutter实战》电子书官网（https://book.flutterchina.club/） ，至今每天有3000多人在线浏览本书。那为什么不直接出版？如果直接出版，不仅有稿费，而且也能保护知识产权，而直接开源，不就就只能当雷锋了？其实，无论是做中文网、写Gitme、做Flutter开源项目，我的出发点都是为了能帮助中国开发者了解、学习Flutter，而这是一件非常有意义的事，只要坚持做对别人有价值的事，那么上帝迟早会奖赏你；当然我们也在网站中留了打赏的按钮，如果读者觉得有帮助，可以扫码打赏，请笔者喝一杯咖啡。另外由于成书比较仓促，当时书中也有很多错误，开源后，有很多读者提PR来纠正书中的错别字，时至今日，有78名开发者为本书提过PR，我在此，衷心的感谢你们。\")]),t._v(\" \"),r(\"p\",[t._v(\"起初，我没有出版实体书的打算，当时我以为开发者直接通过在线访问本书官网即方便又不用付费，何乐而不为。但在本书上线后，很多读者来添加微信好友，表示非常有收益，很期待出版纸质书，甚至有比较热心的读者想提前付定金预定！我理解，这是大家对我所做之事的认可和鼓励。考虑到，确实有一部分读者，尤其是还没毕业的同学，可能更喜欢通过书籍去学习，为此，我已经和机械工业出版社合作，目前本书纸质版正在出版中，敬请关注。\")]),t._v(\" \"),r(\"h3\",{attrs:{id:\"本书组织结构\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本书组织结构\"}},[t._v(\"#\")]),t._v(\" 本书组织结构\")]),t._v(\" \"),r(\"p\",[t._v(\"本书采用由浅入深的方式介绍Flutter技术原理，分为三篇，总计15章，各篇的主要内容如下。\")]),t._v(\" \"),r(\"ul\",[r(\"li\",[t._v(\"第一篇，入门篇（第1章~第7章），包括Flutter技术的出现背景和简介、Flutter的各种类型的Widget以及如何构建UI。通过学习本章，读者可以掌握如何使用Flutter来构建UI界面。\")]),t._v(\" \"),r(\"li\",[t._v(\"第二篇，进阶篇（第8章~第14章），包括Flutter中的事件机制、动画、自定义组件、文件和网络、插件、国际化以及Flutter核心原理等。通过本章内容，读者可以对Flutter整体构建及原理有一个深入的认识。\")]),t._v(\" \"),r(\"li\",[t._v(\"第三篇，实例篇（第15张），本章主要通过一个简版的Github APP来将前面介绍的内容串起来，让开发者对一个完整的Flutter APP开发流有个了解。\")])]),t._v(\" \"),r(\"p\",[t._v(\"由于Flutter的很多知识点是相互交织的，很难将它们彻底划分开，所以本书中也难免会出现一些在前面章节会使用在后面章节的场景，比如我们在入门篇介绍进度指示器时会用到在进阶篇中才介绍的动画相关知识。本书中对于这种情况会在相应的章节进行说明。读者可以直接跳到后面相应知识点章节阅读后再返回，也可以先有个印象，待学习到后面相关章节后再回头来看。\")]),t._v(\" \"),r(\"h3\",{attrs:{id:\"本书特色\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本书特色\"}},[t._v(\"#\")]),t._v(\" 本书特色\")]),t._v(\" \"),r(\"p\",[t._v(\"笔者在大学时读过候捷（真名侯俊杰）写的一些C++相关书籍，在他的《深入浅出MFC》一书中，有一句话我印象非常深 “唯有深入，方能浅出”。我非常认同这句话，对于一门技术，只有了解的深入，才能用最浅显、通俗的话语描述出。我在写作本书时，深入浅出就是一个主要目标。所以，本书的目标不仅是想告诉读者如何使用Flutter，而且也非常关注各个知识点的底层实现以及设计思想。从本书章节划分上来看，入门篇为“浅出”，进阶篇则是“深入”。另外由于PC客户端开发、移动开发、Web开发这些经验我都有，而Flutter本质上是一个UI系统，而UI系统的设计和实现在”大前端“下有很多相通之处，所以在本书中的一些知识点我也会对比一些其他UI系统（主要是Android或Web）相应的实现，便于有相关开发经验的读者对比理解。\")]),t._v(\" \"),r(\"h3\",{attrs:{id:\"本书读者对象\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#本书读者对象\"}},[t._v(\"#\")]),t._v(\" 本书读者对象\")]),t._v(\" \"),r(\"ul\",[r(\"li\",[t._v(\"读者至少熟悉一种编程语言。\")]),t._v(\" \"),r(\"li\",[t._v(\"读者最好接触过PC客户端、移动开发或Web前端开发中的一种。\")]),t._v(\" \"),r(\"li\",[t._v(\"本书不适合做为编程的入门读物。\")])]),t._v(\" \"),r(\"h3\",{attrs:{id:\"关于随书源码\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#关于随书源码\"}},[t._v(\"#\")]),t._v(\" 关于随书源码\")]),t._v(\" \"),r(\"p\",[t._v(\"由于篇幅所限，本书中大多数示例代码都只是部分核心代码，读者可以去https://github.com/wendux/flutter_in_action_source_code 查看\")]),t._v(\" \"),r(\"h3\",{attrs:{id:\"勘误\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#勘误\"}},[t._v(\"#\")]),t._v(\" 勘误\")]),t._v(\" \"),r(\"p\",[t._v(\"由于Flutter SDK在不断更新，本书中的部分内容（如类的继承关系、参数等）可能会和新版本的Flutter不一致，读者以最新的Flutter SDK为准。另外，由于时间仓促，书中难免有错误之处，如果发现错误，可以在本书Github项目issue列表中去反馈，地址是https://github.com/flutterchina/flutter-in-action/issues 。\")]),t._v(\" \"),r(\"h3\",{attrs:{id:\"致谢\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#致谢\"}},[t._v(\"#\")]),t._v(\" 致谢\")]),t._v(\" \"),r(\"p\",[t._v(\"感谢一直以来支持Flutter中文网、Flutter开源项目的人以及所有对本书提过PR的人，正是因为有你们，才有这本书。另外尤其感谢给本书打赏过的同学，你们的支持给了我很大的鼓励。\")])])}),[],!1,null,null,null);e.default=u.exports}}]);"
  },
  {
    "path": "docs/assets/js/228.4dcdd69c.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[228],{939:function(t,e,l){\"use strict\";l.r(e);var p=l(45),i=Object(p.a)({},(function(){var t=this,e=t.$createElement,l=t._self._c||e;return l(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[l(\"h1\",{attrs:{id:\"参考文献\"}},[l(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#参考文献\"}},[t._v(\"#\")]),t._v(\" 参考文献\")]),t._v(\" \"),l(\"ul\",[l(\"li\",[l(\"p\",[t._v(\"React Native官网：https://facebook.github.io/react-native/\")])]),t._v(\" \"),l(\"li\",[l(\"p\",[t._v(\"Weex：https://weex.apache.org/zh/guide/introduction.html\")])]),t._v(\" \"),l(\"li\",[l(\"p\",[t._v(\"快应用：https://www.quickapp.cn/\")])]),t._v(\" \"),l(\"li\",[l(\"p\",[t._v(\"QT for mobile：https://www.qt.io/mobile-app-development/\")])]),t._v(\" \"),l(\"li\",[l(\"p\",[t._v(\"Flutter官网：https://flutter.dev/\")])]),t._v(\" \"),l(\"li\",[l(\"p\",[t._v(\"Flutter中文网社区：https://flutterchina.club/docs/\")])]),t._v(\" \"),l(\"li\",[l(\"p\",[t._v(\"Dart Packages官网：https://pub.dev/\")])]),t._v(\" \"),l(\"li\",[l(\"p\",[t._v(\"Flutter中文开发者社区开源项目：https://github.com/flutterchina\")])]),t._v(\" \"),l(\"li\",[l(\"p\",[t._v(\"Material Design：https://material.io/\")])]),t._v(\" \"),l(\"li\",[l(\"p\",[t._v(\"Github 开发者中心官网：https://developer.github.com/v3/\")])]),t._v(\" \"),l(\"li\",[l(\"p\",[t._v(\"Android开发者中心官网：https://developer.android.google.cn/\")])]),t._v(\" \"),l(\"li\",[l(\"p\",[t._v(\"Apple开发者中心官网：https://developer.apple.com/\")])])])])}),[],!1,null,null,null);e.default=i.exports}}]);"
  },
  {
    "path": "docs/assets/js/229.91061a53.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[229],{940:function(t,r,e){\"use strict\";e.r(r);var o=e(45),a=Object(o.a)({},(function(){var t=this,r=t.$createElement,e=t._self._c||r;return e(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[e(\"h1\",{attrs:{id:\"summary\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#summary\"}},[t._v(\"#\")]),t._v(\" Summary\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/\"}},[t._v(\"简介\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/intro.html\"}},[t._v(\"前言\")])],1)]),t._v(\" \"),e(\"h2\",{attrs:{id:\"入门篇\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#入门篇\"}},[t._v(\"#\")]),t._v(\" 入门篇\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"p\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter1/\"}},[t._v(\"第一章：起步\")])],1),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter1/mobile_development_intro.html\"}},[t._v(\"1.1：移动开发技术简介\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter1/flutter_intro.html\"}},[t._v(\"1.2：初识Flutter\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter1/install_flutter.html\"}},[t._v(\"1.3：搭建Flutter开发环境\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter1/dart.html\"}},[t._v(\"1.4：Dart语言简介\")])],1)])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter2/\"}},[t._v(\"第二章：第一个Flutter应用\")])],1),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter2/first_flutter_app.html\"}},[t._v(\"2.1：计数器示例\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter2/flutter_router.html\"}},[t._v(\"2.2：路由管理\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter2/flutter_package_mgr.html\"}},[t._v(\"2.3：包管理\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter2/flutter_assets_mgr.html\"}},[t._v(\"2.4：资源管理\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter2/flutter_app_debug.html\"}},[t._v(\"2.5：调试Flutter APP\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter2/thread_model_and_error_report.html\"}},[t._v(\"2.6：Dart线程模型及异常捕获\")])],1)])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter3/\"}},[t._v(\"第三章：基础组件\")])],1),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter3/flutter_widget_intro.html\"}},[t._v(\"3.1：Widget简介\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter3/state_manage.html\"}},[t._v(\"3.2：状态管理\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter3/text.html\"}},[t._v(\"3.3：文本、字体样式\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter3/buttons.html\"}},[t._v(\"3.4：按钮\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter3/img_and_icon.html\"}},[t._v(\"3.5：图片和Icon\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter3/radio_and_checkbox.html\"}},[t._v(\"3.6：单选框和复选框\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter3/input_and_form.html\"}},[t._v(\"3.7：输入框和表单\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter3/progress.html\"}},[t._v(\"3.8：进度指示器\")])],1)])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter4/\"}},[t._v(\"第四章：布局类组件\")])],1),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter4/intro.html\"}},[t._v(\"4.1：布局类组件简介\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter4/row_and_column.html\"}},[t._v(\"4.2：线性布局（Row、Column）\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter4/flex.html\"}},[t._v(\"4.3：弹性布局（Flex）\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter4/wrap_and_flow.html\"}},[t._v(\"4.4：流式布局（Wrap、Flow）\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter4/stack.html\"}},[t._v(\"4.5：层叠布局（Stack、Positioned）\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter4/alignment.html\"}},[t._v(\"4.6：对齐与相对定位（Align）\")])],1)])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter5/\"}},[t._v(\"第五章：容器类组件\")])],1),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter5/padding.html\"}},[t._v(\"5.1：填充（Padding）\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter5/constrainedbox_and_sizebox.html\"}},[t._v(\"5.2：尺寸限制类容器（ConstrainedBox等）\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter5/decoratedbox.html\"}},[t._v(\"5.3：装饰容器（DecoratedBox）\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter5/transform.html\"}},[t._v(\"5.4：变换（Transform）\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter5/container.html\"}},[t._v(\"5.5：Container容器\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter5/material_scaffold.html\"}},[t._v(\"5.6：Scaffold、TabBar、底部导航\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter5/clip.html\"}},[t._v(\"5.7：剪裁（Clip）\")])],1)])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter6/\"}},[t._v(\"第六章：可滚动组件\")])],1),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter6/intro.html\"}},[t._v(\"6.1：可滚动组件简介\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter6/single_child_scrollview.html\"}},[t._v(\"6.2：SingleChildScrollView\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter6/listview.html\"}},[t._v(\"6.3：ListView\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter6/gridview.html\"}},[t._v(\"6.4：GridView\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter6/custom_scrollview.html\"}},[t._v(\"6.5：CustomScrollView\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter6/scroll_controller.html\"}},[t._v(\"6.6：滚动监听及控制（ScrollController）\")])],1)])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter7/\"}},[t._v(\"第七章：功能型组件\")])],1),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter7/willpopscope.html\"}},[t._v(\"7.1：导航返回拦截（WillPopScope）\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter7/inherited_widget.html\"}},[t._v(\"7.2：数据共享（InheritedWidget）\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter7/provider.html\"}},[t._v(\"7.3： 跨组件状态共享（Provider）\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter7/theme.html\"}},[t._v(\"7.4：颜色和主题（Theme）\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter7/futurebuilder_and_streambuilder.html\"}},[t._v(\"7.5：异步UI更新（FutureBuilder、StreamBuilder）\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter7/dailog.html\"}},[t._v(\"7.6：对话框详解\")])],1)])])]),t._v(\" \"),e(\"h2\",{attrs:{id:\"进阶篇\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#进阶篇\"}},[t._v(\"#\")]),t._v(\" 进阶篇\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"p\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter8/\"}},[t._v(\"第八章：事件处理与通知\")])],1),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter8/listener.html\"}},[t._v(\"8.1：原始指针事件处理\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter8/gesture.html\"}},[t._v(\"8.2：手势识别\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter8/eventbus.html\"}},[t._v(\"8.3：全局事件总线\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter8/notification.html\"}},[t._v(\"8.4：通知(Notification)\")])],1)])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter9/\"}},[t._v(\"第九章：动画\")])],1),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter9/intro.html\"}},[t._v(\"9.1：Flutter动画简介\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter9/animation_structure.html\"}},[t._v(\"9.2：动画结构\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter9/route_transition.html\"}},[t._v(\"9.3：自定义路由过渡动画\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter9/hero.html\"}},[t._v(\"9.4：Hero动画\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter9/stagger_animation.html\"}},[t._v(\"9.5：交织动画\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter9/animated_switcher.html\"}},[t._v(\"9.6：通用“动画切换”组件（AnimatedSwitcher）\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter9/animated_widgets.html\"}},[t._v(\"9.7：动画过渡组件\")])],1)])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter10/\"}},[t._v(\"第十章：自定义组件\")])],1),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter10/intro.html\"}},[t._v(\"10.1：自定义组件方法简介\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter10/combine.html\"}},[t._v(\"10.2：组合现有组件\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter10/turn_box.html\"}},[t._v(\"10.3：组合实例：TurnBox\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter10/custom_paint.html\"}},[t._v(\"10.4：自绘组件（CustomPaint与Canvas）\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter10/gradient_circular_progress_demo.html\"}},[t._v(\"10.5：自绘实例：圆形渐变进度条(自绘)\")])],1)])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter11/\"}},[t._v(\"第十一章：文件操作与网络请求\")])],1),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter11/file_operation.html\"}},[t._v(\"11.1：文件操作\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter11/http.html\"}},[t._v(\"11.2：Http请求-HttpClient\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter11/dio.html\"}},[t._v(\"11.3：Http请求-Dio package\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter11/download_with_chunks.html\"}},[t._v(\"11.4：实例：Http分块下载\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter11/websocket.html\"}},[t._v(\"11.5：WebSocket\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter11/socket.html\"}},[t._v(\"11.6：使用Socket API\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter11/json_model.html\"}},[t._v(\"11.7：Json转Dart Model类\")])],1)])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter12/\"}},[t._v(\"第十二章：包与插件\")])],1),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter12/develop_package.html\"}},[t._v(\"12.1：开发package\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter12/platform-channel.html\"}},[t._v(\"12.2：平台通道简介\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter12/develop_plugin.html\"}},[t._v(\"12.3：开发Flutter插件\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter12/android_implement.html\"}},[t._v(\"12.4：插件开发：实现Android端API\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter12/ios_implement.html\"}},[t._v(\"12.5：插件开发：实现IOS端API\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter12/texture_platformview.html\"}},[t._v(\"12.6：Texture和PlatformView\")])],1)])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter13/\"}},[t._v(\"第十三章：国际化\")])],1),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter13/multi_languages_support.html\"}},[t._v(\"13.1：让App支持多语言\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter13/locallization_implement.html\"}},[t._v(\"13.2：实现Localizations\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter13/intl.html\"}},[t._v(\"13.3：使用Intl包\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter13/faq.html\"}},[t._v(\"13.4：国际化常见问题\")])],1)])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter14/\"}},[t._v(\"第十四章：Flutter核心原理\")])],1),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter14/flutter_ui_system.html\"}},[t._v(\"14.1：Flutter UI系统\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter14/element_buildcontext.html\"}},[t._v(\"14.2：Element和BuildContext\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter14/render_object.html\"}},[t._v(\"14.3：RenderObject与RenderBox\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter14/flutter_app_startup.html\"}},[t._v(\"14.4：Flutter从启动到显示\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter14/image_and_cache.html\"}},[t._v(\"14.5：Flutter图片加载与缓存\")])],1)])])]),t._v(\" \"),e(\"h2\",{attrs:{id:\"实例篇\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#实例篇\"}},[t._v(\"#\")]),t._v(\" 实例篇\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter15/intro.html\"}},[t._v(\"第十五章：一个完整的Flutter应用\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter15/intro.html\"}},[t._v(\"15.1：应用简介\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter15/code_structure.html\"}},[t._v(\"15.2：APP代码结构\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter15/models.html\"}},[t._v(\"15.3：Model类定义\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter15/globals.html\"}},[t._v(\"15.4：全局变量及共享状态\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter15/network.html\"}},[t._v(\"15.5：网络请求封装\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter15/entry.html\"}},[t._v(\"15.6：App入口及首页\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter15/login_page.html\"}},[t._v(\"15.7：登录页\")])],1),t._v(\" \"),e(\"li\",[e(\"RouterLink\",{attrs:{to:\"/v2/chapter15/language_and_theme_setting.html\"}},[t._v(\"15.8：多语言和多主题\")])],1)])],1)])])}),[],!1,null,null,null);r.default=a.exports}}]);"
  },
  {
    "path": "docs/assets/js/23.87c6db58.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[23],{600:function(t,a,s){t.exports=s.p+\"assets/img/3-1.587e85ad.png\"},601:function(t,a,s){t.exports=s.p+\"assets/img/3-1-1.63daca67.png\"},602:function(t,a,s){t.exports=s.p+\"assets/img/3-2.a59bef97.jpg\"},603:function(t,a,s){t.exports=s.p+\"assets/img/3-1-2.70506d6d.png\"},604:function(t,a,s){t.exports=s.p+\"assets/img/3-3.3b5b2dd3.png\"},888:function(t,a,s){\"use strict\";s.r(a);var e=s(45),n=Object(e.a)({},(function(){var t=this,a=t.$createElement,e=t._self._c||a;return e(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[e(\"h1\",{attrs:{id:\"_3-1-widget简介\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-1-widget简介\"}},[t._v(\"#\")]),t._v(\" 3.1 Widget简介\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"_3-1-1-概念\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-1-1-概念\"}},[t._v(\"#\")]),t._v(\" 3.1.1 概念\")]),t._v(\" \"),e(\"p\",[t._v(\"在前面的介绍中，我们知道在Flutter中几乎所有的对象都是一个Widget。与原生开发中“控件”不同的是，Flutter中的Widget的概念更广泛，它不仅可以表示UI元素，也可以表示一些功能性的组件如：用于手势检测的 \"),e(\"code\",[t._v(\"GestureDetector\")]),t._v(\" widget、用于APP主题数据传递的\"),e(\"code\",[t._v(\"Theme\")]),t._v(\"等等，而原生开发中的控件通常只是指UI元素。在后面的内容中，我们在描述UI元素时可能会用到“控件”、“组件”这样的概念，读者心里需要知道他们就是widget，只是在不同场景的不同表述而已。由于Flutter主要就是用于构建用户界面的，所以，在大多数时候，读者可以认为widget就是一个控件，不必纠结于概念。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"_3-1-2-widget与element\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-1-2-widget与element\"}},[t._v(\"#\")]),t._v(\" 3.1.2 Widget与Element\")]),t._v(\" \"),e(\"p\",[t._v(\"在Flutter中，Widget的功能是“描述一个UI元素的配置数据”，它就是说，Widget其实并不是表示最终绘制在设备屏幕上的显示元素，而它只是描述显示元素的一个配置数据。\")]),t._v(\" \"),e(\"p\",[t._v(\"实际上，Flutter中真正代表屏幕上显示元素的类是\"),e(\"code\",[t._v(\"Element\")]),t._v(\"，也就是说Widget只是描述\"),e(\"code\",[t._v(\"Element\")]),t._v(\"的配置数据！有关\"),e(\"code\",[t._v(\"Element\")]),t._v(\"的详细介绍我们将在本书后面的高级部分深入介绍，现在，读者只需要知道：\"),e(\"strong\",[t._v(\"Widget只是UI元素的一个配置数据，并且一个Widget可以对应多个\"),e(\"code\",[t._v(\"Element\")])]),t._v(\"。这是因为同一个Widget对象可以被添加到UI树的不同部分，而真正渲染时，UI树的每一个\"),e(\"code\",[t._v(\"Element\")]),t._v(\"节点都会对应一个Widget对象。总结一下：\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"Widget实际上就是\"),e(\"code\",[t._v(\"Element\")]),t._v(\"的配置数据，Widget树实际上是一个配置树，而真正的UI渲染树是由\"),e(\"code\",[t._v(\"Element\")]),t._v(\"构成；不过，由于\"),e(\"code\",[t._v(\"Element\")]),t._v(\"是通过Widget生成的，所以它们之间有对应关系，在大多数场景，我们可以宽泛地认为Widget树就是指UI控件树或UI渲染树。\")]),t._v(\" \"),e(\"li\",[t._v(\"一个Widget对象可以对应多个\"),e(\"code\",[t._v(\"Element\")]),t._v(\"对象。这很好理解，根据同一份配置（Widget），可以创建多个实例（Element）。\")])]),t._v(\" \"),e(\"p\",[t._v(\"读者应该将这两点牢记在心中。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"_3-1-3-widget主要接口\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-1-3-widget主要接口\"}},[t._v(\"#\")]),t._v(\" 3.1.3 Widget主要接口\")]),t._v(\" \"),e(\"p\",[t._v(\"我们先来看一下Widget类的声明：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@immutable\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"abstract\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Widget\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"DiagnosticableTree\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Widget\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"key \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Key key\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@protected\")]),t._v(\"\\n  Element \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createElement\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  String \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toStringShort\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" key \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'$runtimeType'\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'$runtimeType-$key'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"debugFillProperties\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"DiagnosticPropertiesBuilder properties\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"debugFillProperties\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"properties\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    properties\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"defaultDiagnosticsTreeStyle \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" DiagnosticsTreeStyle\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dense\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" bool \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"canUpdate\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Widget oldWidget\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Widget newWidget\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" oldWidget\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"runtimeType \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" newWidget\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"runtimeType\\n        \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\" oldWidget\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"key \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" newWidget\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"key\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"ul\",[e(\"li\",[e(\"code\",[t._v(\"Widget\")]),t._v(\"类继承自\"),e(\"code\",[t._v(\"DiagnosticableTree\")]),t._v(\"，\"),e(\"code\",[t._v(\"DiagnosticableTree\")]),t._v(\"即“诊断树”，主要作用是提供调试信息。\")]),t._v(\" \"),e(\"li\",[e(\"code\",[t._v(\"Key\")]),t._v(\": 这个\"),e(\"code\",[t._v(\"key\")]),t._v(\"属性类似于React/Vue中的\"),e(\"code\",[t._v(\"key\")]),t._v(\"，主要的作用是决定是否在下一次\"),e(\"code\",[t._v(\"build\")]),t._v(\"时复用旧的widget，决定的条件在\"),e(\"code\",[t._v(\"canUpdate()\")]),t._v(\"方法中。\")]),t._v(\" \"),e(\"li\",[e(\"code\",[t._v(\"createElement()\")]),t._v(\"：正如前文所述“一个Widget可以对应多个\"),e(\"code\",[t._v(\"Element\")]),t._v(\"”；Flutter Framework在构建UI树时，会先调用此方法生成对应节点的\"),e(\"code\",[t._v(\"Element\")]),t._v(\"对象。此方法是Flutter Framework隐式调用的，在我们开发过程中基本不会调用到。\")]),t._v(\" \"),e(\"li\",[e(\"code\",[t._v(\"debugFillProperties(...)\")]),t._v(\" 复写父类的方法，主要是设置诊断树的一些特性。\")]),t._v(\" \"),e(\"li\",[e(\"code\",[t._v(\"canUpdate(...)\")]),t._v(\"是一个静态方法，它主要用于在Widget树重新\"),e(\"code\",[t._v(\"build\")]),t._v(\"时复用旧的widget，其实具体来说，应该是：是否用新的Widget对象去更新旧UI树上所对应的\"),e(\"code\",[t._v(\"Element\")]),t._v(\"对象的配置；通过其源码我们可以看到，只要\"),e(\"code\",[t._v(\"newWidget\")]),t._v(\"与\"),e(\"code\",[t._v(\"oldWidget\")]),t._v(\"的\"),e(\"code\",[t._v(\"runtimeType\")]),t._v(\"和\"),e(\"code\",[t._v(\"key\")]),t._v(\"同时相等时就会用\"),e(\"code\",[t._v(\"newWidget\")]),t._v(\"去更新\"),e(\"code\",[t._v(\"Element\")]),t._v(\"对象的配置，否则就会创建新的\"),e(\"code\",[t._v(\"Element\")]),t._v(\"。\")])]),t._v(\" \"),e(\"p\",[t._v(\"有关Key和Widget复用的细节将会在本书后面高级部分深入讨论，读者现在只需知道，为Widget显式添加key的话可能（但不一定）会使UI在重新构建时变的高效，读者目前可以先忽略此参数。本书后面的示例中，只会在构建列表项UI时会显式指定Key。\")]),t._v(\" \"),e(\"p\",[t._v(\"另外\"),e(\"code\",[t._v(\"Widget\")]),t._v(\"类本身是一个抽象类，其中最核心的就是定义了\"),e(\"code\",[t._v(\"createElement()\")]),t._v(\"接口，在Flutter开发中，我们一般都不用直接继承\"),e(\"code\",[t._v(\"Widget\")]),t._v(\"类来实现一个新组件，相反，我们通常会通过继承\"),e(\"code\",[t._v(\"StatelessWidget\")]),t._v(\"或\"),e(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"来间接继承\"),e(\"code\",[t._v(\"Widget\")]),t._v(\"类来实现。\"),e(\"code\",[t._v(\"StatelessWidget\")]),t._v(\"和\"),e(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"都是直接继承自\"),e(\"code\",[t._v(\"Widget\")]),t._v(\"类，而这两个类也正是Flutter中非常重要的两个抽象类，它们引入了两种Widget模型，接下来我们将重点介绍一下这两个类。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"_3-1-4-statelesswidget\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-1-4-statelesswidget\"}},[t._v(\"#\")]),t._v(\" 3.1.4 StatelessWidget\")]),t._v(\" \"),e(\"p\",[t._v(\"在之前的章节中，我们已经简单介绍过\"),e(\"code\",[t._v(\"StatelessWidget\")]),t._v(\"，\"),e(\"code\",[t._v(\"StatelessWidget\")]),t._v(\"相对比较简单，它继承自\"),e(\"code\",[t._v(\"Widget\")]),t._v(\"类，重写了\"),e(\"code\",[t._v(\"createElement()\")]),t._v(\"方法：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\nStatelessElement \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createElement\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessElement\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),e(\"p\",[e(\"code\",[t._v(\"StatelessElement\")]),t._v(\" 间接继承自\"),e(\"code\",[t._v(\"Element\")]),t._v(\"类，与\"),e(\"code\",[t._v(\"StatelessWidget\")]),t._v(\"相对应（作为其配置数据）。\")]),t._v(\" \"),e(\"p\",[e(\"code\",[t._v(\"StatelessWidget\")]),t._v(\"用于不需要维护状态的场景，它通常在\"),e(\"code\",[t._v(\"build\")]),t._v(\"方法中通过嵌套其它Widget来构建UI，在构建过程中会递归的构建其嵌套的Widget。我们看一个简单的例子：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Echo\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Echo\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"  \\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"text\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"backgroundColor\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"Colors\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"key\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String text\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Color backgroundColor\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        color\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" backgroundColor\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"text\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"上面的代码，实现了一个回显字符串的\"),e(\"code\",[t._v(\"Echo\")]),t._v(\" widget。\")]),t._v(\" \"),e(\"blockquote\",[e(\"p\",[t._v(\"按照惯例，\"),e(\"code\",[t._v(\"widget\")]),t._v(\"的构造函数参数应使用命名参数，命名参数中的必要参数要添加\"),e(\"code\",[t._v(\"@required\")]),t._v(\"标注，这样有利于静态代码分析器进行检查。另外，在继承\"),e(\"code\",[t._v(\"widget\")]),t._v(\"时，第一个参数通常应该是\"),e(\"code\",[t._v(\"Key\")]),t._v(\"，另外，如果Widget需要接收子Widget，那么\"),e(\"code\",[t._v(\"child\")]),t._v(\"或\"),e(\"code\",[t._v(\"children\")]),t._v(\"参数通常应被放在参数列表的最后。同样是按照惯例，Widget的属性应尽可能的被声明为\"),e(\"code\",[t._v(\"final\")]),t._v(\"，防止被意外改变。\")])]),t._v(\" \"),e(\"p\",[t._v(\"然后我们可以通过如下方式使用它：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[t._v(\"Widget \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Echo\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"text\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"hello world\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"运行后效果如图3-1所示：\")]),t._v(\" \"),e(\"p\",[e(\"img\",{attrs:{src:s(600),alt:\"图3-1\"}})]),t._v(\" \"),e(\"h3\",{attrs:{id:\"context\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#context\"}},[t._v(\"#\")]),t._v(\" Context\")]),t._v(\" \"),e(\"p\",[e(\"code\",[t._v(\"build\")]),t._v(\"方法有一个\"),e(\"code\",[t._v(\"context\")]),t._v(\"参数，它是\"),e(\"code\",[t._v(\"BuildContext\")]),t._v(\"类的一个实例，表示当前widget在widget树中的上下文，每一个widget都会对应一个context对象（因为每一个widget都是widget树上的一个节点）。实际上，\"),e(\"code\",[t._v(\"context\")]),t._v(\"是当前widget在widget树中位置中执行”相关操作“的一个句柄，比如它提供了从当前widget开始向上遍历widget树以及按照widget类型查找父级widget的方法。下面是在子树中获取父级widget的一个示例：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ContextRoute\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      appBar\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        title\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Context测试\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      body\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 在Widget树中向上查找最近的父级`Scaffold` widget\")]),t._v(\"\\n          Scaffold scaffold \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"findAncestorWidgetOfExactType\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Scaffold\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v('// 直接返回 AppBar的title， 此处实际上是Text(\"Context测试\")')]),t._v(\"\\n          \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"scaffold\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"appBar \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"as\")]),t._v(\" AppBar\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"title\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"运行后效果如图3-1-1所示：\")]),t._v(\" \"),e(\"p\",[e(\"img\",{attrs:{src:s(601),alt:\"图3-1-1\"}})]),t._v(\" \"),e(\"blockquote\",[e(\"p\",[e(\"strong\",[t._v(\"注意\")]),t._v(\"：对于\"),e(\"code\",[t._v(\"BuildContext\")]),t._v(\"读者现在可以先作了解，随着本书后面内容的展开，也会用到Context的一些方法，读者可以通过具体的场景对其有个直观的认识。关于\"),e(\"code\",[t._v(\"BuildContext\")]),t._v(\"更多的内容，我们也将在后面高级部分再深入介绍。\")])]),t._v(\" \"),e(\"h2\",{attrs:{id:\"_3-1-5-statefulwidget\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-1-5-statefulwidget\"}},[t._v(\"#\")]),t._v(\" 3.1.5 StatefulWidget\")]),t._v(\" \"),e(\"p\",[t._v(\"和\"),e(\"code\",[t._v(\"StatelessWidget\")]),t._v(\"一样，\"),e(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"也是继承自\"),e(\"code\",[t._v(\"Widget\")]),t._v(\"类，并重写了\"),e(\"code\",[t._v(\"createElement()\")]),t._v(\"方法，不同的是返回的\"),e(\"code\",[t._v(\"Element\")]),t._v(\" 对象并不相同；另外\"),e(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"类中添加了一个新的接口\"),e(\"code\",[t._v(\"createState()\")]),t._v(\"。\")]),t._v(\" \"),e(\"p\",[t._v(\"下面我们看看\"),e(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"的类定义：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"abstract\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Widget\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"StatefulWidget\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" Key key \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  StatefulElement \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createElement\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulElement\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@protected\")]),t._v(\"\\n  State \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"ul\",[e(\"li\",[e(\"p\",[e(\"code\",[t._v(\"StatefulElement\")]),t._v(\" 间接继承自\"),e(\"code\",[t._v(\"Element\")]),t._v(\"类，与StatefulWidget相对应（作为其配置数据）。\"),e(\"code\",[t._v(\"StatefulElement\")]),t._v(\"中可能会多次调用\"),e(\"code\",[t._v(\"createState()\")]),t._v(\"来创建状态(State)对象。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"code\",[t._v(\"createState()\")]),t._v(\" 用于创建和Stateful widget相关的状态，它在Stateful widget的生命周期中可能会被多次调用。例如，当一个Stateful widget同时插入到widget树的多个位置时，Flutter framework就会调用该方法为每一个位置生成一个独立的State实例，其实，本质上就是一个\"),e(\"code\",[t._v(\"StatefulElement\")]),t._v(\"对应一个State实例。\")]),t._v(\" \"),e(\"blockquote\",[e(\"p\",[t._v(\"在本书中经常会出现“树”的概念，在不同的场景可能指不同的意思，在说“widget树”时它可以指widget结构树，但由于widget与Element有对应关系（一可能对多），在有些场景（Flutter的SDK文档中）也代指“UI树”的意思。而在stateful widget中，State对象也和\"),e(\"code\",[t._v(\"StatefulElement\")]),t._v(\"具有对应关系（一对一），所以在Flutter的SDK文档中，可以经常看到“从树中移除State对象”或“插入State对象到树中”这样的描述。其实，无论哪种描述，其意思都是在描述“一棵构成用户界面的节点元素的树”，读者不必纠结于这些概念，还是那句话“得其神，忘其形”，因此，本书中出现的各种“树”，如果没有特别说明，读者都可抽象的认为它是“一棵构成用户界面的节点元素的树”。\")])])])]),t._v(\" \"),e(\"h2\",{attrs:{id:\"_3-1-6-state\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-1-6-state\"}},[t._v(\"#\")]),t._v(\" 3.1.6 State\")]),t._v(\" \"),e(\"p\",[t._v(\"一个StatefulWidget类会对应一个State类，State表示与其对应的StatefulWidget要维护的状态，State中的保存的状态信息可以：\")]),t._v(\" \"),e(\"ol\",[e(\"li\",[t._v(\"在widget 构建时可以被同步读取。\")]),t._v(\" \"),e(\"li\",[t._v(\"在widget生命周期中可以被改变，当State被改变时，可以手动调用其\"),e(\"code\",[t._v(\"setState()\")]),t._v(\"方法通知Flutter framework状态发生改变，Flutter framework在收到消息后，会重新调用其\"),e(\"code\",[t._v(\"build\")]),t._v(\"方法重新构建widget树，从而达到更新UI的目的。\")])]),t._v(\" \"),e(\"p\",[t._v(\"State中有两个常用属性：\")]),t._v(\" \"),e(\"ol\",[e(\"li\",[e(\"p\",[e(\"code\",[t._v(\"widget\")]),t._v(\"，它表示与该State实例关联的widget实例，由Flutter framework动态设置。注意，这种关联并非永久的，因为在应用生命周期中，UI树上的某一个节点的widget实例在重新构建时可能会变化，但State实例只会在第一次插入到树中时被创建，当在重新构建时，如果widget被修改了，Flutter framework会动态设置State.widget为新的widget实例。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"code\",[t._v(\"context\")]),t._v(\"。StatefulWidget对应的BuildContext，作用同StatelessWidget的BuildContext。\")])])]),t._v(\" \"),e(\"h4\",{attrs:{id:\"state生命周期\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#state生命周期\"}},[t._v(\"#\")]),t._v(\" State生命周期\")]),t._v(\" \"),e(\"p\",[t._v(\"理解State的生命周期对flutter开发非常重要，为了加深读者印象，本节我们通过一个实例来演示一下State的生命周期。在接下来的示例中，我们实现一个计数器widget，点击它可以使计数器加1，由于要保存计数器的数值状态，所以我们应继承StatefulWidget，代码如下：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CounterWidget\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CounterWidget\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"initValue\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" int initValue\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _CounterWidgetState \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_CounterWidgetState\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[e(\"code\",[t._v(\"CounterWidget\")]),t._v(\"接收一个\"),e(\"code\",[t._v(\"initValue\")]),t._v(\"整型参数，它表示计数器的初始值。下面我们看一下State的代码：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_CounterWidgetState\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"CounterWidget\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"  \\n  int _counter\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//初始化状态  \")]),t._v(\"\\n    _counter\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"widget\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"initValue\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"initState\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"build\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      body\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          child\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'$_counter'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//点击后计数器自增\")]),t._v(\"\\n          onPressed\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"++\")]),t._v(\"_counter\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didUpdateWidget\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"CounterWidget oldWidget\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didUpdateWidget\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"oldWidget\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"didUpdateWidget\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"deactivate\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"deactivate\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"deactive\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"dispose\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"reassemble\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"reassemble\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"reassemble\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didChangeDependencies\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didChangeDependencies\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"didChangeDependencies\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"接下来，我们创建一个新路由，在新路由中，我们只显示一个\"),e(\"code\",[t._v(\"CounterWidget\")]),t._v(\"：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[t._v(\"Widget \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CounterWidget\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"我们运行应用并打开该路由页面，在新路由页打开后，屏幕中央就会出现一个数字0，然后控制台日志输出：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language- extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[e(\"code\",[t._v(\"I/flutter ( 5436): initState\\nI/flutter ( 5436): didChangeDependencies\\nI/flutter ( 5436): build\\n\")])])]),e(\"p\",[t._v(\"可以看到，在StatefulWidget插入到Widget树时首先\"),e(\"code\",[t._v(\"initState\")]),t._v(\"方法会被调用。\")]),t._v(\" \"),e(\"p\",[t._v(\"然后我们点击⚡️按钮热重载，控制台输出日志如下：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language- extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[e(\"code\",[t._v(\"I/flutter ( 5436): reassemble\\nI/flutter ( 5436): didUpdateWidget\\nI/flutter ( 5436): build\\n\")])])]),e(\"p\",[t._v(\"可以看到此时\"),e(\"code\",[t._v(\"initState\")]),t._v(\" 和\"),e(\"code\",[t._v(\"didChangeDependencies\")]),t._v(\"都没有被调用，而此时\"),e(\"code\",[t._v(\"didUpdateWidget\")]),t._v(\"被调用。\")]),t._v(\" \"),e(\"p\",[t._v(\"接下来，我们在widget树中移除\"),e(\"code\",[t._v(\"CounterWidget\")]),t._v(\"，将路由\"),e(\"code\",[t._v(\"build\")]),t._v(\"方法改为：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[t._v(\"Widget \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//移除计数器 \")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//return CounterWidget();\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//随便返回一个Text()\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"xxx\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"然后热重载，日志如下：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language- extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[e(\"code\",[t._v(\"I/flutter ( 5436): reassemble\\nI/flutter ( 5436): deactive\\nI/flutter ( 5436): dispose\\n\")])])]),e(\"p\",[t._v(\"我们可以看到，在\"),e(\"code\",[t._v(\"CounterWidget\")]),t._v(\"从widget树中移除时，\"),e(\"code\",[t._v(\"deactive\")]),t._v(\"和\"),e(\"code\",[t._v(\"dispose\")]),t._v(\"会依次被调用。\")]),t._v(\" \"),e(\"p\",[t._v(\"下面我们来看看各个回调函数：\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"p\",[e(\"code\",[t._v(\"initState\")]),t._v(\"：当Widget第一次插入到Widget树时会被调用，对于每一个State对象，Flutter framework只会调用一次该回调，所以，通常在该回调中做一些一次性的操作，如状态初始化、订阅子树的事件通知等。不能在该回调中调用\"),e(\"code\",[t._v(\"BuildContext.dependOnInheritedWidgetOfExactType\")]),t._v(\"（该方法用于在Widget树上获取离当前widget最近的一个父级\"),e(\"code\",[t._v(\"InheritFromWidget\")]),t._v(\"，关于\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"我们将在后面章节介绍），原因是在初始化完成后，Widget树中的\"),e(\"code\",[t._v(\"InheritFromWidget\")]),t._v(\"也可能会发生变化，所以正确的做法应该在在\"),e(\"code\",[t._v(\"build（）\")]),t._v(\"方法或\"),e(\"code\",[t._v(\"didChangeDependencies()\")]),t._v(\"中调用它。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"code\",[t._v(\"didChangeDependencies()\")]),t._v(\"：当State对象的依赖发生变化时会被调用；例如：在之前\"),e(\"code\",[t._v(\"build()\")]),t._v(\" 中包含了一个\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"，然后在之后的\"),e(\"code\",[t._v(\"build()\")]),t._v(\" 中\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"发生了变化，那么此时\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"的子widget的\"),e(\"code\",[t._v(\"didChangeDependencies()\")]),t._v(\"回调都会被调用。典型的场景是当系统语言Locale或应用主题改变时，Flutter framework会通知widget调用此回调。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"code\",[t._v(\"build()\")]),t._v(\"：此回调读者现在应该已经相当熟悉了，它主要是用于构建Widget子树的，会在如下场景被调用：\")]),t._v(\" \"),e(\"ol\",[e(\"li\",[t._v(\"在调用\"),e(\"code\",[t._v(\"initState()\")]),t._v(\"之后。\")]),t._v(\" \"),e(\"li\",[t._v(\"在调用\"),e(\"code\",[t._v(\"didUpdateWidget()\")]),t._v(\"之后。\")]),t._v(\" \"),e(\"li\",[t._v(\"在调用\"),e(\"code\",[t._v(\"setState()\")]),t._v(\"之后。\")]),t._v(\" \"),e(\"li\",[t._v(\"在调用\"),e(\"code\",[t._v(\"didChangeDependencies()\")]),t._v(\"之后。\")]),t._v(\" \"),e(\"li\",[t._v(\"在State对象从树中一个位置移除后（会调用deactivate）又重新插入到树的其它位置之后。\")])])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"code\",[t._v(\"reassemble()\")]),t._v(\"：此回调是专门为了开发调试而提供的，在热重载(hot reload)时会被调用，此回调在Release模式下永远不会被调用。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"code\",[t._v(\"didUpdateWidget()\")]),t._v(\"：在widget重新构建时，Flutter framework会调用\"),e(\"code\",[t._v(\"Widget.canUpdate\")]),t._v(\"来检测Widget树中同一位置的新旧节点，然后决定是否需要更新，如果\"),e(\"code\",[t._v(\"Widget.canUpdate\")]),t._v(\"返回\"),e(\"code\",[t._v(\"true\")]),t._v(\"则会调用此回调。正如之前所述，\"),e(\"code\",[t._v(\"Widget.canUpdate\")]),t._v(\"会在新旧widget的key和runtimeType同时相等时会返回true，也就是说在在新旧widget的key和runtimeType同时相等时\"),e(\"code\",[t._v(\"didUpdateWidget()\")]),t._v(\"就会被调用。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"code\",[t._v(\"deactivate()\")]),t._v(\"：当State对象从树中被移除时，会调用此回调。在一些场景下，Flutter framework会将State对象重新插到树中，如包含此State对象的子树在树的一个位置移动到另一个位置时（可以通过GlobalKey来实现）。如果移除后没有重新插入到树中则紧接着会调用\"),e(\"code\",[t._v(\"dispose()\")]),t._v(\"方法。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"code\",[t._v(\"dispose()\")]),t._v(\"：当State对象从树中被永久移除时调用；通常在此回调中释放资源。\")])])]),t._v(\" \"),e(\"p\",[t._v(\"StatefulWidget生命周期如图3-2所示：\")]),t._v(\" \"),e(\"p\",[e(\"img\",{attrs:{src:s(602),alt:\"图3-2\"}})]),t._v(\" \"),e(\"blockquote\",[e(\"p\",[e(\"strong\",[t._v(\"注意\")]),t._v(\"：在继承\"),e(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"重写其方法时，对于包含\"),e(\"code\",[t._v(\"@mustCallSuper\")]),t._v(\"标注的父类方法，都要在子类方法中先调用父类方法。\")])]),t._v(\" \"),e(\"h3\",{attrs:{id:\"为什么要将build方法放在state中-而不是放在statefulwidget中\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#为什么要将build方法放在state中-而不是放在statefulwidget中\"}},[t._v(\"#\")]),t._v(\" 为什么要将build方法放在State中，而不是放在StatefulWidget中？\")]),t._v(\" \"),e(\"p\",[t._v(\"现在，我们回答之前提出的问题，为什么\"),e(\"code\",[t._v(\"build()\")]),t._v(\"方法放在State（而不是\"),e(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"）中 ？这主要是为了提高开发的灵活性。如果将\"),e(\"code\",[t._v(\"build()\")]),t._v(\"方法在\"),e(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"中则会有两个问题：\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"p\",[t._v(\"状态访问不便。\")]),t._v(\" \"),e(\"p\",[t._v(\"试想一下，如果我们的\"),e(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"有很多状态，而每次状态改变都要调用\"),e(\"code\",[t._v(\"build\")]),t._v(\"方法，由于状态是保存在State中的，如果\"),e(\"code\",[t._v(\"build\")]),t._v(\"方法在\"),e(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"中，那么\"),e(\"code\",[t._v(\"build\")]),t._v(\"方法和状态分别在两个类中，那么构建时读取状态将会很不方便！试想一下，如果真的将\"),e(\"code\",[t._v(\"build\")]),t._v(\"方法放在StatefulWidget中的话，由于构建用户界面过程需要依赖State，所以\"),e(\"code\",[t._v(\"build\")]),t._v(\"方法将必须加一个\"),e(\"code\",[t._v(\"State\")]),t._v(\"参数，大概是下面这样：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[t._v(\"  Widget \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" State state\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//state.counter\")]),t._v(\"\\n      \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"这样的话就只能将State的所有状态声明为公开的状态，这样才能在State类外部访问状态！但是，将状态设置为公开后，状态将不再具有私密性，这就会导致对状态的修改将会变的不可控。但如果将\"),e(\"code\",[t._v(\"build()\")]),t._v(\"方法放在State中的话，构建过程不仅可以直接访问状态，而且也无需公开私有状态，这会非常方便。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[t._v(\"继承\"),e(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"不便。\")]),t._v(\" \"),e(\"p\",[t._v(\"例如，Flutter中有一个动画widget的基类\"),e(\"code\",[t._v(\"AnimatedWidget\")]),t._v(\"，它继承自\"),e(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"类。\"),e(\"code\",[t._v(\"AnimatedWidget\")]),t._v(\"中引入了一个抽象方法\"),e(\"code\",[t._v(\"build(BuildContext context)\")]),t._v(\"，继承自\"),e(\"code\",[t._v(\"AnimatedWidget\")]),t._v(\"的动画widget都要实现这个\"),e(\"code\",[t._v(\"build\")]),t._v(\"方法。现在设想一下，如果\"),e(\"code\",[t._v(\"StatefulWidget\")]),t._v(\" 类中已经有了一个\"),e(\"code\",[t._v(\"build\")]),t._v(\"方法，正如上面所述，此时\"),e(\"code\",[t._v(\"build\")]),t._v(\"方法需要接收一个state对象，这就意味着\"),e(\"code\",[t._v(\"AnimatedWidget\")]),t._v(\"必须将自己的State对象(记为_animatedWidgetState)提供给其子类，因为子类需要在其\"),e(\"code\",[t._v(\"build\")]),t._v(\"方法中调用父类的\"),e(\"code\",[t._v(\"build\")]),t._v(\"方法，代码可能如下：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyAnimationWidget\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimatedWidget\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n    Widget \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" State state\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//由于子类要用到AnimatedWidget的状态对象_animatedWidgetState，\")]),t._v(\"\\n      \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//所以AnimatedWidget必须通过某种方式将其状态对象_animatedWidgetState\")]),t._v(\"\\n      \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//暴露给其子类   \")]),t._v(\"\\n      \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" _animatedWidgetState\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"这样很显然是不合理的，因为\")]),t._v(\" \"),e(\"ol\",[e(\"li\",[e(\"code\",[t._v(\"AnimatedWidget\")]),t._v(\"的状态对象是\"),e(\"code\",[t._v(\"AnimatedWidget\")]),t._v(\"内部实现细节，不应该暴露给外部。\")]),t._v(\" \"),e(\"li\",[t._v(\"如果要将父类状态暴露给子类，那么必须得有一种传递机制，而做这一套传递机制是无意义的，因为父子类之间状态的传递和子类本身逻辑是无关的。\")])])])]),t._v(\" \"),e(\"p\",[t._v(\"综上所述，可以发现，对于\"),e(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"，将\"),e(\"code\",[t._v(\"build\")]),t._v(\"方法放在State中，可以给开发带来很大的灵活性。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"_3-1-7-在widget树中获取state对象\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-1-7-在widget树中获取state对象\"}},[t._v(\"#\")]),t._v(\" 3.1.7 在Widget树中获取State对象\")]),t._v(\" \"),e(\"p\",[t._v(\"由于StatefulWidget的的具体逻辑都在其State中，所以很多时候，我们需要获取StatefulWidget对应的State对象来调用一些方法，比如\"),e(\"code\",[t._v(\"Scaffold\")]),t._v(\"组件对应的状态类\"),e(\"code\",[t._v(\"ScaffoldState\")]),t._v(\"中就定义了打开SnackBar(路由页底部提示条)的方法。我们有两种方法在子widget树中获取父级StatefulWidget的State对象。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"通过context获取\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#通过context获取\"}},[t._v(\"#\")]),t._v(\" 通过Context获取\")]),t._v(\" \"),e(\"p\",[e(\"code\",[t._v(\"context\")]),t._v(\"对象有一个\"),e(\"code\",[t._v(\"findAncestorStateOfType()\")]),t._v(\"方法，该方法可以从当前节点沿着widget树向上查找指定类型的StatefulWidget对应的State对象。下面是实现打开SnackBar的示例：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  appBar\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    title\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"子树中获取State对象\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  body\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    child\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        onPressed\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 查找父级最近的Scaffold对应的ScaffoldState对象\")]),t._v(\"\\n          ScaffoldState _state \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"findAncestorStateOfType\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ScaffoldState\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//调用ScaffoldState的showSnackBar来弹出SnackBar\")]),t._v(\"\\n          _state\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showSnackBar\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SnackBar\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              content\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"我是SnackBar\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"显示SnackBar\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"上面示例运行后，点击”显示SnackBar“，效果如图3-1-2所示：\")]),t._v(\" \"),e(\"p\",[e(\"img\",{attrs:{src:s(603),alt:\"图3-1-2\"}})]),t._v(\" \"),e(\"p\",[t._v(\"一般来说，如果StatefulWidget的状态是私有的（不应该向外部暴露），那么我们代码中就不应该去直接获取其State对象；如果StatefulWidget的状态是希望暴露出的（通常还有一些组件的操作方法），我们则可以去直接获取其State对象。但是通过\"),e(\"code\",[t._v(\"context.findAncestorStateOfType\")]),t._v(\"获取StatefulWidget的状态的方法是通用的，我们并不能在语法层面指定StatefulWidget的状态是否私有，所以在Flutter开发中便有了一个默认的约定：如果StatefulWidget的状态是希望暴露出的，应当在StatefulWidget中提供一个\"),e(\"code\",[t._v(\"of\")]),t._v(\"静态方法来获取其State对象，开发者便可直接通过该方法来获取；如果State不希望暴露，则不提供\"),e(\"code\",[t._v(\"of\")]),t._v(\"方法。这个约定在Flutter SDK里随处可见。所以，上面示例中的\"),e(\"code\",[t._v(\"Scaffold\")]),t._v(\"也提供了一个\"),e(\"code\",[t._v(\"of\")]),t._v(\"方法，我们其实是可以直接调用它的：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 直接通过of静态方法来获取ScaffoldState \")]),t._v(\"\\nScaffoldState _state\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"Scaffold\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \\n_state\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showSnackBar\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SnackBar\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    content\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"我是SnackBar\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),e(\"h3\",{attrs:{id:\"通过globalkey\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#通过globalkey\"}},[t._v(\"#\")]),t._v(\" 通过GlobalKey\")]),t._v(\" \"),e(\"p\",[t._v(\"Flutter还有一种通用的获取\"),e(\"code\",[t._v(\"State\")]),t._v(\"对象的方法——通过GlobalKey来获取！ 步骤分两步：\")]),t._v(\" \"),e(\"ol\",[e(\"li\",[e(\"p\",[t._v(\"给目标\"),e(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"添加\"),e(\"code\",[t._v(\"GlobalKey\")]),t._v(\"。\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//定义一个globalKey, 由于GlobalKey要保持全局唯一性，我们使用静态变量存储\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" GlobalKey\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ScaffoldState\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _globalKey\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GlobalKey\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    key\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _globalKey \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//设置key\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"  \\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])])]),t._v(\" \"),e(\"li\",[e(\"p\",[t._v(\"通过\"),e(\"code\",[t._v(\"GlobalKey\")]),t._v(\"来获取\"),e(\"code\",[t._v(\"State\")]),t._v(\"对象\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[t._v(\"_globalKey\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"currentState\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"openDrawer\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])])])]),t._v(\" \"),e(\"p\",[t._v(\"GlobalKey是Flutter提供的一种在整个APP中引用element的机制。如果一个widget设置了\"),e(\"code\",[t._v(\"GlobalKey\")]),t._v(\"，那么我们便可以通过\"),e(\"code\",[t._v(\"globalKey.currentWidget\")]),t._v(\"获得该widget对象、\"),e(\"code\",[t._v(\"globalKey.currentElement\")]),t._v(\"来获得widget对应的element对象，如果当前widget是\"),e(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"，则可以通过\"),e(\"code\",[t._v(\"globalKey.currentState\")]),t._v(\"来获得该widget对应的state对象。\")]),t._v(\" \"),e(\"blockquote\",[e(\"p\",[t._v(\"注意：使用GlobalKey开销较大，如果有其他可选方案，应尽量避免使用它。另外同一个GlobalKey在整个widget树中必须是唯一的，不能重复。\")])]),t._v(\" \"),e(\"h2\",{attrs:{id:\"_3-1-8-flutter-sdk内置组件库介绍\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-1-8-flutter-sdk内置组件库介绍\"}},[t._v(\"#\")]),t._v(\" 3.1.8 Flutter SDK内置组件库介绍\")]),t._v(\" \"),e(\"p\",[t._v(\"Flutter提供了一套丰富、强大的基础组件，在基础组件库之上Flutter又提供了一套Material风格（Android默认的视觉风格）和一套Cupertino风格（iOS视觉风格）的组件库。要使用基础组件库，需要先导入：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/widgets.dart'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"下面我们介绍一下常用的组件。\")]),t._v(\" \"),e(\"h4\",{attrs:{id:\"基础组件\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#基础组件\"}},[t._v(\"#\")]),t._v(\" 基础组件\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/Text-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"Text\")]),e(\"OutboundLink\")],1),t._v(\"：该组件可让您创建一个带格式的文本。\")]),t._v(\" \"),e(\"li\",[e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/Row-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"Row\")]),e(\"OutboundLink\")],1),t._v(\"、 \"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/Column-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"Column\")]),e(\"OutboundLink\")],1),t._v(\"： 这些具有弹性空间的布局类Widget可让您在水平（Row）和垂直（Column）方向上创建灵活的布局。其设计是基于Web开发中的Flexbox布局模型。\")]),t._v(\" \"),e(\"li\",[e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/Stack-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"Stack\")]),e(\"OutboundLink\")],1),t._v(\"： 取代线性布局 (译者语：和Android中的\"),e(\"code\",[t._v(\"FrameLayout\")]),t._v(\"相似)，\"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/Stack-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"Stack\")]),e(\"OutboundLink\")],1),t._v(\"允许子 widget 堆叠， 你可以使用 \"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/Positioned-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"Positioned\")]),e(\"OutboundLink\")],1),t._v(\" 来定位他们相对于\"),e(\"code\",[t._v(\"Stack\")]),t._v(\"的上下左右四条边的位置。Stacks是基于Web开发中的绝对定位（absolute positioning )布局模型设计的。\")]),t._v(\" \"),e(\"li\",[e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/Container-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"Container\")]),e(\"OutboundLink\")],1),t._v(\"： \"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/Container-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"Container\")]),e(\"OutboundLink\")],1),t._v(\" 可让您创建矩形视觉元素。container 可以装饰一个\"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/painting/BoxDecoration-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"BoxDecoration\")]),e(\"OutboundLink\")],1),t._v(\", 如 background、一个边框、或者一个阴影。 \"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/Container-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"Container\")]),e(\"OutboundLink\")],1),t._v(\" 也可以具有边距（margins）、填充(padding)和应用于其大小的约束(constraints)。另外， \"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/Container-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"Container\")]),e(\"OutboundLink\")],1),t._v(\"可以使用矩阵在三维空间中对其进行变换。\")])]),t._v(\" \"),e(\"h4\",{attrs:{id:\"material组件\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#material组件\"}},[t._v(\"#\")]),t._v(\" Material组件\")]),t._v(\" \"),e(\"p\",[t._v(\"Flutter提供了一套丰富的Material组件，它可以帮助我们构建遵循Material Design设计规范的应用程序。Material应用程序以\"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/material/MaterialApp-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"MaterialApp\")]),e(\"OutboundLink\")],1),t._v(\" 组件开始， 该组件在应用程序的根部创建了一些必要的组件，比如\"),e(\"code\",[t._v(\"Theme\")]),t._v(\"组件，它用于配置应用的主题。 是否使用\"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/material/MaterialApp-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"MaterialApp\")]),e(\"OutboundLink\")],1),t._v(\"完全是可选的，但是使用它是一个很好的做法。在之前的示例中，我们已经使用过多个Material 组件了，如：\"),e(\"code\",[t._v(\"Scaffold\")]),t._v(\"、\"),e(\"code\",[t._v(\"AppBar\")]),t._v(\"、\"),e(\"code\",[t._v(\"FlatButton\")]),t._v(\"等。要使用Material 组件，需要先引入它：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),e(\"h4\",{attrs:{id:\"cupertino组件\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#cupertino组件\"}},[t._v(\"#\")]),t._v(\" Cupertino组件\")]),t._v(\" \"),e(\"p\",[t._v(\"Flutter也提供了一套丰富的Cupertino风格的组件，尽管目前还没有Material 组件那么丰富，但是它仍在不断的完善中。值得一提的是在Material 组件库中有一些组件可以根据实际运行平台来切换表现风格，比如\"),e(\"code\",[t._v(\"MaterialPageRoute\")]),t._v(\"，在路由切换时，如果是Android系统，它将会使用Android系统默认的页面切换动画(从底向上)；如果是iOS系统，它会使用iOS系统默认的页面切换动画（从右向左）。由于在前面的示例中还没有Cupertino组件的示例，下面我们实现一个简单的Cupertino组件风格的页面：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//导入cupertino widget库\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/cupertino.dart'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CupertinoTestRoute\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CupertinoPageScaffold\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      navigationBar\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CupertinoNavigationBar\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        middle\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Cupertino Demo\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CupertinoButton\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            color\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" CupertinoColors\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"activeBlue\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Press\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onPressed\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n        \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"下面（图3-3）是在iPhoneX上页面效果截图：\")]),t._v(\" \"),e(\"p\",[e(\"img\",{attrs:{src:s(604),alt:\"图3-3\"}})]),t._v(\" \"),e(\"h3\",{attrs:{id:\"关于示例\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#关于示例\"}},[t._v(\"#\")]),t._v(\" 关于示例\")]),t._v(\" \"),e(\"p\",[t._v(\"本章后面章节的示例中会使用一些布局类组件，如\"),e(\"code\",[t._v(\"Scaffold\")]),t._v(\"、\"),e(\"code\",[t._v(\"Row\")]),t._v(\"、\"),e(\"code\",[t._v(\"Column\")]),t._v(\"等，这些组件将在后面“布局类组件”一章中详细介绍，读者可以先不用关注。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"总结\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),e(\"p\",[t._v(\"Flutter提供了丰富的组件，在实际的开发中你可以根据需要随意使用它们，而不必担心引入过多组件库会让你的应用安装包变大，这不是web开发，dart在编译时只会编译你使用了的代码。由于Material和Cupertino都是在基础组件库之上的，所以如果我们的应用中引入了这两者之一，则不需要再引入\"),e(\"code\",[t._v(\"flutter/widgets.dart\")]),t._v(\"了，因为它们内部已经引入过了。\")])])}),[],!1,null,null,null);a.default=n.exports}}]);"
  },
  {
    "path": "docs/assets/js/24.bf1834df.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[24],{613:function(t,a,s){t.exports=s.p+\"assets/img/3-5.e95eb747.png\"},614:function(t,a){t.exports=\"data:image/png;base64,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\"},615:function(t,a){t.exports=\"data:image/png;base64,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\"},616:function(t,a,s){t.exports=s.p+\"assets/img/3-8.cc415da2.png\"},617:function(t,a,s){t.exports=s.p+\"assets/img/3-9.bb214e70.png\"},892:function(t,a,s){\"use strict\";s.r(a);var n=s(45),e=Object(n.a)({},(function(){var t=this,a=t.$createElement,n=t._self._c||a;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h2\",{attrs:{id:\"_3-3-文本及样式\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-3-文本及样式\"}},[t._v(\"#\")]),t._v(\" 3.3 文本及样式\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"_3-3-1-text\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-3-1-text\"}},[t._v(\"#\")]),t._v(\" 3.3.1 Text\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"Text\")]),t._v(\"用于显示简单样式文本，它包含一些控制文本显示样式的一些属性，一个简单的例子如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hello world\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  textAlign\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" TextAlign\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"left\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hello world! I\\'m Jack. \"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"4\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  maxLines\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  overflow\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" TextOverflow\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"ellipsis\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hello world\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  textScaleFactor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.5\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图3-5所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(613),alt:\"image-20180829103242552\"}})]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"textAlign\")]),t._v(\"：文本的对齐方式；可以选择左对齐、右对齐还是居中。注意，对齐的参考系是Text widget本身。本例中虽然是指定了居中对齐，但因为Text文本内容宽度不足一行，Text的宽度和文本内容长度相等，那么这时指定对齐方式是没有意义的，只有Text宽度大于文本内容长度时指定此属性才有意义。下面我们指定一个较长的字符串：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hello world \"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//字符串重复六次\")]),t._v(\"\\n  textAlign\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" TextAlign\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"；\\n\")])])]),n(\"p\",[t._v(\"运行效果如图3-6所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(614),alt:\"image-20180829104807535\"}})])])]),t._v(\" \"),n(\"p\",[t._v(\"​      字符串内容超过一行，Text宽度等于屏幕宽度，第二行文本便会居中显示。\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"maxLines\")]),t._v(\"、\"),n(\"code\",[t._v(\"overflow\")]),t._v(\"：指定文本显示的最大行数，默认情况下，文本是自动折行的，如果指定此参数，则文本最多不会超过指定的行。如果有多余的文本，可以通过\"),n(\"code\",[t._v(\"overflow\")]),t._v(\"来指定截断方式，默认是直接截断，本例中指定的截断方式\"),n(\"code\",[t._v(\"TextOverflow.ellipsis\")]),t._v(\"，它会将多余文本截断后以省略符“...”表示；TextOverflow的其它截断方式请参考SDK文档。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"textScaleFactor\")]),t._v(\"：代表文本相对于当前字体大小的缩放因子，相对于去设置文本的样式\"),n(\"code\",[t._v(\"style\")]),t._v(\"属性的\"),n(\"code\",[t._v(\"fontSize\")]),t._v(\"，它是调整字体大小的一个快捷方式。该属性的默认值可以通过\"),n(\"code\",[t._v(\"MediaQueryData.textScaleFactor\")]),t._v(\"获得，如果没有\"),n(\"code\",[t._v(\"MediaQuery\")]),t._v(\"，那么会默认值将为1.0。\")])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"_3-3-2-textstyle\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-3-2-textstyle\"}},[t._v(\"#\")]),t._v(\" 3.3.2 TextStyle\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"TextStyle\")]),t._v(\"用于指定文本显示的样式如颜色、字体、粗细、背景等。我们看一个示例：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hello world\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    fontSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"18.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"  \\n    fontFamily\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Courier\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    background\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Paint\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"yellow\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    decoration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"TextDecoration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"underline\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    decorationStyle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" TextDecorationStyle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dashed\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"效果如图3-7所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(615),alt:\"3-7\"}})]),t._v(\" \"),n(\"p\",[t._v(\"此示例只展示了TextStyle的部分属性，它还有一些其它属性，属性名基本都是自解释的，在此不再赘述，读者可以查阅SDK文档。值得注意的是：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"height\")]),t._v(\"：该属性用于指定行高，但它并不是一个绝对值，而是一个因子，具体的行高等于\"),n(\"code\",[t._v(\"fontSize\")]),t._v(\"*\"),n(\"code\",[t._v(\"height\")]),t._v(\"。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"fontFamily\")]),t._v(\" ：由于不同平台默认支持的字体集不同，所以在手动指定字体时一定要先在不同平台测试一下。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"fontSize\")]),t._v(\"：该属性和Text的\"),n(\"code\",[t._v(\"textScaleFactor\")]),t._v(\"都用于控制字体大小。但是有两个主要区别：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"fontSize\")]),t._v(\"可以精确指定字体大小，而\"),n(\"code\",[t._v(\"textScaleFactor\")]),t._v(\"只能通过缩放比例来控制。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"textScaleFactor\")]),t._v(\"主要是用于系统字体大小设置改变时对Flutter应用字体进行全局调整，而\"),n(\"code\",[t._v(\"fontSize\")]),t._v(\"通常用于单个文本，字体大小不会跟随系统字体大小变化。\")])])])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"_3-3-3-textspan\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-3-3-textspan\"}},[t._v(\"#\")]),t._v(\" 3.3.3 TextSpan\")]),t._v(\" \"),n(\"p\",[t._v(\"在上面的例子中，Text的所有文本内容只能按同一种样式，如果我们需要对一个Text内容的不同部分按照不同的样式显示，这时就可以使用\"),n(\"code\",[t._v(\"TextSpan\")]),t._v(\"，它代表文本的一个“片段”。我们看看TextSpan的定义:\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextSpan\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  TextStyle style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n  Sting text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  List\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"TextSpan\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  GestureRecognizer recognizer\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"其中\"),n(\"code\",[t._v(\"style\")]),t._v(\" 和 \"),n(\"code\",[t._v(\"text\")]),t._v(\"属性代表该文本片段的样式和内容。  \"),n(\"code\",[t._v(\"children\")]),t._v(\"是一个\"),n(\"code\",[t._v(\"TextSpan\")]),t._v(\"的数组，也就是说\"),n(\"code\",[t._v(\"TextSpan\")]),t._v(\"可以包括其他\"),n(\"code\",[t._v(\"TextSpan\")]),t._v(\"。而\"),n(\"code\",[t._v(\"recognizer\")]),t._v(\"用于对该文本片段上用于手势进行识别处理。下面我们看一个效果（图3-8），然后用\"),n(\"code\",[t._v(\"TextSpan\")]),t._v(\"实现它。\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(616),alt:\"3-8\"}})]),t._v(\" \"),n(\"p\",[t._v(\"源码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"rich\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextSpan\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextSpan\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n       text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Home: \"')]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextSpan\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n       text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"https://flutterchina.club\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n       style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n         color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\\n       \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"  \\n       recognizer\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _tapRecognizer\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"ul\",[n(\"li\",[t._v(\"上面代码中，我们通过TextSpan实现了一个基础文本片段和一个链接片段，然后通过\"),n(\"code\",[t._v(\"Text.rich\")]),t._v(\" 方法将\"),n(\"code\",[t._v(\"TextSpan\")]),t._v(\" 添加到Text中，之所以可以这样做，是因为Text其实就是RichText的一个包装，而RichText是可以显示多种样式(富文本)的widget。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"_tapRecognizer\")]),t._v(\"，它是点击链接后的一个处理器（代码已省略），关于手势识别的更多内容我们将在后面单独介绍。\")])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"_3-3-4-defaulttextstyle\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-3-4-defaulttextstyle\"}},[t._v(\"#\")]),t._v(\" 3.3.4 DefaultTextStyle\")]),t._v(\" \"),n(\"p\",[t._v(\"在Widget树中，文本的样式默认是可以被继承的（子类文本类组件未指定具体样式时可以使用Widget树中父级设置的默认样式），因此，如果在Widget树的某一个节点处设置一个默认的文本样式，那么该节点的子树中所有文本都会默认使用这个样式，而\"),n(\"code\",[t._v(\"DefaultTextStyle\")]),t._v(\"正是用于设置默认文本样式的。下面我们看一个例子：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DefaultTextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//1.设置文本默认样式  \")]),t._v(\"\\n  style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    fontSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  textAlign\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" TextAlign\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    crossAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" CrossAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"hello world\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"I am Jack\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"I am Jack\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          inherit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//2.不继承默认样式\")]),t._v(\"\\n          color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上面代码中，我们首先设置了一个默认的文本样式，即字体为20像素(逻辑像素)、颜色为红色。然后通过\"),n(\"code\",[t._v(\"DefaultTextStyle\")]),t._v(\" 设置给了子树Column节点处，这样一来Column的所有子孙Text默认都会继承该样式，除非Text显示指定不继承样式，如代码中注释2。示例运行效果如图3-9：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(617),alt:\"3-9\"}})]),t._v(\" \"),n(\"h3\",{attrs:{id:\"_3-3-5-字体\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-3-5-字体\"}},[t._v(\"#\")]),t._v(\" 3.3.5 字体\")]),t._v(\" \"),n(\"p\",[t._v(\"可以在Flutter应用程序中使用不同的字体。例如，我们可能会使用设计人员创建的自定义字体，或者其它第三方的字体，如\"),n(\"a\",{attrs:{href:\"https://fonts.google.com/\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Google Fonts\"),n(\"OutboundLink\")],1),t._v(\"中的字体。本节将介绍如何为Flutter应用配置字体，并在渲染文本时使用它们。\")]),t._v(\" \"),n(\"p\",[t._v(\"在Flutter中使用字体分两步完成。首先在\"),n(\"code\",[t._v(\"pubspec.yaml\")]),t._v(\"中声明它们，以确保它们会打包到应用程序中。然后通过\"),n(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/painting/TextStyle-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[n(\"code\",[t._v(\"TextStyle\")]),n(\"OutboundLink\")],1),t._v(\"属性使用字体。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"在asset中声明\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#在asset中声明\"}},[t._v(\"#\")]),t._v(\" 在asset中声明\")]),t._v(\" \"),n(\"p\",[t._v(\"要将字体文件打包到应用中，和使用其它资源一样，要先在\"),n(\"code\",[t._v(\"pubspec.yaml\")]),t._v(\"中声明它。然后将字体文件复制到在\"),n(\"code\",[t._v(\"pubspec.yaml\")]),t._v(\"中指定的位置。如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-yaml extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"flutter\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"fonts\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"family\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Raleway\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"fonts\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"asset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" assets/fonts/Raleway\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\"Regular.ttf\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"asset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" assets/fonts/Raleway\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\"Medium.ttf\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"weight\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"500\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"asset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" assets/fonts/Raleway\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\"SemiBold.ttf\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"weight\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"600\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"family\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" AbrilFatface\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"fonts\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"asset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" assets/fonts/abrilfatface/AbrilFatface\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\"Regular.ttf\\n\")])])]),n(\"h4\",{attrs:{id:\"使用字体\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#使用字体\"}},[t._v(\"#\")]),t._v(\" 使用字体\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 声明文本样式\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" textStyle \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  fontFamily\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Raleway'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 使用文本样式\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" buttonText \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Use the font for this text\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" textStyle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"h4\",{attrs:{id:\"package中的字体\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#package中的字体\"}},[t._v(\"#\")]),t._v(\" Package中的字体\")]),t._v(\" \"),n(\"p\",[t._v(\"要使用Package中定义的字体，\"),n(\"strong\",[t._v(\"必须提供\"),n(\"code\",[t._v(\"package\")]),t._v(\"参数\")]),t._v(\"。例如，假设上面的字体声明位于\"),n(\"code\",[t._v(\"my_package\")]),t._v(\"包中。然后创建TextStyle的过程如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" textStyle \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  fontFamily\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Raleway'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  package\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'my_package'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//指定包名\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"如果在package包内部使用它自己定义的字体，也应该在创建文本样式时指定\"),n(\"code\",[t._v(\"package\")]),t._v(\"参数，如上例所示。\")]),t._v(\" \"),n(\"p\",[t._v(\"一个包也可以只提供字体文件而不需要在pubspec.yaml中声明。 这些文件应该存放在包的\"),n(\"code\",[t._v(\"lib/\")]),t._v(\"文件夹中。字体文件不会自动绑定到应用程序中，应用程序可以在声明字体时有选择地使用这些字体。假设一个名为my_package的包中有一个字体文件：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language- extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[n(\"code\",[t._v(\"lib/fonts/Raleway-Medium.ttf\\n\")])])]),n(\"p\",[t._v(\"然后，应用程序可以声明一个字体，如下面的示例所示：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-yaml extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[n(\"code\",[t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"flutter\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"fonts\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"family\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Raleway\\n       \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"fonts\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n         \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"asset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" assets/fonts/Raleway\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\"Regular.ttf\\n         \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"asset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" packages/my_package/fonts/Raleway\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\"Medium.ttf\\n           \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"weight\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"500\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"lib/\")]),t._v(\"是隐含的，所以它不应该包含在asset路径中。\")]),t._v(\" \"),n(\"p\",[t._v(\"在这种情况下，由于应用程序本地定义了字体，所以在创建TextStyle时可以不指定\"),n(\"code\",[t._v(\"package\")]),t._v(\"参数：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" textStyle \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  fontFamily\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Raleway'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/25.869317d7.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[25],{388:function(t,s,n){t.exports=n.p+\"assets/img/15-1.c59fd39e.png\"},389:function(t,s,n){t.exports=n.p+\"assets/img/15-2.cb77ca5a.png\"},390:function(t,s,n){t.exports=n.p+\"assets/img/15-3.28296c90.png\"},391:function(t,s,n){t.exports=n.p+\"assets/img/15-4.19779c8f.png\"},761:function(t,s,n){\"use strict\";n.r(s);var a=n(45),p=Object(a.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_15-6-app入口及主页\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_15-6-app入口及主页\"}},[t._v(\"#\")]),t._v(\" 15.6 APP入口及主页\")]),t._v(\" \"),a(\"p\",[t._v(\"本节来介绍一下APP入口及首页。\")]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_15-6-1-app入口\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_15-6-1-app入口\"}},[t._v(\"#\")]),t._v(\" 15.6.1 APP入口\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"main\")]),t._v(\"函数为APP入口函数，实现如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"main\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Global\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"init\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"runApp\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyApp\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"初始化完成后才会加载UI(\"),a(\"code\",[t._v(\"MyApp\")]),t._v(\")，\"),a(\"code\",[t._v(\"MyApp\")]),t._v(\" 是应用的入口Widget，实现如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyApp\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// This widget is the root of your application.\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MultiProvider\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      providers\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"SingleChildCloneableWidget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        ChangeNotifierProvider\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"value\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ThemeModel\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        ChangeNotifierProvider\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"value\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"UserModel\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        ChangeNotifierProvider\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"value\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"LocaleModel\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Consumer2\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ThemeModel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" LocaleModel\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        builder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" themeModel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" localeModel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Widget child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MaterialApp\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            theme\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ThemeData\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              primarySwatch\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" themeModel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"theme\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onGenerateTitle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" GmLocalizations\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"title\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            home\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"HomeRoute\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//应用主页\")]),t._v(\"\\n            locale\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" localeModel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getLocale\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//我们只支持美国英语和中文简体\")]),t._v(\"\\n            supportedLocales\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Locale\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'en'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'US'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 美国英语\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Locale\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'zh'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'CN'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 中文简体\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//其它Locales\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            localizationsDelegates\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 本地化的代理类\")]),t._v(\"\\n              GlobalMaterialLocalizations\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"delegate\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              GlobalWidgetsLocalizations\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"delegate\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GmLocalizationsDelegate\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            localeResolutionCallback\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Locale _locale\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Iterable\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Locale\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" supportedLocales\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"localeModel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getLocale\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//如果已经选定语言，则不跟随系统\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" localeModel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getLocale\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n         \\n                Locale locale\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//APP语言跟随系统语言，如果系统语言不是中文简体或美国英语，\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//则默认使用美国英语\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"supportedLocales\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"contains\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_locale\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                  locale\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _locale\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                  locale\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Locale\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'en'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'US'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" locale\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 注册命名路由表\")]),t._v(\"\\n            routes\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" WidgetBuilder\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"login\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"LoginRoute\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"themes\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ThemeChangeRoute\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"language\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"LanguageRoute\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"在上面的代码中：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"我们的根widget是\"),a(\"code\",[t._v(\"MultiProvider\")]),t._v(\"，它将主题、用户、语言三种状态绑定到了应用的根上，如此一来，任何路由中都可以通过\"),a(\"code\",[t._v(\"Provider.of()\")]),t._v(\"来获取这些状态，也就是说这三种状态是全局共享的！\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"HomeRoute\")]),t._v(\"是应用的主页。\")]),t._v(\" \"),a(\"li\",[t._v(\"在构建\"),a(\"code\",[t._v(\"MaterialApp\")]),t._v(\"时，我们配置了APP支持的语言列表，以及监听了系统语言改变事件；另外\"),a(\"code\",[t._v(\"MaterialApp\")]),t._v(\"消费（依赖）了\"),a(\"code\",[t._v(\"ThemeModel\")]),t._v(\"和\"),a(\"code\",[t._v(\"LocaleModel\")]),t._v(\"，所以当APP主题或语言改变时\"),a(\"code\",[t._v(\"MaterialApp\")]),t._v(\"会重新构建\")]),t._v(\" \"),a(\"li\",[t._v(\"我们注册了命名路由表，以便在APP中可以直接通过路由名跳转。\")]),t._v(\" \"),a(\"li\",[t._v(\"为了支持多语言（本APP中我们支持美国英语和中文简体两种语言）我们实现了一个\"),a(\"code\",[t._v(\"GmLocalizationsDelegate\")]),t._v(\"，子Widget中都可以通过\"),a(\"code\",[t._v(\"GmLocalizations\")]),t._v(\"来动态获取APP当前语言对应的文案。关于\"),a(\"code\",[t._v(\"GmLocalizationsDelegate\")]),t._v(\"和\"),a(\"code\",[t._v(\"GmLocalizations\")]),t._v(\"的实现方式读者可以参考“国际化”一章中的介绍，此处不再赘述。\")])]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_15-6-2-主页\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_15-6-2-主页\"}},[t._v(\"#\")]),t._v(\" 15.6.2 主页\")]),t._v(\" \"),a(\"p\",[t._v(\"为了简单起见，当APP启动后，如果之前已登录了APP，则显示该用户项目列表；如果之前未登录，则显示一个登录按钮，点击后跳转到登录页。另外，我们实现一个抽屉菜单，里面包含当前用户头像及APP的菜单。下面我们先看看要实现的效果，如图15-1、15-2所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(388),alt:\"15-1\"}}),a(\"img\",{attrs:{src:n(389),alt:\"15-2\"}})]),t._v(\" \"),a(\"p\",[t._v(\"我们在“lib/routes”下创建一个“home_page.dart”文件，实现如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"HomeRoute\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _HomeRouteState \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_HomeRouteState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_HomeRouteState\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"HomeRoute\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      appBar\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        title\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"GmLocalizations\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"home\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      body\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_buildBody\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 构建主页面\")]),t._v(\"\\n      drawer\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyDrawer\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//抽屉菜单\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 省略\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"上面代码中，主页的标题（title）我们是通过\"),a(\"code\",[t._v(\"GmLocalizations.of(context).home\")]),t._v(\"来获得，\"),a(\"code\",[t._v(\"GmLocalizations\")]),t._v(\"是我们提供的一个\"),a(\"code\",[t._v(\"Localizations\")]),t._v(\"类，用于支持多语言，因此当APP语言改变时，凡是使用\"),a(\"code\",[t._v(\"GmLocalizations\")]),t._v(\"动态获取的文案都会是相应语言的文案，这在前面“国际化”一章中已经介绍过，读者可以前翻查阅。\")]),t._v(\" \"),a(\"p\",[t._v(\"我们通过 \"),a(\"code\",[t._v(\"_buildBody()\")]),t._v(\"方法来构建主页内容，\"),a(\"code\",[t._v(\"_buildBody()\")]),t._v(\"方法实现代码如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[t._v(\"  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_buildBody\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    UserModel userModel \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Provider\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"of\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"UserModel\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"userModel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isLogin\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//用户未登录，显示登录按钮\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"GmLocalizations\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"login\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Navigator\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pushNamed\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"login\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//已登录，则展示项目列表\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" InfiniteListView\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Repo\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        onRetrieveData\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int page\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" List\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Repo\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" items\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" bool refresh\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" data \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Git\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getRepos\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            refresh\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" refresh\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            queryParameters\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'page'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" page\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'page_size'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//把请求到的新数据添加到items中\")]),t._v(\"\\n          items\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addAll\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"data\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 如果接口返回的数量等于'page_size'，则认为还有数据，反之则认为最后一页\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" data\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"length\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        itemBuilder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"List list\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int index\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" BuildContext ctx\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 项目信息列表项\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RepoItem\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"list\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"index\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"上面代码注释很清楚：如果用户未登录，显示登录按钮；如果用户已登录，则展示项目列表。这里项目列表使用了\"),a(\"code\",[t._v(\"InfiniteListView\")]),t._v(\" Widget，它是flukit package中提供的。\"),a(\"code\",[t._v(\"InfiniteListView\")]),t._v(\"同时支持了下拉刷新和上拉加载更多两种功能。\"),a(\"code\",[t._v(\"onRetrieveData\")]),t._v(\" 为数据获取回调，该回调函数接收三个参数：\")]),t._v(\" \"),a(\"table\",[a(\"thead\",[a(\"tr\",[a(\"th\",[t._v(\"参数名\")]),t._v(\" \"),a(\"th\",[t._v(\"类型\")]),t._v(\" \"),a(\"th\",[t._v(\"解释\")])])]),t._v(\" \"),a(\"tbody\",[a(\"tr\",[a(\"td\",[t._v(\"page\")]),t._v(\" \"),a(\"td\",[t._v(\"int\")]),t._v(\" \"),a(\"td\",[t._v(\"当前页号\")])]),t._v(\" \"),a(\"tr\",[a(\"td\",[t._v(\"items\")]),t._v(\" \"),a(\"td\",[t._v(\"List\"),a(\"T\")],1),t._v(\" \"),a(\"td\",[t._v(\"保存当前列表数据的List\")])]),t._v(\" \"),a(\"tr\",[a(\"td\",[t._v(\"refresh\")]),t._v(\" \"),a(\"td\",[t._v(\"bool\")]),t._v(\" \"),a(\"td\",[t._v(\"是否是下拉刷新触发\")])])])]),t._v(\" \"),a(\"p\",[t._v(\"返回值类型为\"),a(\"code\",[t._v(\"bool\")]),t._v(\"，为\"),a(\"code\",[t._v(\"true\")]),t._v(\"时表示还有数据，为\"),a(\"code\",[t._v(\"false\")]),t._v(\"时则表示后续没有数据了。\"),a(\"code\",[t._v(\"onRetrieveData\")]),t._v(\" 回调中我们调用\"),a(\"code\",[t._v(\"Git(context).getRepos(...)\")]),t._v(\"来获取用户项目列表，同时指定每次请求获取20条。当获取成功时，首先要将新获取的项目数据添加到\"),a(\"code\",[t._v(\"items\")]),t._v(\"中，然后根据本次请求的项目条数是否等于期望的20条来判断还有没有更多的数据。在此需要注意，\"),a(\"code\",[t._v(\"Git(context).getRepos(…)\")]),t._v(\"方法中需要\"),a(\"code\",[t._v(\"refresh\")]),t._v(\"参数来判断是否使用缓存。\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"itemBuilder\")]),t._v(\"为列表项的builder，我们需要在该回调中构建每一个列表项Widget。由于列表项构建逻辑较复杂，我们单独封装一个\"),a(\"code\",[t._v(\"RepoItem\")]),t._v(\" Widget 专门用于构建列表项UI。\"),a(\"code\",[t._v(\"RepoItem\")]),t._v(\" 实现如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'../index.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"RepoItem\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 将`repo.id`作为RepoItem的默认key\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RepoItem\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"repo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ValueKey\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"repo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"id\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Repo repo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _RepoItemState \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_RepoItemState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_RepoItemState\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"RepoItem\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" subtitle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"only\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"top\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"8.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Material\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        shape\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BorderDirectional\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          bottom\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BorderSide\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Theme\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dividerColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".5\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"only\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"top\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" bottom\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            crossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" CrossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                dense\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                leading\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"gmAvatar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//项目owner头像\")]),t._v(\"\\n                  widget\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"repo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"owner\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"avatar_url\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"24.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  borderRadius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BorderRadius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"circular\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"12\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                title\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  widget\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"repo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"owner\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"login\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  textScaleFactor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".9\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                subtitle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" subtitle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                trailing\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"repo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"language \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 构建项目标题和简介\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"symmetric\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"horizontal\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  crossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" CrossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                      widget\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"repo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"fork\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" widget\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"repo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"full_name\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" widget\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"repo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"name\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      style\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                        fontSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"15\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        fontWeight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" FontWeight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"bold\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        fontStyle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" widget\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"repo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"fork\\n                            \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" FontStyle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"italic\\n                            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" FontStyle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"normal\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                      padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"only\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"top\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"8\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" bottom\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"12\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" widget\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"repo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"description \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                              GmLocalizations\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"noDescription\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                              style\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                                  fontStyle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" FontStyle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"italic\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                  color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"700\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                              widget\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"repo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"description\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                              maxLines\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                              style\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                                height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.15\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blueGrey\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"700\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                fontSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"13\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 构建卡片底部信息\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_buildBottom\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 构建卡片底部信息\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_buildBottom\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" paddingWidth \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"10\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"IconTheme\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      data\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"IconThemeData\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        size\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"15\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DefaultTextStyle\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        style\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" fontSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"12\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"symmetric\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"horizontal\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" children \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"star\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\" \"')]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\"\\n                  widget\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"repo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"stargazers_count\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"padRight\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"paddingWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"info_outline\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\" \"')]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\"\\n                  widget\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"repo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"open_issues_count\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"padRight\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"paddingWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"MyIcons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"fork\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//我们的自定义图标\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"repo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"forks_count\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"padRight\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"paddingWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"repo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"fork\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Forked\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"padRight\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"paddingWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"repo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"private \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addAll\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"lock\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\" private\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"padRight\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"paddingWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"上面代码有两点需要注意：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[a(\"p\",[t._v(\"在构建项目拥有者头像时调用了\"),a(\"code\",[t._v(\"gmAvatar(…)\")]),t._v(\"方法，该方法是是一个全局工具函数，专门用于获取头像图片，实现如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[t._v(\"Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"gmAvatar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String url\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  double width \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"30\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  double height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  BoxFit fit\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  BorderRadius borderRadius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" placeholder \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Image\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"asset\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"imgs/avatar-default.png\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//头像占位图，加载过程中显示\")]),t._v(\"\\n      width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" height\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ClipRRect\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    borderRadius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" borderRadius \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" BorderRadius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"circular\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CachedNetworkImage\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \\n      imageUrl\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" url\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      fit\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" fit\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      placeholder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" url\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"placeholder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      errorWidget\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" url\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" error\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"placeholder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"代码中调用了\"),a(\"code\",[t._v(\"CachedNetworkImage\")]),t._v(\" 是cached_network_image包中提供的一个Widget，它不仅可以在图片加载过程中指定一个占位图，而且还可以对网络请求的图片进行缓存，更多详情读者可以自行查阅其文档。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"由于Flutter 的Material 图标库中没有fork图标，所以我们在iconfont.cn上找了一个fork图标，然后根据“图片和Icon”一节中介绍的使用自定义字体图标的方法集成到了我们的项目中。\")])])]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_15-6-3-抽屉菜单\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_15-6-3-抽屉菜单\"}},[t._v(\"#\")]),t._v(\" 15.6.3 抽屉菜单\")]),t._v(\" \"),a(\"p\",[t._v(\"抽屉菜单分为两部分：顶部头像和底部功能菜单项。当用户未登录，则抽屉菜单顶部会显示一个默认的灰色占位图，若用户已登录，则会显示用户的头像。抽屉菜单底部有“换肤”和“语言”两个固定菜单，若用户已登录，则会多一个“注销”菜单。用户点击“换肤”和“语言”两个菜单项，会进入相应的设置页面。我们的抽屉菜单效果如图15-3、15-4所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(390),alt:\"15-3\"}}),a(\"img\",{attrs:{src:n(391),alt:\"15-4\"}})]),t._v(\" \"),a(\"p\",[t._v(\"实现代码如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyDrawer\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyDrawer\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Drawer\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//移除顶部padding\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MediaQuery\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"removePadding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        removeTop\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          crossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" CrossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_buildHeader\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//构建抽屉菜单头部\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Expanded\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_buildMenus\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//构建功能菜单\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_buildHeader\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" Consumer\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"UserModel\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      builder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" UserModel value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Widget child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GestureDetector\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Theme\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"primaryColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"only\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"top\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"40\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" bottom\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"symmetric\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"horizontal\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ClipOval\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 如果已登录，则显示用户头像；若未登录，则显示默认头像\")]),t._v(\"\\n                    child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isLogin\\n                        \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"gmAvatar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"user\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"avatar_url\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Image\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"asset\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                            \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"imgs/avatar-default.png\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isLogin\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"user\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"login\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" GmLocalizations\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"login\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  style\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    fontWeight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" FontWeight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"bold\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          onTap\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isLogin\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" Navigator\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pushNamed\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"login\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 构建菜单项\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_buildMenus\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" Consumer\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"UserModel\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      builder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" UserModel userModel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Widget child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" gm \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" GmLocalizations\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListView\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              leading\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"color_lens\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              title\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"gm\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"theme\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              onTap\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Navigator\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pushNamed\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"themes\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              leading\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"language\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              title\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"gm\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"language\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              onTap\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Navigator\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pushNamed\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"language\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"userModel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isLogin\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              leading\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"power_settings_new\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              title\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"gm\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"logout\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              onTap\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showDialog\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  builder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ctx\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//退出账号前先弹二次确认窗\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlertDialog\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                      content\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"gm\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"logoutTip\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      actions\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                          child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"gm\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cancel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Navigator\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                          child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"gm\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"yes\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                            \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//该赋值语句会触发MaterialApp rebuild\")]),t._v(\"\\n                            userModel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"user \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                            Navigator\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"用户点击“注销”，\"),a(\"code\",[t._v(\"userModel.user\")]),t._v(\" 会被置空，此时所有依赖\"),a(\"code\",[t._v(\"userModel\")]),t._v(\"的组件都会被\"),a(\"code\",[t._v(\"rebuild\")]),t._v(\"，如主页会恢复成未登录的状态。\")]),t._v(\" \"),a(\"p\",[t._v(\"本小节我们介绍了APP入口\"),a(\"code\",[t._v(\"MaterialApp\")]),t._v(\"的一些配置，然后实现了APP的首页。后面我们将展示登录页、换肤页、语言切换页。\")])])}),[],!1,null,null,null);s.default=p.exports}}]);"
  },
  {
    "path": "docs/assets/js/26.e656381b.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[26],{394:function(t,s,a){t.exports=a.p+\"assets/img/2-8.89d0af83.png\"},395:function(t,s,a){t.exports=a.p+\"assets/img/2-9.0a86cf44.png\"},396:function(t,s,a){t.exports=a.p+\"assets/img/2-10.e56b6689.png\"},397:function(t,s,a){t.exports=a.p+\"assets/img/2-11.9f54d13a.png\"},767:function(t,s,a){\"use strict\";a.r(s);var e=a(45),n=Object(e.a)({},(function(){var t=this,s=t.$createElement,e=t._self._c||s;return e(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[e(\"h1\",{attrs:{id:\"_2-4-资源管理\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_2-4-资源管理\"}},[t._v(\"#\")]),t._v(\" 2.4 资源管理\")]),t._v(\" \"),e(\"p\",[t._v(\"Flutter APP安装包中会包含代码和 assets（资源）两部分。Assets是会打包到程序安装包中的，可在运行时访问。常见类型的assets包括静态数据（例如JSON文件）、配置文件、图标和图片（JPEG，WebP，GIF，动画WebP / GIF，PNG，BMP和WBMP）等。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"指定-assets\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#指定-assets\"}},[t._v(\"#\")]),t._v(\" 指定 assets\")]),t._v(\" \"),e(\"p\",[t._v(\"和包管理一样，Flutter也使用\"),e(\"a\",{attrs:{href:\"https://www.dartlang.org/tools/pub/pubspec\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"pubspec.yaml\")]),e(\"OutboundLink\")],1),t._v(\"文件来管理应用程序所需的资源，举个例子:\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-yaml extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"flutter\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"assets\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\" assets/my_icon.png\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\" assets/background.png\\n\")])])]),e(\"p\",[e(\"code\",[t._v(\"assets\")]),t._v(\"指定应包含在应用程序中的文件， 每个asset都通过相对于\"),e(\"code\",[t._v(\"pubspec.yaml\")]),t._v(\"文件所在的文件系统路径来标识自身的路径。asset的声明顺序是无关紧要的，asset的实际目录可以是任意文件夹（在本示例中是assets文件夹）。\")]),t._v(\" \"),e(\"p\",[t._v(\"在构建期间，Flutter将asset放置到称为 \"),e(\"em\",[t._v(\"asset bundle\")]),t._v(\" 的特殊存档中，应用程序可以在运行时读取它们（但不能修改）。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"asset-变体-variant\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#asset-变体-variant\"}},[t._v(\"#\")]),t._v(\" Asset 变体（variant）\")]),t._v(\" \"),e(\"p\",[t._v(\"构建过程支持“asset变体”的概念：不同版本的asset可能会显示在不同的上下文中。 在\"),e(\"code\",[t._v(\"pubspec.yaml\")]),t._v(\"的assets部分中指定asset路径时，构建过程中，会在相邻子目录中查找具有相同名称的任何文件。这些文件随后会与指定的asset一起被包含在asset bundle中。\")]),t._v(\" \"),e(\"p\",[t._v(\"例如，如果应用程序目录中有以下文件:\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"…/pubspec.yaml\")]),t._v(\" \"),e(\"li\",[t._v(\"…/graphics/my_icon.png\")]),t._v(\" \"),e(\"li\",[t._v(\"…/graphics/background.png\")]),t._v(\" \"),e(\"li\",[t._v(\"…/graphics/dark/background.png\")]),t._v(\" \"),e(\"li\",[t._v(\"…etc.\")])]),t._v(\" \"),e(\"p\",[t._v(\"然后\"),e(\"code\",[t._v(\"pubspec.yaml\")]),t._v(\"文件中只需包含:\")]),t._v(\" \"),e(\"div\",{staticClass:\"language- extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[e(\"code\",[t._v(\"flutter:\\n  assets:\\n    - graphics/background.png\\n\")])])]),e(\"p\",[t._v(\"那么这两个\"),e(\"code\",[t._v(\"graphics/background.png\")]),t._v(\"和\"),e(\"code\",[t._v(\"graphics/dark/background.png\")]),t._v(\" 都将包含在您的asset bundle中。前者被认为是_main asset_ （主资源），后者被认为是一种变体（variant）。\")]),t._v(\" \"),e(\"p\",[t._v(\"在选择匹配当前设备分辨率的图片时，Flutter会使用到asset变体（见下文），将来，Flutter可能会将这种机制扩展到本地化、阅读提示等方面。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"加载-assets\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#加载-assets\"}},[t._v(\"#\")]),t._v(\" 加载 assets\")]),t._v(\" \"),e(\"p\",[t._v(\"您的应用可以通过\"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/services/AssetBundle-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"AssetBundle\")]),e(\"OutboundLink\")],1),t._v(\"对象访问其asset 。有两种主要方法允许从Asset bundle中加载字符串或图片（二进制）文件。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"加载文本assets\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#加载文本assets\"}},[t._v(\"#\")]),t._v(\" 加载文本assets\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"通过\"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/services/rootBundle.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"rootBundle\")]),e(\"OutboundLink\")],1),t._v(\" 对象加载：每个Flutter应用程序都有一个\"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/services/rootBundle.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"rootBundle\")]),e(\"OutboundLink\")],1),t._v(\"对象， 通过它可以轻松访问主资源包，直接使用\"),e(\"code\",[t._v(\"package:flutter/services.dart\")]),t._v(\"中全局静态的\"),e(\"code\",[t._v(\"rootBundle\")]),t._v(\"对象来加载asset即可。\")]),t._v(\" \"),e(\"li\",[t._v(\"通过 \"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/DefaultAssetBundle-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"DefaultAssetBundle\")]),e(\"OutboundLink\")],1),t._v(\" 加载：建议使用 \"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/DefaultAssetBundle-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"DefaultAssetBundle\")]),e(\"OutboundLink\")],1),t._v(\" 来获取当前BuildContext的AssetBundle。 这种方法不是使用应用程序构建的默认asset bundle，而是使父级widget在运行时动态替换的不同的AssetBundle，这对于本地化或测试场景很有用。\")])]),t._v(\" \"),e(\"p\",[t._v(\"通常，可以使用\"),e(\"code\",[t._v(\"DefaultAssetBundle.of()\")]),t._v(\"在应用运行时来间接加载asset（例如JSON文件），而在widget上下文之外，或其它\"),e(\"code\",[t._v(\"AssetBundle\")]),t._v(\"句柄不可用时，可以使用\"),e(\"code\",[t._v(\"rootBundle\")]),t._v(\"直接加载这些asset，例如：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'dart:async'\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"show\")]),t._v(\" Future\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/services.dart'\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"show\")]),t._v(\" rootBundle\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\nFuture\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"loadAsset\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" rootBundle\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"loadString\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'assets/config.json'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"h3\",{attrs:{id:\"加载图片\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#加载图片\"}},[t._v(\"#\")]),t._v(\" 加载图片\")]),t._v(\" \"),e(\"p\",[t._v(\"类似于原生开发，Flutter也可以为当前设备加载适合其分辨率的图像。\")]),t._v(\" \"),e(\"h4\",{attrs:{id:\"声明分辨率相关的图片-assets\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#声明分辨率相关的图片-assets\"}},[t._v(\"#\")]),t._v(\" 声明分辨率相关的图片 assets\")]),t._v(\" \"),e(\"p\",[e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/painting/AssetImage-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"AssetImage\")]),e(\"OutboundLink\")],1),t._v(\" 可以将asset的请求逻辑映射到最接近当前设备像素比例（dpi）的asset。为了使这种映射起作用，必须根据特定的目录结构来保存asset：\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"…/image.png\")]),t._v(\" \"),e(\"li\",[t._v(\"…/\"),e(\"strong\",[t._v(\"M\")]),t._v(\"x/image.png\")]),t._v(\" \"),e(\"li\",[t._v(\"…/\"),e(\"strong\",[t._v(\"N\")]),t._v(\"x/image.png\")]),t._v(\" \"),e(\"li\",[t._v(\"…etc.\")])]),t._v(\" \"),e(\"p\",[t._v(\"其中M和N是数字标识符，对应于其中包含的图像的分辨率，也就是说，它们指定不同设备像素比例的图片。\")]),t._v(\" \"),e(\"p\",[t._v(\"主资源默认对应于1.0倍的分辨率图片。看一个例子：\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"…/my_icon.png\")]),t._v(\" \"),e(\"li\",[t._v(\"…/2.0x/my_icon.png\")]),t._v(\" \"),e(\"li\",[t._v(\"…/3.0x/my_icon.png\")])]),t._v(\" \"),e(\"p\",[t._v(\"在设备像素比率为1.8的设备上，\"),e(\"code\",[t._v(\".../2.0x/my_icon.png\")]),t._v(\" 将被选择。对于2.7的设备像素比率，\"),e(\"code\",[t._v(\".../3.0x/my_icon.png\")]),t._v(\"将被选择。\")]),t._v(\" \"),e(\"p\",[t._v(\"如果未在\"),e(\"code\",[t._v(\"Image\")]),t._v(\" widget上指定渲染图像的宽度和高度，那么\"),e(\"code\",[t._v(\"Image\")]),t._v(\" widget将占用与主资源相同的屏幕空间大小。 也就是说，如果\"),e(\"code\",[t._v(\".../my_icon.png\")]),t._v(\"是72px乘72px，那么\"),e(\"code\",[t._v(\".../3.0x/my_icon.png\")]),t._v(\"应该是216px乘216px; 但如果未指定宽度和高度，它们都将渲染为72像素×72像素（以逻辑像素为单位）。\")]),t._v(\" \"),e(\"p\",[e(\"code\",[t._v(\"pubspec.yaml\")]),t._v(\"中asset部分中的每一项都应与实际文件相对应，但主资源项除外。当主资源缺少某个资源时，会按分辨率从低到高的顺序去选择 ，也就是说1x中没有的话会在2x中找，2x中还没有的话就在3x中找。\")]),t._v(\" \"),e(\"h4\",{attrs:{id:\"加载图片-2\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#加载图片-2\"}},[t._v(\"#\")]),t._v(\" 加载图片\")]),t._v(\" \"),e(\"p\",[t._v(\"要加载图片，可以使用 \"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/painting/AssetImage-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"AssetImage\")]),e(\"OutboundLink\")],1),t._v(\"类。例如，我们可以从上面的asset声明中加载背景图片：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[t._v(\"Widget \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"DecoratedBox\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    decoration\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"BoxDecoration\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      image\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"DecorationImage\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        image\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AssetImage\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'graphics/background.png'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"注意，\"),e(\"code\",[t._v(\"AssetImage\")]),t._v(\" 并非是一个widget， 它实际上是一个\"),e(\"code\",[t._v(\"ImageProvider\")]),t._v(\"，有些时候你可能期望直接得到一个显示图片的widget，那么你可以使用\"),e(\"code\",[t._v(\"Image.asset()\")]),t._v(\"方法，如：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[t._v(\"Widget \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" Image\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"asset\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'graphics/background.png'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"使用默认的 asset bundle 加载资源时，内部会自动处理分辨率等，这些处理对开发者来说是无感知的。 (如果使用一些更低级别的类，如 \"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/painting/ImageStream-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"ImageStream\")]),e(\"OutboundLink\")],1),t._v(\"或 \"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/painting/ImageCache-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"ImageCache\")]),e(\"OutboundLink\")],1),t._v(\" 时你会注意到有与缩放相关的参数)\")]),t._v(\" \"),e(\"h4\",{attrs:{id:\"依赖包中的资源图片\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#依赖包中的资源图片\"}},[t._v(\"#\")]),t._v(\" 依赖包中的资源图片\")]),t._v(\" \"),e(\"p\",[t._v(\"要加载依赖包中的图像，必须给\"),e(\"code\",[t._v(\"AssetImage\")]),t._v(\"提供\"),e(\"code\",[t._v(\"package\")]),t._v(\"参数。\")]),t._v(\" \"),e(\"p\",[t._v(\"例如，假设您的应用程序依赖于一个名为“my_icons”的包，它具有如下目录结构：\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"…/pubspec.yaml\")]),t._v(\" \"),e(\"li\",[t._v(\"…/icons/heart.png\")]),t._v(\" \"),e(\"li\",[t._v(\"…/icons/1.5x/heart.png\")]),t._v(\" \"),e(\"li\",[t._v(\"…/icons/2.0x/heart.png\")]),t._v(\" \"),e(\"li\",[t._v(\"…etc.\")])]),t._v(\" \"),e(\"p\",[t._v(\"然后加载图像，使用:\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AssetImage\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'icons/heart.png'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" package\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'my_icons'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"或\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Image\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"asset\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'icons/heart.png'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" package\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'my_icons'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),e(\"p\",[e(\"strong\",[t._v(\"注意：包在使用本身的资源时也应该加上\"),e(\"code\",[t._v(\"package\")]),t._v(\"参数来获取\")]),t._v(\"。\")]),t._v(\" \"),e(\"h5\",{attrs:{id:\"打包包中的-assets\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#打包包中的-assets\"}},[t._v(\"#\")]),t._v(\" 打包包中的 assets\")]),t._v(\" \"),e(\"p\",[t._v(\"如果在\"),e(\"code\",[t._v(\"pubspec.yaml\")]),t._v(\"文件中声明了期望的资源，它将会打包到相应的package中。特别是，包本身使用的资源必须在\"),e(\"code\",[t._v(\"pubspec.yaml\")]),t._v(\"中指定。\")]),t._v(\" \"),e(\"p\",[t._v(\"包也可以选择在其\"),e(\"code\",[t._v(\"lib/\")]),t._v(\"文件夹中包含未在其\"),e(\"code\",[t._v(\"pubspec.yaml\")]),t._v(\"文件中声明的资源。在这种情况下，对于要打包的图片，应用程序必须在\"),e(\"code\",[t._v(\"pubspec.yaml\")]),t._v(\"中指定包含哪些图像。 例如，一个名为“fancy_backgrounds”的包，可能包含以下文件：\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"…/lib/backgrounds/background1.png\")]),t._v(\" \"),e(\"li\",[t._v(\"…/lib/backgrounds/background2.png\")]),t._v(\" \"),e(\"li\",[t._v(\"…/lib/backgrounds/background3.png\")])]),t._v(\" \"),e(\"p\",[t._v(\"要包含第一张图像，必须在\"),e(\"code\",[t._v(\"pubspec.yaml\")]),t._v(\"的assets部分中声明它：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language- extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[e(\"code\",[t._v(\"flutter:\\n  assets:\\n    - packages/fancy_backgrounds/backgrounds/background1.png\\n\")])])]),e(\"p\",[e(\"code\",[t._v(\"lib/\")]),t._v(\"是隐含的，所以它不应该包含在资产路径中。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"特定平台-assets\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#特定平台-assets\"}},[t._v(\"#\")]),t._v(\" 特定平台 assets\")]),t._v(\" \"),e(\"p\",[t._v(\"上面的资源都是flutter应用中的，这些资源只有在Flutter框架运行之后才能使用，如果要给我们的应用设置APP图标或者添加启动图，那我们必须使用特定平台的assets。\")]),t._v(\" \"),e(\"h4\",{attrs:{id:\"设置app图标\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#设置app图标\"}},[t._v(\"#\")]),t._v(\" 设置APP图标\")]),t._v(\" \"),e(\"p\",[t._v(\"更新Flutter应用程序启动图标的方式与在本机Android或iOS应用程序中更新启动图标的方式相同。\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"p\",[t._v(\"Android\")]),t._v(\" \"),e(\"p\",[t._v(\"在Flutter项目的根目录中，导航到\"),e(\"code\",[t._v(\".../android/app/src/main/res\")]),t._v(\"目录，里面包含了各种资源文件夹（如\"),e(\"code\",[t._v(\"mipmap-hdpi\")]),t._v(\"已包含占位符图像“ic_launcher.png”，见图2-8）。 只需按照\"),e(\"a\",{attrs:{href:\"https://developer.android.com/guide/practices/ui_guidelines/icon_design_launcher.html#size\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Android开发人员指南\"),e(\"OutboundLink\")],1),t._v(\"中的说明， 将其替换为所需的资源，并遵守每种屏幕密度（dpi）的建议图标大小标准。\")]),t._v(\" \"),e(\"p\",[e(\"img\",{attrs:{src:a(394),alt:\"图2-8\"}})]),t._v(\" \"),e(\"blockquote\",[e(\"p\",[e(\"strong\",[t._v(\"注意:\")]),t._v(\" 如果您重命名.png文件，则还必须在您\"),e(\"code\",[t._v(\"AndroidManifest.xml\")]),t._v(\"的\"),e(\"code\",[t._v(\"<application>\")]),t._v(\"标签的\"),e(\"code\",[t._v(\"android:icon\")]),t._v(\"属性中更新名称。\")])])]),t._v(\" \"),e(\"li\",[e(\"p\",[t._v(\"iOS\")]),t._v(\" \"),e(\"p\",[t._v(\"在Flutter项目的根目录中，导航到\"),e(\"code\",[t._v(\".../ios/Runner\")]),t._v(\"。该目录中\"),e(\"code\",[t._v(\"Assets.xcassets/AppIcon.appiconset\")]),t._v(\"已经包含占位符图片（见图2-9）， 只需将它们替换为适当大小的图片，保留原始文件名称。\")]),t._v(\" \"),e(\"p\",[e(\"img\",{attrs:{src:a(395),alt:\"图2-9\"}})])])]),t._v(\" \"),e(\"h4\",{attrs:{id:\"更新启动页\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#更新启动页\"}},[t._v(\"#\")]),t._v(\" 更新启动页\")]),t._v(\" \"),e(\"p\",[e(\"img\",{attrs:{src:a(396),alt:\"图2-10\"}})]),t._v(\" \"),e(\"p\",[t._v(\"在Flutter框架加载时，Flutter会使用本地平台机制绘制启动页。此启动页将持续到Flutter渲染应用程序的第一帧时。\")]),t._v(\" \"),e(\"blockquote\",[e(\"p\",[e(\"strong\",[t._v(\"注意:\")]),t._v(\" 这意味着如果您不在应用程序的\"),e(\"code\",[t._v(\"main()\")]),t._v(\"方法中调用\"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/runApp.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"runApp\"),e(\"OutboundLink\")],1),t._v(\" 函数 （或者更具体地说，如果您不调用\"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/dart-ui/Window/render.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"window.render\")]),e(\"OutboundLink\")],1),t._v(\"去响应\"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/dart-ui/Window/onDrawFrame.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"window.onDrawFrame\")]),e(\"OutboundLink\")],1),t._v(\"）的话， 启动屏幕将永远持续显示。\")])]),t._v(\" \"),e(\"h5\",{attrs:{id:\"android\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#android\"}},[t._v(\"#\")]),t._v(\" Android\")]),t._v(\" \"),e(\"p\",[t._v(\"要将启动屏幕（splash screen）添加到您的Flutter应用程序， 请导航至\"),e(\"code\",[t._v(\".../android/app/src/main\")]),t._v(\"。在\"),e(\"code\",[t._v(\"res/drawable/launch_background.xml\")]),t._v(\"，通过自定义drawable来实现自定义启动界面（你也可以直接换一张图片）。\")]),t._v(\" \"),e(\"h5\",{attrs:{id:\"ios\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#ios\"}},[t._v(\"#\")]),t._v(\" iOS\")]),t._v(\" \"),e(\"p\",[t._v(\"要将图片添加到启动屏幕（splash screen）的中心，请导航至\"),e(\"code\",[t._v(\".../ios/Runner\")]),t._v(\"。在\"),e(\"code\",[t._v(\"Assets.xcassets/LaunchImage.imageset\")]),t._v(\"， 拖入图片，并命名为\"),e(\"code\",[t._v(\"LaunchImage.png\")]),t._v(\"、\"),e(\"code\",[t._v(\"LaunchImage@2x.png\")]),t._v(\"、\"),e(\"code\",[t._v(\"LaunchImage@3x.png\")]),t._v(\"。 如果你使用不同的文件名，那您还必须更新同一目录中的\"),e(\"code\",[t._v(\"Contents.json\")]),t._v(\"文件，图片的具体尺寸可以查看苹果官方的标准。\")]),t._v(\" \"),e(\"p\",[t._v(\"您也可以通过打开Xcode完全自定义storyboard。在Project Navigator中导航到\"),e(\"code\",[t._v(\"Runner/Runner\")]),t._v(\"然后通过打开\"),e(\"code\",[t._v(\"Assets.xcassets\")]),t._v(\"拖入图片，或者通过在LaunchScreen.storyboard中使用Interface Builder进行自定义，如图2-11所示。\")]),t._v(\" \"),e(\"p\",[e(\"img\",{attrs:{src:a(397),alt:\"图2-11\"}})])])}),[],!1,null,null,null);s.default=n.exports}}]);"
  },
  {
    "path": "docs/assets/js/27.d1fa65c2.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[27],{428:function(t,s){t.exports=\"data:image/png;base64,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\"},429:function(t,s){t.exports=\"data:image/png;base64,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\"},430:function(t,s,a){t.exports=a.p+\"assets/img/3-32.11e62a02.png\"},431:function(t,s,a){t.exports=a.p+\"assets/img/progress_oval.4912d1a4.png\"},777:function(t,s,a){\"use strict\";a.r(s);var n=a(45),r=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_3-8-进度指示器\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-8-进度指示器\"}},[t._v(\"#\")]),t._v(\" 3.8 进度指示器\")]),t._v(\" \"),n(\"p\",[t._v(\"Material 组件库中提供了两种进度指示器：\"),n(\"code\",[t._v(\"LinearProgressIndicator\")]),t._v(\"和\"),n(\"code\",[t._v(\"CircularProgressIndicator\")]),t._v(\"，它们都可以同时用于精确的进度指示和模糊的进度指示。精确进度通常用于任务进度可以计算和预估的情况，比如文件下载；而模糊进度则用户任务进度无法准确获得的情况，如下拉刷新，数据提交等。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"linearprogressindicator\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#linearprogressindicator\"}},[t._v(\"#\")]),t._v(\" LinearProgressIndicator\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"LinearProgressIndicator\")]),t._v(\"是一个线性、条状的进度条，定义如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"LinearProgressIndicator\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  double value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  Color backgroundColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Color\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" valueColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"value\")]),t._v(\"：\"),n(\"code\",[t._v(\"value\")]),t._v(\"表示当前的进度，取值范围为[0,1]；如果\"),n(\"code\",[t._v(\"value\")]),t._v(\"为\"),n(\"code\",[t._v(\"null\")]),t._v(\"时则指示器会执行一个循环动画（模糊进度）；当\"),n(\"code\",[t._v(\"value\")]),t._v(\"不为\"),n(\"code\",[t._v(\"null\")]),t._v(\"时，指示器为一个具体进度的进度条。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"backgroundColor\")]),t._v(\"：指示器的背景色。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"valueColor\")]),t._v(\": 指示器的进度条颜色；值得注意的是，该值类型是\"),n(\"code\",[t._v(\"Animation<Color>\")]),t._v(\"，这允许我们对进度条的颜色也可以指定动画。如果我们不需要对进度条颜色执行动画，换言之，我们想对进度条应用一种固定的颜色，此时我们可以通过\"),n(\"code\",[t._v(\"AlwaysStoppedAnimation\")]),t._v(\"来指定。\")])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"示例\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 模糊进度条(会执行一个动画)\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"LinearProgressIndicator\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  backgroundColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  valueColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlwaysStoppedAnimation\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//进度条显示50%\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"LinearProgressIndicator\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  backgroundColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  valueColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlwaysStoppedAnimation\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".5\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图3-30所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(428),alt:\"图3-30\"}})]),t._v(\" \"),n(\"p\",[t._v(\"第一个进度条在执行循环动画：蓝色条一直在移动，而第二个进度条是静止的，停在50%的位置。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"circularprogressindicator\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#circularprogressindicator\"}},[t._v(\"#\")]),t._v(\" CircularProgressIndicator\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"CircularProgressIndicator\")]),t._v(\"是一个圆形进度条，定义如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircularProgressIndicator\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  double value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  Color backgroundColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Color\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" valueColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"strokeWidth \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"4.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"   \\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \\n\")])])]),n(\"p\",[t._v(\"前三个参数和\"),n(\"code\",[t._v(\"LinearProgressIndicator\")]),t._v(\"相同，不再赘述。\"),n(\"code\",[t._v(\"strokeWidth\")]),t._v(\" 表示圆形进度条的粗细。示例如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 模糊进度条(会执行一个旋转动画)\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircularProgressIndicator\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  backgroundColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  valueColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlwaysStoppedAnimation\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//进度条显示50%，会显示一个半圆\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircularProgressIndicator\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  backgroundColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  valueColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlwaysStoppedAnimation\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".5\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图3-31所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(429),alt:\"图3-31\"}})]),t._v(\" \"),n(\"p\",[t._v(\"第一个进度条会执行旋转动画，而第二个进度条是静止的，它停在50%的位置。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"自定义尺寸\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#自定义尺寸\"}},[t._v(\"#\")]),t._v(\" 自定义尺寸\")]),t._v(\" \"),n(\"p\",[t._v(\"我们可以发现\"),n(\"code\",[t._v(\"LinearProgressIndicator\")]),t._v(\"和\"),n(\"code\",[t._v(\"CircularProgressIndicator\")]),t._v(\"，并没有提供设置圆形进度条尺寸的参数；如果我们希望\"),n(\"code\",[t._v(\"LinearProgressIndicator\")]),t._v(\"的线细一些，或者希望\"),n(\"code\",[t._v(\"CircularProgressIndicator\")]),t._v(\"的圆大一些该怎么做？\")]),t._v(\" \"),n(\"p\",[t._v(\"其实\"),n(\"code\",[t._v(\"LinearProgressIndicator\")]),t._v(\"和\"),n(\"code\",[t._v(\"CircularProgressIndicator\")]),t._v(\"都是取父容器的尺寸作为绘制的边界的。知道了这点，我们便可以通过尺寸限制类Widget，如\"),n(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"、\"),n(\"code\",[t._v(\"SizedBox\")]),t._v(\" （我们将在后面容器类组件一章中介绍）来指定尺寸，如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 线性进度条高度指定为3\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"LinearProgressIndicator\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    backgroundColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    valueColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlwaysStoppedAnimation\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".5\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 圆形进度条直径指定为100\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircularProgressIndicator\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    backgroundColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    valueColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlwaysStoppedAnimation\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".7\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图3-32所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(430),alt:\"图3-32\"}})]),t._v(\" \"),n(\"p\",[t._v(\"注意，如果\"),n(\"code\",[t._v(\"CircularProgressIndicator\")]),t._v(\"显示空间的宽高不同，则会显示为椭圆。如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 宽高不等\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"130\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircularProgressIndicator\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    backgroundColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    valueColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlwaysStoppedAnimation\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".7\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图3-33所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(431),alt:\"progress_oval\"}})]),t._v(\" \"),n(\"h3\",{attrs:{id:\"进度色动画\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#进度色动画\"}},[t._v(\"#\")]),t._v(\" 进度色动画\")]),t._v(\" \"),n(\"p\",[t._v(\"前面说过可以通过\"),n(\"code\",[t._v(\"valueColor\")]),t._v(\"对进度条颜色做动画，关于动画我们将在后面专门的章节详细介绍，这里先给出一个例子，读者在了解了Flutter动画一章后再回过头来看。\")]),t._v(\" \"),n(\"p\",[t._v(\"我们实现一个进度条在3秒内从灰色变成蓝色的动画：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ProgressRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _ProgressRouteState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_ProgressRouteState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_ProgressRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ProgressRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"with\")]),t._v(\" SingleTickerProviderStateMixin \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  AnimationController _animationController\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//动画执行时间3秒  \")]),t._v(\"\\n    _animationController \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimationController\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"vsync\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" duration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    _animationController\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"forward\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    _animationController\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    _animationController\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SingleChildScrollView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"LinearProgressIndicator\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              backgroundColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              valueColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ColorTween\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"begin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" end\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"animate\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_animationController\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 从灰色变成蓝色\")]),t._v(\"\\n              value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _animationController\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"h3\",{attrs:{id:\"自定义进度指示器样式\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#自定义进度指示器样式\"}},[t._v(\"#\")]),t._v(\" 自定义进度指示器样式\")]),t._v(\" \"),n(\"p\",[t._v(\"定制进度指示器风格样式，可以通过\"),n(\"code\",[t._v(\"CustomPainter\")]),t._v(\" Widget 来自定义绘制逻辑，实际上\"),n(\"code\",[t._v(\"LinearProgressIndicator\")]),t._v(\"和\"),n(\"code\",[t._v(\"CircularProgressIndicator\")]),t._v(\"也正是通过\"),n(\"code\",[t._v(\"CustomPainter\")]),t._v(\"来实现外观绘制的。关于\"),n(\"code\",[t._v(\"CustomPainter\")]),t._v(\"，我们将在后面“自定义Widget”一章中详细介绍。\")]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[n(\"a\",{attrs:{href:\"https://pub.flutter-io.cn/packages/flutter_spinkit\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"flutter_spinkit\"),n(\"OutboundLink\")],1),t._v(\" 包提供了多种风格的模糊进度指示器，读者若是感兴趣，可以参考。\")])])])}),[],!1,null,null,null);s.default=r.exports}}]);"
  },
  {
    "path": "docs/assets/js/28.fbba6f6f.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[28],{439:function(t,n){t.exports=\"data:image/png;base64,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\"},440:function(t,n){t.exports=\"data:image/png;base64,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\"},441:function(t,n){t.exports=\"data:image/png;base64,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\"},442:function(t,n){t.exports=\"data:image/png;base64,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\"},781:function(t,n,a){\"use strict\";a.r(n);var s=a(45),e=Object(s.a)({},(function(){var t=this,n=t.$createElement,s=t._self._c||n;return s(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[s(\"h1\",{attrs:{id:\"_4-6-对齐与相对定位-align\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_4-6-对齐与相对定位-align\"}},[t._v(\"#\")]),t._v(\" 4.6 对齐与相对定位（Align）\")]),t._v(\" \"),s(\"p\",[t._v(\"在上一节中我们讲过通过\"),s(\"code\",[t._v(\"Stack\")]),t._v(\"和\"),s(\"code\",[t._v(\"Positioned\")]),t._v(\"，我们可以指定一个或多个子元素相对于父元素各个边的精确偏移，并且可以重叠。但如果我们只想简单的调整\"),s(\"strong\",[t._v(\"一个\")]),t._v(\"子元素在父元素中的位置的话，使用\"),s(\"code\",[t._v(\"Align\")]),t._v(\"组件会更简单一些。\")]),t._v(\" \"),s(\"h2\",{attrs:{id:\"_4-6-1-align\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_4-6-1-align\"}},[t._v(\"#\")]),t._v(\" 4.6.1 Align\")]),t._v(\" \"),s(\"p\",[s(\"code\",[t._v(\"Align\")]),t._v(\" 组件可以调整子组件的位置，并且可以根据子组件的宽高来确定自身的的宽高，定义如下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Align\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  Key key\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"alignment \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Alignment\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"widthFactor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"heightFactor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  Widget child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),s(\"ul\",[s(\"li\",[s(\"code\",[t._v(\"alignment\")]),t._v(\" : 需要一个\"),s(\"code\",[t._v(\"AlignmentGeometry\")]),t._v(\"类型的值，表示子组件在父组件中的起始位置。\"),s(\"code\",[t._v(\"AlignmentGeometry\")]),t._v(\" 是一个抽象类，它有两个常用的子类：\"),s(\"code\",[t._v(\"Alignment\")]),t._v(\"和 \"),s(\"code\",[t._v(\"FractionalOffset\")]),t._v(\"，我们将在下面的示例中详细介绍。\")]),t._v(\" \"),s(\"li\",[s(\"code\",[t._v(\"widthFactor\")]),t._v(\"和\"),s(\"code\",[t._v(\"heightFactor\")]),t._v(\"是用于确定\"),s(\"code\",[t._v(\"Align\")]),t._v(\" 组件本身宽高的属性；它们是两个缩放因子，会分别乘以子元素的宽、高，最终的结果就是\"),s(\"code\",[t._v(\"Align\")]),t._v(\" 组件的宽高。如果值为\"),s(\"code\",[t._v(\"null\")]),t._v(\"，则组件的宽高将会占用尽可能多的空间。\")])]),t._v(\" \"),s(\"h3\",{attrs:{id:\"示例\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),s(\"p\",[t._v(\"我们先来看一个简单的例子：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  height\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"120.0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  width\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"120.0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Align\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    alignment\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"topRight\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlutterLogo\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      size\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"60\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"运行效果如图4-11所示：\")]),t._v(\" \"),s(\"p\",[s(\"img\",{attrs:{src:a(439),alt:\"图4-11\"}})]),t._v(\" \"),s(\"p\",[s(\"code\",[t._v(\"FlutterLogo\")]),t._v(\" 是Flutter SDK提供的一个组件，内容就是Flutter的商标。在上面的例子中，我们显式指定了\"),s(\"code\",[t._v(\"Container\")]),t._v(\"的宽、高都为120。如果我们不显式指定宽高，而通过同时指定\"),s(\"code\",[t._v(\"widthFactor\")]),t._v(\"和\"),s(\"code\",[t._v(\"heightFactor\")]),t._v(\" 为2也是可以达到同样的效果：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Align\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  widthFactor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  heightFactor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  alignment\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"topRight\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlutterLogo\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    size\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"60\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"因为\"),s(\"code\",[t._v(\"FlutterLogo\")]),t._v(\"的宽高为60，则\"),s(\"code\",[t._v(\"Align\")]),t._v(\"的最终宽高都为\"),s(\"code\",[t._v(\"2*60=120\")]),t._v(\"。\")]),t._v(\" \"),s(\"p\",[t._v(\"另外，我们通过\"),s(\"code\",[t._v(\"Alignment.topRight\")]),t._v(\"将\"),s(\"code\",[t._v(\"FlutterLogo\")]),t._v(\"定位在\"),s(\"code\",[t._v(\"Container\")]),t._v(\"的右上角。那\"),s(\"code\",[t._v(\"Alignment.topRight\")]),t._v(\"是什么呢？通过源码我们可以看到其定义如下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//右上角\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" Alignment topRight \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Alignment\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"可以看到它只是\"),s(\"code\",[t._v(\"Alignment\")]),t._v(\"的一个实例，下面我们介绍一下\"),s(\"code\",[t._v(\"Alignment\")]),t._v(\"。\")]),t._v(\" \"),s(\"h3\",{attrs:{id:\"alignment\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#alignment\"}},[t._v(\"#\")]),t._v(\" Alignment\")]),t._v(\" \"),s(\"p\",[s(\"code\",[t._v(\"Alignment\")]),t._v(\"继承自\"),s(\"code\",[t._v(\"AlignmentGeometry\")]),t._v(\"，表示矩形内的一个点，他有两个属性\"),s(\"code\",[t._v(\"x\")]),t._v(\"、\"),s(\"code\",[t._v(\"y\")]),t._v(\"，分别表示在水平和垂直方向的偏移，\"),s(\"code\",[t._v(\"Alignment\")]),t._v(\"定义如下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Alignment\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"x\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"y\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),s(\"p\",[s(\"code\",[t._v(\"Alignment\")]),t._v(\" Widget会以\"),s(\"strong\",[t._v(\"矩形的中心点作为坐标原点\")]),t._v(\"，即\"),s(\"code\",[t._v(\"Alignment(0.0, 0.0)\")]),t._v(\" 。\"),s(\"code\",[t._v(\"x\")]),t._v(\"、\"),s(\"code\",[t._v(\"y\")]),t._v(\"的值从-1到1分别代表矩形左边到右边的距离和顶部到底边的距离，因此2个水平（或垂直）单位则等于矩形的宽（或高），如\"),s(\"code\",[t._v(\"Alignment(-1.0, -1.0)\")]),t._v(\" 代表矩形的左侧顶点，而\"),s(\"code\",[t._v(\"Alignment(1.0, 1.0)\")]),t._v(\"代表右侧底部终点，而\"),s(\"code\",[t._v(\"Alignment(1.0, -1.0)\")]),t._v(\" 则正是右侧顶点，即\"),s(\"code\",[t._v(\"Alignment.topRight\")]),t._v(\"。为了使用方便，矩形的原点、四个顶点，以及四条边的终点在\"),s(\"code\",[t._v(\"Alignment\")]),t._v(\"类中都已经定义为了静态常量。\")]),t._v(\" \"),s(\"p\",[s(\"code\",[t._v(\"Alignment\")]),t._v(\"可以通过其\"),s(\"strong\",[t._v(\"坐标转换公式\")]),t._v(\"将其坐标转为子元素的具体偏移坐标：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language- extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[s(\"code\",[t._v(\"(Alignment.x*childWidth/2+childWidth/2, Alignment.y*childHeight/2+childHeight/2)\\n\")])])]),s(\"p\",[t._v(\"其中\"),s(\"code\",[t._v(\"childWidth\")]),t._v(\"为子元素的宽度，\"),s(\"code\",[t._v(\"childHeight\")]),t._v(\"为子元素高度。\")]),t._v(\" \"),s(\"p\",[t._v(\"现在我们再看看上面的示例，我们将\"),s(\"code\",[t._v(\"Alignment(1.0, -1.0)\")]),t._v(\"带入上面公式，可得\"),s(\"code\",[t._v(\"FlutterLogo\")]),t._v(\"的实际偏移坐标正是（60，0）。下面再看一个例子：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Align\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  widthFactor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  heightFactor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  alignment\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Alignment\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlutterLogo\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    size\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"60\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"我们可以先想象一下运行效果：将\"),s(\"code\",[t._v(\"Alignment(2,0.0)\")]),t._v(\"带入上述坐标转换公式，可以得到\"),s(\"code\",[t._v(\"FlutterLogo\")]),t._v(\"的实际偏移坐标为（90，30）。实际运行如图4-12所示：\")]),t._v(\" \"),s(\"p\",[s(\"img\",{attrs:{src:a(440),alt:\"图4-12\"}})]),t._v(\" \"),s(\"h3\",{attrs:{id:\"fractionaloffset\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#fractionaloffset\"}},[t._v(\"#\")]),t._v(\" FractionalOffset\")]),t._v(\" \"),s(\"p\",[s(\"code\",[t._v(\"FractionalOffset\")]),t._v(\" 继承自 \"),s(\"code\",[t._v(\"Alignment\")]),t._v(\"，它和 \"),s(\"code\",[t._v(\"Alignment\")]),t._v(\"唯一的区别就是坐标原点不同！\"),s(\"code\",[t._v(\"FractionalOffset\")]),t._v(\" 的坐标原点为矩形的左侧顶点，这和布局系统的一致，所以理解起来会比较容易。\"),s(\"code\",[t._v(\"FractionalOffset\")]),t._v(\"的坐标转换公式为：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language- extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[s(\"code\",[t._v(\"实际偏移 = (FractionalOffse.x * childWidth, FractionalOffse.y * childHeight)\\n\")])])]),s(\"p\",[t._v(\"下面看一个例子：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  height\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"120.0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  width\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"120.0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Align\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    alignment\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FractionalOffset\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.2\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.6\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlutterLogo\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      size\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"60\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"实际运行效果如图4-13所示下：\")]),t._v(\" \"),s(\"p\",[s(\"img\",{attrs:{src:a(441),alt:\"图4-13\"}})]),t._v(\" \"),s(\"p\",[t._v(\"我们将\"),s(\"code\",[t._v(\"FractionalOffset(0.2, 0.6)\")]),t._v(\"带入坐标转换公式得\"),s(\"code\",[t._v(\"FlutterLogo\")]),t._v(\"实际偏移为（12，36），和实际运行效果吻合。\")]),t._v(\" \"),s(\"h2\",{attrs:{id:\"_4-6-2-align和stack对比\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_4-6-2-align和stack对比\"}},[t._v(\"#\")]),t._v(\" 4.6.2 Align和Stack对比\")]),t._v(\" \"),s(\"p\",[t._v(\"可以看到，\"),s(\"code\",[t._v(\"Align\")]),t._v(\"和\"),s(\"code\",[t._v(\"Stack\")]),t._v(\"/\"),s(\"code\",[t._v(\"Positioned\")]),t._v(\"都可以用于指定子元素相对于父元素的偏移，但它们还是有两个主要区别：\")]),t._v(\" \"),s(\"ol\",[s(\"li\",[t._v(\"定位参考系统不同；\"),s(\"code\",[t._v(\"Stack\")]),t._v(\"/\"),s(\"code\",[t._v(\"Positioned\")]),t._v(\"定位的的参考系可以是父容器矩形的四个顶点；而\"),s(\"code\",[t._v(\"Align\")]),t._v(\"则需要先通过\"),s(\"code\",[t._v(\"alignment\")]),t._v(\" 参数来确定坐标原点，不同的\"),s(\"code\",[t._v(\"alignment\")]),t._v(\"会对应不同原点，最终的偏移是需要通过\"),s(\"code\",[t._v(\"alignment\")]),t._v(\"的转换公式来计算出。\")]),t._v(\" \"),s(\"li\",[s(\"code\",[t._v(\"Stack\")]),t._v(\"可以有多个子元素，并且子元素可以堆叠，而\"),s(\"code\",[t._v(\"Align\")]),t._v(\"只能有一个子元素，不存在堆叠。\")])]),t._v(\" \"),s(\"h2\",{attrs:{id:\"_4-6-3-center组件\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_4-6-3-center组件\"}},[t._v(\"#\")]),t._v(\" 4.6.3 Center组件\")]),t._v(\" \"),s(\"p\",[t._v(\"我们在前面章节的例子中已经使用过\"),s(\"code\",[t._v(\"Center\")]),t._v(\"组件来居中子元素了，现在我们正式来介绍一下它。通过查找SDK源码，我们看到\"),s(\"code\",[t._v(\"Center\")]),t._v(\"组件定义如下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Center\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Align\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" Key key\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" double widthFactor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" double heightFactor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Widget child \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" widthFactor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" widthFactor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" heightFactor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" heightFactor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"可以看到\"),s(\"code\",[t._v(\"Center\")]),t._v(\"继承自\"),s(\"code\",[t._v(\"Align\")]),t._v(\"，它比\"),s(\"code\",[t._v(\"Align\")]),t._v(\"只少了一个\"),s(\"code\",[t._v(\"alignment\")]),t._v(\" 参数；由于\"),s(\"code\",[t._v(\"Align\")]),t._v(\"的构造函数中\"),s(\"code\",[t._v(\"alignment\")]),t._v(\" 值为\"),s(\"code\",[t._v(\"Alignment.center\")]),t._v(\"，所以，我们可以认为\"),s(\"code\",[t._v(\"Center\")]),t._v(\"组件其实是对齐方式确定（\"),s(\"code\",[t._v(\"Alignment.center\")]),t._v(\"）了的\"),s(\"code\",[t._v(\"Align\")]),t._v(\"。\")]),t._v(\" \"),s(\"p\",[t._v(\"上面我们讲过当\"),s(\"code\",[t._v(\"widthFactor\")]),t._v(\"或\"),s(\"code\",[t._v(\"heightFactor\")]),t._v(\"为\"),s(\"code\",[t._v(\"null\")]),t._v(\"时组件的宽高将会占用尽可能多的空间，这一点需要特别注意，我们通过一个示例说明：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecoratedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"xxx\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecoratedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    widthFactor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    heightFactor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"xxx\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"运行效果如图4-14所示：\")]),t._v(\" \"),s(\"p\",[s(\"img\",{attrs:{src:a(442),alt:\"图4-14\"}})]),t._v(\" \"),s(\"h2\",{attrs:{id:\"总结\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),s(\"p\",[t._v(\"本节重点介绍了\"),s(\"code\",[t._v(\"Align\")]),t._v(\"组件及两种偏移类\"),s(\"code\",[t._v(\"Alignment\")]),t._v(\" 和\"),s(\"code\",[t._v(\"FractionalOffset\")]),t._v(\"，读者需要理解这两种偏移类的区别及各自的坐标转化公式。另外，在此建议读者在需要制定一些精确的偏移时应优先使用\"),s(\"code\",[t._v(\"FractionalOffset\")]),t._v(\"，因为它的坐标原点和布局系统相同，能更容易算出实际偏移。\")]),t._v(\" \"),s(\"p\",[t._v(\"在后面，我们又介绍了\"),s(\"code\",[t._v(\"Align\")]),t._v(\"组件和\"),s(\"code\",[t._v(\"Stack\")]),t._v(\"/\"),s(\"code\",[t._v(\"Positioned\")]),t._v(\"、\"),s(\"code\",[t._v(\"Center\")]),t._v(\"的关系，读者可以对比理解。\")]),t._v(\" \"),s(\"p\",[t._v(\"还有，熟悉Web开发的同学可能会发现\"),s(\"code\",[t._v(\"Align\")]),t._v(\"组件的特性和Web开发中相对定位（\"),s(\"code\",[t._v(\"position: relative\")]),t._v(\"）非常像，是的！在大多数时候，我们可以直接使用\"),s(\"code\",[t._v(\"Align\")]),t._v(\"组件来实现Web中相对定位的效果，读者可以类比记忆。\")])])}),[],!1,null,null,null);n.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/29.e809e77a.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[29],{444:function(t,n,s){t.exports=s.p+\"assets/img/4-1.86777353.png\"},445:function(t,n,s){t.exports=s.p+\"assets/img/4-2.c3de517d.png\"},446:function(t,n,s){t.exports=s.p+\"assets/img/4-3.cc8d50f6.png\"},447:function(t,n,s){t.exports=s.p+\"assets/img/4-4.00885ea7.png\"},785:function(t,n,s){\"use strict\";s.r(n);var a=s(45),e=Object(a.a)({},(function(){var t=this,n=t.$createElement,a=t._self._c||n;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_4-2-线性布局-row和column\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_4-2-线性布局-row和column\"}},[t._v(\"#\")]),t._v(\" 4.2 线性布局（Row和Column）\")]),t._v(\" \"),a(\"p\",[t._v(\"所谓线性布局，即指沿水平或垂直方向排布子组件。Flutter中通过\"),a(\"code\",[t._v(\"Row\")]),t._v(\"和\"),a(\"code\",[t._v(\"Column\")]),t._v(\"来实现线性布局，类似于Android中的\"),a(\"code\",[t._v(\"LinearLayout\")]),t._v(\"控件。\"),a(\"code\",[t._v(\"Row\")]),t._v(\"和\"),a(\"code\",[t._v(\"Column\")]),t._v(\"都继承自\"),a(\"code\",[t._v(\"Flex\")]),t._v(\"，我们将在弹性布局一节中详细介绍\"),a(\"code\",[t._v(\"Flex\")]),t._v(\"。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"主轴和纵轴\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#主轴和纵轴\"}},[t._v(\"#\")]),t._v(\" 主轴和纵轴\")]),t._v(\" \"),a(\"p\",[t._v(\"对于线性布局，有主轴和纵轴之分，如果布局是沿水平方向，那么主轴就是指水平方向，而纵轴即垂直方向；如果布局沿垂直方向，那么主轴就是指垂直方向，而纵轴就是水平方向。在线性布局中，有两个定义对齐方式的枚举类\"),a(\"code\",[t._v(\"MainAxisAlignment\")]),t._v(\"和\"),a(\"code\",[t._v(\"CrossAxisAlignment\")]),t._v(\"，分别代表主轴对齐和纵轴对齐。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"row\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#row\"}},[t._v(\"#\")]),t._v(\" Row\")]),t._v(\" \"),a(\"p\",[t._v(\"Row可以在水平方向排列其子widget。定义如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"  \\n  TextDirection textDirection\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"    \\n  MainAxisSize mainAxisSize \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" MainAxisSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"max\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"    \\n  MainAxisAlignment mainAxisAlignment \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" MainAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  VerticalDirection verticalDirection \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" VerticalDirection\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"down\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"  \\n  CrossAxisAlignment crossAxisAlignment \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" CrossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  List\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" children \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"ul\",[a(\"li\",[a(\"code\",[t._v(\"textDirection\")]),t._v(\"：表示水平方向子组件的布局顺序(是从左往右还是从右往左)，默认为系统当前Locale环境的文本方向(如中文、英语都是从左往右，而阿拉伯语是从右往左)。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"mainAxisSize\")]),t._v(\"：表示\"),a(\"code\",[t._v(\"Row\")]),t._v(\"在主轴(水平)方向占用的空间，默认是\"),a(\"code\",[t._v(\"MainAxisSize.max\")]),t._v(\"，表示尽可能多的占用水平方向的空间，此时无论子widgets实际占用多少水平空间，\"),a(\"code\",[t._v(\"Row\")]),t._v(\"的宽度始终等于水平方向的最大宽度；而\"),a(\"code\",[t._v(\"MainAxisSize.min\")]),t._v(\"表示尽可能少的占用水平空间，当子组件没有占满水平剩余空间，则\"),a(\"code\",[t._v(\"Row\")]),t._v(\"的实际宽度等于所有子组件占用的的水平空间；\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"mainAxisAlignment\")]),t._v(\"：表示子组件在\"),a(\"code\",[t._v(\"Row\")]),t._v(\"所占用的水平空间内对齐方式，如果\"),a(\"code\",[t._v(\"mainAxisSize\")]),t._v(\"值为\"),a(\"code\",[t._v(\"MainAxisSize.min\")]),t._v(\"，则此属性无意义，因为子组件的宽度等于\"),a(\"code\",[t._v(\"Row\")]),t._v(\"的宽度。只有当\"),a(\"code\",[t._v(\"mainAxisSize\")]),t._v(\"的值为\"),a(\"code\",[t._v(\"MainAxisSize.max\")]),t._v(\"时，此属性才有意义，\"),a(\"code\",[t._v(\"MainAxisAlignment.start\")]),t._v(\"表示沿\"),a(\"code\",[t._v(\"textDirection\")]),t._v(\"的初始方向对齐，如\"),a(\"code\",[t._v(\"textDirection\")]),t._v(\"取值为\"),a(\"code\",[t._v(\"TextDirection.ltr\")]),t._v(\"时，则\"),a(\"code\",[t._v(\"MainAxisAlignment.start\")]),t._v(\"表示左对齐，\"),a(\"code\",[t._v(\"textDirection\")]),t._v(\"取值为\"),a(\"code\",[t._v(\"TextDirection.rtl\")]),t._v(\"时表示从右对齐。而\"),a(\"code\",[t._v(\"MainAxisAlignment.end\")]),t._v(\"和\"),a(\"code\",[t._v(\"MainAxisAlignment.start\")]),t._v(\"正好相反；\"),a(\"code\",[t._v(\"MainAxisAlignment.center\")]),t._v(\"表示居中对齐。读者可以这么理解：\"),a(\"code\",[t._v(\"textDirection\")]),t._v(\"是\"),a(\"code\",[t._v(\"mainAxisAlignment\")]),t._v(\"的参考系。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"verticalDirection\")]),t._v(\"：表示\"),a(\"code\",[t._v(\"Row\")]),t._v(\"纵轴（垂直）的对齐方向，默认是\"),a(\"code\",[t._v(\"VerticalDirection.down\")]),t._v(\"，表示从上到下。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"crossAxisAlignment\")]),t._v(\"：表示子组件在纵轴方向的对齐方式，\"),a(\"code\",[t._v(\"Row\")]),t._v(\"的高度等于子组件中最高的子元素高度，它的取值和\"),a(\"code\",[t._v(\"MainAxisAlignment\")]),t._v(\"一样(包含\"),a(\"code\",[t._v(\"start\")]),t._v(\"、\"),a(\"code\",[t._v(\"end\")]),t._v(\"、 \"),a(\"code\",[t._v(\"center\")]),t._v(\"三个值)，不同的是\"),a(\"code\",[t._v(\"crossAxisAlignment\")]),t._v(\"的参考系是\"),a(\"code\",[t._v(\"verticalDirection\")]),t._v(\"，即\"),a(\"code\",[t._v(\"verticalDirection\")]),t._v(\"值为\"),a(\"code\",[t._v(\"VerticalDirection.down\")]),t._v(\"时\"),a(\"code\",[t._v(\"crossAxisAlignment.start\")]),t._v(\"指顶部对齐，\"),a(\"code\",[t._v(\"verticalDirection\")]),t._v(\"值为\"),a(\"code\",[t._v(\"VerticalDirection.up\")]),t._v(\"时，\"),a(\"code\",[t._v(\"crossAxisAlignment.start\")]),t._v(\"指底部对齐；而\"),a(\"code\",[t._v(\"crossAxisAlignment.end\")]),t._v(\"和\"),a(\"code\",[t._v(\"crossAxisAlignment.start\")]),t._v(\"正好相反；\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"children\")]),t._v(\" ：子组件数组。\")])]),t._v(\" \"),a(\"h3\",{attrs:{id:\"示例\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),a(\"p\",[t._v(\"请阅读下面代码，先想象一下运行的结果：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//测试Row对齐方式，排除Column默认居中对齐的干扰\")]),t._v(\"\\n  crossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" CrossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      mainAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\" hello world \"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\" I am Jack \"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      mainAxisSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"min\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      mainAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\" hello world \"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\" I am Jack \"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      mainAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"end\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      textDirection\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" TextDirection\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"rtl\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\" hello world \"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\" I am Jack \"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      crossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" CrossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"  \\n      verticalDirection\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" VerticalDirection\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"up\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\" hello world \"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" style\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"fontSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"30.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\" I am Jack \"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"实际运行结果如图4-1所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:s(444),alt:\"图4-1\"}})]),t._v(\" \"),a(\"p\",[t._v(\"解释：第一个\"),a(\"code\",[t._v(\"Row\")]),t._v(\"很简单，默认为居中对齐；第二个\"),a(\"code\",[t._v(\"Row\")]),t._v(\"，由于\"),a(\"code\",[t._v(\"mainAxisSize\")]),t._v(\"值为\"),a(\"code\",[t._v(\"MainAxisSize.min\")]),t._v(\"，\"),a(\"code\",[t._v(\"Row\")]),t._v(\"的宽度等于两个\"),a(\"code\",[t._v(\"Text\")]),t._v(\"的宽度和，所以对齐是无意义的，所以会从左往右显示；第三个\"),a(\"code\",[t._v(\"Row\")]),t._v(\"设置\"),a(\"code\",[t._v(\"textDirection\")]),t._v(\"值为\"),a(\"code\",[t._v(\"TextDirection.rtl\")]),t._v(\"，所以子组件会从右向左的顺序排列，而此时\"),a(\"code\",[t._v(\"MainAxisAlignment.end\")]),t._v(\"表示左对齐，所以最终显示结果就是图中第三行的样子；第四个Row测试的是纵轴的对齐方式，由于两个子Text字体不一样，所以其高度也不同，我们指定了\"),a(\"code\",[t._v(\"verticalDirection\")]),t._v(\"值为\"),a(\"code\",[t._v(\"VerticalDirection.up\")]),t._v(\"，即从低向顶排列，而此时\"),a(\"code\",[t._v(\"crossAxisAlignment\")]),t._v(\"值为\"),a(\"code\",[t._v(\"CrossAxisAlignment.start\")]),t._v(\"表示底对齐。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"column\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#column\"}},[t._v(\"#\")]),t._v(\" Column\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"Column\")]),t._v(\"可以在垂直方向排列其子组件。参数和\"),a(\"code\",[t._v(\"Row\")]),t._v(\"一样，不同的是布局方向为垂直，主轴纵轴正好相反，读者可类比\"),a(\"code\",[t._v(\"Row\")]),t._v(\"来理解，下面看一个例子：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CenterColumnRoute\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      crossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" CrossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"hi\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"world\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"运行效果如图4-2所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:s(445),alt:\"图4-2示例\"}})]),t._v(\" \"),a(\"p\",[t._v(\"解释：\")]),t._v(\" \"),a(\"ul\",[a(\"li\",[t._v(\"由于我们没有指定\"),a(\"code\",[t._v(\"Column\")]),t._v(\"的\"),a(\"code\",[t._v(\"mainAxisSize\")]),t._v(\"，所以使用默认值\"),a(\"code\",[t._v(\"MainAxisSize.max\")]),t._v(\"，则\"),a(\"code\",[t._v(\"Column\")]),t._v(\"会在垂直方向占用尽可能多的空间，此例中为屏幕高度。\")]),t._v(\" \"),a(\"li\",[t._v(\"由于我们指定了 \"),a(\"code\",[t._v(\"crossAxisAlignment\")]),t._v(\" 属性为\"),a(\"code\",[t._v(\"CrossAxisAlignment.center\")]),t._v(\"，那么子项在\"),a(\"code\",[t._v(\"Column\")]),t._v(\"纵轴方向（此时为水平方向）会居中对齐。注意，在水平方向对齐是有边界的，总宽度为\"),a(\"code\",[t._v(\"Column\")]),t._v(\"占用空间的实际宽度，而实际的宽度取决于子项中宽度最大的Widget。在本例中，\"),a(\"code\",[t._v(\"Column\")]),t._v(\"有两个子Widget，而显示“world”的\"),a(\"code\",[t._v(\"Text\")]),t._v(\"宽度最大，所以\"),a(\"code\",[t._v(\"Column\")]),t._v(\"的实际宽度则为\"),a(\"code\",[t._v('Text(\"world\")')]),t._v(\" 的宽度，所以居中对齐后\"),a(\"code\",[t._v('Text(\"hi\")')]),t._v(\"会显示在\"),a(\"code\",[t._v('Text(\"world\")')]),t._v(\"的中间部分。\")])]),t._v(\" \"),a(\"p\",[a(\"strong\",[t._v(\"实际上，\"),a(\"code\",[t._v(\"Row\")]),t._v(\"和\"),a(\"code\",[t._v(\"Column\")]),t._v(\"都只会在主轴方向占用尽可能大的空间，而纵轴的长度则取决于他们最大子元素的长度\")]),t._v(\"。如果我们想让本例中的两个文本控件在整个手机屏幕中间对齐，我们有两种方法：\")]),t._v(\" \"),a(\"ul\",[a(\"li\",[a(\"p\",[t._v(\"将\"),a(\"code\",[t._v(\"Column\")]),t._v(\"的宽度指定为屏幕宽度；这很简单，我们可以通过\"),a(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"或\"),a(\"code\",[t._v(\"SizedBox\")]),t._v(\"（我们将在后面章节中专门介绍这两个Widget）来强制更改宽度限制，例如：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ConstrainedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  constraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxConstraints\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"minWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" double\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"infinity\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    crossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" CrossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"hi\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"world\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"将\"),a(\"code\",[t._v(\"minWidth\")]),t._v(\"设为\"),a(\"code\",[t._v(\"double.infinity\")]),t._v(\"，可以使宽度占用尽可能多的空间。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"使用\"),a(\"code\",[t._v(\"Center\")]),t._v(\" Widget；我们将在后面章节中介绍。\")])])]),t._v(\" \"),a(\"h3\",{attrs:{id:\"特殊情况\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#特殊情况\"}},[t._v(\"#\")]),t._v(\" 特殊情况\")]),t._v(\" \"),a(\"p\",[t._v(\"如果\"),a(\"code\",[t._v(\"Row\")]),t._v(\"里面嵌套\"),a(\"code\",[t._v(\"Row\")]),t._v(\"，或者\"),a(\"code\",[t._v(\"Column\")]),t._v(\"里面再嵌套\"),a(\"code\",[t._v(\"Column\")]),t._v(\"，那么只有最外面的\"),a(\"code\",[t._v(\"Row\")]),t._v(\"或\"),a(\"code\",[t._v(\"Column\")]),t._v(\"会占用尽可能大的空间，里面\"),a(\"code\",[t._v(\"Row\")]),t._v(\"或\"),a(\"code\",[t._v(\"Column\")]),t._v(\"所占用的空间为实际大小，下面以\"),a(\"code\",[t._v(\"Column\")]),t._v(\"为例说明：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"green\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      crossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" CrossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      mainAxisSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"max\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//有效，外层Colum高度为整个屏幕\")]),t._v(\"\\n      children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            mainAxisSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"max\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//无效，内层Colum高度为实际高度  \")]),t._v(\"\\n            children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"hello world \"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"I am Jack \"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"运行效果如图4-3所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:s(446),alt:\"图4-3\"}})]),t._v(\" \"),a(\"p\",[t._v(\"如果要让里面的\"),a(\"code\",[t._v(\"Column\")]),t._v(\"占满外部\"),a(\"code\",[t._v(\"Column\")]),t._v(\"，可以使用\"),a(\"code\",[t._v(\"Expanded\")]),t._v(\" 组件：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Expanded\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \\n  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      mainAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//垂直方向居中对齐\")]),t._v(\"\\n      children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"hello world \"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"I am Jack \"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"运行效果如图4-4所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:s(447),alt:\"图4-4\"}})]),t._v(\" \"),a(\"p\",[t._v(\"我们将在介绍弹性布局时详细介绍Expanded。\")])])}),[],!1,null,null,null);n.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/3.cc1736a7.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[3],{495:function(t,s,a){t.exports=a.p+\"assets/img/7-10.47645234.png\"},496:function(t,s,a){t.exports=a.p+\"assets/img/7-11.8cffb68f.png\"},497:function(t,s,a){t.exports=a.p+\"assets/img/7-12.df06c0b8.png\"},498:function(t,s,a){t.exports=a.p+\"assets/img/7-13.45803ea4.png\"},499:function(t,s,a){t.exports=a.p+\"assets/img/7-14.edea3e0f.png\"},500:function(t,s,a){t.exports=a.p+\"assets/img/7-15.8624d4d8.png\"},501:function(t,s,a){t.exports=a.p+\"assets/img/7-16.da05101a.png\"},502:function(t,s,a){t.exports=a.p+\"assets/img/7-17.c0e2d9be.png\"},503:function(t,s,a){t.exports=a.p+\"assets/img/7-18.d0ccba9d.png\"},504:function(t,s,a){t.exports=a.p+\"assets/img/7-19.4c3306a3.png\"},505:function(t,s,a){t.exports=a.p+\"assets/img/7-20.f8ec9897.png\"},506:function(t,s,a){t.exports=a.p+\"assets/img/7-21.d3d1d15f.png\"},804:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_7-6-对话框详解\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_7-6-对话框详解\"}},[t._v(\"#\")]),t._v(\" 7.6 对话框详解\")]),t._v(\" \"),n(\"p\",[t._v(\"本节将详细介绍一下Flutter中对话框的使用方式、实现原理、样式定制及状态管理。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_7-6-1-使用对话框\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_7-6-1-使用对话框\"}},[t._v(\"#\")]),t._v(\" 7.6.1 使用对话框\")]),t._v(\" \"),n(\"p\",[t._v(\"对话框本质上也是UI布局，通常一个对话框会包含标题、内容，以及一些操作按钮，为此，Material库中提供了一些现成的对话框组件来用于快速的构建出一个完整的对话框。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"alertdialog\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#alertdialog\"}},[t._v(\"#\")]),t._v(\" AlertDialog\")]),t._v(\" \"),n(\"p\",[t._v(\"下面我们主要介绍一下Material库中的\"),n(\"code\",[t._v(\"AlertDialog\")]),t._v(\"组件，它的构造函数定义如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlertDialog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//对话框标题组件\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"titlePadding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 标题填充\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"titleTextStyle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//标题文本样式\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"content\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 对话框内容组件\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"contentPadding \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fromLTRB\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"24.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"24.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"24.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//内容的填充\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"contentTextStyle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 内容文本样式\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"actions\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 对话框操作按钮组\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"backgroundColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 对话框背景色\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"elevation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 对话框的阴影\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"semanticLabel\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//对话框语义化标签(用于读屏软件)\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"shape\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 对话框外形\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"参数都比较简单，不在赘述。下面我们看一个例子，假如我们要在删除文件时弹出一个确认对话框，该对话框如图7-10所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(495),alt:\"图7-10\"}})]),t._v(\" \"),n(\"p\",[t._v(\"该对话框样式代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlertDialog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"提示\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  content\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"您确定要删除当前文件吗?\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  actions\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"取消\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//关闭对话框\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"删除\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// ... 执行删除操作\")]),t._v(\"\\n        Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//关闭对话框\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"实现代码很简单，不在赘述。唯一需要注意的是我们是通过\"),n(\"code\",[t._v(\"Navigator.of(context).pop(…)\")]),t._v(\"方法来关闭对话框的，这和路由返回的方式是一致的，并且都可以返回一个结果数据。现在，对话框我们已经构建好了，那么如何将它弹出来呢？还有对话框返回的数据应如何被接收呢？这些问题的答案都在\"),n(\"code\",[t._v(\"showDialog()\")]),t._v(\"方法中。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"showDialog()\")]),t._v(\"是Material组件库提供的一个用于弹出Material风格对话框的方法，签名如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" showDialog\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  bool barrierDismissible \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//点击对话框barrier(遮罩)时是否关闭它\")]),t._v(\"\\n  WidgetBuilder builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 对话框UI的builder\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"该方法只有两个参数，含义见注释。该方法返回一个\"),n(\"code\",[t._v(\"Future\")]),t._v(\"，它正是用于接收对话框的返回值：如果我们是通过点击对话框遮罩关闭的，则\"),n(\"code\",[t._v(\"Future\")]),t._v(\"的值为\"),n(\"code\",[t._v(\"null\")]),t._v(\"，否则为我们通过\"),n(\"code\",[t._v(\"Navigator.of(context).pop(result)\")]),t._v(\"返回的result值，下面我们看一下整个示例：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//点击该按钮后弹出对话框\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"对话框1\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//弹出对话框并等待其关闭\")]),t._v(\"\\n    bool delete \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showDeleteConfirmDialog1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"delete \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"取消删除\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"已确认删除\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//... 删除文件\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 弹出对话框\")]),t._v(\"\\nFuture\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"bool\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showDeleteConfirmDialog1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" showDialog\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"bool\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlertDialog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"提示\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        content\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"您确定要删除当前文件吗?\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        actions\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"取消\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 关闭对话框\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"删除\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//关闭对话框并返回true\")]),t._v(\"\\n              Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v('示例运行后，我们点击对话框“取消”按钮或遮罩，控制台就会输出\"取消删除\"，如果点击“删除”按钮，控制台就会输出\"已确认删除\"。')]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[t._v(\"注意：如果\"),n(\"code\",[t._v(\"AlertDialog\")]),t._v(\"的内容过长，内容将会溢出，这在很多时候可能不是我们期望的，所以如果对话框内容过长时，可以用\"),n(\"code\",[t._v(\"SingleChildScrollView\")]),t._v(\"将内容包裹起来。\")])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"simpledialog\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#simpledialog\"}},[t._v(\"#\")]),t._v(\" SimpleDialog\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"SimpleDialog\")]),t._v(\"也是Material组件库提供的对话框，它会展示一个列表，用于列表选择的场景。下面是一个选择APP语言的示例，运行结果如图7-11。\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(496),alt:\"图7-11\"}})]),t._v(\" \"),n(\"p\",[t._v(\"实现代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"changeLanguage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  int i \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" showDialog\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"int\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SimpleDialog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'请选择语言'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SimpleDialogOption\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 返回1\")]),t._v(\"\\n                Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"symmetric\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"vertical\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'中文简体'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SimpleDialogOption\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 返回2\")]),t._v(\"\\n                Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"symmetric\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"vertical\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'美国英语'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"i \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"选择了：${i == 1 ? \"')]),t._v(\"中文简体\"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\" : \"')]),t._v(\"美国英语\"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"}\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"列表项组件我们使用了\"),n(\"code\",[t._v(\"SimpleDialogOption\")]),t._v(\"组件来包装了一下，它相当于一个FlatButton，只不过按钮文案是左对齐的，并且padding较小。上面示例运行后，用户选择一种语言后，控制台就会打印出它。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"dialog\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#dialog\"}},[t._v(\"#\")]),t._v(\" Dialog\")]),t._v(\" \"),n(\"p\",[t._v(\"实际上\"),n(\"code\",[t._v(\"AlertDialog\")]),t._v(\"和\"),n(\"code\",[t._v(\"SimpleDialog\")]),t._v(\"都使用了\"),n(\"code\",[t._v(\"Dialog\")]),t._v(\"类。由于\"),n(\"code\",[t._v(\"AlertDialog\")]),t._v(\"和\"),n(\"code\",[t._v(\"SimpleDialog\")]),t._v(\"中使用了\"),n(\"code\",[t._v(\"IntrinsicWidth\")]),t._v(\"来尝试通过子组件的实际尺寸来调整自身尺寸，这就导致他们的子组件不能是延迟加载模型的组件（如\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"、\"),n(\"code\",[t._v(\"GridView\")]),t._v(\" 、 \"),n(\"code\",[t._v(\"CustomScrollView\")]),t._v(\"等），如下面的代码运行后会报错。\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlertDialog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  content\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"如果我们就是需要嵌套一个\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"应该怎么做？这时，我们可以直接使用\"),n(\"code\",[t._v(\"Dialog\")]),t._v(\"类，如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Dialog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"下面我们看一个弹出一个有30个列表项的对话框示例，运行效果如图7-12所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(497),alt:\"图7-12\"}})]),t._v(\" \"),n(\"p\",[t._v(\"实现代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showListDialog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  int index \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" showDialog\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"int\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" child \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"请选择\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Expanded\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ListView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            itemCount\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"30\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            itemBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$index\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                onTap\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//使用AlertDialog会报错\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//return AlertDialog(content: child);\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Dialog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"index \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"点击了：$index\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"现在，我们己经介绍完了\"),n(\"code\",[t._v(\"AlertDialog\")]),t._v(\"、\"),n(\"code\",[t._v(\"SimpleDialog\")]),t._v(\"以及\"),n(\"code\",[t._v(\"Dialog\")]),t._v(\"。上面的示例中，我们在调用\"),n(\"code\",[t._v(\"showDialog\")]),t._v(\"时，在\"),n(\"code\",[t._v(\"builder\")]),t._v(\"中都是构建了这三个对话框组件的一种，可能有些读者会惯性的以为在\"),n(\"code\",[t._v(\"builder\")]),t._v(\"中只能返回这三者之一，其实这不是必须的！就拿\"),n(\"code\",[t._v(\"Dialog\")]),t._v(\"的示例来举例，我们完全可以用下面的代码来替代\"),n(\"code\",[t._v(\"Dialog\")]),t._v(\"：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// return Dialog(child: child) \")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"UnconstrainedBox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  constrainedAxis\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Axis\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"vertical\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ConstrainedBox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    constraints\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxConstraints\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"maxWidth\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"280\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Material\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      type\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MaterialType\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"card\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上面代码运行后可以实现一样的效果。现在我们总结一下：\"),n(\"code\",[t._v(\"AlertDialog\")]),t._v(\"、\"),n(\"code\",[t._v(\"SimpleDialog\")]),t._v(\"以及\"),n(\"code\",[t._v(\"Dialog\")]),t._v(\"是Material组件库提供的三种对话框，旨在帮助开发者快速构建出符合Material设计规范的对话框，但读者完全可以自定义对话框样式，因此，我们仍然可以实现各种样式的对话框，这样即带来了易用性，又有很强的扩展性。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_7-6-2-对话框打开动画及遮罩\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_7-6-2-对话框打开动画及遮罩\"}},[t._v(\"#\")]),t._v(\" 7.6.2 对话框打开动画及遮罩\")]),t._v(\" \"),n(\"p\",[t._v(\"我们可以把对话框分为内部样式和外部样式两部分。内部样式指对话框中显示的具体内容，这部分内容我们已经在上面介绍过了；外部样式包含对话框遮罩样式、打开动画等，本节主要介绍如何自定义这些外部样式。\")]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[t._v(\"关于动画相关内容我们将在本书后面章节介绍，下面内容读者可以先了解一下（不必深究），读者可以在学习完动画相关内容后再回头来看。\")])]),t._v(\" \"),n(\"p\",[t._v(\"我们已经介绍过了\"),n(\"code\",[t._v(\"showDialog\")]),t._v(\"方法，它是Material组件库中提供的一个打开Material风格对话框的方法。那如何打开一个普通风格的对话框呢（非Material风格）？ Flutter 提供了一个\"),n(\"code\",[t._v(\"showGeneralDialog\")]),t._v(\"方法，签名如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" showGeneralDialog\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" RoutePageBuilder pageBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//构建对话框内部UI\")]),t._v(\"\\n  bool barrierDismissible\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//点击遮罩是否关闭对话框\")]),t._v(\"\\n  String barrierLabel\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 语义化标签(用于读屏软件)\")]),t._v(\"\\n  Color barrierColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 遮罩颜色\")]),t._v(\"\\n  Duration transitionDuration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 对话框打开/关闭的动画时长\")]),t._v(\"\\n  RouteTransitionsBuilder transitionBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 对话框打开/关闭的动画\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"实际上，\"),n(\"code\",[t._v(\"showDialog\")]),t._v(\"方法正是\"),n(\"code\",[t._v(\"showGeneralDialog\")]),t._v(\"的一个封装，定制了Material风格对话框的遮罩颜色和动画。Material风格对话框打开/关闭动画是一个Fade（渐隐渐显）动画，如果我们想使用一个缩放动画就可以通过\"),n(\"code\",[t._v(\"transitionBuilder\")]),t._v(\"来自定义。下面我们自己封装一个\"),n(\"code\",[t._v(\"showCustomDialog\")]),t._v(\"方法，它定制的对话框动画为缩放动画，并同时制定遮罩颜色为\"),n(\"code\",[t._v(\"Colors.black87\")]),t._v(\"：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" showCustomDialog\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  bool barrierDismissible \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  WidgetBuilder builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" ThemeData theme \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Theme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" shadowThemeOnly\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showGeneralDialog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    pageBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext buildContext\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" secondaryAnimation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Widget pageChild \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SafeArea\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" theme \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Theme\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"data\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" theme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" pageChild\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" pageChild\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    barrierDismissible\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" barrierDismissible\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    barrierLabel\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MaterialLocalizations\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"modalBarrierDismissLabel\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    barrierColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"black87\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 自定义遮罩颜色\")]),t._v(\"\\n    transitionDuration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"milliseconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"150\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    transitionBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _buildMaterialDialogTransitions\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\nWidget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_buildMaterialDialogTransitions\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" secondaryAnimation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    Widget child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 使用缩放动画\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ScaleTransition\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    scale\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CurvedAnimation\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      parent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      curve\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Curves\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"easeOut\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"现在，我们使用\"),n(\"code\",[t._v(\"showCustomDialog\")]),t._v(\"打开文件删除确认对话框，代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\nshowCustomDialog\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"bool\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlertDialog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"提示\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      content\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"您确定要删除当前文件吗?\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      actions\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"取消\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"删除\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 执行删除操作\")]),t._v(\"\\n            Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图7-13所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(498),alt:\"图7-13\"}})]),t._v(\" \"),n(\"p\",[t._v(\"可以发现，遮罩颜色比通过\"),n(\"code\",[t._v(\"showDialog\")]),t._v(\"方法打开的对话框更深。另外对话框打开/关闭的动画已变为缩放动画了，读者可以亲自运行示例查看效果。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_7-6-3-对话框实现原理\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_7-6-3-对话框实现原理\"}},[t._v(\"#\")]),t._v(\" 7.6.3 对话框实现原理\")]),t._v(\" \"),n(\"p\",[t._v(\"我们已经知道对话框最终都是由\"),n(\"code\",[t._v(\"showGeneralDialog\")]),t._v(\"方法打开的，我们来看看它的具体实现：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" showGeneralDialog\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" RoutePageBuilder pageBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  bool barrierDismissible\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  String barrierLabel\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  Color barrierColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  Duration transitionDuration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  RouteTransitionsBuilder transitionBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" rootNavigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"push\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_DialogRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    pageBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" pageBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    barrierDismissible\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" barrierDismissible\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    barrierLabel\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" barrierLabel\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    barrierColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" barrierColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    transitionDuration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" transitionDuration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    transitionBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" transitionBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"实现很简单，直接调用\"),n(\"code\",[t._v(\"Navigator\")]),t._v(\"的\"),n(\"code\",[t._v(\"push\")]),t._v(\"方法打开了一个新的对话框路由\"),n(\"code\",[t._v(\"_DialogRoute\")]),t._v(\"，然后返回了\"),n(\"code\",[t._v(\"push\")]),t._v(\"的返回值。可见对话框实际上正是通过路由的形式实现的，这也是为什么我们可以使用\"),n(\"code\",[t._v(\"Navigator\")]),t._v(\"的\"),n(\"code\",[t._v(\"pop\")]),t._v(\" 方法来退出对话框的原因。关于对话框的样式定制在\"),n(\"code\",[t._v(\"_DialogRoute\")]),t._v(\"中，没有什么新的东西，读者可以自行查看。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_7-6-4-对话框状态管理\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_7-6-4-对话框状态管理\"}},[t._v(\"#\")]),t._v(\" 7.6.4 对话框状态管理\")]),t._v(\" \"),n(\"p\",[t._v(\"我们在用户选择删除一个文件时，会询问是否删除此文件；在用户选择一个文件夹是，应该再让用户确认是否删除子文件夹。为了在用户选择了文件夹时避免二次弹窗确认是否删除子目录，我们在确认对话框底部添加一个“同时删除子目录？”的复选框，如图7-14所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(499),alt:\"图7-14\"}})]),t._v(\" \"),n(\"p\",[t._v(\"现在就有一个问题：如何管理复选框的选中状态？习惯上，我们会在路由页的State中来管理选中状态，我们可能会写出如下这样的代码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_DialogRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"DialogRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  bool withTree \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 复选框选中状态\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"对话框2\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            bool delete \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showDeleteConfirmDialog2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"delete \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"取消删除\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"同时删除子目录: $delete\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"bool\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showDeleteConfirmDialog2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    withTree \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 默认复选框不选中\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" showDialog\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"bool\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlertDialog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"提示\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          content\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            crossAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" CrossAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            mainAxisSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"min\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"您确定要删除当前文件吗?\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"同时删除子目录？\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Checkbox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" withTree\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    onChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"bool value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//复选框选中状态发生变化时重新构建UI\")]),t._v(\"\\n                      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                        \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//更新复选框状态\")]),t._v(\"\\n                        withTree \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"withTree\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          actions\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"取消\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"删除\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//执行删除操作\")]),t._v(\"\\n                Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"withTree\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"然后，当我们运行上面的代码时我们会发现复选框根本选不中！为什么会这样呢？其实原因很简单，我们知道\"),n(\"code\",[t._v(\"setState\")]),t._v(\"方法只会针对当前context的子树重新build，但是我们的对话框并不是在\"),n(\"code\",[t._v(\"_DialogRouteState\")]),t._v(\"的\"),n(\"code\",[t._v(\"build\")]),t._v(\" 方法中构建的，而是通过\"),n(\"code\",[t._v(\"showDialog\")]),t._v(\"单独构建的，所以在\"),n(\"code\",[t._v(\"_DialogRouteState\")]),t._v(\"的context中调用\"),n(\"code\",[t._v(\"setState\")]),t._v(\"是无法影响通过\"),n(\"code\",[t._v(\"showDialog\")]),t._v(\"构建的UI的。另外，我们可以从另外一个角度来理解这个现象，前面说过对话框也是通过路由的方式来实现的，那么上面的代码实际上就等同于企图在父路由中调用\"),n(\"code\",[t._v(\"setState\")]),t._v(\"来让子路由更新，这显然是不行的！简尔言之，根本原因就是context不对。那如何让复选框可点击呢？通常有如下三种方法：\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"单独抽离出statefulwidget\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#单独抽离出statefulwidget\"}},[t._v(\"#\")]),t._v(\" 单独抽离出StatefulWidget\")]),t._v(\" \"),n(\"p\",[t._v(\"既然是context不对，那么直接的思路就是将复选框的选中逻辑单独封装成一个\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"，然后在其内部管理复选状态。我们先来看看这种方法，下面是实现代码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 单独封装一个内部管理选中状态的复选框组件\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"DialogCheckbox\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DialogCheckbox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" ValueChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"bool\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" onChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" bool value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _DialogCheckboxState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_DialogCheckboxState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_DialogCheckboxState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"DialogCheckbox\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  bool value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    value \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Checkbox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      onChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"v\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//将选中状态通过事件的形式抛出\")]),t._v(\"\\n        widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onChanged\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"v\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//更新自身选中状态\")]),t._v(\"\\n          value \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" v\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"下面是弹出对话框的代码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"bool\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showDeleteConfirmDialog3\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  bool _withTree \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//记录复选框是否选中\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" showDialog\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"bool\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlertDialog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"提示\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        content\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          crossAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" CrossAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          mainAxisSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"min\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"您确定要删除当前文件吗?\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"同时删除子目录？\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DialogCheckbox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _withTree\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//默认不选中\")]),t._v(\"\\n                  onChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"bool value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//更新选中状态\")]),t._v(\"\\n                    _withTree \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"_withTree\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        actions\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"取消\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"删除\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 将选中状态返回\")]),t._v(\"\\n              Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_withTree\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"最后，就是使用：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"话框3（复选框可点击）\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//弹出删除确认对话框，等待用户确认\")]),t._v(\"\\n    bool deleteTree \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showDeleteConfirmDialog3\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"deleteTree \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"取消删除\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"同时删除子目录: $deleteTree\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行后效果如图7-15所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(500),alt:\"图7-15\"}})]),t._v(\" \"),n(\"p\",[t._v(\"可见复选框能选中了，点击“取消”或“删除”后，控制台就会打印出最终的确认状态。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"使用statefulbuilder方法\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#使用statefulbuilder方法\"}},[t._v(\"#\")]),t._v(\" 使用StatefulBuilder方法\")]),t._v(\" \"),n(\"p\",[t._v(\"上面的方法虽然能解决对话框状态更新的问题，但是有一个明显的缺点——对话框上所有可能会改变状态的组件都得单独封装在一个在内部管理状态的\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"中，这样不仅麻烦，而且复用性不大。因此，我们来想想能不能找到一种更简单的方法？上面的方法本质上就是将对话框的状态置于一个\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"的上下文中，由\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"在内部管理，那么我们有没有办法在不需要单独抽离组件的情况下创建一个\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"的上下文呢？想到这里，我们可以从\"),n(\"code\",[t._v(\"Builder\")]),t._v(\"组件的实现获得灵感。在前面介绍过\"),n(\"code\",[t._v(\"Builder\")]),t._v(\"组件可以获得组件所在位置的真正的Context，那它是怎么实现的呢，我们看看它的源码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Builder\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n       \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" WidgetBuilder builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"可以看到，\"),n(\"code\",[t._v(\"Builder\")]),t._v(\"实际上只是继承了\"),n(\"code\",[t._v(\"StatelessWidget\")]),t._v(\"，然后在\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法中获取当前context后将构建方法代理到了\"),n(\"code\",[t._v(\"builder\")]),t._v(\"回调，可见，\"),n(\"code\",[t._v(\"Builder\")]),t._v(\"实际上是获取了\"),n(\"code\",[t._v(\"StatelessWidget\")]),t._v(\" 的上下文（context）。那么我们能否用相同的方法获取\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\" 的上下文，并代理其\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法呢？下面我们照猫画虎，来封装一个\"),n(\"code\",[t._v(\"StatefulBuilder\")]),t._v(\"方法：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulBuilder\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"StatefulBuilder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n       \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" StatefulWidgetBuilder builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _StatefulBuilderState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_StatefulBuilderState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_StatefulBuilderState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"StatefulBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" setState\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"代码很简单，\"),n(\"code\",[t._v(\"StatefulBuilder\")]),t._v(\"获取了\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"的上下文，并代理了其构建过程。下面我们就可以通过\"),n(\"code\",[t._v(\"StatefulBuilder\")]),t._v(\"来重构上面的代码了（变动只在\"),n(\"code\",[t._v(\"DialogCheckbox\")]),t._v(\"部分）：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"同时删除子目录？\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//使用StatefulBuilder来构建StatefulWidget上下文\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"StatefulBuilder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" _setState\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Checkbox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _withTree\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//默认不选中\")]),t._v(\"\\n          onChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"bool value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//_setState方法实际就是该StatefulWidget的setState方法，\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//调用后builder方法会重新被调用\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//更新选中状态\")]),t._v(\"\\n              _withTree \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"_withTree\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"实际上，这种方法本质上就是子组件通知父组件（StatefulWidget）重新build子组件本身来实现UI更新的，读者可以对比代码理解。实际上\"),n(\"code\",[t._v(\"StatefulBuilder\")]),t._v(\"正是Flutter SDK中提供的一个类，它和\"),n(\"code\",[t._v(\"Builder\")]),t._v(\"的原理是一样的，在此，提醒读者一定要将\"),n(\"code\",[t._v(\"StatefulBuilder\")]),t._v(\"和\"),n(\"code\",[t._v(\"Builder\")]),t._v(\"理解透彻，因为它们在Flutter中是非常实用的。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"精妙的解法\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#精妙的解法\"}},[t._v(\"#\")]),t._v(\" 精妙的解法\")]),t._v(\" \"),n(\"p\",[t._v(\"是否还有更简单的解决方案呢？要确认这个问题，我们就得先搞清楚UI是怎么更新的，我们知道在调用\"),n(\"code\",[t._v(\"setState\")]),t._v(\"方法后\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"就会重新build，那\"),n(\"code\",[t._v(\"setState\")]),t._v(\"方法做了什么呢？我们能不能从中找到方法？顺着这个思路，我们就得看一下\"),n(\"code\",[t._v(\"setState\")]),t._v(\"的核心源码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"VoidCallback fn\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n  _element\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"markNeedsBuild\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"可以发现，\"),n(\"code\",[t._v(\"setState\")]),t._v(\"中调用了\"),n(\"code\",[t._v(\"Element\")]),t._v(\"的\"),n(\"code\",[t._v(\"markNeedsBuild()\")]),t._v(\"方法，我们前面说过，Flutter是一个响应式框架，要更新UI只需改变状态后通知框架页面需要重构即可，而\"),n(\"code\",[t._v(\"Element\")]),t._v(\"的\"),n(\"code\",[t._v(\"markNeedsBuild()\")]),t._v(\"方法正是来实现这个功能的！\"),n(\"code\",[t._v(\"markNeedsBuild()\")]),t._v(\"方法会将当前的\"),n(\"code\",[t._v(\"Element\")]),t._v(\"对象标记为“dirty”（脏的），在每一个Frame，Flutter都会重新构建被标记为“dirty”\"),n(\"code\",[t._v(\"Element\")]),t._v(\"对象。既然如此，我们有没有办法获取到对话框内部UI的\"),n(\"code\",[t._v(\"Element\")]),t._v(\"对象，然后将其标示为为“dirty”呢？答案是肯定的！我们可以通过Context来得到\"),n(\"code\",[t._v(\"Element\")]),t._v(\"对象，至于\"),n(\"code\",[t._v(\"Element\")]),t._v(\"与\"),n(\"code\",[t._v(\"Context\")]),t._v(\"的关系我们将会在后面“Flutter核心原理”一章中再深入介绍，现在只需要简单的认为：在组件树中，\"),n(\"code\",[t._v(\"context\")]),t._v(\"实际上就是\"),n(\"code\",[t._v(\"Element\")]),t._v(\"对象的引用。知道这个后，那么解决的方案就呼之欲出了，我们可以通过如下方式来让复选框可以更新：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"bool\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showDeleteConfirmDialog4\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  bool _withTree \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" showDialog\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"bool\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlertDialog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"提示\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        content\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          crossAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" CrossAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          mainAxisSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"min\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"您确定要删除当前文件吗?\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"同时删除子目录？\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Checkbox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 依然使用Checkbox组件\")]),t._v(\"\\n                  value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _withTree\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  onChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"bool value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 此时context为对话框UI的根Element，我们 \")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 直接将对话框UI对应的Element标记为dirty\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"as\")]),t._v(\" Element\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"markNeedsBuild\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                    _withTree \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"_withTree\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        actions\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"取消\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"删除\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 执行删除操作\")]),t._v(\"\\n              Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_withTree\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上面的代码运行后复选框也可以正常选中。可以看到，我们只用了一行代码便解决了这个问题！当然上面的代码并不是最优，因为我们只需要更新复选框的状态，而此时的\"),n(\"code\",[t._v(\"context\")]),t._v(\"我们用的是对话框的根\"),n(\"code\",[t._v(\"context\")]),t._v(\"，所以会导致整个对话框UI组件全部rebuild，因此最好的做法是将\"),n(\"code\",[t._v(\"context\")]),t._v(\"的“范围”缩小，也就是说只将\"),n(\"code\",[t._v(\"Checkbox\")]),t._v(\"的Element标记为\"),n(\"code\",[t._v(\"dirty\")]),t._v(\"，优化后的代码为：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"同时删除子目录？\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 通过Builder来获得构建Checkbox的`context`，\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 这是一种常用的缩小`context`范围的方式\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Checkbox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _withTree\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          onChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"bool value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"as\")]),t._v(\" Element\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"markNeedsBuild\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            _withTree \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"_withTree\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),n(\"h2\",{attrs:{id:\"_7-6-5-其它类型的对话框\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_7-6-5-其它类型的对话框\"}},[t._v(\"#\")]),t._v(\" 7.6.5 其它类型的对话框\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"底部菜单列表\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#底部菜单列表\"}},[t._v(\"#\")]),t._v(\" 底部菜单列表\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"showModalBottomSheet\")]),t._v(\"方法可以弹出一个Material风格的底部菜单列表模态对话框，示例如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 弹出底部菜单列表模态对话框\")]),t._v(\"\\nFuture\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"int\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_showModalBottomSheet\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" showModalBottomSheet\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"int\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" ListView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        itemCount\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"30\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        itemBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$index\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onTap\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"点击按钮，弹出该对话框：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"显示底部菜单列表\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    int type \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_showModalBottomSheet\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"type\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行后效果如图7-16所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(501),alt:\"图7-16\"}})]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"showModalBottomSheet\")]),t._v(\"的实现原理和\"),n(\"code\",[t._v(\"showGeneralDialog\")]),t._v(\"实现原理相同，都是通过路由的方式来实现的，读者可以查看源码对比。但值得一提的是还有一个\"),n(\"code\",[t._v(\"showBottomSheet\")]),t._v(\"方法，该方法会从设备底部向上弹出一个全屏的菜单列表，示例如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 返回的是一个controller\")]),t._v(\"\\nPersistentBottomSheetController\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"int\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_showBottomSheet\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" showBottomSheet\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"int\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" ListView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        itemCount\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"30\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        itemBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$index\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onTap\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// do something\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$index\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图7-17所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(502),alt:\"图7-17\"}})]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"PersistentBottomSheetController\")]),t._v(\"中包含了一些控制对话框的方法比如\"),n(\"code\",[t._v(\"close\")]),t._v(\"方法可以关闭该对话框，功能比较简单，读者可以自行查看源码。唯一需要注意的是，\"),n(\"code\",[t._v(\"showBottomSheet\")]),t._v(\"和我们上面介绍的弹出对话框的方法原理不同：\"),n(\"code\",[t._v(\"showBottomSheet\")]),t._v(\"是调用widget树顶部的\"),n(\"code\",[t._v(\"Scaffold\")]),t._v(\"组件的\"),n(\"code\",[t._v(\"ScaffoldState\")]),t._v(\"的\"),n(\"code\",[t._v(\"showBottomSheet\")]),t._v(\"同名方法实现，也就是说要调用\"),n(\"code\",[t._v(\"showBottomSheet\")]),t._v(\"方法就必须得保证父级组件中有\"),n(\"code\",[t._v(\"Scaffold\")]),t._v(\"。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"loading框\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#loading框\"}},[t._v(\"#\")]),t._v(\" Loading框\")]),t._v(\" \"),n(\"p\",[t._v(\"其实Loading框可以直接通过\"),n(\"code\",[t._v(\"showDialog\")]),t._v(\"+\"),n(\"code\",[t._v(\"AlertDialog\")]),t._v(\"来自定义：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showLoadingDialog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showDialog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    barrierDismissible\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//点击遮罩不关闭对话框\")]),t._v(\"\\n    builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlertDialog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        content\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          mainAxisSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"min\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircularProgressIndicator\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"only\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"top\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"26.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"正在加载，请稍后...\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"显示效果如图7-18所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(503),alt:\"图7-18\"}})]),t._v(\" \"),n(\"p\",[t._v(\"如果我们嫌Loading框太宽，想自定义对话框宽度，这时只使用\"),n(\"code\",[t._v(\"SizedBox\")]),t._v(\"或\"),n(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"是不行的，原因是\"),n(\"code\",[t._v(\"showDialog\")]),t._v(\"中已经给对话框设置了宽度限制，根据我们在第五章“尺寸限制类容器”一节中所述，我们可以使用\"),n(\"code\",[t._v(\"UnconstrainedBox\")]),t._v(\"先抵消\"),n(\"code\",[t._v(\"showDialog\")]),t._v(\"对宽度的限制，然后再使用\"),n(\"code\",[t._v(\"SizedBox\")]),t._v(\"指定宽度，代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"UnconstrainedBox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  constrainedAxis\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Axis\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"vertical\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"280\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlertDialog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      content\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        mainAxisSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"min\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircularProgressIndicator\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".8\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"only\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"top\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"26.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"正在加载，请稍后...\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"代码运行后，效果如图7-19所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(504),alt:\"图7-19\"}})]),t._v(\" \"),n(\"h3\",{attrs:{id:\"日历选择\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#日历选择\"}},[t._v(\"#\")]),t._v(\" 日历选择\")]),t._v(\" \"),n(\"p\",[t._v(\"我们先看一下Material风格的日历选择器，如图7-20所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(505),alt:\"图7-20\"}})]),t._v(\" \"),n(\"p\",[t._v(\"实现代码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"DateTime\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_showDatePicker1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" date \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" DateTime\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"now\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showDatePicker\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    initialDate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" date\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    firstDate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" date\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    lastDate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" date\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//未来30天可选\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"days\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"30\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"iOS风格的日历选择器需要使用\"),n(\"code\",[t._v(\"showCupertinoModalPopup\")]),t._v(\"方法和\"),n(\"code\",[t._v(\"CupertinoDatePicker\")]),t._v(\"组件来实现：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"DateTime\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_showDatePicker2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" date \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" DateTime\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"now\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showCupertinoModalPopup\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ctx\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CupertinoDatePicker\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          mode\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" CupertinoDatePickerMode\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dateAndTime\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          minimumDate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" date\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          maximumDate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" date\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"days\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"30\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          maximumYear\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" date\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"year \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          onDateTimeChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"DateTime value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图7-21所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(506),alt:\"图7-21\"}})])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/30.b84a8f21.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[30],{512:function(t,a){t.exports=\"data:image/png;base64,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xsZG22+//WzBggWm/axfv960jyFDhlhTU1N4HQWJww47jKBQUZoNCMQlQFCIq74oLQI/SmDr1q3hpP7dd99VfL5aEtJFAeCbb76xESNGhKCgboxly5aF4HDJJZeElge1Kjz55JPW0tJi559/vg0fPjw8fdy4cTZgwID0pbhFAIHIBQgKkVcgxUdAAvoUP2fOHPv4449t7dq1Nm/evHACf+yxx2zGjBl2xBFH2NVXXx3GLVQSS1sEKm3X+sGDB9ukSZPsvffeCy0MCh56nropGhoabPz48XbwwQdb3759d/cybEMAgYgECAoRVRZFRaCSgD7Vv/vuu3brrbeGLgGNSVALwb333mtJkthtt91mJ510UqWn97he3Qynn3566EJQl8XSpUvD4+bPn2833XST9enTJwSELVu2hJYFdUGcccYZNnHiRNtnn316fE1WIoBAfAIEhfjqjBIjsIuAxhacfPLJ4VP922+/bS+88EIYS3D22WeHVgCNUdAJfk/Ldddd1/kQjUt47bXXwrgEhYENGzaEbWq90P1TTjklhAU9RiHkq6++CsGhaxdG54txBwEEohUgKERbdRQcgR0C+vR/9NFHh+mQ+uSvgYbqDlC3wAknnGD33Xefvf/++zueUOHetddeG8YkXHDBBTZ16tSdHqXWBQ1u1Gtqf6NHjw5dDGo9mDx5cmeQ2OlJ/IIAAtELEBSir0IOAAELgwvVxbB69eow40FBQZ/6NUZh4MCBdt5559m5554bqGbPnm0PPvignXnmmaEloOvYhEKhELop1PrQU8uAXj+dGqkBjury0HgEBi/yLkSgdgUICrVbtxxZnQnopD1r1qzOloNDDjkkCLzyyit21FFHhYGGagnQdMennnrKjjzySNM1DzTD4ZhjjgndFrpOgrop1CqhoKBWiUWLFoUZDfvvv394vXSKpbom9BgFEQ1yZEEAgdoUICjUZr1yVHUooOmMb775ZggCmtao2Qe68JFO4goIN998cwgL6YWVtO6JJ54I4xnuv/9+O/zww4Pa9ddfby+99JI988wzIUBoBsX06dPt4osvDtvfeecdu+GGG0JrgsYraHwEsxzq8A3HIdeNAEGhbqqaA61lAU1T1Il95syZYaaCPvWrK+LUU08N4weee+45+/DDD8OJX7MV0kUtDXfddZe9+uqr4XFqbTjooINMXRBffPGFTZs2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\"},513:function(t,a,s){t.exports=s.p+\"assets/img/7-5.6f1c5012.jpeg\"},514:function(t,a,s){t.exports=s.p+\"assets/img/7-6.3bce9f21.png\"},515:function(t,a,s){t.exports=s.p+\"assets/img/7-7.b9c5d9fe.png\"},809:function(t,a,s){\"use strict\";s.r(a);var n=s(45),e=Object(n.a)({},(function(){var t=this,a=t.$createElement,n=t._self._c||a;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_7-4-颜色和主题\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_7-4-颜色和主题\"}},[t._v(\"#\")]),t._v(\" 7.4 颜色和主题\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_7-4-1-颜色\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_7-4-1-颜色\"}},[t._v(\"#\")]),t._v(\" 7.4.1 颜色\")]),t._v(\" \"),n(\"p\",[t._v(\"在介绍主题前我们先了解一些Flutter中的Color类。Color类中颜色以一个int值保存，我们知道显示器颜色是由红、绿、蓝三基色组成，每种颜色占8比特，存储结构如下：\")]),t._v(\" \"),n(\"table\",[n(\"thead\",[n(\"tr\",[n(\"th\",[t._v(\"Bit（位）\")]),t._v(\" \"),n(\"th\",[t._v(\"颜色\")])])]),t._v(\" \"),n(\"tbody\",[n(\"tr\",[n(\"td\",[t._v(\"0-7\")]),t._v(\" \"),n(\"td\",[t._v(\"蓝色\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"8-15\")]),t._v(\" \"),n(\"td\",[t._v(\"绿色\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"16-23\")]),t._v(\" \"),n(\"td\",[t._v(\"红色\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"24-31\")]),t._v(\" \"),n(\"td\",[t._v(\"Alpha (不透明度)\")])])])]),t._v(\" \"),n(\"p\",[t._v(\"上面表格中的的字段在Color类中都有对应的属性，而Color中的众多方法也就是操作这些属性的，由于大多比较简单，读者可以查看类定义了解。在此我们主要讨论两点：色值转换和亮度。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"如何将颜色字符串转成color对象\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#如何将颜色字符串转成color对象\"}},[t._v(\"#\")]),t._v(\" \"),n(\"strong\",[t._v(\"如何将颜色字符串转成Color对象\")])]),t._v(\" \"),n(\"p\",[t._v('如Web开发中的色值通常是一个字符串如\"#dc380d\"，它是一个RGB值，我们可以通过下面这些方法将其转为Color类：')]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Color\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0xffdc380d\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//如果颜色固定可以直接使用整数值\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//颜色是一个字符串变量\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" c \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"dc380d\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Color\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"parse\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"c\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"radix\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"|\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0xFF000000\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//通过位运算符将Alpha设置为FF\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Color\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"parse\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"c\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"radix\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"withAlpha\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"255\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//通过方法将Alpha设置为FF\")]),t._v(\"\\n\")])])]),n(\"h3\",{attrs:{id:\"颜色亮度\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#颜色亮度\"}},[t._v(\"#\")]),t._v(\" 颜色亮度\")]),t._v(\" \"),n(\"p\",[t._v(\"假如，我们要实现一个背景颜色和Title可以自定义的导航栏，并且背景色为深色时我们应该让Title显示为浅色；背景色为浅色时，Title显示为深色。要实现这个功能，我们就需要来计算背景色的亮度，然后动态来确定Title的颜色。Color类中提供了一个\"),n(\"code\",[t._v(\"computeLuminance()\")]),t._v(\"方法，它可以返回一个[0-1]的一个值，数字越大颜色就越浅，我们可以根据它来动态确定Title的颜色，下面是导航栏NavBar的简单实现：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"NavBar\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Color color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//背景颜色\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"NavBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      constraints\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxConstraints\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        minHeight\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"52\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        minWidth\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" double\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"infinity\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      decoration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        boxShadow\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//阴影\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxShadow\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"black26\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            blurRadius\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          fontWeight\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" FontWeight\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"bold\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//根据背景色亮度来确定Title颜色\")]),t._v(\"\\n          color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"computeLuminance\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.5\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"black\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"测试代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//背景为蓝色，则title自动为白色\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"NavBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"标题\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//背景为白色，则title自动为黑色\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"NavBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"标题\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图7-4所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(512),alt:\"NavBar\"}})]),t._v(\" \"),n(\"h3\",{attrs:{id:\"materialcolor\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#materialcolor\"}},[t._v(\"#\")]),t._v(\" MaterialColor\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"MaterialColor\")]),t._v(\"是实现Material Design中的颜色的类，它包含一种颜色的10个级别的渐变色。\"),n(\"code\",[t._v(\"MaterialColor\")]),t._v('通过\"[]\"运算符的索引值来代表颜色的深度，有效的索引有：50，100，200，…，900，数字越大，颜色越深。'),n(\"code\",[t._v(\"MaterialColor\")]),t._v(\"的默认值为索引等于500的颜色。举个例子，\"),n(\"code\",[t._v(\"Colors.blue\")]),t._v(\"是预定义的一个\"),n(\"code\",[t._v(\"MaterialColor\")]),t._v(\"类对象，定义如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" MaterialColor blue \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MaterialColor\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  _bluePrimaryValue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"int\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Color\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Color\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0xFFE3F2FD\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Color\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0xFFBBDEFB\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Color\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0xFF90CAF9\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Color\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0xFF64B5F6\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"400\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Color\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0xFF42A5F5\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"500\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Color\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_bluePrimaryValue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"600\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Color\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0xFF1E88E5\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"700\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Color\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0xFF1976D2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"800\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Color\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0xFF1565C0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"900\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Color\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0xFF0D47A1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" int _bluePrimaryValue \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0xFF2196F3\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"Colors.blue[50]\")]),t._v(\"到\"),n(\"code\",[t._v(\"Colors.blue[900]\")]),t._v(\"的色值从浅蓝到深蓝渐变，效果如图7-5所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(513),alt:\"NavBar\"}})]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_7-4-2-theme\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_7-4-2-theme\"}},[t._v(\"#\")]),t._v(\" 7.4.2 Theme\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"Theme\")]),t._v(\"组件可以为Material APP定义主题数据（ThemeData）。Material组件库里很多组件都使用了主题数据，如导航栏颜色、标题字体、Icon样式等。\"),n(\"code\",[t._v(\"Theme\")]),t._v(\"内会使用\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"来为其子树共享样式数据。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"themedata\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#themedata\"}},[t._v(\"#\")]),t._v(\" ThemeData\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"ThemeData\")]),t._v(\"用于保存是Material 组件库的主题数据，Material组件需要遵守相应的设计规范，而这些规范可自定义部分都定义在ThemeData中了，所以我们可以通过ThemeData来自定义应用主题。在子组件中，我们可以通过\"),n(\"code\",[t._v(\"Theme.of\")]),t._v(\"方法来获取当前的\"),n(\"code\",[t._v(\"ThemeData\")]),t._v(\"。\")]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[t._v(\"注意：Material Design 设计规范中有些是不能自定义的，如导航栏高度，ThemeData只包含了可自定义部分。\")])]),t._v(\" \"),n(\"p\",[t._v(\"我们看看\"),n(\"code\",[t._v(\"ThemeData\")]),t._v(\"部分数据定义：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ThemeData\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  Brightness brightness\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//深色还是浅色\")]),t._v(\"\\n  MaterialColor primarySwatch\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//主题颜色样本，见下面介绍\")]),t._v(\"\\n  Color primaryColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//主色，决定导航栏颜色\")]),t._v(\"\\n  Color accentColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//次级色，决定大多数Widget的颜色，如进度条、开关等。\")]),t._v(\"\\n  Color cardColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//卡片颜色\")]),t._v(\"\\n  Color dividerColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//分割线颜色\")]),t._v(\"\\n  ButtonThemeData buttonTheme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//按钮主题\")]),t._v(\"\\n  Color cursorColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//输入框光标颜色\")]),t._v(\"\\n  Color dialogBackgroundColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//对话框背景颜色\")]),t._v(\"\\n  String fontFamily\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//文字字体\")]),t._v(\"\\n  TextTheme textTheme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 字体主题，包括标题、body等文字样式\")]),t._v(\"\\n  IconThemeData iconTheme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// Icon的默认样式\")]),t._v(\"\\n  TargetPlatform platform\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//指定平台，应用特定平台控件风格\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上面只是\"),n(\"code\",[t._v(\"ThemeData\")]),t._v(\"的一小部分属性，完整的数据定义读者可以查看SDK。上面属性中需要说明的是\"),n(\"code\",[t._v(\"primarySwatch\")]),t._v('，它是主题颜色的一个\"样本色\"，通过这个样本色可以在一些条件下生成一些其它的属性，例如，如果没有指定'),n(\"code\",[t._v(\"primaryColor\")]),t._v(\"，并且当前主题不是深色主题，那么\"),n(\"code\",[t._v(\"primaryColor\")]),t._v(\"就会默认为\"),n(\"code\",[t._v(\"primarySwatch\")]),t._v(\"指定的颜色，还有一些相似的属性如\"),n(\"code\",[t._v(\"accentColor\")]),t._v(\" 、\"),n(\"code\",[t._v(\"indicatorColor\")]),t._v(\"等也会受\"),n(\"code\",[t._v(\"primarySwatch\")]),t._v(\"影响。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"示例\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),n(\"p\",[t._v(\"我们实现一个路由换肤功能：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ThemeTestRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _ThemeTestRouteState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_ThemeTestRouteState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_ThemeTestRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ThemeTestRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  Color _themeColor \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"teal\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//当前路由主题色\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    ThemeData themeData \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Theme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Theme\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      data\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ThemeData\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          primarySwatch\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _themeColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//用于导航栏、FloatingActionButton的背景色等\")]),t._v(\"\\n          iconTheme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"IconThemeData\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _themeColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//用于Icon颜色\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        appBar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"主题测试\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        body\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          mainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//第一行Icon使用主题中的iconTheme\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                mainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"favorite\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"airport_shuttle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"  颜色跟随主题\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//为第二行Icon自定义颜色（固定为黑色)\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Theme\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              data\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" themeData\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"copyWith\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                iconTheme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" themeData\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"iconTheme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"copyWith\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"black\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  mainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"favorite\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"airport_shuttle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"  颜色固定黑色\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        floatingActionButton\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FloatingActionButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//切换主题\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"\\n                _themeColor \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"\\n                _themeColor \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"teal \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"teal\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"palette\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行后点击右下角悬浮按钮则可以切换主题，如图7-6、7-7所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(514),alt:\"图7-6\"}}),n(\"img\",{attrs:{src:s(515),alt:\"图7-7\"}})]),t._v(\" \"),n(\"p\",[t._v(\"需要注意的有三点：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"p\",[t._v(\"可以通过局部主题覆盖全局主题，正如代码中通过Theme为第二行图标指定固定颜色（黑色）一样，这是一种常用的技巧，Flutter中会经常使用这种方法来自定义子树主题。那么为什么局部主题可以覆盖全局主题？这主要是因为widget中使用主题样式时是通过\"),n(\"code\",[t._v(\"Theme.of(BuildContext context)\")]),t._v(\"来获取的，我们看看其简化后的代码：\")])]),t._v(\" \"),n(\"li\",[n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" ThemeData \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" bool shadowThemeOnly \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 简化代码，并非源码  \")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dependOnInheritedWidgetOfExactType\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"_InheritedTheme\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"theme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"context.dependOnInheritedWidgetOfExactType\")]),t._v(\" 会在widget树中从当前位置向上查找第一个类型为\"),n(\"code\",[t._v(\"_InheritedTheme\")]),t._v(\"的widget。所以当局部指定\"),n(\"code\",[t._v(\"Theme\")]),t._v(\"后，其子树中通过\"),n(\"code\",[t._v(\"Theme.of()\")]),t._v(\"向上查找到的第一个\"),n(\"code\",[t._v(\"_InheritedTheme\")]),t._v(\"便是我们指定的\"),n(\"code\",[t._v(\"Theme\")]),t._v(\"。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"本示例是对单个路由换肤，如果想要对整个应用换肤，则可以去修改\"),n(\"code\",[t._v(\"MaterialApp\")]),t._v(\"的\"),n(\"code\",[t._v(\"theme\")]),t._v(\"属性。\")])])])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/31.dca209c1.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[31],{516:function(t,s,a){t.exports=a.p+\"assets/img/8-2.52bd91bd.png\"},517:function(t,s,a){t.exports=a.p+\"assets/img/8-3.975afea6.png\"},518:function(t,s,a){t.exports=a.p+\"assets/img/8-4.6948ddf4.png\"},519:function(t,s,a){t.exports=a.p+\"assets/img/8-5.ba487ffd.png\"},812:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_8-2-手势识别\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_8-2-手势识别\"}},[t._v(\"#\")]),t._v(\" 8.2 手势识别\")]),t._v(\" \"),n(\"p\",[t._v(\"本节先介绍一些Flutter中用于处理手势的\"),n(\"code\",[t._v(\"GestureDetector\")]),t._v(\"和\"),n(\"code\",[t._v(\"GestureRecognizer\")]),t._v(\"，然后再仔细讨论一下手势竞争与冲突问题。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_8-2-1-gesturedetector\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_8-2-1-gesturedetector\"}},[t._v(\"#\")]),t._v(\" 8.2.1 GestureDetector\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"GestureDetector\")]),t._v(\"是一个用于手势识别的功能性组件，我们通过它可以来识别各种手势。\"),n(\"code\",[t._v(\"GestureDetector\")]),t._v(\"实际上是指针事件的语义化封装，接下来我们详细介绍一下各种手势识别。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"点击、双击、长按\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#点击、双击、长按\"}},[t._v(\"#\")]),t._v(\" 点击、双击、长按\")]),t._v(\" \"),n(\"p\",[t._v(\"我们通过\"),n(\"code\",[t._v(\"GestureDetector\")]),t._v(\"对\"),n(\"code\",[t._v(\"Container\")]),t._v(\"进行手势识别，触发相应事件后，在\"),n(\"code\",[t._v(\"Container\")]),t._v(\"上显示事件名，为了增大点击区域，将\"),n(\"code\",[t._v(\"Container\")]),t._v(\"设置为200×100，代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"GestureDetectorTestRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _GestureDetectorTestRouteState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_GestureDetectorTestRouteState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_GestureDetectorTestRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"GestureDetectorTestRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  String _operation \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"No Gesture detected!\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//保存事件名\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GestureDetector\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n          height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_operation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        onTap\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"updateText\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Tap\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//点击\")]),t._v(\"\\n        onDoubleTap\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"updateText\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"DoubleTap\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//双击\")]),t._v(\"\\n        onLongPress\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"updateText\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"LongPress\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//长按\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"updateText\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//更新显示的事件名\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _operation \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图8-2所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(516),alt:\"图8-2\"}})]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[n(\"strong\",[t._v(\"注意\")]),t._v(\"： 当同时监听\"),n(\"code\",[t._v(\"onTap\")]),t._v(\"和\"),n(\"code\",[t._v(\"onDoubleTap\")]),t._v(\"事件时，当用户触发tap事件时，会有200毫秒左右的延时，这是因为当用户点击完之后很可能会再次点击以触发双击事件，所以\"),n(\"code\",[t._v(\"GestureDetector\")]),t._v(\"会等一段时间来确定是否为双击事件。如果用户只监听了\"),n(\"code\",[t._v(\"onTap\")]),t._v(\"（没有监听\"),n(\"code\",[t._v(\"onDoubleTap\")]),t._v(\"）事件时，则没有延时。\")])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"拖动、滑动\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#拖动、滑动\"}},[t._v(\"#\")]),t._v(\" 拖动、滑动\")]),t._v(\" \"),n(\"p\",[t._v(\"一次完整的手势过程是指用户手指按下到抬起的整个过程，期间，用户按下手指后可能会移动，也可能不会移动。\"),n(\"code\",[t._v(\"GestureDetector\")]),t._v(\"对于拖动和滑动事件是没有区分的，他们本质上是一样的。\"),n(\"code\",[t._v(\"GestureDetector\")]),t._v(\"会将要监听的组件的原点（左上角）作为本次手势的原点，当用户在监听的组件上按下手指时，手势识别就会开始。下面我们看一个拖动圆形字母A的示例：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_Drag\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _DragState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_DragState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_DragState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"_Drag\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"with\")]),t._v(\" SingleTickerProviderStateMixin \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  double _top \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//距顶部的偏移\")]),t._v(\"\\n  double _left \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//距左边的偏移\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Stack\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Positioned\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          top\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _top\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          left\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _left\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GestureDetector\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircleAvatar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"A\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//手指按下时会触发此回调\")]),t._v(\"\\n            onPanDown\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"DragDownDetails e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//打印手指按下的位置(相对于屏幕)\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"用户手指按下：${e.globalPosition}\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//手指滑动时会触发此回调\")]),t._v(\"\\n            onPanUpdate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"DragUpdateDetails e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//用户手指滑动时，更新偏移，重新构建\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                _left \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+=\")]),t._v(\" e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"delta\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dx\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                _top \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+=\")]),t._v(\" e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"delta\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dy\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onPanEnd\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"DragEndDetails e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//打印滑动结束时在x、y轴上的速度\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"velocity\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行后，就可以在任意方向拖动了，运行效果如图8-3所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(517),alt:\"图8-3\"}})]),t._v(\" \"),n(\"p\",[t._v(\"日志：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language- extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[n(\"code\",[t._v(\"I/flutter ( 8513): 用户手指按下：Offset(26.3, 101.8)\\nI/flutter ( 8513): Velocity(235.5, 125.8)\\n\")])])]),n(\"p\",[t._v(\"代码解释：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"DragDownDetails.globalPosition\")]),t._v(\"：当用户按下时，此属性为用户按下的位置相对于\"),n(\"strong\",[t._v(\"屏幕\")]),t._v(\"（而非父组件）原点(左上角)的偏移。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"DragUpdateDetails.delta\")]),t._v(\"：当用户在屏幕上滑动时，会触发多次Update事件，\"),n(\"code\",[t._v(\"delta\")]),t._v(\"指一次Update事件的滑动的偏移量。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"DragEndDetails.velocity\")]),t._v(\"：该属性代表用户抬起手指时的滑动速度(包含x、y两个轴的），示例中并没有处理手指抬起时的速度，常见的效果是根据用户抬起手指时的速度做一个减速动画。\")])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"单一方向拖动\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#单一方向拖动\"}},[t._v(\"#\")]),t._v(\" 单一方向拖动\")]),t._v(\" \"),n(\"p\",[t._v(\"在本示例中，是可以朝任意方向拖动的，但是在很多场景，我们只需要沿一个方向来拖动，如一个垂直方向的列表，\"),n(\"code\",[t._v(\"GestureDetector\")]),t._v(\"可以只识别特定方向的手势事件，我们将上面的例子改为只能沿垂直方向拖动：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_DragVertical\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _DragVerticalState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_DragVerticalState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_DragVerticalState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"_DragVertical\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  double _top \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Stack\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Positioned\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          top\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _top\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GestureDetector\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircleAvatar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"A\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//垂直方向拖动事件\")]),t._v(\"\\n            onVerticalDragUpdate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"DragUpdateDetails details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                _top \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+=\")]),t._v(\" details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"delta\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dy\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"这样就只能在垂直方向拖动了，如果只想在水平方向滑动同理。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"缩放\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#缩放\"}},[t._v(\"#\")]),t._v(\" 缩放\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"GestureDetector\")]),t._v(\"可以监听缩放事件，下面示例演示了一个简单的图片缩放效果：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_ScaleTestRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"_ScaleTestRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  double _width \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//通过修改图片宽度来达到缩放效果\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n     child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GestureDetector\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//指定宽度，高度自适应\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"asset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"./images/sea.png\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        onScaleUpdate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ScaleUpdateDetails details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//缩放倍数在0.8到10倍之间\")]),t._v(\"\\n            _width\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\"details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"scale\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"clamp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".8\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"10.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图8-4所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(518),alt:\"图8-4\"}})]),t._v(\" \"),n(\"p\",[t._v(\"现在在图片上双指张开、收缩就可以放大、缩小图片。本示例比较简单，实际中我们通常还需要一些其它功能，如双击放大或缩小一定倍数、双指张开离开屏幕时执行一个减速放大动画等，读者可以在学习完后面“动画”一章中的内容后自己来尝试实现一下。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_8-2-2-gesturerecognizer\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_8-2-2-gesturerecognizer\"}},[t._v(\"#\")]),t._v(\" 8.2.2 GestureRecognizer\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"GestureDetector\")]),t._v(\"内部是使用一个或多个\"),n(\"code\",[t._v(\"GestureRecognizer\")]),t._v(\"来识别各种手势的，而\"),n(\"code\",[t._v(\"GestureRecognizer\")]),t._v(\"的作用就是通过\"),n(\"code\",[t._v(\"Listener\")]),t._v(\"来将原始指针事件转换为语义手势，\"),n(\"code\",[t._v(\"GestureDetector\")]),t._v(\"直接可以接收一个子widget。\"),n(\"code\",[t._v(\"GestureRecognizer\")]),t._v(\"是一个抽象类，一种手势的识别器对应一个\"),n(\"code\",[t._v(\"GestureRecognizer\")]),t._v(\"的子类，Flutter实现了丰富的手势识别器，我们可以直接使用。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"示例\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),n(\"p\",[t._v(\"假设我们要给一段富文本（\"),n(\"code\",[t._v(\"RichText\")]),t._v(\"）的不同部分分别添加点击事件处理器，但是\"),n(\"code\",[t._v(\"TextSpan\")]),t._v(\"并不是一个widget，这时我们不能用\"),n(\"code\",[t._v(\"GestureDetector\")]),t._v(\"，但\"),n(\"code\",[t._v(\"TextSpan\")]),t._v(\"有一个\"),n(\"code\",[t._v(\"recognizer\")]),t._v(\"属性，它可以接收一个\"),n(\"code\",[t._v(\"GestureRecognizer\")]),t._v(\"。\")]),t._v(\" \"),n(\"p\",[t._v(\"假设我们需要在点击时给文本变色:\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/gestures.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_GestureRecognizerTestRouteState\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"_GestureRecognizerTestRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  TapGestureRecognizer _tapGestureRecognizer \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TapGestureRecognizer\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  bool _toggle \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//变色开关\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//用到GestureRecognizer的话一定要调用其dispose方法释放资源\")]),t._v(\"\\n    _tapGestureRecognizer\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"rich\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextSpan\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextSpan\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"你好世界\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextSpan\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"点我变色\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                      fontSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"30.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _toggle \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  recognizer\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _tapGestureRecognizer\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onTap \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                        _toggle \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"_toggle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextSpan\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"你好世界\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(519),alt:\"图8-5\"}})]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[t._v(\"注意：使用\"),n(\"code\",[t._v(\"GestureRecognizer\")]),t._v(\"后一定要调用其\"),n(\"code\",[t._v(\"dispose()\")]),t._v(\"方法来释放资源（主要是取消内部的计时器）。\")])]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_8-2-3-手势竞争与冲突\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_8-2-3-手势竞争与冲突\"}},[t._v(\"#\")]),t._v(\" 8.2.3 手势竞争与冲突\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"竞争\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#竞争\"}},[t._v(\"#\")]),t._v(\" 竞争\")]),t._v(\" \"),n(\"p\",[t._v(\"如果在上例中我们同时监听水平和垂直方向的拖动事件，那么我们斜着拖动时哪个方向会生效？实际上取决于第一次移动时两个轴上的位移分量，哪个轴的大，哪个轴在本次滑动事件竞争中就胜出。实际上Flutter中的手势识别引入了一个Arena的概念，Arena直译为“竞技场”的意思，每一个手势识别器（\"),n(\"code\",[t._v(\"GestureRecognizer\")]),t._v(\"）都是一个“竞争者”（\"),n(\"code\",[t._v(\"GestureArenaMember\")]),t._v(\"），当发生滑动事件时，他们都要在“竞技场”去竞争本次事件的处理权，而最终只有一个“竞争者”会胜出(win)。例如，假设有一个\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"，它的第一个子组件也是\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"，如果现在滑动这个子\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"，父\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"会动吗？答案是否定的，这时只有子\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"会动，因为这时子\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"会胜出而获得滑动事件的处理权。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"示例-2\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例-2\"}},[t._v(\"#\")]),t._v(\" \"),n(\"strong\",[t._v(\"示例\")])]),t._v(\" \"),n(\"p\",[t._v(\"我们以拖动手势为例，同时识别水平和垂直方向的拖动手势，当用户按下手指时就会触发竞争（水平方向和垂直方向），一旦某个方向“获胜”，则直到当次拖动手势结束都会沿着该方向移动。代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"BothDirectionTestRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  BothDirectionTestRouteState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"BothDirectionTestRouteState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"BothDirectionTestRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"BothDirectionTestRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  double _top \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  double _left \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Stack\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Positioned\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          top\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _top\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          left\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _left\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GestureDetector\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircleAvatar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"A\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//垂直方向拖动事件\")]),t._v(\"\\n            onVerticalDragUpdate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"DragUpdateDetails details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                _top \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+=\")]),t._v(\" details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"delta\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dy\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onHorizontalDragUpdate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"DragUpdateDetails details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                _left \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+=\")]),t._v(\" details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"delta\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dx\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"此示例运行后，每次拖动只会沿一个方向移动（水平或垂直），而竞争发生在手指按下后首次移动（move）时，此例中具体的“获胜”条件是：首次移动时的位移在水平和垂直方向上的分量大的一个获胜。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"手势冲突\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#手势冲突\"}},[t._v(\"#\")]),t._v(\" 手势冲突\")]),t._v(\" \"),n(\"p\",[t._v(\"由于手势竞争最终只有一个胜出者，所以，当有多个手势识别器时，可能会产生冲突。假设有一个widget，它可以左右拖动，现在我们也想检测在它上面手指按下和抬起的事件，代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"GestureConflictTestRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"GestureConflictTestRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  double _left \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Stack\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Positioned\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          left\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _left\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GestureDetector\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircleAvatar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"A\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//要拖动和点击的widget\")]),t._v(\"\\n              onHorizontalDragUpdate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"DragUpdateDetails details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                  _left \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+=\")]),t._v(\" details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"delta\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dx\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              onHorizontalDragEnd\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"onHorizontalDragEnd\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              onTapDown\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"down\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              onTapUp\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"up\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"现在我们按住圆形“A”拖动然后抬起手指，控制台日志如下:\")]),t._v(\" \"),n(\"div\",{staticClass:\"language- extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[n(\"code\",[t._v(\"I/flutter (17539): down\\nI/flutter (17539): onHorizontalDragEnd\\n\")])])]),n(\"p\",[t._v('我们发现没有打印\"up\"，这是因为在拖动时，刚开始按下手指时在没有移动时，拖动手势还没有完整的语义，此时TapDown手势胜出(win)，此时打印\"down\"，而拖动时，拖动手势会胜出，当手指抬起时，'),n(\"code\",[t._v(\"onHorizontalDragEnd\")]),t._v(\" 和 \"),n(\"code\",[t._v(\"onTapUp\")]),t._v(\"发生了冲突，但是因为是在拖动的语义中，所以\"),n(\"code\",[t._v(\"onHorizontalDragEnd\")]),t._v(\"胜出，所以就会打印 “onHorizontalDragEnd”。如果我们的代码逻辑中，对于手指按下和抬起是强依赖的，比如在一个轮播图组件中，我们希望手指按下时，暂停轮播，而抬起时恢复轮播，但是由于轮播图组件中本身可能已经处理了拖动手势（支持手动滑动切换），甚至可能也支持了缩放手势，这时我们如果在外部再用\"),n(\"code\",[t._v(\"onTapDown\")]),t._v(\"、\"),n(\"code\",[t._v(\"onTapUp\")]),t._v(\"来监听的话是不行的。这时我们应该怎么做？其实很简单，通过Listener监听原始指针事件就行：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Positioned\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  top\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  left\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _leftB\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Listener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    onPointerDown\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"down\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    onPointerUp\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//会触发\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"up\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GestureDetector\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircleAvatar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"B\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      onHorizontalDragUpdate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"DragUpdateDetails details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          _leftB \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+=\")]),t._v(\" details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"delta\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dx\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      onHorizontalDragEnd\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"onHorizontalDragEnd\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"手势冲突只是手势级别的，而手势是对原始指针的语义化的识别，所以在遇到复杂的冲突场景时，都可以通过\"),n(\"code\",[t._v(\"Listener\")]),t._v(\"直接识别原始指针事件来解决冲突。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/32.c0efa5ac.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[32],{560:function(t,s,n){t.exports=n.p+\"assets/img/15-1.c59fd39e.png\"},561:function(t,s,n){t.exports=n.p+\"assets/img/15-2.cb77ca5a.png\"},562:function(t,s,n){t.exports=n.p+\"assets/img/15-3.28296c90.png\"},563:function(t,s,n){t.exports=n.p+\"assets/img/15-4.19779c8f.png\"},869:function(t,s,n){\"use strict\";n.r(s);var a=n(45),p=Object(a.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_15-6-app入口及主页\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_15-6-app入口及主页\"}},[t._v(\"#\")]),t._v(\" 15.6 APP入口及主页\")]),t._v(\" \"),a(\"p\",[t._v(\"本节来介绍一下APP入口及首页。\")]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_15-6-1-app入口\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_15-6-1-app入口\"}},[t._v(\"#\")]),t._v(\" 15.6.1 APP入口\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"main\")]),t._v(\"函数为APP入口函数，实现如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"main\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Global\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"init\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"runApp\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyApp\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"初始化完成后才会加载UI(\"),a(\"code\",[t._v(\"MyApp\")]),t._v(\")，\"),a(\"code\",[t._v(\"MyApp\")]),t._v(\" 是应用的入口Widget，实现如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyApp\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// This widget is the root of your application.\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MultiProvider\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      providers\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"SingleChildCloneableWidget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        ChangeNotifierProvider\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"value\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ThemeModel\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        ChangeNotifierProvider\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"value\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"UserModel\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        ChangeNotifierProvider\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"value\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"LocaleModel\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Consumer2\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ThemeModel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" LocaleModel\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        builder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" themeModel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" localeModel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Widget child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MaterialApp\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            theme\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ThemeData\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              primarySwatch\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" themeModel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"theme\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onGenerateTitle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" GmLocalizations\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"title\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            home\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"HomeRoute\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//应用主页\")]),t._v(\"\\n            locale\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" localeModel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getLocale\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//我们只支持美国英语和中文简体\")]),t._v(\"\\n            supportedLocales\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Locale\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'en'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'US'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 美国英语\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Locale\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'zh'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'CN'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 中文简体\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//其它Locales\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            localizationsDelegates\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 本地化的代理类\")]),t._v(\"\\n              GlobalMaterialLocalizations\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"delegate\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              GlobalWidgetsLocalizations\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"delegate\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GmLocalizationsDelegate\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            localeResolutionCallback\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Locale _locale\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Iterable\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Locale\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" supportedLocales\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"localeModel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getLocale\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//如果已经选定语言，则不跟随系统\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" localeModel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getLocale\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n         \\n                Locale locale\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//APP语言跟随系统语言，如果系统语言不是中文简体或美国英语，\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//则默认使用美国英语\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"supportedLocales\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"contains\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_locale\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                  locale\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _locale\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                  locale\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Locale\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'en'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'US'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" locale\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 注册命名路由表\")]),t._v(\"\\n            routes\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" WidgetBuilder\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"login\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"LoginRoute\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"themes\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ThemeChangeRoute\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"language\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"LanguageRoute\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"在上面的代码中：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"我们的根widget是\"),a(\"code\",[t._v(\"MultiProvider\")]),t._v(\"，它将主题、用户、语言三种状态绑定到了应用的根上，如此一来，任何路由中都可以通过\"),a(\"code\",[t._v(\"Provider.of()\")]),t._v(\"来获取这些状态，也就是说这三种状态是全局共享的！\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"HomeRoute\")]),t._v(\"是应用的主页。\")]),t._v(\" \"),a(\"li\",[t._v(\"在构建\"),a(\"code\",[t._v(\"MaterialApp\")]),t._v(\"时，我们配置了APP支持的语言列表，以及监听了系统语言改变事件；另外\"),a(\"code\",[t._v(\"MaterialApp\")]),t._v(\"消费（依赖）了\"),a(\"code\",[t._v(\"ThemeModel\")]),t._v(\"和\"),a(\"code\",[t._v(\"LocaleModel\")]),t._v(\"，所以当APP主题或语言改变时\"),a(\"code\",[t._v(\"MaterialApp\")]),t._v(\"会重新构建\")]),t._v(\" \"),a(\"li\",[t._v(\"我们注册了命名路由表，以便在APP中可以直接通过路由名跳转。\")]),t._v(\" \"),a(\"li\",[t._v(\"为了支持多语言（本APP中我们支持美国英语和中文简体两种语言）我们实现了一个\"),a(\"code\",[t._v(\"GmLocalizationsDelegate\")]),t._v(\"，子Widget中都可以通过\"),a(\"code\",[t._v(\"GmLocalizations\")]),t._v(\"来动态获取APP当前语言对应的文案。关于\"),a(\"code\",[t._v(\"GmLocalizationsDelegate\")]),t._v(\"和\"),a(\"code\",[t._v(\"GmLocalizations\")]),t._v(\"的实现方式读者可以参考“国际化”一章中的介绍，此处不再赘述。\")])]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_15-6-2-主页\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_15-6-2-主页\"}},[t._v(\"#\")]),t._v(\" 15.6.2 主页\")]),t._v(\" \"),a(\"p\",[t._v(\"为了简单起见，当APP启动后，如果之前已登录了APP，则显示该用户项目列表；如果之前未登录，则显示一个登录按钮，点击后跳转到登录页。另外，我们实现一个抽屉菜单，里面包含当前用户头像及APP的菜单。下面我们先看看要实现的效果，如图15-1、15-2所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(560),alt:\"15-1\"}}),a(\"img\",{attrs:{src:n(561),alt:\"15-2\"}})]),t._v(\" \"),a(\"p\",[t._v(\"我们在“lib/routes”下创建一个“home_page.dart”文件，实现如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"HomeRoute\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _HomeRouteState \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_HomeRouteState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_HomeRouteState\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"HomeRoute\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      appBar\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        title\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"GmLocalizations\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"home\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      body\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_buildBody\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 构建主页面\")]),t._v(\"\\n      drawer\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyDrawer\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//抽屉菜单\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 省略\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"上面代码中，主页的标题（title）我们是通过\"),a(\"code\",[t._v(\"GmLocalizations.of(context).home\")]),t._v(\"来获得，\"),a(\"code\",[t._v(\"GmLocalizations\")]),t._v(\"是我们提供的一个\"),a(\"code\",[t._v(\"Localizations\")]),t._v(\"类，用于支持多语言，因此当APP语言改变时，凡是使用\"),a(\"code\",[t._v(\"GmLocalizations\")]),t._v(\"动态获取的文案都会是相应语言的文案，这在前面“国际化”一章中已经介绍过，读者可以前翻查阅。\")]),t._v(\" \"),a(\"p\",[t._v(\"我们通过 \"),a(\"code\",[t._v(\"_buildBody()\")]),t._v(\"方法来构建主页内容，\"),a(\"code\",[t._v(\"_buildBody()\")]),t._v(\"方法实现代码如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[t._v(\"  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_buildBody\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    UserModel userModel \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Provider\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"of\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"UserModel\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"userModel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isLogin\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//用户未登录，显示登录按钮\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"GmLocalizations\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"login\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Navigator\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pushNamed\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"login\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//已登录，则展示项目列表\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" InfiniteListView\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Repo\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        onRetrieveData\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int page\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" List\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Repo\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" items\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" bool refresh\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" data \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Git\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getRepos\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            refresh\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" refresh\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            queryParameters\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'page'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" page\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'page_size'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//把请求到的新数据添加到items中\")]),t._v(\"\\n          items\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addAll\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"data\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 如果接口返回的数量等于'page_size'，则认为还有数据，反之则认为最后一页\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" data\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"length\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        itemBuilder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"List list\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int index\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" BuildContext ctx\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 项目信息列表项\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RepoItem\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"list\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"index\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"上面代码注释很清楚：如果用户未登录，显示登录按钮；如果用户已登录，则展示项目列表。这里项目列表使用了\"),a(\"code\",[t._v(\"InfiniteListView\")]),t._v(\" Widget，它是flukit package中提供的。\"),a(\"code\",[t._v(\"InfiniteListView\")]),t._v(\"同时支持了下拉刷新和上拉加载更多两种功能。\"),a(\"code\",[t._v(\"onRetrieveData\")]),t._v(\" 为数据获取回调，该回调函数接收三个参数：\")]),t._v(\" \"),a(\"table\",[a(\"thead\",[a(\"tr\",[a(\"th\",[t._v(\"参数名\")]),t._v(\" \"),a(\"th\",[t._v(\"类型\")]),t._v(\" \"),a(\"th\",[t._v(\"解释\")])])]),t._v(\" \"),a(\"tbody\",[a(\"tr\",[a(\"td\",[t._v(\"page\")]),t._v(\" \"),a(\"td\",[t._v(\"int\")]),t._v(\" \"),a(\"td\",[t._v(\"当前页号\")])]),t._v(\" \"),a(\"tr\",[a(\"td\",[t._v(\"items\")]),t._v(\" \"),a(\"td\",[t._v(\"List\"),a(\"T\")],1),t._v(\" \"),a(\"td\",[t._v(\"保存当前列表数据的List\")])]),t._v(\" \"),a(\"tr\",[a(\"td\",[t._v(\"refresh\")]),t._v(\" \"),a(\"td\",[t._v(\"bool\")]),t._v(\" \"),a(\"td\",[t._v(\"是否是下拉刷新触发\")])])])]),t._v(\" \"),a(\"p\",[t._v(\"返回值类型为\"),a(\"code\",[t._v(\"bool\")]),t._v(\"，为\"),a(\"code\",[t._v(\"true\")]),t._v(\"时表示还有数据，为\"),a(\"code\",[t._v(\"false\")]),t._v(\"时则表示后续没有数据了。\"),a(\"code\",[t._v(\"onRetrieveData\")]),t._v(\" 回调中我们调用\"),a(\"code\",[t._v(\"Git(context).getRepos(...)\")]),t._v(\"来获取用户项目列表，同时指定每次请求获取20条。当获取成功时，首先要将新获取的项目数据添加到\"),a(\"code\",[t._v(\"items\")]),t._v(\"中，然后根据本次请求的项目条数是否等于期望的20条来判断还有没有更多的数据。在此需要注意，\"),a(\"code\",[t._v(\"Git(context).getRepos(…)\")]),t._v(\"方法中需要\"),a(\"code\",[t._v(\"refresh\")]),t._v(\"参数来判断是否使用缓存。\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"itemBuilder\")]),t._v(\"为列表项的builder，我们需要在该回调中构建每一个列表项Widget。由于列表项构建逻辑较复杂，我们单独封装一个\"),a(\"code\",[t._v(\"RepoItem\")]),t._v(\" Widget 专门用于构建列表项UI。\"),a(\"code\",[t._v(\"RepoItem\")]),t._v(\" 实现如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'../index.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"RepoItem\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 将`repo.id`作为RepoItem的默认key\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RepoItem\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"repo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ValueKey\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"repo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"id\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Repo repo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _RepoItemState \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_RepoItemState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_RepoItemState\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"RepoItem\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" subtitle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"only\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"top\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"8.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Material\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        shape\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BorderDirectional\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          bottom\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BorderSide\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Theme\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dividerColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".5\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"only\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"top\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" bottom\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            crossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" CrossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                dense\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                leading\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"gmAvatar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//项目owner头像\")]),t._v(\"\\n                  widget\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"repo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"owner\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"avatar_url\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"24.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  borderRadius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BorderRadius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"circular\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"12\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                title\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  widget\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"repo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"owner\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"login\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  textScaleFactor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".9\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                subtitle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" subtitle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                trailing\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"repo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"language \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 构建项目标题和简介\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"symmetric\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"horizontal\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  crossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" CrossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                      widget\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"repo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"fork\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" widget\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"repo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"full_name\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" widget\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"repo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"name\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      style\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                        fontSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"15\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        fontWeight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" FontWeight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"bold\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        fontStyle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" widget\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"repo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"fork\\n                            \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" FontStyle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"italic\\n                            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" FontStyle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"normal\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                      padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"only\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"top\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"8\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" bottom\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"12\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" widget\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"repo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"description \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                              GmLocalizations\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"noDescription\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                              style\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                                  fontStyle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" FontStyle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"italic\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                  color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"700\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                              widget\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"repo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"description\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                              maxLines\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                              style\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                                height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.15\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blueGrey\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"700\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                fontSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"13\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 构建卡片底部信息\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_buildBottom\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 构建卡片底部信息\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_buildBottom\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" paddingWidth \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"10\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"IconTheme\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      data\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"IconThemeData\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        size\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"15\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DefaultTextStyle\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        style\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" fontSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"12\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"symmetric\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"horizontal\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" children \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"star\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\" \"')]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\"\\n                  widget\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"repo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"stargazers_count\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"padRight\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"paddingWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"info_outline\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\" \"')]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\"\\n                  widget\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"repo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"open_issues_count\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"padRight\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"paddingWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"MyIcons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"fork\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//我们的自定义图标\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"repo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"forks_count\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"padRight\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"paddingWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"repo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"fork\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Forked\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"padRight\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"paddingWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"repo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"private \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addAll\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"lock\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\" private\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"padRight\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"paddingWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"上面代码有两点需要注意：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[a(\"p\",[t._v(\"在构建项目拥有者头像时调用了\"),a(\"code\",[t._v(\"gmAvatar(…)\")]),t._v(\"方法，该方法是是一个全局工具函数，专门用于获取头像图片，实现如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[t._v(\"Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"gmAvatar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String url\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  double width \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"30\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  double height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  BoxFit fit\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  BorderRadius borderRadius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" placeholder \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Image\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"asset\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"imgs/avatar-default.png\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//头像占位图，加载过程中显示\")]),t._v(\"\\n      width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" height\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ClipRRect\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    borderRadius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" borderRadius \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" BorderRadius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"circular\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CachedNetworkImage\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \\n      imageUrl\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" url\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      fit\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" fit\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      placeholder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" url\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"placeholder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      errorWidget\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" url\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" error\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"placeholder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"代码中调用了\"),a(\"code\",[t._v(\"CachedNetworkImage\")]),t._v(\" 是cached_network_image包中提供的一个Widget，它不仅可以在图片加载过程中指定一个占位图，而且还可以对网络请求的图片进行缓存，更多详情读者可以自行查阅其文档。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"由于Flutter 的Material 图标库中没有fork图标，所以我们在iconfont.cn上找了一个fork图标，然后根据“图片和Icon”一节中介绍的使用自定义字体图标的方法集成到了我们的项目中。\")])])]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_15-6-3-抽屉菜单\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_15-6-3-抽屉菜单\"}},[t._v(\"#\")]),t._v(\" 15.6.3 抽屉菜单\")]),t._v(\" \"),a(\"p\",[t._v(\"抽屉菜单分为两部分：顶部头像和底部功能菜单项。当用户未登录，则抽屉菜单顶部会显示一个默认的灰色占位图，若用户已登录，则会显示用户的头像。抽屉菜单底部有“换肤”和“语言”两个固定菜单，若用户已登录，则会多一个“注销”菜单。用户点击“换肤”和“语言”两个菜单项，会进入相应的设置页面。我们的抽屉菜单效果如图15-3、15-4所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(562),alt:\"15-3\"}}),a(\"img\",{attrs:{src:n(563),alt:\"15-4\"}})]),t._v(\" \"),a(\"p\",[t._v(\"实现代码如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyDrawer\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyDrawer\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Drawer\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//移除顶部padding\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MediaQuery\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"removePadding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        removeTop\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          crossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" CrossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_buildHeader\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//构建抽屉菜单头部\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Expanded\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_buildMenus\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//构建功能菜单\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_buildHeader\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" Consumer\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"UserModel\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      builder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" UserModel value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Widget child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GestureDetector\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Theme\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"primaryColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"only\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"top\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"40\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" bottom\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"symmetric\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"horizontal\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ClipOval\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 如果已登录，则显示用户头像；若未登录，则显示默认头像\")]),t._v(\"\\n                    child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isLogin\\n                        \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"gmAvatar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"user\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"avatar_url\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Image\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"asset\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                            \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"imgs/avatar-default.png\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isLogin\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"user\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"login\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" GmLocalizations\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"login\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  style\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    fontWeight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" FontWeight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"bold\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          onTap\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isLogin\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" Navigator\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pushNamed\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"login\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 构建菜单项\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_buildMenus\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" Consumer\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"UserModel\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      builder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" UserModel userModel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Widget child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" gm \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" GmLocalizations\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListView\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              leading\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"color_lens\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              title\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"gm\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"theme\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              onTap\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Navigator\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pushNamed\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"themes\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              leading\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"language\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              title\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"gm\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"language\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              onTap\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Navigator\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pushNamed\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"language\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"userModel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isLogin\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              leading\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"power_settings_new\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              title\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"gm\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"logout\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              onTap\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showDialog\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  builder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ctx\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//退出账号前先弹二次确认窗\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlertDialog\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                      content\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"gm\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"logoutTip\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      actions\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                          child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"gm\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cancel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Navigator\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                          child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"gm\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"yes\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                            \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//该赋值语句会触发MaterialApp rebuild\")]),t._v(\"\\n                            userModel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"user \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                            Navigator\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"用户点击“注销”，\"),a(\"code\",[t._v(\"userModel.user\")]),t._v(\" 会被置空，此时所有依赖\"),a(\"code\",[t._v(\"userModel\")]),t._v(\"的组件都会被\"),a(\"code\",[t._v(\"rebuild\")]),t._v(\"，如主页会恢复成未登录的状态。\")]),t._v(\" \"),a(\"p\",[t._v(\"本小节我们介绍了APP入口\"),a(\"code\",[t._v(\"MaterialApp\")]),t._v(\"的一些配置，然后实现了APP的首页。后面我们将展示登录页、换肤页、语言切换页。\")])])}),[],!1,null,null,null);s.default=p.exports}}]);"
  },
  {
    "path": "docs/assets/js/33.8b948e2d.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[33],{569:function(t,s,a){t.exports=a.p+\"assets/img/2-8.89d0af83.png\"},570:function(t,s,a){t.exports=a.p+\"assets/img/2-9.0a86cf44.png\"},571:function(t,s,a){t.exports=a.p+\"assets/img/2-10.e56b6689.png\"},572:function(t,s,a){t.exports=a.p+\"assets/img/2-11.9f54d13a.png\"},878:function(t,s,a){\"use strict\";a.r(s);var e=a(45),n=Object(e.a)({},(function(){var t=this,s=t.$createElement,e=t._self._c||s;return e(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[e(\"h1\",{attrs:{id:\"_2-4-资源管理\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_2-4-资源管理\"}},[t._v(\"#\")]),t._v(\" 2.4 资源管理\")]),t._v(\" \"),e(\"p\",[t._v(\"Flutter APP安装包中会包含代码和 assets（资源）两部分。Assets是会打包到程序安装包中的，可在运行时访问。常见类型的assets包括静态数据（例如JSON文件）、配置文件、图标和图片（JPEG，WebP，GIF，动画WebP / GIF，PNG，BMP和WBMP）等。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"指定-assets\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#指定-assets\"}},[t._v(\"#\")]),t._v(\" 指定 assets\")]),t._v(\" \"),e(\"p\",[t._v(\"和包管理一样，Flutter也使用\"),e(\"a\",{attrs:{href:\"https://www.dartlang.org/tools/pub/pubspec\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"pubspec.yaml\")]),e(\"OutboundLink\")],1),t._v(\"文件来管理应用程序所需的资源，举个例子:\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-yaml extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"flutter\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"assets\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\" assets/my_icon.png\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"-\")]),t._v(\" assets/background.png\\n\")])])]),e(\"p\",[e(\"code\",[t._v(\"assets\")]),t._v(\"指定应包含在应用程序中的文件， 每个asset都通过相对于\"),e(\"code\",[t._v(\"pubspec.yaml\")]),t._v(\"文件所在的文件系统路径来标识自身的路径。asset的声明顺序是无关紧要的，asset的实际目录可以是任意文件夹（在本示例中是assets文件夹）。\")]),t._v(\" \"),e(\"p\",[t._v(\"在构建期间，Flutter将asset放置到称为 \"),e(\"em\",[t._v(\"asset bundle\")]),t._v(\" 的特殊存档中，应用程序可以在运行时读取它们（但不能修改）。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"asset-变体-variant\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#asset-变体-variant\"}},[t._v(\"#\")]),t._v(\" Asset 变体（variant）\")]),t._v(\" \"),e(\"p\",[t._v(\"构建过程支持“asset变体”的概念：不同版本的asset可能会显示在不同的上下文中。 在\"),e(\"code\",[t._v(\"pubspec.yaml\")]),t._v(\"的assets部分中指定asset路径时，构建过程中，会在相邻子目录中查找具有相同名称的任何文件。这些文件随后会与指定的asset一起被包含在asset bundle中。\")]),t._v(\" \"),e(\"p\",[t._v(\"例如，如果应用程序目录中有以下文件:\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"…/pubspec.yaml\")]),t._v(\" \"),e(\"li\",[t._v(\"…/graphics/my_icon.png\")]),t._v(\" \"),e(\"li\",[t._v(\"…/graphics/background.png\")]),t._v(\" \"),e(\"li\",[t._v(\"…/graphics/dark/background.png\")]),t._v(\" \"),e(\"li\",[t._v(\"…etc.\")])]),t._v(\" \"),e(\"p\",[t._v(\"然后\"),e(\"code\",[t._v(\"pubspec.yaml\")]),t._v(\"文件中只需包含:\")]),t._v(\" \"),e(\"div\",{staticClass:\"language- extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[e(\"code\",[t._v(\"flutter:\\n  assets:\\n    - graphics/background.png\\n\")])])]),e(\"p\",[t._v(\"那么这两个\"),e(\"code\",[t._v(\"graphics/background.png\")]),t._v(\"和\"),e(\"code\",[t._v(\"graphics/dark/background.png\")]),t._v(\" 都将包含在您的asset bundle中。前者被认为是_main asset_ （主资源），后者被认为是一种变体（variant）。\")]),t._v(\" \"),e(\"p\",[t._v(\"在选择匹配当前设备分辨率的图片时，Flutter会使用到asset变体（见下文），将来，Flutter可能会将这种机制扩展到本地化、阅读提示等方面。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"加载-assets\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#加载-assets\"}},[t._v(\"#\")]),t._v(\" 加载 assets\")]),t._v(\" \"),e(\"p\",[t._v(\"您的应用可以通过\"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/services/AssetBundle-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"AssetBundle\")]),e(\"OutboundLink\")],1),t._v(\"对象访问其asset 。有两种主要方法允许从Asset bundle中加载字符串或图片（二进制）文件。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"加载文本assets\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#加载文本assets\"}},[t._v(\"#\")]),t._v(\" 加载文本assets\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"通过\"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/services/rootBundle.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"rootBundle\")]),e(\"OutboundLink\")],1),t._v(\" 对象加载：每个Flutter应用程序都有一个\"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/services/rootBundle.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"rootBundle\")]),e(\"OutboundLink\")],1),t._v(\"对象， 通过它可以轻松访问主资源包，直接使用\"),e(\"code\",[t._v(\"package:flutter/services.dart\")]),t._v(\"中全局静态的\"),e(\"code\",[t._v(\"rootBundle\")]),t._v(\"对象来加载asset即可。\")]),t._v(\" \"),e(\"li\",[t._v(\"通过 \"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/DefaultAssetBundle-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"DefaultAssetBundle\")]),e(\"OutboundLink\")],1),t._v(\" 加载：建议使用 \"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/DefaultAssetBundle-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"DefaultAssetBundle\")]),e(\"OutboundLink\")],1),t._v(\" 来获取当前BuildContext的AssetBundle。 这种方法不是使用应用程序构建的默认asset bundle，而是使父级widget在运行时动态替换的不同的AssetBundle，这对于本地化或测试场景很有用。\")])]),t._v(\" \"),e(\"p\",[t._v(\"通常，可以使用\"),e(\"code\",[t._v(\"DefaultAssetBundle.of()\")]),t._v(\"在应用运行时来间接加载asset（例如JSON文件），而在widget上下文之外，或其它\"),e(\"code\",[t._v(\"AssetBundle\")]),t._v(\"句柄不可用时，可以使用\"),e(\"code\",[t._v(\"rootBundle\")]),t._v(\"直接加载这些asset，例如：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'dart:async'\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"show\")]),t._v(\" Future\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/services.dart'\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"show\")]),t._v(\" rootBundle\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\nFuture\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"loadAsset\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" rootBundle\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"loadString\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'assets/config.json'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"h3\",{attrs:{id:\"加载图片\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#加载图片\"}},[t._v(\"#\")]),t._v(\" 加载图片\")]),t._v(\" \"),e(\"p\",[t._v(\"类似于原生开发，Flutter也可以为当前设备加载适合其分辨率的图像。\")]),t._v(\" \"),e(\"h4\",{attrs:{id:\"声明分辨率相关的图片-assets\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#声明分辨率相关的图片-assets\"}},[t._v(\"#\")]),t._v(\" 声明分辨率相关的图片 assets\")]),t._v(\" \"),e(\"p\",[e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/painting/AssetImage-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"AssetImage\")]),e(\"OutboundLink\")],1),t._v(\" 可以将asset的请求逻辑映射到最接近当前设备像素比例（dpi）的asset。为了使这种映射起作用，必须根据特定的目录结构来保存asset：\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"…/image.png\")]),t._v(\" \"),e(\"li\",[t._v(\"…/\"),e(\"strong\",[t._v(\"M\")]),t._v(\"x/image.png\")]),t._v(\" \"),e(\"li\",[t._v(\"…/\"),e(\"strong\",[t._v(\"N\")]),t._v(\"x/image.png\")]),t._v(\" \"),e(\"li\",[t._v(\"…etc.\")])]),t._v(\" \"),e(\"p\",[t._v(\"其中M和N是数字标识符，对应于其中包含的图像的分辨率，也就是说，它们指定不同设备像素比例的图片。\")]),t._v(\" \"),e(\"p\",[t._v(\"主资源默认对应于1.0倍的分辨率图片。看一个例子：\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"…/my_icon.png\")]),t._v(\" \"),e(\"li\",[t._v(\"…/2.0x/my_icon.png\")]),t._v(\" \"),e(\"li\",[t._v(\"…/3.0x/my_icon.png\")])]),t._v(\" \"),e(\"p\",[t._v(\"在设备像素比率为1.8的设备上，\"),e(\"code\",[t._v(\".../2.0x/my_icon.png\")]),t._v(\" 将被选择。对于2.7的设备像素比率，\"),e(\"code\",[t._v(\".../3.0x/my_icon.png\")]),t._v(\"将被选择。\")]),t._v(\" \"),e(\"p\",[t._v(\"如果未在\"),e(\"code\",[t._v(\"Image\")]),t._v(\" widget上指定渲染图像的宽度和高度，那么\"),e(\"code\",[t._v(\"Image\")]),t._v(\" widget将占用与主资源相同的屏幕空间大小。 也就是说，如果\"),e(\"code\",[t._v(\".../my_icon.png\")]),t._v(\"是72px乘72px，那么\"),e(\"code\",[t._v(\".../3.0x/my_icon.png\")]),t._v(\"应该是216px乘216px; 但如果未指定宽度和高度，它们都将渲染为72像素×72像素（以逻辑像素为单位）。\")]),t._v(\" \"),e(\"p\",[e(\"code\",[t._v(\"pubspec.yaml\")]),t._v(\"中asset部分中的每一项都应与实际文件相对应，但主资源项除外。当主资源缺少某个资源时，会按分辨率从低到高的顺序去选择 ，也就是说1x中没有的话会在2x中找，2x中还没有的话就在3x中找。\")]),t._v(\" \"),e(\"h4\",{attrs:{id:\"加载图片-2\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#加载图片-2\"}},[t._v(\"#\")]),t._v(\" 加载图片\")]),t._v(\" \"),e(\"p\",[t._v(\"要加载图片，可以使用 \"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/painting/AssetImage-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"AssetImage\")]),e(\"OutboundLink\")],1),t._v(\"类。例如，我们可以从上面的asset声明中加载背景图片：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[t._v(\"Widget \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"DecoratedBox\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    decoration\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"BoxDecoration\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      image\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"DecorationImage\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        image\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AssetImage\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'graphics/background.png'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"注意，\"),e(\"code\",[t._v(\"AssetImage\")]),t._v(\" 并非是一个widget， 它实际上是一个\"),e(\"code\",[t._v(\"ImageProvider\")]),t._v(\"，有些时候你可能期望直接得到一个显示图片的widget，那么你可以使用\"),e(\"code\",[t._v(\"Image.asset()\")]),t._v(\"方法，如：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[t._v(\"Widget \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" Image\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"asset\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'graphics/background.png'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"使用默认的 asset bundle 加载资源时，内部会自动处理分辨率等，这些处理对开发者来说是无感知的。 (如果使用一些更低级别的类，如 \"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/painting/ImageStream-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"ImageStream\")]),e(\"OutboundLink\")],1),t._v(\"或 \"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/painting/ImageCache-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"ImageCache\")]),e(\"OutboundLink\")],1),t._v(\" 时你会注意到有与缩放相关的参数)\")]),t._v(\" \"),e(\"h4\",{attrs:{id:\"依赖包中的资源图片\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#依赖包中的资源图片\"}},[t._v(\"#\")]),t._v(\" 依赖包中的资源图片\")]),t._v(\" \"),e(\"p\",[t._v(\"要加载依赖包中的图像，必须给\"),e(\"code\",[t._v(\"AssetImage\")]),t._v(\"提供\"),e(\"code\",[t._v(\"package\")]),t._v(\"参数。\")]),t._v(\" \"),e(\"p\",[t._v(\"例如，假设您的应用程序依赖于一个名为“my_icons”的包，它具有如下目录结构：\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"…/pubspec.yaml\")]),t._v(\" \"),e(\"li\",[t._v(\"…/icons/heart.png\")]),t._v(\" \"),e(\"li\",[t._v(\"…/icons/1.5x/heart.png\")]),t._v(\" \"),e(\"li\",[t._v(\"…/icons/2.0x/heart.png\")]),t._v(\" \"),e(\"li\",[t._v(\"…etc.\")])]),t._v(\" \"),e(\"p\",[t._v(\"然后加载图像，使用:\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AssetImage\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'icons/heart.png'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" package\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'my_icons'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"或\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Image\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"asset\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'icons/heart.png'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" package\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'my_icons'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),e(\"p\",[e(\"strong\",[t._v(\"注意：包在使用本身的资源时也应该加上\"),e(\"code\",[t._v(\"package\")]),t._v(\"参数来获取\")]),t._v(\"。\")]),t._v(\" \"),e(\"h5\",{attrs:{id:\"打包包中的-assets\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#打包包中的-assets\"}},[t._v(\"#\")]),t._v(\" 打包包中的 assets\")]),t._v(\" \"),e(\"p\",[t._v(\"如果在\"),e(\"code\",[t._v(\"pubspec.yaml\")]),t._v(\"文件中声明了期望的资源，它将会打包到相应的package中。特别是，包本身使用的资源必须在\"),e(\"code\",[t._v(\"pubspec.yaml\")]),t._v(\"中指定。\")]),t._v(\" \"),e(\"p\",[t._v(\"包也可以选择在其\"),e(\"code\",[t._v(\"lib/\")]),t._v(\"文件夹中包含未在其\"),e(\"code\",[t._v(\"pubspec.yaml\")]),t._v(\"文件中声明的资源。在这种情况下，对于要打包的图片，应用程序必须在\"),e(\"code\",[t._v(\"pubspec.yaml\")]),t._v(\"中指定包含哪些图像。 例如，一个名为“fancy_backgrounds”的包，可能包含以下文件：\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[t._v(\"…/lib/backgrounds/background1.png\")]),t._v(\" \"),e(\"li\",[t._v(\"…/lib/backgrounds/background2.png\")]),t._v(\" \"),e(\"li\",[t._v(\"…/lib/backgrounds/background3.png\")])]),t._v(\" \"),e(\"p\",[t._v(\"要包含第一张图像，必须在\"),e(\"code\",[t._v(\"pubspec.yaml\")]),t._v(\"的assets部分中声明它：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language- extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[e(\"code\",[t._v(\"flutter:\\n  assets:\\n    - packages/fancy_backgrounds/backgrounds/background1.png\\n\")])])]),e(\"p\",[e(\"code\",[t._v(\"lib/\")]),t._v(\"是隐含的，所以它不应该包含在资产路径中。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"特定平台-assets\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#特定平台-assets\"}},[t._v(\"#\")]),t._v(\" 特定平台 assets\")]),t._v(\" \"),e(\"p\",[t._v(\"上面的资源都是flutter应用中的，这些资源只有在Flutter框架运行之后才能使用，如果要给我们的应用设置APP图标或者添加启动图，那我们必须使用特定平台的assets。\")]),t._v(\" \"),e(\"h4\",{attrs:{id:\"设置app图标\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#设置app图标\"}},[t._v(\"#\")]),t._v(\" 设置APP图标\")]),t._v(\" \"),e(\"p\",[t._v(\"更新Flutter应用程序启动图标的方式与在本机Android或iOS应用程序中更新启动图标的方式相同。\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"p\",[t._v(\"Android\")]),t._v(\" \"),e(\"p\",[t._v(\"在Flutter项目的根目录中，导航到\"),e(\"code\",[t._v(\".../android/app/src/main/res\")]),t._v(\"目录，里面包含了各种资源文件夹（如\"),e(\"code\",[t._v(\"mipmap-hdpi\")]),t._v(\"已包含占位符图像“ic_launcher.png”，见图2-8）。 只需按照\"),e(\"a\",{attrs:{href:\"https://developer.android.com/guide/practices/ui_guidelines/icon_design_launcher.html#size\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Android开发人员指南\"),e(\"OutboundLink\")],1),t._v(\"中的说明， 将其替换为所需的资源，并遵守每种屏幕密度（dpi）的建议图标大小标准。\")]),t._v(\" \"),e(\"p\",[e(\"img\",{attrs:{src:a(569),alt:\"图2-8\"}})]),t._v(\" \"),e(\"blockquote\",[e(\"p\",[e(\"strong\",[t._v(\"注意:\")]),t._v(\" 如果您重命名.png文件，则还必须在您\"),e(\"code\",[t._v(\"AndroidManifest.xml\")]),t._v(\"的\"),e(\"code\",[t._v(\"<application>\")]),t._v(\"标签的\"),e(\"code\",[t._v(\"android:icon\")]),t._v(\"属性中更新名称。\")])])]),t._v(\" \"),e(\"li\",[e(\"p\",[t._v(\"iOS\")]),t._v(\" \"),e(\"p\",[t._v(\"在Flutter项目的根目录中，导航到\"),e(\"code\",[t._v(\".../ios/Runner\")]),t._v(\"。该目录中\"),e(\"code\",[t._v(\"Assets.xcassets/AppIcon.appiconset\")]),t._v(\"已经包含占位符图片（见图2-9）， 只需将它们替换为适当大小的图片，保留原始文件名称。\")]),t._v(\" \"),e(\"p\",[e(\"img\",{attrs:{src:a(570),alt:\"图2-9\"}})])])]),t._v(\" \"),e(\"h4\",{attrs:{id:\"更新启动页\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#更新启动页\"}},[t._v(\"#\")]),t._v(\" 更新启动页\")]),t._v(\" \"),e(\"p\",[e(\"img\",{attrs:{src:a(571),alt:\"图2-10\"}})]),t._v(\" \"),e(\"p\",[t._v(\"在Flutter框架加载时，Flutter会使用本地平台机制绘制启动页。此启动页将持续到Flutter渲染应用程序的第一帧时。\")]),t._v(\" \"),e(\"blockquote\",[e(\"p\",[e(\"strong\",[t._v(\"注意:\")]),t._v(\" 这意味着如果您不在应用程序的\"),e(\"code\",[t._v(\"main()\")]),t._v(\"方法中调用\"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/runApp.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"runApp\"),e(\"OutboundLink\")],1),t._v(\" 函数 （或者更具体地说，如果您不调用\"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/dart-ui/Window/render.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"window.render\")]),e(\"OutboundLink\")],1),t._v(\"去响应\"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/dart-ui/Window/onDrawFrame.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"window.onDrawFrame\")]),e(\"OutboundLink\")],1),t._v(\"）的话， 启动屏幕将永远持续显示。\")])]),t._v(\" \"),e(\"h5\",{attrs:{id:\"android\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#android\"}},[t._v(\"#\")]),t._v(\" Android\")]),t._v(\" \"),e(\"p\",[t._v(\"要将启动屏幕（splash screen）添加到您的Flutter应用程序， 请导航至\"),e(\"code\",[t._v(\".../android/app/src/main\")]),t._v(\"。在\"),e(\"code\",[t._v(\"res/drawable/launch_background.xml\")]),t._v(\"，通过自定义drawable来实现自定义启动界面（你也可以直接换一张图片）。\")]),t._v(\" \"),e(\"h5\",{attrs:{id:\"ios\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#ios\"}},[t._v(\"#\")]),t._v(\" iOS\")]),t._v(\" \"),e(\"p\",[t._v(\"要将图片添加到启动屏幕（splash screen）的中心，请导航至\"),e(\"code\",[t._v(\".../ios/Runner\")]),t._v(\"。在\"),e(\"code\",[t._v(\"Assets.xcassets/LaunchImage.imageset\")]),t._v(\"， 拖入图片，并命名为\"),e(\"code\",[t._v(\"LaunchImage.png\")]),t._v(\"、\"),e(\"code\",[t._v(\"LaunchImage@2x.png\")]),t._v(\"、\"),e(\"code\",[t._v(\"LaunchImage@3x.png\")]),t._v(\"。 如果你使用不同的文件名，那您还必须更新同一目录中的\"),e(\"code\",[t._v(\"Contents.json\")]),t._v(\"文件，图片的具体尺寸可以查看苹果官方的标准。\")]),t._v(\" \"),e(\"p\",[t._v(\"您也可以通过打开Xcode完全自定义storyboard。在Project Navigator中导航到\"),e(\"code\",[t._v(\"Runner/Runner\")]),t._v(\"然后通过打开\"),e(\"code\",[t._v(\"Assets.xcassets\")]),t._v(\"拖入图片，或者通过在LaunchScreen.storyboard中使用Interface Builder进行自定义，如图2-11所示。\")]),t._v(\" \"),e(\"p\",[e(\"img\",{attrs:{src:a(572),alt:\"图2-11\"}})])])}),[],!1,null,null,null);s.default=n.exports}}]);"
  },
  {
    "path": "docs/assets/js/34.9dff7b9d.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[34],{596:function(t,s){t.exports=\"data:image/png;base64,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\"},597:function(t,s){t.exports=\"data:image/png;base64,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\"},598:function(t,s,a){t.exports=a.p+\"assets/img/3-32.11e62a02.png\"},599:function(t,s,a){t.exports=a.p+\"assets/img/progress_oval.4912d1a4.png\"},887:function(t,s,a){\"use strict\";a.r(s);var n=a(45),r=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_3-8-进度指示器\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-8-进度指示器\"}},[t._v(\"#\")]),t._v(\" 3.8 进度指示器\")]),t._v(\" \"),n(\"p\",[t._v(\"Material 组件库中提供了两种进度指示器：\"),n(\"code\",[t._v(\"LinearProgressIndicator\")]),t._v(\"和\"),n(\"code\",[t._v(\"CircularProgressIndicator\")]),t._v(\"，它们都可以同时用于精确的进度指示和模糊的进度指示。精确进度通常用于任务进度可以计算和预估的情况，比如文件下载；而模糊进度则用户任务进度无法准确获得的情况，如下拉刷新，数据提交等。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"linearprogressindicator\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#linearprogressindicator\"}},[t._v(\"#\")]),t._v(\" LinearProgressIndicator\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"LinearProgressIndicator\")]),t._v(\"是一个线性、条状的进度条，定义如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"LinearProgressIndicator\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  double value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  Color backgroundColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Color\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" valueColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"value\")]),t._v(\"：\"),n(\"code\",[t._v(\"value\")]),t._v(\"表示当前的进度，取值范围为[0,1]；如果\"),n(\"code\",[t._v(\"value\")]),t._v(\"为\"),n(\"code\",[t._v(\"null\")]),t._v(\"时则指示器会执行一个循环动画（模糊进度）；当\"),n(\"code\",[t._v(\"value\")]),t._v(\"不为\"),n(\"code\",[t._v(\"null\")]),t._v(\"时，指示器为一个具体进度的进度条。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"backgroundColor\")]),t._v(\"：指示器的背景色。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"valueColor\")]),t._v(\": 指示器的进度条颜色；值得注意的是，该值类型是\"),n(\"code\",[t._v(\"Animation<Color>\")]),t._v(\"，这允许我们对进度条的颜色也可以指定动画。如果我们不需要对进度条颜色执行动画，换言之，我们想对进度条应用一种固定的颜色，此时我们可以通过\"),n(\"code\",[t._v(\"AlwaysStoppedAnimation\")]),t._v(\"来指定。\")])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"示例\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 模糊进度条(会执行一个动画)\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"LinearProgressIndicator\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  backgroundColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  valueColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlwaysStoppedAnimation\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//进度条显示50%\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"LinearProgressIndicator\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  backgroundColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  valueColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlwaysStoppedAnimation\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".5\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图3-30所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(596),alt:\"图3-30\"}})]),t._v(\" \"),n(\"p\",[t._v(\"第一个进度条在执行循环动画：蓝色条一直在移动，而第二个进度条是静止的，停在50%的位置。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"circularprogressindicator\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#circularprogressindicator\"}},[t._v(\"#\")]),t._v(\" CircularProgressIndicator\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"CircularProgressIndicator\")]),t._v(\"是一个圆形进度条，定义如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircularProgressIndicator\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  double value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  Color backgroundColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Color\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" valueColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"strokeWidth \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"4.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"   \\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \\n\")])])]),n(\"p\",[t._v(\"前三个参数和\"),n(\"code\",[t._v(\"LinearProgressIndicator\")]),t._v(\"相同，不再赘述。\"),n(\"code\",[t._v(\"strokeWidth\")]),t._v(\" 表示圆形进度条的粗细。示例如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 模糊进度条(会执行一个旋转动画)\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircularProgressIndicator\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  backgroundColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  valueColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlwaysStoppedAnimation\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//进度条显示50%，会显示一个半圆\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircularProgressIndicator\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  backgroundColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  valueColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlwaysStoppedAnimation\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".5\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图3-31所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(597),alt:\"图3-31\"}})]),t._v(\" \"),n(\"p\",[t._v(\"第一个进度条会执行旋转动画，而第二个进度条是静止的，它停在50%的位置。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"自定义尺寸\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#自定义尺寸\"}},[t._v(\"#\")]),t._v(\" 自定义尺寸\")]),t._v(\" \"),n(\"p\",[t._v(\"我们可以发现\"),n(\"code\",[t._v(\"LinearProgressIndicator\")]),t._v(\"和\"),n(\"code\",[t._v(\"CircularProgressIndicator\")]),t._v(\"，并没有提供设置圆形进度条尺寸的参数；如果我们希望\"),n(\"code\",[t._v(\"LinearProgressIndicator\")]),t._v(\"的线细一些，或者希望\"),n(\"code\",[t._v(\"CircularProgressIndicator\")]),t._v(\"的圆大一些该怎么做？\")]),t._v(\" \"),n(\"p\",[t._v(\"其实\"),n(\"code\",[t._v(\"LinearProgressIndicator\")]),t._v(\"和\"),n(\"code\",[t._v(\"CircularProgressIndicator\")]),t._v(\"都是取父容器的尺寸作为绘制的边界的。知道了这点，我们便可以通过尺寸限制类Widget，如\"),n(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"、\"),n(\"code\",[t._v(\"SizedBox\")]),t._v(\" （我们将在后面容器类组件一章中介绍）来指定尺寸，如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 线性进度条高度指定为3\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"LinearProgressIndicator\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    backgroundColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    valueColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlwaysStoppedAnimation\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".5\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 圆形进度条直径指定为100\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircularProgressIndicator\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    backgroundColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    valueColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlwaysStoppedAnimation\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".7\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图3-32所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(598),alt:\"图3-32\"}})]),t._v(\" \"),n(\"p\",[t._v(\"注意，如果\"),n(\"code\",[t._v(\"CircularProgressIndicator\")]),t._v(\"显示空间的宽高不同，则会显示为椭圆。如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 宽高不等\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"130\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircularProgressIndicator\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    backgroundColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    valueColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlwaysStoppedAnimation\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".7\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图3-33所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(599),alt:\"progress_oval\"}})]),t._v(\" \"),n(\"h3\",{attrs:{id:\"进度色动画\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#进度色动画\"}},[t._v(\"#\")]),t._v(\" 进度色动画\")]),t._v(\" \"),n(\"p\",[t._v(\"前面说过可以通过\"),n(\"code\",[t._v(\"valueColor\")]),t._v(\"对进度条颜色做动画，关于动画我们将在后面专门的章节详细介绍，这里先给出一个例子，读者在了解了Flutter动画一章后再回过头来看。\")]),t._v(\" \"),n(\"p\",[t._v(\"我们实现一个进度条在3秒内从灰色变成蓝色的动画：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ProgressRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _ProgressRouteState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_ProgressRouteState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_ProgressRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ProgressRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"with\")]),t._v(\" SingleTickerProviderStateMixin \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  AnimationController _animationController\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//动画执行时间3秒  \")]),t._v(\"\\n    _animationController \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimationController\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"vsync\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" duration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    _animationController\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"forward\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    _animationController\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    _animationController\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SingleChildScrollView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"LinearProgressIndicator\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              backgroundColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              valueColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ColorTween\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"begin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" end\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"animate\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_animationController\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 从灰色变成蓝色\")]),t._v(\"\\n              value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _animationController\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"h3\",{attrs:{id:\"自定义进度指示器样式\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#自定义进度指示器样式\"}},[t._v(\"#\")]),t._v(\" 自定义进度指示器样式\")]),t._v(\" \"),n(\"p\",[t._v(\"定制进度指示器风格样式，可以通过\"),n(\"code\",[t._v(\"CustomPainter\")]),t._v(\" Widget 来自定义绘制逻辑，实际上\"),n(\"code\",[t._v(\"LinearProgressIndicator\")]),t._v(\"和\"),n(\"code\",[t._v(\"CircularProgressIndicator\")]),t._v(\"也正是通过\"),n(\"code\",[t._v(\"CustomPainter\")]),t._v(\"来实现外观绘制的。关于\"),n(\"code\",[t._v(\"CustomPainter\")]),t._v(\"，我们将在后面“自定义Widget”一章中详细介绍。\")]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[n(\"a\",{attrs:{href:\"https://pub.flutter-io.cn/packages/flutter_spinkit\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"flutter_spinkit\"),n(\"OutboundLink\")],1),t._v(\" 包提供了多种风格的模糊进度指示器，读者若是感兴趣，可以参考。\")])])])}),[],!1,null,null,null);s.default=r.exports}}]);"
  },
  {
    "path": "docs/assets/js/35.a9c7aa47.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[35],{618:function(t,n){t.exports=\"data:image/png;base64,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\"},619:function(t,n){t.exports=\"data:image/png;base64,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\"},620:function(t,n){t.exports=\"data:image/png;base64,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\"},621:function(t,n){t.exports=\"data:image/png;base64,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\"},894:function(t,n,a){\"use strict\";a.r(n);var s=a(45),e=Object(s.a)({},(function(){var t=this,n=t.$createElement,s=t._self._c||n;return s(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[s(\"h1\",{attrs:{id:\"_4-6-对齐与相对定位-align\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_4-6-对齐与相对定位-align\"}},[t._v(\"#\")]),t._v(\" 4.6 对齐与相对定位（Align）\")]),t._v(\" \"),s(\"p\",[t._v(\"在上一节中我们讲过通过\"),s(\"code\",[t._v(\"Stack\")]),t._v(\"和\"),s(\"code\",[t._v(\"Positioned\")]),t._v(\"，我们可以指定一个或多个子元素相对于父元素各个边的精确偏移，并且可以重叠。但如果我们只想简单的调整\"),s(\"strong\",[t._v(\"一个\")]),t._v(\"子元素在父元素中的位置的话，使用\"),s(\"code\",[t._v(\"Align\")]),t._v(\"组件会更简单一些。\")]),t._v(\" \"),s(\"h2\",{attrs:{id:\"_4-6-1-align\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_4-6-1-align\"}},[t._v(\"#\")]),t._v(\" 4.6.1 Align\")]),t._v(\" \"),s(\"p\",[s(\"code\",[t._v(\"Align\")]),t._v(\" 组件可以调整子组件的位置，并且可以根据子组件的宽高来确定自身的的宽高，定义如下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Align\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  Key key\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"alignment \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Alignment\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"widthFactor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"heightFactor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  Widget child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),s(\"ul\",[s(\"li\",[s(\"code\",[t._v(\"alignment\")]),t._v(\" : 需要一个\"),s(\"code\",[t._v(\"AlignmentGeometry\")]),t._v(\"类型的值，表示子组件在父组件中的起始位置。\"),s(\"code\",[t._v(\"AlignmentGeometry\")]),t._v(\" 是一个抽象类，它有两个常用的子类：\"),s(\"code\",[t._v(\"Alignment\")]),t._v(\"和 \"),s(\"code\",[t._v(\"FractionalOffset\")]),t._v(\"，我们将在下面的示例中详细介绍。\")]),t._v(\" \"),s(\"li\",[s(\"code\",[t._v(\"widthFactor\")]),t._v(\"和\"),s(\"code\",[t._v(\"heightFactor\")]),t._v(\"是用于确定\"),s(\"code\",[t._v(\"Align\")]),t._v(\" 组件本身宽高的属性；它们是两个缩放因子，会分别乘以子元素的宽、高，最终的结果就是\"),s(\"code\",[t._v(\"Align\")]),t._v(\" 组件的宽高。如果值为\"),s(\"code\",[t._v(\"null\")]),t._v(\"，则组件的宽高将会占用尽可能多的空间。\")])]),t._v(\" \"),s(\"h3\",{attrs:{id:\"示例\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),s(\"p\",[t._v(\"我们先来看一个简单的例子：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  height\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"120.0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  width\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"120.0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Align\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    alignment\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"topRight\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlutterLogo\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      size\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"60\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"运行效果如图4-11所示：\")]),t._v(\" \"),s(\"p\",[s(\"img\",{attrs:{src:a(618),alt:\"图4-11\"}})]),t._v(\" \"),s(\"p\",[s(\"code\",[t._v(\"FlutterLogo\")]),t._v(\" 是Flutter SDK提供的一个组件，内容就是Flutter的商标。在上面的例子中，我们显式指定了\"),s(\"code\",[t._v(\"Container\")]),t._v(\"的宽、高都为120。如果我们不显式指定宽高，而通过同时指定\"),s(\"code\",[t._v(\"widthFactor\")]),t._v(\"和\"),s(\"code\",[t._v(\"heightFactor\")]),t._v(\" 为2也是可以达到同样的效果：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Align\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  widthFactor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  heightFactor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  alignment\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"topRight\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlutterLogo\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    size\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"60\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"因为\"),s(\"code\",[t._v(\"FlutterLogo\")]),t._v(\"的宽高为60，则\"),s(\"code\",[t._v(\"Align\")]),t._v(\"的最终宽高都为\"),s(\"code\",[t._v(\"2*60=120\")]),t._v(\"。\")]),t._v(\" \"),s(\"p\",[t._v(\"另外，我们通过\"),s(\"code\",[t._v(\"Alignment.topRight\")]),t._v(\"将\"),s(\"code\",[t._v(\"FlutterLogo\")]),t._v(\"定位在\"),s(\"code\",[t._v(\"Container\")]),t._v(\"的右上角。那\"),s(\"code\",[t._v(\"Alignment.topRight\")]),t._v(\"是什么呢？通过源码我们可以看到其定义如下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//右上角\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" Alignment topRight \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Alignment\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"可以看到它只是\"),s(\"code\",[t._v(\"Alignment\")]),t._v(\"的一个实例，下面我们介绍一下\"),s(\"code\",[t._v(\"Alignment\")]),t._v(\"。\")]),t._v(\" \"),s(\"h3\",{attrs:{id:\"alignment\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#alignment\"}},[t._v(\"#\")]),t._v(\" Alignment\")]),t._v(\" \"),s(\"p\",[s(\"code\",[t._v(\"Alignment\")]),t._v(\"继承自\"),s(\"code\",[t._v(\"AlignmentGeometry\")]),t._v(\"，表示矩形内的一个点，他有两个属性\"),s(\"code\",[t._v(\"x\")]),t._v(\"、\"),s(\"code\",[t._v(\"y\")]),t._v(\"，分别表示在水平和垂直方向的偏移，\"),s(\"code\",[t._v(\"Alignment\")]),t._v(\"定义如下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Alignment\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"x\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"y\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),s(\"p\",[s(\"code\",[t._v(\"Alignment\")]),t._v(\" Widget会以\"),s(\"strong\",[t._v(\"矩形的中心点作为坐标原点\")]),t._v(\"，即\"),s(\"code\",[t._v(\"Alignment(0.0, 0.0)\")]),t._v(\" 。\"),s(\"code\",[t._v(\"x\")]),t._v(\"、\"),s(\"code\",[t._v(\"y\")]),t._v(\"的值从-1到1分别代表矩形左边到右边的距离和顶部到底边的距离，因此2个水平（或垂直）单位则等于矩形的宽（或高），如\"),s(\"code\",[t._v(\"Alignment(-1.0, -1.0)\")]),t._v(\" 代表矩形的左侧顶点，而\"),s(\"code\",[t._v(\"Alignment(1.0, 1.0)\")]),t._v(\"代表右侧底部终点，而\"),s(\"code\",[t._v(\"Alignment(1.0, -1.0)\")]),t._v(\" 则正是右侧顶点，即\"),s(\"code\",[t._v(\"Alignment.topRight\")]),t._v(\"。为了使用方便，矩形的原点、四个顶点，以及四条边的终点在\"),s(\"code\",[t._v(\"Alignment\")]),t._v(\"类中都已经定义为了静态常量。\")]),t._v(\" \"),s(\"p\",[s(\"code\",[t._v(\"Alignment\")]),t._v(\"可以通过其\"),s(\"strong\",[t._v(\"坐标转换公式\")]),t._v(\"将其坐标转为子元素的具体偏移坐标：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language- extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[s(\"code\",[t._v(\"(Alignment.x*childWidth/2+childWidth/2, Alignment.y*childHeight/2+childHeight/2)\\n\")])])]),s(\"p\",[t._v(\"其中\"),s(\"code\",[t._v(\"childWidth\")]),t._v(\"为子元素的宽度，\"),s(\"code\",[t._v(\"childHeight\")]),t._v(\"为子元素高度。\")]),t._v(\" \"),s(\"p\",[t._v(\"现在我们再看看上面的示例，我们将\"),s(\"code\",[t._v(\"Alignment(1.0, -1.0)\")]),t._v(\"带入上面公式，可得\"),s(\"code\",[t._v(\"FlutterLogo\")]),t._v(\"的实际偏移坐标正是（60，0）。下面再看一个例子：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Align\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  widthFactor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  heightFactor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  alignment\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Alignment\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlutterLogo\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    size\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"60\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"我们可以先想象一下运行效果：将\"),s(\"code\",[t._v(\"Alignment(2,0.0)\")]),t._v(\"带入上述坐标转换公式，可以得到\"),s(\"code\",[t._v(\"FlutterLogo\")]),t._v(\"的实际偏移坐标为（90，30）。实际运行如图4-12所示：\")]),t._v(\" \"),s(\"p\",[s(\"img\",{attrs:{src:a(619),alt:\"图4-12\"}})]),t._v(\" \"),s(\"h3\",{attrs:{id:\"fractionaloffset\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#fractionaloffset\"}},[t._v(\"#\")]),t._v(\" FractionalOffset\")]),t._v(\" \"),s(\"p\",[s(\"code\",[t._v(\"FractionalOffset\")]),t._v(\" 继承自 \"),s(\"code\",[t._v(\"Alignment\")]),t._v(\"，它和 \"),s(\"code\",[t._v(\"Alignment\")]),t._v(\"唯一的区别就是坐标原点不同！\"),s(\"code\",[t._v(\"FractionalOffset\")]),t._v(\" 的坐标原点为矩形的左侧顶点，这和布局系统的一致，所以理解起来会比较容易。\"),s(\"code\",[t._v(\"FractionalOffset\")]),t._v(\"的坐标转换公式为：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language- extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[s(\"code\",[t._v(\"实际偏移 = (FractionalOffse.x * childWidth, FractionalOffse.y * childHeight)\\n\")])])]),s(\"p\",[t._v(\"下面看一个例子：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  height\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"120.0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  width\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"120.0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Align\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    alignment\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FractionalOffset\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.2\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.6\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlutterLogo\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      size\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"60\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"实际运行效果如图4-13所示下：\")]),t._v(\" \"),s(\"p\",[s(\"img\",{attrs:{src:a(620),alt:\"图4-13\"}})]),t._v(\" \"),s(\"p\",[t._v(\"我们将\"),s(\"code\",[t._v(\"FractionalOffset(0.2, 0.6)\")]),t._v(\"带入坐标转换公式得\"),s(\"code\",[t._v(\"FlutterLogo\")]),t._v(\"实际偏移为（12，36），和实际运行效果吻合。\")]),t._v(\" \"),s(\"h2\",{attrs:{id:\"_4-6-2-align和stack对比\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_4-6-2-align和stack对比\"}},[t._v(\"#\")]),t._v(\" 4.6.2 Align和Stack对比\")]),t._v(\" \"),s(\"p\",[t._v(\"可以看到，\"),s(\"code\",[t._v(\"Align\")]),t._v(\"和\"),s(\"code\",[t._v(\"Stack\")]),t._v(\"/\"),s(\"code\",[t._v(\"Positioned\")]),t._v(\"都可以用于指定子元素相对于父元素的偏移，但它们还是有两个主要区别：\")]),t._v(\" \"),s(\"ol\",[s(\"li\",[t._v(\"定位参考系统不同；\"),s(\"code\",[t._v(\"Stack\")]),t._v(\"/\"),s(\"code\",[t._v(\"Positioned\")]),t._v(\"定位的的参考系可以是父容器矩形的四个顶点；而\"),s(\"code\",[t._v(\"Align\")]),t._v(\"则需要先通过\"),s(\"code\",[t._v(\"alignment\")]),t._v(\" 参数来确定坐标原点，不同的\"),s(\"code\",[t._v(\"alignment\")]),t._v(\"会对应不同原点，最终的偏移是需要通过\"),s(\"code\",[t._v(\"alignment\")]),t._v(\"的转换公式来计算出。\")]),t._v(\" \"),s(\"li\",[s(\"code\",[t._v(\"Stack\")]),t._v(\"可以有多个子元素，并且子元素可以堆叠，而\"),s(\"code\",[t._v(\"Align\")]),t._v(\"只能有一个子元素，不存在堆叠。\")])]),t._v(\" \"),s(\"h2\",{attrs:{id:\"_4-6-3-center组件\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_4-6-3-center组件\"}},[t._v(\"#\")]),t._v(\" 4.6.3 Center组件\")]),t._v(\" \"),s(\"p\",[t._v(\"我们在前面章节的例子中已经使用过\"),s(\"code\",[t._v(\"Center\")]),t._v(\"组件来居中子元素了，现在我们正式来介绍一下它。通过查找SDK源码，我们看到\"),s(\"code\",[t._v(\"Center\")]),t._v(\"组件定义如下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Center\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Align\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" Key key\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" double widthFactor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" double heightFactor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Widget child \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" widthFactor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" widthFactor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" heightFactor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" heightFactor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"可以看到\"),s(\"code\",[t._v(\"Center\")]),t._v(\"继承自\"),s(\"code\",[t._v(\"Align\")]),t._v(\"，它比\"),s(\"code\",[t._v(\"Align\")]),t._v(\"只少了一个\"),s(\"code\",[t._v(\"alignment\")]),t._v(\" 参数；由于\"),s(\"code\",[t._v(\"Align\")]),t._v(\"的构造函数中\"),s(\"code\",[t._v(\"alignment\")]),t._v(\" 值为\"),s(\"code\",[t._v(\"Alignment.center\")]),t._v(\"，所以，我们可以认为\"),s(\"code\",[t._v(\"Center\")]),t._v(\"组件其实是对齐方式确定（\"),s(\"code\",[t._v(\"Alignment.center\")]),t._v(\"）了的\"),s(\"code\",[t._v(\"Align\")]),t._v(\"。\")]),t._v(\" \"),s(\"p\",[t._v(\"上面我们讲过当\"),s(\"code\",[t._v(\"widthFactor\")]),t._v(\"或\"),s(\"code\",[t._v(\"heightFactor\")]),t._v(\"为\"),s(\"code\",[t._v(\"null\")]),t._v(\"时组件的宽高将会占用尽可能多的空间，这一点需要特别注意，我们通过一个示例说明：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecoratedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"xxx\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecoratedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    widthFactor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    heightFactor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"xxx\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"运行效果如图4-14所示：\")]),t._v(\" \"),s(\"p\",[s(\"img\",{attrs:{src:a(621),alt:\"图4-14\"}})]),t._v(\" \"),s(\"h2\",{attrs:{id:\"总结\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),s(\"p\",[t._v(\"本节重点介绍了\"),s(\"code\",[t._v(\"Align\")]),t._v(\"组件及两种偏移类\"),s(\"code\",[t._v(\"Alignment\")]),t._v(\" 和\"),s(\"code\",[t._v(\"FractionalOffset\")]),t._v(\"，读者需要理解这两种偏移类的区别及各自的坐标转化公式。另外，在此建议读者在需要制定一些精确的偏移时应优先使用\"),s(\"code\",[t._v(\"FractionalOffset\")]),t._v(\"，因为它的坐标原点和布局系统相同，能更容易算出实际偏移。\")]),t._v(\" \"),s(\"p\",[t._v(\"在后面，我们又介绍了\"),s(\"code\",[t._v(\"Align\")]),t._v(\"组件和\"),s(\"code\",[t._v(\"Stack\")]),t._v(\"/\"),s(\"code\",[t._v(\"Positioned\")]),t._v(\"、\"),s(\"code\",[t._v(\"Center\")]),t._v(\"的关系，读者可以对比理解。\")]),t._v(\" \"),s(\"p\",[t._v(\"还有，熟悉Web开发的同学可能会发现\"),s(\"code\",[t._v(\"Align\")]),t._v(\"组件的特性和Web开发中相对定位（\"),s(\"code\",[t._v(\"position: relative\")]),t._v(\"）非常像，是的！在大多数时候，我们可以直接使用\"),s(\"code\",[t._v(\"Align\")]),t._v(\"组件来实现Web中相对定位的效果，读者可以类比记忆。\")])])}),[],!1,null,null,null);n.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/36.d9eeb78f.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[36],{623:function(t,n,s){t.exports=s.p+\"assets/img/4-1.86777353.png\"},624:function(t,n,s){t.exports=s.p+\"assets/img/4-2.c3de517d.png\"},625:function(t,n,s){t.exports=s.p+\"assets/img/4-3.cc8d50f6.png\"},626:function(t,n,s){t.exports=s.p+\"assets/img/4-4.00885ea7.png\"},897:function(t,n,s){\"use strict\";s.r(n);var a=s(45),e=Object(a.a)({},(function(){var t=this,n=t.$createElement,a=t._self._c||n;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_4-2-线性布局-row和column\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_4-2-线性布局-row和column\"}},[t._v(\"#\")]),t._v(\" 4.2 线性布局（Row和Column）\")]),t._v(\" \"),a(\"p\",[t._v(\"所谓线性布局，即指沿水平或垂直方向排布子组件。Flutter中通过\"),a(\"code\",[t._v(\"Row\")]),t._v(\"和\"),a(\"code\",[t._v(\"Column\")]),t._v(\"来实现线性布局，类似于Android中的\"),a(\"code\",[t._v(\"LinearLayout\")]),t._v(\"控件。\"),a(\"code\",[t._v(\"Row\")]),t._v(\"和\"),a(\"code\",[t._v(\"Column\")]),t._v(\"都继承自\"),a(\"code\",[t._v(\"Flex\")]),t._v(\"，我们将在弹性布局一节中详细介绍\"),a(\"code\",[t._v(\"Flex\")]),t._v(\"。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"主轴和纵轴\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#主轴和纵轴\"}},[t._v(\"#\")]),t._v(\" 主轴和纵轴\")]),t._v(\" \"),a(\"p\",[t._v(\"对于线性布局，有主轴和纵轴之分，如果布局是沿水平方向，那么主轴就是指水平方向，而纵轴即垂直方向；如果布局沿垂直方向，那么主轴就是指垂直方向，而纵轴就是水平方向。在线性布局中，有两个定义对齐方式的枚举类\"),a(\"code\",[t._v(\"MainAxisAlignment\")]),t._v(\"和\"),a(\"code\",[t._v(\"CrossAxisAlignment\")]),t._v(\"，分别代表主轴对齐和纵轴对齐。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"row\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#row\"}},[t._v(\"#\")]),t._v(\" Row\")]),t._v(\" \"),a(\"p\",[t._v(\"Row可以在水平方向排列其子widget。定义如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"  \\n  TextDirection textDirection\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"    \\n  MainAxisSize mainAxisSize \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" MainAxisSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"max\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"    \\n  MainAxisAlignment mainAxisAlignment \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" MainAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  VerticalDirection verticalDirection \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" VerticalDirection\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"down\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"  \\n  CrossAxisAlignment crossAxisAlignment \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" CrossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  List\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" children \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"ul\",[a(\"li\",[a(\"code\",[t._v(\"textDirection\")]),t._v(\"：表示水平方向子组件的布局顺序(是从左往右还是从右往左)，默认为系统当前Locale环境的文本方向(如中文、英语都是从左往右，而阿拉伯语是从右往左)。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"mainAxisSize\")]),t._v(\"：表示\"),a(\"code\",[t._v(\"Row\")]),t._v(\"在主轴(水平)方向占用的空间，默认是\"),a(\"code\",[t._v(\"MainAxisSize.max\")]),t._v(\"，表示尽可能多的占用水平方向的空间，此时无论子widgets实际占用多少水平空间，\"),a(\"code\",[t._v(\"Row\")]),t._v(\"的宽度始终等于水平方向的最大宽度；而\"),a(\"code\",[t._v(\"MainAxisSize.min\")]),t._v(\"表示尽可能少的占用水平空间，当子组件没有占满水平剩余空间，则\"),a(\"code\",[t._v(\"Row\")]),t._v(\"的实际宽度等于所有子组件占用的的水平空间；\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"mainAxisAlignment\")]),t._v(\"：表示子组件在\"),a(\"code\",[t._v(\"Row\")]),t._v(\"所占用的水平空间内对齐方式，如果\"),a(\"code\",[t._v(\"mainAxisSize\")]),t._v(\"值为\"),a(\"code\",[t._v(\"MainAxisSize.min\")]),t._v(\"，则此属性无意义，因为子组件的宽度等于\"),a(\"code\",[t._v(\"Row\")]),t._v(\"的宽度。只有当\"),a(\"code\",[t._v(\"mainAxisSize\")]),t._v(\"的值为\"),a(\"code\",[t._v(\"MainAxisSize.max\")]),t._v(\"时，此属性才有意义，\"),a(\"code\",[t._v(\"MainAxisAlignment.start\")]),t._v(\"表示沿\"),a(\"code\",[t._v(\"textDirection\")]),t._v(\"的初始方向对齐，如\"),a(\"code\",[t._v(\"textDirection\")]),t._v(\"取值为\"),a(\"code\",[t._v(\"TextDirection.ltr\")]),t._v(\"时，则\"),a(\"code\",[t._v(\"MainAxisAlignment.start\")]),t._v(\"表示左对齐，\"),a(\"code\",[t._v(\"textDirection\")]),t._v(\"取值为\"),a(\"code\",[t._v(\"TextDirection.rtl\")]),t._v(\"时表示从右对齐。而\"),a(\"code\",[t._v(\"MainAxisAlignment.end\")]),t._v(\"和\"),a(\"code\",[t._v(\"MainAxisAlignment.start\")]),t._v(\"正好相反；\"),a(\"code\",[t._v(\"MainAxisAlignment.center\")]),t._v(\"表示居中对齐。读者可以这么理解：\"),a(\"code\",[t._v(\"textDirection\")]),t._v(\"是\"),a(\"code\",[t._v(\"mainAxisAlignment\")]),t._v(\"的参考系。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"verticalDirection\")]),t._v(\"：表示\"),a(\"code\",[t._v(\"Row\")]),t._v(\"纵轴（垂直）的对齐方向，默认是\"),a(\"code\",[t._v(\"VerticalDirection.down\")]),t._v(\"，表示从上到下。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"crossAxisAlignment\")]),t._v(\"：表示子组件在纵轴方向的对齐方式，\"),a(\"code\",[t._v(\"Row\")]),t._v(\"的高度等于子组件中最高的子元素高度，它的取值和\"),a(\"code\",[t._v(\"MainAxisAlignment\")]),t._v(\"一样(包含\"),a(\"code\",[t._v(\"start\")]),t._v(\"、\"),a(\"code\",[t._v(\"end\")]),t._v(\"、 \"),a(\"code\",[t._v(\"center\")]),t._v(\"三个值)，不同的是\"),a(\"code\",[t._v(\"crossAxisAlignment\")]),t._v(\"的参考系是\"),a(\"code\",[t._v(\"verticalDirection\")]),t._v(\"，即\"),a(\"code\",[t._v(\"verticalDirection\")]),t._v(\"值为\"),a(\"code\",[t._v(\"VerticalDirection.down\")]),t._v(\"时\"),a(\"code\",[t._v(\"crossAxisAlignment.start\")]),t._v(\"指顶部对齐，\"),a(\"code\",[t._v(\"verticalDirection\")]),t._v(\"值为\"),a(\"code\",[t._v(\"VerticalDirection.up\")]),t._v(\"时，\"),a(\"code\",[t._v(\"crossAxisAlignment.start\")]),t._v(\"指底部对齐；而\"),a(\"code\",[t._v(\"crossAxisAlignment.end\")]),t._v(\"和\"),a(\"code\",[t._v(\"crossAxisAlignment.start\")]),t._v(\"正好相反；\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"children\")]),t._v(\" ：子组件数组。\")])]),t._v(\" \"),a(\"h3\",{attrs:{id:\"示例\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),a(\"p\",[t._v(\"请阅读下面代码，先想象一下运行的结果：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//测试Row对齐方式，排除Column默认居中对齐的干扰\")]),t._v(\"\\n  crossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" CrossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      mainAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\" hello world \"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\" I am Jack \"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      mainAxisSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"min\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      mainAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\" hello world \"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\" I am Jack \"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      mainAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"end\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      textDirection\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" TextDirection\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"rtl\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\" hello world \"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\" I am Jack \"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      crossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" CrossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"  \\n      verticalDirection\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" VerticalDirection\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"up\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\" hello world \"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" style\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"fontSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"30.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\" I am Jack \"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"实际运行结果如图4-1所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:s(623),alt:\"图4-1\"}})]),t._v(\" \"),a(\"p\",[t._v(\"解释：第一个\"),a(\"code\",[t._v(\"Row\")]),t._v(\"很简单，默认为居中对齐；第二个\"),a(\"code\",[t._v(\"Row\")]),t._v(\"，由于\"),a(\"code\",[t._v(\"mainAxisSize\")]),t._v(\"值为\"),a(\"code\",[t._v(\"MainAxisSize.min\")]),t._v(\"，\"),a(\"code\",[t._v(\"Row\")]),t._v(\"的宽度等于两个\"),a(\"code\",[t._v(\"Text\")]),t._v(\"的宽度和，所以对齐是无意义的，所以会从左往右显示；第三个\"),a(\"code\",[t._v(\"Row\")]),t._v(\"设置\"),a(\"code\",[t._v(\"textDirection\")]),t._v(\"值为\"),a(\"code\",[t._v(\"TextDirection.rtl\")]),t._v(\"，所以子组件会从右向左的顺序排列，而此时\"),a(\"code\",[t._v(\"MainAxisAlignment.end\")]),t._v(\"表示左对齐，所以最终显示结果就是图中第三行的样子；第四个Row测试的是纵轴的对齐方式，由于两个子Text字体不一样，所以其高度也不同，我们指定了\"),a(\"code\",[t._v(\"verticalDirection\")]),t._v(\"值为\"),a(\"code\",[t._v(\"VerticalDirection.up\")]),t._v(\"，即从低向顶排列，而此时\"),a(\"code\",[t._v(\"crossAxisAlignment\")]),t._v(\"值为\"),a(\"code\",[t._v(\"CrossAxisAlignment.start\")]),t._v(\"表示底对齐。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"column\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#column\"}},[t._v(\"#\")]),t._v(\" Column\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"Column\")]),t._v(\"可以在垂直方向排列其子组件。参数和\"),a(\"code\",[t._v(\"Row\")]),t._v(\"一样，不同的是布局方向为垂直，主轴纵轴正好相反，读者可类比\"),a(\"code\",[t._v(\"Row\")]),t._v(\"来理解，下面看一个例子：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CenterColumnRoute\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      crossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" CrossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"hi\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"world\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"运行效果如图4-2所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:s(624),alt:\"图4-2示例\"}})]),t._v(\" \"),a(\"p\",[t._v(\"解释：\")]),t._v(\" \"),a(\"ul\",[a(\"li\",[t._v(\"由于我们没有指定\"),a(\"code\",[t._v(\"Column\")]),t._v(\"的\"),a(\"code\",[t._v(\"mainAxisSize\")]),t._v(\"，所以使用默认值\"),a(\"code\",[t._v(\"MainAxisSize.max\")]),t._v(\"，则\"),a(\"code\",[t._v(\"Column\")]),t._v(\"会在垂直方向占用尽可能多的空间，此例中为屏幕高度。\")]),t._v(\" \"),a(\"li\",[t._v(\"由于我们指定了 \"),a(\"code\",[t._v(\"crossAxisAlignment\")]),t._v(\" 属性为\"),a(\"code\",[t._v(\"CrossAxisAlignment.center\")]),t._v(\"，那么子项在\"),a(\"code\",[t._v(\"Column\")]),t._v(\"纵轴方向（此时为水平方向）会居中对齐。注意，在水平方向对齐是有边界的，总宽度为\"),a(\"code\",[t._v(\"Column\")]),t._v(\"占用空间的实际宽度，而实际的宽度取决于子项中宽度最大的Widget。在本例中，\"),a(\"code\",[t._v(\"Column\")]),t._v(\"有两个子Widget，而显示“world”的\"),a(\"code\",[t._v(\"Text\")]),t._v(\"宽度最大，所以\"),a(\"code\",[t._v(\"Column\")]),t._v(\"的实际宽度则为\"),a(\"code\",[t._v('Text(\"world\")')]),t._v(\" 的宽度，所以居中对齐后\"),a(\"code\",[t._v('Text(\"hi\")')]),t._v(\"会显示在\"),a(\"code\",[t._v('Text(\"world\")')]),t._v(\"的中间部分。\")])]),t._v(\" \"),a(\"p\",[a(\"strong\",[t._v(\"实际上，\"),a(\"code\",[t._v(\"Row\")]),t._v(\"和\"),a(\"code\",[t._v(\"Column\")]),t._v(\"都只会在主轴方向占用尽可能大的空间，而纵轴的长度则取决于他们最大子元素的长度\")]),t._v(\"。如果我们想让本例中的两个文本控件在整个手机屏幕中间对齐，我们有两种方法：\")]),t._v(\" \"),a(\"ul\",[a(\"li\",[a(\"p\",[t._v(\"将\"),a(\"code\",[t._v(\"Column\")]),t._v(\"的宽度指定为屏幕宽度；这很简单，我们可以通过\"),a(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"或\"),a(\"code\",[t._v(\"SizedBox\")]),t._v(\"（我们将在后面章节中专门介绍这两个Widget）来强制更改宽度限制，例如：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ConstrainedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  constraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxConstraints\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"minWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" double\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"infinity\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    crossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" CrossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"hi\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"world\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"将\"),a(\"code\",[t._v(\"minWidth\")]),t._v(\"设为\"),a(\"code\",[t._v(\"double.infinity\")]),t._v(\"，可以使宽度占用尽可能多的空间。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"使用\"),a(\"code\",[t._v(\"Center\")]),t._v(\" Widget；我们将在后面章节中介绍。\")])])]),t._v(\" \"),a(\"h3\",{attrs:{id:\"特殊情况\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#特殊情况\"}},[t._v(\"#\")]),t._v(\" 特殊情况\")]),t._v(\" \"),a(\"p\",[t._v(\"如果\"),a(\"code\",[t._v(\"Row\")]),t._v(\"里面嵌套\"),a(\"code\",[t._v(\"Row\")]),t._v(\"，或者\"),a(\"code\",[t._v(\"Column\")]),t._v(\"里面再嵌套\"),a(\"code\",[t._v(\"Column\")]),t._v(\"，那么只有最外面的\"),a(\"code\",[t._v(\"Row\")]),t._v(\"或\"),a(\"code\",[t._v(\"Column\")]),t._v(\"会占用尽可能大的空间，里面\"),a(\"code\",[t._v(\"Row\")]),t._v(\"或\"),a(\"code\",[t._v(\"Column\")]),t._v(\"所占用的空间为实际大小，下面以\"),a(\"code\",[t._v(\"Column\")]),t._v(\"为例说明：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"green\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      crossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" CrossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      mainAxisSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"max\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//有效，外层Colum高度为整个屏幕\")]),t._v(\"\\n      children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            mainAxisSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"max\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//无效，内层Colum高度为实际高度  \")]),t._v(\"\\n            children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"hello world \"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"I am Jack \"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"运行效果如图4-3所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:s(625),alt:\"图4-3\"}})]),t._v(\" \"),a(\"p\",[t._v(\"如果要让里面的\"),a(\"code\",[t._v(\"Column\")]),t._v(\"占满外部\"),a(\"code\",[t._v(\"Column\")]),t._v(\"，可以使用\"),a(\"code\",[t._v(\"Expanded\")]),t._v(\" 组件：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Expanded\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \\n  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      mainAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//垂直方向居中对齐\")]),t._v(\"\\n      children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"hello world \"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"I am Jack \"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"运行效果如图4-4所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:s(626),alt:\"图4-4\"}})]),t._v(\" \"),a(\"p\",[t._v(\"我们将在介绍弹性布局时详细介绍Expanded。\")])])}),[],!1,null,null,null);n.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/37.7ecb6ef5.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[37],{678:function(t,a){t.exports=\"data:image/png;base64,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xsZG22+//WzBggWm/axfv960jyFDhlhTU1N4HQWJww47jKBQUZoNCMQlQFCIq74oLQI/SmDr1q3hpP7dd99VfL5aEtJFAeCbb76xESNGhKCgboxly5aF4HDJJZeElge1Kjz55JPW0tJi559/vg0fPjw8fdy4cTZgwID0pbhFAIHIBQgKkVcgxUdAAvoUP2fOHPv4449t7dq1Nm/evHACf+yxx2zGjBl2xBFH2NVXXx3GLVQSS1sEKm3X+sGDB9ukSZPsvffeCy0MCh56nropGhoabPz48XbwwQdb3759d/cybEMAgYgECAoRVRZFRaCSgD7Vv/vuu3brrbeGLgGNSVALwb333mtJkthtt91mJ510UqWn97he3Qynn3566EJQl8XSpUvD4+bPn2833XST9enTJwSELVu2hJYFdUGcccYZNnHiRNtnn316fE1WIoBAfAIEhfjqjBIjsIuAxhacfPLJ4VP922+/bS+88EIYS3D22WeHVgCNUdAJfk/Ldddd1/kQjUt47bXXwrgEhYENGzaEbWq90P1TTjklhAU9RiHkq6++CsGhaxdG54txBwEEohUgKERbdRQcgR0C+vR/9NFHh+mQ+uSvgYbqDlC3wAknnGD33Xefvf/++zueUOHetddeG8YkXHDBBTZ16tSdHqXWBQ1u1Gtqf6NHjw5dDGo9mDx5cmeQ2OlJ/IIAAtELEBSir0IOAAELgwvVxbB69eow40FBQZ/6NUZh4MCBdt5559m5554bqGbPnm0PPvignXnmmaEloOvYhEKhELop1PrQU8uAXj+dGqkBjury0HgEBi/yLkSgdgUICrVbtxxZnQnopD1r1qzOloNDDjkkCLzyyit21FFHhYGGagnQdMennnrKjjzySNM1DzTD4ZhjjgndFrpOgrop1CqhoKBWiUWLFoUZDfvvv394vXSKpbom9BgFEQ1yZEEAgdoUICjUZr1yVHUooOmMb775ZggCmtao2Qe68JFO4goIN998cwgL6YWVtO6JJ54I4xnuv/9+O/zww4Pa9ddfby+99JI988wzIUBoBsX06dPt4osvDtvfeecdu+GGG0JrgsYraHwEsxzq8A3HIdeNAEGhbqqaA61lAU1T1Il95syZYaaCPvWrK+LUU08N4weee+45+/DDD8OJX7MV0kUtDXfddZe9+uqr4XFqbTjooINMXRBffPGFTZs2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\"},679:function(t,a,s){t.exports=s.p+\"assets/img/7-5.6f1c5012.jpeg\"},680:function(t,a,s){t.exports=s.p+\"assets/img/7-6.3bce9f21.png\"},681:function(t,a,s){t.exports=s.p+\"assets/img/7-7.b9c5d9fe.png\"},919:function(t,a,s){\"use strict\";s.r(a);var n=s(45),e=Object(n.a)({},(function(){var t=this,a=t.$createElement,n=t._self._c||a;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_7-4-颜色和主题\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_7-4-颜色和主题\"}},[t._v(\"#\")]),t._v(\" 7.4 颜色和主题\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_7-4-1-颜色\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_7-4-1-颜色\"}},[t._v(\"#\")]),t._v(\" 7.4.1 颜色\")]),t._v(\" \"),n(\"p\",[t._v(\"在介绍主题前我们先了解一些Flutter中的Color类。Color类中颜色以一个int值保存，我们知道显示器颜色是由红、绿、蓝三基色组成，每种颜色占8比特，存储结构如下：\")]),t._v(\" \"),n(\"table\",[n(\"thead\",[n(\"tr\",[n(\"th\",[t._v(\"Bit（位）\")]),t._v(\" \"),n(\"th\",[t._v(\"颜色\")])])]),t._v(\" \"),n(\"tbody\",[n(\"tr\",[n(\"td\",[t._v(\"0-7\")]),t._v(\" \"),n(\"td\",[t._v(\"蓝色\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"8-15\")]),t._v(\" \"),n(\"td\",[t._v(\"绿色\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"16-23\")]),t._v(\" \"),n(\"td\",[t._v(\"红色\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"24-31\")]),t._v(\" \"),n(\"td\",[t._v(\"Alpha (不透明度)\")])])])]),t._v(\" \"),n(\"p\",[t._v(\"上面表格中的的字段在Color类中都有对应的属性，而Color中的众多方法也就是操作这些属性的，由于大多比较简单，读者可以查看类定义了解。在此我们主要讨论两点：色值转换和亮度。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"如何将颜色字符串转成color对象\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#如何将颜色字符串转成color对象\"}},[t._v(\"#\")]),t._v(\" \"),n(\"strong\",[t._v(\"如何将颜色字符串转成Color对象\")])]),t._v(\" \"),n(\"p\",[t._v('如Web开发中的色值通常是一个字符串如\"#dc380d\"，它是一个RGB值，我们可以通过下面这些方法将其转为Color类：')]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Color\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0xffdc380d\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//如果颜色固定可以直接使用整数值\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//颜色是一个字符串变量\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" c \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"dc380d\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Color\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"parse\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"c\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"radix\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"|\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0xFF000000\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//通过位运算符将Alpha设置为FF\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Color\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"parse\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"c\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"radix\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"withAlpha\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"255\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//通过方法将Alpha设置为FF\")]),t._v(\"\\n\")])])]),n(\"h3\",{attrs:{id:\"颜色亮度\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#颜色亮度\"}},[t._v(\"#\")]),t._v(\" 颜色亮度\")]),t._v(\" \"),n(\"p\",[t._v(\"假如，我们要实现一个背景颜色和Title可以自定义的导航栏，并且背景色为深色时我们应该让Title显示为浅色；背景色为浅色时，Title显示为深色。要实现这个功能，我们就需要来计算背景色的亮度，然后动态来确定Title的颜色。Color类中提供了一个\"),n(\"code\",[t._v(\"computeLuminance()\")]),t._v(\"方法，它可以返回一个[0-1]的一个值，数字越大颜色就越浅，我们可以根据它来动态确定Title的颜色，下面是导航栏NavBar的简单实现：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"NavBar\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Color color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//背景颜色\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"NavBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      constraints\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxConstraints\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        minHeight\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"52\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        minWidth\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" double\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"infinity\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      decoration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        boxShadow\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//阴影\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxShadow\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"black26\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            blurRadius\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          fontWeight\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" FontWeight\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"bold\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//根据背景色亮度来确定Title颜色\")]),t._v(\"\\n          color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"computeLuminance\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.5\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"black\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"测试代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//背景为蓝色，则title自动为白色\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"NavBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"标题\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//背景为白色，则title自动为黑色\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"NavBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"标题\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图7-4所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(678),alt:\"NavBar\"}})]),t._v(\" \"),n(\"h3\",{attrs:{id:\"materialcolor\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#materialcolor\"}},[t._v(\"#\")]),t._v(\" MaterialColor\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"MaterialColor\")]),t._v(\"是实现Material Design中的颜色的类，它包含一种颜色的10个级别的渐变色。\"),n(\"code\",[t._v(\"MaterialColor\")]),t._v('通过\"[]\"运算符的索引值来代表颜色的深度，有效的索引有：50，100，200，…，900，数字越大，颜色越深。'),n(\"code\",[t._v(\"MaterialColor\")]),t._v(\"的默认值为索引等于500的颜色。举个例子，\"),n(\"code\",[t._v(\"Colors.blue\")]),t._v(\"是预定义的一个\"),n(\"code\",[t._v(\"MaterialColor\")]),t._v(\"类对象，定义如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" MaterialColor blue \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MaterialColor\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  _bluePrimaryValue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"int\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Color\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Color\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0xFFE3F2FD\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Color\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0xFFBBDEFB\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Color\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0xFF90CAF9\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Color\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0xFF64B5F6\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"400\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Color\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0xFF42A5F5\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"500\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Color\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_bluePrimaryValue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"600\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Color\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0xFF1E88E5\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"700\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Color\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0xFF1976D2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"800\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Color\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0xFF1565C0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"900\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Color\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0xFF0D47A1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" int _bluePrimaryValue \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0xFF2196F3\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"Colors.blue[50]\")]),t._v(\"到\"),n(\"code\",[t._v(\"Colors.blue[900]\")]),t._v(\"的色值从浅蓝到深蓝渐变，效果如图7-5所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(679),alt:\"NavBar\"}})]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_7-4-2-theme\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_7-4-2-theme\"}},[t._v(\"#\")]),t._v(\" 7.4.2 Theme\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"Theme\")]),t._v(\"组件可以为Material APP定义主题数据（ThemeData）。Material组件库里很多组件都使用了主题数据，如导航栏颜色、标题字体、Icon样式等。\"),n(\"code\",[t._v(\"Theme\")]),t._v(\"内会使用\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"来为其子树共享样式数据。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"themedata\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#themedata\"}},[t._v(\"#\")]),t._v(\" ThemeData\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"ThemeData\")]),t._v(\"用于保存是Material 组件库的主题数据，Material组件需要遵守相应的设计规范，而这些规范可自定义部分都定义在ThemeData中了，所以我们可以通过ThemeData来自定义应用主题。在子组件中，我们可以通过\"),n(\"code\",[t._v(\"Theme.of\")]),t._v(\"方法来获取当前的\"),n(\"code\",[t._v(\"ThemeData\")]),t._v(\"。\")]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[t._v(\"注意：Material Design 设计规范中有些是不能自定义的，如导航栏高度，ThemeData只包含了可自定义部分。\")])]),t._v(\" \"),n(\"p\",[t._v(\"我们看看\"),n(\"code\",[t._v(\"ThemeData\")]),t._v(\"部分数据定义：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ThemeData\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  Brightness brightness\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//深色还是浅色\")]),t._v(\"\\n  MaterialColor primarySwatch\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//主题颜色样本，见下面介绍\")]),t._v(\"\\n  Color primaryColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//主色，决定导航栏颜色\")]),t._v(\"\\n  Color accentColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//次级色，决定大多数Widget的颜色，如进度条、开关等。\")]),t._v(\"\\n  Color cardColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//卡片颜色\")]),t._v(\"\\n  Color dividerColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//分割线颜色\")]),t._v(\"\\n  ButtonThemeData buttonTheme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//按钮主题\")]),t._v(\"\\n  Color cursorColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//输入框光标颜色\")]),t._v(\"\\n  Color dialogBackgroundColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//对话框背景颜色\")]),t._v(\"\\n  String fontFamily\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//文字字体\")]),t._v(\"\\n  TextTheme textTheme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 字体主题，包括标题、body等文字样式\")]),t._v(\"\\n  IconThemeData iconTheme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// Icon的默认样式\")]),t._v(\"\\n  TargetPlatform platform\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//指定平台，应用特定平台控件风格\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上面只是\"),n(\"code\",[t._v(\"ThemeData\")]),t._v(\"的一小部分属性，完整的数据定义读者可以查看SDK。上面属性中需要说明的是\"),n(\"code\",[t._v(\"primarySwatch\")]),t._v('，它是主题颜色的一个\"样本色\"，通过这个样本色可以在一些条件下生成一些其它的属性，例如，如果没有指定'),n(\"code\",[t._v(\"primaryColor\")]),t._v(\"，并且当前主题不是深色主题，那么\"),n(\"code\",[t._v(\"primaryColor\")]),t._v(\"就会默认为\"),n(\"code\",[t._v(\"primarySwatch\")]),t._v(\"指定的颜色，还有一些相似的属性如\"),n(\"code\",[t._v(\"accentColor\")]),t._v(\" 、\"),n(\"code\",[t._v(\"indicatorColor\")]),t._v(\"等也会受\"),n(\"code\",[t._v(\"primarySwatch\")]),t._v(\"影响。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"示例\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),n(\"p\",[t._v(\"我们实现一个路由换肤功能：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ThemeTestRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _ThemeTestRouteState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_ThemeTestRouteState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_ThemeTestRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ThemeTestRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  Color _themeColor \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"teal\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//当前路由主题色\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    ThemeData themeData \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Theme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Theme\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      data\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ThemeData\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          primarySwatch\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _themeColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//用于导航栏、FloatingActionButton的背景色等\")]),t._v(\"\\n          iconTheme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"IconThemeData\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _themeColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//用于Icon颜色\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        appBar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"主题测试\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        body\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          mainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//第一行Icon使用主题中的iconTheme\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                mainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"favorite\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"airport_shuttle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"  颜色跟随主题\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//为第二行Icon自定义颜色（固定为黑色)\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Theme\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              data\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" themeData\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"copyWith\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                iconTheme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" themeData\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"iconTheme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"copyWith\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"black\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  mainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"favorite\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"airport_shuttle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"  颜色固定黑色\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        floatingActionButton\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FloatingActionButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//切换主题\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"\\n                _themeColor \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"\\n                _themeColor \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"teal \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"teal\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"palette\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行后点击右下角悬浮按钮则可以切换主题，如图7-6、7-7所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(680),alt:\"图7-6\"}}),n(\"img\",{attrs:{src:s(681),alt:\"图7-7\"}})]),t._v(\" \"),n(\"p\",[t._v(\"需要注意的有三点：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"p\",[t._v(\"可以通过局部主题覆盖全局主题，正如代码中通过Theme为第二行图标指定固定颜色（黑色）一样，这是一种常用的技巧，Flutter中会经常使用这种方法来自定义子树主题。那么为什么局部主题可以覆盖全局主题？这主要是因为widget中使用主题样式时是通过\"),n(\"code\",[t._v(\"Theme.of(BuildContext context)\")]),t._v(\"来获取的，我们看看其简化后的代码：\")])]),t._v(\" \"),n(\"li\",[n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" ThemeData \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" bool shadowThemeOnly \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 简化代码，并非源码  \")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dependOnInheritedWidgetOfExactType\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"_InheritedTheme\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"theme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"context.dependOnInheritedWidgetOfExactType\")]),t._v(\" 会在widget树中从当前位置向上查找第一个类型为\"),n(\"code\",[t._v(\"_InheritedTheme\")]),t._v(\"的widget。所以当局部指定\"),n(\"code\",[t._v(\"Theme\")]),t._v(\"后，其子树中通过\"),n(\"code\",[t._v(\"Theme.of()\")]),t._v(\"向上查找到的第一个\"),n(\"code\",[t._v(\"_InheritedTheme\")]),t._v(\"便是我们指定的\"),n(\"code\",[t._v(\"Theme\")]),t._v(\"。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"本示例是对单个路由换肤，如果想要对整个应用换肤，则可以去修改\"),n(\"code\",[t._v(\"MaterialApp\")]),t._v(\"的\"),n(\"code\",[t._v(\"theme\")]),t._v(\"属性。\")])])])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/38.260a994c.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[38],{682:function(t,s,a){t.exports=a.p+\"assets/img/8-2.52bd91bd.png\"},683:function(t,s,a){t.exports=a.p+\"assets/img/8-3.975afea6.png\"},684:function(t,s,a){t.exports=a.p+\"assets/img/8-4.6948ddf4.png\"},685:function(t,s,a){t.exports=a.p+\"assets/img/8-5.ba487ffd.png\"},922:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_8-2-手势识别\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_8-2-手势识别\"}},[t._v(\"#\")]),t._v(\" 8.2 手势识别\")]),t._v(\" \"),n(\"p\",[t._v(\"本节先介绍一些Flutter中用于处理手势的\"),n(\"code\",[t._v(\"GestureDetector\")]),t._v(\"和\"),n(\"code\",[t._v(\"GestureRecognizer\")]),t._v(\"，然后再仔细讨论一下手势竞争与冲突问题。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_8-2-1-gesturedetector\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_8-2-1-gesturedetector\"}},[t._v(\"#\")]),t._v(\" 8.2.1 GestureDetector\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"GestureDetector\")]),t._v(\"是一个用于手势识别的功能性组件，我们通过它可以来识别各种手势。\"),n(\"code\",[t._v(\"GestureDetector\")]),t._v(\"实际上是指针事件的语义化封装，接下来我们详细介绍一下各种手势识别。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"点击、双击、长按\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#点击、双击、长按\"}},[t._v(\"#\")]),t._v(\" 点击、双击、长按\")]),t._v(\" \"),n(\"p\",[t._v(\"我们通过\"),n(\"code\",[t._v(\"GestureDetector\")]),t._v(\"对\"),n(\"code\",[t._v(\"Container\")]),t._v(\"进行手势识别，触发相应事件后，在\"),n(\"code\",[t._v(\"Container\")]),t._v(\"上显示事件名，为了增大点击区域，将\"),n(\"code\",[t._v(\"Container\")]),t._v(\"设置为200×100，代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"GestureDetectorTestRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _GestureDetectorTestRouteState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_GestureDetectorTestRouteState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_GestureDetectorTestRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"GestureDetectorTestRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  String _operation \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"No Gesture detected!\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//保存事件名\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GestureDetector\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n          height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_operation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        onTap\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"updateText\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Tap\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//点击\")]),t._v(\"\\n        onDoubleTap\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"updateText\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"DoubleTap\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//双击\")]),t._v(\"\\n        onLongPress\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"updateText\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"LongPress\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//长按\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"updateText\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//更新显示的事件名\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _operation \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图8-2所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(682),alt:\"图8-2\"}})]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[n(\"strong\",[t._v(\"注意\")]),t._v(\"： 当同时监听\"),n(\"code\",[t._v(\"onTap\")]),t._v(\"和\"),n(\"code\",[t._v(\"onDoubleTap\")]),t._v(\"事件时，当用户触发tap事件时，会有200毫秒左右的延时，这是因为当用户点击完之后很可能会再次点击以触发双击事件，所以\"),n(\"code\",[t._v(\"GestureDetector\")]),t._v(\"会等一段时间来确定是否为双击事件。如果用户只监听了\"),n(\"code\",[t._v(\"onTap\")]),t._v(\"（没有监听\"),n(\"code\",[t._v(\"onDoubleTap\")]),t._v(\"）事件时，则没有延时。\")])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"拖动、滑动\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#拖动、滑动\"}},[t._v(\"#\")]),t._v(\" 拖动、滑动\")]),t._v(\" \"),n(\"p\",[t._v(\"一次完整的手势过程是指用户手指按下到抬起的整个过程，期间，用户按下手指后可能会移动，也可能不会移动。\"),n(\"code\",[t._v(\"GestureDetector\")]),t._v(\"对于拖动和滑动事件是没有区分的，他们本质上是一样的。\"),n(\"code\",[t._v(\"GestureDetector\")]),t._v(\"会将要监听的组件的原点（左上角）作为本次手势的原点，当用户在监听的组件上按下手指时，手势识别就会开始。下面我们看一个拖动圆形字母A的示例：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_Drag\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _DragState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_DragState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_DragState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"_Drag\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"with\")]),t._v(\" SingleTickerProviderStateMixin \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  double _top \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//距顶部的偏移\")]),t._v(\"\\n  double _left \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//距左边的偏移\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Stack\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Positioned\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          top\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _top\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          left\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _left\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GestureDetector\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircleAvatar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"A\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//手指按下时会触发此回调\")]),t._v(\"\\n            onPanDown\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"DragDownDetails e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//打印手指按下的位置(相对于屏幕)\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"用户手指按下：${e.globalPosition}\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//手指滑动时会触发此回调\")]),t._v(\"\\n            onPanUpdate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"DragUpdateDetails e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//用户手指滑动时，更新偏移，重新构建\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                _left \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+=\")]),t._v(\" e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"delta\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dx\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                _top \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+=\")]),t._v(\" e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"delta\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dy\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onPanEnd\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"DragEndDetails e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//打印滑动结束时在x、y轴上的速度\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"velocity\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行后，就可以在任意方向拖动了，运行效果如图8-3所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(683),alt:\"图8-3\"}})]),t._v(\" \"),n(\"p\",[t._v(\"日志：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language- extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[n(\"code\",[t._v(\"I/flutter ( 8513): 用户手指按下：Offset(26.3, 101.8)\\nI/flutter ( 8513): Velocity(235.5, 125.8)\\n\")])])]),n(\"p\",[t._v(\"代码解释：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"DragDownDetails.globalPosition\")]),t._v(\"：当用户按下时，此属性为用户按下的位置相对于\"),n(\"strong\",[t._v(\"屏幕\")]),t._v(\"（而非父组件）原点(左上角)的偏移。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"DragUpdateDetails.delta\")]),t._v(\"：当用户在屏幕上滑动时，会触发多次Update事件，\"),n(\"code\",[t._v(\"delta\")]),t._v(\"指一次Update事件的滑动的偏移量。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"DragEndDetails.velocity\")]),t._v(\"：该属性代表用户抬起手指时的滑动速度(包含x、y两个轴的），示例中并没有处理手指抬起时的速度，常见的效果是根据用户抬起手指时的速度做一个减速动画。\")])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"单一方向拖动\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#单一方向拖动\"}},[t._v(\"#\")]),t._v(\" 单一方向拖动\")]),t._v(\" \"),n(\"p\",[t._v(\"在本示例中，是可以朝任意方向拖动的，但是在很多场景，我们只需要沿一个方向来拖动，如一个垂直方向的列表，\"),n(\"code\",[t._v(\"GestureDetector\")]),t._v(\"可以只识别特定方向的手势事件，我们将上面的例子改为只能沿垂直方向拖动：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_DragVertical\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _DragVerticalState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_DragVerticalState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_DragVerticalState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"_DragVertical\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  double _top \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Stack\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Positioned\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          top\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _top\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GestureDetector\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircleAvatar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"A\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//垂直方向拖动事件\")]),t._v(\"\\n            onVerticalDragUpdate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"DragUpdateDetails details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                _top \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+=\")]),t._v(\" details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"delta\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dy\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"这样就只能在垂直方向拖动了，如果只想在水平方向滑动同理。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"缩放\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#缩放\"}},[t._v(\"#\")]),t._v(\" 缩放\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"GestureDetector\")]),t._v(\"可以监听缩放事件，下面示例演示了一个简单的图片缩放效果：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_ScaleTestRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"_ScaleTestRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  double _width \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//通过修改图片宽度来达到缩放效果\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n     child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GestureDetector\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//指定宽度，高度自适应\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"asset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"./images/sea.png\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        onScaleUpdate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ScaleUpdateDetails details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//缩放倍数在0.8到10倍之间\")]),t._v(\"\\n            _width\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\"details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"scale\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"clamp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".8\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"10.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图8-4所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(684),alt:\"图8-4\"}})]),t._v(\" \"),n(\"p\",[t._v(\"现在在图片上双指张开、收缩就可以放大、缩小图片。本示例比较简单，实际中我们通常还需要一些其它功能，如双击放大或缩小一定倍数、双指张开离开屏幕时执行一个减速放大动画等，读者可以在学习完后面“动画”一章中的内容后自己来尝试实现一下。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_8-2-2-gesturerecognizer\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_8-2-2-gesturerecognizer\"}},[t._v(\"#\")]),t._v(\" 8.2.2 GestureRecognizer\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"GestureDetector\")]),t._v(\"内部是使用一个或多个\"),n(\"code\",[t._v(\"GestureRecognizer\")]),t._v(\"来识别各种手势的，而\"),n(\"code\",[t._v(\"GestureRecognizer\")]),t._v(\"的作用就是通过\"),n(\"code\",[t._v(\"Listener\")]),t._v(\"来将原始指针事件转换为语义手势，\"),n(\"code\",[t._v(\"GestureDetector\")]),t._v(\"直接可以接收一个子widget。\"),n(\"code\",[t._v(\"GestureRecognizer\")]),t._v(\"是一个抽象类，一种手势的识别器对应一个\"),n(\"code\",[t._v(\"GestureRecognizer\")]),t._v(\"的子类，Flutter实现了丰富的手势识别器，我们可以直接使用。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"示例\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),n(\"p\",[t._v(\"假设我们要给一段富文本（\"),n(\"code\",[t._v(\"RichText\")]),t._v(\"）的不同部分分别添加点击事件处理器，但是\"),n(\"code\",[t._v(\"TextSpan\")]),t._v(\"并不是一个widget，这时我们不能用\"),n(\"code\",[t._v(\"GestureDetector\")]),t._v(\"，但\"),n(\"code\",[t._v(\"TextSpan\")]),t._v(\"有一个\"),n(\"code\",[t._v(\"recognizer\")]),t._v(\"属性，它可以接收一个\"),n(\"code\",[t._v(\"GestureRecognizer\")]),t._v(\"。\")]),t._v(\" \"),n(\"p\",[t._v(\"假设我们需要在点击时给文本变色:\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/gestures.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_GestureRecognizerTestRouteState\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"_GestureRecognizerTestRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  TapGestureRecognizer _tapGestureRecognizer \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TapGestureRecognizer\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  bool _toggle \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//变色开关\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//用到GestureRecognizer的话一定要调用其dispose方法释放资源\")]),t._v(\"\\n    _tapGestureRecognizer\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"rich\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextSpan\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextSpan\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"你好世界\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextSpan\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"点我变色\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                      fontSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"30.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _toggle \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  recognizer\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _tapGestureRecognizer\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onTap \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                        _toggle \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"_toggle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextSpan\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"你好世界\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(685),alt:\"图8-5\"}})]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[t._v(\"注意：使用\"),n(\"code\",[t._v(\"GestureRecognizer\")]),t._v(\"后一定要调用其\"),n(\"code\",[t._v(\"dispose()\")]),t._v(\"方法来释放资源（主要是取消内部的计时器）。\")])]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_8-2-3-手势竞争与冲突\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_8-2-3-手势竞争与冲突\"}},[t._v(\"#\")]),t._v(\" 8.2.3 手势竞争与冲突\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"竞争\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#竞争\"}},[t._v(\"#\")]),t._v(\" 竞争\")]),t._v(\" \"),n(\"p\",[t._v(\"如果在上例中我们同时监听水平和垂直方向的拖动事件，那么我们斜着拖动时哪个方向会生效？实际上取决于第一次移动时两个轴上的位移分量，哪个轴的大，哪个轴在本次滑动事件竞争中就胜出。实际上Flutter中的手势识别引入了一个Arena的概念，Arena直译为“竞技场”的意思，每一个手势识别器（\"),n(\"code\",[t._v(\"GestureRecognizer\")]),t._v(\"）都是一个“竞争者”（\"),n(\"code\",[t._v(\"GestureArenaMember\")]),t._v(\"），当发生滑动事件时，他们都要在“竞技场”去竞争本次事件的处理权，而最终只有一个“竞争者”会胜出(win)。例如，假设有一个\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"，它的第一个子组件也是\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"，如果现在滑动这个子\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"，父\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"会动吗？答案是否定的，这时只有子\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"会动，因为这时子\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"会胜出而获得滑动事件的处理权。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"示例-2\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例-2\"}},[t._v(\"#\")]),t._v(\" \"),n(\"strong\",[t._v(\"示例\")])]),t._v(\" \"),n(\"p\",[t._v(\"我们以拖动手势为例，同时识别水平和垂直方向的拖动手势，当用户按下手指时就会触发竞争（水平方向和垂直方向），一旦某个方向“获胜”，则直到当次拖动手势结束都会沿着该方向移动。代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"BothDirectionTestRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  BothDirectionTestRouteState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"BothDirectionTestRouteState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"BothDirectionTestRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"BothDirectionTestRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  double _top \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  double _left \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Stack\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Positioned\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          top\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _top\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          left\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _left\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GestureDetector\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircleAvatar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"A\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//垂直方向拖动事件\")]),t._v(\"\\n            onVerticalDragUpdate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"DragUpdateDetails details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                _top \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+=\")]),t._v(\" details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"delta\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dy\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onHorizontalDragUpdate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"DragUpdateDetails details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                _left \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+=\")]),t._v(\" details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"delta\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dx\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"此示例运行后，每次拖动只会沿一个方向移动（水平或垂直），而竞争发生在手指按下后首次移动（move）时，此例中具体的“获胜”条件是：首次移动时的位移在水平和垂直方向上的分量大的一个获胜。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"手势冲突\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#手势冲突\"}},[t._v(\"#\")]),t._v(\" 手势冲突\")]),t._v(\" \"),n(\"p\",[t._v(\"由于手势竞争最终只有一个胜出者，所以，当有多个手势识别器时，可能会产生冲突。假设有一个widget，它可以左右拖动，现在我们也想检测在它上面手指按下和抬起的事件，代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"GestureConflictTestRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"GestureConflictTestRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  double _left \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Stack\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Positioned\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          left\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _left\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GestureDetector\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircleAvatar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"A\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//要拖动和点击的widget\")]),t._v(\"\\n              onHorizontalDragUpdate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"DragUpdateDetails details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                  _left \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+=\")]),t._v(\" details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"delta\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dx\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              onHorizontalDragEnd\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"onHorizontalDragEnd\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              onTapDown\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"down\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              onTapUp\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"up\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"现在我们按住圆形“A”拖动然后抬起手指，控制台日志如下:\")]),t._v(\" \"),n(\"div\",{staticClass:\"language- extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[n(\"code\",[t._v(\"I/flutter (17539): down\\nI/flutter (17539): onHorizontalDragEnd\\n\")])])]),n(\"p\",[t._v('我们发现没有打印\"up\"，这是因为在拖动时，刚开始按下手指时在没有移动时，拖动手势还没有完整的语义，此时TapDown手势胜出(win)，此时打印\"down\"，而拖动时，拖动手势会胜出，当手指抬起时，'),n(\"code\",[t._v(\"onHorizontalDragEnd\")]),t._v(\" 和 \"),n(\"code\",[t._v(\"onTapUp\")]),t._v(\"发生了冲突，但是因为是在拖动的语义中，所以\"),n(\"code\",[t._v(\"onHorizontalDragEnd\")]),t._v(\"胜出，所以就会打印 “onHorizontalDragEnd”。如果我们的代码逻辑中，对于手指按下和抬起是强依赖的，比如在一个轮播图组件中，我们希望手指按下时，暂停轮播，而抬起时恢复轮播，但是由于轮播图组件中本身可能已经处理了拖动手势（支持手动滑动切换），甚至可能也支持了缩放手势，这时我们如果在外部再用\"),n(\"code\",[t._v(\"onTapDown\")]),t._v(\"、\"),n(\"code\",[t._v(\"onTapUp\")]),t._v(\"来监听的话是不行的。这时我们应该怎么做？其实很简单，通过Listener监听原始指针事件就行：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Positioned\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  top\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  left\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _leftB\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Listener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    onPointerDown\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"down\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    onPointerUp\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//会触发\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"up\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GestureDetector\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircleAvatar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"B\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      onHorizontalDragUpdate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"DragUpdateDetails details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          _leftB \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+=\")]),t._v(\" details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"delta\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dx\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      onHorizontalDragEnd\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"onHorizontalDragEnd\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"手势冲突只是手势级别的，而手势是对原始指针的语义化的识别，所以在遇到复杂的冲突场景时，都可以通过\"),n(\"code\",[t._v(\"Listener\")]),t._v(\"直接识别原始指针事件来解决冲突。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/39.c3d0c942.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[39],{328:function(t,e,a){},335:function(t,e,a){t.exports=a.p+\"assets/img/book.17ed07e5.png\"},359:function(t,e,a){\"use strict\";a(328)},718:function(t,e,a){\"use strict\";a.r(e);a(164);var n,o=a(715),i=a(713),s=a(714),r=a(716),c=a(305);var h=[\"/join_us.html\"],l={name:\"Layout\",components:{Home:o.a,Page:s.a,Sidebar:r.a,Navbar:i.a},data:function(){return{isSidebarOpen:!1,showBook:!1}},computed:{shouldShowNavbar:function(){var t=this.$site.themeConfig;return!1!==this.$page.frontmatter.navbar&&!1!==t.navbar&&(this.$title||t.logo||t.repo||t.nav||this.$themeLocaleConfig.nav)},shouldShowSidebar:function(){var t=this.$page.frontmatter;return!t.home&&!1!==t.sidebar&&this.sidebarItems.length},sidebarItems:function(){return Object(c.l)(this.$page,this.$page.regularPath,this.$site,this.$localePath)},pageClasses:function(){var t=this.$page.frontmatter.pageClass;return[{\"no-navbar\":!this.shouldShowNavbar,\"sidebar-open\":this.isSidebarOpen,\"no-sidebar\":!this.shouldShowSidebar},t]}},mounted:function(){var t=this;!function(){n=n||[];var t=document.createElement(\"script\");t.src=\"https://hm.baidu.com/hm.js?170231fea4f81697eb046edc1a91fe5b\";var e=document.getElementsByTagName(\"script\")[0];t.id=\"bd\",e.parentNode.insertBefore(t,e)}(),this.lastPathname=\"\",this.$router.afterEach((function(){if(-1===h.indexOf(location.pathname)&&(t.showBook=!0),t.lastPathname!==location.pathname){console.log(location.pathname),n.push([\"_trackPageview\",t.lastPathname=location.pathname]);var e=window.location.href,a=document.referrer;if(!/([http|https]:\\/\\/[a-zA-Z0-9\\_\\.]+\\.baidu\\.com)/gi.test(e)){var o=\"https://sp0.baidu.com/9_Q4simg2RQJ8t7jm9iCKT-xh_/s.gif\";a?(o+=\"?r=\"+encodeURIComponent(document.referrer),e&&(o+=\"&l=\"+e)):e&&(o+=\"?l=\"+e),(new Image).src=o}}t.isSidebarOpen=!1}));var e=this.$site.themeConfig,a=e.logo,o=function(){window.innerWidth<720&&\"\"!==e.log?(e.logo=\"\",t.$refs.nav.$forceUpdate()):window.innerWidth>=720&&\"\"===e.logo&&(e.logo=a,console.log(\"xx\"),t.$refs.nav.$forceUpdate())};o(),window.addEventListener(\"resize\",o)},methods:{buy:function(t){n.push([\"_trackEvent\",\"buy\",\"click\",t]),open(\"https://item.jd.com/12816296.html\",\"_blank\")},toggleSidebar:function(t){this.isSidebarOpen=\"boolean\"==typeof t?t:!this.isSidebarOpen,this.$emit(\"toggle-sidebar\",this.isSidebarOpen)},onTouchStart:function(t){this.touchStart={x:t.changedTouches[0].clientX,y:t.changedTouches[0].clientY}},onTouchEnd:function(t){var e=t.changedTouches[0].clientX-this.touchStart.x,a=t.changedTouches[0].clientY-this.touchStart.y;Math.abs(e)>Math.abs(a)&&Math.abs(e)>40&&(e>0&&this.touchStart.x<=80?this.toggleSidebar(!0):this.toggleSidebar(!1))}}},u=(a(359),a(45)),d=Object(u.a)(l,(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n(\"div\",{staticClass:\"theme-container\",class:t.pageClasses,on:{touchstart:t.onTouchStart,touchend:t.onTouchEnd}},[t.shouldShowNavbar?n(\"Navbar\",{ref:\"nav\",on:{\"toggle-sidebar\":t.toggleSidebar}}):t._e(),t._v(\" \"),n(\"div\",{staticClass:\"sidebar-mask\",on:{click:function(e){return t.toggleSidebar(!1)}}}),t._v(\" \"),n(\"Sidebar\",{attrs:{items:t.sidebarItems},on:{\"toggle-sidebar\":t.toggleSidebar},scopedSlots:t._u([{key:\"top\",fn:function(){return[t._t(\"sidebar-top\")]},proxy:!0},{key:\"bottom\",fn:function(){return[t._t(\"sidebar-bottom\")]},proxy:!0}],null,!0)}),t._v(\" \"),t.$page.frontmatter.home?n(\"Home\"):n(\"Page\",{attrs:{\"sidebar-items\":t.sidebarItems},scopedSlots:t._u([{key:\"top\",fn:function(){return[t._t(\"page-top\"),t._v(\" \"),t.showBook?n(\"div\",{staticStyle:{\"text-align\":\"center\",\"margin-top\":\"100px\"}},[n(\"img\",{staticClass:\"book\",attrs:{src:a(335),title:\"点击去购买\"},on:{click:function(e){return t.buy(\"btn\")}}})]):t._e()]},proxy:!0},{key:\"bottom\",fn:function(){return[n(\"div\",{staticClass:\"copyright\"},[t._v(\" 版权所有，禁止私自转发、克隆网站。\")]),t._v(\" \"),n(\"div\",{staticClass:\"f-links\",staticStyle:{\"text-align\":\"center\"}},[n(\"a\",{attrs:{title:\"点击购买\",target:\"_blank\"},on:{click:function(e){return t.buy(\"link\")}}},[t._v(\" 购买实体书\\n        \")]),t._v(\" |\\n        \"),n(\"a\",{attrs:{href:\"https://github.com/flutterchina\"}},[t._v(\"\\n          Flutter中国开源项目\\n        \")]),t._v(\" |\\n        \"),n(\"a\",{attrs:{href:\"/join_us.html\"}},[t._v(\"\\n          和作者做同事\\n        \")])])]},proxy:!0}],null,!0)})],1)}),[],!1,null,null,null);e.default=d.exports}}]);"
  },
  {
    "path": "docs/assets/js/4.2eb3fd02.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[4],{698:function(t,s,a){t.exports=a.p+\"assets/img/7-10.47645234.png\"},699:function(t,s,a){t.exports=a.p+\"assets/img/7-11.8cffb68f.png\"},700:function(t,s,a){t.exports=a.p+\"assets/img/7-12.df06c0b8.png\"},701:function(t,s,a){t.exports=a.p+\"assets/img/7-13.45803ea4.png\"},702:function(t,s,a){t.exports=a.p+\"assets/img/7-14.edea3e0f.png\"},703:function(t,s,a){t.exports=a.p+\"assets/img/7-15.8624d4d8.png\"},704:function(t,s,a){t.exports=a.p+\"assets/img/7-16.da05101a.png\"},705:function(t,s,a){t.exports=a.p+\"assets/img/7-17.c0e2d9be.png\"},706:function(t,s,a){t.exports=a.p+\"assets/img/7-18.d0ccba9d.png\"},707:function(t,s,a){t.exports=a.p+\"assets/img/7-19.4c3306a3.png\"},708:function(t,s,a){t.exports=a.p+\"assets/img/7-20.f8ec9897.png\"},709:function(t,s,a){t.exports=a.p+\"assets/img/7-21.d3d1d15f.png\"},942:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_7-6-对话框详解\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_7-6-对话框详解\"}},[t._v(\"#\")]),t._v(\" 7.6 对话框详解\")]),t._v(\" \"),n(\"p\",[t._v(\"本节将详细介绍一下Flutter中对话框的使用方式、实现原理、样式定制及状态管理。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_7-6-1-使用对话框\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_7-6-1-使用对话框\"}},[t._v(\"#\")]),t._v(\" 7.6.1 使用对话框\")]),t._v(\" \"),n(\"p\",[t._v(\"对话框本质上也是UI布局，通常一个对话框会包含标题、内容，以及一些操作按钮，为此，Material库中提供了一些现成的对话框组件来用于快速的构建出一个完整的对话框。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"alertdialog\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#alertdialog\"}},[t._v(\"#\")]),t._v(\" AlertDialog\")]),t._v(\" \"),n(\"p\",[t._v(\"下面我们主要介绍一下Material库中的\"),n(\"code\",[t._v(\"AlertDialog\")]),t._v(\"组件，它的构造函数定义如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlertDialog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//对话框标题组件\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"titlePadding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 标题填充\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"titleTextStyle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//标题文本样式\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"content\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 对话框内容组件\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"contentPadding \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fromLTRB\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"24.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"24.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"24.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//内容的填充\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"contentTextStyle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 内容文本样式\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"actions\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 对话框操作按钮组\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"backgroundColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 对话框背景色\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"elevation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 对话框的阴影\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"semanticLabel\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//对话框语义化标签(用于读屏软件)\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"shape\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 对话框外形\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"参数都比较简单，不在赘述。下面我们看一个例子，假如我们要在删除文件时弹出一个确认对话框，该对话框如图7-10所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(698),alt:\"图7-10\"}})]),t._v(\" \"),n(\"p\",[t._v(\"该对话框样式代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlertDialog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"提示\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  content\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"您确定要删除当前文件吗?\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  actions\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"取消\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//关闭对话框\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"删除\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// ... 执行删除操作\")]),t._v(\"\\n        Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//关闭对话框\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"实现代码很简单，不在赘述。唯一需要注意的是我们是通过\"),n(\"code\",[t._v(\"Navigator.of(context).pop(…)\")]),t._v(\"方法来关闭对话框的，这和路由返回的方式是一致的，并且都可以返回一个结果数据。现在，对话框我们已经构建好了，那么如何将它弹出来呢？还有对话框返回的数据应如何被接收呢？这些问题的答案都在\"),n(\"code\",[t._v(\"showDialog()\")]),t._v(\"方法中。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"showDialog()\")]),t._v(\"是Material组件库提供的一个用于弹出Material风格对话框的方法，签名如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" showDialog\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  bool barrierDismissible \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//点击对话框barrier(遮罩)时是否关闭它\")]),t._v(\"\\n  WidgetBuilder builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 对话框UI的builder\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"该方法只有两个参数，含义见注释。该方法返回一个\"),n(\"code\",[t._v(\"Future\")]),t._v(\"，它正是用于接收对话框的返回值：如果我们是通过点击对话框遮罩关闭的，则\"),n(\"code\",[t._v(\"Future\")]),t._v(\"的值为\"),n(\"code\",[t._v(\"null\")]),t._v(\"，否则为我们通过\"),n(\"code\",[t._v(\"Navigator.of(context).pop(result)\")]),t._v(\"返回的result值，下面我们看一下整个示例：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//点击该按钮后弹出对话框\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"对话框1\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//弹出对话框并等待其关闭\")]),t._v(\"\\n    bool delete \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showDeleteConfirmDialog1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"delete \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"取消删除\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"已确认删除\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//... 删除文件\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 弹出对话框\")]),t._v(\"\\nFuture\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"bool\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showDeleteConfirmDialog1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" showDialog\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"bool\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlertDialog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"提示\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        content\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"您确定要删除当前文件吗?\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        actions\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"取消\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 关闭对话框\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"删除\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//关闭对话框并返回true\")]),t._v(\"\\n              Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v('示例运行后，我们点击对话框“取消”按钮或遮罩，控制台就会输出\"取消删除\"，如果点击“删除”按钮，控制台就会输出\"已确认删除\"。')]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[t._v(\"注意：如果\"),n(\"code\",[t._v(\"AlertDialog\")]),t._v(\"的内容过长，内容将会溢出，这在很多时候可能不是我们期望的，所以如果对话框内容过长时，可以用\"),n(\"code\",[t._v(\"SingleChildScrollView\")]),t._v(\"将内容包裹起来。\")])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"simpledialog\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#simpledialog\"}},[t._v(\"#\")]),t._v(\" SimpleDialog\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"SimpleDialog\")]),t._v(\"也是Material组件库提供的对话框，它会展示一个列表，用于列表选择的场景。下面是一个选择APP语言的示例，运行结果如图7-11。\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(699),alt:\"图7-11\"}})]),t._v(\" \"),n(\"p\",[t._v(\"实现代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"changeLanguage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  int i \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" showDialog\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"int\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SimpleDialog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'请选择语言'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SimpleDialogOption\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 返回1\")]),t._v(\"\\n                Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"symmetric\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"vertical\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'中文简体'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SimpleDialogOption\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 返回2\")]),t._v(\"\\n                Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"symmetric\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"vertical\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"6\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'美国英语'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"i \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"选择了：${i == 1 ? \"')]),t._v(\"中文简体\"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\" : \"')]),t._v(\"美国英语\"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"}\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"列表项组件我们使用了\"),n(\"code\",[t._v(\"SimpleDialogOption\")]),t._v(\"组件来包装了一下，它相当于一个FlatButton，只不过按钮文案是左对齐的，并且padding较小。上面示例运行后，用户选择一种语言后，控制台就会打印出它。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"dialog\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#dialog\"}},[t._v(\"#\")]),t._v(\" Dialog\")]),t._v(\" \"),n(\"p\",[t._v(\"实际上\"),n(\"code\",[t._v(\"AlertDialog\")]),t._v(\"和\"),n(\"code\",[t._v(\"SimpleDialog\")]),t._v(\"都使用了\"),n(\"code\",[t._v(\"Dialog\")]),t._v(\"类。由于\"),n(\"code\",[t._v(\"AlertDialog\")]),t._v(\"和\"),n(\"code\",[t._v(\"SimpleDialog\")]),t._v(\"中使用了\"),n(\"code\",[t._v(\"IntrinsicWidth\")]),t._v(\"来尝试通过子组件的实际尺寸来调整自身尺寸，这就导致他们的子组件不能是延迟加载模型的组件（如\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"、\"),n(\"code\",[t._v(\"GridView\")]),t._v(\" 、 \"),n(\"code\",[t._v(\"CustomScrollView\")]),t._v(\"等），如下面的代码运行后会报错。\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlertDialog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  content\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"如果我们就是需要嵌套一个\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"应该怎么做？这时，我们可以直接使用\"),n(\"code\",[t._v(\"Dialog\")]),t._v(\"类，如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Dialog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"下面我们看一个弹出一个有30个列表项的对话框示例，运行效果如图7-12所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(700),alt:\"图7-12\"}})]),t._v(\" \"),n(\"p\",[t._v(\"实现代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showListDialog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  int index \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" showDialog\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"int\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" child \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"请选择\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Expanded\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ListView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            itemCount\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"30\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            itemBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$index\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                onTap\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//使用AlertDialog会报错\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//return AlertDialog(content: child);\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Dialog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"index \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"点击了：$index\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"现在，我们己经介绍完了\"),n(\"code\",[t._v(\"AlertDialog\")]),t._v(\"、\"),n(\"code\",[t._v(\"SimpleDialog\")]),t._v(\"以及\"),n(\"code\",[t._v(\"Dialog\")]),t._v(\"。上面的示例中，我们在调用\"),n(\"code\",[t._v(\"showDialog\")]),t._v(\"时，在\"),n(\"code\",[t._v(\"builder\")]),t._v(\"中都是构建了这三个对话框组件的一种，可能有些读者会惯性的以为在\"),n(\"code\",[t._v(\"builder\")]),t._v(\"中只能返回这三者之一，其实这不是必须的！就拿\"),n(\"code\",[t._v(\"Dialog\")]),t._v(\"的示例来举例，我们完全可以用下面的代码来替代\"),n(\"code\",[t._v(\"Dialog\")]),t._v(\"：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// return Dialog(child: child) \")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"UnconstrainedBox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  constrainedAxis\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Axis\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"vertical\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ConstrainedBox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    constraints\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxConstraints\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"maxWidth\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"280\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Material\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      type\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MaterialType\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"card\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上面代码运行后可以实现一样的效果。现在我们总结一下：\"),n(\"code\",[t._v(\"AlertDialog\")]),t._v(\"、\"),n(\"code\",[t._v(\"SimpleDialog\")]),t._v(\"以及\"),n(\"code\",[t._v(\"Dialog\")]),t._v(\"是Material组件库提供的三种对话框，旨在帮助开发者快速构建出符合Material设计规范的对话框，但读者完全可以自定义对话框样式，因此，我们仍然可以实现各种样式的对话框，这样即带来了易用性，又有很强的扩展性。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_7-6-2-对话框打开动画及遮罩\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_7-6-2-对话框打开动画及遮罩\"}},[t._v(\"#\")]),t._v(\" 7.6.2 对话框打开动画及遮罩\")]),t._v(\" \"),n(\"p\",[t._v(\"我们可以把对话框分为内部样式和外部样式两部分。内部样式指对话框中显示的具体内容，这部分内容我们已经在上面介绍过了；外部样式包含对话框遮罩样式、打开动画等，本节主要介绍如何自定义这些外部样式。\")]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[t._v(\"关于动画相关内容我们将在本书后面章节介绍，下面内容读者可以先了解一下（不必深究），读者可以在学习完动画相关内容后再回头来看。\")])]),t._v(\" \"),n(\"p\",[t._v(\"我们已经介绍过了\"),n(\"code\",[t._v(\"showDialog\")]),t._v(\"方法，它是Material组件库中提供的一个打开Material风格对话框的方法。那如何打开一个普通风格的对话框呢（非Material风格）？ Flutter 提供了一个\"),n(\"code\",[t._v(\"showGeneralDialog\")]),t._v(\"方法，签名如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" showGeneralDialog\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" RoutePageBuilder pageBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//构建对话框内部UI\")]),t._v(\"\\n  bool barrierDismissible\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//点击遮罩是否关闭对话框\")]),t._v(\"\\n  String barrierLabel\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 语义化标签(用于读屏软件)\")]),t._v(\"\\n  Color barrierColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 遮罩颜色\")]),t._v(\"\\n  Duration transitionDuration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 对话框打开/关闭的动画时长\")]),t._v(\"\\n  RouteTransitionsBuilder transitionBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 对话框打开/关闭的动画\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"实际上，\"),n(\"code\",[t._v(\"showDialog\")]),t._v(\"方法正是\"),n(\"code\",[t._v(\"showGeneralDialog\")]),t._v(\"的一个封装，定制了Material风格对话框的遮罩颜色和动画。Material风格对话框打开/关闭动画是一个Fade（渐隐渐显）动画，如果我们想使用一个缩放动画就可以通过\"),n(\"code\",[t._v(\"transitionBuilder\")]),t._v(\"来自定义。下面我们自己封装一个\"),n(\"code\",[t._v(\"showCustomDialog\")]),t._v(\"方法，它定制的对话框动画为缩放动画，并同时制定遮罩颜色为\"),n(\"code\",[t._v(\"Colors.black87\")]),t._v(\"：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" showCustomDialog\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  bool barrierDismissible \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  WidgetBuilder builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" ThemeData theme \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Theme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" shadowThemeOnly\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showGeneralDialog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    pageBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext buildContext\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" secondaryAnimation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Widget pageChild \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SafeArea\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" theme \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Theme\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"data\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" theme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" pageChild\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" pageChild\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    barrierDismissible\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" barrierDismissible\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    barrierLabel\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MaterialLocalizations\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"modalBarrierDismissLabel\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    barrierColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"black87\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 自定义遮罩颜色\")]),t._v(\"\\n    transitionDuration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"milliseconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"150\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    transitionBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _buildMaterialDialogTransitions\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\nWidget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_buildMaterialDialogTransitions\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" secondaryAnimation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    Widget child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 使用缩放动画\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ScaleTransition\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    scale\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CurvedAnimation\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      parent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      curve\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Curves\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"easeOut\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"现在，我们使用\"),n(\"code\",[t._v(\"showCustomDialog\")]),t._v(\"打开文件删除确认对话框，代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\nshowCustomDialog\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"bool\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlertDialog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"提示\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      content\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"您确定要删除当前文件吗?\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      actions\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"取消\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"删除\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 执行删除操作\")]),t._v(\"\\n            Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图7-13所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(701),alt:\"图7-13\"}})]),t._v(\" \"),n(\"p\",[t._v(\"可以发现，遮罩颜色比通过\"),n(\"code\",[t._v(\"showDialog\")]),t._v(\"方法打开的对话框更深。另外对话框打开/关闭的动画已变为缩放动画了，读者可以亲自运行示例查看效果。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_7-6-3-对话框实现原理\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_7-6-3-对话框实现原理\"}},[t._v(\"#\")]),t._v(\" 7.6.3 对话框实现原理\")]),t._v(\" \"),n(\"p\",[t._v(\"我们已经知道对话框最终都是由\"),n(\"code\",[t._v(\"showGeneralDialog\")]),t._v(\"方法打开的，我们来看看它的具体实现：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" showGeneralDialog\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" RoutePageBuilder pageBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  bool barrierDismissible\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  String barrierLabel\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  Color barrierColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  Duration transitionDuration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  RouteTransitionsBuilder transitionBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" rootNavigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"push\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_DialogRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    pageBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" pageBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    barrierDismissible\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" barrierDismissible\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    barrierLabel\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" barrierLabel\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    barrierColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" barrierColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    transitionDuration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" transitionDuration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    transitionBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" transitionBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"实现很简单，直接调用\"),n(\"code\",[t._v(\"Navigator\")]),t._v(\"的\"),n(\"code\",[t._v(\"push\")]),t._v(\"方法打开了一个新的对话框路由\"),n(\"code\",[t._v(\"_DialogRoute\")]),t._v(\"，然后返回了\"),n(\"code\",[t._v(\"push\")]),t._v(\"的返回值。可见对话框实际上正是通过路由的形式实现的，这也是为什么我们可以使用\"),n(\"code\",[t._v(\"Navigator\")]),t._v(\"的\"),n(\"code\",[t._v(\"pop\")]),t._v(\" 方法来退出对话框的原因。关于对话框的样式定制在\"),n(\"code\",[t._v(\"_DialogRoute\")]),t._v(\"中，没有什么新的东西，读者可以自行查看。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_7-6-4-对话框状态管理\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_7-6-4-对话框状态管理\"}},[t._v(\"#\")]),t._v(\" 7.6.4 对话框状态管理\")]),t._v(\" \"),n(\"p\",[t._v(\"我们在用户选择删除一个文件时，会询问是否删除此文件；在用户选择一个文件夹是，应该再让用户确认是否删除子文件夹。为了在用户选择了文件夹时避免二次弹窗确认是否删除子目录，我们在确认对话框底部添加一个“同时删除子目录？”的复选框，如图7-14所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(702),alt:\"图7-14\"}})]),t._v(\" \"),n(\"p\",[t._v(\"现在就有一个问题：如何管理复选框的选中状态？习惯上，我们会在路由页的State中来管理选中状态，我们可能会写出如下这样的代码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_DialogRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"DialogRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  bool withTree \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 复选框选中状态\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"对话框2\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            bool delete \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showDeleteConfirmDialog2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"delete \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"取消删除\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"同时删除子目录: $delete\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"bool\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showDeleteConfirmDialog2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    withTree \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 默认复选框不选中\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" showDialog\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"bool\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlertDialog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"提示\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          content\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            crossAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" CrossAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            mainAxisSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"min\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"您确定要删除当前文件吗?\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"同时删除子目录？\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Checkbox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" withTree\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    onChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"bool value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//复选框选中状态发生变化时重新构建UI\")]),t._v(\"\\n                      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                        \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//更新复选框状态\")]),t._v(\"\\n                        withTree \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"withTree\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          actions\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"取消\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"删除\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//执行删除操作\")]),t._v(\"\\n                Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"withTree\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"然后，当我们运行上面的代码时我们会发现复选框根本选不中！为什么会这样呢？其实原因很简单，我们知道\"),n(\"code\",[t._v(\"setState\")]),t._v(\"方法只会针对当前context的子树重新build，但是我们的对话框并不是在\"),n(\"code\",[t._v(\"_DialogRouteState\")]),t._v(\"的\"),n(\"code\",[t._v(\"build\")]),t._v(\" 方法中构建的，而是通过\"),n(\"code\",[t._v(\"showDialog\")]),t._v(\"单独构建的，所以在\"),n(\"code\",[t._v(\"_DialogRouteState\")]),t._v(\"的context中调用\"),n(\"code\",[t._v(\"setState\")]),t._v(\"是无法影响通过\"),n(\"code\",[t._v(\"showDialog\")]),t._v(\"构建的UI的。另外，我们可以从另外一个角度来理解这个现象，前面说过对话框也是通过路由的方式来实现的，那么上面的代码实际上就等同于企图在父路由中调用\"),n(\"code\",[t._v(\"setState\")]),t._v(\"来让子路由更新，这显然是不行的！简尔言之，根本原因就是context不对。那如何让复选框可点击呢？通常有如下三种方法：\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"单独抽离出statefulwidget\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#单独抽离出statefulwidget\"}},[t._v(\"#\")]),t._v(\" 单独抽离出StatefulWidget\")]),t._v(\" \"),n(\"p\",[t._v(\"既然是context不对，那么直接的思路就是将复选框的选中逻辑单独封装成一个\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"，然后在其内部管理复选状态。我们先来看看这种方法，下面是实现代码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 单独封装一个内部管理选中状态的复选框组件\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"DialogCheckbox\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DialogCheckbox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" ValueChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"bool\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" onChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" bool value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _DialogCheckboxState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_DialogCheckboxState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_DialogCheckboxState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"DialogCheckbox\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  bool value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    value \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Checkbox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      onChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"v\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//将选中状态通过事件的形式抛出\")]),t._v(\"\\n        widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onChanged\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"v\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//更新自身选中状态\")]),t._v(\"\\n          value \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" v\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"下面是弹出对话框的代码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"bool\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showDeleteConfirmDialog3\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  bool _withTree \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//记录复选框是否选中\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" showDialog\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"bool\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlertDialog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"提示\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        content\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          crossAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" CrossAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          mainAxisSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"min\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"您确定要删除当前文件吗?\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"同时删除子目录？\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DialogCheckbox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _withTree\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//默认不选中\")]),t._v(\"\\n                  onChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"bool value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//更新选中状态\")]),t._v(\"\\n                    _withTree \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"_withTree\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        actions\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"取消\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"删除\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 将选中状态返回\")]),t._v(\"\\n              Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_withTree\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"最后，就是使用：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"话框3（复选框可点击）\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//弹出删除确认对话框，等待用户确认\")]),t._v(\"\\n    bool deleteTree \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showDeleteConfirmDialog3\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"deleteTree \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"取消删除\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"同时删除子目录: $deleteTree\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行后效果如图7-15所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(703),alt:\"图7-15\"}})]),t._v(\" \"),n(\"p\",[t._v(\"可见复选框能选中了，点击“取消”或“删除”后，控制台就会打印出最终的确认状态。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"使用statefulbuilder方法\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#使用statefulbuilder方法\"}},[t._v(\"#\")]),t._v(\" 使用StatefulBuilder方法\")]),t._v(\" \"),n(\"p\",[t._v(\"上面的方法虽然能解决对话框状态更新的问题，但是有一个明显的缺点——对话框上所有可能会改变状态的组件都得单独封装在一个在内部管理状态的\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"中，这样不仅麻烦，而且复用性不大。因此，我们来想想能不能找到一种更简单的方法？上面的方法本质上就是将对话框的状态置于一个\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"的上下文中，由\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"在内部管理，那么我们有没有办法在不需要单独抽离组件的情况下创建一个\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"的上下文呢？想到这里，我们可以从\"),n(\"code\",[t._v(\"Builder\")]),t._v(\"组件的实现获得灵感。在前面介绍过\"),n(\"code\",[t._v(\"Builder\")]),t._v(\"组件可以获得组件所在位置的真正的Context，那它是怎么实现的呢，我们看看它的源码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Builder\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n       \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" WidgetBuilder builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"可以看到，\"),n(\"code\",[t._v(\"Builder\")]),t._v(\"实际上只是继承了\"),n(\"code\",[t._v(\"StatelessWidget\")]),t._v(\"，然后在\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法中获取当前context后将构建方法代理到了\"),n(\"code\",[t._v(\"builder\")]),t._v(\"回调，可见，\"),n(\"code\",[t._v(\"Builder\")]),t._v(\"实际上是获取了\"),n(\"code\",[t._v(\"StatelessWidget\")]),t._v(\" 的上下文（context）。那么我们能否用相同的方法获取\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\" 的上下文，并代理其\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法呢？下面我们照猫画虎，来封装一个\"),n(\"code\",[t._v(\"StatefulBuilder\")]),t._v(\"方法：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulBuilder\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"StatefulBuilder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n       \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" StatefulWidgetBuilder builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _StatefulBuilderState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_StatefulBuilderState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_StatefulBuilderState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"StatefulBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" setState\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"代码很简单，\"),n(\"code\",[t._v(\"StatefulBuilder\")]),t._v(\"获取了\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"的上下文，并代理了其构建过程。下面我们就可以通过\"),n(\"code\",[t._v(\"StatefulBuilder\")]),t._v(\"来重构上面的代码了（变动只在\"),n(\"code\",[t._v(\"DialogCheckbox\")]),t._v(\"部分）：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"同时删除子目录？\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//使用StatefulBuilder来构建StatefulWidget上下文\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"StatefulBuilder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" _setState\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Checkbox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _withTree\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//默认不选中\")]),t._v(\"\\n          onChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"bool value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//_setState方法实际就是该StatefulWidget的setState方法，\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//调用后builder方法会重新被调用\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//更新选中状态\")]),t._v(\"\\n              _withTree \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"_withTree\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"实际上，这种方法本质上就是子组件通知父组件（StatefulWidget）重新build子组件本身来实现UI更新的，读者可以对比代码理解。实际上\"),n(\"code\",[t._v(\"StatefulBuilder\")]),t._v(\"正是Flutter SDK中提供的一个类，它和\"),n(\"code\",[t._v(\"Builder\")]),t._v(\"的原理是一样的，在此，提醒读者一定要将\"),n(\"code\",[t._v(\"StatefulBuilder\")]),t._v(\"和\"),n(\"code\",[t._v(\"Builder\")]),t._v(\"理解透彻，因为它们在Flutter中是非常实用的。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"精妙的解法\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#精妙的解法\"}},[t._v(\"#\")]),t._v(\" 精妙的解法\")]),t._v(\" \"),n(\"p\",[t._v(\"是否还有更简单的解决方案呢？要确认这个问题，我们就得先搞清楚UI是怎么更新的，我们知道在调用\"),n(\"code\",[t._v(\"setState\")]),t._v(\"方法后\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"就会重新build，那\"),n(\"code\",[t._v(\"setState\")]),t._v(\"方法做了什么呢？我们能不能从中找到方法？顺着这个思路，我们就得看一下\"),n(\"code\",[t._v(\"setState\")]),t._v(\"的核心源码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"VoidCallback fn\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n  _element\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"markNeedsBuild\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"可以发现，\"),n(\"code\",[t._v(\"setState\")]),t._v(\"中调用了\"),n(\"code\",[t._v(\"Element\")]),t._v(\"的\"),n(\"code\",[t._v(\"markNeedsBuild()\")]),t._v(\"方法，我们前面说过，Flutter是一个响应式框架，要更新UI只需改变状态后通知框架页面需要重构即可，而\"),n(\"code\",[t._v(\"Element\")]),t._v(\"的\"),n(\"code\",[t._v(\"markNeedsBuild()\")]),t._v(\"方法正是来实现这个功能的！\"),n(\"code\",[t._v(\"markNeedsBuild()\")]),t._v(\"方法会将当前的\"),n(\"code\",[t._v(\"Element\")]),t._v(\"对象标记为“dirty”（脏的），在每一个Frame，Flutter都会重新构建被标记为“dirty”\"),n(\"code\",[t._v(\"Element\")]),t._v(\"对象。既然如此，我们有没有办法获取到对话框内部UI的\"),n(\"code\",[t._v(\"Element\")]),t._v(\"对象，然后将其标示为为“dirty”呢？答案是肯定的！我们可以通过Context来得到\"),n(\"code\",[t._v(\"Element\")]),t._v(\"对象，至于\"),n(\"code\",[t._v(\"Element\")]),t._v(\"与\"),n(\"code\",[t._v(\"Context\")]),t._v(\"的关系我们将会在后面“Flutter核心原理”一章中再深入介绍，现在只需要简单的认为：在组件树中，\"),n(\"code\",[t._v(\"context\")]),t._v(\"实际上就是\"),n(\"code\",[t._v(\"Element\")]),t._v(\"对象的引用。知道这个后，那么解决的方案就呼之欲出了，我们可以通过如下方式来让复选框可以更新：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"bool\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showDeleteConfirmDialog4\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  bool _withTree \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" showDialog\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"bool\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlertDialog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"提示\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        content\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          crossAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" CrossAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          mainAxisSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"min\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"您确定要删除当前文件吗?\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"同时删除子目录？\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Checkbox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 依然使用Checkbox组件\")]),t._v(\"\\n                  value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _withTree\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  onChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"bool value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 此时context为对话框UI的根Element，我们 \")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 直接将对话框UI对应的Element标记为dirty\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"as\")]),t._v(\" Element\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"markNeedsBuild\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                    _withTree \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"_withTree\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        actions\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"取消\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"删除\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 执行删除操作\")]),t._v(\"\\n              Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_withTree\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上面的代码运行后复选框也可以正常选中。可以看到，我们只用了一行代码便解决了这个问题！当然上面的代码并不是最优，因为我们只需要更新复选框的状态，而此时的\"),n(\"code\",[t._v(\"context\")]),t._v(\"我们用的是对话框的根\"),n(\"code\",[t._v(\"context\")]),t._v(\"，所以会导致整个对话框UI组件全部rebuild，因此最好的做法是将\"),n(\"code\",[t._v(\"context\")]),t._v(\"的“范围”缩小，也就是说只将\"),n(\"code\",[t._v(\"Checkbox\")]),t._v(\"的Element标记为\"),n(\"code\",[t._v(\"dirty\")]),t._v(\"，优化后的代码为：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"同时删除子目录？\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 通过Builder来获得构建Checkbox的`context`，\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 这是一种常用的缩小`context`范围的方式\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Checkbox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _withTree\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          onChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"bool value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"as\")]),t._v(\" Element\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"markNeedsBuild\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            _withTree \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"_withTree\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),n(\"h2\",{attrs:{id:\"_7-6-5-其它类型的对话框\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_7-6-5-其它类型的对话框\"}},[t._v(\"#\")]),t._v(\" 7.6.5 其它类型的对话框\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"底部菜单列表\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#底部菜单列表\"}},[t._v(\"#\")]),t._v(\" 底部菜单列表\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"showModalBottomSheet\")]),t._v(\"方法可以弹出一个Material风格的底部菜单列表模态对话框，示例如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 弹出底部菜单列表模态对话框\")]),t._v(\"\\nFuture\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"int\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_showModalBottomSheet\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" showModalBottomSheet\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"int\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" ListView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        itemCount\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"30\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        itemBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$index\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onTap\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"点击按钮，弹出该对话框：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"显示底部菜单列表\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    int type \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_showModalBottomSheet\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"type\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行后效果如图7-16所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(704),alt:\"图7-16\"}})]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"showModalBottomSheet\")]),t._v(\"的实现原理和\"),n(\"code\",[t._v(\"showGeneralDialog\")]),t._v(\"实现原理相同，都是通过路由的方式来实现的，读者可以查看源码对比。但值得一提的是还有一个\"),n(\"code\",[t._v(\"showBottomSheet\")]),t._v(\"方法，该方法会从设备底部向上弹出一个全屏的菜单列表，示例如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 返回的是一个controller\")]),t._v(\"\\nPersistentBottomSheetController\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"int\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_showBottomSheet\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" showBottomSheet\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"int\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" ListView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        itemCount\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"30\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        itemBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$index\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onTap\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// do something\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$index\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图7-17所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(705),alt:\"图7-17\"}})]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"PersistentBottomSheetController\")]),t._v(\"中包含了一些控制对话框的方法比如\"),n(\"code\",[t._v(\"close\")]),t._v(\"方法可以关闭该对话框，功能比较简单，读者可以自行查看源码。唯一需要注意的是，\"),n(\"code\",[t._v(\"showBottomSheet\")]),t._v(\"和我们上面介绍的弹出对话框的方法原理不同：\"),n(\"code\",[t._v(\"showBottomSheet\")]),t._v(\"是调用widget树顶部的\"),n(\"code\",[t._v(\"Scaffold\")]),t._v(\"组件的\"),n(\"code\",[t._v(\"ScaffoldState\")]),t._v(\"的\"),n(\"code\",[t._v(\"showBottomSheet\")]),t._v(\"同名方法实现，也就是说要调用\"),n(\"code\",[t._v(\"showBottomSheet\")]),t._v(\"方法就必须得保证父级组件中有\"),n(\"code\",[t._v(\"Scaffold\")]),t._v(\"。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"loading框\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#loading框\"}},[t._v(\"#\")]),t._v(\" Loading框\")]),t._v(\" \"),n(\"p\",[t._v(\"其实Loading框可以直接通过\"),n(\"code\",[t._v(\"showDialog\")]),t._v(\"+\"),n(\"code\",[t._v(\"AlertDialog\")]),t._v(\"来自定义：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showLoadingDialog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showDialog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    barrierDismissible\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//点击遮罩不关闭对话框\")]),t._v(\"\\n    builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlertDialog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        content\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          mainAxisSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"min\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircularProgressIndicator\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"only\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"top\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"26.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"正在加载，请稍后...\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"显示效果如图7-18所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(706),alt:\"图7-18\"}})]),t._v(\" \"),n(\"p\",[t._v(\"如果我们嫌Loading框太宽，想自定义对话框宽度，这时只使用\"),n(\"code\",[t._v(\"SizedBox\")]),t._v(\"或\"),n(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"是不行的，原因是\"),n(\"code\",[t._v(\"showDialog\")]),t._v(\"中已经给对话框设置了宽度限制，根据我们在第五章“尺寸限制类容器”一节中所述，我们可以使用\"),n(\"code\",[t._v(\"UnconstrainedBox\")]),t._v(\"先抵消\"),n(\"code\",[t._v(\"showDialog\")]),t._v(\"对宽度的限制，然后再使用\"),n(\"code\",[t._v(\"SizedBox\")]),t._v(\"指定宽度，代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"UnconstrainedBox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  constrainedAxis\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Axis\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"vertical\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"280\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlertDialog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      content\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        mainAxisSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"min\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircularProgressIndicator\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".8\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"only\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"top\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"26.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"正在加载，请稍后...\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"代码运行后，效果如图7-19所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(707),alt:\"图7-19\"}})]),t._v(\" \"),n(\"h3\",{attrs:{id:\"日历选择\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#日历选择\"}},[t._v(\"#\")]),t._v(\" 日历选择\")]),t._v(\" \"),n(\"p\",[t._v(\"我们先看一下Material风格的日历选择器，如图7-20所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(708),alt:\"图7-20\"}})]),t._v(\" \"),n(\"p\",[t._v(\"实现代码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"DateTime\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_showDatePicker1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" date \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" DateTime\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"now\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showDatePicker\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    initialDate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" date\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    firstDate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" date\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    lastDate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" date\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//未来30天可选\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"days\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"30\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"iOS风格的日历选择器需要使用\"),n(\"code\",[t._v(\"showCupertinoModalPopup\")]),t._v(\"方法和\"),n(\"code\",[t._v(\"CupertinoDatePicker\")]),t._v(\"组件来实现：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"DateTime\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_showDatePicker2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" date \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" DateTime\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"now\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showCupertinoModalPopup\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ctx\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CupertinoDatePicker\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          mode\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" CupertinoDatePickerMode\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dateAndTime\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          minimumDate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" date\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          maximumDate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" date\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"days\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"30\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          maximumYear\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" date\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"year \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          onDateTimeChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"DateTime value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图7-21所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(709),alt:\"图7-21\"}})])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/40.31216541.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[40],{383:function(t,e,n){t.exports=n.p+\"assets/img/14-0.bd5c2d9d.png\"},384:function(t,e,n){t.exports=n.p+\"assets/img/14-1.075fa468.png\"},385:function(t,e,n){t.exports=n.p+\"assets/img/14-2.7141498f.png\"},752:function(t,e,n){\"use strict\";n.r(e);var s=n(45),a=Object(s.a)({},(function(){var t=this,e=t.$createElement,s=t._self._c||e;return s(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[s(\"h1\",{attrs:{id:\"_14-2-element与buildcontext\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_14-2-element与buildcontext\"}},[t._v(\"#\")]),t._v(\" 14.2 Element与BuildContext\")]),t._v(\" \"),s(\"h2\",{attrs:{id:\"_14-2-1-element\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_14-2-1-element\"}},[t._v(\"#\")]),t._v(\" 14.2.1 Element\")]),t._v(\" \"),s(\"p\",[t._v(\"在“Widget简介”一节，我们介绍了Widget和Element的关系，我们知道最终的UI树其实是由一个个独立的Element节点构成。我们也说过组件最终的Layout、渲染都是通过\"),s(\"code\",[t._v(\"RenderObject\")]),t._v(\"来完成的，从创建到渲染的大体流程是：根据Widget生成Element，然后创建相应的\"),s(\"code\",[t._v(\"RenderObject\")]),t._v(\"并关联到\"),s(\"code\",[t._v(\"Element.renderObject\")]),t._v(\"属性上，最后再通过\"),s(\"code\",[t._v(\"RenderObject\")]),t._v(\"来完成布局排列和绘制。\")]),t._v(\" \"),s(\"p\",[t._v(\"Element就是Widget在UI树具体位置的一个实例化对象，大多数Element只有唯一的\"),s(\"code\",[t._v(\"renderObject\")]),t._v(\"，但还有一些Element会有多个子节点，如继承自\"),s(\"code\",[t._v(\"RenderObjectElement\")]),t._v(\"的一些类，比如\"),s(\"code\",[t._v(\"MultiChildRenderObjectElement\")]),t._v(\"。最终所有Element的RenderObject构成一棵树，我们称之为”Render Tree“即”渲染树“。总结一下，我们可以认为Flutter的UI系统包含三棵树：Widget树、Element树、渲染树。他们的依赖关系是：Element树根据Widget树生成，而渲染树又依赖于Element树，如图14-0所示。\")]),t._v(\" \"),s(\"p\",[s(\"img\",{attrs:{src:n(383),alt:\"图14-0\"}})]),t._v(\" \"),s(\"p\",[t._v(\"现在我们重点看一下Element，Element的生命周期如下：\")]),t._v(\" \"),s(\"ol\",[s(\"li\",[t._v(\"Framework 调用\"),s(\"code\",[t._v(\"Widget.createElement\")]),t._v(\" 创建一个Element实例，记为\"),s(\"code\",[t._v(\"element\")])]),t._v(\" \"),s(\"li\",[t._v(\"Framework 调用 \"),s(\"code\",[t._v(\"element.mount(parentElement,newSlot)\")]),t._v(\" ，mount方法中首先调用\"),s(\"code\",[t._v(\"element\")]),t._v(\"所对应Widget的\"),s(\"code\",[t._v(\"createRenderObject\")]),t._v(\"方法创建与\"),s(\"code\",[t._v(\"element\")]),t._v(\"相关联的RenderObject对象，然后调用\"),s(\"code\",[t._v(\"element.attachRenderObject\")]),t._v(\"方法将\"),s(\"code\",[t._v(\"element.renderObject\")]),t._v(\"添加到渲染树中插槽指定的位置（这一步不是必须的，一般发生在Element树结构发生变化时才需要重新attach）。插入到渲染树后的\"),s(\"code\",[t._v(\"element\")]),t._v(\"就处于“active”状态，处于“active”状态后就可以显示在屏幕上了（可以隐藏）。\")]),t._v(\" \"),s(\"li\",[t._v(\"当有父Widget的配置数据改变时，同时其\"),s(\"code\",[t._v(\"State.build\")]),t._v(\"返回的Widget结构与之前不同，此时就需要重新构建对应的Element树。为了进行Element复用，在Element重新构建前会先尝试是否可以复用旧树上相同位置的element，element节点在更新前都会调用其对应Widget的\"),s(\"code\",[t._v(\"canUpdate\")]),t._v(\"方法，如果返回\"),s(\"code\",[t._v(\"true\")]),t._v(\"，则复用旧Element，旧的Element会使用新Widget配置数据更新，反之则会创建一个新的Element。\"),s(\"code\",[t._v(\"Widget.canUpdate\")]),t._v(\"主要是判断\"),s(\"code\",[t._v(\"newWidget\")]),t._v(\"与\"),s(\"code\",[t._v(\"oldWidget\")]),t._v(\"的\"),s(\"code\",[t._v(\"runtimeType\")]),t._v(\"和\"),s(\"code\",[t._v(\"key\")]),t._v(\"是否同时相等，如果同时相等就返回\"),s(\"code\",[t._v(\"true\")]),t._v(\"，否则就会返回\"),s(\"code\",[t._v(\"false\")]),t._v(\"。根据这个原理，当我们需要强制更新一个Widget时，可以通过指定不同的Key来避免复用。\")]),t._v(\" \"),s(\"li\",[t._v(\"当有祖先Element决定要移除\"),s(\"code\",[t._v(\"element\")]),t._v(\" 时（如Widget树结构发生了变化，导致\"),s(\"code\",[t._v(\"element\")]),t._v(\"对应的Widget被移除），这时该祖先Element就会调用\"),s(\"code\",[t._v(\"deactivateChild\")]),t._v(\" 方法来移除它，移除后\"),s(\"code\",[t._v(\"element.renderObject\")]),t._v(\"也会被从渲染树中移除，然后Framework会调用\"),s(\"code\",[t._v(\"element.deactivate\")]),t._v(\" 方法，这时\"),s(\"code\",[t._v(\"element\")]),t._v(\"状态变为“inactive”状态。\")]),t._v(\" \"),s(\"li\",[t._v(\"“inactive”态的element将不会再显示到屏幕。为了避免在一次动画执行过程中反复创建、移除某个特定element，“inactive”态的element在当前动画最后一帧结束前都会保留，如果在动画执行结束后它还未能重新变成“active”状态，Framework就会调用其\"),s(\"code\",[t._v(\"unmount\")]),t._v(\"方法将其彻底移除，这时element的状态为\"),s(\"code\",[t._v(\"defunct\")]),t._v(\",它将永远不会再被插入到树中。\")]),t._v(\" \"),s(\"li\",[t._v(\"如果\"),s(\"code\",[t._v(\"element\")]),t._v(\"要重新插入到Element树的其它位置，如\"),s(\"code\",[t._v(\"element\")]),t._v(\"或\"),s(\"code\",[t._v(\"element\")]),t._v(\"的祖先拥有一个GlobalKey（用于全局复用元素），那么Framework会先将element从现有位置移除，然后再调用其\"),s(\"code\",[t._v(\"activate\")]),t._v(\"方法，并将其\"),s(\"code\",[t._v(\"renderObject\")]),t._v(\"重新attach到渲染树。\")])]),t._v(\" \"),s(\"p\",[t._v(\"看完Element的生命周期，可能有些读者会有疑问，开发者会直接操作Element树吗？其实对于开发者来说，大多数情况下只需要关注Widget树就行，Flutter框架已经将对Widget树的操作映射到了Element树上，这可以极大的降低复杂度，提高开发效率。但是了解Element对理解整个Flutter UI框架是至关重要的，Flutter正是通过Element这个纽带将Widget和RenderObject关联起来，了解Element层不仅会帮助读者对Flutter UI框架有个清晰的认识，而且也会提高自己的抽象能力和设计能力。另外在有些时候，我们必须得直接使用Element对象来完成一些操作，比如获取主题Theme数据，具体细节将在下文介绍。\")]),t._v(\" \"),s(\"h2\",{attrs:{id:\"_14-2-2-buildcontext\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_14-2-2-buildcontext\"}},[t._v(\"#\")]),t._v(\" 14.2.2 BuildContext\")]),t._v(\" \"),s(\"p\",[t._v(\"我们已经知道，\"),s(\"code\",[t._v(\"StatelessWidget\")]),t._v(\"和\"),s(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"的\"),s(\"code\",[t._v(\"build\")]),t._v(\"方法都会传一个\"),s(\"code\",[t._v(\"BuildContext\")]),t._v(\"对象：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"Widget \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"我们也知道，在很多时候我们都需要使用这个\"),s(\"code\",[t._v(\"context\")]),t._v(\" 做一些事，比如：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"Theme\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//获取主题\")]),t._v(\"\\nNavigator\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"push\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" route\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//入栈新路由\")]),t._v(\"\\nLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" type\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//获取Local\")]),t._v(\"\\ncontext\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"size \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//获取上下文大小\")]),t._v(\"\\ncontext\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"findRenderObject\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//查找当前或最近的一个祖先RenderObject\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"那么\"),s(\"code\",[t._v(\"BuildContext\")]),t._v(\"到底是什么呢，查看其定义，发现其是一个抽象接口类：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"abstract\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"BuildContext\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"那这个\"),s(\"code\",[t._v(\"context\")]),t._v(\"对象对应的实现类到底是谁呢？我们顺藤摸瓜，发现\"),s(\"code\",[t._v(\"build\")]),t._v(\"调用是发生在\"),s(\"code\",[t._v(\"StatelessWidget\")]),t._v(\"和\"),s(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"对应的\"),s(\"code\",[t._v(\"StatelessElement\")]),t._v(\"和\"),s(\"code\",[t._v(\"StatefulElement\")]),t._v(\"的\"),s(\"code\",[t._v(\"build\")]),t._v(\"方法中，以\"),s(\"code\",[t._v(\"StatelessElement\")]),t._v(\"为例：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessElement\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ComponentElement\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" widget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"发现\"),s(\"code\",[t._v(\"build\")]),t._v(\"传递的参数是\"),s(\"code\",[t._v(\"this\")]),t._v(\"，很明显！这个\"),s(\"code\",[t._v(\"BuildContext\")]),t._v(\"就是\"),s(\"code\",[t._v(\"StatelessElement\")]),t._v(\"。同样，我们同样发现\"),s(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"的\"),s(\"code\",[t._v(\"context\")]),t._v(\"是\"),s(\"code\",[t._v(\"StatefulElement\")]),t._v(\"。但\"),s(\"code\",[t._v(\"StatelessElement\")]),t._v(\"和\"),s(\"code\",[t._v(\"StatefulElement\")]),t._v(\"本身并没有实现\"),s(\"code\",[t._v(\"BuildContext\")]),t._v(\"接口，继续跟踪代码，发现它们间接继承自\"),s(\"code\",[t._v(\"Element\")]),t._v(\"类，然后查看\"),s(\"code\",[t._v(\"Element\")]),t._v(\"类定义，发现\"),s(\"code\",[t._v(\"Element\")]),t._v(\"类果然实现了\"),s(\"code\",[t._v(\"BuildContext\")]),t._v(\"接口:\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Element\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"DiagnosticableTree\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"implements\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"BuildContext\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"至此真相大白，\"),s(\"code\",[t._v(\"BuildContext\")]),t._v(\"就是widget对应的\"),s(\"code\",[t._v(\"Element\")]),t._v(\"，所以我们可以通过\"),s(\"code\",[t._v(\"context\")]),t._v(\"在\"),s(\"code\",[t._v(\"StatelessWidget\")]),t._v(\"和\"),s(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"的\"),s(\"code\",[t._v(\"build\")]),t._v(\"方法中直接访问\"),s(\"code\",[t._v(\"Element\")]),t._v(\"对象。我们获取主题数据的代码\"),s(\"code\",[t._v(\"Theme.of(context)\")]),t._v(\"内部正是调用了Element的\"),s(\"code\",[t._v(\"dependOnInheritedWidgetOfExactType()\")]),t._v(\"方法。\")]),t._v(\" \"),s(\"blockquote\",[s(\"p\",[t._v(\"思考题：为什么build方法的参数不定义成Element对象，而要定义成BuildContext ?\")])]),t._v(\" \"),s(\"h3\",{attrs:{id:\"进阶\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#进阶\"}},[t._v(\"#\")]),t._v(\" 进阶\")]),t._v(\" \"),s(\"p\",[t._v(\"我们可以看到Element是Flutter UI框架内部连接widget和\"),s(\"code\",[t._v(\"RenderObject\")]),t._v(\"的纽带，大多数时候开发者只需要关注widget层即可，但是widget层有时候并不能完全屏蔽\"),s(\"code\",[t._v(\"Element\")]),t._v(\"细节，所以Framework在\"),s(\"code\",[t._v(\"StatelessWidget\")]),t._v(\"和\"),s(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"中通过\"),s(\"code\",[t._v(\"build\")]),t._v(\"方法参数又将\"),s(\"code\",[t._v(\"Element\")]),t._v(\"对象也传递给了开发者，这样一来，开发者便可以在需要时直接操作\"),s(\"code\",[t._v(\"Element\")]),t._v(\"对象。那么现在笔者提两个问题，请读者先自己思考一下：\")]),t._v(\" \"),s(\"ol\",[s(\"li\",[t._v(\"如果没有widget层，单靠\"),s(\"code\",[t._v(\"Element\")]),t._v(\"层是否可以搭建起一个可用的UI框架？如果可以应该是什么样子？\")]),t._v(\" \"),s(\"li\",[t._v(\"Flutter UI框架能不做成响应式吗？\")])]),t._v(\" \"),s(\"p\",[t._v(\"对于问题1，答案当然是肯定的，因为我们之前说过widget树只是\"),s(\"code\",[t._v(\"Element\")]),t._v(\"树的映射，我们完全可以直接通过Element来搭建一个UI框架。下面举一个例子：\")]),t._v(\" \"),s(\"p\",[t._v(\"我们通过纯粹的Element来模拟一个\"),s(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"的功能，假设有一个页面，该页面有一个按钮，按钮的文本是一个9位数，点击一次按钮，则对9个数随机排一次序，代码如下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"HomeView\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ComponentElement\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"HomeView\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Widget widget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  String text \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"123456789\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Color primary\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"Theme\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"primaryColor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//1\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GestureDetector\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"text\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" style\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" primary\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          onPressed\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" t \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" text\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"split\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"shuffle\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            text \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" t\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"join\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"markNeedsBuild\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//点击后将该Element标记为dirty，Element将会rebuild\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"ul\",[s(\"li\",[s(\"p\",[t._v(\"上面\"),s(\"code\",[t._v(\"build\")]),t._v(\"方法不接收参数，这一点和在\"),s(\"code\",[t._v(\"StatelessWidget\")]),t._v(\"和\"),s(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"中\"),s(\"code\",[t._v(\"build(BuildContext)\")]),t._v(\"方法不同。代码中需要用到\"),s(\"code\",[t._v(\"BuildContext\")]),t._v(\"的地方直接用\"),s(\"code\",[t._v(\"this\")]),t._v(\"代替即可，如代码注释1处\"),s(\"code\",[t._v(\"Theme.of(this)\")]),t._v(\"参数直接传\"),s(\"code\",[t._v(\"this\")]),t._v(\"即可，因为当前对象本身就是\"),s(\"code\",[t._v(\"Element\")]),t._v(\"实例。\")])]),t._v(\" \"),s(\"li\",[s(\"p\",[t._v(\"当\"),s(\"code\",[t._v(\"text\")]),t._v(\"发生改变时，我们调用\"),s(\"code\",[t._v(\"markNeedsBuild()\")]),t._v(\"方法将当前Element标记为dirty即可，标记为dirty的Element会在下一帧中重建。实际上，\"),s(\"code\",[t._v(\"State.setState()\")]),t._v(\"在内部也是调用的\"),s(\"code\",[t._v(\"markNeedsBuild()\")]),t._v(\"方法。\")])]),t._v(\" \"),s(\"li\",[s(\"p\",[t._v(\"上面代码中build方法返回的仍然是一个widget，这是由于Flutter框架中已经有了widget这一层，并且组件库都已经是以widget的形式提供了，如果在Flutter框架中所有组件都像示例的\"),s(\"code\",[t._v(\"HomeView\")]),t._v(\"一样以\"),s(\"code\",[t._v(\"Element\")]),t._v(\"形式提供，那么就可以用纯\"),s(\"code\",[t._v(\"Element\")]),t._v(\"来构建UI了\"),s(\"code\",[t._v(\"HomeView\")]),t._v(\"的build方法返回值类型就可以是\"),s(\"code\",[t._v(\"Element\")]),t._v(\"了。\")])])]),t._v(\" \"),s(\"p\",[t._v(\"如果我们需要将上面代码在现有Flutter框架中跑起来，那么还是得提供一个“适配器”widget将\"),s(\"code\",[t._v(\"HomeView\")]),t._v(\"结合到现有框架中，下面\"),s(\"code\",[t._v(\"CustomHome\")]),t._v(\"就相当于“适配器”：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CustomHome\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Widget\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Element \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createElement\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"HomeView\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"现在就可以将\"),s(\"code\",[t._v(\"CustomHome\")]),t._v(\"添加到widget树了，我们在一个新路由页创建它，最终效果如下如图14-1和14-2（点击后）所示：\")]),t._v(\" \"),s(\"p\",[s(\"img\",{attrs:{src:n(384),alt:\"图14-1\"}}),t._v(\" \"),s(\"img\",{attrs:{src:n(385),alt:\"图14-2\"}})]),t._v(\" \"),s(\"p\",[t._v(\"点击按钮则按钮文本会随机排序。\")]),t._v(\" \"),s(\"p\",[t._v(\"对于问题2，答案当然也是肯定的，Flutter engine提供的dart API是原始且独立的，这个与操作系统提供的API类似，上层UI框架设计成什么样完全取决于设计者，完全可以将UI框架设计成Android风格或iOS风格，但这些事Google不会再去做，我们也没必要再去搞这一套，这是因为响应式的思想本身是很棒的，之所以提出这个问题，是因为笔者认为做与不做是一回事，但知道能不能做是另一回事，这能反映出我们对知识的理解程度。\")]),t._v(\" \"),s(\"h3\",{attrs:{id:\"总结\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),s(\"p\",[t._v(\"本节详细的介绍了\"),s(\"code\",[t._v(\"Element\")]),t._v(\"的生命周期，以及它Widget、BuildContext的关系，也介绍了Element在Flutter UI系统中的角色和作用，我们将在下一节介绍Flutter UI系统中另一个重要的角色RenderObject。\")])])}),[],!1,null,null,null);e.default=a.exports}}]);"
  },
  {
    "path": "docs/assets/js/41.7b70eaf0.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[41],{401:function(t,s,a){t.exports=a.p+\"assets/img/15-6.92dd6b15.png\"},402:function(t,s,a){t.exports=a.p+\"assets/img/15-7.aa6d4238.png\"},403:function(t,s,a){t.exports=a.p+\"assets/img/15-8.1362ff85.png\"},771:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_15-8-多语言和多主题\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_15-8-多语言和多主题\"}},[t._v(\"#\")]),t._v(\" 15.8 多语言和多主题\")]),t._v(\" \"),n(\"p\",[t._v(\"本实例APP中语言和主题都是可以设置的，而两者都是通过\"),n(\"code\",[t._v(\"ChangeNotifierProvider\")]),t._v(\"来实现的：我们在\"),n(\"code\",[t._v(\"main\")]),t._v(\"函数中使用了\"),n(\"code\",[t._v(\"Consumer2\")]),t._v(\"，依赖了\"),n(\"code\",[t._v(\"ThemeModel\")]),t._v(\"和\"),n(\"code\",[t._v(\"LocaleModel\")]),t._v(\"，因此，当我们在语言和主题设置页更该当前的配置后，\"),n(\"code\",[t._v(\"Consumer2\")]),t._v(\"的\"),n(\"code\",[t._v(\"builder\")]),t._v(\"都会重新执行，构建一个新的\"),n(\"code\",[t._v(\"MaterialApp\")]),t._v(\"，所以修改会立即生效。下面看一下语言和主题设置页的实现。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_15-8-1-语言选择页\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_15-8-1-语言选择页\"}},[t._v(\"#\")]),t._v(\" 15.8.1 语言选择页\")]),t._v(\" \"),n(\"p\",[t._v(\"APP语言选择页提供三个选项：中文简体、美国英语、跟随系统。我们将当前APP使用的语言高亮显示，并且在后面添加一个“对号”图标，实现如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"LanguageRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" color \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Theme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"primaryColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" localeModel \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Provider\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"of\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"LocaleModel\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" gm \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" GmLocalizations\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//构建语言选择项\")]),t._v(\"\\n    Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_buildLanguageItem\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String lan\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          lan\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 对APP当前语言进行高亮显示\")]),t._v(\"\\n          style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" localeModel\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"locale \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" value \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" color \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        trailing\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n            localeModel\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"locale \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" value \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"done\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        onTap\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 更新locale后MaterialApp会重新build\")]),t._v(\"\\n          localeModel\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"locale \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      appBar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"gm\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"language\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      body\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_buildLanguageItem\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"中文简体\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"zh_CN\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_buildLanguageItem\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"English\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"en_US\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_buildLanguageItem\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"gm\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"auto\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上面代码逻辑很简单，唯一需要注意的是我们在\"),n(\"code\",[t._v(\"build(…)\")]),t._v(\"方法里面定义了\"),n(\"code\",[t._v(\"_buildLanguageItem(…)\")]),t._v(\"方法，它和在\"),n(\"code\",[t._v(\"LanguageRoute\")]),t._v(\"类中定义该方法的区别就在于：在\"),n(\"code\",[t._v(\"build(…)\")]),t._v(\"内定义的方法可以共享\"),n(\"code\",[t._v(\"build(...)\")]),t._v(\"方法上下文中的变量，本例中是共享了\"),n(\"code\",[t._v(\"localeModel\")]),t._v(\"。当然，如果\"),n(\"code\",[t._v(\"_buildLanguageItem(…)\")]),t._v(\"的实现复杂一些的话不建议这样做，此时最好是将其作为\"),n(\"code\",[t._v(\"LanguageRoute\")]),t._v(\"类的方法。该页面运行效果如图15-6、15-7所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(401),alt:\"15-6\"}}),n(\"img\",{attrs:{src:a(402),alt:\"15-7\"}})]),t._v(\" \"),n(\"p\",[t._v(\"切换语言后立即生效。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_15-8-2-主题选择页\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_15-8-2-主题选择页\"}},[t._v(\"#\")]),t._v(\" 15.8.2 主题选择页\")]),t._v(\" \"),n(\"p\",[t._v(\"一个完整的主题\"),n(\"code\",[t._v(\"Theme\")]),t._v(\"包括很多选项，这些选项在\"),n(\"code\",[t._v(\"ThemeData\")]),t._v(\"中定义。本实例为了简单起见，我们只配置主题颜色。我们提供几种默认预定义的主题色供用户选择，用户点击一种色块后则更新主题。主题选择页的实现代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ThemeChangeRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      appBar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"GmLocalizations\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"theme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      body\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//显示主题色块\")]),t._v(\"\\n        children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Global\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"themes\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"map\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GestureDetector\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"symmetric\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"vertical\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"5\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" horizontal\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"40\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onTap\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//主题更新后，MaterialApp会重新build\")]),t._v(\"\\n              Provider\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"of\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ThemeModel\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"theme \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toList\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图15-8所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(403),alt:\"15-8\"}})]),t._v(\" \"),n(\"p\",[t._v(\"点击其它主题色块后，APP主题色立马切换生效。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/42.057d8df6.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[42],{398:function(t,s,a){t.exports=a.p+\"assets/img/2-5.7456ef8f.png\"},399:function(t,s,a){t.exports=a.p+\"assets/img/2-6.fb55e91b.png\"},400:function(t,s,a){t.exports=a.p+\"assets/img/2-7.90b5e799.png\"},769:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_2-3-包管理\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_2-3-包管理\"}},[t._v(\"#\")]),t._v(\" 2.3 包管理\")]),t._v(\" \"),n(\"p\",[t._v(\"在软件开发中，很多时候有一些公共的库或SDK可能会被很多项目用到，因此，将这些代码单独抽到一个独立模块，然后哪个项目需要使用时再直接集成这个模块，便可大大提高开发效率。很多编程语言或开发工具都支持这种“模块共享”机制，如Java语言中这种独立模块会被打成一个jar包，Android中的aar包，Web开发中的npm包等。为了方便表述，我们将这种可共享的独立模块统一称为“包”（ Package）。\")]),t._v(\" \"),n(\"p\",[t._v(\"一个APP在实际开发中往往会依赖很多包，而这些包通常都有交叉依赖关系、版本依赖等，如果由开发者手动来管理应用中的依赖包将会非常麻烦。因此，各种开发生态或编程语言官方通常都会提供一些包管理工具，比如在Android提供了Gradle来管理依赖，iOS用Cocoapods或Carthage来管理依赖，Node中通过npm等。而在Flutter开发中也有自己的包管理工具。本节我们主要介绍一下flutter如何使用配置文件\"),n(\"code\",[t._v(\"pubspec.yaml\")]),t._v(\"（位于项目根目录）来管理第三方依赖包。\")]),t._v(\" \"),n(\"p\",[t._v(\"YAML是一种直观、可读性高并且容易被人类阅读的文件格式，它和xml或Json相比，它语法简单并非常容易解析，所以YAML常用于配置文件，Flutter也是用yaml文件作为其配置文件。Flutter项目默认的配置文件是\"),n(\"code\",[t._v(\"pubspec.yaml\")]),t._v(\"，我们看一个简单的示例：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-yaml extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"name\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" flutter_in_action\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"description\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" First Flutter application.\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"version\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" 1.0.0+1\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"dependencies\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"flutter\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"sdk\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" flutter\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"cupertino_icons\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ^0.1.2\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"dev_dependencies\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"flutter_test\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"sdk\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" flutter\\n    \\n\"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"flutter\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"uses-material-design\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean important\"}},[t._v(\"true\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"下面，我们逐一解释一下各个字段的意义：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"name\")]),t._v(\"：应用或包名称。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"description\")]),t._v(\": 应用或包的描述、简介。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"version\")]),t._v(\"：应用或包的版本号。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"dependencies\")]),t._v(\"：应用或包依赖的其它包或插件。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"dev_dependencies\")]),t._v(\"：开发环境依赖的工具包（而不是flutter应用本身依赖的包）。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"flutter\")]),t._v(\"：flutter相关的配置选项。\")])]),t._v(\" \"),n(\"p\",[t._v(\"如果我们的Flutter应用本身依赖某个包，我们需要将所依赖的包添加到\"),n(\"code\",[t._v(\"dependencies\")]),t._v(\" 下，接下来我们通过一个例子来演示一下如何添加、下载并使用第三方包。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"pub仓库\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#pub仓库\"}},[t._v(\"#\")]),t._v(\" Pub仓库\")]),t._v(\" \"),n(\"p\",[t._v(\"Pub（https://pub.dev/ ）是Google官方的Dart Packages仓库，类似于node中的npm仓库，android中的jcenter。我们可以在Pub上面查找我们需要的包和插件，也可以向Pub发布我们的包和插件。我们将在后面的章节中介绍如何向Pub发布我们的包和插件。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"示例\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),n(\"p\",[t._v(\"接下来，我们实现一个显示随机字符串的widget。有一个名为“english_words”的开源软件包，其中包含数千个常用的英文单词以及一些实用功能。我们首先在pub上找到english_words这个包（如图2-5所示），确定其最新的版本号和是否支持Flutter。\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(398),alt:\"图2-5\"}})]),t._v(\" \"),n(\"p\",[t._v(\"我们看到“english_words”包最新的版本是3.1.3，并且支持flutter，接下来：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"p\",[t._v(\"将“english_words”（3.1.3版本）添加到依赖项列表，如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-yaml extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"dependencies\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"flutter\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"sdk\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" flutter\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"cupertino_icons\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ^0.1.0\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"# 新添加的依赖\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"english_words\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ^3.1.3\\n\")])])])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"下载包。在Android Studio的编辑器视图中查看pubspec.yaml时（图2-6），单击右上角的 \"),n(\"strong\",[t._v(\"Packages get\")]),t._v(\" 。\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(399),alt:\"图2-6\"}})]),t._v(\" \"),n(\"p\",[t._v(\"这会将依赖包安装到您的项目。我们可以在控制台中看到以下内容：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-shell extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[n(\"code\",[t._v(\"flutter packages get\\nRunning \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"flutter packages get\"')]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"in\")]),t._v(\" flutter_in_action\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"..\")]),t._v(\".\\nProcess finished with \"),n(\"span\",{pre:!0,attrs:{class:\"token builtin class-name\"}},[t._v(\"exit\")]),t._v(\" code \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们也可以在控制台，定位到当前工程目录，然后手动运行\"),n(\"code\",[t._v(\"flutter packages get\")]),t._v(\" 命令来下载依赖包。另外，需要注意\"),n(\"code\",[t._v(\"dependencies\")]),t._v(\"和\"),n(\"code\",[t._v(\"dev_dependencies\")]),t._v(\"的区别，前者的依赖包将作为APP的源码的一部分参与编译，生成最终的安装包。而后者的依赖包只是作为开发阶段的一些工具包，主要是用于帮助我们提高开发、测试效率，比如flutter的自动化测试包等。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"引入\"),n(\"code\",[t._v(\"english_words\")]),t._v(\"包。\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:english_words/english_words.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"在输入时，Android Studio会自动提供有关库导入的建议选项。导入后该行代码将会显示为灰色，表示导入的库尚未使用。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"使用\"),n(\"code\",[t._v(\"english_words\")]),t._v(\"包来生成随机字符串。\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"RandomWordsWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 生成随机字符串\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" wordPair \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"WordPair\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"random\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"8.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"wordPair\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们将\"),n(\"code\",[t._v(\"RandomWordsWidget\")]),t._v(\" 添加到 \"),n(\"code\",[t._v(\"_MyHomePageState.build\")]),t._v(\" 的\"),n(\"code\",[t._v(\"Column\")]),t._v(\"的子widget中。\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  mainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RandomWordsWidget\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"如果应用程序正在运行，请使用热重载按钮（⚡️图标） 更新正在运行的应用程序。每次单击热重载或保存项目时，都会在正在运行的应用程序中随机选择不同的单词对。 这是因为单词对是在 \"),n(\"code\",[t._v(\"build\")]),t._v(\" 方法内部生成的。每次热更新时，\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法都会被执行，运行效果如图2-7所示。\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(400),alt:\"图2-7\"}})])])]),t._v(\" \"),n(\"h2\",{attrs:{id:\"其它依赖方式\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#其它依赖方式\"}},[t._v(\"#\")]),t._v(\" 其它依赖方式\")]),t._v(\" \"),n(\"p\",[t._v(\"上文所述的依赖方式是依赖Pub仓库的。但我们还可以依赖本地包和git仓库。\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"p\",[t._v(\"依赖本地包\")]),t._v(\" \"),n(\"p\",[t._v(\"如果我们正在本地开发一个包，包名为pkg1，我们可以通过下面方式依赖：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-yaml extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"dependencies\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n\\t\"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"pkg1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"path\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ../../code/pkg1\\n\")])])]),n(\"p\",[t._v(\"路径可以是相对的，也可以是绝对的。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"依赖Git：你也可以依赖存储在Git仓库中的包。如果软件包位于仓库的根目录中，请使用以下语法\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-yaml extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"dependencies\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"pkg1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"git\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"url\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" git\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"//github.com/xxx/pkg1.git\\n\")])])]),n(\"p\",[t._v(\"上面假定包位于Git存储库的根目录中。如果不是这种情况，可以使用path参数指定相对位置，例如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-yaml extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"dependencies\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"package1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"git\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"url\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" git\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"//github.com/flutter/packages.git\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"path\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" packages/package1        \\n\")])])])])]),t._v(\" \"),n(\"p\",[t._v(\"上面介绍的这些依赖方式是Flutter开发中常用的，但还有一些其它依赖方式，完整的内容读者可以自行查看：https://www.dartlang.org/tools/pub/dependencies 。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"总结\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),n(\"p\",[t._v(\"本节介绍了Flutter中包管理、引用、下载的整体流程，我们将在后面的章节中介绍如何开发并发布我们自己的包。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/43.b17a6aae.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[43],{576:function(t,a,s){t.exports=s.p+\"assets/img/2-2.de6feb35.png\"},577:function(t,a,s){t.exports=s.p+\"assets/img/2-3.c20b3236.png\"},578:function(t,a,s){t.exports=s.p+\"assets/img/2-4.1abb1cab.png\"},880:function(t,a,s){\"use strict\";s.r(a);var n=s(45),e=Object(n.a)({},(function(){var t=this,a=t.$createElement,n=t._self._c||a;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_2-2-路由管理\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_2-2-路由管理\"}},[t._v(\"#\")]),t._v(\" 2.2 路由管理\")]),t._v(\" \"),n(\"p\",[t._v(\"路由(Route)在移动开发中通常指页面（Page），这跟web开发中单页应用的Route概念意义是相同的，Route在Android中通常指一个Activity，在iOS中指一个ViewController。所谓路由管理，就是管理页面之间如何跳转，通常也可被称为导航管理。Flutter中的路由管理和原生开发类似，无论是Android还是iOS，导航管理都会维护一个路由栈，路由入栈(push)操作对应打开一个新页面，路由出栈(pop)操作对应页面关闭操作，而路由管理主要是指如何来管理路由栈。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_2-2-1-一个简单示例\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_2-2-1-一个简单示例\"}},[t._v(\"#\")]),t._v(\" 2.2.1 一个简单示例\")]),t._v(\" \"),n(\"p\",[t._v(\"我们在上一节“计数器”示例的基础上，做如下修改：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"p\",[t._v(\"创建一个新路由，命名“NewRoute”\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"NewRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      appBar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"New route\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      body\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"This is new route\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"新路由继承自\"),n(\"code\",[t._v(\"StatelessWidget\")]),t._v('，界面很简单，在页面中间显示一句\"This is new route\"。')])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"在\"),n(\"code\",[t._v(\"_MyHomePageState.build\")]),t._v(\"方法中的\"),n(\"code\",[t._v(\"Column\")]),t._v(\"的子widget中添加一个按钮（\"),n(\"code\",[t._v(\"FlatButton\")]),t._v(\"） :\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      mainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n         child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"open new route\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n         textColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n         onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//导航到新路由   \")]),t._v(\"\\n          Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"push\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n           \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MaterialPageRoute\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"NewRoute\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n           \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n         \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n       \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们添加了一个打开新路由的按钮，并将按钮文字颜色设置为蓝色，点击该按钮后就会打开新的路由页面，效果如图2-2和2-3所示。\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(576),alt:\"图2-2\"}}),t._v(\" \"),n(\"img\",{attrs:{src:s(577),alt:\"图2-3\"}})])])]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_2-2-2-materialpageroute\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_2-2-2-materialpageroute\"}},[t._v(\"#\")]),t._v(\" 2.2.2 MaterialPageRoute\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"MaterialPageRoute\")]),t._v(\"继承自\"),n(\"code\",[t._v(\"PageRoute\")]),t._v(\"类，\"),n(\"code\",[t._v(\"PageRoute\")]),t._v(\"类是一个抽象类，表示占有整个屏幕空间的一个模态路由页面，它还定义了路由构建及切换时过渡动画的相关接口及属性。\"),n(\"code\",[t._v(\"MaterialPageRoute\")]),t._v(\" 是Material组件库提供的组件，它可以针对不同平台，实现与平台页面切换动画风格一致的路由切换动画：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[t._v(\"对于Android，当打开新页面时，新的页面会从屏幕底部滑动到屏幕顶部；当关闭页面时，当前页面会从屏幕顶部滑动到屏幕底部后消失，同时上一个页面会显示到屏幕上。\")]),t._v(\" \"),n(\"li\",[t._v(\"对于iOS，当打开页面时，新的页面会从屏幕右侧边缘一致滑动到屏幕左边，直到新页面全部显示到屏幕上，而上一个页面则会从当前屏幕滑动到屏幕左侧而消失；当关闭页面时，正好相反，当前页面会从屏幕右侧滑出，同时上一个页面会从屏幕左侧滑入。\")])]),t._v(\" \"),n(\"p\",[t._v(\"下面我们介绍一下\"),n(\"code\",[t._v(\"MaterialPageRoute\")]),t._v(\" 构造函数的各个参数的意义：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MaterialPageRoute\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    WidgetBuilder builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    RouteSettings settings\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    bool maintainState \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    bool fullscreenDialog \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"builder\")]),t._v(\" 是一个WidgetBuilder类型的回调函数，它的作用是构建路由页面的具体内容，返回值是一个widget。我们通常要实现此回调，返回新路由的实例。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"settings\")]),t._v(\" 包含路由的配置信息，如路由名称、是否初始路由（首页）。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"maintainState\")]),t._v(\"：默认情况下，当入栈一个新路由时，原来的路由仍然会被保存在内存中，如果想在路由没用的时候释放其所占用的所有资源，可以设置\"),n(\"code\",[t._v(\"maintainState\")]),t._v(\"为false。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"fullscreenDialog\")]),t._v(\"表示新的路由页面是否是一个全屏的模态对话框，在iOS中，如果\"),n(\"code\",[t._v(\"fullscreenDialog\")]),t._v(\"为\"),n(\"code\",[t._v(\"true\")]),t._v(\"，新页面将会从屏幕底部滑入（而不是水平方向）。\")])]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[t._v(\"如果想自定义路由切换动画，可以自己继承PageRoute来实现，我们将在后面介绍动画时，实现一个自定义的路由组件。\")])]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_2-2-3-navigator\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_2-2-3-navigator\"}},[t._v(\"#\")]),t._v(\" 2.2.3 Navigator\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"Navigator\")]),t._v(\"是一个路由管理的组件，它提供了打开和退出路由页方法。\"),n(\"code\",[t._v(\"Navigator\")]),t._v(\"通过一个栈来管理活动路由集合。通常当前屏幕显示的页面就是栈顶的路由。\"),n(\"code\",[t._v(\"Navigator\")]),t._v(\"提供了一系列方法来管理路由栈，在此我们只介绍其最常用的两个方法：\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"future-push-buildcontext-context-route-route\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#future-push-buildcontext-context-route-route\"}},[t._v(\"#\")]),t._v(\" Future  push(BuildContext context, Route route)\")]),t._v(\" \"),n(\"p\",[t._v(\"将给定的路由入栈（即打开新的页面），返回值是一个\"),n(\"code\",[t._v(\"Future\")]),t._v(\"对象，用以接收新路由出栈（即关闭）时的返回数据。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"bool-pop-buildcontext-context-result\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#bool-pop-buildcontext-context-result\"}},[t._v(\"#\")]),t._v(\" bool  pop(BuildContext context, [ result ])\")]),t._v(\" \"),n(\"p\",[t._v(\"将栈顶路由出栈，\"),n(\"code\",[t._v(\"result\")]),t._v(\"为页面关闭时返回给上一个页面的数据。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"Navigator\")]),t._v(\" 还有很多其它方法，如\"),n(\"code\",[t._v(\"Navigator.replace\")]),t._v(\"、\"),n(\"code\",[t._v(\"Navigator.popUntil\")]),t._v(\"等，详情请参考API文档或SDK源码注释，在此不再赘述。下面我们还需要介绍一下路由相关的另一个概念“命名路由”。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"实例方法\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#实例方法\"}},[t._v(\"#\")]),t._v(\" 实例方法\")]),t._v(\" \"),n(\"p\",[t._v(\"Navigator类中第一个参数为context的\"),n(\"strong\",[t._v(\"静态方法\")]),t._v(\"都对应一个Navigator的\"),n(\"strong\",[t._v(\"实例方法\")]),t._v(\"， 比如\"),n(\"code\",[t._v(\"Navigator.push(BuildContext context, Route route)\")]),t._v(\"等价于\"),n(\"code\",[t._v(\"Navigator.of(context).push(Route route)\")]),t._v(\" ，下面命名路由相关的方法也是一样的。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_2-2-4-路由传值\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_2-2-4-路由传值\"}},[t._v(\"#\")]),t._v(\" 2.2.4 路由传值\")]),t._v(\" \"),n(\"p\",[t._v(\"很多时候，在路由跳转时我们需要带一些参数，比如打开商品详情页时，我们需要带一个商品id，这样商品详情页才知道展示哪个商品信息；又比如我们在填写订单时需要选择收货地址，打开地址选择页并选择地址后，可以将用户选择的地址返回到订单页等等。下面我们通过一个简单的示例来演示新旧路由如何传参。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"示例\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),n(\"p\",[t._v(\"我们创建一个\"),n(\"code\",[t._v(\"TipRoute\")]),t._v(\"路由，它接受一个提示文本参数，负责将传入它的文本显示在页面上，另外\"),n(\"code\",[t._v(\"TipRoute\")]),t._v(\"中我们添加一个“返回”按钮，点击后在返回上一个路由的同时会带上一个返回参数，下面我们看一下实现代码。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"TipRoute\")]),t._v(\"实现代码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TipRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TipRoute\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 接收一个text参数\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      appBar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"提示\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      body\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"18\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"我是返回值\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"返回\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"下面是打开新路由\"),n(\"code\",[t._v(\"TipRoute\")]),t._v(\"的代码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"RouterTestRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 打开`TipRoute`，并等待返回结果\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" result \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"push\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MaterialPageRoute\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TipRoute\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 路由参数\")]),t._v(\"\\n                  text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"我是提示xxxx\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//输出`TipRoute`路由返回结果\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"路由返回值: $result\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"打开提示页\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行上面代码，点击\"),n(\"code\",[t._v(\"RouterTestRoute\")]),t._v(\"页的“打开提示页”按钮，会打开\"),n(\"code\",[t._v(\"TipRoute\")]),t._v(\"页，运行效果如图2-4所示下：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(578),alt:\"图2-4\"}})]),t._v(\" \"),n(\"p\",[t._v(\"需要说明：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"p\",[t._v(\"提示文案“我是提示xxxx”是通过\"),n(\"code\",[t._v(\"TipRoute\")]),t._v(\"的\"),n(\"code\",[t._v(\"text\")]),t._v(\"参数传递给新路由页的。我们可以通过等待\"),n(\"code\",[t._v(\"Navigator.push(…)\")]),t._v(\"返回的\"),n(\"code\",[t._v(\"Future\")]),t._v(\"来获取新路由的返回数据。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"在\"),n(\"code\",[t._v(\"TipRoute\")]),t._v(\"页中有两种方式可以返回到上一页；第一种方式时直接点击导航栏返回箭头，第二种方式是点击页面中的“返回”按钮。这两种返回方式的区别是前者不会返回数据给上一个路由，而后者会。下面是分别点击页面中的返回按钮和导航栏返回箭头后，\"),n(\"code\",[t._v(\"RouterTestRoute\")]),t._v(\"页中\"),n(\"code\",[t._v(\"print\")]),t._v(\"方法在控制台输出的内容：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language- extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[n(\"code\",[t._v(\"I/flutter (27896): 路由返回值: 我是返回值\\nI/flutter (27896): 路由返回值: null\\n\")])])])])]),t._v(\" \"),n(\"p\",[t._v(\"上面介绍的是非命名路由的传值方式，命名路由的传值方式会有所不同，我们会在下面介绍命名路由时介绍。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_2-2-5-命名路由\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_2-2-5-命名路由\"}},[t._v(\"#\")]),t._v(\" 2.2.5 命名路由\")]),t._v(\" \"),n(\"p\",[t._v(\"所谓“命名路由”（Named Route）即有名字的路由，我们可以先给路由起一个名字，然后就可以通过路由名字直接打开新的路由了，这为路由管理带来了一种直观、简单的方式。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"路由表\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#路由表\"}},[t._v(\"#\")]),t._v(\" 路由表\")]),t._v(\" \"),n(\"p\",[t._v(\"要想使用命名路由，我们必须先提供并注册一个路由表（routing table），这样应用程序才知道哪个名字与哪个路由组件相对应。其实注册路由表就是给路由起名字，路由表的定义如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Map\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" WidgetBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" routes\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"它是一个\"),n(\"code\",[t._v(\"Map\")]),t._v(\"，key为路由的名字，是个字符串；value是个\"),n(\"code\",[t._v(\"builder\")]),t._v(\"回调函数，用于生成相应的路由widget。我们在通过路由名字打开新路由时，应用会根据路由名字在路由表中查找到对应的\"),n(\"code\",[t._v(\"WidgetBuilder\")]),t._v(\"回调函数，然后调用该回调函数生成路由widget并返回。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"注册路由表\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#注册路由表\"}},[t._v(\"#\")]),t._v(\" 注册路由表\")]),t._v(\" \"),n(\"p\",[t._v(\"路由表的注册方式很简单，我们回到之前“计数器”的示例，然后在\"),n(\"code\",[t._v(\"MyApp\")]),t._v(\"类的\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法中找到\"),n(\"code\",[t._v(\"MaterialApp\")]),t._v(\"，添加\"),n(\"code\",[t._v(\"routes\")]),t._v(\"属性，代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MaterialApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Flutter Demo'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  theme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ThemeData\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    primarySwatch\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//注册路由表\")]),t._v(\"\\n  routes\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"new_page\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"NewRoute\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 省略其它路由注册信息\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  home\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyHomePage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Flutter Demo Home Page'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"现在我们就完成了路由表的注册。上面的代码中\"),n(\"code\",[t._v(\"home\")]),t._v(\"路由并没有使用命名路由，如果我们也想将\"),n(\"code\",[t._v(\"home\")]),t._v(\"注册为命名路由应该怎么做呢？其实很简单，直接看代码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MaterialApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Flutter Demo'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  initialRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"/\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v('//名为\"/\"的路由作为应用的home(首页)')]),t._v(\"\\n  theme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ThemeData\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    primarySwatch\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//注册路由表\")]),t._v(\"\\n  routes\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"new_page\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"NewRoute\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"/\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyHomePage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Flutter Demo Home Page'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//注册首页路由\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"可以看到，我们只需在路由表中注册一下\"),n(\"code\",[t._v(\"MyHomePage\")]),t._v(\"路由，然后将其名字作为\"),n(\"code\",[t._v(\"MaterialApp\")]),t._v(\"的\"),n(\"code\",[t._v(\"initialRoute\")]),t._v(\"属性值即可，该属性决定应用的初始路由页是哪一个命名路由。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"通过路由名打开新路由页\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#通过路由名打开新路由页\"}},[t._v(\"#\")]),t._v(\" 通过路由名打开新路由页\")]),t._v(\" \"),n(\"p\",[t._v(\"要通过路由名称来打开新路由，可以使用\"),n(\"code\",[t._v(\"Navigator\")]),t._v(\" 的\"),n(\"code\",[t._v(\"pushNamed\")]),t._v(\"方法：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Future \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pushNamed\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" String routeName\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"Object arguments\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"Navigator\")]),t._v(\" 除了\"),n(\"code\",[t._v(\"pushNamed\")]),t._v(\"方法，还有\"),n(\"code\",[t._v(\"pushReplacementNamed\")]),t._v(\"等其他管理命名路由的方法，读者可以自行查看API文档。接下来我们通过路由名来打开新的路由页，修改\"),n(\"code\",[t._v(\"FlatButton\")]),t._v(\"的\"),n(\"code\",[t._v(\"onPressed\")]),t._v(\"回调代码，改为：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pushNamed\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"new_page\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//Navigator.push(context,\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//  MaterialPageRoute(builder: (context) {\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//  return NewRoute();\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//}));  \")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"热重载应用，再次点击“open new route”按钮，依然可以打开新的路由页。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"命名路由参数传递\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#命名路由参数传递\"}},[t._v(\"#\")]),t._v(\" 命名路由参数传递\")]),t._v(\" \"),n(\"p\",[t._v(\"在Flutter最初的版本中，命名路由是不能传递参数的，后来才支持了参数；下面展示命名路由如何传递并获取路由参数：\")]),t._v(\" \"),n(\"p\",[t._v(\"我们先注册一个路由：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\" routes\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"new_page\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"EchoRoute\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"在路由页通过\"),n(\"code\",[t._v(\"RouteSetting\")]),t._v(\"对象获取路由参数：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"EchoRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//获取路由参数  \")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" args\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"ModalRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"settings\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"arguments\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//...省略无关代码\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"在打开路由时传递参数\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pushNamed\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"new_page\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" arguments\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"hi\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"h3\",{attrs:{id:\"适配\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#适配\"}},[t._v(\"#\")]),t._v(\" 适配\")]),t._v(\" \"),n(\"p\",[t._v(\"假设我们也想将上面路由传参示例中的\"),n(\"code\",[t._v(\"TipRoute\")]),t._v(\"路由页注册到路由表中，以便也可以通过路由名来打开它。但是，由于\"),n(\"code\",[t._v(\"TipRoute\")]),t._v(\"接受一个\"),n(\"code\",[t._v(\"text\")]),t._v(\" 参数，我们如何在不改变\"),n(\"code\",[t._v(\"TipRoute\")]),t._v(\"源码的前提下适配这种情况？其实很简单：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MaterialApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n  routes\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"tip2\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TipRoute\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ModalRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"settings\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"arguments\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"h2\",{attrs:{id:\"_2-2-6-路由生成钩子\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_2-2-6-路由生成钩子\"}},[t._v(\"#\")]),t._v(\" 2.2.6 路由生成钩子\")]),t._v(\" \"),n(\"p\",[t._v(\"假设我们要开发一个电商APP，当用户没有登录时可以看店铺、商品等信息，但交易记录、购物车、用户个人信息等页面需要登录后才能看。为了实现上述功能，我们需要在打开每一个路由页前判断用户登录状态！如果每次打开路由前我们都需要去判断一下将会非常麻烦，那有什么更好的办法吗？答案是有！\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"MaterialApp\")]),t._v(\"有一个\"),n(\"code\",[t._v(\"onGenerateRoute\")]),t._v(\"属性，它在打开命名路由时可能会被调用，之所以说可能，是因为当调用\"),n(\"code\",[t._v(\"Navigator.pushNamed(...)\")]),t._v(\"打开命名路由时，如果指定的路由名在路由表中已注册，则会调用路由表中的\"),n(\"code\",[t._v(\"builder\")]),t._v(\"函数来生成路由组件；如果路由表中没有注册，才会调用\"),n(\"code\",[t._v(\"onGenerateRoute\")]),t._v(\"来生成路由。\"),n(\"code\",[t._v(\"onGenerateRoute\")]),t._v(\"回调签名如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Route\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"Function\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"RouteSettings settings\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"有了\"),n(\"code\",[t._v(\"onGenerateRoute\")]),t._v(\"回调，要实现上面控制页面权限的功能就非常容易：我们放弃使用路由表，取而代之的是提供一个\"),n(\"code\",[t._v(\"onGenerateRoute\")]),t._v(\"回调，然后在该回调中进行统一的权限控制，如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MaterialApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n  onGenerateRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"RouteSettings settings\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\t  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MaterialPageRoute\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\t\\t   String routeName \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" settings\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"name\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n       \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 如果访问的路由页需要登录，但当前未登录，则直接返回登录页路由，\")]),t._v(\"\\n       \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 引导用户登录；其它情况则正常打开路由。\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"blockquote\",[n(\"p\",[t._v(\"注意，\"),n(\"code\",[t._v(\"onGenerateRoute\")]),t._v(\"只会对命名路由生效。\")])]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_2-2-7-总结\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_2-2-7-总结\"}},[t._v(\"#\")]),t._v(\" 2.2.7 总结\")]),t._v(\" \"),n(\"p\",[t._v(\"本章先介绍了Flutter中路由管理、传参的方式，然后又着重介绍了命名路由相关内容。在此需要说明一点，由于命名路由只是一种可选的路由管理方式，在实际开发中，读者可能心中会犹豫到底使用哪种路由管理方式。在此，根据笔者经验，建议读者最好统一使用命名路由的管理方式，这将会带来如下好处：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"语义化更明确。\")]),t._v(\" \"),n(\"li\",[t._v(\"代码更好维护；如果使用匿名路由，则必须在调用\"),n(\"code\",[t._v(\"Navigator.push\")]),t._v(\"的地方创建新路由页，这样不仅需要import新路由页的dart文件，而且这样的代码将会非常分散。\")]),t._v(\" \"),n(\"li\",[t._v(\"可以通过\"),n(\"code\",[t._v(\"onGenerateRoute\")]),t._v(\"做一些全局的路由跳转前置处理逻辑。\")])]),t._v(\" \"),n(\"p\",[t._v(\"综上所述，笔者比较建议使用命名路由，当然这并不是什么金科玉律，读者可以根据自己偏好或实际情况来决定。\")]),t._v(\" \"),n(\"p\",[t._v(\"另外，还有一些关于路由管理的内容我们没有介绍，比如路由MaterialApp中还有\"),n(\"code\",[t._v(\"navigatorObservers\")]),t._v(\"和\"),n(\"code\",[t._v(\"onUnknownRoute\")]),t._v(\"两个回调属性，前者可以监听所有路由跳转动作，后者在打开一个不存在的命名路由时会被调用，由于这些功能并不常用，而且也比较简单，我们便不再花费篇幅来介绍了，读者可以自行查看API文档。\")])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/44.5f61dab7.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[44],{450:function(t,s,a){t.exports=a.p+\"assets/img/4-6.563f1017.png\"},451:function(t,s,a){t.exports=a.p+\"assets/img/4-7.632eedaf.png\"},452:function(t,s){t.exports=\"data:image/png;base64,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\"},787:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_4-4-流式布局\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_4-4-流式布局\"}},[t._v(\"#\")]),t._v(\" 4.4 流式布局\")]),t._v(\" \"),n(\"p\",[t._v(\"在介绍Row和Colum时，如果子widget超出屏幕范围，则会报溢出错误，如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"xxx\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图4-6所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(450),alt:\"图4-6\"}})]),t._v(\" \"),n(\"p\",[t._v(\"可以看到，右边溢出部分报错。这是因为Row默认只有一行，如果超出屏幕不会折行。我们把超出屏幕显示范围会自动折行的布局称为流式布局。Flutter中通过\"),n(\"code\",[t._v(\"Wrap\")]),t._v(\"和\"),n(\"code\",[t._v(\"Flow\")]),t._v(\"来支持流式布局，将上例中的Row换成Wrap后溢出部分则会自动折行，下面我们分别介绍\"),n(\"code\",[t._v(\"Wrap\")]),t._v(\"和\"),n(\"code\",[t._v(\"Flow\")]),t._v(\".\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_4-4-1-wrap\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_4-4-1-wrap\"}},[t._v(\"#\")]),t._v(\" 4.4.1 Wrap\")]),t._v(\" \"),n(\"p\",[t._v(\"下面是Wrap的定义:\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Wrap\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"direction \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Axis\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"horizontal\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"alignment \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" WrapAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"spacing \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"runAlignment \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" WrapAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"runSpacing \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"crossAxisAlignment \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" WrapCrossAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"textDirection\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"verticalDirection \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" VerticalDirection\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"down\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  List\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" children \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们可以看到Wrap的很多属性在\"),n(\"code\",[t._v(\"Row\")]),t._v(\"（包括\"),n(\"code\",[t._v(\"Flex\")]),t._v(\"和\"),n(\"code\",[t._v(\"Column\")]),t._v(\"）中也有，如\"),n(\"code\",[t._v(\"direction\")]),t._v(\"、\"),n(\"code\",[t._v(\"crossAxisAlignment\")]),t._v(\"、\"),n(\"code\",[t._v(\"textDirection\")]),t._v(\"、\"),n(\"code\",[t._v(\"verticalDirection\")]),t._v(\"等，这些参数意义是相同的，我们不再重复介绍，读者可以查阅前面介绍\"),n(\"code\",[t._v(\"Row\")]),t._v(\"的部分。读者可以认为\"),n(\"code\",[t._v(\"Wrap\")]),t._v(\"和\"),n(\"code\",[t._v(\"Flex\")]),t._v(\"（包括\"),n(\"code\",[t._v(\"Row\")]),t._v(\"和\"),n(\"code\",[t._v(\"Column\")]),t._v(\"）除了超出显示范围后\"),n(\"code\",[t._v(\"Wrap\")]),t._v(\"会折行外，其它行为基本相同。下面我们看一下\"),n(\"code\",[t._v(\"Wrap\")]),t._v(\"特有的几个属性：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"spacing\")]),t._v(\"：主轴方向子widget的间距\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"runSpacing\")]),t._v(\"：纵轴方向的间距\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"runAlignment\")]),t._v(\"：纵轴方向的对齐方式\")])]),t._v(\" \"),n(\"p\",[t._v(\"下面看一个示例子：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Wrap\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  spacing\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"8.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 主轴(水平)方向间距\")]),t._v(\"\\n  runSpacing\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"4.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 纵轴（垂直）方向间距\")]),t._v(\"\\n  alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" WrapAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//沿主轴方向居中\")]),t._v(\"\\n  children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Chip\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      avatar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CircleAvatar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"backgroundColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'A'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      label\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Hamilton'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Chip\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      avatar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CircleAvatar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"backgroundColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'M'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      label\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Lafayette'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Chip\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      avatar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CircleAvatar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"backgroundColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'H'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      label\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Mulligan'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Chip\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      avatar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CircleAvatar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"backgroundColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'J'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      label\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Laurens'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图4-7所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(451),alt:\"图4-7\"}})]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_4-4-2-flow\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_4-4-2-flow\"}},[t._v(\"#\")]),t._v(\" 4.4.2 Flow\")]),t._v(\" \"),n(\"p\",[t._v(\"我们一般很少会使用\"),n(\"code\",[t._v(\"Flow\")]),t._v(\"，因为其过于复杂，需要自己实现子widget的位置转换，在很多场景下首先要考虑的是\"),n(\"code\",[t._v(\"Wrap\")]),t._v(\"是否满足需求。\"),n(\"code\",[t._v(\"Flow\")]),t._v(\"主要用于一些需要自定义布局策略或性能要求较高(如动画中)的场景。\"),n(\"code\",[t._v(\"Flow\")]),t._v(\"有如下优点：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[t._v(\"性能好；\"),n(\"code\",[t._v(\"Flow\")]),t._v(\"是一个对子组件尺寸以及位置调整非常高效的控件，\"),n(\"code\",[t._v(\"Flow\")]),t._v(\"用转换矩阵在对子组件进行位置调整的时候进行了优化：在\"),n(\"code\",[t._v(\"Flow\")]),t._v(\"定位过后，如果子组件的尺寸或者位置发生了变化，在\"),n(\"code\",[t._v(\"FlowDelegate\")]),t._v(\"中的\"),n(\"code\",[t._v(\"paintChildren()\")]),t._v(\"方法中调用\"),n(\"code\",[t._v(\"context.paintChild\")]),t._v(\" 进行重绘，而\"),n(\"code\",[t._v(\"context.paintChild\")]),t._v(\"在重绘时使用了转换矩阵，并没有实际调整组件位置。\")]),t._v(\" \"),n(\"li\",[t._v(\"灵活；由于我们需要自己实现\"),n(\"code\",[t._v(\"FlowDelegate\")]),t._v(\"的\"),n(\"code\",[t._v(\"paintChildren()\")]),t._v(\"方法，所以我们需要自己计算每一个组件的位置，因此，可以自定义布局策略。\")])]),t._v(\" \"),n(\"p\",[t._v(\"缺点：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[t._v(\"使用复杂。\")]),t._v(\" \"),n(\"li\",[t._v(\"不能自适应子组件大小，必须通过指定父容器大小或实现\"),n(\"code\",[t._v(\"TestFlowDelegate\")]),t._v(\"的\"),n(\"code\",[t._v(\"getSize\")]),t._v(\"返回固定大小。\")])]),t._v(\" \"),n(\"p\",[t._v(\"示例：\")]),t._v(\" \"),n(\"p\",[t._v(\"我们对六个色块进行自定义流式布局：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Flow\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  delegate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TestFlowDelegate\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"margin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"10.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"green\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"  color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"yellow\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"brown\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"  color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"purple\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"实现TestFlowDelegate:\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TestFlowDelegate\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FlowDelegate\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  EdgeInsets margin \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"zero\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TestFlowDelegate\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"margin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"paintChildren\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"FlowPaintingContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" x \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" margin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"left\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" y \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" margin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"top\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//计算每一个子widget的位置  \")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"for\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int i \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" i \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"childCount\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" i\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"++\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" w \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getChildSize\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"i\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"width \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" x \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" margin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"right\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"w \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"paintChild\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"i\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            transform\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Matrix4\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"translationValues\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                x\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" y\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        x \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" w \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" margin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"left\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        x \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" margin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"left\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        y \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+=\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getChildSize\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"i\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"height \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" margin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"top \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" margin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"bottom\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//绘制子widget(有优化)  \")]),t._v(\"\\n        context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"paintChild\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"i\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            transform\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Matrix4\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"translationValues\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                x\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" y\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n         x \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+=\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getChildSize\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"i\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"width \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" margin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"left \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" margin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"right\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getSize\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BoxConstraints constraints\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//指定Flow的大小  \")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Size\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"infinity\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  bool \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"shouldRepaint\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"FlowDelegate oldDelegate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" oldDelegate \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果见图4-8：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(452),alt:\"图4-8\"}})]),t._v(\" \"),n(\"p\",[t._v(\"可以看到我们主要的任务就是实现\"),n(\"code\",[t._v(\"paintChildren\")]),t._v(\"，它的主要任务是确定每个子widget位置。由于Flow不能自适应子widget的大小，我们通过在\"),n(\"code\",[t._v(\"getSize\")]),t._v(\"返回一个固定大小来指定Flow的大小。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/45.381b04c9.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[45],{481:function(t,s,a){t.exports=a.p+\"assets/img/6-9.865c35f8.png\"},482:function(t,s,a){t.exports=a.p+\"assets/img/6-10.202cef73.png\"},483:function(t,s,a){t.exports=a.p+\"assets/img/6-11.a491f457.png\"},798:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_6-4-gridview\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_6-4-gridview\"}},[t._v(\"#\")]),t._v(\" 6.4 GridView\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"GridView\")]),t._v(\"可以构建一个二维网格列表，其默认构造函数定义如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GridView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  Axis scrollDirection \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Axis\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"vertical\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  bool reverse \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  ScrollController controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  bool primary\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  ScrollPhysics physics\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  bool shrinkWrap \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  EdgeInsetsGeometry padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" SliverGridDelegate gridDelegate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//控制子widget layout的委托\")]),t._v(\"\\n  bool addAutomaticKeepAlives \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  bool addRepaintBoundaries \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  double cacheExtent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  List\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" children \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们可以看到，\"),n(\"code\",[t._v(\"GridView\")]),t._v(\"和\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"的大多数参数都是相同的，它们的含义也都相同的，如有疑惑读者可以翻阅ListView一节，在此不再赘述。我们唯一需要关注的是\"),n(\"code\",[t._v(\"gridDelegate\")]),t._v(\"参数，类型是\"),n(\"code\",[t._v(\"SliverGridDelegate\")]),t._v(\"，它的作用是控制\"),n(\"code\",[t._v(\"GridView\")]),t._v(\"子组件如何排列(layout)。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"SliverGridDelegate\")]),t._v(\"是一个抽象类，定义了\"),n(\"code\",[t._v(\"GridView\")]),t._v(\" Layout相关接口，子类需要通过实现它们来实现具体的布局算法。Flutter中提供了两个\"),n(\"code\",[t._v(\"SliverGridDelegate\")]),t._v(\"的子类\"),n(\"code\",[t._v(\"SliverGridDelegateWithFixedCrossAxisCount\")]),t._v(\"和\"),n(\"code\",[t._v(\"SliverGridDelegateWithMaxCrossAxisExtent\")]),t._v(\"，我们可以直接使用，下面我们分别来介绍一下它们。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"slivergriddelegatewithfixedcrossaxiscount\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#slivergriddelegatewithfixedcrossaxiscount\"}},[t._v(\"#\")]),t._v(\" SliverGridDelegateWithFixedCrossAxisCount\")]),t._v(\" \"),n(\"p\",[t._v(\"该子类实现了一个横轴为固定数量子元素的layout算法，其构造函数为：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SliverGridDelegateWithFixedCrossAxisCount\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" double crossAxisCount\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n  double mainAxisSpacing \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  double crossAxisSpacing \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  double childAspectRatio \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"crossAxisCount\")]),t._v(\"：横轴子元素的数量。此属性值确定后子元素在横轴的长度就确定了，即ViewPort横轴长度除以\"),n(\"code\",[t._v(\"crossAxisCount\")]),t._v(\"的商。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"mainAxisSpacing\")]),t._v(\"：主轴方向的间距。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"crossAxisSpacing\")]),t._v(\"：横轴方向子元素的间距。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"childAspectRatio\")]),t._v(\"：子元素在横轴长度和主轴长度的比例。由于\"),n(\"code\",[t._v(\"crossAxisCount\")]),t._v(\"指定后，子元素横轴长度就确定了，然后通过此参数值就可以确定子元素在主轴的长度。\")])]),t._v(\" \"),n(\"p\",[t._v(\"可以发现，子元素的大小是通过\"),n(\"code\",[t._v(\"crossAxisCount\")]),t._v(\"和\"),n(\"code\",[t._v(\"childAspectRatio\")]),t._v(\"两个参数共同决定的。注意，这里的子元素指的是子组件的最大显示空间，注意确保子组件的实际大小不要超出子元素的空间。\")]),t._v(\" \"),n(\"p\",[t._v(\"下面看一个例子：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GridView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  gridDelegate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SliverGridDelegateWithFixedCrossAxisCount\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      crossAxisCount\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//横轴三个子widget\")]),t._v(\"\\n      childAspectRatio\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.0\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//宽高比为1时，子widget\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"ac_unit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"airport_shuttle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"all_inclusive\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"beach_access\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cake\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"free_breakfast\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"img\",{attrs:{src:a(481),alt:\"图6-9\"}})]),t._v(\" \"),n(\"h4\",{attrs:{id:\"gridview-count\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#gridview-count\"}},[t._v(\"#\")]),t._v(\" GridView.count\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"GridView.count\")]),t._v(\"构造函数内部使用了\"),n(\"code\",[t._v(\"SliverGridDelegateWithFixedCrossAxisCount\")]),t._v(\"，我们通过它可以快速的创建横轴固定数量子元素的\"),n(\"code\",[t._v(\"GridView\")]),t._v(\"，上面的示例代码等价于：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"GridView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"count\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \\n  crossAxisCount\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  childAspectRatio\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"ac_unit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"airport_shuttle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"all_inclusive\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"beach_access\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cake\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"free_breakfast\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"h3\",{attrs:{id:\"slivergriddelegatewithmaxcrossaxisextent\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#slivergriddelegatewithmaxcrossaxisextent\"}},[t._v(\"#\")]),t._v(\" SliverGridDelegateWithMaxCrossAxisExtent\")]),t._v(\" \"),n(\"p\",[t._v(\"该子类实现了一个横轴子元素为固定最大长度的layout算法，其构造函数为：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SliverGridDelegateWithMaxCrossAxisExtent\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  double maxCrossAxisExtent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  double mainAxisSpacing \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  double crossAxisSpacing \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  double childAspectRatio \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"maxCrossAxisExtent\")]),t._v(\"为子元素在横轴上的最大长度，之所以是“最大”长度，是\"),n(\"strong\",[t._v(\"因为横轴方向每个子元素的长度仍然是等分的\")]),t._v(\"，举个例子，如果ViewPort的横轴长度是450，那么当\"),n(\"code\",[t._v(\"maxCrossAxisExtent\")]),t._v(\"的值在区间[450/4，450/3)内的话，子元素最终实际长度都为112.5，而\"),n(\"code\",[t._v(\"childAspectRatio\")]),t._v(\"所指的子元素横轴和主轴的长度比为\"),n(\"strong\",[t._v(\"最终的长度比\")]),t._v(\"。其它参数和\"),n(\"code\",[t._v(\"SliverGridDelegateWithFixedCrossAxisCount\")]),t._v(\"相同。\")]),t._v(\" \"),n(\"p\",[t._v(\"下面我们看一个例子：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GridView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"zero\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  gridDelegate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SliverGridDelegateWithMaxCrossAxisExtent\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      maxCrossAxisExtent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"120.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      childAspectRatio\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2.0\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//宽高比为2\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"ac_unit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"airport_shuttle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"all_inclusive\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"beach_access\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cake\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"free_breakfast\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"img\",{attrs:{src:a(482),alt:\"图6-10\"}})]),t._v(\" \"),n(\"h4\",{attrs:{id:\"gridview-extent\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#gridview-extent\"}},[t._v(\"#\")]),t._v(\" GridView.extent\")]),t._v(\" \"),n(\"p\",[t._v(\"GridView.extent构造函数内部使用了SliverGridDelegateWithMaxCrossAxisExtent，我们通过它可以快速的创建纵轴子元素为固定最大长度的的GridView，上面的示例代码等价于：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"GridView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"extent\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n   maxCrossAxisExtent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"120.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n   childAspectRatio\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n   children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"ac_unit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"airport_shuttle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"all_inclusive\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"beach_access\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cake\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"free_breakfast\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"h3\",{attrs:{id:\"gridview-builder\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#gridview-builder\"}},[t._v(\"#\")]),t._v(\" GridView.builder\")]),t._v(\" \"),n(\"p\",[t._v(\"上面我们介绍的GridView都需要一个widget数组作为其子元素，这些方式都会提前将所有子widget都构建好，所以只适用于子widget数量比较少时，当子widget比较多时，我们可以通过\"),n(\"code\",[t._v(\"GridView.builder\")]),t._v(\"来动态创建子widget。\"),n(\"code\",[t._v(\"GridView.builder\")]),t._v(\" 必须指定的参数有两个：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"GridView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" SliverGridDelegate gridDelegate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" IndexedWidgetBuilder itemBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"其中\"),n(\"code\",[t._v(\"itemBuilder\")]),t._v(\"为子widget构建器。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"示例\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),n(\"p\",[t._v(\"假设我们需要从一个异步数据源（如网络）分批获取一些\"),n(\"code\",[t._v(\"Icon\")]),t._v(\"，然后用\"),n(\"code\",[t._v(\"GridView\")]),t._v(\"来展示：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"InfiniteGridView\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _InfiniteGridViewState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_InfiniteGridViewState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_InfiniteGridViewState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"InfiniteGridView\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\n  List\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"IconData\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _icons \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//保存Icon数据\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 初始化数据  \")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_retrieveIcons\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" GridView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        gridDelegate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SliverGridDelegateWithFixedCrossAxisCount\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            crossAxisCount\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//每行三列\")]),t._v(\"\\n            childAspectRatio\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.0\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//显示区域宽高相等\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        itemCount\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"length\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        itemBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//如果显示到最后一个并且Icon总数小于200时继续获取数据\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"index \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" _icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"length \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\" _icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"length \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_retrieveIcons\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//模拟异步获取数据\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_retrieveIcons\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Future\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"delayed\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"milliseconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        _icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addAll\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"ac_unit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"airport_shuttle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"all_inclusive\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"beach_access\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cake\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"free_breakfast\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"_retrieveIcons()\")]),t._v(\"：在此方法中我们通过\"),n(\"code\",[t._v(\"Future.delayed\")]),t._v(\"来模拟从异步数据源获取数据，每次获取数据需要200毫秒，获取成功后将新数据添加到_icons，然后调用setState重新构建。\")]),t._v(\" \"),n(\"li\",[t._v(\"在itemBuilder中，如果显示到最后一个时，判断是否需要继续获取数据，然后返回一个Icon。\")])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"更多\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#更多\"}},[t._v(\"#\")]),t._v(\" 更多\")]),t._v(\" \"),n(\"p\",[t._v(\"Flutter的\"),n(\"code\",[t._v(\"GridView\")]),t._v(\"默认子元素显示空间是相等的，但在实际开发中，你可能会遇到子元素大小不等的情况，如下面这样的布局：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(483),alt:\"图6-11\"}})]),t._v(\" \"),n(\"p\",[t._v(\"Pub上有一个包“flutter_staggered_grid_view” ，它实现了一个交错GridView的布局模型，可以很轻松的实现这种布局，详情读者可以自行了解。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/46.1c911d7c.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[46],{491:function(t,s,a){t.exports=a.p+\"assets/img/6-14.1b612437.png\"},492:function(t,s,a){t.exports=a.p+\"assets/img/6-15.00ac68b6.png\"},493:function(t,s,a){t.exports=a.p+\"assets/img/6-16.20d0839c.png\"},802:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_6-6-滚动监听及控制\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_6-6-滚动监听及控制\"}},[t._v(\"#\")]),t._v(\" 6.6 滚动监听及控制\")]),t._v(\" \"),n(\"p\",[t._v(\"在前几节中，我们介绍了Flutter中常用的可滚动组件，也说过可以用\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"来控制可滚动组件的滚动位置，本节先介绍一下\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"，然后以\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"为例，展示一下\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"的具体用法。最后，再介绍一下路由切换时如何来保存滚动位置。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_6-6-1-scrollcontroller\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_6-6-1-scrollcontroller\"}},[t._v(\"#\")]),t._v(\" 6.6.1 ScrollController\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"ScrollController\")]),t._v(\"构造函数如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ScrollController\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  double initialScrollOffset \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//初始滚动位置\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"keepScrollOffset \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//是否保存滚动位置\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们介绍一下\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"常用的属性和方法：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"offset\")]),t._v(\"：可滚动组件当前的滚动位置。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"jumpTo(double offset)\")]),t._v(\"、\"),n(\"code\",[t._v(\"animateTo(double offset,...)\")]),t._v(\"：这两个方法用于跳转到指定的位置，它们不同之处在于，后者在跳转时会执行一个动画，而前者不会。\")])]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"ScrollController\")]),t._v(\"还有一些属性和方法，我们将在后面原理部分解释。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"滚动监听\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#滚动监听\"}},[t._v(\"#\")]),t._v(\" 滚动监听\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"ScrollController\")]),t._v(\"间接继承自\"),n(\"code\",[t._v(\"Listenable\")]),t._v(\"，我们可以根据\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"来监听滚动事件，如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"h3\",{attrs:{id:\"示例\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),n(\"p\",[t._v(\"我们创建一个\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"，当滚动位置发生变化时，我们先打印出当前滚动位置，然后判断当前位置是否超过1000像素，如果超过则在屏幕右下角显示一个“返回顶部”的按钮，该按钮点击后可以使ListView恢复到初始位置；如果没有超过1000像素，则隐藏“返回顶部”按钮。代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ScrollControllerTestRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  ScrollControllerTestRouteState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ScrollControllerTestRouteState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ScrollControllerTestRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ScrollControllerTestRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  ScrollController _controller \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ScrollController\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  bool showToTopBtn \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//是否显示“返回到顶部”按钮\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//监听滚动事件，打印滚动位置\")]),t._v(\"\\n    _controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//打印滚动位置\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"offset \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1000\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\" showToTopBtn\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          showToTopBtn \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"offset \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1000\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\" showToTopBtn \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          showToTopBtn \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//为了避免内存泄露，需要调用_controller.dispose\")]),t._v(\"\\n    _controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      appBar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"滚动控制\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      body\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scrollbar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ListView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            itemCount\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            itemExtent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//列表项高度固定时，显式指定高度是一个好习惯(性能消耗小)\")]),t._v(\"\\n            controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            itemBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$index\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      floatingActionButton\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"showToTopBtn \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FloatingActionButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"arrow_upward\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//返回到顶部时执行动画\")]),t._v(\"\\n            _controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"animateTo\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                duration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"milliseconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                curve\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Curves\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"ease\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"代码说明已经包含在注释里，下面我们看看运行效果：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(491),alt:\"图6-14\"}}),n(\"img\",{attrs:{src:a(492),alt:\"图6-15\"}})]),t._v(\" \"),n(\"p\",[t._v(\"由于列表项高度为50像素，当滑动到第20个列表项后，右下角“返回顶部”按钮会显示，点击该按钮，ListView会在返回顶部的过程中执行一个滚动动画，动画时间是200毫秒，动画曲线是\"),n(\"code\",[t._v(\"Curves.ease\")]),t._v(\"，关于动画的详细内容我们将在后面“动画”一章中详细介绍。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"滚动位置恢复\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#滚动位置恢复\"}},[t._v(\"#\")]),t._v(\" 滚动位置恢复\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"PageStorage\")]),t._v(\"是一个用于保存页面(路由)相关数据的组件，它并不会影响子树的UI外观，其实，\"),n(\"code\",[t._v(\"PageStorage\")]),t._v(\"是一个功能型组件，它拥有一个存储桶（bucket），子树中的Widget可以通过指定不同的\"),n(\"code\",[t._v(\"PageStorageKey\")]),t._v(\"来存储各自的数据或状态。\")]),t._v(\" \"),n(\"p\",[t._v(\"每次滚动结束，可滚动组件都会将滚动位置\"),n(\"code\",[t._v(\"offset\")]),t._v(\"存储到\"),n(\"code\",[t._v(\"PageStorage\")]),t._v(\"中，当可滚动组件重新创建时再恢复。如果\"),n(\"code\",[t._v(\"ScrollController.keepScrollOffset\")]),t._v(\"为\"),n(\"code\",[t._v(\"false\")]),t._v(\"，则滚动位置将不会被存储，可滚动组件重新创建时会使用\"),n(\"code\",[t._v(\"ScrollController.initialScrollOffset\")]),t._v(\"；\"),n(\"code\",[t._v(\"ScrollController.keepScrollOffset\")]),t._v(\"为\"),n(\"code\",[t._v(\"true\")]),t._v(\"时，可滚动组件在\"),n(\"strong\",[t._v(\"第一次\")]),t._v(\"创建时，会滚动到\"),n(\"code\",[t._v(\"initialScrollOffset\")]),t._v(\"处，因为这时还没有存储过滚动位置。在接下来的滚动中就会存储、恢复滚动位置，而\"),n(\"code\",[t._v(\"initialScrollOffset\")]),t._v(\"会被忽略。\")]),t._v(\" \"),n(\"p\",[t._v(\"当一个路由中包含多个可滚动组件时，如果你发现在进行一些跳转或切换操作后，滚动位置不能正确恢复，这时你可以通过显式指定\"),n(\"code\",[t._v(\"PageStorageKey\")]),t._v(\"来分别跟踪不同的可滚动组件的位置，如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"PageStorageKey\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"PageStorageKey\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"不同的\"),n(\"code\",[t._v(\"PageStorageKey\")]),t._v(\"，需要不同的值，这样才可以为不同可滚动组件保存其滚动位置。\")]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[t._v(\"注意：一个路由中包含多个可滚动组件时，如果要分别跟踪它们的滚动位置，并非一定就得给他们分别提供\"),n(\"code\",[t._v(\"PageStorageKey\")]),t._v(\"。这是因为Scrollable本身是一个StatefulWidget，它的状态中也会保存当前滚动位置，所以，只要可滚动组件本身没有被从树上detach掉，那么其State就不会销毁(dispose)，滚动位置就不会丢失。只有当Widget发生结构变化，导致可滚动组件的State销毁或重新构建时才会丢失状态，这种情况就需要显式指定\"),n(\"code\",[t._v(\"PageStorageKey\")]),t._v(\"，通过\"),n(\"code\",[t._v(\"PageStorage\")]),t._v(\"来存储滚动位置，一个典型的场景是在使用\"),n(\"code\",[t._v(\"TabBarView\")]),t._v(\"时，在Tab发生切换时，Tab页中的可滚动组件的State就会销毁，这时如果想恢复滚动位置就需要指定\"),n(\"code\",[t._v(\"PageStorageKey\")]),t._v(\"。\")])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"scrollposition\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#scrollposition\"}},[t._v(\"#\")]),t._v(\" ScrollPosition\")]),t._v(\" \"),n(\"p\",[t._v(\"ScrollPosition是用来保存可滚动组件的滚动位置的。一个\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"对象可以同时被多个可滚动组件使用，\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"会为每一个可滚动组件创建一个\"),n(\"code\",[t._v(\"ScrollPosition\")]),t._v(\"对象，这些\"),n(\"code\",[t._v(\"ScrollPosition\")]),t._v(\"保存在\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"的\"),n(\"code\",[t._v(\"positions\")]),t._v(\"属性中（\"),n(\"code\",[t._v(\"List<ScrollPosition>\")]),t._v(\"）。\"),n(\"code\",[t._v(\"ScrollPosition\")]),t._v(\"是真正保存滑动位置信息的对象，\"),n(\"code\",[t._v(\"offset\")]),t._v(\"只是一个便捷属性：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"double \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" offset \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"pixels\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"一个\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"虽然可以对应多个可滚动组件，但是有一些操作，如读取滚动位置\"),n(\"code\",[t._v(\"offset\")]),t._v(\"，则需要一对一！但是我们仍然可以在一对多的情况下，通过其它方法读取滚动位置，举个例子，假设一个\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"同时被两个可滚动组件使用，那么我们可以通过如下方式分别读取他们的滚动位置：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\ncontroller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"positions\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"elementAt\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"pixels\\ncontroller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"positions\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"elementAt\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"pixels\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"    \\n\")])])]),n(\"p\",[t._v(\"我们可以通过\"),n(\"code\",[t._v(\"controller.positions.length\")]),t._v(\"来确定\"),n(\"code\",[t._v(\"controller\")]),t._v(\"被几个可滚动组件使用。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"scrollposition的方法\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#scrollposition的方法\"}},[t._v(\"#\")]),t._v(\" ScrollPosition的方法\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"ScrollPosition\")]),t._v(\"有两个常用方法：\"),n(\"code\",[t._v(\"animateTo()\")]),t._v(\" 和 \"),n(\"code\",[t._v(\"jumpTo()\")]),t._v(\"，它们是真正来控制跳转滚动位置的方法，\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"的这两个同名方法，内部最终都会调用\"),n(\"code\",[t._v(\"ScrollPosition\")]),t._v(\"的。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"scrollcontroller控制原理\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#scrollcontroller控制原理\"}},[t._v(\"#\")]),t._v(\" ScrollController控制原理\")]),t._v(\" \"),n(\"p\",[t._v(\"我们来介绍一下\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"的另外三个方法：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"ScrollPosition \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createScrollPosition\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    ScrollPhysics physics\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    ScrollContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    ScrollPosition oldPosition\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"attach\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ScrollPosition position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"detach\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ScrollPosition position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"当\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"和可滚动组件关联时，可滚动组件首先会调用\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"的\"),n(\"code\",[t._v(\"createScrollPosition()\")]),t._v(\"方法来创建一个\"),n(\"code\",[t._v(\"ScrollPosition\")]),t._v(\"来存储滚动位置信息，接着，可滚动组件会调用\"),n(\"code\",[t._v(\"attach()\")]),t._v(\"方法，将创建的\"),n(\"code\",[t._v(\"ScrollPosition\")]),t._v(\"添加到\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"的\"),n(\"code\",[t._v(\"positions\")]),t._v(\"属性中，这一步称为“注册位置”，只有注册后\"),n(\"code\",[t._v(\"animateTo()\")]),t._v(\" 和 \"),n(\"code\",[t._v(\"jumpTo()\")]),t._v(\"才可以被调用。\")]),t._v(\" \"),n(\"p\",[t._v(\"当可滚动组件销毁时，会调用\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"的\"),n(\"code\",[t._v(\"detach()\")]),t._v(\"方法，将其\"),n(\"code\",[t._v(\"ScrollPosition\")]),t._v(\"对象从\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"的\"),n(\"code\",[t._v(\"positions\")]),t._v(\"属性中移除，这一步称为“注销位置”，注销后\"),n(\"code\",[t._v(\"animateTo()\")]),t._v(\" 和 \"),n(\"code\",[t._v(\"jumpTo()\")]),t._v(\" 将不能再被调用。\")]),t._v(\" \"),n(\"p\",[t._v(\"需要注意的是，\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"的\"),n(\"code\",[t._v(\"animateTo()\")]),t._v(\" 和 \"),n(\"code\",[t._v(\"jumpTo()\")]),t._v(\"内部会调用所有\"),n(\"code\",[t._v(\"ScrollPosition\")]),t._v(\"的\"),n(\"code\",[t._v(\"animateTo()\")]),t._v(\" 和 \"),n(\"code\",[t._v(\"jumpTo()\")]),t._v(\"，以实现所有和该\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"关联的可滚动组件都滚动到指定的位置。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_6-6-2-滚动监听\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_6-6-2-滚动监听\"}},[t._v(\"#\")]),t._v(\" 6.6.2 滚动监听\")]),t._v(\" \"),n(\"p\",[t._v(\"Flutter Widget树中子Widget可以通过发送通知（Notification）与父(包括祖先)Widget通信。父级组件可以通过\"),n(\"code\",[t._v(\"NotificationListener\")]),t._v(\"组件来监听自己关注的通知，这种通信方式类似于Web开发中浏览器的事件冒泡，我们在Flutter中沿用“冒泡”这个术语，关于通知冒泡我们将在后面“事件处理与通知”一章中详细介绍。\")]),t._v(\" \"),n(\"p\",[t._v(\"可滚动组件在滚动时会发送\"),n(\"code\",[t._v(\"ScrollNotification\")]),t._v(\"类型的通知，\"),n(\"code\",[t._v(\"ScrollBar\")]),t._v(\"正是通过监听滚动通知来实现的。通过\"),n(\"code\",[t._v(\"NotificationListener\")]),t._v(\"监听滚动事件和通过\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"有两个主要的不同：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"通过NotificationListener可以在从可滚动组件到widget树根之间任意位置都能监听。而\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"只能和具体的可滚动组件关联后才可以。\")]),t._v(\" \"),n(\"li\",[t._v(\"收到滚动事件后获得的信息不同；\"),n(\"code\",[t._v(\"NotificationListener\")]),t._v(\"在收到滚动事件时，通知中会携带当前滚动位置和ViewPort的一些信息，而\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"只能获取当前滚动位置。\")])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"示例-2\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例-2\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),n(\"p\",[t._v(\"下面，我们监听\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"的滚动通知，然后显示当前滚动进度百分比：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ScrollNotificationTestRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _ScrollNotificationTestRouteState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_ScrollNotificationTestRouteState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_ScrollNotificationTestRouteState\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ScrollNotificationTestRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  String _progress \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"0%\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//保存进度百分比\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scrollbar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//进度条\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 监听滚动通知\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" NotificationListener\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ScrollNotification\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        onNotification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ScrollNotification notification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          double progress \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" notification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"metrics\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"pixels \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\"\\n              notification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"metrics\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"maxScrollExtent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//重新构建\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            _progress \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"${(progress * 100).toInt()}%\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"BottomEdge: ${notification.metrics.extentAfter == 0}\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//return true; //放开此行注释后，进度条将失效\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Stack\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            ListView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                itemCount\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                itemExtent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                itemBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$index\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircleAvatar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//显示进度百分比\")]),t._v(\"\\n              radius\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"30.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_progress\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              backgroundColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"black54\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行结果如图6-16所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(493),alt:\"图6-16\"}})]),t._v(\" \"),n(\"p\",[t._v(\"在接收到滚动事件时，参数类型为\"),n(\"code\",[t._v(\"ScrollNotification\")]),t._v(\"，它包括一个\"),n(\"code\",[t._v(\"metrics\")]),t._v(\"属性，它的类型是\"),n(\"code\",[t._v(\"ScrollMetrics\")]),t._v(\"，该属性包含当前ViewPort及滚动位置等信息：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"pixels\")]),t._v(\"：当前滚动位置。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"maxScrollExtent\")]),t._v(\"：最大可滚动长度。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"extentBefore\")]),t._v(\"：滑出ViewPort顶部的长度；此示例中相当于顶部滑出屏幕上方的列表长度。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"extentInside\")]),t._v(\"：ViewPort内部长度；此示例中屏幕显示的列表部分的长度。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"extentAfter\")]),t._v(\"：列表中未滑入ViewPort部分的长度；此示例中列表底部未显示到屏幕范围部分的长度。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"atEdge\")]),t._v(\"：是否滑到了可滚动组件的边界（此示例中相当于列表顶或底部）。\")])]),t._v(\" \"),n(\"p\",[t._v(\"ScrollMetrics还有一些其它属性，读者可以自行查阅API文档。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/47.692e2e27.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[47],{529:function(t,s){t.exports=\"data:image/png;base64,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\"},530:function(t,s,a){t.exports=a.p+\"assets/img/9-6.7d1d25c1.png\"},531:function(t,s,a){t.exports=a.p+\"assets/img/9-7.d8052e62.png\"},830:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_9-6-通用-动画切换-组件-animatedswitcher\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_9-6-通用-动画切换-组件-animatedswitcher\"}},[t._v(\"#\")]),t._v(\" 9.6 通用“动画切换”组件（AnimatedSwitcher）\")]),t._v(\" \"),n(\"p\",[t._v(\"实际开发中，我们经常会遇到切换UI元素的场景，比如Tab切换、路由切换。为了增强用户体验，通常在切换时都会指定一个动画，以使切换过程显得平滑。Flutter SDK组件库中已经提供了一些常用的切换组件，如\"),n(\"code\",[t._v(\"PageView\")]),t._v(\"、\"),n(\"code\",[t._v(\"TabView\")]),t._v(\"等，但是，这些组件并不能覆盖全部的需求场景，为此，Flutter SDK中提供了一个\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"组件，它定义了一种通用的UI切换抽象。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_9-6-1-animatedswitcher\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_9-6-1-animatedswitcher\"}},[t._v(\"#\")]),t._v(\" 9.6.1 AnimatedSwitcher\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\" 可以同时对其新、旧子元素添加显示、隐藏动画。也就是说在\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"的子元素发生变化时，会对其旧元素和新元素，我们先看看\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\" 的定义：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedSwitcher\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"duration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 新child显示动画时长\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"reverseDuration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 旧child隐藏的动画时长\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"switchInCurve \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Curves\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"linear\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 新child显示的动画曲线\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"switchOutCurve \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Curves\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"linear\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 旧child隐藏的动画曲线\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"transitionBuilder \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" AnimatedSwitcher\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"defaultTransitionBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 动画构建器\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"layoutBuilder \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" AnimatedSwitcher\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"defaultLayoutBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//布局构建器\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"当\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"的child发生变化时（类型或Key不同），旧child会执行隐藏动画，新child会执行执行显示动画。究竟执行何种动画效果则由\"),n(\"code\",[t._v(\"transitionBuilder\")]),t._v(\"参数决定，该参数接受一个\"),n(\"code\",[t._v(\"AnimatedSwitcherTransitionBuilder\")]),t._v(\"类型的builder，定义如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"typedef\")]),t._v(\" AnimatedSwitcherTransitionBuilder \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"Function\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Widget child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"该\"),n(\"code\",[t._v(\"builder\")]),t._v(\"在\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"的child切换时会分别对新、旧child绑定动画：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"对旧child，绑定的动画会反向执行（reverse）\")]),t._v(\" \"),n(\"li\",[t._v(\"对新child，绑定的动画会正向指向（forward）\")])]),t._v(\" \"),n(\"p\",[t._v(\"这样一下，便实现了对新、旧child的动画绑定。\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"的默认值是\"),n(\"code\",[t._v(\"AnimatedSwitcher.defaultTransitionBuilder\")]),t._v(\" ：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"defaultTransitionBuilder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Widget child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FadeTransition\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    opacity\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"可以看到，返回了\"),n(\"code\",[t._v(\"FadeTransition\")]),t._v(\"对象，也就是说默认情况，\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"会对新旧child执行“渐隐”和“渐显”动画。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"例子\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#例子\"}},[t._v(\"#\")]),t._v(\" 例子\")]),t._v(\" \"),n(\"p\",[t._v(\"下面我们看一个列子：实现一个计数器，然后再每一次自增的过程中，旧数字执行缩小动画隐藏，新数字执行放大动画显示，代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimatedSwitcherCounterRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedSwitcherCounterRoute\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n   _AnimatedSwitcherCounterRouteState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_AnimatedSwitcherCounterRouteState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_AnimatedSwitcherCounterRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"AnimatedSwitcherCounterRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   int _count \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n   Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n       child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n         mainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n         children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n           \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedSwitcher\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n             duration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"milliseconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"500\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n             transitionBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Widget child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n               \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//执行缩放动画\")]),t._v(\"\\n               \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ScaleTransition\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" scale\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n             \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n             child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n               \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'$_count'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n               \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//显示指定key，不同的key会被认为是不同的Text，这样才能执行动画\")]),t._v(\"\\n               key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ValueKey\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"int\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_count\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n               style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Theme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"textTheme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"headline4\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n             \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n           \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n           \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n             child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'+1'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n             onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n               \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                 _count \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n               \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n             \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n           \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n         \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n       \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行示例代码，当点击“+1”按钮时，原先的数字会逐渐缩小直至隐藏，而新数字会逐渐放大，我截取了动画执行过程的一帧，如图9-5所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(529),alt:\"图9-5\"}})]),t._v(\" \"),n(\"p\",[t._v(\"上图是第一次点击“+1”按钮后切换动画的一帧，此时“0”正在逐渐缩小，而“1”正在“0”的中间，正在逐渐放大。\")]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[t._v(\"注意：AnimatedSwitcher的新旧child，如果类型相同，则Key必须不相等。\")])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"animatedswitcher实现原理\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#animatedswitcher实现原理\"}},[t._v(\"#\")]),t._v(\" AnimatedSwitcher实现原理\")]),t._v(\" \"),n(\"p\",[t._v(\"实际上，\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"的实现原理是比较简单的，我们根据\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"的使用方式也可以猜个大概。要想实现新旧child切换动画，只需要明确两个问题：动画执行的时机是和如何对新旧child执行动画。从\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"的使用方式我们可以看到，当child发生变化时（子widget的key和类型\"),n(\"strong\",[t._v(\"不\")]),t._v(\"同时相等则认为发生变化），则重新会重新执行\"),n(\"code\",[t._v(\"build\")]),t._v(\"，然后动画开始执行。我们可以通过继承StatefulWidget来实现\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"，具体做法是在\"),n(\"code\",[t._v(\"didUpdateWidget\")]),t._v(\" 回调中判断其新旧child是否发生变化，如果发生变化，则对旧child执行反向退场（reverse）动画，对新child执行正向（forward）入场动画即可。下面是\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"实现的部分核心伪代码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Widget _widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didUpdateWidget\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"AnimatedSwitcher oldWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didUpdateWidget\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"oldWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 检查新旧child是否发生变化(key和类型同时相等则返回true，认为没变化)\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"canUpdate\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" oldWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// child没变化，...\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//child发生了变化，构建一个Stack来分别给新旧child执行动画\")]),t._v(\"\\n   _widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Stack\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//旧child应用FadeTransition\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FadeTransition\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n         opacity\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _controllerOldAnimation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n         child \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" oldWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//新child应用FadeTransition\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FadeTransition\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n         opacity\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _controllerNewAnimation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n         child \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 给旧child执行反向退场动画\")]),t._v(\"\\n    _controllerOldAnimation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"reverse\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//给新child执行正向入场动画\")]),t._v(\"\\n    _controllerNewAnimation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"forward\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//build方法\")]),t._v(\"\\nWidget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" _widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上面伪代码展示了\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"实现的核心逻辑，当然\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"真正的实现比这个复杂，它可以自定义进退场过渡动画以及执行动画时的布局等。在此，我们删繁就简，通过伪代码形式让读者能够清楚看到主要的实现思路，具体的实现读者可以参考\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"源码。\")]),t._v(\" \"),n(\"p\",[t._v(\"另外，Flutter SDK中还提供了一个\"),n(\"code\",[t._v(\"AnimatedCrossFade\")]),t._v(\"组件，它也可以切换两个子元素，切换过程执行渐隐渐显的动画，和\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"不同的是\"),n(\"code\",[t._v(\"AnimatedCrossFade\")]),t._v(\"是针对两个子元素，而\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"是在一个子元素的新旧值之间切换。\"),n(\"code\",[t._v(\"AnimatedCrossFade\")]),t._v(\"实现原理比较简单，也有和\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"类似的地方，因此不再赘述，读者有兴趣可以查看其源码。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_9-6-2-animatedswitcher高级用法\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_9-6-2-animatedswitcher高级用法\"}},[t._v(\"#\")]),t._v(\" 9.6.2 AnimatedSwitcher高级用法\")]),t._v(\" \"),n(\"p\",[t._v(\"假设现在我们想实现一个类似路由平移切换的动画：旧页面屏幕中向左侧平移退出，新页面重屏幕右侧平移进入。如果要用AnimatedSwitcher的话，我们很快就会发现一个问题：做不到！我们可能会写出下面的代码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedSwitcher\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  duration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"milliseconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  transitionBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Widget child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" tween\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"Tween\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Offset\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"begin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" end\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SlideTransition\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n       child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n       position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" tween\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"animate\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上面的代码有什么问题呢？我们前面说过在\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"的child切换时会分别对新child执行正向动画（forward），而对旧child执行反向动画（reverse），所以真正的效果便是：新child确实从屏幕右侧平移进入了，但旧child却会从屏幕\"),n(\"strong\",[t._v(\"右侧\")]),t._v(\"（而不是左侧）退出。其实也很容易理解，因为在没有特殊处理的情况下，同一个动画的正向和逆向正好是相反（对称）的。\")]),t._v(\" \"),n(\"p\",[t._v(\"那么问题来了，难道就不能使用\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"了？答案当然是否定的！仔细想想这个问题，究其原因，就是因为同一个\"),n(\"code\",[t._v(\"Animation\")]),t._v(\"正向（forward）和反向（reverse）是对称的。所以如果我们可以打破这种对称性，那么便可以实现这个功能了，下面我们来封装一个\"),n(\"code\",[t._v(\"MySlideTransition\")]),t._v(\"，它与\"),n(\"code\",[t._v(\"SlideTransition\")]),t._v(\"唯一的不同就是对动画的反向执行进行了定制（从左边滑出隐藏），代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MySlideTransition\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimatedWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MySlideTransition\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Offset\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"transformHitTests \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"position \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" listenable\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Offset\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" position \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" listenable\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" bool transformHitTests\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Widget child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Offset offset\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//动画反向执行时，调整x偏移，实现“从左边滑出隐藏”\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"status \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" AnimationStatus\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"reverse\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n         offset \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\"offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dx\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dy\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FractionalTranslation\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      translation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      transformHitTests\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" transformHitTests\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"调用时，将\"),n(\"code\",[t._v(\"SlideTransition\")]),t._v(\"替换成\"),n(\"code\",[t._v(\"MySlideTransition\")]),t._v(\"即可：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedSwitcher\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  duration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"milliseconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  transitionBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Widget child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" tween\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"Tween\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Offset\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"begin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" end\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MySlideTransition\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" tween\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"animate\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \\t      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行后，我截取动画执行过程中的一帧，如图9-6所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(530),alt:\"图9-6\"}})]),t._v(\" \"),n(\"p\",[t._v(\"上图中“0”从左侧滑出，而“1”从右侧滑入。可以看到，我们通过这种巧妙的方式实现了类似路由进场切换的动画，实际上Flutter路由切换也正是通过\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"来实现的。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"slidetransitionx\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#slidetransitionx\"}},[t._v(\"#\")]),t._v(\" SlideTransitionX\")]),t._v(\" \"),n(\"p\",[t._v(\"上面的示例我们实现了“左出右入”的动画，那如果要实现“右入左出”、“上入下出”或者 “下入上出”怎么办？当然，我们可以分别修改上面的代码，但是这样每种动画都得单独定义一个“Transition”，这很麻烦。本节将封装一个通用的\"),n(\"code\",[t._v(\"SlideTransitionX\")]),t._v(\" 来实现这种“出入滑动动画”，代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"SlideTransitionX\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimatedWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SlideTransitionX\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"transformHitTests \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"direction \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" AxisDirection\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"down\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"position \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" listenable\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 偏移在内部处理      \")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"switch\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"direction\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" AxisDirection\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"up\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n        _tween \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Tween\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"begin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" end\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"break\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" AxisDirection\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"right\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n        _tween \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Tween\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"begin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" end\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"break\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" AxisDirection\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"down\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n        _tween \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Tween\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"begin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" end\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"break\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" AxisDirection\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"left\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n        _tween \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Tween\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"begin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" end\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"break\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\\n  Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" position \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" listenable\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" bool transformHitTests\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Widget child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//退场（出）方向\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" AxisDirection direction\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  Tween\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Offset\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _tween\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Offset offset \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _tween\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"evaluate\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"status \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" AnimationStatus\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"reverse\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"switch\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"direction\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" AxisDirection\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"up\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n          offset \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dx\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\"offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dy\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"break\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" AxisDirection\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"right\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n          offset \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\"offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dx\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dy\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"break\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" AxisDirection\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"down\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n          offset \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dx\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\"offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dy\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"break\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" AxisDirection\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"left\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n          offset \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\"offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dx\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dy\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"break\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FractionalTranslation\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      translation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      transformHitTests\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" transformHitTests\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"现在如果我们想实现各种“滑动出入动画”便非常容易，只需给\"),n(\"code\",[t._v(\"direction\")]),t._v(\"传递不同的方向值即可，比如要实现“上入下出”，则：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedSwitcher\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  duration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"milliseconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  transitionBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Widget child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" tween\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"Tween\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Offset\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"begin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" end\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SlideTransitionX\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n     \\t\\t\\t\\t  direction\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" AxisDirection\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"down\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//上入下出\")]),t._v(\"\\n              position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \\t      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略其余代码\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行后，我截取动画执行过程中的一帧，如图9-7所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(531),alt:\"图9-7\"}})]),t._v(\" \"),n(\"p\",[t._v(\"上图中“1”从底部滑出，而“2”从顶部滑入。读者可以尝试给\"),n(\"code\",[t._v(\"SlideTransitionX\")]),t._v(\"的\"),n(\"code\",[t._v(\"direction\")]),t._v(\"取不同的值来查看运行效果。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"总结\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),n(\"p\",[t._v(\"本节我们学习了\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"的详细用法，同时也介绍了打破\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"动画对称性的方法。我们可以发现：在需要切换新旧UI元素的场景，\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"将十分实用。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/48.6747a87a.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[48],{522:function(t,n){t.exports=\"data:image/png;base64,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Ow40Fzl2XaO5Ns9TBXb6hjrZov1lZSAU7atpnpqLrtU+fLYwLfvOsb+0JaFtAPWjJs6yWefZm5vG7MzcZZO61jVysPvhKZGgJg4aX3boZ72s436al9ZHvytToCOZWg/VHLRc0z+xj2t1ZFF9i2vLNRU/nV5oXnfxlZjRtoPs0PXqn5dCCr5Kwzez4uXsWqkuQT9rfN0HndivF3PNSf1Ww1m2tmxirvXUmus+ajl3ZRKaGWb75bqD8FFBpGwQuw66x+hQRz4lHDRHfTWzxlOfuh++7PbYxaV8RwXFj192x+6v6AlYABT9En95Bn7ZAuDLm5l7MoHoErAAiO7an67l6FI3EzABEzABEzCBqBGwAIjaitteEzABEzABE4CABYBvAxMwARMwAROIIAELgAguuk02ARMwARMwAQsA3wMmYAImYAImEEECFgARXHSbbAImYAImYAIWAL4HTMAETMAETCCCBCwAIrjoNtkETMAETMAELAB8D5iACZiACZhABAlYAERw0W2yCZiACZiACVgA+B4wARMwARMwgQgSsACI4KLbZBMwARMwAROwAPA9YAImYAImYAIRJGABEMFFt8kmYAImYAImYAHge8AETMAETMAEIkjAAiCCi26TTcAETMAETMACwPeACZiACZiACUSQgAVABBfdJpuACZiACZiABYDvARMwARMwAROIIAELgAguuk02ARMwARMwAQsA3wMmYAImYAImEEECFgARXHSbbAImYAImYAKxZDLZaAwm0BoCW/c3hnc2N7TmUl9jAiZwhghM610SaipjZ2g0D1NMBCwAimk182xLBt+ftlzMM2V3bwJtI5DA98edy20bNF+dJZAwBxNoLQE9ZOKtvdjXmYAJmIAJFDQB68aCXh5PzgRMwARMwATyQ8ACID9c3asJmIAJmIAJFDQBC4CCXh5PzgRMwARMwATyQ8ACID9c3asJmIAJmIAJFDQBC4CCXh5PzgRMwARMwATyQ8ACID9c3asJmIAJmIAJFDQBC4CCXh5PzgRMwARMwATyQ8ACID9c3asJmIAJmIAJFDQBC4CCXh5PzgRMwARMwATyQ8ACID9c3asJmIAJmIAJFDQBC4CCXh5PzgRMwARMwATyQ8ACID9c3asJmIAJmIAJFDQBC4CCXh5PzgRMwARMwATyQ8ACID9c3asJmIAJmIAJFDQBC4CCXh5PzgRMwARMwATyQ8ACID9c3asJmIAJmIAJFDSB/wN9/AZcme7YQQAAAABJRU5ErkJggg==\"},523:function(t,n){t.exports=\"data:image/png;base64,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+XFQoXmzf25v5Tx4PYrcn7p9/M+Hffvpj6l5+KnHouu9nQQkAN7OdYvqqNdCAER1AupMBAJIQAIggIsa5SkpBRBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSaQMD8/vxzlPtXdW0Zg/NGU3bs98JaNWsMVgWATqGkotbyCrGBPUrNbUwISAGuKNxgXX1patqWlpWBMRrMQgYAQSExMtMTEhIDMRtNYDwLJ69Gp+ny7CPAhk5iY9HYNWqMVAREQARGISEA1ABHx6KAIiIAIiIAIBJOABEAw11WzEgEREAEREIGIBCQAIuLRQREQAREQAREIJgEJgGCuq2YlAiIgAiIgAhEJSABExKODIiACIiACIhBMAhIAwVxXzUoEREAEREAEIhKQAIiIRwdFQAREQAREIJgEJACCua6alQiIgAiIgAhEJCABEBGPDoqACIiACIhAMAlIAARzXTUrERABERABEYhIQAIgIh4dFAEREAEREIFgEpAACOa6alYiIAIiIAIiEJGABEBEPDooAiIgAiIgAsEkIAEQzHXVrERABERABEQgIgEJgIh4dFAEREAEREAEgklAAiCY66pZiYAIiIAIiEBEAv8PGQS+vtZoAOcAAAAASUVORK5CYII=\"},524:function(t,n,a){t.exports=a.p+\"assets/img/9-10.fefb9fd0.png\"},816:function(t,n,a){\"use strict\";a.r(n);var s=a(45),e=Object(s.a)({},(function(){var t=this,n=t.$createElement,s=t._self._c||n;return s(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[s(\"h1\",{attrs:{id:\"_9-7-动画过渡组件\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_9-7-动画过渡组件\"}},[t._v(\"#\")]),t._v(\" 9.7 动画过渡组件\")]),t._v(\" \"),s(\"p\",[t._v(\"为了表述方便，本书约定，将在Widget属性发生变化时会执行过渡动画的组件统称为”动画过渡组件“，而动画过渡组件最明显的一个特征就是它会在内部自管理\"),s(\"code\",[t._v(\"AnimationController\")]),t._v(\"。我们知道，为了方便使用者可以自定义动画的曲线、执行时长、方向等，在前面介绍过的动画封装方法中，通常都需要使用者自己提供一个\"),s(\"code\",[t._v(\"AnimationController\")]),t._v(\"对象来自定义这些属性值。但是，如此一来，使用者就必须得手动管理\"),s(\"code\",[t._v(\"AnimationController\")]),t._v(\"，这又会增加使用的复杂性。因此，如果也能将\"),s(\"code\",[t._v(\"AnimationController\")]),t._v(\"进行封装，则会大大提高动画组件的易用性。\")]),t._v(\" \"),s(\"h2\",{attrs:{id:\"_9-7-1-自定义动画过渡组件\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_9-7-1-自定义动画过渡组件\"}},[t._v(\"#\")]),t._v(\" 9.7.1 自定义动画过渡组件\")]),t._v(\" \"),s(\"p\",[t._v(\"我们要实现一个\"),s(\"code\",[t._v(\"AnimatedDecoratedBox\")]),t._v(\"，它可以在\"),s(\"code\",[t._v(\"decoration\")]),t._v(\"属性发生变化时，从旧状态变成新状态的过程可以执行一个过渡动画。根据前面所学的知识，我们实现了一个\"),s(\"code\",[t._v(\"AnimatedDecoratedBox1\")]),t._v(\"组件：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimatedDecoratedBox1\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedDecoratedBox1\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"curve \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Curves\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"linear\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"reverseDuration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" BoxDecoration decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Widget child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Duration duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Curve curve\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Duration reverseDuration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _AnimatedDecoratedBox1State \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_AnimatedDecoratedBox1State\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_AnimatedDecoratedBox1State\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"AnimatedDecoratedBox1\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"with\")]),t._v(\" SingleTickerProviderStateMixin \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@protected\")]),t._v(\"\\n  AnimationController \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" controller \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _controller\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  AnimationController _controller\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  Animation\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" animation \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _animation\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  Animation\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _animation\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  DecorationTween _tween\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedBuilder\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      animation\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _animation\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      builder\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecoratedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _tween\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"animate\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_animation\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" widget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    _controller \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimationController\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" widget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      reverseDuration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" widget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"reverseDuration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      vsync\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    _tween \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecorationTween\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"begin\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" widget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_updateCurve\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_updateCurve\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"curve \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      _animation \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CurvedAnimation\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"parent\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _controller\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" curve\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" widget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"curve\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\"\\n      _animation \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _controller\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didUpdateWidget\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"AnimatedDecoratedBox1 oldWidget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didUpdateWidget\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"oldWidget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"curve \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" oldWidget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"curve\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_updateCurve\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    _controller\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"duration \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" widget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    _controller\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"reverseDuration \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" widget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"reverseDuration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_tween\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"end \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" _tween\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"begin\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _tween\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"begin \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _tween\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"evaluate\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_animation\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"end \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" widget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      _controller\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"forward\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    _controller\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"下面我们来使用\"),s(\"code\",[t._v(\"AnimatedDecoratedBox1\")]),t._v(\"来实现按钮点击后背景色从蓝色过渡到红色的效果：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"Color _decorationColor \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" duration \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedDecoratedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _decorationColor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    onPressed\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        _decorationColor \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"AnimatedDecoratedBox\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      style\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"点击前效果如图9-8所示，点击后截取了过渡过程的一帧如图9-9所示： \"),s(\"img\",{attrs:{src:a(522),alt:\"img\"}}),s(\"img\",{attrs:{src:a(523),alt:\"img\"}})]),t._v(\" \"),s(\"p\",[t._v(\"点击后，按钮背景色会从蓝色向红色过渡，图9-9是过渡过程中的一帧，有点偏紫色，整个过渡动画结束后背景会变为红色。\")]),t._v(\" \"),s(\"p\",[t._v(\"上面的代码虽然实现了我们期望的功能，但是代码却比较复杂。稍加思考后，我们就可以发现，\"),s(\"code\",[t._v(\"AnimationController\")]),t._v(\"的管理以及Tween更新部分的代码都是可以抽象出来的，如果我们这些通用逻辑封装成基类，那么要实现动画过渡组件只需要继承这些基类，然后定制自身不同的代码（比如动画每一帧的构建方法）即可，这样将会简化代码。\")]),t._v(\" \"),s(\"p\",[t._v(\"为了方便开发者来实现动画过渡组件的封装，Flutter提供了一个\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidget\")]),t._v(\"抽象类，它继承自StatefulWidget，同时提供了一个对应的\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidgetState\")]),t._v(\"类，\"),s(\"code\",[t._v(\"AnimationController\")]),t._v(\"的管理就在\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidgetState\")]),t._v(\"类中。开发者如果要封装动画，只需要分别继承\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidget\")]),t._v(\"和\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidgetState\")]),t._v(\"类即可，下面我们演示一下具体如何实现。\")]),t._v(\" \"),s(\"p\",[t._v(\"我们需要分两步实现：\")]),t._v(\" \"),s(\"ol\",[s(\"li\",[s(\"p\",[t._v(\"继承\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidget\")]),t._v(\"类。\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimatedDecoratedBox\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ImplicitlyAnimatedWidget\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedDecoratedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    Curve curve \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Curves\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"linear\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//动画曲线\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" Duration duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 正向动画执行时长\")]),t._v(\"\\n    Duration reverseDuration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 反向动画执行时长\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          key\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          curve\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" curve\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          reverseDuration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" reverseDuration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" BoxDecoration decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Widget child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _AnimatedDecoratedBoxState \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_AnimatedDecoratedBoxState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"其中\"),s(\"code\",[t._v(\"curve\")]),t._v(\"、\"),s(\"code\",[t._v(\"duration\")]),t._v(\"、\"),s(\"code\",[t._v(\"reverseDuration\")]),t._v(\"三个属性在\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidget\")]),t._v(\"中已定义。 可以看到\"),s(\"code\",[t._v(\"AnimatedDecoratedBox\")]),t._v(\"类和普通继承自\"),s(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"的类没有什么不同。\")])]),t._v(\" \"),s(\"li\",[s(\"p\",[t._v(\"State类继承自\"),s(\"code\",[t._v(\"AnimatedWidgetBaseState\")]),t._v(\"（该类继承自\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidgetState\")]),t._v(\"类）。\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_AnimatedDecoratedBoxState\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimatedWidgetBaseState\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"AnimatedDecoratedBox\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  DecorationTween _decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//定义一个Tween\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecoratedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"evaluate\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"animation\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" widget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"forEachTween\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"visitor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 在需要更新Tween时，基类会调用此方法\")]),t._v(\"\\n    _decoration \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"visitor\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" widget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"value\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecorationTween\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"begin\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" value\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"可以看到我们实现了\"),s(\"code\",[t._v(\"build\")]),t._v(\"和\"),s(\"code\",[t._v(\"forEachTween\")]),t._v(\"两个方法。在动画执行过程中，每一帧都会调用\"),s(\"code\",[t._v(\"build\")]),t._v(\"方法（调用逻辑在\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidgetState\")]),t._v(\"中），所以在\"),s(\"code\",[t._v(\"build\")]),t._v(\"方法中我们需要构建每一帧的\"),s(\"code\",[t._v(\"DecoratedBox\")]),t._v(\"状态，因此得算出每一帧的\"),s(\"code\",[t._v(\"decoration\")]),t._v(\" 状态，这个我们可以通过\"),s(\"code\",[t._v(\"_decoration.evaluate(animation)\")]),t._v(\" 来算出，其中\"),s(\"code\",[t._v(\"animation\")]),t._v(\"是\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidgetState\")]),t._v(\"基类中定义的对象，\"),s(\"code\",[t._v(\"_decoration\")]),t._v(\"是我们自定义的一个\"),s(\"code\",[t._v(\"DecorationTween\")]),t._v(\"类型的对象，那么现在的问题就是它是在什么时候被赋值的呢？要回答这个问题，我们就得搞清楚什么时候需要对\"),s(\"code\",[t._v(\"_decoration\")]),t._v(\"赋值。我们知道\"),s(\"code\",[t._v(\"_decoration\")]),t._v(\"是一个Tween，而Tween的主要职责就是定义动画的起始状态（begin）和终止状态(end)。对于\"),s(\"code\",[t._v(\"AnimatedDecoratedBox\")]),t._v(\"来说，\"),s(\"code\",[t._v(\"decoration\")]),t._v(\"的终止状态就是用户传给它的值，而起始状态是不确定的，有以下两种情况：\")]),t._v(\" \"),s(\"ol\",[s(\"li\",[s(\"code\",[t._v(\"AnimatedDecoratedBox\")]),t._v(\"首次build，此时直接将其\"),s(\"code\",[t._v(\"decoration\")]),t._v(\"值置为起始状态，即\"),s(\"code\",[t._v(\"_decoration\")]),t._v(\"值为\"),s(\"code\",[t._v(\"DecorationTween(begin: decoration)\")]),t._v(\" 。\")]),t._v(\" \"),s(\"li\",[s(\"code\",[t._v(\"AnimatedDecoratedBox\")]),t._v(\"的\"),s(\"code\",[t._v(\"decoration\")]),t._v(\"更新时，则起始状态为\"),s(\"code\",[t._v(\"_decoration.animate(animation)\")]),t._v(\"，即\"),s(\"code\",[t._v(\"_decoration\")]),t._v(\"值为\"),s(\"code\",[t._v(\"DecorationTween(begin: _decoration.animate(animation)，end:decoration)\")]),t._v(\"。\")])])])]),t._v(\" \"),s(\"p\",[t._v(\"现在\"),s(\"code\",[t._v(\"forEachTween\")]),t._v(\"的作用就很明显了，它正是用于来更新Tween的初始值的，在上述两种情况下会被调用，而开发者只需重写此方法，并在此方法中更新Tween的起始状态值即可。而一些更新的逻辑被屏蔽在了\"),s(\"code\",[t._v(\"visitor\")]),t._v(\"回调，我们只需要调用它并给它传递正确的参数即可，\"),s(\"code\",[t._v(\"visitor\")]),t._v(\"方法签名如下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"   Tween \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"visitor\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n     Tween\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" tween\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//当前的tween，第一次调用为null\")]),t._v(\"\\n     \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),t._v(\" targetValue\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 终止状态\")]),t._v(\"\\n     TweenConstructor\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" constructor，\"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//Tween构造器，在上述三种情况下会被调用以更新tween\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"可以看到，通过继承\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidget\")]),t._v(\"和\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidgetState\")]),t._v(\"类可以快速的实现动画过渡组件的封装，这和我们纯手工实现相比，代码简化了很多。\")]),t._v(\" \"),s(\"blockquote\",[s(\"p\",[t._v(\"如果读者还有疑惑，建议查看\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidgetState\")]),t._v(\"的源码并结合本示例代码对比理解。\")])]),t._v(\" \"),s(\"h3\",{attrs:{id:\"动画过渡组件的反向动画\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#动画过渡组件的反向动画\"}},[t._v(\"#\")]),t._v(\" 动画过渡组件的反向动画\")]),t._v(\" \"),s(\"p\",[t._v(\"在使用动画过渡组件，我们只需要在改变一些属性值后重新build组件即可，所以要实现状态反向过渡，只需要将前后状态值互换即可实现，这本来是不需要再浪费笔墨的。但是\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidget\")]),t._v(\"构造函数中却有一个\"),s(\"code\",[t._v(\"reverseDuration\")]),t._v(\"属性用于设置反向动画的执行时长，这貌似在告诉读者\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidget\")]),t._v(\"本身也提供了执行反向动画的接口，于是笔者查看了\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidgetState\")]),t._v(\"源码并未发现有执行反向动画的接口，唯一有用的是它暴露了控制动画的\"),s(\"code\",[t._v(\"controller\")]),t._v(\"。所以如果要让\"),s(\"code\",[t._v(\"reverseDuration\")]),t._v(\"生效，我们只能先获取\"),s(\"code\",[t._v(\"controller\")]),t._v(\"，然后再通过\"),s(\"code\",[t._v(\"controller.reverse()\")]),t._v(\"来启动反向动画，比如我们在上面示例的基础上实现一个循环的点击背景颜色变换效果，要求从蓝色变为红色时动画执行时间为400ms，从红变蓝为2s，如果要使\"),s(\"code\",[t._v(\"reverseDuration\")]),t._v(\"生效，我们需要这么做：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedDecoratedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" milliseconds\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"400\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _decorationColor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  reverseDuration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      onPressed\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_decorationColor \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          ImplicitlyAnimatedWidgetState _state \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"\\n              context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"findAncestorStateOfType\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ImplicitlyAnimatedWidgetState\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n           \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 通过controller来启动反向动画\")]),t._v(\"\\n          _state\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"controller\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"reverse\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 经验证必须调用setState来触发rebuild，否则状态同步会有问题\")]),t._v(\"\\n            \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              _decorationColor \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            _decorationColor \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"AnimatedDecoratedBox toggle\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        style\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"上面的代码实际上是非常糟糕且没必要的，它需要我们了解\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidgetState\")]),t._v(\"内部实现，并且要手动去启动反向动画。我们完全可以通过如下代码实现相同的效果：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedDecoratedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      milliseconds\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _decorationColor \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"400\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2000\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _decorationColor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      onPressed\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          _decorationColor \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _decorationColor \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\\n              \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\\n              \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"AnimatedDecoratedBox toggle\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        style\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"这样的代码是不是优雅的多！那么现在问题来了，为什么\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidgetState\")]),t._v(\"要提供一个\"),s(\"code\",[t._v(\"reverseDuration\")]),t._v(\"参数呢？笔者仔细研究了\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidgetState\")]),t._v(\"的实现，发现唯一的解释就是该参数并非是给\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidgetState\")]),t._v(\"用的，而是给子类用的！原因正如我们前面说的，要使\"),s(\"code\",[t._v(\"reverseDuration\")]),t._v(\" 有用就必须得获取\"),s(\"code\",[t._v(\"controller\")]),t._v(\" 属性来手动启动反向动画，\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidgetState\")]),t._v(\"中的\"),s(\"code\",[t._v(\"controller\")]),t._v(\" 属性是一个保护属性，定义如下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@protected\")]),t._v(\"\\n  AnimationController \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" controller \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _controller\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"而保护属性原则上只应该在子类中使用，而不应该像上面示例代码一样在外部使用。综上，我们可以得出两条结论：\")]),t._v(\" \"),s(\"ol\",[s(\"li\",[s(\"p\",[t._v(\"使用动画过渡组件时如果需要执行反向动画的场景，应尽量使用状态互换的方法，而不应该通过获取\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidgetState\")]),t._v(\"中\"),s(\"code\",[t._v(\"controller\")]),t._v(\"的方式。\")])]),t._v(\" \"),s(\"li\",[s(\"p\",[t._v(\"如果我们自定义的动画过渡组件用不到\"),s(\"code\",[t._v(\"reverseDuration\")]),t._v(\" ，那么最好就不要暴露此参数，比如我们上面自定义的\"),s(\"code\",[t._v(\"AnimatedDecoratedBox\")]),t._v(\"定义中就可以去除\"),s(\"code\",[t._v(\"reverseDuration\")]),t._v(\" 可选参数，如：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimatedDecoratedBox\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ImplicitlyAnimatedWidget\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedDecoratedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    Curve curve \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Curves\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"linear\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" Duration duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          key\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          curve\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" curve\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])])])]),t._v(\" \"),s(\"h2\",{attrs:{id:\"_9-7-2-flutter预置的动画过渡组件\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_9-7-2-flutter预置的动画过渡组件\"}},[t._v(\"#\")]),t._v(\" 9.7.2 Flutter预置的动画过渡组件\")]),t._v(\" \"),s(\"p\",[t._v(\"Flutter SDK中也预置了很多动画过渡组件，实现方式和大都和\"),s(\"code\",[t._v(\"AnimatedDecoratedBox\")]),t._v(\"差不多，如表9-1所示：\")]),t._v(\" \"),s(\"table\",[s(\"thead\",[s(\"tr\",[s(\"th\",[t._v(\"组件名\")]),t._v(\" \"),s(\"th\",[t._v(\"功能\")])])]),t._v(\" \"),s(\"tbody\",[s(\"tr\",[s(\"td\",[t._v(\"AnimatedPadding\")]),t._v(\" \"),s(\"td\",[t._v(\"在padding发生变化时会执行过渡动画到新状态\")])]),t._v(\" \"),s(\"tr\",[s(\"td\",[t._v(\"AnimatedPositioned\")]),t._v(\" \"),s(\"td\",[t._v(\"配合Stack一起使用，当定位状态发生变化时会执行过渡动画到新的状态。\")])]),t._v(\" \"),s(\"tr\",[s(\"td\",[t._v(\"AnimatedOpacity\")]),t._v(\" \"),s(\"td\",[t._v(\"在透明度opacity发生变化时执行过渡动画到新状态\")])]),t._v(\" \"),s(\"tr\",[s(\"td\",[t._v(\"AnimatedAlign\")]),t._v(\" \"),s(\"td\",[t._v(\"当\"),s(\"code\",[t._v(\"alignment\")]),t._v(\"发生变化时会执行过渡动画到新的状态。\")])]),t._v(\" \"),s(\"tr\",[s(\"td\",[t._v(\"AnimatedContainer\")]),t._v(\" \"),s(\"td\",[t._v(\"当Container属性发生变化时会执行过渡动画到新的状态。\")])]),t._v(\" \"),s(\"tr\",[s(\"td\",[t._v(\"AnimatedDefaultTextStyle\")]),t._v(\" \"),s(\"td\",[t._v(\"当字体样式发生变化时，子组件中继承了该样式的文本组件会动态过渡到新样式。\")])])])]),t._v(\" \"),s(\"center\",[t._v(\"表9-1：Flutter预置的动画过渡组件\")]),t._v(\"\\n下面我们通过一个示例来感受一下这些预置的动画过渡组件效果：\\n\"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimatedWidgetsTest\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _AnimatedWidgetsTestState \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_AnimatedWidgetsTestState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_AnimatedWidgetsTestState\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"AnimatedWidgetsTest\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  double _padding \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"10\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" _align \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Alignment\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"topRight\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  double _height \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  double _left \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  Color _color \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  TextStyle _style \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"black\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  Color _decorationColor \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" duration \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"5\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SingleChildScrollView\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        children\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            onPressed\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                _padding \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedPadding\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              padding\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_padding\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"AnimatedPadding\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            height\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Stack\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              children\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedPositioned\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  left\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _left\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    onPressed\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                      \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                        _left \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"AnimatedPositioned\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n              \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            height\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedAlign\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              alignment\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _align\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                onPressed\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                  \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                    _align \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Alignment\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"AnimatedAlign\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedContainer\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            height\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _height\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              onPressed\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                  _height \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"150\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                  _color \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"AnimatedContainer\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                style\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedDefaultTextStyle\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GestureDetector\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"hello world\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              onTap\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                  _style \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    decorationStyle\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" TextDecorationStyle\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"solid\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    decorationColor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            style\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _style\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedDecoratedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _decorationColor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              onPressed\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                  _decorationColor \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"AnimatedDecoratedBox\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                style\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"map\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            padding\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"symmetric\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"vertical\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toList\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"运行后效果如图9-10所示：\")]),t._v(\" \"),s(\"p\",[s(\"img\",{attrs:{src:a(524),alt:\"图9-10\"}})]),t._v(\" \"),s(\"p\",[t._v(\"读者可以点击一下相应组件来查看一下实际的运行效果。\")])],1)}),[],!1,null,null,null);n.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/49.e636869b.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[49],{555:function(t,e,n){t.exports=n.p+\"assets/img/14-0.bd5c2d9d.png\"},556:function(t,e,n){t.exports=n.p+\"assets/img/14-1.075fa468.png\"},557:function(t,e,n){t.exports=n.p+\"assets/img/14-2.7141498f.png\"},862:function(t,e,n){\"use strict\";n.r(e);var s=n(45),a=Object(s.a)({},(function(){var t=this,e=t.$createElement,s=t._self._c||e;return s(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[s(\"h1\",{attrs:{id:\"_14-2-element与buildcontext\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_14-2-element与buildcontext\"}},[t._v(\"#\")]),t._v(\" 14.2 Element与BuildContext\")]),t._v(\" \"),s(\"h2\",{attrs:{id:\"_14-2-1-element\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_14-2-1-element\"}},[t._v(\"#\")]),t._v(\" 14.2.1 Element\")]),t._v(\" \"),s(\"p\",[t._v(\"在“Widget简介”一节，我们介绍了Widget和Element的关系，我们知道最终的UI树其实是由一个个独立的Element节点构成。我们也说过组件最终的Layout、渲染都是通过\"),s(\"code\",[t._v(\"RenderObject\")]),t._v(\"来完成的，从创建到渲染的大体流程是：根据Widget生成Element，然后创建相应的\"),s(\"code\",[t._v(\"RenderObject\")]),t._v(\"并关联到\"),s(\"code\",[t._v(\"Element.renderObject\")]),t._v(\"属性上，最后再通过\"),s(\"code\",[t._v(\"RenderObject\")]),t._v(\"来完成布局排列和绘制。\")]),t._v(\" \"),s(\"p\",[t._v(\"Element就是Widget在UI树具体位置的一个实例化对象，大多数Element只有唯一的\"),s(\"code\",[t._v(\"renderObject\")]),t._v(\"，但还有一些Element会有多个子节点，如继承自\"),s(\"code\",[t._v(\"RenderObjectElement\")]),t._v(\"的一些类，比如\"),s(\"code\",[t._v(\"MultiChildRenderObjectElement\")]),t._v(\"。最终所有Element的RenderObject构成一棵树，我们称之为”Render Tree“即”渲染树“。总结一下，我们可以认为Flutter的UI系统包含三棵树：Widget树、Element树、渲染树。他们的依赖关系是：Element树根据Widget树生成，而渲染树又依赖于Element树，如图14-0所示。\")]),t._v(\" \"),s(\"p\",[s(\"img\",{attrs:{src:n(555),alt:\"图14-0\"}})]),t._v(\" \"),s(\"p\",[t._v(\"现在我们重点看一下Element，Element的生命周期如下：\")]),t._v(\" \"),s(\"ol\",[s(\"li\",[t._v(\"Framework 调用\"),s(\"code\",[t._v(\"Widget.createElement\")]),t._v(\" 创建一个Element实例，记为\"),s(\"code\",[t._v(\"element\")])]),t._v(\" \"),s(\"li\",[t._v(\"Framework 调用 \"),s(\"code\",[t._v(\"element.mount(parentElement,newSlot)\")]),t._v(\" ，mount方法中首先调用\"),s(\"code\",[t._v(\"element\")]),t._v(\"所对应Widget的\"),s(\"code\",[t._v(\"createRenderObject\")]),t._v(\"方法创建与\"),s(\"code\",[t._v(\"element\")]),t._v(\"相关联的RenderObject对象，然后调用\"),s(\"code\",[t._v(\"element.attachRenderObject\")]),t._v(\"方法将\"),s(\"code\",[t._v(\"element.renderObject\")]),t._v(\"添加到渲染树中插槽指定的位置（这一步不是必须的，一般发生在Element树结构发生变化时才需要重新attach）。插入到渲染树后的\"),s(\"code\",[t._v(\"element\")]),t._v(\"就处于“active”状态，处于“active”状态后就可以显示在屏幕上了（可以隐藏）。\")]),t._v(\" \"),s(\"li\",[t._v(\"当有父Widget的配置数据改变时，同时其\"),s(\"code\",[t._v(\"State.build\")]),t._v(\"返回的Widget结构与之前不同，此时就需要重新构建对应的Element树。为了进行Element复用，在Element重新构建前会先尝试是否可以复用旧树上相同位置的element，element节点在更新前都会调用其对应Widget的\"),s(\"code\",[t._v(\"canUpdate\")]),t._v(\"方法，如果返回\"),s(\"code\",[t._v(\"true\")]),t._v(\"，则复用旧Element，旧的Element会使用新Widget配置数据更新，反之则会创建一个新的Element。\"),s(\"code\",[t._v(\"Widget.canUpdate\")]),t._v(\"主要是判断\"),s(\"code\",[t._v(\"newWidget\")]),t._v(\"与\"),s(\"code\",[t._v(\"oldWidget\")]),t._v(\"的\"),s(\"code\",[t._v(\"runtimeType\")]),t._v(\"和\"),s(\"code\",[t._v(\"key\")]),t._v(\"是否同时相等，如果同时相等就返回\"),s(\"code\",[t._v(\"true\")]),t._v(\"，否则就会返回\"),s(\"code\",[t._v(\"false\")]),t._v(\"。根据这个原理，当我们需要强制更新一个Widget时，可以通过指定不同的Key来避免复用。\")]),t._v(\" \"),s(\"li\",[t._v(\"当有祖先Element决定要移除\"),s(\"code\",[t._v(\"element\")]),t._v(\" 时（如Widget树结构发生了变化，导致\"),s(\"code\",[t._v(\"element\")]),t._v(\"对应的Widget被移除），这时该祖先Element就会调用\"),s(\"code\",[t._v(\"deactivateChild\")]),t._v(\" 方法来移除它，移除后\"),s(\"code\",[t._v(\"element.renderObject\")]),t._v(\"也会被从渲染树中移除，然后Framework会调用\"),s(\"code\",[t._v(\"element.deactivate\")]),t._v(\" 方法，这时\"),s(\"code\",[t._v(\"element\")]),t._v(\"状态变为“inactive”状态。\")]),t._v(\" \"),s(\"li\",[t._v(\"“inactive”态的element将不会再显示到屏幕。为了避免在一次动画执行过程中反复创建、移除某个特定element，“inactive”态的element在当前动画最后一帧结束前都会保留，如果在动画执行结束后它还未能重新变成“active”状态，Framework就会调用其\"),s(\"code\",[t._v(\"unmount\")]),t._v(\"方法将其彻底移除，这时element的状态为\"),s(\"code\",[t._v(\"defunct\")]),t._v(\",它将永远不会再被插入到树中。\")]),t._v(\" \"),s(\"li\",[t._v(\"如果\"),s(\"code\",[t._v(\"element\")]),t._v(\"要重新插入到Element树的其它位置，如\"),s(\"code\",[t._v(\"element\")]),t._v(\"或\"),s(\"code\",[t._v(\"element\")]),t._v(\"的祖先拥有一个GlobalKey（用于全局复用元素），那么Framework会先将element从现有位置移除，然后再调用其\"),s(\"code\",[t._v(\"activate\")]),t._v(\"方法，并将其\"),s(\"code\",[t._v(\"renderObject\")]),t._v(\"重新attach到渲染树。\")])]),t._v(\" \"),s(\"p\",[t._v(\"看完Element的生命周期，可能有些读者会有疑问，开发者会直接操作Element树吗？其实对于开发者来说，大多数情况下只需要关注Widget树就行，Flutter框架已经将对Widget树的操作映射到了Element树上，这可以极大的降低复杂度，提高开发效率。但是了解Element对理解整个Flutter UI框架是至关重要的，Flutter正是通过Element这个纽带将Widget和RenderObject关联起来，了解Element层不仅会帮助读者对Flutter UI框架有个清晰的认识，而且也会提高自己的抽象能力和设计能力。另外在有些时候，我们必须得直接使用Element对象来完成一些操作，比如获取主题Theme数据，具体细节将在下文介绍。\")]),t._v(\" \"),s(\"h2\",{attrs:{id:\"_14-2-2-buildcontext\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_14-2-2-buildcontext\"}},[t._v(\"#\")]),t._v(\" 14.2.2 BuildContext\")]),t._v(\" \"),s(\"p\",[t._v(\"我们已经知道，\"),s(\"code\",[t._v(\"StatelessWidget\")]),t._v(\"和\"),s(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"的\"),s(\"code\",[t._v(\"build\")]),t._v(\"方法都会传一个\"),s(\"code\",[t._v(\"BuildContext\")]),t._v(\"对象：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"Widget \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"我们也知道，在很多时候我们都需要使用这个\"),s(\"code\",[t._v(\"context\")]),t._v(\" 做一些事，比如：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"Theme\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//获取主题\")]),t._v(\"\\nNavigator\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"push\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" route\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//入栈新路由\")]),t._v(\"\\nLocalizations\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" type\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//获取Local\")]),t._v(\"\\ncontext\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"size \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//获取上下文大小\")]),t._v(\"\\ncontext\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"findRenderObject\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//查找当前或最近的一个祖先RenderObject\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"那么\"),s(\"code\",[t._v(\"BuildContext\")]),t._v(\"到底是什么呢，查看其定义，发现其是一个抽象接口类：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"abstract\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"BuildContext\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"那这个\"),s(\"code\",[t._v(\"context\")]),t._v(\"对象对应的实现类到底是谁呢？我们顺藤摸瓜，发现\"),s(\"code\",[t._v(\"build\")]),t._v(\"调用是发生在\"),s(\"code\",[t._v(\"StatelessWidget\")]),t._v(\"和\"),s(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"对应的\"),s(\"code\",[t._v(\"StatelessElement\")]),t._v(\"和\"),s(\"code\",[t._v(\"StatefulElement\")]),t._v(\"的\"),s(\"code\",[t._v(\"build\")]),t._v(\"方法中，以\"),s(\"code\",[t._v(\"StatelessElement\")]),t._v(\"为例：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessElement\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ComponentElement\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" widget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"发现\"),s(\"code\",[t._v(\"build\")]),t._v(\"传递的参数是\"),s(\"code\",[t._v(\"this\")]),t._v(\"，很明显！这个\"),s(\"code\",[t._v(\"BuildContext\")]),t._v(\"就是\"),s(\"code\",[t._v(\"StatelessElement\")]),t._v(\"。同样，我们同样发现\"),s(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"的\"),s(\"code\",[t._v(\"context\")]),t._v(\"是\"),s(\"code\",[t._v(\"StatefulElement\")]),t._v(\"。但\"),s(\"code\",[t._v(\"StatelessElement\")]),t._v(\"和\"),s(\"code\",[t._v(\"StatefulElement\")]),t._v(\"本身并没有实现\"),s(\"code\",[t._v(\"BuildContext\")]),t._v(\"接口，继续跟踪代码，发现它们间接继承自\"),s(\"code\",[t._v(\"Element\")]),t._v(\"类，然后查看\"),s(\"code\",[t._v(\"Element\")]),t._v(\"类定义，发现\"),s(\"code\",[t._v(\"Element\")]),t._v(\"类果然实现了\"),s(\"code\",[t._v(\"BuildContext\")]),t._v(\"接口:\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Element\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"DiagnosticableTree\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"implements\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"BuildContext\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"至此真相大白，\"),s(\"code\",[t._v(\"BuildContext\")]),t._v(\"就是widget对应的\"),s(\"code\",[t._v(\"Element\")]),t._v(\"，所以我们可以通过\"),s(\"code\",[t._v(\"context\")]),t._v(\"在\"),s(\"code\",[t._v(\"StatelessWidget\")]),t._v(\"和\"),s(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"的\"),s(\"code\",[t._v(\"build\")]),t._v(\"方法中直接访问\"),s(\"code\",[t._v(\"Element\")]),t._v(\"对象。我们获取主题数据的代码\"),s(\"code\",[t._v(\"Theme.of(context)\")]),t._v(\"内部正是调用了Element的\"),s(\"code\",[t._v(\"dependOnInheritedWidgetOfExactType()\")]),t._v(\"方法。\")]),t._v(\" \"),s(\"blockquote\",[s(\"p\",[t._v(\"思考题：为什么build方法的参数不定义成Element对象，而要定义成BuildContext ?\")])]),t._v(\" \"),s(\"h3\",{attrs:{id:\"进阶\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#进阶\"}},[t._v(\"#\")]),t._v(\" 进阶\")]),t._v(\" \"),s(\"p\",[t._v(\"我们可以看到Element是Flutter UI框架内部连接widget和\"),s(\"code\",[t._v(\"RenderObject\")]),t._v(\"的纽带，大多数时候开发者只需要关注widget层即可，但是widget层有时候并不能完全屏蔽\"),s(\"code\",[t._v(\"Element\")]),t._v(\"细节，所以Framework在\"),s(\"code\",[t._v(\"StatelessWidget\")]),t._v(\"和\"),s(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"中通过\"),s(\"code\",[t._v(\"build\")]),t._v(\"方法参数又将\"),s(\"code\",[t._v(\"Element\")]),t._v(\"对象也传递给了开发者，这样一来，开发者便可以在需要时直接操作\"),s(\"code\",[t._v(\"Element\")]),t._v(\"对象。那么现在笔者提两个问题，请读者先自己思考一下：\")]),t._v(\" \"),s(\"ol\",[s(\"li\",[t._v(\"如果没有widget层，单靠\"),s(\"code\",[t._v(\"Element\")]),t._v(\"层是否可以搭建起一个可用的UI框架？如果可以应该是什么样子？\")]),t._v(\" \"),s(\"li\",[t._v(\"Flutter UI框架能不做成响应式吗？\")])]),t._v(\" \"),s(\"p\",[t._v(\"对于问题1，答案当然是肯定的，因为我们之前说过widget树只是\"),s(\"code\",[t._v(\"Element\")]),t._v(\"树的映射，我们完全可以直接通过Element来搭建一个UI框架。下面举一个例子：\")]),t._v(\" \"),s(\"p\",[t._v(\"我们通过纯粹的Element来模拟一个\"),s(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"的功能，假设有一个页面，该页面有一个按钮，按钮的文本是一个9位数，点击一次按钮，则对9个数随机排一次序，代码如下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"HomeView\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ComponentElement\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"HomeView\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Widget widget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  String text \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"123456789\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Color primary\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"Theme\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"primaryColor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//1\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GestureDetector\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"text\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" style\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" primary\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          onPressed\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" t \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" text\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"split\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"shuffle\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            text \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" t\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"join\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"markNeedsBuild\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//点击后将该Element标记为dirty，Element将会rebuild\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"ul\",[s(\"li\",[s(\"p\",[t._v(\"上面\"),s(\"code\",[t._v(\"build\")]),t._v(\"方法不接收参数，这一点和在\"),s(\"code\",[t._v(\"StatelessWidget\")]),t._v(\"和\"),s(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"中\"),s(\"code\",[t._v(\"build(BuildContext)\")]),t._v(\"方法不同。代码中需要用到\"),s(\"code\",[t._v(\"BuildContext\")]),t._v(\"的地方直接用\"),s(\"code\",[t._v(\"this\")]),t._v(\"代替即可，如代码注释1处\"),s(\"code\",[t._v(\"Theme.of(this)\")]),t._v(\"参数直接传\"),s(\"code\",[t._v(\"this\")]),t._v(\"即可，因为当前对象本身就是\"),s(\"code\",[t._v(\"Element\")]),t._v(\"实例。\")])]),t._v(\" \"),s(\"li\",[s(\"p\",[t._v(\"当\"),s(\"code\",[t._v(\"text\")]),t._v(\"发生改变时，我们调用\"),s(\"code\",[t._v(\"markNeedsBuild()\")]),t._v(\"方法将当前Element标记为dirty即可，标记为dirty的Element会在下一帧中重建。实际上，\"),s(\"code\",[t._v(\"State.setState()\")]),t._v(\"在内部也是调用的\"),s(\"code\",[t._v(\"markNeedsBuild()\")]),t._v(\"方法。\")])]),t._v(\" \"),s(\"li\",[s(\"p\",[t._v(\"上面代码中build方法返回的仍然是一个widget，这是由于Flutter框架中已经有了widget这一层，并且组件库都已经是以widget的形式提供了，如果在Flutter框架中所有组件都像示例的\"),s(\"code\",[t._v(\"HomeView\")]),t._v(\"一样以\"),s(\"code\",[t._v(\"Element\")]),t._v(\"形式提供，那么就可以用纯\"),s(\"code\",[t._v(\"Element\")]),t._v(\"来构建UI了\"),s(\"code\",[t._v(\"HomeView\")]),t._v(\"的build方法返回值类型就可以是\"),s(\"code\",[t._v(\"Element\")]),t._v(\"了。\")])])]),t._v(\" \"),s(\"p\",[t._v(\"如果我们需要将上面代码在现有Flutter框架中跑起来，那么还是得提供一个“适配器”widget将\"),s(\"code\",[t._v(\"HomeView\")]),t._v(\"结合到现有框架中，下面\"),s(\"code\",[t._v(\"CustomHome\")]),t._v(\"就相当于“适配器”：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CustomHome\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Widget\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Element \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createElement\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"HomeView\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"现在就可以将\"),s(\"code\",[t._v(\"CustomHome\")]),t._v(\"添加到widget树了，我们在一个新路由页创建它，最终效果如下如图14-1和14-2（点击后）所示：\")]),t._v(\" \"),s(\"p\",[s(\"img\",{attrs:{src:n(556),alt:\"图14-1\"}}),t._v(\" \"),s(\"img\",{attrs:{src:n(557),alt:\"图14-2\"}})]),t._v(\" \"),s(\"p\",[t._v(\"点击按钮则按钮文本会随机排序。\")]),t._v(\" \"),s(\"p\",[t._v(\"对于问题2，答案当然也是肯定的，Flutter engine提供的dart API是原始且独立的，这个与操作系统提供的API类似，上层UI框架设计成什么样完全取决于设计者，完全可以将UI框架设计成Android风格或iOS风格，但这些事Google不会再去做，我们也没必要再去搞这一套，这是因为响应式的思想本身是很棒的，之所以提出这个问题，是因为笔者认为做与不做是一回事，但知道能不能做是另一回事，这能反映出我们对知识的理解程度。\")]),t._v(\" \"),s(\"h3\",{attrs:{id:\"总结\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),s(\"p\",[t._v(\"本节详细的介绍了\"),s(\"code\",[t._v(\"Element\")]),t._v(\"的生命周期，以及它Widget、BuildContext的关系，也介绍了Element在Flutter UI系统中的角色和作用，我们将在下一节介绍Flutter UI系统中另一个重要的角色RenderObject。\")])])}),[],!1,null,null,null);e.default=a.exports}}]);"
  },
  {
    "path": "docs/assets/js/5.0941abdb.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[5],{361:function(t,e,r){t.exports=r.p+\"assets/img/1-2.c3960e42.png\"},362:function(t,e,r){t.exports=r.p+\"assets/img/1-3.656e852b.png\"},363:function(t,e,r){t.exports=r.p+\"assets/img/1-4.801e91b2.png\"},364:function(t,e,r){t.exports=r.p+\"assets/img/1-5.cc75b912.png\"},365:function(t,e,r){t.exports=r.p+\"assets/img/1-6.739453e7.png\"},366:function(t,e,r){t.exports=r.p+\"assets/img/1-7.4b555c37.png\"},367:function(t,e){t.exports=\"data:image/png;base64,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\"},368:function(t,e,r){t.exports=r.p+\"assets/img/1-9.69ea9a5c.png\"},369:function(t,e,r){t.exports=r.p+\"assets/img/1-10.a2561ece.png\"},723:function(t,e,r){\"use strict\";r.r(e);var a=r(45),v=Object(a.a)({},(function(){var t=this,e=t.$createElement,a=t._self._c||e;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_1-3-搭建flutter开发环境\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-3-搭建flutter开发环境\"}},[t._v(\"#\")]),t._v(\" 1.3 搭建Flutter开发环境\")]),t._v(\" \"),a(\"p\",[t._v(\"工欲善其事必先利其器，本节首先会分别介绍一下在Windows和macOS下Flutter SDK的安装，然后再介绍一下配IDE和模拟器的使用。\")]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_1-3-1-安装flutter\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-3-1-安装flutter\"}},[t._v(\"#\")]),t._v(\" 1.3.1 安装Flutter\")]),t._v(\" \"),a(\"p\",[t._v(\"由于Flutter会同时构建Android和IOS两个平台的发布包，所以Flutter同时依赖Android SDK和iOS SDK，在安装Flutter时也需要安装相应平台的构建工具和SDK。下面我们分别介绍一下Windows和macOS下的环境搭建。\")]),t._v(\" \"),a(\"blockquote\",[a(\"p\",[t._v(\"注意：本节介绍的安装方式随着Flutter的升级可能会发生变化，如果下面介绍的内容在您安装Flutter时已经失效，请访问Flutter官网，按照官网最新的安装教程安装。\")])]),t._v(\" \"),a(\"h3\",{attrs:{id:\"使用镜像\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#使用镜像\"}},[t._v(\"#\")]),t._v(\" 使用镜像\")]),t._v(\" \"),a(\"p\",[t._v(\"由于在国内访问Flutter有时可能会受到限制，Flutter官方为中国开发者搭建了临时镜像，大家可以将如下环境变量加入到用户环境变量中：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language- extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[a(\"code\",[t._v(\"export PUB_HOSTED_URL=https://pub.flutter-io.cn\\nexport FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn\\n\")])])]),a(\"p\",[a(\"strong\",[t._v(\"注意：\")]),t._v(\" 此镜像为临时镜像，并不能保证一直可用，读者可以参考https://flutter.io/community/china 以获得有关镜像服务器的最新动态。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"在windows上搭建flutter开发环境\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#在windows上搭建flutter开发环境\"}},[t._v(\"#\")]),t._v(\" 在Windows上搭建Flutter开发环境\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"系统要求\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#系统要求\"}},[t._v(\"#\")]),t._v(\" 系统要求\")]),t._v(\" \"),a(\"p\",[t._v(\"要安装并运行Flutter，您的开发环境必须满足以下最低要求:\")]),t._v(\" \"),a(\"ul\",[a(\"li\",[a(\"p\",[t._v(\"操作系统: Windows 7 或更高版本 (64-bit)\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"磁盘空间: 400 MB (不包括Android Studio的磁盘空间).\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"工具: Flutter 依赖下面这些命令行工具.\")]),t._v(\" \"),a(\"ul\",[a(\"li\",[a(\"p\",[a(\"a\",{attrs:{href:\"https://docs.microsoft.com/en-us/powershell/scripting/setup/installing-windows-powershell#upgrading-existing-windows-powershell\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"PowerShell 5.0\"),a(\"OutboundLink\")],1),t._v(\" 或更新的版本\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[a(\"a\",{attrs:{href:\"https://git-scm.com/download/win\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Git for Windows\"),a(\"OutboundLink\")],1),t._v(\"  (Git命令行工具)；\")])])]),t._v(\" \"),a(\"p\",[t._v(\"如果已安装Git for Windows，请确保可以在命令提示符或PowerShell中运行 git 命令\")])])]),t._v(\" \"),a(\"h4\",{attrs:{id:\"获取flutter-sdk\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#获取flutter-sdk\"}},[t._v(\"#\")]),t._v(\" 获取Flutter SDK\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[a(\"p\",[t._v(\"去flutter官网下载其最新可用的安装包，下载地址：https://flutter.dev/docs/development/tools/sdk/releases ，打开后如图1-2所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:r(361),alt:\"图1-2\"}})]),t._v(\" \"),a(\"p\",[t._v(\"注意，Flutter的渠道版本会不停变动，请以Flutter官网为准。另外，在中国大陆地区，要想正常获取安装包列表或下载安装包，可能需要翻墙，读者也可以去Flutter github项目下去下载安装包，地址：https://github.com/flutter/flutter/releases 。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"将安装包zip解压到你想安装Flutter SDK的路径（如：\"),a(\"code\",[t._v(\"C:\\\\src\\\\flutter\")]),t._v(\"；注意，\"),a(\"strong\",[t._v(\"不要\")]),t._v(\"将flutter安装到需要一些高权限的路径如\"),a(\"code\",[t._v(\"C:\\\\Program Files\\\\\")]),t._v(\"）。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"在Flutter安装目录的\"),a(\"code\",[t._v(\"flutter\")]),t._v(\"文件下找到\"),a(\"code\",[t._v(\"flutter_console.bat\")]),t._v(\"，双击运行并启动\"),a(\"strong\",[t._v(\"flutter命令行\")]),t._v(\"，接下来，你就可以在Flutter命令行运行flutter命令了。\")])])]),t._v(\" \"),a(\"h5\",{attrs:{id:\"更新环境变量\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#更新环境变量\"}},[t._v(\"#\")]),t._v(\" 更新环境变量\")]),t._v(\" \"),a(\"p\",[t._v(\"如果你想在Windows系统自带命令行运行flutter命令，需要添加以下环境变量到用户PATH：\")]),t._v(\" \"),a(\"ul\",[a(\"li\",[t._v(\"转到 “控制面板>用户帐户>用户帐户>更改我的环境变量”\")]),t._v(\" \"),a(\"li\",[t._v(\"在“用户变量”下检查是否有名为“Path”的条目:\\n\"),a(\"ul\",[a(\"li\",[t._v(\"如果该条目存在， 追加 flutter\\\\bin的全路径，使用 ; 作为分隔符.\")]),t._v(\" \"),a(\"li\",[t._v(\"如果该条目不存在，创建一个新用户变量 Path ，然后将 \"),a(\"code\",[t._v(\"flutter\\\\bin\")]),t._v(\" 的全路径作为它的值.\")])])])]),t._v(\" \"),a(\"p\",[t._v(\"重启Windows以应用此更改.\")]),t._v(\" \"),a(\"h5\",{attrs:{id:\"运行-flutter-doctor命令\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#运行-flutter-doctor命令\"}},[t._v(\"#\")]),t._v(\" 运行 flutter doctor命令\")]),t._v(\" \"),a(\"p\",[t._v(\"在Flutter命令行运行如下命令来查看是否还需要安装其它依赖，如果需要，安装它们：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-shell extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[a(\"code\",[t._v(\"flutter doctor\\n\")])])]),a(\"p\",[t._v(\"该命令检查你的环境并在命令行窗口中显示报告。Dart SDK已经在打包在Flutter SDK里了，没有必要单独安装Dart。 仔细检查命令行输出以获取可能需要安装的其他软件或进一步需要执行的任务。\")]),t._v(\" \"),a(\"p\",[t._v(\"例如：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language- extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[a(\"code\",[t._v(\"[-] Android toolchain - develop for Android devices\\n    • Android SDK at D:\\\\Android\\\\sdk\\n    ✗ Android SDK is missing command line tools; download from https://goo.gl/XxQghQ\\n    • Try re-installing or updating your Android SDK,\\n      visit https://flutter.io/setup/#android-setup for detailed instructions.\\n\")])])]),a(\"p\",[t._v(\"第一次运行flutter命令（如\"),a(\"code\",[t._v(\"flutter doctor\")]),t._v(\"）时，它会下载它自己的依赖项并自行编译。以后再运行就会快得多。缺失的依赖需要安装一下，安装完成后再运行\"),a(\"code\",[t._v(\"flutter doctor\")]),t._v(\"命令来验证是否安装成功。\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"android设置\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#android设置\"}},[t._v(\"#\")]),t._v(\" Android设置\")]),t._v(\" \"),a(\"p\",[t._v(\"Flutter依赖于Android Studio的全量安装。Android Studio不仅可以管理Android 平台依赖、SDK版本等，而且它也是Flutter开发推荐的IDE之一（当然，你也可以使用其它编辑器或IDE，我们将会在后面讨论）。\")]),t._v(\" \"),a(\"h5\",{attrs:{id:\"安装android-studio\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#安装android-studio\"}},[t._v(\"#\")]),t._v(\" 安装Android Studio\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"下载并安装 Android Studio，下载地址：https://developer.android.com/studio/index.html 。\")]),t._v(\" \"),a(\"li\",[t._v(\"启动Android Studio，然后执行“Android Studio安装向导”。这将安装最新的Android SDK、Android SDK平台工具和Android SDK构建工具，这些是用Flutter进行Android开发所需要的。\")])]),t._v(\" \"),a(\"h4\",{attrs:{id:\"安装遇到问题\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#安装遇到问题\"}},[t._v(\"#\")]),t._v(\" 安装遇到问题？\")]),t._v(\" \"),a(\"p\",[t._v(\"如果在安装过程中遇到问题，可以先去flutter官网查看一下安装方式是否发生变化，或者在网上搜索一下解决方案。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"在macos上搭建flutter开发环境\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#在macos上搭建flutter开发环境\"}},[t._v(\"#\")]),t._v(\" 在macOS上搭建Flutter开发环境\")]),t._v(\" \"),a(\"p\",[t._v(\"在masOS下可以同时进行Android和iOS设备的测试。\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"系统要求-2\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#系统要求-2\"}},[t._v(\"#\")]),t._v(\" 系统要求\")]),t._v(\" \"),a(\"p\",[t._v(\"要安装并运行Flutter，您的开发环境必须满足以下最低要求:\")]),t._v(\" \"),a(\"ul\",[a(\"li\",[t._v(\"操作系统: macOS (64-bit)\")]),t._v(\" \"),a(\"li\",[t._v(\"磁盘空间: 700 MB (不包括Xcode或Android Studio的磁盘空间）.\")]),t._v(\" \"),a(\"li\",[t._v(\"工具: Flutter 依赖下面这些命令行工具.\\n\"),a(\"ul\",[a(\"li\",[a(\"code\",[t._v(\"bash、mkdir、rm、git、curl、unzip、which\")])])])])]),t._v(\" \"),a(\"h4\",{attrs:{id:\"获取flutter-sdk-2\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#获取flutter-sdk-2\"}},[t._v(\"#\")]),t._v(\" 获取Flutter SDK\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[a(\"p\",[t._v(\"去flutter官网下载其最新可用的安装包，官网地址：https://flutter.io/sdk-archive/#macos\")]),t._v(\" \"),a(\"p\",[t._v(\"注意，Flutter的渠道版本会不停变动，请以Flutter官网为准。另外，在中国大陆地区，要想正常获取安装包列表或下载安装包，可能需要翻墙，读者也可以去Flutter github项目下去下载安装包，地址：https://github.com/flutter/flutter/releases 。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"解压安装包到你想安装的目录，如：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-shell extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token builtin class-name\"}},[t._v(\"cd\")]),t._v(\" ~/development\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"unzip\")]),t._v(\" ~/Downloads/flutter_macos_v0.5.1-beta.zip\\n\")])])])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"添加\"),a(\"code\",[t._v(\"flutter\")]),t._v(\"相关工具到path中：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-shell extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token builtin class-name\"}},[t._v(\"export\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token assign-left variable\"}},[a(\"span\",{pre:!0,attrs:{class:\"token environment constant\"}},[t._v(\"PATH\")])]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token variable\"}},[a(\"span\",{pre:!0,attrs:{class:\"token variable\"}},[t._v(\"`\")]),a(\"span\",{pre:!0,attrs:{class:\"token builtin class-name\"}},[t._v(\"pwd\")]),a(\"span\",{pre:!0,attrs:{class:\"token variable\"}},[t._v(\"`\")])]),t._v(\"/flutter/bin:\"),a(\"span\",{pre:!0,attrs:{class:\"token environment constant\"}},[t._v(\"$PATH\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"此代码只能暂时针对当前命令行窗口设置PATH环境变量，要想永久将Flutter添加到PATH中请参考下面\"),a(\"strong\",[t._v(\"更新环境变量\")]),t._v(\" 部分。\")])])]),t._v(\" \"),a(\"h5\",{attrs:{id:\"运行-flutter-doctor命令-2\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#运行-flutter-doctor命令-2\"}},[t._v(\"#\")]),t._v(\" 运行 flutter doctor命令\")]),t._v(\" \"),a(\"p\",[t._v(\"这一步和Windows下步骤一致，不再赘述。\")]),t._v(\" \"),a(\"h5\",{attrs:{id:\"更新环境变量-2\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#更新环境变量-2\"}},[t._v(\"#\")]),t._v(\" 更新环境变量\")]),t._v(\" \"),a(\"p\",[t._v(\"将Flutter添加到PATH中，可以在任何终端会话中运行\"),a(\"code\",[t._v(\"flutter\")]),t._v(\"命令。\")]),t._v(\" \"),a(\"p\",[t._v(\"对于所有终端会话永久修改此变量的步骤是和特定计算机系统相关的。通常，您会在打开新窗口时将设置环境变量的命令添加到执行的文件中。例如\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[a(\"p\",[t._v(\"确定您Flutter SDK的目录记为“FLUTTER_INSTALL_PATH”，您将在步骤3中用到。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"打开(或创建) \"),a(\"code\",[t._v(\"$HOME/.bash_profile\")]),t._v(\"。文件路径和文件名可能在你的电脑上不同.\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"添加以下路径:\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-shell extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token builtin class-name\"}},[t._v(\"export\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token assign-left variable\"}},[a(\"span\",{pre:!0,attrs:{class:\"token environment constant\"}},[t._v(\"PATH\")])]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"FLUTTER_INSTALL_PATH\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\"/flutter/bin:\"),a(\"span\",{pre:!0,attrs:{class:\"token environment constant\"}},[t._v(\"$PATH\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"例如笔者Flutter 安装目录是“~/code/flutter_dir”，那么代码为：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language- extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[a(\"code\",[t._v(\"export PATH=~/code/flutter_dir/flutter/bin:$PATH\\n\")])])])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"运行 \"),a(\"code\",[t._v(\"source $HOME/.bash_profile\")]),t._v(\" 刷新当前终端窗口。\")]),t._v(\" \"),a(\"blockquote\",[a(\"p\",[a(\"strong\",[t._v(\"注意:\")]),t._v(\" 如果你使用终端是zsh，终端启动时 \"),a(\"code\",[t._v(\"~/.bash_profile\")]),t._v(\" 将不会被加载，解决办法就是修改 \"),a(\"code\",[t._v(\"～/.zshrc\")]),t._v(\" ，在其中添加：source ～/.bash_profile\")])])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"验证“flutter/bin”是否已在PATH中：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language- extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[a(\"code\",[t._v(\"echo $PATH\\n\")])])])])]),t._v(\" \"),a(\"h4\",{attrs:{id:\"安装-xcode\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#安装-xcode\"}},[t._v(\"#\")]),t._v(\" 安装 Xcode\")]),t._v(\" \"),a(\"p\",[t._v(\"要为iOS开发Flutter应用程序，您需要Xcode 9.0或更高版本:\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"安装Xcode 9.0或更新版本(通过\"),a(\"a\",{attrs:{href:\"https://developer.apple.com/xcode/\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"链接下载\"),a(\"OutboundLink\")],1),t._v(\"或\"),a(\"a\",{attrs:{href:\"https://itunes.apple.com/us/app/xcode/id497799835\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"苹果应用商店\"),a(\"OutboundLink\")],1),t._v(\").\")]),t._v(\" \"),a(\"li\",[t._v(\"配置Xcode命令行工具以使用新安装的Xcode版本 \"),a(\"code\",[t._v(\"sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer\")]),t._v(\" 对于大多数情况，当您想要使用最新版本的Xcode时，这是正确的路径。如果您需要使用不同的版本，请指定相应路径。\")]),t._v(\" \"),a(\"li\",[t._v(\"确保Xcode许可协议是通过打开一次Xcode或通过命令\"),a(\"code\",[t._v(\"sudo xcodebuild -license\")]),t._v(\"同意过了.\")])]),t._v(\" \"),a(\"p\",[t._v(\"使用Xcode，您可以在iOS设备或模拟器上运行Flutter应用程序。\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"安装android-studio-2\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#安装android-studio-2\"}},[t._v(\"#\")]),t._v(\" 安装Android Studio\")]),t._v(\" \"),a(\"p\",[t._v(\"和Window一样，要在Android设备上构建并运行Flutter程序都需要先安装Android Studio，读者可以先自行下载并安装Android Studio，在此不再赘述。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"升级-flutter\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#升级-flutter\"}},[t._v(\"#\")]),t._v(\" 升级 Flutter\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"flutter-sdk分支\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#flutter-sdk分支\"}},[t._v(\"#\")]),t._v(\" Flutter SDK分支\")]),t._v(\" \"),a(\"p\",[t._v(\"Flutter SDK有多个分支，如beta、dev、master、stable，其中stable分支为稳定分支（日后有新的稳定版本发布后可能也会有新的稳定分支，如1.0.0），dev和master为开发分支，安装flutter后，你可以运行\"),a(\"code\",[t._v(\"flutter channel\")]),t._v(\"查看所有分支，如笔者本地运行后，结果如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language- extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[a(\"code\",[t._v(\"Flutter channels:\\n  beta\\n  dev\\n* master\\n\")])])]),a(\"p\",[t._v('带\"*\"号的分支即你本地的Flutter SDK 跟踪的分支，要切换分支，可以使用'),a(\"code\",[t._v(\"flutter channel beta\")]),t._v(\" 或 \"),a(\"code\",[t._v(\"flutter channel master\")]),t._v(\"，Flutter官方建议跟踪稳定分支，但你也可以跟踪\"),a(\"code\",[t._v(\"master\")]),t._v(\"分支，这样可以查看最新的变化，但这样稳定性要低的多。\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"升级flutter-sdk和依赖包\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#升级flutter-sdk和依赖包\"}},[t._v(\"#\")]),t._v(\" 升级Flutter SDK和依赖包\")]),t._v(\" \"),a(\"p\",[t._v(\"要升级flutter sdk，只需一句命令：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-shell extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[a(\"code\",[t._v(\"flutter upgrade\\n\")])])]),a(\"p\",[t._v(\"该命令会同时更新Flutter SDK和你的flutter项目依赖包。如果你只想更新项目依赖包（不包括Flutter SDK），可以使用如下命令：\")]),t._v(\" \"),a(\"ul\",[a(\"li\",[a(\"code\",[t._v(\"flutter packages get\")]),t._v(\"获取项目所有的依赖包。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"flutter packages upgrade\")]),t._v(\" 获取项目所有依赖包的最新版本。\")])]),t._v(\" \"),a(\"h4\",{attrs:{id:\"\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#\"}},[t._v(\"#\")])]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_1-3-2-ide配置与使用\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-3-2-ide配置与使用\"}},[t._v(\"#\")]),t._v(\" 1.3.2 IDE配置与使用\")]),t._v(\" \"),a(\"p\",[t._v(\"理论上可以使用任何文本编辑器与命令行工具来构建Flutter应用程序。 不过，Flutter官方建议使用Android Studio和VS Code之一以获得更好的开发体验。Flutter官方提供了这两款编辑器插件，通过IDE和插件可获得代码补全、语法高亮、widget编辑辅助、运行和调试支持等功能，可以帮助我们极大的提高开发效率。下面我们分别介绍一下Android Studio和VS Code的配置及使用（Android Studio和VS Code读者可以在其官网获得最新的安装，由于安装比较简单，故不再赘述）。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"android-studio-配置与使用\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#android-studio-配置与使用\"}},[t._v(\"#\")]),t._v(\" Android Studio 配置与使用\")]),t._v(\" \"),a(\"p\",[t._v(\"由于Android Studio是基于IntelliJ IDEA开发的，所以读者也可以使用IntelliJ IDEA。\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"安装flutter和dart插件\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#安装flutter和dart插件\"}},[t._v(\"#\")]),t._v(\" 安装Flutter和Dart插件\")]),t._v(\" \"),a(\"p\",[t._v(\"需要安装两个插件:\")]),t._v(\" \"),a(\"ul\",[a(\"li\",[a(\"code\",[t._v(\"Flutter\")]),t._v(\"插件： 支持Flutter开发工作流 (运行、调试、热重载等)。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"Dart\")]),t._v(\"插件： 提供代码分析 (输入代码时进行验证、代码补全等)。\")])]),t._v(\" \"),a(\"p\",[t._v(\"安装步骤：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"启动Android Studio。\")]),t._v(\" \"),a(\"li\",[t._v(\"打开插件首选项 (macOS：\"),a(\"strong\",[t._v(\"Preferences>Plugins\")]),t._v(\", Windows：\"),a(\"strong\",[t._v(\"File>Settings>Plugins\")]),t._v(\")。\")]),t._v(\" \"),a(\"li\",[t._v(\"选择 \"),a(\"strong\",[t._v(\"Browse repositories…\")]),t._v(\"，选择 flutter 插件并点击 \"),a(\"code\",[t._v(\"install\")]),t._v(\"。\")]),t._v(\" \"),a(\"li\",[t._v(\"重启Android Studio后插件生效。\")])]),t._v(\" \"),a(\"p\",[t._v(\"接下来，让我们用Android Studio创建一个Flutter项目，然后运行它，并体验“热重载”。\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"创建flutter应用\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#创建flutter应用\"}},[t._v(\"#\")]),t._v(\" 创建Flutter应用\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"选择 \"),a(\"strong\",[t._v(\"File>New Flutter Project\")]),t._v(\" 。\")]),t._v(\" \"),a(\"li\",[t._v(\"选择 \"),a(\"strong\",[t._v(\"Flutter application\")]),t._v(\" 作为 project 类型, 然后点击 Next。\")]),t._v(\" \"),a(\"li\",[t._v(\"输入项目名称 (如 \"),a(\"code\",[t._v(\"myapp\")]),t._v(\")，然后点击 Next。\")]),t._v(\" \"),a(\"li\",[t._v(\"点击 \"),a(\"strong\",[t._v(\"Finish\")]),t._v(\"。\")]),t._v(\" \"),a(\"li\",[t._v(\"等待Android Studio安装SDK并创建项目。\")])]),t._v(\" \"),a(\"p\",[t._v(\"上述命令创建一个Flutter项目，项目名为myapp，其中包含一个使用\"),a(\"a\",{attrs:{href:\"https://material.io/guidelines/\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Material 组件\"),a(\"OutboundLink\")],1),t._v(\"的简单演示应用程序。\")]),t._v(\" \"),a(\"p\",[t._v(\"在项目目录中，您应用程序的代码位于 \"),a(\"code\",[t._v(\"lib/main.dart\")]),t._v(\"。\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"运行应用程序\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#运行应用程序\"}},[t._v(\"#\")]),t._v(\" 运行应用程序\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[a(\"p\",[t._v(\"定位到Android Studio工具栏，如图1-3所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:r(362),alt:\"图1-3\"}})])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"在 \"),a(\"strong\",[t._v(\"target selector\")]),t._v(\" 中, 选择一个运行该应用的Android设备。如果没有列出可用，请选择 \"),a(\"strong\",[t._v(\"Tools>Android>AVD Manager\")]),t._v(\" 并在那里创建一个。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"在工具栏中点击 \"),a(\"strong\",[t._v(\"Run图标\")]),t._v(\"。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"如果一切正常, 您应该在您的设备或模拟器上会看到启动的应用程序：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:r(363),alt:\"图1-4\"}})])])]),t._v(\" \"),a(\"h4\",{attrs:{id:\"体验热重载\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#体验热重载\"}},[t._v(\"#\")]),t._v(\" 体验热重载\")]),t._v(\" \"),a(\"p\",[t._v(\"Flutter 可以通过 \"),a(\"em\",[t._v(\"热重载（hot reload）\")]),t._v(\" 实现快速的开发周期，热重载就是无需重启应用程序就能实时加载修改后的代码，并且不会丢失状态。简单的对代码进行更改，然后告诉IDE或命令行工具你需要重新加载（点击reload按钮），你就会在你的设备或模拟器上看到更改。\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[a(\"p\",[t._v(\"打开\"),a(\"code\",[t._v(\"lib/main.dart\")]),t._v(\"文件\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"将字符串\\n\"),a(\"code\",[t._v(\"'You have pushed the button this many times:'\")]),t._v(\" 更改为\\n\"),a(\"code\",[t._v(\"'You have clicked the button this many times:'\")])])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"不要按“停止”按钮; 让您的应用继续运行.\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"要查更改，请调用 \"),a(\"strong\",[t._v(\"Save\")]),t._v(\" (\"),a(\"code\",[t._v(\"cmd-s\")]),t._v(\" / \"),a(\"code\",[t._v(\"ctrl-s\")]),t._v(\")，或者点击 \"),a(\"strong\",[t._v(\"热重载按钮\")]),t._v(\" (带有闪电⚡️图标的按钮)。\")]),t._v(\" \"),a(\"p\",[t._v(\"你会立即在运行的应用程序中看到更新的字符串。\")])])]),t._v(\" \"),a(\"h3\",{attrs:{id:\"vs-code的配置与使用\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#vs-code的配置与使用\"}},[t._v(\"#\")]),t._v(\" VS Code的配置与使用\")]),t._v(\" \"),a(\"p\",[t._v(\"VS Code是一个轻量级编辑器，支持Flutter运行和调试。\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"安装flutter插件\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#安装flutter插件\"}},[t._v(\"#\")]),t._v(\" 安装flutter插件\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"启动 VS Code。\")]),t._v(\" \"),a(\"li\",[t._v(\"调用 \"),a(\"strong\",[t._v(\"View>Command Palette…\")]),t._v(\"。\")]),t._v(\" \"),a(\"li\",[t._v(\"输入 ‘install’, 然后选择 \"),a(\"strong\",[t._v(\"Extensions: Install Extension\")]),t._v(\" action。\")]),t._v(\" \"),a(\"li\",[t._v(\"在搜索框输入 \"),a(\"code\",[t._v(\"flutter\")]),t._v(\" ，在搜索结果列表中选择 ‘Flutter’, 然后点击 \"),a(\"strong\",[t._v(\"Install\")]),t._v(\"。\")]),t._v(\" \"),a(\"li\",[t._v(\"选择 ‘OK’ 重新启动 VS Code。\")]),t._v(\" \"),a(\"li\",[t._v(\"验证配置\\n\"),a(\"ul\",[a(\"li\",[t._v(\"调用 \"),a(\"strong\",[t._v(\"View>Command Palette…\")])]),t._v(\" \"),a(\"li\",[t._v(\"输入 ‘doctor’, 然后选择 \"),a(\"strong\",[t._v(\"‘Flutter: Run Flutter Doctor’\")]),t._v(\" action。\")]),t._v(\" \"),a(\"li\",[t._v(\"查看“OUTPUT”窗口中的输出是否有问题\")])])])]),t._v(\" \"),a(\"h4\",{attrs:{id:\"创建flutter应用-2\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#创建flutter应用-2\"}},[t._v(\"#\")]),t._v(\" 创建Flutter应用\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"启动 VS Code\")]),t._v(\" \"),a(\"li\",[t._v(\"调用 \"),a(\"strong\",[t._v(\"View>Command Palette…\")])]),t._v(\" \"),a(\"li\",[t._v(\"输入 ‘flutter’, 然后选择 \"),a(\"strong\",[t._v(\"‘Flutter: New Project’\")]),t._v(\" action\")]),t._v(\" \"),a(\"li\",[t._v(\"输入 Project 名称 (如\"),a(\"code\",[t._v(\"myapp\")]),t._v(\"), 然后按回车键\")]),t._v(\" \"),a(\"li\",[t._v(\"指定放置项目的位置，然后按蓝色的确定按钮\")]),t._v(\" \"),a(\"li\",[t._v(\"等待项目创建继续，并显示main.dart文件\")])]),t._v(\" \"),a(\"h4\",{attrs:{id:\"体验热重载-2\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#体验热重载-2\"}},[t._v(\"#\")]),t._v(\" 体验热重载\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"打开\"),a(\"code\",[t._v(\"lib/main.dart\")]),t._v(\"文件。\")]),t._v(\" \"),a(\"li\",[t._v(\"将字符串\\n\"),a(\"code\",[t._v(\"'You have pushed the button this many times:'\")]),t._v(\" 更改为\\n\"),a(\"code\",[t._v(\"'You have clicked the button this many times:'\")]),t._v(\"。\")]),t._v(\" \"),a(\"li\",[t._v(\"不要按“停止”按钮; 让您的应用继续运行。\")]),t._v(\" \"),a(\"li\",[t._v(\"要查看您的更改，请调用 \"),a(\"strong\",[t._v(\"Save\")]),t._v(\" (\"),a(\"code\",[t._v(\"cmd-s\")]),t._v(\" / \"),a(\"code\",[t._v(\"ctrl-s\")]),t._v(\"), 或者点击 \"),a(\"strong\",[t._v(\"热重载按钮\")]),t._v(\" (绿色圆形箭头按钮)。\")])]),t._v(\" \"),a(\"p\",[t._v(\"你会立即在运行的应用程序中看到更新的字符串。\")]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_1-3-3-连接设备运行flutter应用\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-3-3-连接设备运行flutter应用\"}},[t._v(\"#\")]),t._v(\" 1.3.3 连接设备运行Flutter应用\")]),t._v(\" \"),a(\"p\",[t._v(\"Window下只支持为Android设备构建并运行Flutter应用，而macOS同时支持iOS和Android设备。下面分别介绍如何连接Android和iOS设备来运行flutter应用。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"连接android模拟器\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#连接android模拟器\"}},[t._v(\"#\")]),t._v(\" 连接Android模拟器\")]),t._v(\" \"),a(\"p\",[t._v(\"要准备在Android模拟器上运行并测试Flutter应用，请按照以下步骤操作：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[a(\"p\",[t._v(\"启动 \"),a(\"strong\",[t._v(\"Android Studio>Tools>Android>AVD Manager\")]),t._v(\" 并选择 \"),a(\"strong\",[t._v(\"Create Virtual Device\")]),t._v(\".\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"选择一个设备并选择 \"),a(\"strong\",[t._v(\"Next\")]),t._v(\"。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"为要模拟的Android版本选择一个或多个系统印象，然后选择 \"),a(\"strong\",[t._v(\"Next\")]),t._v(\". 建议使用 \"),a(\"em\",[t._v(\"x86\")]),t._v(\" 或 \"),a(\"em\",[t._v(\"x86_64\")]),t._v(\" image .\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"在 “Emulated Performance”下, 选择 \"),a(\"strong\",[t._v(\"Hardware - GLES 2.0\")]),t._v(\" 以启用 \"),a(\"a\",{attrs:{href:\"https://developer.android.com/studio/run/emulator-acceleration.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"硬件加速\"),a(\"OutboundLink\")],1),t._v(\".\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"验证AVD配置是否正确，然后选择 \"),a(\"strong\",[t._v(\"Finish\")]),t._v(\"。\")]),t._v(\" \"),a(\"p\",[t._v(\"有关上述步骤的详细信息，请参阅 \"),a(\"a\",{attrs:{href:\"https://developer.android.com/studio/run/managing-avds.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Managing AVDs\"),a(\"OutboundLink\")],1),t._v(\".\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"在“Android Virtual Device Manager”中，点击工具栏的 \"),a(\"strong\",[t._v(\"Run\")]),t._v(\"。模拟器启动并显示所选操作系统版本或设备的启动画面。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"运行 \"),a(\"code\",[t._v(\"flutter run\")]),t._v(\" 启动您的设备。 连接的设备名是 \"),a(\"code\",[t._v(\"Android SDK built for <platform>\")]),t._v(\"，其中 \"),a(\"em\",[t._v(\"platform\")]),t._v(\" 是芯片系列，如 x86。\")])])]),t._v(\" \"),a(\"h3\",{attrs:{id:\"连接android真机设备\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#连接android真机设备\"}},[t._v(\"#\")]),t._v(\" 连接Android真机设备\")]),t._v(\" \"),a(\"p\",[t._v(\"要准备在Android设备上运行并测试Flutter应用，需要Android 4.1（API level 16）或更高版本的Android设备.\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"在Android设备上启用 \"),a(\"strong\",[t._v(\"开发人员选项\")]),t._v(\" 和 \"),a(\"strong\",[t._v(\"USB调试\")]),t._v(\" 。详细说明可在\"),a(\"a\",{attrs:{href:\"https://developer.android.com/studio/debug/dev-options.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Android文档\"),a(\"OutboundLink\")],1),t._v(\"中找到。\")]),t._v(\" \"),a(\"li\",[t._v(\"使用USB将手机插入电脑。如果设备出现调试授权提示，请授权你的电脑可以访问该设备。\")]),t._v(\" \"),a(\"li\",[t._v(\"在命令行运行 \"),a(\"code\",[t._v(\"flutter devices\")]),t._v(\" 命令以验证Flutter识别您连接的Android设备。\")]),t._v(\" \"),a(\"li\",[t._v(\"运行启动你应用程序 \"),a(\"code\",[t._v(\"flutter run\")]),t._v(\"。\")])]),t._v(\" \"),a(\"p\",[t._v(\"默认情况下，Flutter使用的Android SDK版本是基于你的 \"),a(\"code\",[t._v(\"adb\")]),t._v(\" 工具版本。 如果想让Flutter使用不同版本的Android SDK，则必须将该 \"),a(\"code\",[t._v(\"ANDROID_HOME\")]),t._v(\" 环境变量设置为相应的SDK安装目录。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"连接ios模拟器\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#连接ios模拟器\"}},[t._v(\"#\")]),t._v(\" 连接iOS模拟器\")]),t._v(\" \"),a(\"p\",[t._v(\"要准备在iOS模拟器上运行并测试Flutter应用，请按以下步骤操作：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[a(\"p\",[t._v(\"在你的MAC上，通过 Spotlight 或以下命令找到模拟器：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-shell extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"open\")]),t._v(\" -a Simulator\\n\")])])])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"通过检查模拟器 \"),a(\"strong\",[t._v(\"Hardware > Device\")]),t._v(\" 菜单中的设置，确保模拟器正在使用64位设备（iPhone 5s或更高版本）。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"根据你电脑屏幕大小，模拟高清屏iOS设备可能会溢出屏幕。可以在模拟器的 \"),a(\"strong\",[t._v(\"Window> Scale\")]),t._v(\" 菜单下设置设备比例。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"运行 \"),a(\"code\",[t._v(\"flutter run\")]),t._v(\"启动flutter应用程序。\")])])]),t._v(\" \"),a(\"h3\",{attrs:{id:\"连接ios真机设备\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#连接ios真机设备\"}},[t._v(\"#\")]),t._v(\" 连接iOS真机设备\")]),t._v(\" \"),a(\"p\",[t._v(\"要将Flutter应用安装到iOS真机设备，需要一些额外的工具和一个Apple帐户，还需要在Xcode中进行一些设置。\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[a(\"p\",[t._v(\"安装 \"),a(\"a\",{attrs:{href:\"http://brew.sh/\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"homebrew\"),a(\"OutboundLink\")],1),t._v(\" （如果已经安装了brew,跳过此步骤）。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"打开终端并运行如下这些命令:\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-shell extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[a(\"code\",[t._v(\"brew update\\nbrew \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"install\")]),t._v(\" --HEAD libimobiledevice\\nbrew \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"install\")]),t._v(\" ideviceinstaller ios-deploy cocoapods\\npod setup\\n\")])])]),a(\"p\",[t._v(\"如果这些命令中的任何一个失败并出现错误，请运行brew doctor并按照说明解决问题.\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"遵循Xcode签名流程来配置您的项目:\")]),t._v(\" \"),a(\"ul\",[a(\"li\",[a(\"p\",[t._v(\"在你Flutter项目目录中通过 \"),a(\"code\",[t._v(\"open ios/Runner.xcworkspace\")]),t._v(\" 打开默认的Xcode workspace.\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"在Xcode中，选择导航面板左侧中的\"),a(\"code\",[t._v(\"Runner\")]),t._v(\"项目。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"在\"),a(\"code\",[t._v(\"Runner\")]),t._v(\" target设置页面中，确保在 \"),a(\"strong\",[t._v(\"General > Signing > Team\")]),t._v(\" 下选择了你的开发团队。当你选择一个团队时，Xcode会创建并下载开发证书，向你的设备注册你的帐户，并创建和下载配置文件（如果需要）。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"要开始您的第一个iOS开发项目，您可能需要使用您的Apple ID登录Xcode，如图1-5：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:r(364),alt:\"图1-5\"}})]),t._v(\" \"),a(\"p\",[t._v(\"任何Apple ID都支持开发和测试，但若想将应用分发到App Store，就必须注册Apple开发者计划，有关详情读者可以自行了解。\")])])]),t._v(\" \"),a(\"ol\",{attrs:{start:\"4\"}},[a(\"li\",[a(\"p\",[t._v(\"当您第一次attach真机设备进行iOS开发时，需要同时信任你的Mac和该设备上的开发证书。首次将iOS设备连接到Mac时，请在对话框中选择 \"),a(\"code\",[t._v(\"Trust\")]),t._v(\"。\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:r(365),alt:\"添加信任\"}})]),t._v(\" \"),a(\"p\",[t._v(\"然后，转到iOS设备上的\"),a(\"strong\",[t._v(\"设置\")]),t._v(\"菜单，选择 \"),a(\"strong\",[t._v(\"常规>设备管理\")]),t._v(\" 并信任您的证书。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"如果Xcode中的自动签名失败，请验证项目的 \"),a(\"strong\",[t._v(\"General > Identity > Bundle Identifier\")]),t._v(\" 值是否唯一，如图1-7所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:r(366),alt:\"验证bundle id是否唯一\"}})])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"运行 \"),a(\"code\",[t._v(\"flutter run\")]),t._v(\"启动flutter应用程序。\")])])])])]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_1-3-4-常见配置问题\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-3-4-常见配置问题\"}},[t._v(\"#\")]),t._v(\" 1.3.4 常见配置问题\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"android-studio问题\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#android-studio问题\"}},[t._v(\"#\")]),t._v(\" Android Studio问题\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"缺少依赖库问题\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#缺少依赖库问题\"}},[t._v(\"#\")]),t._v(\" 缺少依赖库问题\")]),t._v(\" \"),a(\"p\",[t._v(\"上手安卓最常遇见的问题之一，错误如图1-8所示，此时点击超链接即可自动跳转到安装页面\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:r(367),alt:\"图1-8\"}})]),t._v(\" \"),a(\"p\",[t._v(\"安装之后重新运行即可，如图1-9：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:r(368),alt:\"install_request_components.png\"}})]),t._v(\" \"),a(\"h4\",{attrs:{id:\"连接不上android-repository\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#连接不上android-repository\"}},[t._v(\"#\")]),t._v(\" 连接不上Android Repository\")]),t._v(\" \"),a(\"p\",[t._v(\"这也是最常见的问题之一，当你发现自己无法下载部分依赖的时候，请优先考虑这种情况。进入 \"),a(\"code\",[t._v(\"File\")]),t._v(\" -> \"),a(\"code\",[t._v(\"Settings\")]),t._v(\" -> \"),a(\"code\",[t._v(\"Appearance & Behavior\")]),t._v(\" -> \"),a(\"code\",[t._v(\"System Settings\")]),t._v(\" -> \"),a(\"code\",[t._v(\"Android SDK\")]),t._v(\" -> \"),a(\"code\",[t._v(\"SDK Update Sites\")]),t._v(\" 列表，可以看到此时的 \"),a(\"code\",[t._v(\"Android Repository\")]),t._v(\" 无法连接，如图1-10所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:r(369),alt:\"下载依赖失败\"}})]),t._v(\" \"),a(\"p\",[t._v(\"这是由于要去Google下载Android SDK，但在国内目前无法访问Google所致，因此，我们可以配置代理或使用vpn。\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"安卓包配置问题\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#安卓包配置问题\"}},[t._v(\"#\")]),t._v(\" 安卓包配置问题\")]),t._v(\" \"),a(\"p\",[t._v(\"一般格式为\")]),t._v(\" \"),a(\"div\",{staticClass:\"language- extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[a(\"code\",[t._v(\"Could not HEAD **\\nCould not Get **\\n\")])])]),a(\"p\",[t._v(\"如：\"),a(\"code\",[t._v(\"Android Studio Could not GET gradle-3.2.0.pom\")])]),t._v(\" \"),a(\"p\",[t._v(\"这一类问题是由于无法连接到 Maven 库造成的，解决方法如下：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[a(\"p\",[t._v(\"进入\"),a(\"code\",[t._v(\"当前所在项目名/android\")])])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"打开 \"),a(\"code\",[t._v(\"build.gradle\")])])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"找到下面这一部分，并加上 \"),a(\"code\",[t._v(\"maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }\")])]),t._v(\" \"),a(\"div\",{staticClass:\"language- extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[a(\"code\",[t._v(\"allprojects {\\n    repositories {\\n      google()\\n      jcenter()\\n      maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' } //添加这一句\\n\\t}\\n}\\n\")])])])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"进入 File/ Settings/ Build, Execution, Deployment/ BuildTools/ Gradle/ Android Studio 中，勾选上 Enable embedded Maven repository ，重启 Android Studio 即可解决。\")]),t._v(\" \"),a(\"blockquote\",[a(\"p\",[t._v(\"**注意：**存在这样的一种情况，当你根据上述步骤设置了之后，依旧无法解决这个问题，并有类似于 \"),a(\"code\",[t._v(\"Could not HEAD maven.aliyun.com\")]),t._v(\" 的报错信息，请检查 \"),a(\"code\",[t._v(\"C:\\\\Users\\\\{user_name}\\\\.gradle\\\\gradle.properties\")]),t._v(\" 是否有设置代理。删除后问题即可解决。\")])])])]),t._v(\" \"),a(\"h4\",{attrs:{id:\"hot-reload-热重载失效问题\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#hot-reload-热重载失效问题\"}},[t._v(\"#\")]),t._v(\" Hot Reload 热重载失效问题\")]),t._v(\" \"),a(\"p\",[t._v(\"在给 \"),a(\"code\",[t._v(\"Terminal\")]),t._v(\" 之类的终端模拟器设置代理之后，会导致“Hot Reload”重载失效，此时调用 \"),a(\"strong\",[t._v(\"Save\")]),t._v(\" (\"),a(\"code\",[t._v(\"cmd-s\")]),t._v(\" / \"),a(\"code\",[t._v(\"ctrl-s\")]),t._v(\")将不会进行热重载，\"),a(\"strong\",[t._v(\"热重载按钮\")]),t._v(\" (带有闪电⚡️图标的按钮)也不会显示，将代理移除即可解决。\")]),t._v(\" \"),a(\"p\",[t._v(\"另外，有些情况下热重载是不生效的，比如修改了\"),a(\"code\",[t._v(\"main\")]),t._v(\"函数、修改了全局静态方法等，读者可以认为“Hot Reload”只会重新构建整个widget树，如果变动不在构建widget树的过程中，“Hot Reload”就不会起作用。\")])])}),[],!1,null,null,null);e.default=v.exports}}]);"
  },
  {
    "path": "docs/assets/js/50.57b758f4.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[50],{564:function(t,s,a){t.exports=a.p+\"assets/img/15-6.92dd6b15.png\"},565:function(t,s,a){t.exports=a.p+\"assets/img/15-7.aa6d4238.png\"},566:function(t,s,a){t.exports=a.p+\"assets/img/15-8.1362ff85.png\"},872:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_15-8-多语言和多主题\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_15-8-多语言和多主题\"}},[t._v(\"#\")]),t._v(\" 15.8 多语言和多主题\")]),t._v(\" \"),n(\"p\",[t._v(\"本实例APP中语言和主题都是可以设置的，而两者都是通过\"),n(\"code\",[t._v(\"ChangeNotifierProvider\")]),t._v(\"来实现的：我们在\"),n(\"code\",[t._v(\"main\")]),t._v(\"函数中使用了\"),n(\"code\",[t._v(\"Consumer2\")]),t._v(\"，依赖了\"),n(\"code\",[t._v(\"ThemeModel\")]),t._v(\"和\"),n(\"code\",[t._v(\"LocaleModel\")]),t._v(\"，因此，当我们在语言和主题设置页更该当前的配置后，\"),n(\"code\",[t._v(\"Consumer2\")]),t._v(\"的\"),n(\"code\",[t._v(\"builder\")]),t._v(\"都会重新执行，构建一个新的\"),n(\"code\",[t._v(\"MaterialApp\")]),t._v(\"，所以修改会立即生效。下面看一下语言和主题设置页的实现。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_15-8-1-语言选择页\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_15-8-1-语言选择页\"}},[t._v(\"#\")]),t._v(\" 15.8.1 语言选择页\")]),t._v(\" \"),n(\"p\",[t._v(\"APP语言选择页提供三个选项：中文简体、美国英语、跟随系统。我们将当前APP使用的语言高亮显示，并且在后面添加一个“对号”图标，实现如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"LanguageRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" color \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Theme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"primaryColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" localeModel \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Provider\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"of\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"LocaleModel\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" gm \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" GmLocalizations\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//构建语言选择项\")]),t._v(\"\\n    Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_buildLanguageItem\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String lan\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          lan\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 对APP当前语言进行高亮显示\")]),t._v(\"\\n          style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" localeModel\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"locale \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" value \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" color \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        trailing\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n            localeModel\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"locale \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" value \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"done\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        onTap\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 更新locale后MaterialApp会重新build\")]),t._v(\"\\n          localeModel\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"locale \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      appBar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"gm\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"language\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      body\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_buildLanguageItem\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"中文简体\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"zh_CN\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_buildLanguageItem\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"English\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"en_US\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_buildLanguageItem\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"gm\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"auto\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上面代码逻辑很简单，唯一需要注意的是我们在\"),n(\"code\",[t._v(\"build(…)\")]),t._v(\"方法里面定义了\"),n(\"code\",[t._v(\"_buildLanguageItem(…)\")]),t._v(\"方法，它和在\"),n(\"code\",[t._v(\"LanguageRoute\")]),t._v(\"类中定义该方法的区别就在于：在\"),n(\"code\",[t._v(\"build(…)\")]),t._v(\"内定义的方法可以共享\"),n(\"code\",[t._v(\"build(...)\")]),t._v(\"方法上下文中的变量，本例中是共享了\"),n(\"code\",[t._v(\"localeModel\")]),t._v(\"。当然，如果\"),n(\"code\",[t._v(\"_buildLanguageItem(…)\")]),t._v(\"的实现复杂一些的话不建议这样做，此时最好是将其作为\"),n(\"code\",[t._v(\"LanguageRoute\")]),t._v(\"类的方法。该页面运行效果如图15-6、15-7所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(564),alt:\"15-6\"}}),n(\"img\",{attrs:{src:a(565),alt:\"15-7\"}})]),t._v(\" \"),n(\"p\",[t._v(\"切换语言后立即生效。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_15-8-2-主题选择页\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_15-8-2-主题选择页\"}},[t._v(\"#\")]),t._v(\" 15.8.2 主题选择页\")]),t._v(\" \"),n(\"p\",[t._v(\"一个完整的主题\"),n(\"code\",[t._v(\"Theme\")]),t._v(\"包括很多选项，这些选项在\"),n(\"code\",[t._v(\"ThemeData\")]),t._v(\"中定义。本实例为了简单起见，我们只配置主题颜色。我们提供几种默认预定义的主题色供用户选择，用户点击一种色块后则更新主题。主题选择页的实现代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ThemeChangeRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      appBar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"GmLocalizations\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"theme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      body\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//显示主题色块\")]),t._v(\"\\n        children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Global\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"themes\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"map\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GestureDetector\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"symmetric\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"vertical\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"5\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" horizontal\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"40\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onTap\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//主题更新后，MaterialApp会重新build\")]),t._v(\"\\n              Provider\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"of\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ThemeModel\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"theme \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toList\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图15-8所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(566),alt:\"15-8\"}})]),t._v(\" \"),n(\"p\",[t._v(\"点击其它主题色块后，APP主题色立马切换生效。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/51.4c0d2270.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[51],{573:function(t,s,a){t.exports=a.p+\"assets/img/2-5.7456ef8f.png\"},574:function(t,s,a){t.exports=a.p+\"assets/img/2-6.fb55e91b.png\"},575:function(t,s,a){t.exports=a.p+\"assets/img/2-7.90b5e799.png\"},879:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_2-3-包管理\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_2-3-包管理\"}},[t._v(\"#\")]),t._v(\" 2.3 包管理\")]),t._v(\" \"),n(\"p\",[t._v(\"在软件开发中，很多时候有一些公共的库或SDK可能会被很多项目用到，因此，将这些代码单独抽到一个独立模块，然后哪个项目需要使用时再直接集成这个模块，便可大大提高开发效率。很多编程语言或开发工具都支持这种“模块共享”机制，如Java语言中这种独立模块会被打成一个jar包，Android中的aar包，Web开发中的npm包等。为了方便表述，我们将这种可共享的独立模块统一称为“包”（ Package）。\")]),t._v(\" \"),n(\"p\",[t._v(\"一个APP在实际开发中往往会依赖很多包，而这些包通常都有交叉依赖关系、版本依赖等，如果由开发者手动来管理应用中的依赖包将会非常麻烦。因此，各种开发生态或编程语言官方通常都会提供一些包管理工具，比如在Android提供了Gradle来管理依赖，iOS用Cocoapods或Carthage来管理依赖，Node中通过npm等。而在Flutter开发中也有自己的包管理工具。本节我们主要介绍一下flutter如何使用配置文件\"),n(\"code\",[t._v(\"pubspec.yaml\")]),t._v(\"（位于项目根目录）来管理第三方依赖包。\")]),t._v(\" \"),n(\"p\",[t._v(\"YAML是一种直观、可读性高并且容易被人类阅读的文件格式，它和xml或Json相比，它语法简单并非常容易解析，所以YAML常用于配置文件，Flutter也是用yaml文件作为其配置文件。Flutter项目默认的配置文件是\"),n(\"code\",[t._v(\"pubspec.yaml\")]),t._v(\"，我们看一个简单的示例：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-yaml extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"name\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" flutter_in_action\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"description\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" First Flutter application.\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"version\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" 1.0.0+1\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"dependencies\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"flutter\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"sdk\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" flutter\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"cupertino_icons\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ^0.1.2\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"dev_dependencies\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"flutter_test\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"sdk\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" flutter\\n    \\n\"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"flutter\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"uses-material-design\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean important\"}},[t._v(\"true\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"下面，我们逐一解释一下各个字段的意义：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"name\")]),t._v(\"：应用或包名称。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"description\")]),t._v(\": 应用或包的描述、简介。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"version\")]),t._v(\"：应用或包的版本号。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"dependencies\")]),t._v(\"：应用或包依赖的其它包或插件。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"dev_dependencies\")]),t._v(\"：开发环境依赖的工具包（而不是flutter应用本身依赖的包）。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"flutter\")]),t._v(\"：flutter相关的配置选项。\")])]),t._v(\" \"),n(\"p\",[t._v(\"如果我们的Flutter应用本身依赖某个包，我们需要将所依赖的包添加到\"),n(\"code\",[t._v(\"dependencies\")]),t._v(\" 下，接下来我们通过一个例子来演示一下如何添加、下载并使用第三方包。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"pub仓库\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#pub仓库\"}},[t._v(\"#\")]),t._v(\" Pub仓库\")]),t._v(\" \"),n(\"p\",[t._v(\"Pub（https://pub.dev/ ）是Google官方的Dart Packages仓库，类似于node中的npm仓库，android中的jcenter。我们可以在Pub上面查找我们需要的包和插件，也可以向Pub发布我们的包和插件。我们将在后面的章节中介绍如何向Pub发布我们的包和插件。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"示例\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),n(\"p\",[t._v(\"接下来，我们实现一个显示随机字符串的widget。有一个名为“english_words”的开源软件包，其中包含数千个常用的英文单词以及一些实用功能。我们首先在pub上找到english_words这个包（如图2-5所示），确定其最新的版本号和是否支持Flutter。\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(573),alt:\"图2-5\"}})]),t._v(\" \"),n(\"p\",[t._v(\"我们看到“english_words”包最新的版本是3.1.3，并且支持flutter，接下来：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"p\",[t._v(\"将“english_words”（3.1.3版本）添加到依赖项列表，如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-yaml extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"dependencies\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"flutter\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"sdk\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" flutter\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"cupertino_icons\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ^0.1.0\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"# 新添加的依赖\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"english_words\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ^3.1.3\\n\")])])])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"下载包。在Android Studio的编辑器视图中查看pubspec.yaml时（图2-6），单击右上角的 \"),n(\"strong\",[t._v(\"Packages get\")]),t._v(\" 。\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(574),alt:\"图2-6\"}})]),t._v(\" \"),n(\"p\",[t._v(\"这会将依赖包安装到您的项目。我们可以在控制台中看到以下内容：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-shell extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[n(\"code\",[t._v(\"flutter packages get\\nRunning \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"flutter packages get\"')]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"in\")]),t._v(\" flutter_in_action\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"..\")]),t._v(\".\\nProcess finished with \"),n(\"span\",{pre:!0,attrs:{class:\"token builtin class-name\"}},[t._v(\"exit\")]),t._v(\" code \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们也可以在控制台，定位到当前工程目录，然后手动运行\"),n(\"code\",[t._v(\"flutter packages get\")]),t._v(\" 命令来下载依赖包。另外，需要注意\"),n(\"code\",[t._v(\"dependencies\")]),t._v(\"和\"),n(\"code\",[t._v(\"dev_dependencies\")]),t._v(\"的区别，前者的依赖包将作为APP的源码的一部分参与编译，生成最终的安装包。而后者的依赖包只是作为开发阶段的一些工具包，主要是用于帮助我们提高开发、测试效率，比如flutter的自动化测试包等。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"引入\"),n(\"code\",[t._v(\"english_words\")]),t._v(\"包。\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:english_words/english_words.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"在输入时，Android Studio会自动提供有关库导入的建议选项。导入后该行代码将会显示为灰色，表示导入的库尚未使用。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"使用\"),n(\"code\",[t._v(\"english_words\")]),t._v(\"包来生成随机字符串。\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"RandomWordsWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 生成随机字符串\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" wordPair \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"WordPair\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"random\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"8.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"wordPair\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们将\"),n(\"code\",[t._v(\"RandomWordsWidget\")]),t._v(\" 添加到 \"),n(\"code\",[t._v(\"_MyHomePageState.build\")]),t._v(\" 的\"),n(\"code\",[t._v(\"Column\")]),t._v(\"的子widget中。\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  mainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RandomWordsWidget\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"如果应用程序正在运行，请使用热重载按钮（⚡️图标） 更新正在运行的应用程序。每次单击热重载或保存项目时，都会在正在运行的应用程序中随机选择不同的单词对。 这是因为单词对是在 \"),n(\"code\",[t._v(\"build\")]),t._v(\" 方法内部生成的。每次热更新时，\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法都会被执行，运行效果如图2-7所示。\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(575),alt:\"图2-7\"}})])])]),t._v(\" \"),n(\"h2\",{attrs:{id:\"其它依赖方式\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#其它依赖方式\"}},[t._v(\"#\")]),t._v(\" 其它依赖方式\")]),t._v(\" \"),n(\"p\",[t._v(\"上文所述的依赖方式是依赖Pub仓库的。但我们还可以依赖本地包和git仓库。\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"p\",[t._v(\"依赖本地包\")]),t._v(\" \"),n(\"p\",[t._v(\"如果我们正在本地开发一个包，包名为pkg1，我们可以通过下面方式依赖：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-yaml extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"dependencies\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n\\t\"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"pkg1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"path\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ../../code/pkg1\\n\")])])]),n(\"p\",[t._v(\"路径可以是相对的，也可以是绝对的。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"依赖Git：你也可以依赖存储在Git仓库中的包。如果软件包位于仓库的根目录中，请使用以下语法\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-yaml extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"dependencies\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"pkg1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"git\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"url\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" git\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"//github.com/xxx/pkg1.git\\n\")])])]),n(\"p\",[t._v(\"上面假定包位于Git存储库的根目录中。如果不是这种情况，可以使用path参数指定相对位置，例如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-yaml extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"dependencies\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"package1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"git\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"url\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" git\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"//github.com/flutter/packages.git\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"path\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" packages/package1        \\n\")])])])])]),t._v(\" \"),n(\"p\",[t._v(\"上面介绍的这些依赖方式是Flutter开发中常用的，但还有一些其它依赖方式，完整的内容读者可以自行查看：https://www.dartlang.org/tools/pub/dependencies 。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"总结\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),n(\"p\",[t._v(\"本节介绍了Flutter中包管理、引用、下载的整体流程，我们将在后面的章节中介绍如何开发并发布我们自己的包。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/52.61d5b4f1.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[52],{579:function(t,a,s){t.exports=s.p+\"assets/img/2-2.de6feb35.png\"},580:function(t,a,s){t.exports=s.p+\"assets/img/2-3.c20b3236.png\"},581:function(t,a,s){t.exports=s.p+\"assets/img/2-4.1abb1cab.png\"},882:function(t,a,s){\"use strict\";s.r(a);var n=s(45),e=Object(n.a)({},(function(){var t=this,a=t.$createElement,n=t._self._c||a;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_2-2-路由管理\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_2-2-路由管理\"}},[t._v(\"#\")]),t._v(\" 2.2 路由管理\")]),t._v(\" \"),n(\"p\",[t._v(\"路由(Route)在移动开发中通常指页面（Page），这跟web开发中单页应用的Route概念意义是相同的，Route在Android中通常指一个Activity，在iOS中指一个ViewController。所谓路由管理，就是管理页面之间如何跳转，通常也可被称为导航管理。Flutter中的路由管理和原生开发类似，无论是Android还是iOS，导航管理都会维护一个路由栈，路由入栈(push)操作对应打开一个新页面，路由出栈(pop)操作对应页面关闭操作，而路由管理主要是指如何来管理路由栈。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_2-2-1-一个简单示例\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_2-2-1-一个简单示例\"}},[t._v(\"#\")]),t._v(\" 2.2.1 一个简单示例\")]),t._v(\" \"),n(\"p\",[t._v(\"我们在上一节“计数器”示例的基础上，做如下修改：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"p\",[t._v(\"创建一个新路由，命名“NewRoute”\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"NewRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      appBar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"New route\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      body\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"This is new route\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"新路由继承自\"),n(\"code\",[t._v(\"StatelessWidget\")]),t._v('，界面很简单，在页面中间显示一句\"This is new route\"。')])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"在\"),n(\"code\",[t._v(\"_MyHomePageState.build\")]),t._v(\"方法中的\"),n(\"code\",[t._v(\"Column\")]),t._v(\"的子widget中添加一个按钮（\"),n(\"code\",[t._v(\"FlatButton\")]),t._v(\"） :\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      mainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n         child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"open new route\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n         textColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n         onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//导航到新路由   \")]),t._v(\"\\n          Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"push\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n           \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MaterialPageRoute\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"NewRoute\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n           \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n         \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n       \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们添加了一个打开新路由的按钮，并将按钮文字颜色设置为蓝色，点击该按钮后就会打开新的路由页面，效果如图2-2和2-3所示。\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(579),alt:\"图2-2\"}}),t._v(\" \"),n(\"img\",{attrs:{src:s(580),alt:\"图2-3\"}})])])]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_2-2-2-materialpageroute\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_2-2-2-materialpageroute\"}},[t._v(\"#\")]),t._v(\" 2.2.2 MaterialPageRoute\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"MaterialPageRoute\")]),t._v(\"继承自\"),n(\"code\",[t._v(\"PageRoute\")]),t._v(\"类，\"),n(\"code\",[t._v(\"PageRoute\")]),t._v(\"类是一个抽象类，表示占有整个屏幕空间的一个模态路由页面，它还定义了路由构建及切换时过渡动画的相关接口及属性。\"),n(\"code\",[t._v(\"MaterialPageRoute\")]),t._v(\" 是Material组件库提供的组件，它可以针对不同平台，实现与平台页面切换动画风格一致的路由切换动画：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[t._v(\"对于Android，当打开新页面时，新的页面会从屏幕底部滑动到屏幕顶部；当关闭页面时，当前页面会从屏幕顶部滑动到屏幕底部后消失，同时上一个页面会显示到屏幕上。\")]),t._v(\" \"),n(\"li\",[t._v(\"对于iOS，当打开页面时，新的页面会从屏幕右侧边缘一致滑动到屏幕左边，直到新页面全部显示到屏幕上，而上一个页面则会从当前屏幕滑动到屏幕左侧而消失；当关闭页面时，正好相反，当前页面会从屏幕右侧滑出，同时上一个页面会从屏幕左侧滑入。\")])]),t._v(\" \"),n(\"p\",[t._v(\"下面我们介绍一下\"),n(\"code\",[t._v(\"MaterialPageRoute\")]),t._v(\" 构造函数的各个参数的意义：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MaterialPageRoute\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    WidgetBuilder builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    RouteSettings settings\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    bool maintainState \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    bool fullscreenDialog \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"builder\")]),t._v(\" 是一个WidgetBuilder类型的回调函数，它的作用是构建路由页面的具体内容，返回值是一个widget。我们通常要实现此回调，返回新路由的实例。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"settings\")]),t._v(\" 包含路由的配置信息，如路由名称、是否初始路由（首页）。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"maintainState\")]),t._v(\"：默认情况下，当入栈一个新路由时，原来的路由仍然会被保存在内存中，如果想在路由没用的时候释放其所占用的所有资源，可以设置\"),n(\"code\",[t._v(\"maintainState\")]),t._v(\"为false。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"fullscreenDialog\")]),t._v(\"表示新的路由页面是否是一个全屏的模态对话框，在iOS中，如果\"),n(\"code\",[t._v(\"fullscreenDialog\")]),t._v(\"为\"),n(\"code\",[t._v(\"true\")]),t._v(\"，新页面将会从屏幕底部滑入（而不是水平方向）。\")])]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[t._v(\"如果想自定义路由切换动画，可以自己继承PageRoute来实现，我们将在后面介绍动画时，实现一个自定义的路由组件。\")])]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_2-2-3-navigator\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_2-2-3-navigator\"}},[t._v(\"#\")]),t._v(\" 2.2.3 Navigator\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"Navigator\")]),t._v(\"是一个路由管理的组件，它提供了打开和退出路由页方法。\"),n(\"code\",[t._v(\"Navigator\")]),t._v(\"通过一个栈来管理活动路由集合。通常当前屏幕显示的页面就是栈顶的路由。\"),n(\"code\",[t._v(\"Navigator\")]),t._v(\"提供了一系列方法来管理路由栈，在此我们只介绍其最常用的两个方法：\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"future-push-buildcontext-context-route-route\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#future-push-buildcontext-context-route-route\"}},[t._v(\"#\")]),t._v(\" Future  push(BuildContext context, Route route)\")]),t._v(\" \"),n(\"p\",[t._v(\"将给定的路由入栈（即打开新的页面），返回值是一个\"),n(\"code\",[t._v(\"Future\")]),t._v(\"对象，用以接收新路由出栈（即关闭）时的返回数据。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"bool-pop-buildcontext-context-result\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#bool-pop-buildcontext-context-result\"}},[t._v(\"#\")]),t._v(\" bool  pop(BuildContext context, [ result ])\")]),t._v(\" \"),n(\"p\",[t._v(\"将栈顶路由出栈，\"),n(\"code\",[t._v(\"result\")]),t._v(\"为页面关闭时返回给上一个页面的数据。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"Navigator\")]),t._v(\" 还有很多其它方法，如\"),n(\"code\",[t._v(\"Navigator.replace\")]),t._v(\"、\"),n(\"code\",[t._v(\"Navigator.popUntil\")]),t._v(\"等，详情请参考API文档或SDK源码注释，在此不再赘述。下面我们还需要介绍一下路由相关的另一个概念“命名路由”。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"实例方法\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#实例方法\"}},[t._v(\"#\")]),t._v(\" 实例方法\")]),t._v(\" \"),n(\"p\",[t._v(\"Navigator类中第一个参数为context的\"),n(\"strong\",[t._v(\"静态方法\")]),t._v(\"都对应一个Navigator的\"),n(\"strong\",[t._v(\"实例方法\")]),t._v(\"， 比如\"),n(\"code\",[t._v(\"Navigator.push(BuildContext context, Route route)\")]),t._v(\"等价于\"),n(\"code\",[t._v(\"Navigator.of(context).push(Route route)\")]),t._v(\" ，下面命名路由相关的方法也是一样的。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_2-2-4-路由传值\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_2-2-4-路由传值\"}},[t._v(\"#\")]),t._v(\" 2.2.4 路由传值\")]),t._v(\" \"),n(\"p\",[t._v(\"很多时候，在路由跳转时我们需要带一些参数，比如打开商品详情页时，我们需要带一个商品id，这样商品详情页才知道展示哪个商品信息；又比如我们在填写订单时需要选择收货地址，打开地址选择页并选择地址后，可以将用户选择的地址返回到订单页等等。下面我们通过一个简单的示例来演示新旧路由如何传参。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"示例\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),n(\"p\",[t._v(\"我们创建一个\"),n(\"code\",[t._v(\"TipRoute\")]),t._v(\"路由，它接受一个提示文本参数，负责将传入它的文本显示在页面上，另外\"),n(\"code\",[t._v(\"TipRoute\")]),t._v(\"中我们添加一个“返回”按钮，点击后在返回上一个路由的同时会带上一个返回参数，下面我们看一下实现代码。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"TipRoute\")]),t._v(\"实现代码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TipRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TipRoute\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 接收一个text参数\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      appBar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"提示\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      body\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"18\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"我是返回值\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"返回\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"下面是打开新路由\"),n(\"code\",[t._v(\"TipRoute\")]),t._v(\"的代码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"RouterTestRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 打开`TipRoute`，并等待返回结果\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" result \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"push\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MaterialPageRoute\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TipRoute\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 路由参数\")]),t._v(\"\\n                  text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"我是提示xxxx\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//输出`TipRoute`路由返回结果\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"路由返回值: $result\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"打开提示页\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行上面代码，点击\"),n(\"code\",[t._v(\"RouterTestRoute\")]),t._v(\"页的“打开提示页”按钮，会打开\"),n(\"code\",[t._v(\"TipRoute\")]),t._v(\"页，运行效果如图2-4所示下：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(581),alt:\"图2-4\"}})]),t._v(\" \"),n(\"p\",[t._v(\"需要说明：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"p\",[t._v(\"提示文案“我是提示xxxx”是通过\"),n(\"code\",[t._v(\"TipRoute\")]),t._v(\"的\"),n(\"code\",[t._v(\"text\")]),t._v(\"参数传递给新路由页的。我们可以通过等待\"),n(\"code\",[t._v(\"Navigator.push(…)\")]),t._v(\"返回的\"),n(\"code\",[t._v(\"Future\")]),t._v(\"来获取新路由的返回数据。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"在\"),n(\"code\",[t._v(\"TipRoute\")]),t._v(\"页中有两种方式可以返回到上一页；第一种方式时直接点击导航栏返回箭头，第二种方式是点击页面中的“返回”按钮。这两种返回方式的区别是前者不会返回数据给上一个路由，而后者会。下面是分别点击页面中的返回按钮和导航栏返回箭头后，\"),n(\"code\",[t._v(\"RouterTestRoute\")]),t._v(\"页中\"),n(\"code\",[t._v(\"print\")]),t._v(\"方法在控制台输出的内容：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language- extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[n(\"code\",[t._v(\"I/flutter (27896): 路由返回值: 我是返回值\\nI/flutter (27896): 路由返回值: null\\n\")])])])])]),t._v(\" \"),n(\"p\",[t._v(\"上面介绍的是非命名路由的传值方式，命名路由的传值方式会有所不同，我们会在下面介绍命名路由时介绍。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_2-2-5-命名路由\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_2-2-5-命名路由\"}},[t._v(\"#\")]),t._v(\" 2.2.5 命名路由\")]),t._v(\" \"),n(\"p\",[t._v(\"所谓“命名路由”（Named Route）即有名字的路由，我们可以先给路由起一个名字，然后就可以通过路由名字直接打开新的路由了，这为路由管理带来了一种直观、简单的方式。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"路由表\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#路由表\"}},[t._v(\"#\")]),t._v(\" 路由表\")]),t._v(\" \"),n(\"p\",[t._v(\"要想使用命名路由，我们必须先提供并注册一个路由表（routing table），这样应用程序才知道哪个名字与哪个路由组件相对应。其实注册路由表就是给路由起名字，路由表的定义如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Map\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" WidgetBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" routes\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"它是一个\"),n(\"code\",[t._v(\"Map\")]),t._v(\"，key为路由的名字，是个字符串；value是个\"),n(\"code\",[t._v(\"builder\")]),t._v(\"回调函数，用于生成相应的路由widget。我们在通过路由名字打开新路由时，应用会根据路由名字在路由表中查找到对应的\"),n(\"code\",[t._v(\"WidgetBuilder\")]),t._v(\"回调函数，然后调用该回调函数生成路由widget并返回。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"注册路由表\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#注册路由表\"}},[t._v(\"#\")]),t._v(\" 注册路由表\")]),t._v(\" \"),n(\"p\",[t._v(\"路由表的注册方式很简单，我们回到之前“计数器”的示例，然后在\"),n(\"code\",[t._v(\"MyApp\")]),t._v(\"类的\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法中找到\"),n(\"code\",[t._v(\"MaterialApp\")]),t._v(\"，添加\"),n(\"code\",[t._v(\"routes\")]),t._v(\"属性，代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MaterialApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Flutter Demo'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  theme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ThemeData\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    primarySwatch\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//注册路由表\")]),t._v(\"\\n  routes\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"new_page\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"NewRoute\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 省略其它路由注册信息\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  home\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyHomePage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Flutter Demo Home Page'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"现在我们就完成了路由表的注册。上面的代码中\"),n(\"code\",[t._v(\"home\")]),t._v(\"路由并没有使用命名路由，如果我们也想将\"),n(\"code\",[t._v(\"home\")]),t._v(\"注册为命名路由应该怎么做呢？其实很简单，直接看代码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MaterialApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Flutter Demo'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  initialRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"/\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v('//名为\"/\"的路由作为应用的home(首页)')]),t._v(\"\\n  theme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ThemeData\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    primarySwatch\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//注册路由表\")]),t._v(\"\\n  routes\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"new_page\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"NewRoute\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"/\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyHomePage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Flutter Demo Home Page'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//注册首页路由\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"可以看到，我们只需在路由表中注册一下\"),n(\"code\",[t._v(\"MyHomePage\")]),t._v(\"路由，然后将其名字作为\"),n(\"code\",[t._v(\"MaterialApp\")]),t._v(\"的\"),n(\"code\",[t._v(\"initialRoute\")]),t._v(\"属性值即可，该属性决定应用的初始路由页是哪一个命名路由。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"通过路由名打开新路由页\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#通过路由名打开新路由页\"}},[t._v(\"#\")]),t._v(\" 通过路由名打开新路由页\")]),t._v(\" \"),n(\"p\",[t._v(\"要通过路由名称来打开新路由，可以使用\"),n(\"code\",[t._v(\"Navigator\")]),t._v(\" 的\"),n(\"code\",[t._v(\"pushNamed\")]),t._v(\"方法：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Future \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pushNamed\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" String routeName\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"Object arguments\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"Navigator\")]),t._v(\" 除了\"),n(\"code\",[t._v(\"pushNamed\")]),t._v(\"方法，还有\"),n(\"code\",[t._v(\"pushReplacementNamed\")]),t._v(\"等其他管理命名路由的方法，读者可以自行查看API文档。接下来我们通过路由名来打开新的路由页，修改\"),n(\"code\",[t._v(\"FlatButton\")]),t._v(\"的\"),n(\"code\",[t._v(\"onPressed\")]),t._v(\"回调代码，改为：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pushNamed\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"new_page\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//Navigator.push(context,\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//  MaterialPageRoute(builder: (context) {\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//  return NewRoute();\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//}));  \")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"热重载应用，再次点击“open new route”按钮，依然可以打开新的路由页。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"命名路由参数传递\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#命名路由参数传递\"}},[t._v(\"#\")]),t._v(\" 命名路由参数传递\")]),t._v(\" \"),n(\"p\",[t._v(\"在Flutter最初的版本中，命名路由是不能传递参数的，后来才支持了参数；下面展示命名路由如何传递并获取路由参数：\")]),t._v(\" \"),n(\"p\",[t._v(\"我们先注册一个路由：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\" routes\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"new_page\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"EchoRoute\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"在路由页通过\"),n(\"code\",[t._v(\"RouteSetting\")]),t._v(\"对象获取路由参数：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"EchoRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//获取路由参数  \")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" args\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"ModalRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"settings\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"arguments\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//...省略无关代码\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"在打开路由时传递参数\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Navigator\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pushNamed\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"new_page\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" arguments\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"hi\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"h3\",{attrs:{id:\"适配\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#适配\"}},[t._v(\"#\")]),t._v(\" 适配\")]),t._v(\" \"),n(\"p\",[t._v(\"假设我们也想将上面路由传参示例中的\"),n(\"code\",[t._v(\"TipRoute\")]),t._v(\"路由页注册到路由表中，以便也可以通过路由名来打开它。但是，由于\"),n(\"code\",[t._v(\"TipRoute\")]),t._v(\"接受一个\"),n(\"code\",[t._v(\"text\")]),t._v(\" 参数，我们如何在不改变\"),n(\"code\",[t._v(\"TipRoute\")]),t._v(\"源码的前提下适配这种情况？其实很简单：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MaterialApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n  routes\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"tip2\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TipRoute\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ModalRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"settings\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"arguments\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"h2\",{attrs:{id:\"_2-2-6-路由生成钩子\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_2-2-6-路由生成钩子\"}},[t._v(\"#\")]),t._v(\" 2.2.6 路由生成钩子\")]),t._v(\" \"),n(\"p\",[t._v(\"假设我们要开发一个电商APP，当用户没有登录时可以看店铺、商品等信息，但交易记录、购物车、用户个人信息等页面需要登录后才能看。为了实现上述功能，我们需要在打开每一个路由页前判断用户登录状态！如果每次打开路由前我们都需要去判断一下将会非常麻烦，那有什么更好的办法吗？答案是有！\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"MaterialApp\")]),t._v(\"有一个\"),n(\"code\",[t._v(\"onGenerateRoute\")]),t._v(\"属性，它在打开命名路由时可能会被调用，之所以说可能，是因为当调用\"),n(\"code\",[t._v(\"Navigator.pushNamed(...)\")]),t._v(\"打开命名路由时，如果指定的路由名在路由表中已注册，则会调用路由表中的\"),n(\"code\",[t._v(\"builder\")]),t._v(\"函数来生成路由组件；如果路由表中没有注册，才会调用\"),n(\"code\",[t._v(\"onGenerateRoute\")]),t._v(\"来生成路由。\"),n(\"code\",[t._v(\"onGenerateRoute\")]),t._v(\"回调签名如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Route\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"Function\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"RouteSettings settings\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"有了\"),n(\"code\",[t._v(\"onGenerateRoute\")]),t._v(\"回调，要实现上面控制页面权限的功能就非常容易：我们放弃使用路由表，取而代之的是提供一个\"),n(\"code\",[t._v(\"onGenerateRoute\")]),t._v(\"回调，然后在该回调中进行统一的权限控制，如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MaterialApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n  onGenerateRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"RouteSettings settings\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\t  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MaterialPageRoute\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\t\\t   String routeName \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" settings\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"name\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n       \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 如果访问的路由页需要登录，但当前未登录，则直接返回登录页路由，\")]),t._v(\"\\n       \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 引导用户登录；其它情况则正常打开路由。\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"blockquote\",[n(\"p\",[t._v(\"注意，\"),n(\"code\",[t._v(\"onGenerateRoute\")]),t._v(\"只会对命名路由生效。\")])]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_2-2-7-总结\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_2-2-7-总结\"}},[t._v(\"#\")]),t._v(\" 2.2.7 总结\")]),t._v(\" \"),n(\"p\",[t._v(\"本章先介绍了Flutter中路由管理、传参的方式，然后又着重介绍了命名路由相关内容。在此需要说明一点，由于命名路由只是一种可选的路由管理方式，在实际开发中，读者可能心中会犹豫到底使用哪种路由管理方式。在此，根据笔者经验，建议读者最好统一使用命名路由的管理方式，这将会带来如下好处：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"语义化更明确。\")]),t._v(\" \"),n(\"li\",[t._v(\"代码更好维护；如果使用匿名路由，则必须在调用\"),n(\"code\",[t._v(\"Navigator.push\")]),t._v(\"的地方创建新路由页，这样不仅需要import新路由页的dart文件，而且这样的代码将会非常分散。\")]),t._v(\" \"),n(\"li\",[t._v(\"可以通过\"),n(\"code\",[t._v(\"onGenerateRoute\")]),t._v(\"做一些全局的路由跳转前置处理逻辑。\")])]),t._v(\" \"),n(\"p\",[t._v(\"综上所述，笔者比较建议使用命名路由，当然这并不是什么金科玉律，读者可以根据自己偏好或实际情况来决定。\")]),t._v(\" \"),n(\"p\",[t._v(\"另外，还有一些关于路由管理的内容我们没有介绍，比如路由MaterialApp中还有\"),n(\"code\",[t._v(\"navigatorObservers\")]),t._v(\"和\"),n(\"code\",[t._v(\"onUnknownRoute\")]),t._v(\"两个回调属性，前者可以监听所有路由跳转动作，后者在打开一个不存在的命名路由时会被调用，由于这些功能并不常用，而且也比较简单，我们便不再花费篇幅来介绍了，读者可以自行查看API文档。\")])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/53.56438d06.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[53],{629:function(t,s,a){t.exports=a.p+\"assets/img/4-6.563f1017.png\"},630:function(t,s,a){t.exports=a.p+\"assets/img/4-7.632eedaf.png\"},631:function(t,s){t.exports=\"data:image/png;base64,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\"},899:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_4-4-流式布局\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_4-4-流式布局\"}},[t._v(\"#\")]),t._v(\" 4.4 流式布局\")]),t._v(\" \"),n(\"p\",[t._v(\"在介绍Row和Colum时，如果子widget超出屏幕范围，则会报溢出错误，如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"xxx\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图4-6所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(629),alt:\"图4-6\"}})]),t._v(\" \"),n(\"p\",[t._v(\"可以看到，右边溢出部分报错。这是因为Row默认只有一行，如果超出屏幕不会折行。我们把超出屏幕显示范围会自动折行的布局称为流式布局。Flutter中通过\"),n(\"code\",[t._v(\"Wrap\")]),t._v(\"和\"),n(\"code\",[t._v(\"Flow\")]),t._v(\"来支持流式布局，将上例中的Row换成Wrap后溢出部分则会自动折行，下面我们分别介绍\"),n(\"code\",[t._v(\"Wrap\")]),t._v(\"和\"),n(\"code\",[t._v(\"Flow\")]),t._v(\".\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_4-4-1-wrap\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_4-4-1-wrap\"}},[t._v(\"#\")]),t._v(\" 4.4.1 Wrap\")]),t._v(\" \"),n(\"p\",[t._v(\"下面是Wrap的定义:\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Wrap\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"direction \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Axis\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"horizontal\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"alignment \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" WrapAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"spacing \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"runAlignment \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" WrapAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"runSpacing \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"crossAxisAlignment \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" WrapCrossAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"textDirection\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"verticalDirection \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" VerticalDirection\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"down\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  List\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" children \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们可以看到Wrap的很多属性在\"),n(\"code\",[t._v(\"Row\")]),t._v(\"（包括\"),n(\"code\",[t._v(\"Flex\")]),t._v(\"和\"),n(\"code\",[t._v(\"Column\")]),t._v(\"）中也有，如\"),n(\"code\",[t._v(\"direction\")]),t._v(\"、\"),n(\"code\",[t._v(\"crossAxisAlignment\")]),t._v(\"、\"),n(\"code\",[t._v(\"textDirection\")]),t._v(\"、\"),n(\"code\",[t._v(\"verticalDirection\")]),t._v(\"等，这些参数意义是相同的，我们不再重复介绍，读者可以查阅前面介绍\"),n(\"code\",[t._v(\"Row\")]),t._v(\"的部分。读者可以认为\"),n(\"code\",[t._v(\"Wrap\")]),t._v(\"和\"),n(\"code\",[t._v(\"Flex\")]),t._v(\"（包括\"),n(\"code\",[t._v(\"Row\")]),t._v(\"和\"),n(\"code\",[t._v(\"Column\")]),t._v(\"）除了超出显示范围后\"),n(\"code\",[t._v(\"Wrap\")]),t._v(\"会折行外，其它行为基本相同。下面我们看一下\"),n(\"code\",[t._v(\"Wrap\")]),t._v(\"特有的几个属性：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"spacing\")]),t._v(\"：主轴方向子widget的间距\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"runSpacing\")]),t._v(\"：纵轴方向的间距\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"runAlignment\")]),t._v(\"：纵轴方向的对齐方式\")])]),t._v(\" \"),n(\"p\",[t._v(\"下面看一个示例子：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Wrap\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  spacing\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"8.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 主轴(水平)方向间距\")]),t._v(\"\\n  runSpacing\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"4.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 纵轴（垂直）方向间距\")]),t._v(\"\\n  alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" WrapAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//沿主轴方向居中\")]),t._v(\"\\n  children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Chip\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      avatar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CircleAvatar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"backgroundColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'A'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      label\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Hamilton'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Chip\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      avatar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CircleAvatar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"backgroundColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'M'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      label\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Lafayette'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Chip\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      avatar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CircleAvatar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"backgroundColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'H'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      label\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Mulligan'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Chip\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      avatar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CircleAvatar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"backgroundColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'J'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      label\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Laurens'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图4-7所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(630),alt:\"图4-7\"}})]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_4-4-2-flow\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_4-4-2-flow\"}},[t._v(\"#\")]),t._v(\" 4.4.2 Flow\")]),t._v(\" \"),n(\"p\",[t._v(\"我们一般很少会使用\"),n(\"code\",[t._v(\"Flow\")]),t._v(\"，因为其过于复杂，需要自己实现子widget的位置转换，在很多场景下首先要考虑的是\"),n(\"code\",[t._v(\"Wrap\")]),t._v(\"是否满足需求。\"),n(\"code\",[t._v(\"Flow\")]),t._v(\"主要用于一些需要自定义布局策略或性能要求较高(如动画中)的场景。\"),n(\"code\",[t._v(\"Flow\")]),t._v(\"有如下优点：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[t._v(\"性能好；\"),n(\"code\",[t._v(\"Flow\")]),t._v(\"是一个对子组件尺寸以及位置调整非常高效的控件，\"),n(\"code\",[t._v(\"Flow\")]),t._v(\"用转换矩阵在对子组件进行位置调整的时候进行了优化：在\"),n(\"code\",[t._v(\"Flow\")]),t._v(\"定位过后，如果子组件的尺寸或者位置发生了变化，在\"),n(\"code\",[t._v(\"FlowDelegate\")]),t._v(\"中的\"),n(\"code\",[t._v(\"paintChildren()\")]),t._v(\"方法中调用\"),n(\"code\",[t._v(\"context.paintChild\")]),t._v(\" 进行重绘，而\"),n(\"code\",[t._v(\"context.paintChild\")]),t._v(\"在重绘时使用了转换矩阵，并没有实际调整组件位置。\")]),t._v(\" \"),n(\"li\",[t._v(\"灵活；由于我们需要自己实现\"),n(\"code\",[t._v(\"FlowDelegate\")]),t._v(\"的\"),n(\"code\",[t._v(\"paintChildren()\")]),t._v(\"方法，所以我们需要自己计算每一个组件的位置，因此，可以自定义布局策略。\")])]),t._v(\" \"),n(\"p\",[t._v(\"缺点：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[t._v(\"使用复杂。\")]),t._v(\" \"),n(\"li\",[t._v(\"不能自适应子组件大小，必须通过指定父容器大小或实现\"),n(\"code\",[t._v(\"TestFlowDelegate\")]),t._v(\"的\"),n(\"code\",[t._v(\"getSize\")]),t._v(\"返回固定大小。\")])]),t._v(\" \"),n(\"p\",[t._v(\"示例：\")]),t._v(\" \"),n(\"p\",[t._v(\"我们对六个色块进行自定义流式布局：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Flow\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  delegate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TestFlowDelegate\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"margin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"10.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"green\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"  color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"yellow\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"brown\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"  color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"purple\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"实现TestFlowDelegate:\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TestFlowDelegate\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FlowDelegate\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  EdgeInsets margin \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"zero\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TestFlowDelegate\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"margin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"paintChildren\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"FlowPaintingContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" x \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" margin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"left\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" y \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" margin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"top\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//计算每一个子widget的位置  \")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"for\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int i \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" i \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"childCount\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" i\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"++\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" w \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getChildSize\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"i\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"width \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" x \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" margin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"right\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"w \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"paintChild\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"i\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            transform\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Matrix4\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"translationValues\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                x\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" y\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        x \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" w \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" margin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"left\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        x \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" margin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"left\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        y \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+=\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getChildSize\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"i\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"height \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" margin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"top \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" margin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"bottom\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//绘制子widget(有优化)  \")]),t._v(\"\\n        context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"paintChild\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"i\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            transform\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Matrix4\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"translationValues\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                x\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" y\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n         x \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+=\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getChildSize\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"i\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"width \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" margin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"left \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" margin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"right\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getSize\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BoxConstraints constraints\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//指定Flow的大小  \")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Size\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"infinity\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  bool \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"shouldRepaint\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"FlowDelegate oldDelegate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" oldDelegate \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果见图4-8：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(631),alt:\"图4-8\"}})]),t._v(\" \"),n(\"p\",[t._v(\"可以看到我们主要的任务就是实现\"),n(\"code\",[t._v(\"paintChildren\")]),t._v(\"，它的主要任务是确定每个子widget位置。由于Flow不能自适应子widget的大小，我们通过在\"),n(\"code\",[t._v(\"getSize\")]),t._v(\"返回一个固定大小来指定Flow的大小。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/54.4afb8008.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[54],{659:function(t,s,a){t.exports=a.p+\"assets/img/6-9.865c35f8.png\"},660:function(t,s,a){t.exports=a.p+\"assets/img/6-10.202cef73.png\"},661:function(t,s,a){t.exports=a.p+\"assets/img/6-11.a491f457.png\"},910:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_6-4-gridview\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_6-4-gridview\"}},[t._v(\"#\")]),t._v(\" 6.4 GridView\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"GridView\")]),t._v(\"可以构建一个二维网格列表，其默认构造函数定义如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GridView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  Axis scrollDirection \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Axis\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"vertical\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  bool reverse \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  ScrollController controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  bool primary\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  ScrollPhysics physics\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  bool shrinkWrap \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  EdgeInsetsGeometry padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" SliverGridDelegate gridDelegate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//控制子widget layout的委托\")]),t._v(\"\\n  bool addAutomaticKeepAlives \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  bool addRepaintBoundaries \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  double cacheExtent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  List\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" children \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们可以看到，\"),n(\"code\",[t._v(\"GridView\")]),t._v(\"和\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"的大多数参数都是相同的，它们的含义也都相同的，如有疑惑读者可以翻阅ListView一节，在此不再赘述。我们唯一需要关注的是\"),n(\"code\",[t._v(\"gridDelegate\")]),t._v(\"参数，类型是\"),n(\"code\",[t._v(\"SliverGridDelegate\")]),t._v(\"，它的作用是控制\"),n(\"code\",[t._v(\"GridView\")]),t._v(\"子组件如何排列(layout)。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"SliverGridDelegate\")]),t._v(\"是一个抽象类，定义了\"),n(\"code\",[t._v(\"GridView\")]),t._v(\" Layout相关接口，子类需要通过实现它们来实现具体的布局算法。Flutter中提供了两个\"),n(\"code\",[t._v(\"SliverGridDelegate\")]),t._v(\"的子类\"),n(\"code\",[t._v(\"SliverGridDelegateWithFixedCrossAxisCount\")]),t._v(\"和\"),n(\"code\",[t._v(\"SliverGridDelegateWithMaxCrossAxisExtent\")]),t._v(\"，我们可以直接使用，下面我们分别来介绍一下它们。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"slivergriddelegatewithfixedcrossaxiscount\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#slivergriddelegatewithfixedcrossaxiscount\"}},[t._v(\"#\")]),t._v(\" SliverGridDelegateWithFixedCrossAxisCount\")]),t._v(\" \"),n(\"p\",[t._v(\"该子类实现了一个横轴为固定数量子元素的layout算法，其构造函数为：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SliverGridDelegateWithFixedCrossAxisCount\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" double crossAxisCount\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n  double mainAxisSpacing \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  double crossAxisSpacing \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  double childAspectRatio \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"crossAxisCount\")]),t._v(\"：横轴子元素的数量。此属性值确定后子元素在横轴的长度就确定了，即ViewPort横轴长度除以\"),n(\"code\",[t._v(\"crossAxisCount\")]),t._v(\"的商。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"mainAxisSpacing\")]),t._v(\"：主轴方向的间距。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"crossAxisSpacing\")]),t._v(\"：横轴方向子元素的间距。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"childAspectRatio\")]),t._v(\"：子元素在横轴长度和主轴长度的比例。由于\"),n(\"code\",[t._v(\"crossAxisCount\")]),t._v(\"指定后，子元素横轴长度就确定了，然后通过此参数值就可以确定子元素在主轴的长度。\")])]),t._v(\" \"),n(\"p\",[t._v(\"可以发现，子元素的大小是通过\"),n(\"code\",[t._v(\"crossAxisCount\")]),t._v(\"和\"),n(\"code\",[t._v(\"childAspectRatio\")]),t._v(\"两个参数共同决定的。注意，这里的子元素指的是子组件的最大显示空间，注意确保子组件的实际大小不要超出子元素的空间。\")]),t._v(\" \"),n(\"p\",[t._v(\"下面看一个例子：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GridView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  gridDelegate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SliverGridDelegateWithFixedCrossAxisCount\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      crossAxisCount\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//横轴三个子widget\")]),t._v(\"\\n      childAspectRatio\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.0\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//宽高比为1时，子widget\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"ac_unit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"airport_shuttle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"all_inclusive\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"beach_access\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cake\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"free_breakfast\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"img\",{attrs:{src:a(659),alt:\"图6-9\"}})]),t._v(\" \"),n(\"h4\",{attrs:{id:\"gridview-count\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#gridview-count\"}},[t._v(\"#\")]),t._v(\" GridView.count\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"GridView.count\")]),t._v(\"构造函数内部使用了\"),n(\"code\",[t._v(\"SliverGridDelegateWithFixedCrossAxisCount\")]),t._v(\"，我们通过它可以快速的创建横轴固定数量子元素的\"),n(\"code\",[t._v(\"GridView\")]),t._v(\"，上面的示例代码等价于：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"GridView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"count\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \\n  crossAxisCount\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  childAspectRatio\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"ac_unit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"airport_shuttle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"all_inclusive\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"beach_access\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cake\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"free_breakfast\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"h3\",{attrs:{id:\"slivergriddelegatewithmaxcrossaxisextent\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#slivergriddelegatewithmaxcrossaxisextent\"}},[t._v(\"#\")]),t._v(\" SliverGridDelegateWithMaxCrossAxisExtent\")]),t._v(\" \"),n(\"p\",[t._v(\"该子类实现了一个横轴子元素为固定最大长度的layout算法，其构造函数为：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SliverGridDelegateWithMaxCrossAxisExtent\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  double maxCrossAxisExtent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  double mainAxisSpacing \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  double crossAxisSpacing \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  double childAspectRatio \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"maxCrossAxisExtent\")]),t._v(\"为子元素在横轴上的最大长度，之所以是“最大”长度，是\"),n(\"strong\",[t._v(\"因为横轴方向每个子元素的长度仍然是等分的\")]),t._v(\"，举个例子，如果ViewPort的横轴长度是450，那么当\"),n(\"code\",[t._v(\"maxCrossAxisExtent\")]),t._v(\"的值在区间[450/4，450/3)内的话，子元素最终实际长度都为112.5，而\"),n(\"code\",[t._v(\"childAspectRatio\")]),t._v(\"所指的子元素横轴和主轴的长度比为\"),n(\"strong\",[t._v(\"最终的长度比\")]),t._v(\"。其它参数和\"),n(\"code\",[t._v(\"SliverGridDelegateWithFixedCrossAxisCount\")]),t._v(\"相同。\")]),t._v(\" \"),n(\"p\",[t._v(\"下面我们看一个例子：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GridView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"zero\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  gridDelegate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SliverGridDelegateWithMaxCrossAxisExtent\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      maxCrossAxisExtent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"120.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      childAspectRatio\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2.0\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//宽高比为2\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"ac_unit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"airport_shuttle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"all_inclusive\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"beach_access\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cake\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"free_breakfast\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"img\",{attrs:{src:a(660),alt:\"图6-10\"}})]),t._v(\" \"),n(\"h4\",{attrs:{id:\"gridview-extent\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#gridview-extent\"}},[t._v(\"#\")]),t._v(\" GridView.extent\")]),t._v(\" \"),n(\"p\",[t._v(\"GridView.extent构造函数内部使用了SliverGridDelegateWithMaxCrossAxisExtent，我们通过它可以快速的创建纵轴子元素为固定最大长度的的GridView，上面的示例代码等价于：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"GridView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"extent\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n   maxCrossAxisExtent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"120.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n   childAspectRatio\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n   children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"ac_unit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"airport_shuttle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"all_inclusive\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"beach_access\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cake\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"free_breakfast\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"h3\",{attrs:{id:\"gridview-builder\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#gridview-builder\"}},[t._v(\"#\")]),t._v(\" GridView.builder\")]),t._v(\" \"),n(\"p\",[t._v(\"上面我们介绍的GridView都需要一个widget数组作为其子元素，这些方式都会提前将所有子widget都构建好，所以只适用于子widget数量比较少时，当子widget比较多时，我们可以通过\"),n(\"code\",[t._v(\"GridView.builder\")]),t._v(\"来动态创建子widget。\"),n(\"code\",[t._v(\"GridView.builder\")]),t._v(\" 必须指定的参数有两个：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"GridView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" SliverGridDelegate gridDelegate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" IndexedWidgetBuilder itemBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"其中\"),n(\"code\",[t._v(\"itemBuilder\")]),t._v(\"为子widget构建器。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"示例\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),n(\"p\",[t._v(\"假设我们需要从一个异步数据源（如网络）分批获取一些\"),n(\"code\",[t._v(\"Icon\")]),t._v(\"，然后用\"),n(\"code\",[t._v(\"GridView\")]),t._v(\"来展示：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"InfiniteGridView\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _InfiniteGridViewState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_InfiniteGridViewState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_InfiniteGridViewState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"InfiniteGridView\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\n  List\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"IconData\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _icons \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//保存Icon数据\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 初始化数据  \")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_retrieveIcons\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" GridView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        gridDelegate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SliverGridDelegateWithFixedCrossAxisCount\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            crossAxisCount\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//每行三列\")]),t._v(\"\\n            childAspectRatio\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.0\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//显示区域宽高相等\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        itemCount\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"length\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        itemBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//如果显示到最后一个并且Icon总数小于200时继续获取数据\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"index \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" _icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"length \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\" _icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"length \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_retrieveIcons\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//模拟异步获取数据\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_retrieveIcons\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Future\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"delayed\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"milliseconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        _icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addAll\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"ac_unit\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"airport_shuttle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"all_inclusive\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"beach_access\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cake\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"free_breakfast\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"_retrieveIcons()\")]),t._v(\"：在此方法中我们通过\"),n(\"code\",[t._v(\"Future.delayed\")]),t._v(\"来模拟从异步数据源获取数据，每次获取数据需要200毫秒，获取成功后将新数据添加到_icons，然后调用setState重新构建。\")]),t._v(\" \"),n(\"li\",[t._v(\"在itemBuilder中，如果显示到最后一个时，判断是否需要继续获取数据，然后返回一个Icon。\")])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"更多\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#更多\"}},[t._v(\"#\")]),t._v(\" 更多\")]),t._v(\" \"),n(\"p\",[t._v(\"Flutter的\"),n(\"code\",[t._v(\"GridView\")]),t._v(\"默认子元素显示空间是相等的，但在实际开发中，你可能会遇到子元素大小不等的情况，如下面这样的布局：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(661),alt:\"图6-11\"}})]),t._v(\" \"),n(\"p\",[t._v(\"Pub上有一个包“flutter_staggered_grid_view” ，它实现了一个交错GridView的布局模型，可以很轻松的实现这种布局，详情读者可以自行了解。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/55.a7c27448.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[55],{669:function(t,s,a){t.exports=a.p+\"assets/img/6-14.1b612437.png\"},670:function(t,s,a){t.exports=a.p+\"assets/img/6-15.00ac68b6.png\"},671:function(t,s,a){t.exports=a.p+\"assets/img/6-16.20d0839c.png\"},913:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_6-6-滚动监听及控制\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_6-6-滚动监听及控制\"}},[t._v(\"#\")]),t._v(\" 6.6 滚动监听及控制\")]),t._v(\" \"),n(\"p\",[t._v(\"在前几节中，我们介绍了Flutter中常用的可滚动组件，也说过可以用\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"来控制可滚动组件的滚动位置，本节先介绍一下\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"，然后以\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"为例，展示一下\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"的具体用法。最后，再介绍一下路由切换时如何来保存滚动位置。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_6-6-1-scrollcontroller\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_6-6-1-scrollcontroller\"}},[t._v(\"#\")]),t._v(\" 6.6.1 ScrollController\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"ScrollController\")]),t._v(\"构造函数如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ScrollController\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  double initialScrollOffset \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//初始滚动位置\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"keepScrollOffset \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//是否保存滚动位置\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们介绍一下\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"常用的属性和方法：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"offset\")]),t._v(\"：可滚动组件当前的滚动位置。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"jumpTo(double offset)\")]),t._v(\"、\"),n(\"code\",[t._v(\"animateTo(double offset,...)\")]),t._v(\"：这两个方法用于跳转到指定的位置，它们不同之处在于，后者在跳转时会执行一个动画，而前者不会。\")])]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"ScrollController\")]),t._v(\"还有一些属性和方法，我们将在后面原理部分解释。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"滚动监听\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#滚动监听\"}},[t._v(\"#\")]),t._v(\" 滚动监听\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"ScrollController\")]),t._v(\"间接继承自\"),n(\"code\",[t._v(\"Listenable\")]),t._v(\"，我们可以根据\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"来监听滚动事件，如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"h3\",{attrs:{id:\"示例\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),n(\"p\",[t._v(\"我们创建一个\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"，当滚动位置发生变化时，我们先打印出当前滚动位置，然后判断当前位置是否超过1000像素，如果超过则在屏幕右下角显示一个“返回顶部”的按钮，该按钮点击后可以使ListView恢复到初始位置；如果没有超过1000像素，则隐藏“返回顶部”按钮。代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ScrollControllerTestRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  ScrollControllerTestRouteState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ScrollControllerTestRouteState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ScrollControllerTestRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ScrollControllerTestRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  ScrollController _controller \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ScrollController\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  bool showToTopBtn \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//是否显示“返回到顶部”按钮\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//监听滚动事件，打印滚动位置\")]),t._v(\"\\n    _controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//打印滚动位置\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"offset \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1000\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\" showToTopBtn\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          showToTopBtn \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"offset \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1000\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\" showToTopBtn \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          showToTopBtn \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//为了避免内存泄露，需要调用_controller.dispose\")]),t._v(\"\\n    _controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      appBar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"滚动控制\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      body\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scrollbar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ListView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            itemCount\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            itemExtent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//列表项高度固定时，显式指定高度是一个好习惯(性能消耗小)\")]),t._v(\"\\n            controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            itemBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$index\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      floatingActionButton\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"showToTopBtn \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FloatingActionButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"arrow_upward\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//返回到顶部时执行动画\")]),t._v(\"\\n            _controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"animateTo\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                duration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"milliseconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                curve\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Curves\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"ease\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"代码说明已经包含在注释里，下面我们看看运行效果：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(669),alt:\"图6-14\"}}),n(\"img\",{attrs:{src:a(670),alt:\"图6-15\"}})]),t._v(\" \"),n(\"p\",[t._v(\"由于列表项高度为50像素，当滑动到第20个列表项后，右下角“返回顶部”按钮会显示，点击该按钮，ListView会在返回顶部的过程中执行一个滚动动画，动画时间是200毫秒，动画曲线是\"),n(\"code\",[t._v(\"Curves.ease\")]),t._v(\"，关于动画的详细内容我们将在后面“动画”一章中详细介绍。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"滚动位置恢复\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#滚动位置恢复\"}},[t._v(\"#\")]),t._v(\" 滚动位置恢复\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"PageStorage\")]),t._v(\"是一个用于保存页面(路由)相关数据的组件，它并不会影响子树的UI外观，其实，\"),n(\"code\",[t._v(\"PageStorage\")]),t._v(\"是一个功能型组件，它拥有一个存储桶（bucket），子树中的Widget可以通过指定不同的\"),n(\"code\",[t._v(\"PageStorageKey\")]),t._v(\"来存储各自的数据或状态。\")]),t._v(\" \"),n(\"p\",[t._v(\"每次滚动结束，可滚动组件都会将滚动位置\"),n(\"code\",[t._v(\"offset\")]),t._v(\"存储到\"),n(\"code\",[t._v(\"PageStorage\")]),t._v(\"中，当可滚动组件重新创建时再恢复。如果\"),n(\"code\",[t._v(\"ScrollController.keepScrollOffset\")]),t._v(\"为\"),n(\"code\",[t._v(\"false\")]),t._v(\"，则滚动位置将不会被存储，可滚动组件重新创建时会使用\"),n(\"code\",[t._v(\"ScrollController.initialScrollOffset\")]),t._v(\"；\"),n(\"code\",[t._v(\"ScrollController.keepScrollOffset\")]),t._v(\"为\"),n(\"code\",[t._v(\"true\")]),t._v(\"时，可滚动组件在\"),n(\"strong\",[t._v(\"第一次\")]),t._v(\"创建时，会滚动到\"),n(\"code\",[t._v(\"initialScrollOffset\")]),t._v(\"处，因为这时还没有存储过滚动位置。在接下来的滚动中就会存储、恢复滚动位置，而\"),n(\"code\",[t._v(\"initialScrollOffset\")]),t._v(\"会被忽略。\")]),t._v(\" \"),n(\"p\",[t._v(\"当一个路由中包含多个可滚动组件时，如果你发现在进行一些跳转或切换操作后，滚动位置不能正确恢复，这时你可以通过显式指定\"),n(\"code\",[t._v(\"PageStorageKey\")]),t._v(\"来分别跟踪不同的可滚动组件的位置，如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"PageStorageKey\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"PageStorageKey\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"不同的\"),n(\"code\",[t._v(\"PageStorageKey\")]),t._v(\"，需要不同的值，这样才可以为不同可滚动组件保存其滚动位置。\")]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[t._v(\"注意：一个路由中包含多个可滚动组件时，如果要分别跟踪它们的滚动位置，并非一定就得给他们分别提供\"),n(\"code\",[t._v(\"PageStorageKey\")]),t._v(\"。这是因为Scrollable本身是一个StatefulWidget，它的状态中也会保存当前滚动位置，所以，只要可滚动组件本身没有被从树上detach掉，那么其State就不会销毁(dispose)，滚动位置就不会丢失。只有当Widget发生结构变化，导致可滚动组件的State销毁或重新构建时才会丢失状态，这种情况就需要显式指定\"),n(\"code\",[t._v(\"PageStorageKey\")]),t._v(\"，通过\"),n(\"code\",[t._v(\"PageStorage\")]),t._v(\"来存储滚动位置，一个典型的场景是在使用\"),n(\"code\",[t._v(\"TabBarView\")]),t._v(\"时，在Tab发生切换时，Tab页中的可滚动组件的State就会销毁，这时如果想恢复滚动位置就需要指定\"),n(\"code\",[t._v(\"PageStorageKey\")]),t._v(\"。\")])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"scrollposition\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#scrollposition\"}},[t._v(\"#\")]),t._v(\" ScrollPosition\")]),t._v(\" \"),n(\"p\",[t._v(\"ScrollPosition是用来保存可滚动组件的滚动位置的。一个\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"对象可以同时被多个可滚动组件使用，\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"会为每一个可滚动组件创建一个\"),n(\"code\",[t._v(\"ScrollPosition\")]),t._v(\"对象，这些\"),n(\"code\",[t._v(\"ScrollPosition\")]),t._v(\"保存在\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"的\"),n(\"code\",[t._v(\"positions\")]),t._v(\"属性中（\"),n(\"code\",[t._v(\"List<ScrollPosition>\")]),t._v(\"）。\"),n(\"code\",[t._v(\"ScrollPosition\")]),t._v(\"是真正保存滑动位置信息的对象，\"),n(\"code\",[t._v(\"offset\")]),t._v(\"只是一个便捷属性：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"double \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" offset \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"pixels\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"一个\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"虽然可以对应多个可滚动组件，但是有一些操作，如读取滚动位置\"),n(\"code\",[t._v(\"offset\")]),t._v(\"，则需要一对一！但是我们仍然可以在一对多的情况下，通过其它方法读取滚动位置，举个例子，假设一个\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"同时被两个可滚动组件使用，那么我们可以通过如下方式分别读取他们的滚动位置：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\ncontroller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"positions\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"elementAt\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"pixels\\ncontroller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"positions\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"elementAt\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"pixels\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"    \\n\")])])]),n(\"p\",[t._v(\"我们可以通过\"),n(\"code\",[t._v(\"controller.positions.length\")]),t._v(\"来确定\"),n(\"code\",[t._v(\"controller\")]),t._v(\"被几个可滚动组件使用。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"scrollposition的方法\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#scrollposition的方法\"}},[t._v(\"#\")]),t._v(\" ScrollPosition的方法\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"ScrollPosition\")]),t._v(\"有两个常用方法：\"),n(\"code\",[t._v(\"animateTo()\")]),t._v(\" 和 \"),n(\"code\",[t._v(\"jumpTo()\")]),t._v(\"，它们是真正来控制跳转滚动位置的方法，\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"的这两个同名方法，内部最终都会调用\"),n(\"code\",[t._v(\"ScrollPosition\")]),t._v(\"的。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"scrollcontroller控制原理\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#scrollcontroller控制原理\"}},[t._v(\"#\")]),t._v(\" ScrollController控制原理\")]),t._v(\" \"),n(\"p\",[t._v(\"我们来介绍一下\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"的另外三个方法：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"ScrollPosition \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createScrollPosition\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    ScrollPhysics physics\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    ScrollContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    ScrollPosition oldPosition\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"attach\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ScrollPosition position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"detach\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ScrollPosition position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"当\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"和可滚动组件关联时，可滚动组件首先会调用\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"的\"),n(\"code\",[t._v(\"createScrollPosition()\")]),t._v(\"方法来创建一个\"),n(\"code\",[t._v(\"ScrollPosition\")]),t._v(\"来存储滚动位置信息，接着，可滚动组件会调用\"),n(\"code\",[t._v(\"attach()\")]),t._v(\"方法，将创建的\"),n(\"code\",[t._v(\"ScrollPosition\")]),t._v(\"添加到\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"的\"),n(\"code\",[t._v(\"positions\")]),t._v(\"属性中，这一步称为“注册位置”，只有注册后\"),n(\"code\",[t._v(\"animateTo()\")]),t._v(\" 和 \"),n(\"code\",[t._v(\"jumpTo()\")]),t._v(\"才可以被调用。\")]),t._v(\" \"),n(\"p\",[t._v(\"当可滚动组件销毁时，会调用\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"的\"),n(\"code\",[t._v(\"detach()\")]),t._v(\"方法，将其\"),n(\"code\",[t._v(\"ScrollPosition\")]),t._v(\"对象从\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"的\"),n(\"code\",[t._v(\"positions\")]),t._v(\"属性中移除，这一步称为“注销位置”，注销后\"),n(\"code\",[t._v(\"animateTo()\")]),t._v(\" 和 \"),n(\"code\",[t._v(\"jumpTo()\")]),t._v(\" 将不能再被调用。\")]),t._v(\" \"),n(\"p\",[t._v(\"需要注意的是，\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"的\"),n(\"code\",[t._v(\"animateTo()\")]),t._v(\" 和 \"),n(\"code\",[t._v(\"jumpTo()\")]),t._v(\"内部会调用所有\"),n(\"code\",[t._v(\"ScrollPosition\")]),t._v(\"的\"),n(\"code\",[t._v(\"animateTo()\")]),t._v(\" 和 \"),n(\"code\",[t._v(\"jumpTo()\")]),t._v(\"，以实现所有和该\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"关联的可滚动组件都滚动到指定的位置。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_6-6-2-滚动监听\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_6-6-2-滚动监听\"}},[t._v(\"#\")]),t._v(\" 6.6.2 滚动监听\")]),t._v(\" \"),n(\"p\",[t._v(\"Flutter Widget树中子Widget可以通过发送通知（Notification）与父(包括祖先)Widget通信。父级组件可以通过\"),n(\"code\",[t._v(\"NotificationListener\")]),t._v(\"组件来监听自己关注的通知，这种通信方式类似于Web开发中浏览器的事件冒泡，我们在Flutter中沿用“冒泡”这个术语，关于通知冒泡我们将在后面“事件处理与通知”一章中详细介绍。\")]),t._v(\" \"),n(\"p\",[t._v(\"可滚动组件在滚动时会发送\"),n(\"code\",[t._v(\"ScrollNotification\")]),t._v(\"类型的通知，\"),n(\"code\",[t._v(\"ScrollBar\")]),t._v(\"正是通过监听滚动通知来实现的。通过\"),n(\"code\",[t._v(\"NotificationListener\")]),t._v(\"监听滚动事件和通过\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"有两个主要的不同：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"通过NotificationListener可以在从可滚动组件到widget树根之间任意位置都能监听。而\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"只能和具体的可滚动组件关联后才可以。\")]),t._v(\" \"),n(\"li\",[t._v(\"收到滚动事件后获得的信息不同；\"),n(\"code\",[t._v(\"NotificationListener\")]),t._v(\"在收到滚动事件时，通知中会携带当前滚动位置和ViewPort的一些信息，而\"),n(\"code\",[t._v(\"ScrollController\")]),t._v(\"只能获取当前滚动位置。\")])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"示例-2\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例-2\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),n(\"p\",[t._v(\"下面，我们监听\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"的滚动通知，然后显示当前滚动进度百分比：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ScrollNotificationTestRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _ScrollNotificationTestRouteState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_ScrollNotificationTestRouteState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_ScrollNotificationTestRouteState\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ScrollNotificationTestRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  String _progress \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"0%\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//保存进度百分比\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scrollbar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//进度条\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 监听滚动通知\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" NotificationListener\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ScrollNotification\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        onNotification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ScrollNotification notification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          double progress \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" notification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"metrics\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"pixels \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\"\\n              notification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"metrics\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"maxScrollExtent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//重新构建\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            _progress \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"${(progress * 100).toInt()}%\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"BottomEdge: ${notification.metrics.extentAfter == 0}\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//return true; //放开此行注释后，进度条将失效\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Stack\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            ListView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                itemCount\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                itemExtent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                itemBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$index\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircleAvatar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//显示进度百分比\")]),t._v(\"\\n              radius\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"30.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_progress\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              backgroundColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"black54\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行结果如图6-16所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(671),alt:\"图6-16\"}})]),t._v(\" \"),n(\"p\",[t._v(\"在接收到滚动事件时，参数类型为\"),n(\"code\",[t._v(\"ScrollNotification\")]),t._v(\"，它包括一个\"),n(\"code\",[t._v(\"metrics\")]),t._v(\"属性，它的类型是\"),n(\"code\",[t._v(\"ScrollMetrics\")]),t._v(\"，该属性包含当前ViewPort及滚动位置等信息：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"pixels\")]),t._v(\"：当前滚动位置。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"maxScrollExtent\")]),t._v(\"：最大可滚动长度。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"extentBefore\")]),t._v(\"：滑出ViewPort顶部的长度；此示例中相当于顶部滑出屏幕上方的列表长度。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"extentInside\")]),t._v(\"：ViewPort内部长度；此示例中屏幕显示的列表部分的长度。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"extentAfter\")]),t._v(\"：列表中未滑入ViewPort部分的长度；此示例中列表底部未显示到屏幕范围部分的长度。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"atEdge\")]),t._v(\"：是否滑到了可滚动组件的边界（此示例中相当于列表顶或底部）。\")])]),t._v(\" \"),n(\"p\",[t._v(\"ScrollMetrics还有一些其它属性，读者可以自行查阅API文档。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/56.8a9849da.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[56],{688:function(t,s){t.exports=\"data:image/png;base64,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\"},689:function(t,s,a){t.exports=a.p+\"assets/img/9-6.7d1d25c1.png\"},690:function(t,s,a){t.exports=a.p+\"assets/img/9-7.d8052e62.png\"},926:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_9-6-通用-动画切换-组件-animatedswitcher\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_9-6-通用-动画切换-组件-animatedswitcher\"}},[t._v(\"#\")]),t._v(\" 9.6 通用“动画切换”组件（AnimatedSwitcher）\")]),t._v(\" \"),n(\"p\",[t._v(\"实际开发中，我们经常会遇到切换UI元素的场景，比如Tab切换、路由切换。为了增强用户体验，通常在切换时都会指定一个动画，以使切换过程显得平滑。Flutter SDK组件库中已经提供了一些常用的切换组件，如\"),n(\"code\",[t._v(\"PageView\")]),t._v(\"、\"),n(\"code\",[t._v(\"TabView\")]),t._v(\"等，但是，这些组件并不能覆盖全部的需求场景，为此，Flutter SDK中提供了一个\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"组件，它定义了一种通用的UI切换抽象。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_9-6-1-animatedswitcher\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_9-6-1-animatedswitcher\"}},[t._v(\"#\")]),t._v(\" 9.6.1 AnimatedSwitcher\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\" 可以同时对其新、旧子元素添加显示、隐藏动画。也就是说在\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"的子元素发生变化时，会对其旧元素和新元素，我们先看看\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\" 的定义：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedSwitcher\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"duration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 新child显示动画时长\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"reverseDuration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 旧child隐藏的动画时长\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"switchInCurve \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Curves\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"linear\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 新child显示的动画曲线\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"switchOutCurve \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Curves\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"linear\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 旧child隐藏的动画曲线\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"transitionBuilder \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" AnimatedSwitcher\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"defaultTransitionBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 动画构建器\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"layoutBuilder \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" AnimatedSwitcher\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"defaultLayoutBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//布局构建器\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"当\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"的child发生变化时（类型或Key不同），旧child会执行隐藏动画，新child会执行执行显示动画。究竟执行何种动画效果则由\"),n(\"code\",[t._v(\"transitionBuilder\")]),t._v(\"参数决定，该参数接受一个\"),n(\"code\",[t._v(\"AnimatedSwitcherTransitionBuilder\")]),t._v(\"类型的builder，定义如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"typedef\")]),t._v(\" AnimatedSwitcherTransitionBuilder \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"Function\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Widget child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"该\"),n(\"code\",[t._v(\"builder\")]),t._v(\"在\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"的child切换时会分别对新、旧child绑定动画：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"对旧child，绑定的动画会反向执行（reverse）\")]),t._v(\" \"),n(\"li\",[t._v(\"对新child，绑定的动画会正向指向（forward）\")])]),t._v(\" \"),n(\"p\",[t._v(\"这样一下，便实现了对新、旧child的动画绑定。\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"的默认值是\"),n(\"code\",[t._v(\"AnimatedSwitcher.defaultTransitionBuilder\")]),t._v(\" ：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"defaultTransitionBuilder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Widget child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FadeTransition\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    opacity\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"可以看到，返回了\"),n(\"code\",[t._v(\"FadeTransition\")]),t._v(\"对象，也就是说默认情况，\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"会对新旧child执行“渐隐”和“渐显”动画。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"例子\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#例子\"}},[t._v(\"#\")]),t._v(\" 例子\")]),t._v(\" \"),n(\"p\",[t._v(\"下面我们看一个列子：实现一个计数器，然后再每一次自增的过程中，旧数字执行缩小动画隐藏，新数字执行放大动画显示，代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimatedSwitcherCounterRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedSwitcherCounterRoute\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n   _AnimatedSwitcherCounterRouteState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_AnimatedSwitcherCounterRouteState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_AnimatedSwitcherCounterRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"AnimatedSwitcherCounterRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   int _count \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n   Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n       child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n         mainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n         children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n           \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedSwitcher\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n             duration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"milliseconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"500\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n             transitionBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Widget child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n               \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//执行缩放动画\")]),t._v(\"\\n               \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ScaleTransition\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" scale\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n             \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n             child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n               \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'$_count'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n               \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//显示指定key，不同的key会被认为是不同的Text，这样才能执行动画\")]),t._v(\"\\n               key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ValueKey\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"int\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_count\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n               style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Theme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"textTheme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"headline4\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n             \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n           \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n           \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n             child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'+1'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n             onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n               \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                 _count \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n               \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n             \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n           \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n         \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n       \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行示例代码，当点击“+1”按钮时，原先的数字会逐渐缩小直至隐藏，而新数字会逐渐放大，我截取了动画执行过程的一帧，如图9-5所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(688),alt:\"图9-5\"}})]),t._v(\" \"),n(\"p\",[t._v(\"上图是第一次点击“+1”按钮后切换动画的一帧，此时“0”正在逐渐缩小，而“1”正在“0”的中间，正在逐渐放大。\")]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[t._v(\"注意：AnimatedSwitcher的新旧child，如果类型相同，则Key必须不相等。\")])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"animatedswitcher实现原理\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#animatedswitcher实现原理\"}},[t._v(\"#\")]),t._v(\" AnimatedSwitcher实现原理\")]),t._v(\" \"),n(\"p\",[t._v(\"实际上，\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"的实现原理是比较简单的，我们根据\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"的使用方式也可以猜个大概。要想实现新旧child切换动画，只需要明确两个问题：动画执行的时机是和如何对新旧child执行动画。从\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"的使用方式我们可以看到，当child发生变化时（子widget的key和类型\"),n(\"strong\",[t._v(\"不\")]),t._v(\"同时相等则认为发生变化），则重新会重新执行\"),n(\"code\",[t._v(\"build\")]),t._v(\"，然后动画开始执行。我们可以通过继承StatefulWidget来实现\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"，具体做法是在\"),n(\"code\",[t._v(\"didUpdateWidget\")]),t._v(\" 回调中判断其新旧child是否发生变化，如果发生变化，则对旧child执行反向退场（reverse）动画，对新child执行正向（forward）入场动画即可。下面是\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"实现的部分核心伪代码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Widget _widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didUpdateWidget\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"AnimatedSwitcher oldWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didUpdateWidget\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"oldWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 检查新旧child是否发生变化(key和类型同时相等则返回true，认为没变化)\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"canUpdate\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" oldWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// child没变化，...\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//child发生了变化，构建一个Stack来分别给新旧child执行动画\")]),t._v(\"\\n   _widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Stack\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//旧child应用FadeTransition\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FadeTransition\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n         opacity\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _controllerOldAnimation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n         child \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" oldWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//新child应用FadeTransition\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FadeTransition\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n         opacity\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _controllerNewAnimation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n         child \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 给旧child执行反向退场动画\")]),t._v(\"\\n    _controllerOldAnimation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"reverse\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//给新child执行正向入场动画\")]),t._v(\"\\n    _controllerNewAnimation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"forward\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//build方法\")]),t._v(\"\\nWidget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" _widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上面伪代码展示了\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"实现的核心逻辑，当然\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"真正的实现比这个复杂，它可以自定义进退场过渡动画以及执行动画时的布局等。在此，我们删繁就简，通过伪代码形式让读者能够清楚看到主要的实现思路，具体的实现读者可以参考\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"源码。\")]),t._v(\" \"),n(\"p\",[t._v(\"另外，Flutter SDK中还提供了一个\"),n(\"code\",[t._v(\"AnimatedCrossFade\")]),t._v(\"组件，它也可以切换两个子元素，切换过程执行渐隐渐显的动画，和\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"不同的是\"),n(\"code\",[t._v(\"AnimatedCrossFade\")]),t._v(\"是针对两个子元素，而\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"是在一个子元素的新旧值之间切换。\"),n(\"code\",[t._v(\"AnimatedCrossFade\")]),t._v(\"实现原理比较简单，也有和\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"类似的地方，因此不再赘述，读者有兴趣可以查看其源码。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_9-6-2-animatedswitcher高级用法\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_9-6-2-animatedswitcher高级用法\"}},[t._v(\"#\")]),t._v(\" 9.6.2 AnimatedSwitcher高级用法\")]),t._v(\" \"),n(\"p\",[t._v(\"假设现在我们想实现一个类似路由平移切换的动画：旧页面屏幕中向左侧平移退出，新页面重屏幕右侧平移进入。如果要用AnimatedSwitcher的话，我们很快就会发现一个问题：做不到！我们可能会写出下面的代码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedSwitcher\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  duration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"milliseconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  transitionBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Widget child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" tween\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"Tween\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Offset\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"begin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" end\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SlideTransition\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n       child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n       position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" tween\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"animate\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上面的代码有什么问题呢？我们前面说过在\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"的child切换时会分别对新child执行正向动画（forward），而对旧child执行反向动画（reverse），所以真正的效果便是：新child确实从屏幕右侧平移进入了，但旧child却会从屏幕\"),n(\"strong\",[t._v(\"右侧\")]),t._v(\"（而不是左侧）退出。其实也很容易理解，因为在没有特殊处理的情况下，同一个动画的正向和逆向正好是相反（对称）的。\")]),t._v(\" \"),n(\"p\",[t._v(\"那么问题来了，难道就不能使用\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"了？答案当然是否定的！仔细想想这个问题，究其原因，就是因为同一个\"),n(\"code\",[t._v(\"Animation\")]),t._v(\"正向（forward）和反向（reverse）是对称的。所以如果我们可以打破这种对称性，那么便可以实现这个功能了，下面我们来封装一个\"),n(\"code\",[t._v(\"MySlideTransition\")]),t._v(\"，它与\"),n(\"code\",[t._v(\"SlideTransition\")]),t._v(\"唯一的不同就是对动画的反向执行进行了定制（从左边滑出隐藏），代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MySlideTransition\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimatedWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MySlideTransition\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Offset\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"transformHitTests \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"position \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" listenable\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Offset\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" position \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" listenable\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" bool transformHitTests\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Widget child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Offset offset\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//动画反向执行时，调整x偏移，实现“从左边滑出隐藏”\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"status \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" AnimationStatus\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"reverse\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n         offset \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\"offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dx\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dy\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FractionalTranslation\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      translation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      transformHitTests\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" transformHitTests\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"调用时，将\"),n(\"code\",[t._v(\"SlideTransition\")]),t._v(\"替换成\"),n(\"code\",[t._v(\"MySlideTransition\")]),t._v(\"即可：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedSwitcher\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  duration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"milliseconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  transitionBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Widget child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" tween\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"Tween\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Offset\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"begin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" end\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MySlideTransition\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" tween\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"animate\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \\t      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行后，我截取动画执行过程中的一帧，如图9-6所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(689),alt:\"图9-6\"}})]),t._v(\" \"),n(\"p\",[t._v(\"上图中“0”从左侧滑出，而“1”从右侧滑入。可以看到，我们通过这种巧妙的方式实现了类似路由进场切换的动画，实际上Flutter路由切换也正是通过\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"来实现的。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"slidetransitionx\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#slidetransitionx\"}},[t._v(\"#\")]),t._v(\" SlideTransitionX\")]),t._v(\" \"),n(\"p\",[t._v(\"上面的示例我们实现了“左出右入”的动画，那如果要实现“右入左出”、“上入下出”或者 “下入上出”怎么办？当然，我们可以分别修改上面的代码，但是这样每种动画都得单独定义一个“Transition”，这很麻烦。本节将封装一个通用的\"),n(\"code\",[t._v(\"SlideTransitionX\")]),t._v(\" 来实现这种“出入滑动动画”，代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"SlideTransitionX\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimatedWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SlideTransitionX\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"transformHitTests \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"direction \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" AxisDirection\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"down\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"position \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" listenable\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 偏移在内部处理      \")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"switch\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"direction\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" AxisDirection\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"up\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n        _tween \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Tween\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"begin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" end\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"break\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" AxisDirection\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"right\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n        _tween \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Tween\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"begin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" end\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"break\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" AxisDirection\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"down\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n        _tween \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Tween\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"begin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" end\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"break\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" AxisDirection\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"left\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n        _tween \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Tween\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"begin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" end\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"break\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\\n  Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" position \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" listenable\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" bool transformHitTests\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Widget child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//退场（出）方向\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" AxisDirection direction\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  Tween\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Offset\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _tween\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Offset offset \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _tween\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"evaluate\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"status \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" AnimationStatus\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"reverse\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"switch\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"direction\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" AxisDirection\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"up\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n          offset \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dx\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\"offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dy\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"break\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" AxisDirection\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"right\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n          offset \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\"offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dx\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dy\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"break\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" AxisDirection\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"down\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n          offset \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dx\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\"offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dy\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"break\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" AxisDirection\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"left\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n          offset \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\"offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dx\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dy\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"break\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FractionalTranslation\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      translation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      transformHitTests\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" transformHitTests\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"现在如果我们想实现各种“滑动出入动画”便非常容易，只需给\"),n(\"code\",[t._v(\"direction\")]),t._v(\"传递不同的方向值即可，比如要实现“上入下出”，则：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedSwitcher\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  duration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"milliseconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  transitionBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Widget child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" tween\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"Tween\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Offset\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"begin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" end\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SlideTransitionX\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n     \\t\\t\\t\\t  direction\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" AxisDirection\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"down\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//上入下出\")]),t._v(\"\\n              position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \\t      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略其余代码\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行后，我截取动画执行过程中的一帧，如图9-7所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(690),alt:\"图9-7\"}})]),t._v(\" \"),n(\"p\",[t._v(\"上图中“1”从底部滑出，而“2”从顶部滑入。读者可以尝试给\"),n(\"code\",[t._v(\"SlideTransitionX\")]),t._v(\"的\"),n(\"code\",[t._v(\"direction\")]),t._v(\"取不同的值来查看运行效果。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"总结\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),n(\"p\",[t._v(\"本节我们学习了\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"的详细用法，同时也介绍了打破\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"动画对称性的方法。我们可以发现：在需要切换新旧UI元素的场景，\"),n(\"code\",[t._v(\"AnimatedSwitcher\")]),t._v(\"将十分实用。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/57.371d9d12.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[57],{691:function(t,n){t.exports=\"data:image/png;base64,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Ow40Fzl2XaO5Ns9TBXb6hjrZov1lZSAU7atpnpqLrtU+fLYwLfvOsb+0JaFtAPWjJs6yWefZm5vG7MzcZZO61jVysPvhKZGgJg4aX3boZ72s436al9ZHvytToCOZWg/VHLRc0z+xj2t1ZFF9i2vLNRU/nV5oXnfxlZjRtoPs0PXqn5dCCr5Kwzez4uXsWqkuQT9rfN0HndivF3PNSf1Ww1m2tmxirvXUmus+ajl3ZRKaGWb75bqD8FFBpGwQuw66x+hQRz4lHDRHfTWzxlOfuh++7PbYxaV8RwXFj192x+6v6AlYABT9En95Bn7ZAuDLm5l7MoHoErAAiO7an67l6FI3EzABEzABEzCBqBGwAIjaitteEzABEzABE4CABYBvAxMwARMwAROIIAELgAguuk02ARMwARMwAQsA3wMmYAImYAImEEECFgARXHSbbAImYAImYAIWAL4HTMAETMAETCCCBCwAIrjoNtkETMAETMAELAB8D5iACZiACZhABAlYAERw0W2yCZiACZiACVgA+B4wARMwARMwgQgSsACI4KLbZBMwARMwAROwAPA9YAImYAImYAIRJGABEMFFt8kmYAImYAImYAHge8AETMAETMAEIkjAAiCCi26TTcAETMAETMACwPeACZiACZiACUSQgAVABBfdJpuACZiACZiABYDvARMwARMwAROIIAELgAguuk02ARMwARMwAQsA3wMmYAImYAImEEECFgARXHSbbAImYAImYAKxZDLZaAwm0BoCW/c3hnc2N7TmUl9jAiZwhghM610SaipjZ2g0D1NMBCwAimk182xLBt+ftlzMM2V3bwJtI5DA98edy20bNF+dJZAwBxNoLQE9ZOKtvdjXmYAJmIAJFDQB68aCXh5PzgRMwARMwATyQ8ACID9c3asJmIAJmIAJFDQBC4CCXh5PzgRMwARMwATyQ8ACID9c3asJmIAJmIAJFDQBC4CCXh5PzgRMwARMwATyQ8ACID9c3asJmIAJmIAJFDQBC4CCXh5PzgRMwARMwATyQ8ACID9c3asJmIAJmIAJFDQBC4CCXh5PzgRMwARMwATyQ8ACID9c3asJmIAJmIAJFDQBC4CCXh5PzgRMwARMwATyQ8ACID9c3asJmIAJmIAJFDQBC4CCXh5PzgRMwARMwATyQ8ACID9c3asJmIAJmIAJFDQBC4CCXh5PzgRMwARMwATyQ8ACID9c3asJmIAJmIAJFDSB/wN9/AZcme7YQQAAAABJRU5ErkJggg==\"},692:function(t,n){t.exports=\"data:image/png;base64,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+XFQoXmzf25v5Tx4PYrcn7p9/M+Hffvpj6l5+KnHouu9nQQkAN7OdYvqqNdCAER1AupMBAJIQAIggIsa5SkpBRBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSaQMD8/vxzlPtXdW0Zg/NGU3bs98JaNWsMVgWATqGkotbyCrGBPUrNbUwISAGuKNxgXX1patqWlpWBMRrMQgYAQSExMtMTEhIDMRtNYDwLJ69Gp+ny7CPAhk5iY9HYNWqMVAREQARGISEA1ABHx6KAIiIAIiIAIBJOABEAw11WzEgEREAEREIGIBCQAIuLRQREQAREQAREIJgEJgGCuq2YlAiIgAiIgAhEJSABExKODIiACIiACIhBMAhIAwVxXzUoEREAEREAEIhKQAIiIRwdFQAREQAREIJgEJACCua6alQiIgAiIgAhEJCABEBGPDoqACIiACIhAMAlIAARzXTUrERABERABEYhIQAIgIh4dFAEREAEREIFgEpAACOa6alYiIAIiIAIiEJGABEBEPDooAiIgAiIgAsEkIAEQzHXVrERABERABEQgIgEJgIh4dFAEREAEREAEgklAAiCY66pZiYAIiIAIiEBEAv8PGQS+vtZoAOcAAAAASUVORK5CYII=\"},693:function(t,n,a){t.exports=a.p+\"assets/img/9-10.fefb9fd0.png\"},927:function(t,n,a){\"use strict\";a.r(n);var s=a(45),e=Object(s.a)({},(function(){var t=this,n=t.$createElement,s=t._self._c||n;return s(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[s(\"h1\",{attrs:{id:\"_9-7-动画过渡组件\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_9-7-动画过渡组件\"}},[t._v(\"#\")]),t._v(\" 9.7 动画过渡组件\")]),t._v(\" \"),s(\"p\",[t._v(\"为了表述方便，本书约定，将在Widget属性发生变化时会执行过渡动画的组件统称为”动画过渡组件“，而动画过渡组件最明显的一个特征就是它会在内部自管理\"),s(\"code\",[t._v(\"AnimationController\")]),t._v(\"。我们知道，为了方便使用者可以自定义动画的曲线、执行时长、方向等，在前面介绍过的动画封装方法中，通常都需要使用者自己提供一个\"),s(\"code\",[t._v(\"AnimationController\")]),t._v(\"对象来自定义这些属性值。但是，如此一来，使用者就必须得手动管理\"),s(\"code\",[t._v(\"AnimationController\")]),t._v(\"，这又会增加使用的复杂性。因此，如果也能将\"),s(\"code\",[t._v(\"AnimationController\")]),t._v(\"进行封装，则会大大提高动画组件的易用性。\")]),t._v(\" \"),s(\"h2\",{attrs:{id:\"_9-7-1-自定义动画过渡组件\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_9-7-1-自定义动画过渡组件\"}},[t._v(\"#\")]),t._v(\" 9.7.1 自定义动画过渡组件\")]),t._v(\" \"),s(\"p\",[t._v(\"我们要实现一个\"),s(\"code\",[t._v(\"AnimatedDecoratedBox\")]),t._v(\"，它可以在\"),s(\"code\",[t._v(\"decoration\")]),t._v(\"属性发生变化时，从旧状态变成新状态的过程可以执行一个过渡动画。根据前面所学的知识，我们实现了一个\"),s(\"code\",[t._v(\"AnimatedDecoratedBox1\")]),t._v(\"组件：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimatedDecoratedBox1\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedDecoratedBox1\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"curve \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Curves\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"linear\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"reverseDuration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" BoxDecoration decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Widget child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Duration duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Curve curve\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Duration reverseDuration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _AnimatedDecoratedBox1State \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_AnimatedDecoratedBox1State\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_AnimatedDecoratedBox1State\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"AnimatedDecoratedBox1\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"with\")]),t._v(\" SingleTickerProviderStateMixin \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@protected\")]),t._v(\"\\n  AnimationController \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" controller \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _controller\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  AnimationController _controller\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  Animation\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" animation \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _animation\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  Animation\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _animation\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  DecorationTween _tween\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedBuilder\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      animation\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _animation\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      builder\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecoratedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _tween\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"animate\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_animation\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" widget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    _controller \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimationController\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" widget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      reverseDuration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" widget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"reverseDuration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      vsync\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    _tween \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecorationTween\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"begin\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" widget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_updateCurve\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_updateCurve\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"curve \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      _animation \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CurvedAnimation\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"parent\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _controller\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" curve\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" widget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"curve\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\"\\n      _animation \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _controller\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didUpdateWidget\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"AnimatedDecoratedBox1 oldWidget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didUpdateWidget\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"oldWidget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"curve \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" oldWidget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"curve\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_updateCurve\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    _controller\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"duration \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" widget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    _controller\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"reverseDuration \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" widget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"reverseDuration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_tween\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"end \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" _tween\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"begin\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _tween\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"begin \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _tween\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"evaluate\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_animation\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"end \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" widget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      _controller\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"forward\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    _controller\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"下面我们来使用\"),s(\"code\",[t._v(\"AnimatedDecoratedBox1\")]),t._v(\"来实现按钮点击后背景色从蓝色过渡到红色的效果：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"Color _decorationColor \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" duration \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedDecoratedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _decorationColor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    onPressed\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        _decorationColor \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"AnimatedDecoratedBox\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      style\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"点击前效果如图9-8所示，点击后截取了过渡过程的一帧如图9-9所示： \"),s(\"img\",{attrs:{src:a(691),alt:\"img\"}}),s(\"img\",{attrs:{src:a(692),alt:\"img\"}})]),t._v(\" \"),s(\"p\",[t._v(\"点击后，按钮背景色会从蓝色向红色过渡，图9-9是过渡过程中的一帧，有点偏紫色，整个过渡动画结束后背景会变为红色。\")]),t._v(\" \"),s(\"p\",[t._v(\"上面的代码虽然实现了我们期望的功能，但是代码却比较复杂。稍加思考后，我们就可以发现，\"),s(\"code\",[t._v(\"AnimationController\")]),t._v(\"的管理以及Tween更新部分的代码都是可以抽象出来的，如果我们这些通用逻辑封装成基类，那么要实现动画过渡组件只需要继承这些基类，然后定制自身不同的代码（比如动画每一帧的构建方法）即可，这样将会简化代码。\")]),t._v(\" \"),s(\"p\",[t._v(\"为了方便开发者来实现动画过渡组件的封装，Flutter提供了一个\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidget\")]),t._v(\"抽象类，它继承自StatefulWidget，同时提供了一个对应的\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidgetState\")]),t._v(\"类，\"),s(\"code\",[t._v(\"AnimationController\")]),t._v(\"的管理就在\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidgetState\")]),t._v(\"类中。开发者如果要封装动画，只需要分别继承\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidget\")]),t._v(\"和\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidgetState\")]),t._v(\"类即可，下面我们演示一下具体如何实现。\")]),t._v(\" \"),s(\"p\",[t._v(\"我们需要分两步实现：\")]),t._v(\" \"),s(\"ol\",[s(\"li\",[s(\"p\",[t._v(\"继承\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidget\")]),t._v(\"类。\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimatedDecoratedBox\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ImplicitlyAnimatedWidget\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedDecoratedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    Curve curve \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Curves\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"linear\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//动画曲线\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" Duration duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 正向动画执行时长\")]),t._v(\"\\n    Duration reverseDuration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 反向动画执行时长\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          key\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          curve\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" curve\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          reverseDuration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" reverseDuration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" BoxDecoration decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Widget child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _AnimatedDecoratedBoxState \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_AnimatedDecoratedBoxState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"其中\"),s(\"code\",[t._v(\"curve\")]),t._v(\"、\"),s(\"code\",[t._v(\"duration\")]),t._v(\"、\"),s(\"code\",[t._v(\"reverseDuration\")]),t._v(\"三个属性在\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidget\")]),t._v(\"中已定义。 可以看到\"),s(\"code\",[t._v(\"AnimatedDecoratedBox\")]),t._v(\"类和普通继承自\"),s(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"的类没有什么不同。\")])]),t._v(\" \"),s(\"li\",[s(\"p\",[t._v(\"State类继承自\"),s(\"code\",[t._v(\"AnimatedWidgetBaseState\")]),t._v(\"（该类继承自\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidgetState\")]),t._v(\"类）。\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_AnimatedDecoratedBoxState\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimatedWidgetBaseState\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"AnimatedDecoratedBox\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  DecorationTween _decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//定义一个Tween\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecoratedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"evaluate\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"animation\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" widget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"forEachTween\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"visitor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 在需要更新Tween时，基类会调用此方法\")]),t._v(\"\\n    _decoration \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"visitor\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" widget\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"value\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecorationTween\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"begin\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" value\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"可以看到我们实现了\"),s(\"code\",[t._v(\"build\")]),t._v(\"和\"),s(\"code\",[t._v(\"forEachTween\")]),t._v(\"两个方法。在动画执行过程中，每一帧都会调用\"),s(\"code\",[t._v(\"build\")]),t._v(\"方法（调用逻辑在\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidgetState\")]),t._v(\"中），所以在\"),s(\"code\",[t._v(\"build\")]),t._v(\"方法中我们需要构建每一帧的\"),s(\"code\",[t._v(\"DecoratedBox\")]),t._v(\"状态，因此得算出每一帧的\"),s(\"code\",[t._v(\"decoration\")]),t._v(\" 状态，这个我们可以通过\"),s(\"code\",[t._v(\"_decoration.evaluate(animation)\")]),t._v(\" 来算出，其中\"),s(\"code\",[t._v(\"animation\")]),t._v(\"是\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidgetState\")]),t._v(\"基类中定义的对象，\"),s(\"code\",[t._v(\"_decoration\")]),t._v(\"是我们自定义的一个\"),s(\"code\",[t._v(\"DecorationTween\")]),t._v(\"类型的对象，那么现在的问题就是它是在什么时候被赋值的呢？要回答这个问题，我们就得搞清楚什么时候需要对\"),s(\"code\",[t._v(\"_decoration\")]),t._v(\"赋值。我们知道\"),s(\"code\",[t._v(\"_decoration\")]),t._v(\"是一个Tween，而Tween的主要职责就是定义动画的起始状态（begin）和终止状态(end)。对于\"),s(\"code\",[t._v(\"AnimatedDecoratedBox\")]),t._v(\"来说，\"),s(\"code\",[t._v(\"decoration\")]),t._v(\"的终止状态就是用户传给它的值，而起始状态是不确定的，有以下两种情况：\")]),t._v(\" \"),s(\"ol\",[s(\"li\",[s(\"code\",[t._v(\"AnimatedDecoratedBox\")]),t._v(\"首次build，此时直接将其\"),s(\"code\",[t._v(\"decoration\")]),t._v(\"值置为起始状态，即\"),s(\"code\",[t._v(\"_decoration\")]),t._v(\"值为\"),s(\"code\",[t._v(\"DecorationTween(begin: decoration)\")]),t._v(\" 。\")]),t._v(\" \"),s(\"li\",[s(\"code\",[t._v(\"AnimatedDecoratedBox\")]),t._v(\"的\"),s(\"code\",[t._v(\"decoration\")]),t._v(\"更新时，则起始状态为\"),s(\"code\",[t._v(\"_decoration.animate(animation)\")]),t._v(\"，即\"),s(\"code\",[t._v(\"_decoration\")]),t._v(\"值为\"),s(\"code\",[t._v(\"DecorationTween(begin: _decoration.animate(animation)，end:decoration)\")]),t._v(\"。\")])])])]),t._v(\" \"),s(\"p\",[t._v(\"现在\"),s(\"code\",[t._v(\"forEachTween\")]),t._v(\"的作用就很明显了，它正是用于来更新Tween的初始值的，在上述两种情况下会被调用，而开发者只需重写此方法，并在此方法中更新Tween的起始状态值即可。而一些更新的逻辑被屏蔽在了\"),s(\"code\",[t._v(\"visitor\")]),t._v(\"回调，我们只需要调用它并给它传递正确的参数即可，\"),s(\"code\",[t._v(\"visitor\")]),t._v(\"方法签名如下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\"   Tween \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"visitor\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n     Tween\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" tween\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//当前的tween，第一次调用为null\")]),t._v(\"\\n     \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),t._v(\" targetValue\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 终止状态\")]),t._v(\"\\n     TweenConstructor\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" constructor，\"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//Tween构造器，在上述三种情况下会被调用以更新tween\")]),t._v(\"\\n   \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"可以看到，通过继承\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidget\")]),t._v(\"和\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidgetState\")]),t._v(\"类可以快速的实现动画过渡组件的封装，这和我们纯手工实现相比，代码简化了很多。\")]),t._v(\" \"),s(\"blockquote\",[s(\"p\",[t._v(\"如果读者还有疑惑，建议查看\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidgetState\")]),t._v(\"的源码并结合本示例代码对比理解。\")])]),t._v(\" \"),s(\"h3\",{attrs:{id:\"动画过渡组件的反向动画\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#动画过渡组件的反向动画\"}},[t._v(\"#\")]),t._v(\" 动画过渡组件的反向动画\")]),t._v(\" \"),s(\"p\",[t._v(\"在使用动画过渡组件，我们只需要在改变一些属性值后重新build组件即可，所以要实现状态反向过渡，只需要将前后状态值互换即可实现，这本来是不需要再浪费笔墨的。但是\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidget\")]),t._v(\"构造函数中却有一个\"),s(\"code\",[t._v(\"reverseDuration\")]),t._v(\"属性用于设置反向动画的执行时长，这貌似在告诉读者\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidget\")]),t._v(\"本身也提供了执行反向动画的接口，于是笔者查看了\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidgetState\")]),t._v(\"源码并未发现有执行反向动画的接口，唯一有用的是它暴露了控制动画的\"),s(\"code\",[t._v(\"controller\")]),t._v(\"。所以如果要让\"),s(\"code\",[t._v(\"reverseDuration\")]),t._v(\"生效，我们只能先获取\"),s(\"code\",[t._v(\"controller\")]),t._v(\"，然后再通过\"),s(\"code\",[t._v(\"controller.reverse()\")]),t._v(\"来启动反向动画，比如我们在上面示例的基础上实现一个循环的点击背景颜色变换效果，要求从蓝色变为红色时动画执行时间为400ms，从红变蓝为2s，如果要使\"),s(\"code\",[t._v(\"reverseDuration\")]),t._v(\"生效，我们需要这么做：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedDecoratedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" milliseconds\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"400\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _decorationColor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  reverseDuration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      onPressed\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_decorationColor \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          ImplicitlyAnimatedWidgetState _state \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"\\n              context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"findAncestorStateOfType\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ImplicitlyAnimatedWidgetState\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n           \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 通过controller来启动反向动画\")]),t._v(\"\\n          _state\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"controller\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"reverse\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 经验证必须调用setState来触发rebuild，否则状态同步会有问题\")]),t._v(\"\\n            \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              _decorationColor \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            _decorationColor \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"AnimatedDecoratedBox toggle\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        style\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"上面的代码实际上是非常糟糕且没必要的，它需要我们了解\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidgetState\")]),t._v(\"内部实现，并且要手动去启动反向动画。我们完全可以通过如下代码实现相同的效果：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedDecoratedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      milliseconds\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _decorationColor \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"400\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2000\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _decorationColor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      onPressed\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          _decorationColor \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _decorationColor \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\\n              \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\\n              \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"AnimatedDecoratedBox toggle\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        style\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"这样的代码是不是优雅的多！那么现在问题来了，为什么\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidgetState\")]),t._v(\"要提供一个\"),s(\"code\",[t._v(\"reverseDuration\")]),t._v(\"参数呢？笔者仔细研究了\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidgetState\")]),t._v(\"的实现，发现唯一的解释就是该参数并非是给\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidgetState\")]),t._v(\"用的，而是给子类用的！原因正如我们前面说的，要使\"),s(\"code\",[t._v(\"reverseDuration\")]),t._v(\" 有用就必须得获取\"),s(\"code\",[t._v(\"controller\")]),t._v(\" 属性来手动启动反向动画，\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidgetState\")]),t._v(\"中的\"),s(\"code\",[t._v(\"controller\")]),t._v(\" 属性是一个保护属性，定义如下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@protected\")]),t._v(\"\\n  AnimationController \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" controller \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _controller\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"而保护属性原则上只应该在子类中使用，而不应该像上面示例代码一样在外部使用。综上，我们可以得出两条结论：\")]),t._v(\" \"),s(\"ol\",[s(\"li\",[s(\"p\",[t._v(\"使用动画过渡组件时如果需要执行反向动画的场景，应尽量使用状态互换的方法，而不应该通过获取\"),s(\"code\",[t._v(\"ImplicitlyAnimatedWidgetState\")]),t._v(\"中\"),s(\"code\",[t._v(\"controller\")]),t._v(\"的方式。\")])]),t._v(\" \"),s(\"li\",[s(\"p\",[t._v(\"如果我们自定义的动画过渡组件用不到\"),s(\"code\",[t._v(\"reverseDuration\")]),t._v(\" ，那么最好就不要暴露此参数，比如我们上面自定义的\"),s(\"code\",[t._v(\"AnimatedDecoratedBox\")]),t._v(\"定义中就可以去除\"),s(\"code\",[t._v(\"reverseDuration\")]),t._v(\" 可选参数，如：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimatedDecoratedBox\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ImplicitlyAnimatedWidget\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedDecoratedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    Curve curve \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Curves\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"linear\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" Duration duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          key\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          curve\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" curve\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])])])]),t._v(\" \"),s(\"h2\",{attrs:{id:\"_9-7-2-flutter预置的动画过渡组件\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_9-7-2-flutter预置的动画过渡组件\"}},[t._v(\"#\")]),t._v(\" 9.7.2 Flutter预置的动画过渡组件\")]),t._v(\" \"),s(\"p\",[t._v(\"Flutter SDK中也预置了很多动画过渡组件，实现方式和大都和\"),s(\"code\",[t._v(\"AnimatedDecoratedBox\")]),t._v(\"差不多，如表9-1所示：\")]),t._v(\" \"),s(\"table\",[s(\"thead\",[s(\"tr\",[s(\"th\",[t._v(\"组件名\")]),t._v(\" \"),s(\"th\",[t._v(\"功能\")])])]),t._v(\" \"),s(\"tbody\",[s(\"tr\",[s(\"td\",[t._v(\"AnimatedPadding\")]),t._v(\" \"),s(\"td\",[t._v(\"在padding发生变化时会执行过渡动画到新状态\")])]),t._v(\" \"),s(\"tr\",[s(\"td\",[t._v(\"AnimatedPositioned\")]),t._v(\" \"),s(\"td\",[t._v(\"配合Stack一起使用，当定位状态发生变化时会执行过渡动画到新的状态。\")])]),t._v(\" \"),s(\"tr\",[s(\"td\",[t._v(\"AnimatedOpacity\")]),t._v(\" \"),s(\"td\",[t._v(\"在透明度opacity发生变化时执行过渡动画到新状态\")])]),t._v(\" \"),s(\"tr\",[s(\"td\",[t._v(\"AnimatedAlign\")]),t._v(\" \"),s(\"td\",[t._v(\"当\"),s(\"code\",[t._v(\"alignment\")]),t._v(\"发生变化时会执行过渡动画到新的状态。\")])]),t._v(\" \"),s(\"tr\",[s(\"td\",[t._v(\"AnimatedContainer\")]),t._v(\" \"),s(\"td\",[t._v(\"当Container属性发生变化时会执行过渡动画到新的状态。\")])]),t._v(\" \"),s(\"tr\",[s(\"td\",[t._v(\"AnimatedDefaultTextStyle\")]),t._v(\" \"),s(\"td\",[t._v(\"当字体样式发生变化时，子组件中继承了该样式的文本组件会动态过渡到新样式。\")])])])]),t._v(\" \"),s(\"center\",[t._v(\"表9-1：Flutter预置的动画过渡组件\")]),t._v(\"\\n下面我们通过一个示例来感受一下这些预置的动画过渡组件效果：\\n\"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimatedWidgetsTest\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _AnimatedWidgetsTestState \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_AnimatedWidgetsTestState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_AnimatedWidgetsTestState\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"AnimatedWidgetsTest\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  double _padding \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"10\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" _align \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Alignment\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"topRight\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  double _height \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  double _left \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  Color _color \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  TextStyle _style \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"black\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  Color _decorationColor \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" duration \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"5\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SingleChildScrollView\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        children\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            onPressed\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                _padding \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedPadding\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              padding\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_padding\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"AnimatedPadding\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            height\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Stack\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              children\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedPositioned\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  left\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _left\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    onPressed\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                      \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                        _left \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"AnimatedPositioned\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n              \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            height\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedAlign\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              alignment\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _align\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                onPressed\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                  \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                    _align \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Alignment\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"AnimatedAlign\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedContainer\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            height\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _height\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              onPressed\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                  _height \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"150\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                  _color \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"AnimatedContainer\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                style\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedDefaultTextStyle\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GestureDetector\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"hello world\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              onTap\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                  _style \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    decorationStyle\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" TextDecorationStyle\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"solid\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    decorationColor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            style\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _style\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedDecoratedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" duration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _decorationColor\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              onPressed\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                  _decorationColor \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                \"),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"AnimatedDecoratedBox\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                style\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"map\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            padding\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"symmetric\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"vertical\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" e\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toList\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"运行后效果如图9-10所示：\")]),t._v(\" \"),s(\"p\",[s(\"img\",{attrs:{src:a(693),alt:\"图9-10\"}})]),t._v(\" \"),s(\"p\",[t._v(\"读者可以点击一下相应组件来查看一下实际的运行效果。\")])],1)}),[],!1,null,null,null);n.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/58.3d2501b6.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[58],{330:function(t,e,n){},711:function(t,e,n){\"use strict\";n(330)},943:function(t,e,n){\"use strict\";n.r(e);var i={functional:!0,props:{type:{type:String,default:\"tip\"},text:String,vertical:{type:String,default:\"top\"}},render:function(t,e){var n=e.props,i=e.slots;return t(\"span\",{class:[\"badge\",n.type],style:{verticalAlign:n.vertical}},n.text||i().default)}},r=(n(711),n(45)),p=Object(r.a)(i,void 0,void 0,!1,null,\"15b7b770\",null);e.default=p.exports}}]);"
  },
  {
    "path": "docs/assets/js/59.ab37b38d.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[59],{329:function(t,e,a){},710:function(t,e,a){\"use strict\";a(329)},719:function(t,e,a){\"use strict\";a.r(e);var n={name:\"CodeBlock\",props:{title:{type:String,required:!0},active:{type:Boolean,default:!1}},mounted:function(){this.$parent&&this.$parent.loadTabs&&this.$parent.loadTabs()}},i=(a(710),a(45)),s=Object(i.a)(n,(function(){var t=this.$createElement;return(this._self._c||t)(\"div\",{staticClass:\"theme-code-block\",class:{\"theme-code-block__active\":this.active}},[this._t(\"default\")],2)}),[],!1,null,\"759a7d02\",null);e.default=s.exports}}]);"
  },
  {
    "path": "docs/assets/js/6.15c5e328.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[6],{533:function(t,e,r){t.exports=r.p+\"assets/img/1-2.c3960e42.png\"},534:function(t,e,r){t.exports=r.p+\"assets/img/1-3.656e852b.png\"},535:function(t,e,r){t.exports=r.p+\"assets/img/1-4.801e91b2.png\"},536:function(t,e,r){t.exports=r.p+\"assets/img/1-5.cc75b912.png\"},537:function(t,e,r){t.exports=r.p+\"assets/img/1-6.739453e7.png\"},538:function(t,e,r){t.exports=r.p+\"assets/img/1-7.4b555c37.png\"},539:function(t,e){t.exports=\"data:image/png;base64,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\"},540:function(t,e,r){t.exports=r.p+\"assets/img/1-9.69ea9a5c.png\"},541:function(t,e,r){t.exports=r.p+\"assets/img/1-10.a2561ece.png\"},834:function(t,e,r){\"use strict\";r.r(e);var a=r(45),v=Object(a.a)({},(function(){var t=this,e=t.$createElement,a=t._self._c||e;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_1-3-搭建flutter开发环境\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-3-搭建flutter开发环境\"}},[t._v(\"#\")]),t._v(\" 1.3 搭建Flutter开发环境\")]),t._v(\" \"),a(\"p\",[t._v(\"工欲善其事必先利其器，本节首先会分别介绍一下在Windows和macOS下Flutter SDK的安装，然后再介绍一下配IDE和模拟器的使用。\")]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_1-3-1-安装flutter\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-3-1-安装flutter\"}},[t._v(\"#\")]),t._v(\" 1.3.1 安装Flutter\")]),t._v(\" \"),a(\"p\",[t._v(\"由于Flutter会同时构建Android和IOS两个平台的发布包，所以Flutter同时依赖Android SDK和iOS SDK，在安装Flutter时也需要安装相应平台的构建工具和SDK。下面我们分别介绍一下Windows和macOS下的环境搭建。\")]),t._v(\" \"),a(\"blockquote\",[a(\"p\",[t._v(\"注意：本节介绍的安装方式随着Flutter的升级可能会发生变化，如果下面介绍的内容在您安装Flutter时已经失效，请访问Flutter官网，按照官网最新的安装教程安装。\")])]),t._v(\" \"),a(\"h3\",{attrs:{id:\"使用镜像\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#使用镜像\"}},[t._v(\"#\")]),t._v(\" 使用镜像\")]),t._v(\" \"),a(\"p\",[t._v(\"由于在国内访问Flutter有时可能会受到限制，Flutter官方为中国开发者搭建了临时镜像，大家可以将如下环境变量加入到用户环境变量中：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language- extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[a(\"code\",[t._v(\"export PUB_HOSTED_URL=https://pub.flutter-io.cn\\nexport FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn\\n\")])])]),a(\"p\",[a(\"strong\",[t._v(\"注意：\")]),t._v(\" 此镜像为临时镜像，并不能保证一直可用，读者可以参考https://flutter.io/community/china 以获得有关镜像服务器的最新动态。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"在windows上搭建flutter开发环境\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#在windows上搭建flutter开发环境\"}},[t._v(\"#\")]),t._v(\" 在Windows上搭建Flutter开发环境\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"系统要求\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#系统要求\"}},[t._v(\"#\")]),t._v(\" 系统要求\")]),t._v(\" \"),a(\"p\",[t._v(\"要安装并运行Flutter，您的开发环境必须满足以下最低要求:\")]),t._v(\" \"),a(\"ul\",[a(\"li\",[a(\"p\",[t._v(\"操作系统: Windows 7 或更高版本 (64-bit)\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"磁盘空间: 400 MB (不包括Android Studio的磁盘空间).\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"工具: Flutter 依赖下面这些命令行工具.\")]),t._v(\" \"),a(\"ul\",[a(\"li\",[a(\"p\",[a(\"a\",{attrs:{href:\"https://docs.microsoft.com/en-us/powershell/scripting/setup/installing-windows-powershell#upgrading-existing-windows-powershell\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"PowerShell 5.0\"),a(\"OutboundLink\")],1),t._v(\" 或更新的版本\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[a(\"a\",{attrs:{href:\"https://git-scm.com/download/win\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Git for Windows\"),a(\"OutboundLink\")],1),t._v(\"  (Git命令行工具)；\")])])]),t._v(\" \"),a(\"p\",[t._v(\"如果已安装Git for Windows，请确保可以在命令提示符或PowerShell中运行 git 命令\")])])]),t._v(\" \"),a(\"h4\",{attrs:{id:\"获取flutter-sdk\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#获取flutter-sdk\"}},[t._v(\"#\")]),t._v(\" 获取Flutter SDK\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[a(\"p\",[t._v(\"去flutter官网下载其最新可用的安装包，下载地址：https://flutter.dev/docs/development/tools/sdk/releases ，打开后如图1-2所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:r(533),alt:\"图1-2\"}})]),t._v(\" \"),a(\"p\",[t._v(\"注意，Flutter的渠道版本会不停变动，请以Flutter官网为准。另外，在中国大陆地区，要想正常获取安装包列表或下载安装包，可能需要翻墙，读者也可以去Flutter github项目下去下载安装包，地址：https://github.com/flutter/flutter/releases 。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"将安装包zip解压到你想安装Flutter SDK的路径（如：\"),a(\"code\",[t._v(\"C:\\\\src\\\\flutter\")]),t._v(\"；注意，\"),a(\"strong\",[t._v(\"不要\")]),t._v(\"将flutter安装到需要一些高权限的路径如\"),a(\"code\",[t._v(\"C:\\\\Program Files\\\\\")]),t._v(\"）。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"在Flutter安装目录的\"),a(\"code\",[t._v(\"flutter\")]),t._v(\"文件下找到\"),a(\"code\",[t._v(\"flutter_console.bat\")]),t._v(\"，双击运行并启动\"),a(\"strong\",[t._v(\"flutter命令行\")]),t._v(\"，接下来，你就可以在Flutter命令行运行flutter命令了。\")])])]),t._v(\" \"),a(\"h5\",{attrs:{id:\"更新环境变量\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#更新环境变量\"}},[t._v(\"#\")]),t._v(\" 更新环境变量\")]),t._v(\" \"),a(\"p\",[t._v(\"如果你想在Windows系统自带命令行运行flutter命令，需要添加以下环境变量到用户PATH：\")]),t._v(\" \"),a(\"ul\",[a(\"li\",[t._v(\"转到 “控制面板>用户帐户>用户帐户>更改我的环境变量”\")]),t._v(\" \"),a(\"li\",[t._v(\"在“用户变量”下检查是否有名为“Path”的条目:\\n\"),a(\"ul\",[a(\"li\",[t._v(\"如果该条目存在， 追加 flutter\\\\bin的全路径，使用 ; 作为分隔符.\")]),t._v(\" \"),a(\"li\",[t._v(\"如果该条目不存在，创建一个新用户变量 Path ，然后将 \"),a(\"code\",[t._v(\"flutter\\\\bin\")]),t._v(\" 的全路径作为它的值.\")])])])]),t._v(\" \"),a(\"p\",[t._v(\"重启Windows以应用此更改.\")]),t._v(\" \"),a(\"h5\",{attrs:{id:\"运行-flutter-doctor命令\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#运行-flutter-doctor命令\"}},[t._v(\"#\")]),t._v(\" 运行 flutter doctor命令\")]),t._v(\" \"),a(\"p\",[t._v(\"在Flutter命令行运行如下命令来查看是否还需要安装其它依赖，如果需要，安装它们：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-shell extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[a(\"code\",[t._v(\"flutter doctor\\n\")])])]),a(\"p\",[t._v(\"该命令检查你的环境并在命令行窗口中显示报告。Dart SDK已经在打包在Flutter SDK里了，没有必要单独安装Dart。 仔细检查命令行输出以获取可能需要安装的其他软件或进一步需要执行的任务。\")]),t._v(\" \"),a(\"p\",[t._v(\"例如：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language- extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[a(\"code\",[t._v(\"[-] Android toolchain - develop for Android devices\\n    • Android SDK at D:\\\\Android\\\\sdk\\n    ✗ Android SDK is missing command line tools; download from https://goo.gl/XxQghQ\\n    • Try re-installing or updating your Android SDK,\\n      visit https://flutter.io/setup/#android-setup for detailed instructions.\\n\")])])]),a(\"p\",[t._v(\"第一次运行flutter命令（如\"),a(\"code\",[t._v(\"flutter doctor\")]),t._v(\"）时，它会下载它自己的依赖项并自行编译。以后再运行就会快得多。缺失的依赖需要安装一下，安装完成后再运行\"),a(\"code\",[t._v(\"flutter doctor\")]),t._v(\"命令来验证是否安装成功。\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"android设置\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#android设置\"}},[t._v(\"#\")]),t._v(\" Android设置\")]),t._v(\" \"),a(\"p\",[t._v(\"Flutter依赖于Android Studio的全量安装。Android Studio不仅可以管理Android 平台依赖、SDK版本等，而且它也是Flutter开发推荐的IDE之一（当然，你也可以使用其它编辑器或IDE，我们将会在后面讨论）。\")]),t._v(\" \"),a(\"h5\",{attrs:{id:\"安装android-studio\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#安装android-studio\"}},[t._v(\"#\")]),t._v(\" 安装Android Studio\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"下载并安装 Android Studio，下载地址：https://developer.android.com/studio/index.html 。\")]),t._v(\" \"),a(\"li\",[t._v(\"启动Android Studio，然后执行“Android Studio安装向导”。这将安装最新的Android SDK、Android SDK平台工具和Android SDK构建工具，这些是用Flutter进行Android开发所需要的。\")])]),t._v(\" \"),a(\"h4\",{attrs:{id:\"安装遇到问题\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#安装遇到问题\"}},[t._v(\"#\")]),t._v(\" 安装遇到问题？\")]),t._v(\" \"),a(\"p\",[t._v(\"如果在安装过程中遇到问题，可以先去flutter官网查看一下安装方式是否发生变化，或者在网上搜索一下解决方案。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"在macos上搭建flutter开发环境\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#在macos上搭建flutter开发环境\"}},[t._v(\"#\")]),t._v(\" 在macOS上搭建Flutter开发环境\")]),t._v(\" \"),a(\"p\",[t._v(\"在masOS下可以同时进行Android和iOS设备的测试。\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"系统要求-2\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#系统要求-2\"}},[t._v(\"#\")]),t._v(\" 系统要求\")]),t._v(\" \"),a(\"p\",[t._v(\"要安装并运行Flutter，您的开发环境必须满足以下最低要求:\")]),t._v(\" \"),a(\"ul\",[a(\"li\",[t._v(\"操作系统: macOS (64-bit)\")]),t._v(\" \"),a(\"li\",[t._v(\"磁盘空间: 700 MB (不包括Xcode或Android Studio的磁盘空间）.\")]),t._v(\" \"),a(\"li\",[t._v(\"工具: Flutter 依赖下面这些命令行工具.\\n\"),a(\"ul\",[a(\"li\",[a(\"code\",[t._v(\"bash、mkdir、rm、git、curl、unzip、which\")])])])])]),t._v(\" \"),a(\"h4\",{attrs:{id:\"获取flutter-sdk-2\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#获取flutter-sdk-2\"}},[t._v(\"#\")]),t._v(\" 获取Flutter SDK\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[a(\"p\",[t._v(\"去flutter官网下载其最新可用的安装包，官网地址：https://flutter.io/sdk-archive/#macos\")]),t._v(\" \"),a(\"p\",[t._v(\"注意，Flutter的渠道版本会不停变动，请以Flutter官网为准。另外，在中国大陆地区，要想正常获取安装包列表或下载安装包，可能需要翻墙，读者也可以去Flutter github项目下去下载安装包，地址：https://github.com/flutter/flutter/releases 。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"解压安装包到你想安装的目录，如：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-shell extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token builtin class-name\"}},[t._v(\"cd\")]),t._v(\" ~/development\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"unzip\")]),t._v(\" ~/Downloads/flutter_macos_v0.5.1-beta.zip\\n\")])])])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"添加\"),a(\"code\",[t._v(\"flutter\")]),t._v(\"相关工具到path中：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-shell extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token builtin class-name\"}},[t._v(\"export\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token assign-left variable\"}},[a(\"span\",{pre:!0,attrs:{class:\"token environment constant\"}},[t._v(\"PATH\")])]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token variable\"}},[a(\"span\",{pre:!0,attrs:{class:\"token variable\"}},[t._v(\"`\")]),a(\"span\",{pre:!0,attrs:{class:\"token builtin class-name\"}},[t._v(\"pwd\")]),a(\"span\",{pre:!0,attrs:{class:\"token variable\"}},[t._v(\"`\")])]),t._v(\"/flutter/bin:\"),a(\"span\",{pre:!0,attrs:{class:\"token environment constant\"}},[t._v(\"$PATH\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"此代码只能暂时针对当前命令行窗口设置PATH环境变量，要想永久将Flutter添加到PATH中请参考下面\"),a(\"strong\",[t._v(\"更新环境变量\")]),t._v(\" 部分。\")])])]),t._v(\" \"),a(\"h5\",{attrs:{id:\"运行-flutter-doctor命令-2\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#运行-flutter-doctor命令-2\"}},[t._v(\"#\")]),t._v(\" 运行 flutter doctor命令\")]),t._v(\" \"),a(\"p\",[t._v(\"这一步和Windows下步骤一致，不再赘述。\")]),t._v(\" \"),a(\"h5\",{attrs:{id:\"更新环境变量-2\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#更新环境变量-2\"}},[t._v(\"#\")]),t._v(\" 更新环境变量\")]),t._v(\" \"),a(\"p\",[t._v(\"将Flutter添加到PATH中，可以在任何终端会话中运行\"),a(\"code\",[t._v(\"flutter\")]),t._v(\"命令。\")]),t._v(\" \"),a(\"p\",[t._v(\"对于所有终端会话永久修改此变量的步骤是和特定计算机系统相关的。通常，您会在打开新窗口时将设置环境变量的命令添加到执行的文件中。例如\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[a(\"p\",[t._v(\"确定您Flutter SDK的目录记为“FLUTTER_INSTALL_PATH”，您将在步骤3中用到。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"打开(或创建) \"),a(\"code\",[t._v(\"$HOME/.bash_profile\")]),t._v(\"。文件路径和文件名可能在你的电脑上不同.\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"添加以下路径:\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-shell extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token builtin class-name\"}},[t._v(\"export\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token assign-left variable\"}},[a(\"span\",{pre:!0,attrs:{class:\"token environment constant\"}},[t._v(\"PATH\")])]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"FLUTTER_INSTALL_PATH\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\"/flutter/bin:\"),a(\"span\",{pre:!0,attrs:{class:\"token environment constant\"}},[t._v(\"$PATH\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"例如笔者Flutter 安装目录是“~/code/flutter_dir”，那么代码为：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language- extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[a(\"code\",[t._v(\"export PATH=~/code/flutter_dir/flutter/bin:$PATH\\n\")])])])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"运行 \"),a(\"code\",[t._v(\"source $HOME/.bash_profile\")]),t._v(\" 刷新当前终端窗口。\")]),t._v(\" \"),a(\"blockquote\",[a(\"p\",[a(\"strong\",[t._v(\"注意:\")]),t._v(\" 如果你使用终端是zsh，终端启动时 \"),a(\"code\",[t._v(\"~/.bash_profile\")]),t._v(\" 将不会被加载，解决办法就是修改 \"),a(\"code\",[t._v(\"～/.zshrc\")]),t._v(\" ，在其中添加：source ～/.bash_profile\")])])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"验证“flutter/bin”是否已在PATH中：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language- extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[a(\"code\",[t._v(\"echo $PATH\\n\")])])])])]),t._v(\" \"),a(\"h4\",{attrs:{id:\"安装-xcode\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#安装-xcode\"}},[t._v(\"#\")]),t._v(\" 安装 Xcode\")]),t._v(\" \"),a(\"p\",[t._v(\"要为iOS开发Flutter应用程序，您需要Xcode 9.0或更高版本:\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"安装Xcode 9.0或更新版本(通过\"),a(\"a\",{attrs:{href:\"https://developer.apple.com/xcode/\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"链接下载\"),a(\"OutboundLink\")],1),t._v(\"或\"),a(\"a\",{attrs:{href:\"https://itunes.apple.com/us/app/xcode/id497799835\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"苹果应用商店\"),a(\"OutboundLink\")],1),t._v(\").\")]),t._v(\" \"),a(\"li\",[t._v(\"配置Xcode命令行工具以使用新安装的Xcode版本 \"),a(\"code\",[t._v(\"sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer\")]),t._v(\" 对于大多数情况，当您想要使用最新版本的Xcode时，这是正确的路径。如果您需要使用不同的版本，请指定相应路径。\")]),t._v(\" \"),a(\"li\",[t._v(\"确保Xcode许可协议是通过打开一次Xcode或通过命令\"),a(\"code\",[t._v(\"sudo xcodebuild -license\")]),t._v(\"同意过了.\")])]),t._v(\" \"),a(\"p\",[t._v(\"使用Xcode，您可以在iOS设备或模拟器上运行Flutter应用程序。\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"安装android-studio-2\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#安装android-studio-2\"}},[t._v(\"#\")]),t._v(\" 安装Android Studio\")]),t._v(\" \"),a(\"p\",[t._v(\"和Window一样，要在Android设备上构建并运行Flutter程序都需要先安装Android Studio，读者可以先自行下载并安装Android Studio，在此不再赘述。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"升级-flutter\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#升级-flutter\"}},[t._v(\"#\")]),t._v(\" 升级 Flutter\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"flutter-sdk分支\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#flutter-sdk分支\"}},[t._v(\"#\")]),t._v(\" Flutter SDK分支\")]),t._v(\" \"),a(\"p\",[t._v(\"Flutter SDK有多个分支，如beta、dev、master、stable，其中stable分支为稳定分支（日后有新的稳定版本发布后可能也会有新的稳定分支，如1.0.0），dev和master为开发分支，安装flutter后，你可以运行\"),a(\"code\",[t._v(\"flutter channel\")]),t._v(\"查看所有分支，如笔者本地运行后，结果如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language- extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[a(\"code\",[t._v(\"Flutter channels:\\n  beta\\n  dev\\n* master\\n\")])])]),a(\"p\",[t._v('带\"*\"号的分支即你本地的Flutter SDK 跟踪的分支，要切换分支，可以使用'),a(\"code\",[t._v(\"flutter channel beta\")]),t._v(\" 或 \"),a(\"code\",[t._v(\"flutter channel master\")]),t._v(\"，Flutter官方建议跟踪稳定分支，但你也可以跟踪\"),a(\"code\",[t._v(\"master\")]),t._v(\"分支，这样可以查看最新的变化，但这样稳定性要低的多。\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"升级flutter-sdk和依赖包\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#升级flutter-sdk和依赖包\"}},[t._v(\"#\")]),t._v(\" 升级Flutter SDK和依赖包\")]),t._v(\" \"),a(\"p\",[t._v(\"要升级flutter sdk，只需一句命令：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-shell extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[a(\"code\",[t._v(\"flutter upgrade\\n\")])])]),a(\"p\",[t._v(\"该命令会同时更新Flutter SDK和你的flutter项目依赖包。如果你只想更新项目依赖包（不包括Flutter SDK），可以使用如下命令：\")]),t._v(\" \"),a(\"ul\",[a(\"li\",[a(\"code\",[t._v(\"flutter packages get\")]),t._v(\"获取项目所有的依赖包。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"flutter packages upgrade\")]),t._v(\" 获取项目所有依赖包的最新版本。\")])]),t._v(\" \"),a(\"h4\",{attrs:{id:\"\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#\"}},[t._v(\"#\")])]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_1-3-2-ide配置与使用\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-3-2-ide配置与使用\"}},[t._v(\"#\")]),t._v(\" 1.3.2 IDE配置与使用\")]),t._v(\" \"),a(\"p\",[t._v(\"理论上可以使用任何文本编辑器与命令行工具来构建Flutter应用程序。 不过，Flutter官方建议使用Android Studio和VS Code之一以获得更好的开发体验。Flutter官方提供了这两款编辑器插件，通过IDE和插件可获得代码补全、语法高亮、widget编辑辅助、运行和调试支持等功能，可以帮助我们极大的提高开发效率。下面我们分别介绍一下Android Studio和VS Code的配置及使用（Android Studio和VS Code读者可以在其官网获得最新的安装，由于安装比较简单，故不再赘述）。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"android-studio-配置与使用\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#android-studio-配置与使用\"}},[t._v(\"#\")]),t._v(\" Android Studio 配置与使用\")]),t._v(\" \"),a(\"p\",[t._v(\"由于Android Studio是基于IntelliJ IDEA开发的，所以读者也可以使用IntelliJ IDEA。\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"安装flutter和dart插件\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#安装flutter和dart插件\"}},[t._v(\"#\")]),t._v(\" 安装Flutter和Dart插件\")]),t._v(\" \"),a(\"p\",[t._v(\"需要安装两个插件:\")]),t._v(\" \"),a(\"ul\",[a(\"li\",[a(\"code\",[t._v(\"Flutter\")]),t._v(\"插件： 支持Flutter开发工作流 (运行、调试、热重载等)。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"Dart\")]),t._v(\"插件： 提供代码分析 (输入代码时进行验证、代码补全等)。\")])]),t._v(\" \"),a(\"p\",[t._v(\"安装步骤：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"启动Android Studio。\")]),t._v(\" \"),a(\"li\",[t._v(\"打开插件首选项 (macOS：\"),a(\"strong\",[t._v(\"Preferences>Plugins\")]),t._v(\", Windows：\"),a(\"strong\",[t._v(\"File>Settings>Plugins\")]),t._v(\")。\")]),t._v(\" \"),a(\"li\",[t._v(\"选择 \"),a(\"strong\",[t._v(\"Browse repositories…\")]),t._v(\"，选择 flutter 插件并点击 \"),a(\"code\",[t._v(\"install\")]),t._v(\"。\")]),t._v(\" \"),a(\"li\",[t._v(\"重启Android Studio后插件生效。\")])]),t._v(\" \"),a(\"p\",[t._v(\"接下来，让我们用Android Studio创建一个Flutter项目，然后运行它，并体验“热重载”。\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"创建flutter应用\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#创建flutter应用\"}},[t._v(\"#\")]),t._v(\" 创建Flutter应用\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"选择 \"),a(\"strong\",[t._v(\"File>New Flutter Project\")]),t._v(\" 。\")]),t._v(\" \"),a(\"li\",[t._v(\"选择 \"),a(\"strong\",[t._v(\"Flutter application\")]),t._v(\" 作为 project 类型, 然后点击 Next。\")]),t._v(\" \"),a(\"li\",[t._v(\"输入项目名称 (如 \"),a(\"code\",[t._v(\"myapp\")]),t._v(\")，然后点击 Next。\")]),t._v(\" \"),a(\"li\",[t._v(\"点击 \"),a(\"strong\",[t._v(\"Finish\")]),t._v(\"。\")]),t._v(\" \"),a(\"li\",[t._v(\"等待Android Studio安装SDK并创建项目。\")])]),t._v(\" \"),a(\"p\",[t._v(\"上述命令创建一个Flutter项目，项目名为myapp，其中包含一个使用\"),a(\"a\",{attrs:{href:\"https://material.io/guidelines/\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Material 组件\"),a(\"OutboundLink\")],1),t._v(\"的简单演示应用程序。\")]),t._v(\" \"),a(\"p\",[t._v(\"在项目目录中，您应用程序的代码位于 \"),a(\"code\",[t._v(\"lib/main.dart\")]),t._v(\"。\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"运行应用程序\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#运行应用程序\"}},[t._v(\"#\")]),t._v(\" 运行应用程序\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[a(\"p\",[t._v(\"定位到Android Studio工具栏，如图1-3所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:r(534),alt:\"图1-3\"}})])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"在 \"),a(\"strong\",[t._v(\"target selector\")]),t._v(\" 中, 选择一个运行该应用的Android设备。如果没有列出可用，请选择 \"),a(\"strong\",[t._v(\"Tools>Android>AVD Manager\")]),t._v(\" 并在那里创建一个。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"在工具栏中点击 \"),a(\"strong\",[t._v(\"Run图标\")]),t._v(\"。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"如果一切正常, 您应该在您的设备或模拟器上会看到启动的应用程序：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:r(535),alt:\"图1-4\"}})])])]),t._v(\" \"),a(\"h4\",{attrs:{id:\"体验热重载\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#体验热重载\"}},[t._v(\"#\")]),t._v(\" 体验热重载\")]),t._v(\" \"),a(\"p\",[t._v(\"Flutter 可以通过 \"),a(\"em\",[t._v(\"热重载（hot reload）\")]),t._v(\" 实现快速的开发周期，热重载就是无需重启应用程序就能实时加载修改后的代码，并且不会丢失状态。简单的对代码进行更改，然后告诉IDE或命令行工具你需要重新加载（点击reload按钮），你就会在你的设备或模拟器上看到更改。\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[a(\"p\",[t._v(\"打开\"),a(\"code\",[t._v(\"lib/main.dart\")]),t._v(\"文件\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"将字符串\\n\"),a(\"code\",[t._v(\"'You have pushed the button this many times:'\")]),t._v(\" 更改为\\n\"),a(\"code\",[t._v(\"'You have clicked the button this many times:'\")])])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"不要按“停止”按钮; 让您的应用继续运行.\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"要查更改，请调用 \"),a(\"strong\",[t._v(\"Save\")]),t._v(\" (\"),a(\"code\",[t._v(\"cmd-s\")]),t._v(\" / \"),a(\"code\",[t._v(\"ctrl-s\")]),t._v(\")，或者点击 \"),a(\"strong\",[t._v(\"热重载按钮\")]),t._v(\" (带有闪电⚡️图标的按钮)。\")]),t._v(\" \"),a(\"p\",[t._v(\"你会立即在运行的应用程序中看到更新的字符串。\")])])]),t._v(\" \"),a(\"h3\",{attrs:{id:\"vs-code的配置与使用\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#vs-code的配置与使用\"}},[t._v(\"#\")]),t._v(\" VS Code的配置与使用\")]),t._v(\" \"),a(\"p\",[t._v(\"VS Code是一个轻量级编辑器，支持Flutter运行和调试。\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"安装flutter插件\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#安装flutter插件\"}},[t._v(\"#\")]),t._v(\" 安装flutter插件\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"启动 VS Code。\")]),t._v(\" \"),a(\"li\",[t._v(\"调用 \"),a(\"strong\",[t._v(\"View>Command Palette…\")]),t._v(\"。\")]),t._v(\" \"),a(\"li\",[t._v(\"输入 ‘install’, 然后选择 \"),a(\"strong\",[t._v(\"Extensions: Install Extension\")]),t._v(\" action。\")]),t._v(\" \"),a(\"li\",[t._v(\"在搜索框输入 \"),a(\"code\",[t._v(\"flutter\")]),t._v(\" ，在搜索结果列表中选择 ‘Flutter’, 然后点击 \"),a(\"strong\",[t._v(\"Install\")]),t._v(\"。\")]),t._v(\" \"),a(\"li\",[t._v(\"选择 ‘OK’ 重新启动 VS Code。\")]),t._v(\" \"),a(\"li\",[t._v(\"验证配置\\n\"),a(\"ul\",[a(\"li\",[t._v(\"调用 \"),a(\"strong\",[t._v(\"View>Command Palette…\")])]),t._v(\" \"),a(\"li\",[t._v(\"输入 ‘doctor’, 然后选择 \"),a(\"strong\",[t._v(\"‘Flutter: Run Flutter Doctor’\")]),t._v(\" action。\")]),t._v(\" \"),a(\"li\",[t._v(\"查看“OUTPUT”窗口中的输出是否有问题\")])])])]),t._v(\" \"),a(\"h4\",{attrs:{id:\"创建flutter应用-2\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#创建flutter应用-2\"}},[t._v(\"#\")]),t._v(\" 创建Flutter应用\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"启动 VS Code\")]),t._v(\" \"),a(\"li\",[t._v(\"调用 \"),a(\"strong\",[t._v(\"View>Command Palette…\")])]),t._v(\" \"),a(\"li\",[t._v(\"输入 ‘flutter’, 然后选择 \"),a(\"strong\",[t._v(\"‘Flutter: New Project’\")]),t._v(\" action\")]),t._v(\" \"),a(\"li\",[t._v(\"输入 Project 名称 (如\"),a(\"code\",[t._v(\"myapp\")]),t._v(\"), 然后按回车键\")]),t._v(\" \"),a(\"li\",[t._v(\"指定放置项目的位置，然后按蓝色的确定按钮\")]),t._v(\" \"),a(\"li\",[t._v(\"等待项目创建继续，并显示main.dart文件\")])]),t._v(\" \"),a(\"h4\",{attrs:{id:\"体验热重载-2\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#体验热重载-2\"}},[t._v(\"#\")]),t._v(\" 体验热重载\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"打开\"),a(\"code\",[t._v(\"lib/main.dart\")]),t._v(\"文件。\")]),t._v(\" \"),a(\"li\",[t._v(\"将字符串\\n\"),a(\"code\",[t._v(\"'You have pushed the button this many times:'\")]),t._v(\" 更改为\\n\"),a(\"code\",[t._v(\"'You have clicked the button this many times:'\")]),t._v(\"。\")]),t._v(\" \"),a(\"li\",[t._v(\"不要按“停止”按钮; 让您的应用继续运行。\")]),t._v(\" \"),a(\"li\",[t._v(\"要查看您的更改，请调用 \"),a(\"strong\",[t._v(\"Save\")]),t._v(\" (\"),a(\"code\",[t._v(\"cmd-s\")]),t._v(\" / \"),a(\"code\",[t._v(\"ctrl-s\")]),t._v(\"), 或者点击 \"),a(\"strong\",[t._v(\"热重载按钮\")]),t._v(\" (绿色圆形箭头按钮)。\")])]),t._v(\" \"),a(\"p\",[t._v(\"你会立即在运行的应用程序中看到更新的字符串。\")]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_1-3-3-连接设备运行flutter应用\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-3-3-连接设备运行flutter应用\"}},[t._v(\"#\")]),t._v(\" 1.3.3 连接设备运行Flutter应用\")]),t._v(\" \"),a(\"p\",[t._v(\"Window下只支持为Android设备构建并运行Flutter应用，而macOS同时支持iOS和Android设备。下面分别介绍如何连接Android和iOS设备来运行flutter应用。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"连接android模拟器\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#连接android模拟器\"}},[t._v(\"#\")]),t._v(\" 连接Android模拟器\")]),t._v(\" \"),a(\"p\",[t._v(\"要准备在Android模拟器上运行并测试Flutter应用，请按照以下步骤操作：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[a(\"p\",[t._v(\"启动 \"),a(\"strong\",[t._v(\"Android Studio>Tools>Android>AVD Manager\")]),t._v(\" 并选择 \"),a(\"strong\",[t._v(\"Create Virtual Device\")]),t._v(\".\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"选择一个设备并选择 \"),a(\"strong\",[t._v(\"Next\")]),t._v(\"。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"为要模拟的Android版本选择一个或多个系统印象，然后选择 \"),a(\"strong\",[t._v(\"Next\")]),t._v(\". 建议使用 \"),a(\"em\",[t._v(\"x86\")]),t._v(\" 或 \"),a(\"em\",[t._v(\"x86_64\")]),t._v(\" image .\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"在 “Emulated Performance”下, 选择 \"),a(\"strong\",[t._v(\"Hardware - GLES 2.0\")]),t._v(\" 以启用 \"),a(\"a\",{attrs:{href:\"https://developer.android.com/studio/run/emulator-acceleration.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"硬件加速\"),a(\"OutboundLink\")],1),t._v(\".\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"验证AVD配置是否正确，然后选择 \"),a(\"strong\",[t._v(\"Finish\")]),t._v(\"。\")]),t._v(\" \"),a(\"p\",[t._v(\"有关上述步骤的详细信息，请参阅 \"),a(\"a\",{attrs:{href:\"https://developer.android.com/studio/run/managing-avds.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Managing AVDs\"),a(\"OutboundLink\")],1),t._v(\".\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"在“Android Virtual Device Manager”中，点击工具栏的 \"),a(\"strong\",[t._v(\"Run\")]),t._v(\"。模拟器启动并显示所选操作系统版本或设备的启动画面。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"运行 \"),a(\"code\",[t._v(\"flutter run\")]),t._v(\" 启动您的设备。 连接的设备名是 \"),a(\"code\",[t._v(\"Android SDK built for <platform>\")]),t._v(\"，其中 \"),a(\"em\",[t._v(\"platform\")]),t._v(\" 是芯片系列，如 x86。\")])])]),t._v(\" \"),a(\"h3\",{attrs:{id:\"连接android真机设备\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#连接android真机设备\"}},[t._v(\"#\")]),t._v(\" 连接Android真机设备\")]),t._v(\" \"),a(\"p\",[t._v(\"要准备在Android设备上运行并测试Flutter应用，需要Android 4.1（API level 16）或更高版本的Android设备.\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"在Android设备上启用 \"),a(\"strong\",[t._v(\"开发人员选项\")]),t._v(\" 和 \"),a(\"strong\",[t._v(\"USB调试\")]),t._v(\" 。详细说明可在\"),a(\"a\",{attrs:{href:\"https://developer.android.com/studio/debug/dev-options.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Android文档\"),a(\"OutboundLink\")],1),t._v(\"中找到。\")]),t._v(\" \"),a(\"li\",[t._v(\"使用USB将手机插入电脑。如果设备出现调试授权提示，请授权你的电脑可以访问该设备。\")]),t._v(\" \"),a(\"li\",[t._v(\"在命令行运行 \"),a(\"code\",[t._v(\"flutter devices\")]),t._v(\" 命令以验证Flutter识别您连接的Android设备。\")]),t._v(\" \"),a(\"li\",[t._v(\"运行启动你应用程序 \"),a(\"code\",[t._v(\"flutter run\")]),t._v(\"。\")])]),t._v(\" \"),a(\"p\",[t._v(\"默认情况下，Flutter使用的Android SDK版本是基于你的 \"),a(\"code\",[t._v(\"adb\")]),t._v(\" 工具版本。 如果想让Flutter使用不同版本的Android SDK，则必须将该 \"),a(\"code\",[t._v(\"ANDROID_HOME\")]),t._v(\" 环境变量设置为相应的SDK安装目录。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"连接ios模拟器\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#连接ios模拟器\"}},[t._v(\"#\")]),t._v(\" 连接iOS模拟器\")]),t._v(\" \"),a(\"p\",[t._v(\"要准备在iOS模拟器上运行并测试Flutter应用，请按以下步骤操作：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[a(\"p\",[t._v(\"在你的MAC上，通过 Spotlight 或以下命令找到模拟器：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-shell extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"open\")]),t._v(\" -a Simulator\\n\")])])])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"通过检查模拟器 \"),a(\"strong\",[t._v(\"Hardware > Device\")]),t._v(\" 菜单中的设置，确保模拟器正在使用64位设备（iPhone 5s或更高版本）。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"根据你电脑屏幕大小，模拟高清屏iOS设备可能会溢出屏幕。可以在模拟器的 \"),a(\"strong\",[t._v(\"Window> Scale\")]),t._v(\" 菜单下设置设备比例。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"运行 \"),a(\"code\",[t._v(\"flutter run\")]),t._v(\"启动flutter应用程序。\")])])]),t._v(\" \"),a(\"h3\",{attrs:{id:\"连接ios真机设备\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#连接ios真机设备\"}},[t._v(\"#\")]),t._v(\" 连接iOS真机设备\")]),t._v(\" \"),a(\"p\",[t._v(\"要将Flutter应用安装到iOS真机设备，需要一些额外的工具和一个Apple帐户，还需要在Xcode中进行一些设置。\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[a(\"p\",[t._v(\"安装 \"),a(\"a\",{attrs:{href:\"http://brew.sh/\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"homebrew\"),a(\"OutboundLink\")],1),t._v(\" （如果已经安装了brew,跳过此步骤）。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"打开终端并运行如下这些命令:\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-shell extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[a(\"code\",[t._v(\"brew update\\nbrew \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"install\")]),t._v(\" --HEAD libimobiledevice\\nbrew \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"install\")]),t._v(\" ideviceinstaller ios-deploy cocoapods\\npod setup\\n\")])])]),a(\"p\",[t._v(\"如果这些命令中的任何一个失败并出现错误，请运行brew doctor并按照说明解决问题.\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"遵循Xcode签名流程来配置您的项目:\")]),t._v(\" \"),a(\"ul\",[a(\"li\",[a(\"p\",[t._v(\"在你Flutter项目目录中通过 \"),a(\"code\",[t._v(\"open ios/Runner.xcworkspace\")]),t._v(\" 打开默认的Xcode workspace.\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"在Xcode中，选择导航面板左侧中的\"),a(\"code\",[t._v(\"Runner\")]),t._v(\"项目。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"在\"),a(\"code\",[t._v(\"Runner\")]),t._v(\" target设置页面中，确保在 \"),a(\"strong\",[t._v(\"General > Signing > Team\")]),t._v(\" 下选择了你的开发团队。当你选择一个团队时，Xcode会创建并下载开发证书，向你的设备注册你的帐户，并创建和下载配置文件（如果需要）。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"要开始您的第一个iOS开发项目，您可能需要使用您的Apple ID登录Xcode，如图1-5：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:r(536),alt:\"图1-5\"}})]),t._v(\" \"),a(\"p\",[t._v(\"任何Apple ID都支持开发和测试，但若想将应用分发到App Store，就必须注册Apple开发者计划，有关详情读者可以自行了解。\")])])]),t._v(\" \"),a(\"ol\",{attrs:{start:\"4\"}},[a(\"li\",[a(\"p\",[t._v(\"当您第一次attach真机设备进行iOS开发时，需要同时信任你的Mac和该设备上的开发证书。首次将iOS设备连接到Mac时，请在对话框中选择 \"),a(\"code\",[t._v(\"Trust\")]),t._v(\"。\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:r(537),alt:\"添加信任\"}})]),t._v(\" \"),a(\"p\",[t._v(\"然后，转到iOS设备上的\"),a(\"strong\",[t._v(\"设置\")]),t._v(\"菜单，选择 \"),a(\"strong\",[t._v(\"常规>设备管理\")]),t._v(\" 并信任您的证书。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"如果Xcode中的自动签名失败，请验证项目的 \"),a(\"strong\",[t._v(\"General > Identity > Bundle Identifier\")]),t._v(\" 值是否唯一，如图1-7所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:r(538),alt:\"验证bundle id是否唯一\"}})])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"运行 \"),a(\"code\",[t._v(\"flutter run\")]),t._v(\"启动flutter应用程序。\")])])])])]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_1-3-4-常见配置问题\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-3-4-常见配置问题\"}},[t._v(\"#\")]),t._v(\" 1.3.4 常见配置问题\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"android-studio问题\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#android-studio问题\"}},[t._v(\"#\")]),t._v(\" Android Studio问题\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"缺少依赖库问题\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#缺少依赖库问题\"}},[t._v(\"#\")]),t._v(\" 缺少依赖库问题\")]),t._v(\" \"),a(\"p\",[t._v(\"上手安卓最常遇见的问题之一，错误如图1-8所示，此时点击超链接即可自动跳转到安装页面\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:r(539),alt:\"图1-8\"}})]),t._v(\" \"),a(\"p\",[t._v(\"安装之后重新运行即可，如图1-9：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:r(540),alt:\"install_request_components.png\"}})]),t._v(\" \"),a(\"h4\",{attrs:{id:\"连接不上android-repository\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#连接不上android-repository\"}},[t._v(\"#\")]),t._v(\" 连接不上Android Repository\")]),t._v(\" \"),a(\"p\",[t._v(\"这也是最常见的问题之一，当你发现自己无法下载部分依赖的时候，请优先考虑这种情况。进入 \"),a(\"code\",[t._v(\"File\")]),t._v(\" -> \"),a(\"code\",[t._v(\"Settings\")]),t._v(\" -> \"),a(\"code\",[t._v(\"Appearance & Behavior\")]),t._v(\" -> \"),a(\"code\",[t._v(\"System Settings\")]),t._v(\" -> \"),a(\"code\",[t._v(\"Android SDK\")]),t._v(\" -> \"),a(\"code\",[t._v(\"SDK Update Sites\")]),t._v(\" 列表，可以看到此时的 \"),a(\"code\",[t._v(\"Android Repository\")]),t._v(\" 无法连接，如图1-10所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:r(541),alt:\"下载依赖失败\"}})]),t._v(\" \"),a(\"p\",[t._v(\"这是由于要去Google下载Android SDK，但在国内目前无法访问Google所致，因此，我们可以配置代理或使用vpn。\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"安卓包配置问题\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#安卓包配置问题\"}},[t._v(\"#\")]),t._v(\" 安卓包配置问题\")]),t._v(\" \"),a(\"p\",[t._v(\"一般格式为\")]),t._v(\" \"),a(\"div\",{staticClass:\"language- extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[a(\"code\",[t._v(\"Could not HEAD **\\nCould not Get **\\n\")])])]),a(\"p\",[t._v(\"如：\"),a(\"code\",[t._v(\"Android Studio Could not GET gradle-3.2.0.pom\")])]),t._v(\" \"),a(\"p\",[t._v(\"这一类问题是由于无法连接到 Maven 库造成的，解决方法如下：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[a(\"p\",[t._v(\"进入\"),a(\"code\",[t._v(\"当前所在项目名/android\")])])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"打开 \"),a(\"code\",[t._v(\"build.gradle\")])])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"找到下面这一部分，并加上 \"),a(\"code\",[t._v(\"maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }\")])]),t._v(\" \"),a(\"div\",{staticClass:\"language- extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[a(\"code\",[t._v(\"allprojects {\\n    repositories {\\n      google()\\n      jcenter()\\n      maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' } //添加这一句\\n\\t}\\n}\\n\")])])])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"进入 File/ Settings/ Build, Execution, Deployment/ BuildTools/ Gradle/ Android Studio 中，勾选上 Enable embedded Maven repository ，重启 Android Studio 即可解决。\")]),t._v(\" \"),a(\"blockquote\",[a(\"p\",[t._v(\"**注意：**存在这样的一种情况，当你根据上述步骤设置了之后，依旧无法解决这个问题，并有类似于 \"),a(\"code\",[t._v(\"Could not HEAD maven.aliyun.com\")]),t._v(\" 的报错信息，请检查 \"),a(\"code\",[t._v(\"C:\\\\Users\\\\{user_name}\\\\.gradle\\\\gradle.properties\")]),t._v(\" 是否有设置代理。删除后问题即可解决。\")])])])]),t._v(\" \"),a(\"h4\",{attrs:{id:\"hot-reload-热重载失效问题\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#hot-reload-热重载失效问题\"}},[t._v(\"#\")]),t._v(\" Hot Reload 热重载失效问题\")]),t._v(\" \"),a(\"p\",[t._v(\"在给 \"),a(\"code\",[t._v(\"Terminal\")]),t._v(\" 之类的终端模拟器设置代理之后，会导致“Hot Reload”重载失效，此时调用 \"),a(\"strong\",[t._v(\"Save\")]),t._v(\" (\"),a(\"code\",[t._v(\"cmd-s\")]),t._v(\" / \"),a(\"code\",[t._v(\"ctrl-s\")]),t._v(\")将不会进行热重载，\"),a(\"strong\",[t._v(\"热重载按钮\")]),t._v(\" (带有闪电⚡️图标的按钮)也不会显示，将代理移除即可解决。\")]),t._v(\" \"),a(\"p\",[t._v(\"另外，有些情况下热重载是不生效的，比如修改了\"),a(\"code\",[t._v(\"main\")]),t._v(\"函数、修改了全局静态方法等，读者可以认为“Hot Reload”只会重新构建整个widget树，如果变动不在构建widget树的过程中，“Hot Reload”就不会起作用。\")])])}),[],!1,null,null,null);e.default=v.exports}}]);"
  },
  {
    "path": "docs/assets/js/60.7043cbf5.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[60],{331:function(e,t,a){},712:function(e,t,a){\"use strict\";a(331)},720:function(e,t,a){\"use strict\";a.r(t);a(65),a(27),a(94),a(95);var o={name:\"CodeGroup\",data:function(){return{codeTabs:[],activeCodeTabIndex:-1}},watch:{activeCodeTabIndex:function(e){this.activateCodeTab(e)}},mounted:function(){this.loadTabs()},methods:{changeCodeTab:function(e){this.activeCodeTabIndex=e},loadTabs:function(){var e=this;this.codeTabs=(this.$slots.default||[]).filter((function(e){return Boolean(e.componentOptions)})).map((function(t,a){return\"\"===t.componentOptions.propsData.active&&(e.activeCodeTabIndex=a),{title:t.componentOptions.propsData.title,elm:t.elm}})),-1===this.activeCodeTabIndex&&this.codeTabs.length>0&&(this.activeCodeTabIndex=0),this.activateCodeTab(0)},activateCodeTab:function(e){this.codeTabs.forEach((function(e){e.elm&&e.elm.classList.remove(\"theme-code-block__active\")})),this.codeTabs[e].elm&&this.codeTabs[e].elm.classList.add(\"theme-code-block__active\")}}},n=(a(712),a(45)),c=Object(n.a)(o,(function(){var e=this,t=e.$createElement,a=e._self._c||t;return a(\"ClientOnly\",[a(\"div\",{staticClass:\"theme-code-group\"},[a(\"div\",{staticClass:\"theme-code-group__nav\"},[a(\"ul\",{staticClass:\"theme-code-group__ul\"},e._l(e.codeTabs,(function(t,o){return a(\"li\",{key:t.title,staticClass:\"theme-code-group__li\"},[a(\"button\",{staticClass:\"theme-code-group__nav-tab\",class:{\"theme-code-group__nav-tab-active\":o===e.activeCodeTabIndex},on:{click:function(t){return e.changeCodeTab(o)}}},[e._v(\"\\n            \"+e._s(t.title)+\"\\n          \")])])})),0)]),e._v(\" \"),e._t(\"default\"),e._v(\" \"),e.codeTabs.length<1?a(\"pre\",{staticClass:\"pre-blank\"},[e._v(\"// Make sure to add code blocks to your code group\")]):e._e()],2)])}),[],!1,null,\"deefee04\",null);t.default=c.exports}}]);"
  },
  {
    "path": "docs/assets/js/61.470fa853.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[61],{375:function(A,a,t){A.exports=t.p+\"assets/img/12-1.456f4488.png\"},376:function(A,a){A.exports=\"data:image/png;base64,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\"},739:function(A,a,t){\"use strict\";t.r(a);var e=t(45),s=Object(e.a)({},(function(){var A=this,a=A.$createElement,e=A._self._c||a;return e(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":A.$parent.slotKey}},[e(\"h1\",{attrs:{id:\"_12-1-开发package\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_12-1-开发package\"}},[A._v(\"#\")]),A._v(\" 12.1 开发Package\")]),A._v(\" \"),e(\"p\",[A._v(\"第二章中已经讲过如何使用Package（包），我们知道通过package可以创建共享的模块化代码，本节将重点讲一下如何开发并发布我们自己的Package。一个最小的Package包括：\")]),A._v(\" \"),e(\"ul\",[e(\"li\",[A._v(\"一个\"),e(\"code\",[A._v(\"pubspec.yaml\")]),A._v(\"文件：声明了Package的名称、版本、作者等的元数据文件。\")]),A._v(\" \"),e(\"li\",[A._v(\"一个 \"),e(\"code\",[A._v(\"lib\")]),A._v(\" 文件夹：包括包中公开的(public)代码，最少应有一个\"),e(\"code\",[A._v(\"<package-name>.dart\")]),A._v(\"文件\")])]),A._v(\" \"),e(\"p\",[A._v(\"Flutter Packages分为两类：\")]),A._v(\" \"),e(\"ul\",[e(\"li\",[A._v(\"Dart包：其中一些可能包含Flutter的特定功能，因此对Flutter框架具有依赖性，这种包仅用于Flutter，例如\"),e(\"a\",{attrs:{href:\"https://pub.dartlang.org/packages/fluro\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[A._v(\"fluro\")]),e(\"OutboundLink\")],1),A._v(\"包。\")]),A._v(\" \"),e(\"li\",[A._v(\"插件包：一种专用的Dart包，其中包含用Dart代码编写的API，以及针对Android（使用Java或Kotlin）和针对iOS（使用OC或Swift）平台的特定实现，也就是说插件包括原生代码，一个具体的例子是\"),e(\"a\",{attrs:{href:\"https://pub.dartlang.org/packages/battery\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[A._v(\"battery\")]),e(\"OutboundLink\")],1),A._v(\"插件包。\")])]),A._v(\" \"),e(\"p\",[A._v(\"注意，虽然Flutter的Dart运行时和Dart VM运行时不是完全相同，但是如果Package中没有涉及这些存在差异的部分，那么这样的包可以同时支持Flutter和Dart VM，如Dart http网络库\"),e(\"a\",{attrs:{href:\"https://github.com/flutterchina/dio\",target:\"_blank\",rel:\"noopener noreferrer\"}},[A._v(\"dio\"),e(\"OutboundLink\")],1),A._v(\"。\")]),A._v(\" \"),e(\"p\",[A._v(\"下面我将带领读者一步步来开发一个Dart Package。\")]),A._v(\" \"),e(\"h3\",{attrs:{id:\"第一步-创建dart包\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#第一步-创建dart包\"}},[A._v(\"#\")]),A._v(\" 第一步：创建Dart包\")]),A._v(\" \"),e(\"p\",[A._v(\"您可以通过Android Studio：File>New>New Flutter Project 来创建一个Package工程，如图12-1所示：\")]),A._v(\" \"),e(\"p\",[e(\"img\",{attrs:{src:t(375),alt:\"图12-1\"}})]),A._v(\" \"),e(\"p\",[A._v(\"您也可以通过使用\"),e(\"code\",[A._v(\"--template=package\")]),A._v(\" 来执行 \"),e(\"code\",[A._v(\"flutter create\")]),A._v(\" 命令来创建：\")]),A._v(\" \"),e(\"div\",{staticClass:\"language-shell extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[e(\"code\",[A._v(\"flutter create --template\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[A._v(\"=\")]),A._v(\"package hello\\n\")])])]),e(\"p\",[A._v(\"这将在\"),e(\"code\",[A._v(\"hello/\")]),A._v(\"文件夹下创建一个具有以下专用内容的package工程：\")]),A._v(\" \"),e(\"ul\",[e(\"li\",[e(\"p\",[e(\"code\",[A._v(\"lib/hello.dart\")]),A._v(\"：Package的Dart代码\")])]),A._v(\" \"),e(\"li\",[e(\"p\",[e(\"code\",[A._v(\"test/hello_test.dart\")]),A._v(\"：Package的单元测试代码。\")])])]),A._v(\" \"),e(\"h3\",{attrs:{id:\"实现package\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#实现package\"}},[A._v(\"#\")]),A._v(\" 实现package\")]),A._v(\" \"),e(\"p\",[A._v(\"对于纯Dart包，只需在主\"),e(\"code\",[A._v(\"lib/<package name>.dart\")]),A._v(\"文件内或\"),e(\"code\",[A._v(\"lib\")]),A._v(\"目录中的文件中添加功能即可 。要测试软件包，请在\"),e(\"code\",[A._v(\"test\")]),A._v(\"目录中添加\"),e(\"a\",{attrs:{href:\"https://flutter.io/testing/#unit-testing\",target:\"_blank\",rel:\"noopener noreferrer\"}},[A._v(\"unit tests\"),e(\"OutboundLink\")],1),A._v(\"。下面我们看看如何组织Package包的代码，我们以shelf Package为例，它的目录结构如图12-2所示：\")]),A._v(\" \"),e(\"p\",[e(\"img\",{attrs:{src:t(376),alt:\"图12-2\"}})]),A._v(\" \"),e(\"p\",[A._v(\"在lib根目录下的“shelf.dart”中，导出了多个“lib/src”目录下的dart文件：\")]),A._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[A._v(\"export\")]),A._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[A._v(\"'src/cascade.dart'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\";\")]),A._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[A._v(\"export\")]),A._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[A._v(\"'src/handler.dart'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\";\")]),A._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[A._v(\"export\")]),A._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[A._v(\"'src/handlers/logger.dart'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\";\")]),A._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[A._v(\"export\")]),A._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[A._v(\"'src/hijack_exception.dart'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\";\")]),A._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[A._v(\"export\")]),A._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[A._v(\"'src/middleware.dart'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\";\")]),A._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[A._v(\"export\")]),A._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[A._v(\"'src/pipeline.dart'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\";\")]),A._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[A._v(\"export\")]),A._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[A._v(\"'src/request.dart'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\";\")]),A._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[A._v(\"export\")]),A._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[A._v(\"'src/response.dart'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\";\")]),A._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[A._v(\"export\")]),A._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[A._v(\"'src/server.dart'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\";\")]),A._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[A._v(\"export\")]),A._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[A._v(\"'src/server_handler.dart'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\";\")]),A._v(\"\\n\")])])]),e(\"p\",[A._v(\"而Package中主要的功能的源码都在src目录下。shelf Package也导出了一个迷你库: shelf_io，它主要是处理HttpRequest的。\")]),A._v(\" \"),e(\"h3\",{attrs:{id:\"导入包\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#导入包\"}},[A._v(\"#\")]),A._v(\" \"),e(\"strong\",[A._v(\"导入包\")])]),A._v(\" \"),e(\"p\",[A._v('当需要使用这个Package时，我们可以通过\"package:\"指令来指定包的入口文件：')]),A._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[A._v(\"import\")]),A._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[A._v(\"'package:utilities/utilities.dart'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\";\")]),A._v(\"\\n\")])])]),e(\"p\",[A._v(\"同一个包中的源码文件之间也可以使用相对路径来导入。\")]),A._v(\" \"),e(\"h3\",{attrs:{id:\"生成文档\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#生成文档\"}},[A._v(\"#\")]),A._v(\" 生成文档\")]),A._v(\" \"),e(\"p\",[A._v(\"可以使用 \"),e(\"a\",{attrs:{href:\"https://github.com/dart-lang/dartdoc#dartdoc\",target:\"_blank\",rel:\"noopener noreferrer\"}},[A._v(\"dartdoc\"),e(\"OutboundLink\")],1),A._v(' 工具来为Package生成文档，开发者需要做的就是遵守文档注释语法在代码中添加文档注释，最后使用dartdoc可以直接生成API文档（一个静态网站）。文档注释是使用三斜线\"///\"开始，如：')]),A._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[A._v(\"/// The event handler responsible for updating the badge in the UI.\")]),A._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[A._v(\"void\")]),A._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[A._v(\"updateBadge\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\")\")]),A._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\"{\")]),A._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\".\")]),A._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\"}\")]),A._v(\"\\n\")])])]),e(\"p\",[A._v(\"详细的文档语法请参考\"),e(\"a\",{attrs:{href:\"https://github.com/dart-lang/dartdoc#dartdoc\",target:\"_blank\",rel:\"noopener noreferrer\"}},[A._v(\"dartdoc\"),e(\"OutboundLink\")],1),A._v(\" 。\")]),A._v(\" \"),e(\"h3\",{attrs:{id:\"处理包的相互依赖\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#处理包的相互依赖\"}},[A._v(\"#\")]),A._v(\" 处理包的相互依赖\")]),A._v(\" \"),e(\"p\",[A._v(\"如果我们正在开发一个\"),e(\"code\",[A._v(\"hello\")]),A._v(\"包，它依赖于另一个包，则需要将该依赖包添加到\"),e(\"code\",[A._v(\"pubspec.yaml\")]),A._v(\"文件的\"),e(\"code\",[A._v(\"dependencies\")]),A._v(\"部分。 下面的代码使\"),e(\"code\",[A._v(\"url_launcher\")]),A._v(\"插件的API在\"),e(\"code\",[A._v(\"hello\")]),A._v(\"包中是可用的：\")]),A._v(\" \"),e(\"p\",[A._v(\"在 \"),e(\"code\",[A._v(\"hello/pubspec.yaml\")]),A._v(\"中:\")]),A._v(\" \"),e(\"div\",{staticClass:\"language-yaml extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[A._v(\"dependencies\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\":\")]),A._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[A._v(\"url_launcher\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\":\")]),A._v(\" ^0.4.2\\n\")])])]),e(\"p\",[A._v(\"现在可以在\"),e(\"code\",[A._v(\"hello\")]),A._v(\"中\"),e(\"code\",[A._v(\"import 'package:url_launcher/url_launcher.dart'\")]),A._v(\" 然后调用 \"),e(\"code\",[A._v(\"launch()\")]),A._v(\"方法了。\")]),A._v(\" \"),e(\"p\",[A._v(\"这与在Flutter应用程序或任何其他Dart项目中引用软件包没有什么不同。\")]),A._v(\" \"),e(\"p\",[A._v(\"但是，如果\"),e(\"code\",[A._v(\"hello\")]),A._v(\"碰巧是一个插件包，其平台特定的代码需要访问\"),e(\"code\",[A._v(\"url_launcher\")]),A._v(\"公开的特定于平台的API，那么我们还需要为特定于平台的构建文件添加合适的依赖声明，如下所示。\")]),A._v(\" \"),e(\"p\",[e(\"strong\",[A._v(\"Android\")])]),A._v(\" \"),e(\"p\",[A._v(\"在 \"),e(\"code\",[A._v(\"hello/android/build.gradle\")]),A._v(\":\")]),A._v(\" \"),e(\"div\",{staticClass:\"language-groovy extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-groovy\"}},[e(\"code\",[A._v(\"android \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\"{\")]),A._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[A._v(\"// lines skipped\")]),A._v(\"\\n    dependencies \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\"{\")]),A._v(\"\\n        provided rootProject\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[A._v(\"findProject\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string gstring\"}},[A._v('\":url_launcher\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\")\")]),A._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\"}\")]),A._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\"}\")]),A._v(\"\\n\")])])]),e(\"p\",[A._v(\"您现在可以在\"),e(\"code\",[A._v(\"hello/android/src\")]),A._v(\"源码中\"),e(\"code\",[A._v(\"import io.flutter.plugins.urllauncher.UrlLauncherPlugin\")]),A._v(\"访问\"),e(\"code\",[A._v(\"UrlLauncherPlugin\")]),A._v(\"类。\")]),A._v(\" \"),e(\"p\",[e(\"strong\",[A._v(\"iOS\")])]),A._v(\" \"),e(\"p\",[A._v(\"在\"),e(\"code\",[A._v(\"hello/ios/hello.podspec\")]),A._v(\":\")]),A._v(\" \"),e(\"div\",{staticClass:\"language-ruby extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-ruby\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token constant\"}},[A._v(\"Pod\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\":\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\":\")]),e(\"span\",{pre:!0,attrs:{class:\"token constant\"}},[A._v(\"Spec\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[A._v(\"new\")]),A._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[A._v(\"do\")]),A._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[A._v(\"|\")]),A._v(\"s\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[A._v(\"|\")]),A._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[A._v(\"# lines skipped\")]),A._v(\"\\n  s\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\".\")]),A._v(\"dependency \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[A._v(\"'url_launcher'\")]),A._v(\"\\n\")])])]),e(\"p\",[A._v(\"您现在可以在\"),e(\"code\",[A._v(\"hello/ios/Classes\")]),A._v(\"源码中 \"),e(\"code\",[A._v('#import \"UrlLauncherPlugin.h\"')]),A._v(\" 然后访问 \"),e(\"code\",[A._v(\"UrlLauncherPlugin\")]),A._v(\"类。\")]),A._v(\" \"),e(\"h3\",{attrs:{id:\"解决依赖冲突\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#解决依赖冲突\"}},[A._v(\"#\")]),A._v(\" 解决依赖冲突\")]),A._v(\" \"),e(\"p\",[A._v(\"假设我们想在我们的\"),e(\"code\",[A._v(\"hello\")]),A._v(\"包中使用\"),e(\"code\",[A._v(\"some_package\")]),A._v(\"和\"),e(\"code\",[A._v(\"other_package\")]),A._v(\"，并且这两个包都依赖\"),e(\"code\",[A._v(\"url_launcher\")]),A._v(\"，但是依赖的是\"),e(\"code\",[A._v(\"url_launcher\")]),A._v(\"的不同的版本。 那我们就有潜在的冲突。避免这种情况的最好方法是在指定依赖关系时，程序包作者使用\"),e(\"a\",{attrs:{href:\"https://www.dartlang.org/tools/pub/dependencies#version-constraints\",target:\"_blank\",rel:\"noopener noreferrer\"}},[A._v(\"版本范围\"),e(\"OutboundLink\")],1),A._v(\"而不是特定版本。\")]),A._v(\" \"),e(\"div\",{staticClass:\"language-yaml extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[A._v(\"dependencies\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\":\")]),A._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[A._v(\"url_launcher\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\":\")]),A._v(\" ^0.4.2    \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[A._v(\"# 这样会较好, 任何0.4.x(x >= 2)都可.\")]),A._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[A._v(\"image_picker\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\":\")]),A._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[A._v(\"'0.1.1'\")]),A._v(\"   \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[A._v(\"# 不是很好，只有0.1.1版本.\")]),A._v(\"\\n\")])])]),e(\"p\",[A._v(\"如果\"),e(\"code\",[A._v(\"some_package\")]),A._v(\"声明了上面的依赖关系,\"),e(\"code\",[A._v(\"other_package\")]),A._v(\"声明了\"),e(\"code\",[A._v(\"url_launcher\")]),A._v(\"版本像’0.4.5’或’^0.4.0’，pub将能够自动解决问题。\")]),A._v(\" \"),e(\"p\",[A._v(\"即使\"),e(\"code\",[A._v(\"some_package\")]),A._v(\"和\"),e(\"code\",[A._v(\"other_package\")]),A._v(\"声明了不兼容的\"),e(\"code\",[A._v(\"url_launcher\")]),A._v(\"版本，它仍然可能会和\"),e(\"code\",[A._v(\"url_launcher\")]),A._v(\"以兼容的方式正常工作。 你可以通过向\"),e(\"code\",[A._v(\"hello\")]),A._v(\"包的\"),e(\"code\",[A._v(\"pubspec.yaml\")]),A._v(\"文件中添加依赖性覆盖声明来处理冲突，从而强制使用特定版本：\")]),A._v(\" \"),e(\"p\",[A._v(\"强制使用 \"),e(\"code\",[A._v(\"0.4.3\")]),A._v(\"版本的\"),e(\"code\",[A._v(\"url_launcher\")]),A._v(\"，在 \"),e(\"code\",[A._v(\"hello/pubspec.yaml\")]),A._v(\"中:\")]),A._v(\" \"),e(\"div\",{staticClass:\"language-yaml extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[A._v(\"dependencies\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\":\")]),A._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[A._v(\"some_package\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\":\")]),A._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[A._v(\"other_package\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\":\")]),A._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[A._v(\"dependency_overrides\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\":\")]),A._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[A._v(\"url_launcher\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\":\")]),A._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[A._v(\"'0.4.3'\")]),A._v(\"\\n\")])])]),e(\"p\",[A._v(\"如果冲突的依赖不是一个包，而是一个特定于Android的库，比如\"),e(\"code\",[A._v(\"guava\")]),A._v(\"，那么必须将依赖重写声明添加到Gradle构建逻辑中。\")]),A._v(\" \"),e(\"p\",[A._v(\"强制使用\"),e(\"code\",[A._v(\"23.0\")]),A._v(\"版本的\"),e(\"code\",[A._v(\"guava\")]),A._v(\"库，在\"),e(\"code\",[A._v(\"hello/android/build.gradle\")]),A._v(\"中：\")]),A._v(\" \"),e(\"div\",{staticClass:\"language-groovy extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-groovy\"}},[e(\"code\",[A._v(\"configurations\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\".\")]),A._v(\"all \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\"{\")]),A._v(\"\\n    resolutionStrategy \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\"{\")]),A._v(\"\\n        force \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[A._v(\"'com.google.guava:guava:23.0-android'\")]),A._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\"}\")]),A._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\"}\")]),A._v(\"\\n\")])])]),e(\"p\",[A._v(\"Cocoapods目前不提供依赖覆盖功能。\")]),A._v(\" \"),e(\"h3\",{attrs:{id:\"发布package\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#发布package\"}},[A._v(\"#\")]),A._v(\" 发布Package\")]),A._v(\" \"),e(\"p\",[A._v(\"一旦实现了一个包，我们可以在\"),e(\"a\",{attrs:{href:\"https://pub.dartlang.org/\",target:\"_blank\",rel:\"noopener noreferrer\"}},[A._v(\"Pub\"),e(\"OutboundLink\")],1),A._v(\"上发布它 ，这样其他开发者就可以轻松使用它。\")]),A._v(\" \"),e(\"p\",[A._v(\"在发布之前，检查\"),e(\"code\",[A._v(\"pubspec.yaml\")]),A._v(\"、\"),e(\"code\",[A._v(\"README.md\")]),A._v(\"以及\"),e(\"code\",[A._v(\"CHANGELOG.md\")]),A._v(\"文件，以确保其内容的完整性和正确性。然后，运行 dry-run 命令以查看是否都准备OK了:\")]),A._v(\" \"),e(\"div\",{staticClass:\"language-shell extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[e(\"code\",[A._v(\"flutter packages pub publish --dry-run\\n\")])])]),e(\"p\",[A._v(\"验证无误后，我们就可以运行发布命令了：\")]),A._v(\" \"),e(\"div\",{staticClass:\"language-shell extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[e(\"code\",[A._v(\"flutter packages pub publish\\n\")])])]),e(\"blockquote\",[e(\"p\",[A._v(\"如果你遇到包发布失败的情况，先检查是否因为众所周知的网络原因，如果是网络问题，可以使用VPN，这里需要注意的是一些代理只会代理部分APP的网络请求，如浏览器的，它们可能并不能代理dart的网络请求，所以在这种情况下，即使开了代理也依然无法连接到Pub，因此，在发布Pub包时使用全局代理或全局VPN会保险些。如果网络没有问题，以管理员权限(sudo)运行发布命令重试。\"),e(\"br\"),A._v(\"\\n很多时候开启全局代理也不会让terminal中的流量打代理服务器走，以socks5为例，应该在终端下输入以下指令：\")])]),A._v(\" \"),e(\"div\",{staticClass:\"language-shell extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token builtin class-name\"}},[A._v(\"export\")]),A._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token assign-left variable\"}},[A._v(\"all_proxy\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[A._v(\"=\")]),A._v(\"socks5://127.0.0.1:1080\\n\")])])]),e(\"blockquote\",[e(\"p\",[A._v(\"此时终端中的http和https流量会打代理服务器走，可以通过\"),e(\"code\",[A._v(\"curl -i https://ip.cn\")]),A._v(\"指令查看代理设置是否成功。\")])])])}),[],!1,null,null,null);a.default=s.exports}}]);"
  },
  {
    "path": "docs/assets/js/62.b51637d5.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[62],{380:function(t,s,n){t.exports=n.p+\"assets/img/12-4.49c3c9bf.jpg\"},381:function(t,s){t.exports=\"data:image/jpeg;base64,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\"},748:function(t,s,n){\"use strict\";n.r(s);var a=n(45),e=Object(a.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_12-6-texture和platformview\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_12-6-texture和platformview\"}},[t._v(\"#\")]),t._v(\" 12.6 Texture和PlatformView\")]),t._v(\" \"),a(\"p\",[t._v(\"本节主要介绍原生和Flutter之间如何共享图像，以及如何在Flutter中嵌套原生组件。\")]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_12-6-1-texture-示例-使用摄像头\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_12-6-1-texture-示例-使用摄像头\"}},[t._v(\"#\")]),t._v(\" 12.6.1 Texture（示例：使用摄像头）\")]),t._v(\" \"),a(\"p\",[t._v(\"前面说过Flutter本身只是一个UI系统，对于一些系统能力的调用我们可以通过消息传送机制与原生交互。但是这种消息传送机制并不能覆盖所有的应用场景，比如我们想调用摄像头来拍照或录视频，但在拍照和录视频的过程中我们需要将预览画面显示到我们的Flutter UI中，如果我们要用Flutter定义的消息通道机制来实现这个功能，就需要将摄像头采集的每一帧图片都要从原生传递到Flutter中，这样做代价将会非常大，因为将图像或视频数据通过消息通道实时传输必然会引起内存和CPU的巨大消耗！为此，Flutter提供了一种基于Texture的图片数据共享机制。\")]),t._v(\" \"),a(\"p\",[t._v(\"Texture可以理解为GPU内保存将要绘制的图像数据的一个对象，Flutter engine会将Texture的数据在内存中直接进行映射（而无需在原生和Flutter之间再进行数据传递），Flutter会给每一个Texture分配一个id，同时Flutter中提供了一个\"),a(\"code\",[t._v(\"Texture\")]),t._v(\"组件，\"),a(\"code\",[t._v(\"Texture\")]),t._v(\"构造函数定义如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Texture\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  Key key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"textureId\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[a(\"code\",[t._v(\"Texture\")]),t._v(\" 组件正是通过\"),a(\"code\",[t._v(\"textureId\")]),t._v(\"与Texture数据关联起来；在\"),a(\"code\",[t._v(\"Texture\")]),t._v(\"组件绘制时，Flutter会自动从内存中找到相应id的Texture数据，然后进行绘制。可以总结一下整个流程：图像数据先在原生部分缓存，然后在Flutter部分再通过\"),a(\"code\",[t._v(\"textureId\")]),t._v(\"和缓存关联起来，最后绘制由Flutter完成。\")]),t._v(\" \"),a(\"p\",[t._v(\"如果我们作为一个插件开发者，我们在原生代码中分配了\"),a(\"code\",[t._v(\"textureId\")]),t._v(\"，那么在Flutter侧使用\"),a(\"code\",[t._v(\"Texture\")]),t._v(\"组件时要如何获取\"),a(\"code\",[t._v(\"textureId\")]),t._v(\"呢？这又回到了之前的内容了，\"),a(\"code\",[t._v(\"textureId\")]),t._v(\"完全可以通过MethodChannel来传递。\")]),t._v(\" \"),a(\"p\",[t._v(\"另外，值得注意的是，当原生摄像头捕获的图像发生变化时，\"),a(\"code\",[t._v(\"Texture\")]),t._v(\" 组件会自动重绘，这不需要我们写任何Dart 代码去控制。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"texture用法\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#texture用法\"}},[t._v(\"#\")]),t._v(\" Texture用法\")]),t._v(\" \"),a(\"p\",[t._v(\"如果我们要手动实现一个相机插件，和前面几节介绍的“获取剩余电量”插件的步骤一样，需要分别实现原生部分和Flutter部分。考虑到大多数读者可能并非同时既了解Android开发，又了解iOS开发，如果我们再花大量篇幅来介绍不同端的实现可能会没什么意义，另外，由于Flutter官方提供的相机（camera）插件和视频播放（video_player）插件都是使用Texture来实现的，它们本身就是Texture非常好的示例，所以在本书中将不会再介绍使用Texture的具体流程了，读者有兴趣查看camera和video_player的实现代码。下面我们重点介绍一下如何使用camera和video_player。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"相机示例\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#相机示例\"}},[t._v(\"#\")]),t._v(\" 相机示例\")]),t._v(\" \"),a(\"p\",[t._v(\"下面我们看一下camera包自带的一个示例，它包含如下功能：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"可以拍照，也可以拍视频，拍摄完成后可以保存；排号的视频可以播放预览。\")]),t._v(\" \"),a(\"li\",[t._v(\"可以切换摄像头（前置摄像头、后置摄像头、其它）\")]),t._v(\" \"),a(\"li\",[t._v(\"可以显示已经拍摄内容的预览图。\")])]),t._v(\" \"),a(\"p\",[t._v(\"下面我们看一下具体代码：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[a(\"p\",[t._v(\"首先，依赖camera插件的最新版，并下载依赖。\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-yaml extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"dependencies\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"...\")]),t._v(\"  //省略无关代码\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"camera\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ^0.5.2+2\\n\")])])])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"在\"),a(\"code\",[t._v(\"main\")]),t._v(\"方法中获取可用摄像头列表。\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"main\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 获取可用摄像头列表，cameras为全局变量\")]),t._v(\"\\n  cameras \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"availableCameras\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"runApp\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyApp\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"构建UI。现在我们构建如图12-4的测试界面：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(380),alt:\"12-4\"}}),t._v(\"\\n线面是完整的代码：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:camera/camera.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'../common.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'dart:async'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'dart:io'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:path_provider/path_provider.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:video_player/video_player.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//用于播放录制的视频\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// 获取不同摄像头的图标（前置、后置、其它）\")]),t._v(\"\\nIconData \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getCameraLensIcon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"CameraLensDirection direction\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"switch\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"direction\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" CameraLensDirection\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"back\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"camera_rear\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" CameraLensDirection\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"front\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"camera_front\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" CameraLensDirection\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"external\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"camera\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"throw\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ArgumentError\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Unknown lens direction'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"logError\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String code\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" String message\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Error: $code\\\\nError Message: $message'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 示例页面路由\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CameraExampleHome\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _CameraExampleHomeState \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_CameraExampleHomeState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_CameraExampleHomeState\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"CameraExampleHome\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"with\")]),t._v(\" WidgetsBindingObserver \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  CameraController controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  String imagePath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 图片保存路径\")]),t._v(\"\\n  String videoPath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//视频保存路径\")]),t._v(\"\\n  VideoPlayerController videoController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  VoidCallback videoPlayerListener\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  bool enableAudio \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 监听APP状态改变，是否在前台\")]),t._v(\"\\n    WidgetsBinding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"instance\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addObserver\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    WidgetsBinding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"instance\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"removeObserver\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didChangeAppLifecycleState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"AppLifecycleState state\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 如果APP不在在前台\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"state \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" AppLifecycleState\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"inactive\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      controller\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"state \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" AppLifecycleState\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"resumed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 在前台\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"controller \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onNewCameraSelected\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"description\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" GlobalKey\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ScaffoldState\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _scaffoldKey \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" GlobalKey\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ScaffoldState\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _scaffoldKey\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      appBar\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        title\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'相机示例'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      body\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Expanded\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_cameraPreviewWidget\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"black\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                border\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Border\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" controller \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\" controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isRecordingVideo\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"redAccent\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_captureControlRowWidget\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_toggleAudioWidget\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"5.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              mainAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_cameraTogglesRowWidget\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_thumbnailWidget\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// 展示预览窗口\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_cameraPreviewWidget\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"controller \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"||\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isInitialized\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'选择一个摄像头'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        style\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          fontSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"24.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          fontWeight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" FontWeight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"w900\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AspectRatio\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        aspectRatio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"aspectRatio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CameraPreview\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// 开启或关闭录音\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_toggleAudioWidget\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"only\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"left\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"25\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'开启录音:'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Switch\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" enableAudio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onChanged\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"bool value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              enableAudio \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"controller \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onNewCameraSelected\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"description\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// 显示已拍摄的图片/视频缩略图。\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_thumbnailWidget\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Expanded\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Align\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"centerRight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          mainAxisSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"min\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            videoController \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\" imagePath \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"videoController \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" Image\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"file\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"File\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"imagePath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AspectRatio\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                      aspectRatio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n                      videoController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"size \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" videoController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"aspectRatio\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"VideoPlayer\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"videoController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    border\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Border\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"pink\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"64.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"64.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// 相机工具栏\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_captureControlRowWidget\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      mainAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"spaceEvenly\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      mainAxisSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"max\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"IconButton\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          icon\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"camera_alt\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" controller \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\"\\n              controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isInitialized \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isRecordingVideo\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" onTakePictureButtonPressed\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"IconButton\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          icon\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"videocam\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" controller \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\"\\n              controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isInitialized \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isRecordingVideo\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" onVideoRecordButtonPressed\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"IconButton\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          icon\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"stop\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" controller \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\"\\n              controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isInitialized \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\"\\n              controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isRecordingVideo\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" onStopButtonPressed\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// 展示所有摄像头\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_cameraTogglesRowWidget\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" List\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" toggles \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"cameras\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isEmpty\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'没有检测到摄像头'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"for\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"CameraDescription cameraDescription \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"in\")]),t._v(\" cameras\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        toggles\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"90.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" RadioListTile\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"CameraDescription\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              title\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getCameraLensIcon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"cameraDescription\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"lensDirection\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              groupValue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" controller\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"description\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" cameraDescription\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              onChanged\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" controller \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\" controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isRecordingVideo\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" onNewCameraSelected\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" toggles\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  String \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"timestamp\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" DateTime\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"now\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"millisecondsSinceEpoch\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showInSnackBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String message\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    _scaffoldKey\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"currentState\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showSnackBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SnackBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"content\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"message\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 摄像头选中回调\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onNewCameraSelected\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"CameraDescription cameraDescription\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"controller \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    controller \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CameraController\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      cameraDescription\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      ResolutionPreset\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"high\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      enableAudio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" enableAudio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addListener\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"mounted\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"hasError\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showInSnackBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Camera error ${controller.value.errorDescription}'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initialize\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"on\")]),t._v(\" CameraException \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"catch\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_showCameraException\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"mounted\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 拍照按钮点击回调\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onTakePictureButtonPressed\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"takePicture\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String filePath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"mounted\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          imagePath \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" filePath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          videoController\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          videoController \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"filePath \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showInSnackBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'图片保存在 $filePath'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 开始录制视频\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onVideoRecordButtonPressed\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"startVideoRecording\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String filePath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"mounted\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"filePath \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showInSnackBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'正在保存视频于 $filePath'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 终止视频录制\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onStopButtonPressed\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"stopVideoRecording\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"mounted\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showInSnackBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'视频保存在: $videoPath'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  Future\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"startVideoRecording\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isInitialized\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showInSnackBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'请先选择一个摄像头'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 确定视频保存的路径\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Directory extDir \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getApplicationDocumentsDirectory\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String dirPath \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'${extDir.path}/Movies/flutter_test'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Directory\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"dirPath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"create\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"recursive\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String filePath \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'$dirPath/${timestamp()}.mp4'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isRecordingVideo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 如果正在录制，则直接返回\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      videoPath \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" filePath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"startVideoRecording\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"filePath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"on\")]),t._v(\" CameraException \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"catch\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_showCameraException\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" filePath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  Future\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"stopVideoRecording\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isRecordingVideo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"stopVideoRecording\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"on\")]),t._v(\" CameraException \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"catch\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_showCameraException\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_startVideoPlayer\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  Future\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_startVideoPlayer\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" VideoPlayerController vcontroller \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"\\n    VideoPlayerController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"file\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"File\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"videoPath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    videoPlayerListener \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"videoController \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\" videoController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"size \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// Refreshing the state to update video player with the correct ratio.\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"mounted\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        videoController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"removeListener\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"videoPlayerListener\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    vcontroller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addListener\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"videoPlayerListener\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" vcontroller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setLooping\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" vcontroller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initialize\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" videoController\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"mounted\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        imagePath \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        videoController \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" vcontroller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" vcontroller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"play\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  Future\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"takePicture\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isInitialized\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showInSnackBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'错误: 请先选择一个相机'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Directory extDir \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getApplicationDocumentsDirectory\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String dirPath \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'${extDir.path}/Pictures/flutter_test'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Directory\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"dirPath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"create\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"recursive\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String filePath \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'$dirPath/${timestamp()}.jpg'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isTakingPicture\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// A capture is already pending, do nothing.\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"takePicture\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"filePath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"on\")]),t._v(\" CameraException \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"catch\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_showCameraException\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" filePath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_showCameraException\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"CameraException e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"logError\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"code\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"description\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showInSnackBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Error: ${e.code}\\\\n${e.description}'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])])])]),t._v(\" \"),a(\"blockquote\",[a(\"p\",[t._v(\"如果代码运行遇到困难，请直接查看\"),a(\"a\",{attrs:{href:\"https://pub.dev/packages/camera\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"camera官方文档\"),a(\"OutboundLink\")],1),t._v(\"。\")])]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_12-6-2-platformview-示例-webview\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_12-6-2-platformview-示例-webview\"}},[t._v(\"#\")]),t._v(\" 12.6.2 PlatformView （示例：WebView）\")]),t._v(\" \"),a(\"p\",[t._v(\"如果我们在开发过程中需要使用一个原生组件，但这个原生组件在Flutter中很难实现时怎么办（如webview）？这时一个简单的方法就是将需要使用原生组件的页面全部用原生实现，在flutter中需要打开该页面时通过消息通道打开这个原生的页面。但是这种方法有一个最大的缺点，就是原生组件很难和Flutter组件进行组合。\")]),t._v(\" \"),a(\"p\",[t._v(\"在 Flutter 1.0版本中，Flutter SDK中新增了\"),a(\"code\",[t._v(\"AndroidView\")]),t._v(\"和\"),a(\"code\",[t._v(\"UIKitView\")]),t._v(\" 两个组件，这两个组件的主要功能就是将原生的Android组件和iOS组件嵌入到Flutter的组件树中，这个功能是非常重要的，尤其是对一些实现非常复杂的组件，比如webview，这些组件原生已经有了，如果Flutter中要用，重新实现的话成本将非常高，所以如果有一种机制能让Flutter共享原生组件，这将会非常有用，也正因如此，Flutter才提供了这两个组件。\")]),t._v(\" \"),a(\"p\",[t._v(\"由于\"),a(\"code\",[t._v(\"AndroidView\")]),t._v(\"和\"),a(\"code\",[t._v(\"UIKitView\")]),t._v(\" 是和具体平台相关的，所以称它们为PlatformView。需要说明的是将来Flutter支持的平台可能会增多，则相应的PlatformView也将会变多。那么如何使用Platform View呢？我们以Flutter官方提供的\"),a(\"a\",{attrs:{href:\"https://github.com/flutter/plugins/tree/master/packages/webview_flutter\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"webview_flutter插件\"),a(\"OutboundLink\")],1),t._v(\"为例：\")]),t._v(\" \"),a(\"blockquote\",[a(\"p\",[t._v(\"注意，在本书写作之时，webview_flutter仍处于预览阶段，如您想在项目中使用它，请查看一下webview_flutter插件最新版本及动态。\")])]),t._v(\" \"),a(\"ol\",[a(\"li\",[a(\"p\",[t._v(\"原生代码中注册要被Flutter嵌入的组件工厂，如webview_flutter插件中Android端注册webview插件代码：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-java extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-java\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"public\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"registerWith\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Registrar\")]),t._v(\" registrar\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   registrar\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"platformViewRegistry\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"registerViewFactory\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"webview\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n   \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"WebViewFactory\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"registrar\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"messenger\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[a(\"code\",[t._v(\"WebViewFactory\")]),t._v(\"的具体实现请参考webview_flutter插件的实现源码，在此不再赘述。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"在Flutter中使用；打开Flutter中文社区首页。\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"PlatformViewRoute\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"WebView\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      initialUrl\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"https://flutterchina.club\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      javascriptMode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" JavascriptMode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"unrestricted\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"运行效果如图12-5所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(381),alt:\"12-5\"}})])])]),t._v(\" \"),a(\"p\",[t._v(\"注意，使用PlatformView的开销是非常大的，因此，如果一个原生组件用Flutter实现的难度不大时，我们应该首选Flutter实现。\")]),t._v(\" \"),a(\"p\",[t._v(\"另外，PlatformView的相关功能在作者写作时还处于预览阶段，可能还会发生变化，因此，读者如果需要在项目中使用的话，应查看一下最新的文档。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/63.ff70c9ee.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[63],{448:function(t,s,n){t.exports=n.p+\"assets/img/4-9.7897e012.png\"},449:function(t,s,n){t.exports=n.p+\"assets/img/4-10.9e758696.png\"},786:function(t,s,n){\"use strict\";n.r(s);var a=n(45),e=Object(a.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_4-5-层叠布局-stack、positioned\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_4-5-层叠布局-stack、positioned\"}},[t._v(\"#\")]),t._v(\" 4.5 层叠布局 Stack、Positioned\")]),t._v(\" \"),a(\"p\",[t._v(\"层叠布局和Web中的绝对定位、Android中的Frame布局是相似的，子组件可以根据距父容器四个角的位置来确定自身的位置。绝对定位允许子组件堆叠起来（按照代码中声明的顺序）。Flutter中使用\"),a(\"code\",[t._v(\"Stack\")]),t._v(\"和\"),a(\"code\",[t._v(\"Positioned\")]),t._v(\"这两个组件来配合实现绝对定位。\"),a(\"code\",[t._v(\"Stack\")]),t._v(\"允许子组件堆叠，而\"),a(\"code\",[t._v(\"Positioned\")]),t._v(\"用于根据\"),a(\"code\",[t._v(\"Stack\")]),t._v(\"的四个角来确定子组件的位置。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"stack\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#stack\"}},[t._v(\"#\")]),t._v(\" Stack\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Stack\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"alignment \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" AlignmentDirectional\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"topStart\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"textDirection\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"fit \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" StackFit\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"loose\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"overflow \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Overflow\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"clip\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  List\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" children \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"ul\",[a(\"li\",[a(\"code\",[t._v(\"alignment\")]),t._v(\"：此参数决定如何去对齐没有定位（没有使用\"),a(\"code\",[t._v(\"Positioned\")]),t._v(\"）或部分定位的子组件。所谓部分定位，在这里\"),a(\"strong\",[t._v(\"特指没有在某一个轴上定位：\")]),a(\"code\",[t._v(\"left\")]),t._v(\"、\"),a(\"code\",[t._v(\"right\")]),t._v(\"为横轴，\"),a(\"code\",[t._v(\"top\")]),t._v(\"、\"),a(\"code\",[t._v(\"bottom\")]),t._v(\"为纵轴，只要包含某个轴上的一个定位属性就算在该轴上有定位。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"textDirection\")]),t._v(\"：和\"),a(\"code\",[t._v(\"Row\")]),t._v(\"、\"),a(\"code\",[t._v(\"Wrap\")]),t._v(\"的\"),a(\"code\",[t._v(\"textDirection\")]),t._v(\"功能一样，都用于确定\"),a(\"code\",[t._v(\"alignment\")]),t._v(\"对齐的参考系，即：\"),a(\"code\",[t._v(\"textDirection\")]),t._v(\"的值为\"),a(\"code\",[t._v(\"TextDirection.ltr\")]),t._v(\"，则\"),a(\"code\",[t._v(\"alignment\")]),t._v(\"的\"),a(\"code\",[t._v(\"start\")]),t._v(\"代表左，\"),a(\"code\",[t._v(\"end\")]),t._v(\"代表右，即\"),a(\"code\",[t._v(\"从左往右\")]),t._v(\"的顺序；\"),a(\"code\",[t._v(\"textDirection\")]),t._v(\"的值为\"),a(\"code\",[t._v(\"TextDirection.rtl\")]),t._v(\"，则alignment的\"),a(\"code\",[t._v(\"start\")]),t._v(\"代表右，\"),a(\"code\",[t._v(\"end\")]),t._v(\"代表左，即\"),a(\"code\",[t._v(\"从右往左\")]),t._v(\"的顺序。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"fit\")]),t._v(\"：此参数用于确定\"),a(\"strong\",[t._v(\"没有定位\")]),t._v(\"的子组件如何去适应\"),a(\"code\",[t._v(\"Stack\")]),t._v(\"的大小。\"),a(\"code\",[t._v(\"StackFit.loose\")]),t._v(\"表示使用子组件的大小，\"),a(\"code\",[t._v(\"StackFit.expand\")]),t._v(\"表示扩伸到\"),a(\"code\",[t._v(\"Stack\")]),t._v(\"的大小。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"overflow\")]),t._v(\"：此属性决定如何显示超出\"),a(\"code\",[t._v(\"Stack\")]),t._v(\"显示空间的子组件；值为\"),a(\"code\",[t._v(\"Overflow.clip\")]),t._v(\"时，超出部分会被剪裁（隐藏），值为\"),a(\"code\",[t._v(\"Overflow.visible\")]),t._v(\" 时则不会。\")])]),t._v(\" \"),a(\"h3\",{attrs:{id:\"positioned\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#positioned\"}},[t._v(\"#\")]),t._v(\" Positioned\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Positioned\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  Key key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"left\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"top\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"right\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"bottom\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" Widget child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[a(\"code\",[t._v(\"left\")]),t._v(\"、\"),a(\"code\",[t._v(\"top\")]),t._v(\" 、\"),a(\"code\",[t._v(\"right\")]),t._v(\"、 \"),a(\"code\",[t._v(\"bottom\")]),t._v(\"分别代表离\"),a(\"code\",[t._v(\"Stack\")]),t._v(\"左、上、右、底四边的距离。\"),a(\"code\",[t._v(\"width\")]),t._v(\"和\"),a(\"code\",[t._v(\"height\")]),t._v(\"用于指定需要定位元素的宽度和高度。注意，\"),a(\"code\",[t._v(\"Positioned\")]),t._v(\"的\"),a(\"code\",[t._v(\"width\")]),t._v(\"、\"),a(\"code\",[t._v(\"height\")]),t._v(\" 和其它地方的意义稍微有点区别，此处用于配合\"),a(\"code\",[t._v(\"left\")]),t._v(\"、\"),a(\"code\",[t._v(\"top\")]),t._v(\" 、\"),a(\"code\",[t._v(\"right\")]),t._v(\"、 \"),a(\"code\",[t._v(\"bottom\")]),t._v(\"来定位组件，举个例子，在水平方向时，你只能指定\"),a(\"code\",[t._v(\"left\")]),t._v(\"、\"),a(\"code\",[t._v(\"right\")]),t._v(\"、\"),a(\"code\",[t._v(\"width\")]),t._v(\"三个属性中的两个，如指定\"),a(\"code\",[t._v(\"left\")]),t._v(\"和\"),a(\"code\",[t._v(\"width\")]),t._v(\"后，\"),a(\"code\",[t._v(\"right\")]),t._v(\"会自动算出(\"),a(\"code\",[t._v(\"left\")]),t._v(\"+\"),a(\"code\",[t._v(\"width\")]),t._v(\")，如果同时指定三个属性则会报错，垂直方向同理。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"示例\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),a(\"p\",[t._v(\"在下面的例子中，我们通过对几个\"),a(\"code\",[t._v(\"Text\")]),t._v(\"组件的定位来演示\"),a(\"code\",[t._v(\"Stack\")]),t._v(\"和\"),a(\"code\",[t._v(\"Positioned\")]),t._v(\"的特性：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//通过ConstrainedBox来确保Stack占满屏幕\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ConstrainedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  constraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BoxConstraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"expand\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Stack\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"Alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//指定未定位或部分定位widget的对齐方式\")]),t._v(\"\\n    children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hello world\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"style\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Positioned\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        left\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"18.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"I am Jack\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Positioned\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        top\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"18.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Your friend\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"        \\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"运行效果见图4-9：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(448),alt:\"图4-9\"}})]),t._v(\" \"),a(\"p\",[t._v(\"由于第一个子文本组件\"),a(\"code\",[t._v('Text(\"Hello world\")')]),t._v(\"没有指定定位，并且\"),a(\"code\",[t._v(\"alignment\")]),t._v(\"值为\"),a(\"code\",[t._v(\"Alignment.center\")]),t._v(\"，所以它会居中显示。第二个子文本组件\"),a(\"code\",[t._v('Text(\"I am Jack\")')]),t._v(\"只指定了水平方向的定位(\"),a(\"code\",[t._v(\"left\")]),t._v(\")，所以属于部分定位，即垂直方向上没有定位，那么它在垂直方向的对齐方式则会按照\"),a(\"code\",[t._v(\"alignment\")]),t._v(\"指定的对齐方式对齐，即垂直方向居中。对于第三个子文本组件\"),a(\"code\",[t._v('Text(\"Your friend\")')]),t._v(\"，和第二个\"),a(\"code\",[t._v(\"Text\")]),t._v(\"原理一样，只不过是水平方向没有定位，则水平方向居中。\")]),t._v(\" \"),a(\"p\",[t._v(\"我们给上例中的\"),a(\"code\",[t._v(\"Stack\")]),t._v(\"指定一个\"),a(\"code\",[t._v(\"fit\")]),t._v(\"属性，然后将三个子文本组件的顺序调整一下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Stack\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"Alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  fit\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" StackFit\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"expand\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//未定位widget占满Stack整个空间\")]),t._v(\"\\n  children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Positioned\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      left\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"18.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"I am Jack\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hello world\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"style\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Positioned\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      top\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"18.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Your friend\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"显示效果如图4-10所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(449),alt:\"图4-10\"}})]),t._v(\" \"),a(\"p\",[t._v(\"可以看到，由于第二个子文本组件没有定位，所以\"),a(\"code\",[t._v(\"fit\")]),t._v(\"属性会对它起作用，就会占满\"),a(\"code\",[t._v(\"Stack\")]),t._v(\"。由于\"),a(\"code\",[t._v(\"Stack\")]),t._v(\"子元素是堆叠的，所以第一个子文本组件被第二个遮住了，而第三个在最上层，所以可以正常显示。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/64.4621481d.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[64],{453:function(t,s,a){t.exports=a.p+\"assets/img/5-24.f113edc6.png\"},454:function(t,s){t.exports=\"data:image/png;base64,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\"},788:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_5-7-剪裁-clip\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_5-7-剪裁-clip\"}},[t._v(\"#\")]),t._v(\" 5.7 剪裁（Clip）\")]),t._v(\" \"),n(\"p\",[t._v(\"Flutter中提供了一些剪裁函数，用于对组件进行剪裁。\")]),t._v(\" \"),n(\"table\",[n(\"thead\",[n(\"tr\",[n(\"th\",[t._v(\"剪裁Widget\")]),t._v(\" \"),n(\"th\",[t._v(\"作用\")])])]),t._v(\" \"),n(\"tbody\",[n(\"tr\",[n(\"td\",[t._v(\"ClipOval\")]),t._v(\" \"),n(\"td\",[t._v(\"子组件为正方形时剪裁为内贴圆形，为矩形时，剪裁为内贴椭圆\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"ClipRRect\")]),t._v(\" \"),n(\"td\",[t._v(\"将子组件剪裁为圆角矩形\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"ClipRect\")]),t._v(\" \"),n(\"td\",[t._v(\"剪裁子组件到实际占用的矩形大小（溢出部分剪裁）\")])])])]),t._v(\" \"),n(\"p\",[t._v(\"下面看一个例子：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ClipTestRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 头像  \")]),t._v(\"\\n    Widget avatar \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"asset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"imgs/avatar.png\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"60.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          avatar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//不剪裁\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ClipOval\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" avatar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//剪裁为圆形\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ClipRRect\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//剪裁为圆角矩形\")]),t._v(\"\\n            borderRadius\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BorderRadius\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"circular\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"5.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" avatar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            mainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Align\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"topLeft\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                widthFactor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".5\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//宽度设为原来宽度一半，另一半会溢出\")]),t._v(\"\\n                child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" avatar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"你好世界\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"green\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            mainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ClipRect\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//将溢出部分剪裁\")]),t._v(\"\\n                child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Align\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"topLeft\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  widthFactor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".5\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//宽度设为原来宽度一半\")]),t._v(\"\\n                  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" avatar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"你好世界\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"green\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图5-24所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(453),alt:\"图5-24\"}})]),t._v(\" \"),n(\"p\",[t._v(\"上面示例代码注释比较详细，在此不再赘述。但值得一提的是最后的两个\"),n(\"code\",[t._v(\"Row\")]),t._v(\"！它们通过\"),n(\"code\",[t._v(\"Align\")]),t._v(\"设置\"),n(\"code\",[t._v(\"widthFactor\")]),t._v(\"为0.5后，图片的实际宽度等于60×0.5，即原宽度一半，但此时图片溢出部分依然会显示，所以第一个“你好世界”会和图片的另一部分重合，为了剪裁掉溢出部分，我们在第二个\"),n(\"code\",[t._v(\"Row\")]),t._v(\"中通过\"),n(\"code\",[t._v(\"ClipRect\")]),t._v(\"将溢出部分剪裁掉了。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"customclipper\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#customclipper\"}},[t._v(\"#\")]),t._v(\" CustomClipper\")]),t._v(\" \"),n(\"p\",[t._v(\"如果我们想剪裁子组件的特定区域，比如，在上面示例的图片中，如果我们只想截取图片中部40×30像素的范围应该怎么做？这时我们可以使用\"),n(\"code\",[t._v(\"CustomClipper\")]),t._v(\"来自定义剪裁区域，实现代码如下：\")]),t._v(\" \"),n(\"p\",[t._v(\"首先，自定义一个\"),n(\"code\",[t._v(\"CustomClipper\")]),t._v(\"：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyClipper\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CustomClipper\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Rect\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Rect \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getClip\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Size size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Rect\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fromLTWH\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"10.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"15.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"40.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"30.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  bool \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"shouldReclip\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"CustomClipper\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Rect\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" oldClipper\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"getClip()\")]),t._v(\"是用于获取剪裁区域的接口，由于图片大小是60×60，我们返回剪裁区域为\"),n(\"code\",[t._v(\"Rect.fromLTWH(10.0, 15.0, 40.0, 30.0)\")]),t._v(\"，即图片中部40×30像素的范围。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"shouldReclip()\")]),t._v(\" 接口决定是否重新剪裁。如果在应用中，剪裁区域始终不会发生变化时应该返回\"),n(\"code\",[t._v(\"false\")]),t._v(\"，这样就不会触发重新剪裁，避免不必要的性能开销。如果剪裁区域会发生变化（比如在对剪裁区域执行一个动画），那么变化后应该返回\"),n(\"code\",[t._v(\"true\")]),t._v(\"来重新执行剪裁。\")])]),t._v(\" \"),n(\"p\",[t._v(\"然后，我们通过\"),n(\"code\",[t._v(\"ClipRect\")]),t._v(\"来执行剪裁，为了看清图片实际所占用的位置，我们设置一个红色背景：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecoratedBox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  decoration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ClipRect\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      clipper\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyClipper\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//使用自定义的clipper\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" avatar\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图5-25所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(454),alt:\"图5-25\"}})]),t._v(\" \"),n(\"p\",[t._v(\"可以看到我们的剪裁成功了，但是图片所占用的空间大小仍然是60×60（红色区域），这是因为剪裁是在layout完成后的绘制阶段进行的，所以不会影响组件的大小，这和\"),n(\"code\",[t._v(\"Transform\")]),t._v(\"原理是相似的。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/65.2d58a524.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[65],{462:function(t,n,s){t.exports=s.p+\"assets/img/5-16.24d30a6e.png\"},463:function(t,n){t.exports=\"data:image/png;base64,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\"},790:function(t,n,s){\"use strict\";s.r(n);var a=s(45),e=Object(a.a)({},(function(){var t=this,n=t.$createElement,a=t._self._c||n;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_5-5-container\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_5-5-container\"}},[t._v(\"#\")]),t._v(\" 5.5 Container\")]),t._v(\" \"),a(\"p\",[t._v(\"我们在前面的章节示例中多次用到过\"),a(\"code\",[t._v(\"Container\")]),t._v(\"组件，本节我们就详细介绍一下\"),a(\"code\",[t._v(\"Container\")]),t._v(\"组件。\"),a(\"code\",[t._v(\"Container\")]),t._v(\"是一个组合类容器，它本身不对应具体的\"),a(\"code\",[t._v(\"RenderObject\")]),t._v(\"，它是\"),a(\"code\",[t._v(\"DecoratedBox\")]),t._v(\"、\"),a(\"code\",[t._v(\"ConstrainedBox、Transform\")]),t._v(\"、\"),a(\"code\",[t._v(\"Padding\")]),t._v(\"、\"),a(\"code\",[t._v(\"Align\")]),t._v(\"等组件组合的一个多功能容器，所以我们只需通过一个\"),a(\"code\",[t._v(\"Container\")]),t._v(\"组件可以实现同时需要装饰、变换、限制的场景。下面是\"),a(\"code\",[t._v(\"Container\")]),t._v(\"的定义：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//容器内补白，属于decoration的装饰范围\")]),t._v(\"\\n  Color color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 背景色\")]),t._v(\"\\n  Decoration decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 背景装饰\")]),t._v(\"\\n  Decoration foregroundDecoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//前景装饰\")]),t._v(\"\\n  double width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//容器的宽度\")]),t._v(\"\\n  double height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//容器的高度\")]),t._v(\"\\n  BoxConstraints constraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//容器大小的限制条件\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"margin\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//容器外补白，不属于decoration的装饰范围\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"transform\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//变换\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[a(\"code\",[t._v(\"Container\")]),t._v(\"的大多数属性在介绍其它容器时都已经介绍过了，不再赘述，但有两点需要说明：\")]),t._v(\" \"),a(\"ul\",[a(\"li\",[t._v(\"容器的大小可以通过\"),a(\"code\",[t._v(\"width\")]),t._v(\"、\"),a(\"code\",[t._v(\"height\")]),t._v(\"属性来指定，也可以通过\"),a(\"code\",[t._v(\"constraints\")]),t._v(\"来指定；如果它们同时存在时，\"),a(\"code\",[t._v(\"width\")]),t._v(\"、\"),a(\"code\",[t._v(\"height\")]),t._v(\"优先。实际上Container内部会根据\"),a(\"code\",[t._v(\"width\")]),t._v(\"、\"),a(\"code\",[t._v(\"height\")]),t._v(\"来生成一个\"),a(\"code\",[t._v(\"constraints\")]),t._v(\"。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"color\")]),t._v(\"和\"),a(\"code\",[t._v(\"decoration\")]),t._v(\"是互斥的，如果同时设置它们则会报错！实际上，当指定\"),a(\"code\",[t._v(\"color\")]),t._v(\"时，\"),a(\"code\",[t._v(\"Container\")]),t._v(\"内会自动创建一个\"),a(\"code\",[t._v(\"decoration\")]),t._v(\"。\")])]),t._v(\" \"),a(\"h3\",{attrs:{id:\"实例\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#实例\"}},[t._v(\"#\")]),t._v(\" 实例\")]),t._v(\" \"),a(\"p\",[t._v(\"我们通过\"),a(\"code\",[t._v(\"Container\")]),t._v(\"来实现如图5-16所示的卡片：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:s(462),alt:\"图5-16\"}})]),t._v(\" \"),a(\"p\",[t._v(\"实现代码如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  margin\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"only\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"top\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" left\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"120.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//容器外填充\")]),t._v(\"\\n  constraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BoxConstraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"tightFor\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"150.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//卡片大小\")]),t._v(\"\\n  decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//背景装饰\")]),t._v(\"\\n      gradient\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RadialGradient\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//背景径向渐变\")]),t._v(\"\\n          colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"orange\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          center\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"topLeft\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".98\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      boxShadow\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//卡片阴影\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxShadow\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"black54\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            offset\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            blurRadius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"4.0\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  transform\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Matrix4\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"rotationZ\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".2\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//卡片倾斜变换\")]),t._v(\"\\n  alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//卡片内文字居中\")]),t._v(\"\\n  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//卡片文字\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"5.20\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" style\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" fontSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"40.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"可以看到\"),a(\"code\",[t._v(\"Container\")]),t._v(\"具备多种组件的功能，通过查看\"),a(\"code\",[t._v(\"Container\")]),t._v(\"源码，我们会很容易发现它正是前面我们介绍过的多种组件组合而成。在Flutter中，\"),a(\"code\",[t._v(\"Container\")]),t._v(\"组件也正是组合优先于继承的实例。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"padding和margin\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#padding和margin\"}},[t._v(\"#\")]),t._v(\" Padding和Margin\")]),t._v(\" \"),a(\"p\",[t._v(\"接下来我们来研究一下\"),a(\"code\",[t._v(\"Container\")]),t._v(\"组件\"),a(\"code\",[t._v(\"margin\")]),t._v(\"和\"),a(\"code\",[t._v(\"padding\")]),t._v(\"属性的区别:\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  margin\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//容器外补白\")]),t._v(\"\\n  color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"orange\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hello world!\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//容器内补白\")]),t._v(\"\\n  color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"orange\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hello world!\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\")])])]),a(\"p\",[a(\"img\",{attrs:{src:s(463),alt:\"图5-17\"}})]),t._v(\" \"),a(\"p\",[t._v(\"可以发现，直观的感觉就是\"),a(\"code\",[t._v(\"margin\")]),t._v(\"的留白是在容器外部，而\"),a(\"code\",[t._v(\"padding\")]),t._v(\"的留白是在容器内部，读者需要记住这个差异。事实上，\"),a(\"code\",[t._v(\"Container\")]),t._v(\"内\"),a(\"code\",[t._v(\"margin\")]),t._v(\"和\"),a(\"code\",[t._v(\"padding\")]),t._v(\"都是通过\"),a(\"code\",[t._v(\"Padding\")]),t._v(\" 组件来实现的，上面的示例代码实际上等价于：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecoratedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"orange\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hello world!\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecoratedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"orange\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hello world!\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"    \\n\")])])])])}),[],!1,null,null,null);n.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/66.5df03e62.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[66],{479:function(t,s,n){t.exports=n.p+\"assets/img/6-12.d5a5a078.png\"},480:function(t,s,n){t.exports=n.p+\"assets/img/6-13.7ffa2d43.png\"},797:function(t,s,n){\"use strict\";n.r(s);var a=n(45),e=Object(a.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_6-5-customscrollview\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_6-5-customscrollview\"}},[t._v(\"#\")]),t._v(\" 6.5 CustomScrollView\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"CustomScrollView\")]),t._v(\"是可以使用Sliver来自定义滚动模型（效果）的组件。它可以包含多种滚动模型，举个例子，假设有一个页面，顶部需要一个\"),a(\"code\",[t._v(\"GridView\")]),t._v(\"，底部需要一个\"),a(\"code\",[t._v(\"ListView\")]),t._v(\"，而要求整个页面的滑动效果是统一的，即它们看起来是一个整体。如果使用\"),a(\"code\",[t._v(\"GridView\")]),t._v(\"+\"),a(\"code\",[t._v(\"ListView\")]),t._v('来实现的话，就不能保证一致的滑动效果，因为它们的滚动效果是分离的，所以这时就需要一个\"胶水\"，把这些彼此独立的可滚动组件\"粘\"起来，而'),a(\"code\",[t._v(\"CustomScrollView\")]),t._v(\"的功能就相当于“胶水”。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"可滚动组件的sliver版\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#可滚动组件的sliver版\"}},[t._v(\"#\")]),t._v(\" 可滚动组件的Sliver版\")]),t._v(\" \"),a(\"p\",[t._v(\"Sliver在前面讲过，有细片、薄片之意，在Flutter中，Sliver通常指可滚动组件子元素（就像一个个薄片一样）。但是在\"),a(\"code\",[t._v(\"CustomScrollView\")]),t._v(\"中，需要粘起来的可滚动组件就是\"),a(\"code\",[t._v(\"CustomScrollView\")]),t._v(\"的Sliver了，如果直接将\"),a(\"code\",[t._v(\"ListView\")]),t._v(\"、\"),a(\"code\",[t._v(\"GridView\")]),t._v(\"作为\"),a(\"code\",[t._v(\"CustomScrollView\")]),t._v(\"是不行的，因为它们本身是可滚动组件而并不是Sliver！因此，为了能让可滚动组件能和\"),a(\"code\",[t._v(\"CustomScrollView\")]),t._v(\"配合使用，Flutter提供了一些可滚动组件的Sliver版，如SliverList、SliverGrid等。实际上Sliver版的可滚动组件和非Sliver版的可滚动组件最大的区别就是\"),a(\"strong\",[t._v(\"前者不包含滚动模型（自身不能再滚动），而后者包含滚动模型\")]),t._v(\" ，也正因如此，\"),a(\"code\",[t._v(\"CustomScrollView\")]),t._v('才可以将多个Sliver\"粘\"在一起，这些Sliver共用'),a(\"code\",[t._v(\"CustomScrollView\")]),t._v(\"的\"),a(\"code\",[t._v(\"Scrollable\")]),t._v(\"，所以最终才实现了统一的滑动效果。\")]),t._v(\" \"),a(\"blockquote\",[a(\"p\",[t._v(\"Sliver系列Widget比较多，我们不会一一介绍，读者只需记住它的特点，需要时再去查看文档即可。上面之所以说“大多数”Sliver都和可滚动组件对应，是由于还有一些如SliverPadding、SliverAppBar等是和可滚动组件无关的，它们主要是为了结合CustomScrollView一起使用，这是因为\"),a(\"strong\",[t._v(\"CustomScrollView的子组件必须都是Sliver\")]),t._v(\"。\")])]),t._v(\" \"),a(\"h3\",{attrs:{id:\"示例\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CustomScrollViewTestRoute\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//因为本路由没有使用Scaffold，为了让子级Widget(如Text)使用\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//Material Design 默认的样式风格,我们使用Material作为本路由的根。\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Material\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CustomScrollView\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        slivers\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//AppBar，包含一个导航栏\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SliverAppBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            pinned\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            expandedHeight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"250.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            flexibleSpace\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlexibleSpaceBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              title\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Demo'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              background\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Image\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"asset\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"./images/avatar.png\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" fit\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BoxFit\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cover\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SliverPadding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"8.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            sliver\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"SliverGrid\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//Grid\")]),t._v(\"\\n              gridDelegate\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"SliverGridDelegateWithFixedCrossAxisCount\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                crossAxisCount\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//Grid按两列显示\")]),t._v(\"\\n                mainAxisSpacing\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"10.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                crossAxisSpacing\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"10.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                childAspectRatio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"4.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              delegate\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"SliverChildBuilderDelegate\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int index\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//创建子widget      \")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cyan\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"index \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"%\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"9\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'grid item $index'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                childCount\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//List\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"SliverFixedExtentList\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            itemExtent\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            delegate\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"SliverChildBuilderDelegate\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int index\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//创建列表项      \")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"lightBlue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"index \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"%\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"9\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'list item $index'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                childCount\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//50个列表项\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"代码分为三部分：\")]),t._v(\" \"),a(\"ul\",[a(\"li\",[t._v(\"头部\"),a(\"code\",[t._v(\"SliverAppBar\")]),t._v(\"：\"),a(\"code\",[t._v(\"SliverAppBar\")]),t._v(\"对应\"),a(\"code\",[t._v(\"AppBar\")]),t._v(\"，两者不同之处在于\"),a(\"code\",[t._v(\"SliverAppBar\")]),t._v(\"可以集成到\"),a(\"code\",[t._v(\"CustomScrollView\")]),t._v(\"。\"),a(\"code\",[t._v(\"SliverAppBar\")]),t._v(\"可以结合\"),a(\"code\",[t._v(\"FlexibleSpaceBar\")]),t._v(\"实现Material Design中头部伸缩的模型，具体效果，读者可以运行该示例查看。\")]),t._v(\" \"),a(\"li\",[t._v(\"中间的\"),a(\"code\",[t._v(\"SliverGrid\")]),t._v(\"：它用\"),a(\"code\",[t._v(\"SliverPadding\")]),t._v(\"包裹以给\"),a(\"code\",[t._v(\"SliverGrid\")]),t._v(\"添加补白。\"),a(\"code\",[t._v(\"SliverGrid\")]),t._v(\"是一个两列，宽高比为4的网格，它有20个子组件。\")]),t._v(\" \"),a(\"li\",[t._v(\"底部\"),a(\"code\",[t._v(\"SliverFixedExtentList\")]),t._v(\"：它是一个所有子元素高度都为50像素的列表。\")])]),t._v(\" \"),a(\"p\",[t._v(\"运行效果如图：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(479),alt:\"图6-12\"}}),a(\"img\",{attrs:{src:n(480),alt:\"图6-13\"}})])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/67.73c671d9.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[67],{507:function(t,s,a){t.exports=a.p+\"assets/img/7-8.c316cc7f.png\"},508:function(t,s,a){t.exports=a.p+\"assets/img/7-9.62d054b0.png\"},805:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_7-5-异步ui更新-futurebuilder、streambuilder\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_7-5-异步ui更新-futurebuilder、streambuilder\"}},[t._v(\"#\")]),t._v(\" 7.5 异步UI更新（FutureBuilder、StreamBuilder）\")]),t._v(\" \"),n(\"p\",[t._v(\"很多时候我们会依赖一些异步数据来动态更新UI，比如在打开一个页面时我们需要先从互联网上获取数据，在获取数据的过程中我们显示一个加载框，等获取到数据时我们再渲染页面；又比如我们想展示Stream（比如文件流、互联网数据接收流）的进度。当然，通过StatefulWidget我们完全可以实现上述这些功能。但由于在实际开发中依赖异步数据更新UI的这种场景非常常见，因此Flutter专门提供了\"),n(\"code\",[t._v(\"FutureBuilder\")]),t._v(\"和\"),n(\"code\",[t._v(\"StreamBuilder\")]),t._v(\"两个组件来快速实现这种功能。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_7-5-1-futurebuilder\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_7-5-1-futurebuilder\"}},[t._v(\"#\")]),t._v(\" 7.5.1 FutureBuilder\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"FutureBuilder\")]),t._v(\"会依赖一个\"),n(\"code\",[t._v(\"Future\")]),t._v(\"，它会根据所依赖的\"),n(\"code\",[t._v(\"Future\")]),t._v(\"的状态来动态构建自身。我们看一下\"),n(\"code\",[t._v(\"FutureBuilder\")]),t._v(\"构造函数：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FutureBuilder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"future\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"initialData\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"ul\",[n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"future\")]),t._v(\"：\"),n(\"code\",[t._v(\"FutureBuilder\")]),t._v(\"依赖的\"),n(\"code\",[t._v(\"Future\")]),t._v(\"，通常是一个异步耗时任务。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"initialData\")]),t._v(\"：初始数据，用户设置默认数据。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"builder\")]),t._v(\"：Widget构建器；该构建器会在\"),n(\"code\",[t._v(\"Future\")]),t._v(\"执行的不同阶段被多次调用，构建器签名如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"Function\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" AsyncSnapshot snapshot\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"snapshot\")]),t._v(\"会包含当前异步任务的状态信息及结果信息 ，比如我们可以通过\"),n(\"code\",[t._v(\"snapshot.connectionState\")]),t._v(\"获取异步任务的状态信息、通过\"),n(\"code\",[t._v(\"snapshot.hasError\")]),t._v(\"判断异步任务是否有错误等等，完整的定义读者可以查看\"),n(\"code\",[t._v(\"AsyncSnapshot\")]),t._v(\"类定义。\")]),t._v(\" \"),n(\"p\",[t._v(\"另外，\"),n(\"code\",[t._v(\"FutureBuilder\")]),t._v(\"的\"),n(\"code\",[t._v(\"builder\")]),t._v(\"函数签名和\"),n(\"code\",[t._v(\"StreamBuilder\")]),t._v(\"的\"),n(\"code\",[t._v(\"builder\")]),t._v(\"是相同的。\")])])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"示例\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),n(\"p\",[t._v(\"我们实现一个路由，当该路由打开时我们从网上获取数据，获取数据时弹一个加载框；获取结束时，如果成功则显示获取到的数据，如果失败则显示错误。由于我们还没有介绍在flutter中如何发起网络请求，所以在这里我们不真正去网络请求数据，而是模拟一下这个过程，隔3秒后返回一个字符串：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"mockNetworkData\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" Future\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"delayed\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"我是从互联网上获取的数据\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"FutureBuilder\")]),t._v(\"使用代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\nWidget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" FutureBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      future\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"mockNetworkData\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" AsyncSnapshot snapshot\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 请求已结束\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"snapshot\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"connectionState \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" ConnectionState\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"done\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"snapshot\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"hasError\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 请求失败，显示错误\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Error: ${snapshot.error}\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 请求成功，显示数据\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Contents: ${snapshot.data}\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 请求未结束，显示loading\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircularProgressIndicator\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行结果如图7-8、7-9所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(507),alt:\"图7-8\"}}),n(\"img\",{attrs:{src:a(508),alt:\"图7-9\"}})]),t._v(\" \"),n(\"p\",[t._v(\"上面代码中我们在\"),n(\"code\",[t._v(\"builder\")]),t._v(\"中根据当前异步任务状态\"),n(\"code\",[t._v(\"ConnectionState\")]),t._v(\"来返回不同的widget。\"),n(\"code\",[t._v(\"ConnectionState\")]),t._v(\"是一个枚举类，定义如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"enum\")]),t._v(\" ConnectionState \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// 当前没有异步任务，比如[FutureBuilder]的[future]为null时\")]),t._v(\"\\n  none\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// 异步任务处于等待状态\")]),t._v(\"\\n  waiting\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// Stream处于激活状态（流上已经有数据传递了），对于FutureBuilder没有该状态。\")]),t._v(\"\\n  active\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// 异步任务已经终止.\")]),t._v(\"\\n  done\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"注意，\"),n(\"code\",[t._v(\"ConnectionState.active\")]),t._v(\"只在\"),n(\"code\",[t._v(\"StreamBuilder\")]),t._v(\"中才会出现。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_7-5-2-streambuilder\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_7-5-2-streambuilder\"}},[t._v(\"#\")]),t._v(\" 7.5.2 StreamBuilder\")]),t._v(\" \"),n(\"p\",[t._v(\"我们知道，在Dart中\"),n(\"code\",[t._v(\"Stream\")]),t._v(\" 也是用于接收异步事件数据，和\"),n(\"code\",[t._v(\"Future\")]),t._v(\" 不同的是，它可以接收多个异步操作的结果，它常用于会多次读取数据的异步任务场景，如网络内容下载、文件读写等。\"),n(\"code\",[t._v(\"StreamBuilder\")]),t._v(\"正是用于配合\"),n(\"code\",[t._v(\"Stream\")]),t._v(\"来展示流上事件（数据）变化的UI组件。下面看一下\"),n(\"code\",[t._v(\"StreamBuilder\")]),t._v(\"的默认构造函数：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"StreamBuilder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"initialData\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  Stream\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" stream\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \\n\")])])]),n(\"p\",[t._v(\"可以看到和\"),n(\"code\",[t._v(\"FutureBuilder\")]),t._v(\"的构造函数只有一点不同：前者需要一个\"),n(\"code\",[t._v(\"future\")]),t._v(\"，而后者需要一个\"),n(\"code\",[t._v(\"stream\")]),t._v(\"。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"示例-2\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例-2\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),n(\"p\",[t._v(\"我们创建一个计时器的示例：每隔1秒，计数加1。这里，我们使用\"),n(\"code\",[t._v(\"Stream\")]),t._v(\"来实现每隔一秒生成一个数字:\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Stream\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"int\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"counter\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" Stream\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"periodic\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"i\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" i\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"StreamBuilder\")]),t._v(\"使用代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"  \\n Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" StreamBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"int\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      stream\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"counter\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//initialData: ,// a Stream<int> or null\")]),t._v(\"\\n      builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" AsyncSnapshot\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"int\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" snapshot\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"snapshot\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"hasError\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Error: ${snapshot.error}'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"switch\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"snapshot\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"connectionState\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" ConnectionState\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"none\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'没有Stream'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" ConnectionState\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"waiting\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'等待数据...'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" ConnectionState\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"active\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'active: ${snapshot.data}'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" ConnectionState\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"done\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Stream已关闭'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// unreachable\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"读者可以自己运行本示例查看运行结果。注意，本示例只是为了演示\"),n(\"code\",[t._v(\"StreamBuilder\")]),t._v(\"的使用，在实战中，凡是UI会依赖多个异步数据而发生变化的场景都可以使用\"),n(\"code\",[t._v(\"StreamBuilder\")]),t._v(\"。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/68.4de1c271.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[68],{510:function(t,s,a){t.exports=a.p+\"assets/img/7-2.20458eff.png\"},511:function(t,s,a){t.exports=a.p+\"assets/img/7-3.531c5fdf.png\"},808:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_7-3-跨组件状态共享-provider\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_7-3-跨组件状态共享-provider\"}},[t._v(\"#\")]),t._v(\" 7.3 跨组件状态共享（Provider）\")]),t._v(\" \"),n(\"p\",[t._v(\"在Flutter开发中，状态管理是一个永恒的话题。一般的原则是：如果状态是组件私有的，则应该由组件自己管理；如果状态要跨组件共享，则该状态应该由各个组件共同的父元素来管理。对于组件私有的状态管理很好理解，但对于跨组件共享的状态，管理的方式就比较多了，如使用全局事件总线EventBus（将在下一章中介绍），它是一个观察者模式的实现，通过它就可以实现跨组件状态同步：状态持有方（发布者）负责更新、发布状态，状态使用方（观察者）监听状态改变事件来执行一些操作。下面我们看一个登陆状态同步的简单示例：\")]),t._v(\" \"),n(\"p\",[t._v(\"定义事件：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"enum\")]),t._v(\" Event\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  login\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略其它事件\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"登录页代码大致如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 登录状态改变后发布状态改变事件\")]),t._v(\"\\nbus\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"emit\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Event\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"login\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"依赖登录状态的页面：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onLoginChanged\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//登录状态变化处理逻辑\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//订阅登录状态改变事件\")]),t._v(\"\\n  bus\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"on\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Event\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"login\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"onLogin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//取消订阅\")]),t._v(\"\\n  bus\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"off\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Event\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"login\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"onLogin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们可以发现，通过观察者模式来实现跨组件状态共享有一些明显的缺点：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"必须显式定义各种事件，不好管理\")]),t._v(\" \"),n(\"li\",[t._v(\"订阅者必须需显式注册状态改变回调，也必须在组件销毁时手动去解绑回调以避免内存泄露。\")])]),t._v(\" \"),n(\"p\",[t._v(\"在Flutter当中有没有更好的跨组件状态管理方式了呢？答案是肯定的，那怎么做的？我们想想前面介绍的\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"，它的天生特性就是能绑定\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"与依赖它的子孙组件的依赖关系，并且当\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"数据发生变化时，可以自动更新依赖的子孙组件！利用这个特性，我们可以将需要跨组件共享的状态保存在\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"中，然后在子组件中引用\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"即可，Flutter社区著名的Provider包正是基于这个思想实现的一套跨组件状态共享解决方案，接下来我们便详细介绍一下Provider的用法及原理。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"provider\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#provider\"}},[t._v(\"#\")]),t._v(\" Provider\")]),t._v(\" \"),n(\"p\",[t._v(\"为了加强读者的理解，我们不直接去看Provider包的源代码，相反，我会带着你根据上面描述的通过\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"实现的思路来一步一步地实现一个最小功能的Provider。\")]),t._v(\" \"),n(\"p\",[t._v(\"首先，我们需要一个保存需要共享的数据\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"，由于具体业务数据类型不可预期，为了通用性，我们使用泛型，定义一个通用的\"),n(\"code\",[t._v(\"InheritedProvider\")]),t._v(\"类，它继承自\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 一个通用的InheritedWidget，保存任需要跨组件共享的状态\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"InheritedProvider\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"InheritedWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"InheritedProvider\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Widget child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//共享状态使用泛型\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" T data\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  bool \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"updateShouldNotify\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"InheritedProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" old\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//在此简单返回true，则每次更新都会调用依赖其的子孙节点的`didChangeDependencies`。\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"数据保存的地方有了，那么接下来我们需要做的就是在数据发生变化的时候来重新构建\"),n(\"code\",[t._v(\"InheritedProvider\")]),t._v(\"，那么现在就面临两个问题：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"数据发生变化怎么通知？\")]),t._v(\" \"),n(\"li\",[t._v(\"谁来重新构建\"),n(\"code\",[t._v(\"InheritedProvider\")]),t._v(\"？\")])]),t._v(\" \"),n(\"p\",[t._v(\"第一个问题其实很好解决，我们当然可以使用之前介绍的eventBus来进行事件通知，但是为了更贴近Flutter开发，我们使用Flutter SDK中提供的\"),n(\"code\",[t._v(\"ChangeNotifier\")]),t._v(\"类 ，它继承自\"),n(\"code\",[t._v(\"Listenable\")]),t._v(\"，也实现了一个Flutter风格的发布者-订阅者模式，\"),n(\"code\",[t._v(\"ChangeNotifier\")]),t._v(\"定义大致如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ChangeNotifier\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"implements\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Listenable\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  List listeners\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"VoidCallback listener\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//添加监听器\")]),t._v(\"\\n     listeners\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"listener\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"removeListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"VoidCallback listener\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//移除监听器\")]),t._v(\"\\n    listeners\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"remove\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"listener\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"notifyListeners\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//通知所有监听器，触发监听器回调 \")]),t._v(\"\\n    listeners\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"forEach\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"item\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"item\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n   \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\")])])]),n(\"p\",[t._v(\"我们可以通过调用\"),n(\"code\",[t._v(\"addListener()\")]),t._v(\"和\"),n(\"code\",[t._v(\"removeListener()\")]),t._v(\"来添加、移除监听器（订阅者）；通过调用\"),n(\"code\",[t._v(\"notifyListeners()\")]),t._v(\" 可以触发所有监听器回调。\")]),t._v(\" \"),n(\"p\",[t._v(\"现在，我们将要共享的状态放到一个Model类中，然后让它继承自\"),n(\"code\",[t._v(\"ChangeNotifier\")]),t._v(\"，这样当共享的状态改变时，我们只需要调用\"),n(\"code\",[t._v(\"notifyListeners()\")]),t._v(\" 来通知订阅者，然后由订阅者来重新构建\"),n(\"code\",[t._v(\"InheritedProvider\")]),t._v(\"，这也是第二个问题的答案！接下来我们便实现这个订阅者类：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ChangeNotifierProvider\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ChangeNotifier\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ChangeNotifierProvider\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Widget child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" T data\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//定义一个便捷方法，方便子树中的widget获取共享数据\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" T of\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" type \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _typeOf\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"InheritedProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">>\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" provider \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"  context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dependOnInheritedWidgetOfExactType\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"InheritedProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">>\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" provider\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _ChangeNotifierProviderState\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _ChangeNotifierProviderState\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"该类继承\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"，然后定义了一个\"),n(\"code\",[t._v(\"of()\")]),t._v(\"静态方法供子类方便获取Widget树中的\"),n(\"code\",[t._v(\"InheritedProvider\")]),t._v(\"中保存的共享状态(model)，下面我们实现该类对应的\"),n(\"code\",[t._v(\"_ChangeNotifierProviderState\")]),t._v(\"类：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_ChangeNotifierProviderState\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ChangeNotifier\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ChangeNotifierProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">>\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"update\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//如果数据发生变化（model类调用了notifyListeners），重新构建InheritedProvider\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didUpdateWidget\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ChangeNotifierProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" oldWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v('//当Provider更新时，如果新旧数据不\"==\"，则解绑旧数据监听，同时添加新数据监听')]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" oldWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      oldWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"removeListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"update\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"update\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didUpdateWidget\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"oldWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 给model添加监听器\")]),t._v(\"\\n    widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"update\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 移除model的监听器\")]),t._v(\"\\n    widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"removeListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"update\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" InheritedProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      data\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"可以看到\"),n(\"code\",[t._v(\"_ChangeNotifierProviderState\")]),t._v(\"类的主要作用就是监听到共享状态（model）改变时重新构建Widget树。注意，在\"),n(\"code\",[t._v(\"_ChangeNotifierProviderState\")]),t._v(\"类中调用\"),n(\"code\",[t._v(\"setState()\")]),t._v(\"方法，\"),n(\"code\",[t._v(\"widget.child\")]),t._v(\"始终是同一个，所以执行build时，\"),n(\"code\",[t._v(\"InheritedProvider\")]),t._v(\"的child引用的始终是同一个子widget，所以\"),n(\"code\",[t._v(\"widget.child\")]),t._v(\"并不会重新\"),n(\"code\",[t._v(\"build\")]),t._v(\"，这也就相当于对\"),n(\"code\",[t._v(\"child\")]),t._v(\"进行了缓存！当然如果\"),n(\"code\",[t._v(\"ChangeNotifierProvider\")]),t._v(\"父级Widget重新build时，则其传入的\"),n(\"code\",[t._v(\"child\")]),t._v(\"便有可能会发生变化。\")]),t._v(\" \"),n(\"p\",[t._v(\"现在我们所需要的各个工具类都已完成，下面我们通过一个购物车的例子来看看怎么使用上面的这些类。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"购物车示例\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#购物车示例\"}},[t._v(\"#\")]),t._v(\" 购物车示例\")]),t._v(\" \"),n(\"p\",[t._v(\"我们需要实现一个显示购物车中所有商品总价的功能：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"向购物车中添加新商品时总价更新\")])]),t._v(\" \"),n(\"p\",[t._v(\"定义一个\"),n(\"code\",[t._v(\"Item\")]),t._v(\"类，用于表示商品信息：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Item\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Item\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"price\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"count\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  double price\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//商品单价\")]),t._v(\"\\n  int count\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 商品份数\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//... 省略其它属性\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"定义一个保存购物车内商品数据的\"),n(\"code\",[t._v(\"CartModel\")]),t._v(\"类:\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CartModel\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ChangeNotifier\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 用于保存购物车中商品列表\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" List\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Item\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _items \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 禁止改变购物车里的商品信息\")]),t._v(\"\\n  UnmodifiableListView\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Item\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" items \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"UnmodifiableListView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_items\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 购物车中商品的总价\")]),t._v(\"\\n  double \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" totalPrice \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"\\n      _items\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fold\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" item\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" value \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" item\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"count \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" item\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"price\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 将 [item] 添加到购物车。这是唯一一种能从外部改变购物车的方法。\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Item item\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    _items\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"item\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 通知监听器（订阅者），重新构建InheritedProvider， 更新状态。\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"notifyListeners\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"CartModel\")]),t._v(\"即要跨组件共享的model类。最后我们构建示例页面：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ProviderRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _ProviderRouteState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_ProviderRouteState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_ProviderRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ProviderRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ChangeNotifierProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"CartModel\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        data\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CartModel\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" cart\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"ChangeNotifierProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"of\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"CartModel\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"总价: ${cart.totalPrice}\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"RaisedButton build\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//在后面优化部分会用到\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"添加商品\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//给购物车中添加商品，添加后总价会更新\")]),t._v(\"\\n                    ChangeNotifierProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"of\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"CartModel\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Item\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行示例后效果如图7-2所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(510),alt:\"provider\"}})]),t._v(\" \"),n(\"p\",[t._v(\"每次点击”添加商品“按钮，总价就会增加20，我们期望的功能实现了！可能有些读者会疑惑，我们饶了一大圈实现这么简单的功能有意义么？其实，就这个例子来看，只是更新同一个路由页中的一个状态，我们使用\"),n(\"code\",[t._v(\"ChangeNotifierProvider\")]),t._v(\"的优势并不明显，但是如果我们是做一个购物APP呢？由于购物车数据是通常是会在整个APP中共享的，比如会跨路由共享。如果我们将\"),n(\"code\",[t._v(\"ChangeNotifierProvider\")]),t._v(\"放在整个应用的Widget树的根上，那么整个APP就可以共享购物车的数据了，这时\"),n(\"code\",[t._v(\"ChangeNotifierProvider\")]),t._v(\"的优势将会非常明显。\")]),t._v(\" \"),n(\"p\",[t._v(\"虽然上面的例子比较简单，但它却将Provider的原理和流程体现的很清楚，图7-3是Provider的原理图：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(511),alt:\"图7-3\"}})]),t._v(\" \"),n(\"p\",[t._v(\"Model变化后会自动通知\"),n(\"code\",[t._v(\"ChangeNotifierProvider\")]),t._v(\"（订阅者），\"),n(\"code\",[t._v(\"ChangeNotifierProvider\")]),t._v(\"内部会重新构建\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"，而依赖该\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"的子孙Widget就会更新。\")]),t._v(\" \"),n(\"p\",[t._v(\"我们可以发现使用Provider，将会带来如下收益：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"我们的业务代码更关注数据了，只要更新Model，则UI会自动更新，而不用在状态改变后再去手动调用\"),n(\"code\",[t._v(\"setState()\")]),t._v(\"来显式更新页面。\")]),t._v(\" \"),n(\"li\",[t._v(\"数据改变的消息传递被屏蔽了，我们无需手动去处理状态改变事件的发布和订阅了，这一切都被封装在Provider中了。这真的很棒，帮我们省掉了大量的工作！\")]),t._v(\" \"),n(\"li\",[t._v(\"在大型复杂应用中，尤其是需要全局共享的状态非常多时，使用Provider将会大大简化我们的代码逻辑，降低出错的概率，提高开发效率。\")])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"优化\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#优化\"}},[t._v(\"#\")]),t._v(\" 优化\")]),t._v(\" \"),n(\"p\",[t._v(\"我们上面实现的\"),n(\"code\",[t._v(\"ChangeNotifierProvider\")]),t._v(\"是有两个明显缺点：代码组织问题和性能问题，下面我们一一讨论。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"代码组织问题\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#代码组织问题\"}},[t._v(\"#\")]),t._v(\" 代码组织问题\")]),t._v(\" \"),n(\"p\",[t._v(\"我们先看一下构建显示总价Text的代码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" cart\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"ChangeNotifierProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"of\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"CartModel\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"总价: ${cart.totalPrice}\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"这段代码有两点可以优化：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"需要显式调用\"),n(\"code\",[t._v(\"ChangeNotifierProvider.of\")]),t._v(\"，当APP内部依赖\"),n(\"code\",[t._v(\"CartModel\")]),t._v(\"很多时，这样的代码将很冗余。\")]),t._v(\" \"),n(\"li\",[t._v(\"语义不明确；由于\"),n(\"code\",[t._v(\"ChangeNotifierProvider\")]),t._v(\"是订阅者，那么依赖\"),n(\"code\",[t._v(\"CartModel\")]),t._v(\"的Widget自然就是订阅者，其实也就是状态的消费者，如果我们用\"),n(\"code\",[t._v(\"Builder\")]),t._v(\" 来构建，语义就不是很明确；如果我们能使用一个具有明确语义的Widget，比如就叫\"),n(\"code\",[t._v(\"Consumer\")]),t._v(\"，这样最终的代码语义将会很明确，只要看到\"),n(\"code\",[t._v(\"Consumer\")]),t._v(\"，我们就知道它是依赖某个跨组件或全局的状态。\")])]),t._v(\" \"),n(\"p\",[t._v(\"为了优化这两个问题，我们可以封装一个\"),n(\"code\",[t._v(\"Consumer\")]),t._v(\" Widget，实现如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 这是一个便捷类，会获得当前context和指定数据类型的Provider\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Consumer\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Consumer\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Widget child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"Function\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" T value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      ChangeNotifierProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"of\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//自动获取Model\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"Consumer\")]),t._v(\"实现非常简单，它通过指定模板参数，然后再内部自动调用\"),n(\"code\",[t._v(\"ChangeNotifierProvider.of\")]),t._v(\"获取相应的Model，并且\"),n(\"code\",[t._v(\"Consumer\")]),t._v(\"这个名字本身也是具有确切语义（消费者）。现在上面的代码块可以优化为如下这样：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Consumer\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"CartModel\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" cart\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"总价: ${cart.totalPrice}\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"是不是很优雅！\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"性能问题\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#性能问题\"}},[t._v(\"#\")]),t._v(\" 性能问题\")]),t._v(\" \"),n(\"p\",[t._v(\"上面的代码还有一个性能问题，就在构建”添加按钮“的代码处：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"RaisedButton build\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 构建时输出日志\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"添加商品\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      ChangeNotifierProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"of\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"CartModel\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Item\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v('我们点击”添加商品“按钮后，由于购物车商品总价会变化，所以显示总价的Text更新是符合预期的，但是”添加商品“按钮本身没有变化，是不应该被重新build的。但是我们运行示例，每次点击”添加商品“按钮，控制台都会输出\"RaisedButton build\"日志，也就是说”添加商品“按钮在每次点击时其自身都会重新build！这是为什么呢？如果你已经理解了'),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"的更新机制，那么答案一眼就能看出：这是因为构建\"),n(\"code\",[t._v(\"RaisedButton\")]),t._v(\"的\"),n(\"code\",[t._v(\"Builder\")]),t._v(\"中调用了\"),n(\"code\",[t._v(\"ChangeNotifierProvider.of\")]),t._v(\"，也就是说依赖了Widget树上面的\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"（即\"),n(\"code\",[t._v(\"InheritedProvider\")]),t._v(\" ）Widget，所以当添加完商品后，\"),n(\"code\",[t._v(\"CartModel\")]),t._v(\"发生变化，会通知\"),n(\"code\",[t._v(\"ChangeNotifierProvider\")]),t._v(\", 而\"),n(\"code\",[t._v(\"ChangeNotifierProvider\")]),t._v(\"则会重新构建子树，所以\"),n(\"code\",[t._v(\"InheritedProvider\")]),t._v(\"将会更新，此时依赖它的子孙Widget就会被重新构建。\")]),t._v(\" \"),n(\"p\",[t._v(\"问题的原因搞清楚了，那么我们如何避免这不必要重构呢？既然按钮重新被build是因为按钮和\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"建立了依赖关系，那么我们只要打破或解除这种依赖关系就可以了。那么如何解除按钮和\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"的依赖关系呢？我们上一节介绍\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"时已经讲过了：调用\"),n(\"code\",[t._v(\"dependOnInheritedWidgetOfExactType()\")]),t._v(\" 和 \"),n(\"code\",[t._v(\"getElementForInheritedWidgetOfExactType()\")]),t._v(\"的区别就是前者会注册依赖关系，而后者不会。所以我们只需要将\"),n(\"code\",[t._v(\"ChangeNotifierProvider.of\")]),t._v(\"的实现改为下面这样即可：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//添加一个listen参数，表示是否建立依赖关系\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" T of\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"bool listen \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" type \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _typeOf\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"InheritedProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">>\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" provider \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" listen\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dependOnInheritedWidgetOfExactType\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"InheritedProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">>\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"getElementForInheritedWidgetOfExactType\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"InheritedProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">>\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"widget\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"as\")]),t._v(\" InheritedProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" provider\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"然后我们将调用部分代码改为：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n      Consumer\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"CartModel\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" cart\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"总价: ${cart.totalPrice}\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"RaisedButton build\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"添加商品\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// listen 设为false，不建立依赖关系\")]),t._v(\"\\n            ChangeNotifierProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"of\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"CartModel\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" listen\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Item\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v('修改后再次运行上面的示例，我们会发现点击”添加商品“按钮后，控制台不会再输出\"RaisedButton build\"了，即按钮不会被重新构建了。而总价仍然会更新，这是因为'),n(\"code\",[t._v(\"Consumer\")]),t._v(\"中调用\"),n(\"code\",[t._v(\"ChangeNotifierProvider.of\")]),t._v(\"时\"),n(\"code\",[t._v(\"listen\")]),t._v(\"值为默认值true，所以还是会建立依赖关系。\")]),t._v(\" \"),n(\"p\",[t._v(\"至此我们便实现了一个迷你的Provider，它具备Pub上Provider Package中的核心功能；但是我们的迷你版功能并不全面，如只实现了一个可监听的ChangeNotifierProvider，并没有实现只用于数据共享的Provider；另外，我们的实现有些边界也没有考虑的到，比如如何保证在Widget树重新build时Model始终是单例等。所以建议读者在实战中还是使用Provider Package，而本节实现这个迷你Provider的主要目的主要是为了帮助读者了解Provider Package底层的原理。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"其它状态管理包\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#其它状态管理包\"}},[t._v(\"#\")]),t._v(\" 其它状态管理包\")]),t._v(\" \"),n(\"p\",[t._v(\"现在Flutter社区已经有很多专门用于状态管理的包了，在此我们列出几个相对评分比较高的：\")]),t._v(\" \"),n(\"table\",[n(\"thead\",[n(\"tr\",[n(\"th\",[t._v(\"包名\")]),t._v(\" \"),n(\"th\",[t._v(\"介绍\")])])]),t._v(\" \"),n(\"tbody\",[n(\"tr\",[n(\"td\",[n(\"a\",{attrs:{href:\"https://pub.flutter-io.cn/packages/provider\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Provider\"),n(\"OutboundLink\")],1),t._v(\" & \"),n(\"a\",{attrs:{href:\"https://pub.flutter-io.cn/packages/scoped_model\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Scoped Model\"),n(\"OutboundLink\")],1)]),t._v(\" \"),n(\"td\",[t._v(\"这两个包都是基于\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"的，原理相似\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[n(\"a\",{attrs:{href:\"https://pub.flutter-io.cn/packages/flutter_redux\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Redux\"),n(\"OutboundLink\")],1)]),t._v(\" \"),n(\"td\",[t._v(\"是Web开发中React生态链中Redux包的Flutter实现\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[n(\"a\",{attrs:{href:\"https://pub.dev/packages/flutter_mobx\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"MobX\"),n(\"OutboundLink\")],1)]),t._v(\" \"),n(\"td\",[t._v(\"是Web开发中React生态链中MobX包的Flutter实现\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[n(\"a\",{attrs:{href:\"https://pub.dev/packages/flutter_bloc\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"BLoC\"),n(\"OutboundLink\")],1)]),t._v(\" \"),n(\"td\",[t._v(\"是BLoC模式的Flutter实现\")])])])]),t._v(\" \"),n(\"p\",[t._v(\"在此笔者不对这些包做推荐，读者有兴趣都可以研究一下，了解它们各自的思想。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"总结\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),n(\"p\",[t._v(\"本节通过介绍事件总线在跨组件共享中的一些缺点引出了通过\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"来实现状态的共享的思想，然后基于该思想实现了一个简单的Provider，在实现的过程中也更深入的探索了\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"与其依赖项的注册机制和更新机制。通过本节的学习，读者应该达到两个目标，首先是对\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"彻底吃透，其次是Provider的设计思想。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"是Flutter中非常重要的一个Widget，像国际化、主题等都是通过它来实现，所以我们也不惜篇幅，通过好几节来介绍它的，在下一节中，我们将介绍另一个基于\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"的组件Theme(主题)。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/69.46ed47d5.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[69],{525:function(t,a,s){t.exports=s.p+\"assets/img/9-1.67235fc3.png\"},526:function(t,a,s){t.exports=s.p+\"assets/img/9-2.53c1a9c2.png\"},817:function(t,a,s){\"use strict\";s.r(a);var n=s(45),e=Object(n.a)({},(function(){var t=this,a=t.$createElement,n=t._self._c||a;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_9-2-动画基本结构及状态监听\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_9-2-动画基本结构及状态监听\"}},[t._v(\"#\")]),t._v(\" 9.2 动画基本结构及状态监听\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_9-2-1-动画基本结构\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_9-2-1-动画基本结构\"}},[t._v(\"#\")]),t._v(\" 9.2.1 动画基本结构\")]),t._v(\" \"),n(\"p\",[t._v(\"在Flutter中我们可以通过多种方式来实现动画，下面通过一个图片逐渐放大示例的不同实现来演示Flutter中动画的不同实现方式的区别。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"基础版本\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#基础版本\"}},[t._v(\"#\")]),t._v(\" 基础版本\")]),t._v(\" \"),n(\"p\",[t._v(\"下面我们演示一下最基础的动画实现方式：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ScaleAnimationRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _ScaleAnimationRouteState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_ScaleAnimationRouteState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//需要继承TickerProvider，如果有多个AnimationController，则应该使用TickerProviderStateMixin。\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_ScaleAnimationRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ScaleAnimationRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"with\")]),t._v(\" SingleTickerProviderStateMixin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" \\n    \\n  Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  AnimationController controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    controller \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimationController\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        duration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" vsync\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//图片宽高从0变到300\")]),t._v(\"\\n    animation \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Tween\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"begin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" end\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"animate\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//启动动画(正向执行)\")]),t._v(\"\\n    controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"forward\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n       child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"asset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"imgs/avatar.png\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//路由销毁时需要释放动画资源\")]),t._v(\"\\n    controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上面代码中\"),n(\"code\",[t._v(\"addListener()\")]),t._v(\"函数调用了\"),n(\"code\",[t._v(\"setState()\")]),t._v(\"，所以每次动画生成一个新的数字时，当前帧被标记为脏(dirty)，这会导致widget的\"),n(\"code\",[t._v(\"build()\")]),t._v(\"方法再次被调用，而在\"),n(\"code\",[t._v(\"build()\")]),t._v(\"中，改变Image的宽高，因为它的高度和宽度现在使用的是\"),n(\"code\",[t._v(\"animation.value\")]),t._v(\" ，所以就会逐渐放大。值得注意的是动画完成时要释放控制器(调用\"),n(\"code\",[t._v(\"dispose()\")]),t._v(\"方法)以防止内存泄漏。\")]),t._v(\" \"),n(\"p\",[t._v(\"上面的例子中并没有指定Curve，所以放大的过程是线性的（匀速），下面我们指定一个Curve，来实现一个类似于弹簧效果的动画过程，我们只需要将\"),n(\"code\",[t._v(\"initState\")]),t._v(\"中的代码改为下面这样即可：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    controller \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimationController\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        duration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" vsync\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//使用弹性曲线\")]),t._v(\"\\n    animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CurvedAnimation\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"parent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" curve\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Curves\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"bounceIn\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//图片宽高从0变到300\")]),t._v(\"\\n    animation \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Tween\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"begin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" end\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"animate\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//启动动画\")]),t._v(\"\\n    controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"forward\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上面代码执行后截取了其中的两帧，效果如图9-1、9-2所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(525),alt:\"图9-1\"}}),n(\"img\",{attrs:{src:s(526),alt:\"图9-2\"}})]),t._v(\" \"),n(\"h3\",{attrs:{id:\"使用animatedwidget简化\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#使用animatedwidget简化\"}},[t._v(\"#\")]),t._v(\" 使用AnimatedWidget简化\")]),t._v(\" \"),n(\"p\",[t._v(\"细心的读者可能已经发现上面示例中通过\"),n(\"code\",[t._v(\"addListener()\")]),t._v(\"和\"),n(\"code\",[t._v(\"setState()\")]),t._v(\" 来更新UI这一步其实是通用的，如果每个动画中都加这么一句是比较繁琐的。\"),n(\"code\",[t._v(\"AnimatedWidget\")]),t._v(\"类封装了调用\"),n(\"code\",[t._v(\"setState()\")]),t._v(\"的细节，并允许我们将widget分离出来，重构后的代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimatedImage\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimatedWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedImage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" listenable\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" animation \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" listenable\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"asset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"imgs/avatar.png\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ScaleAnimationRoute1\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _ScaleAnimationRouteState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_ScaleAnimationRouteState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_ScaleAnimationRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ScaleAnimationRoute1\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"with\")]),t._v(\" SingleTickerProviderStateMixin \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\n  Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  AnimationController controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    controller \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimationController\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        duration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" vsync\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//图片宽高从0变到300\")]),t._v(\"\\n    animation \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Tween\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"begin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" end\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"animate\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//启动动画\")]),t._v(\"\\n    controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"forward\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedImage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//路由销毁时需要释放动画资源\")]),t._v(\"\\n    controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"h3\",{attrs:{id:\"用animatedbuilder重构\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#用animatedbuilder重构\"}},[t._v(\"#\")]),t._v(\" 用AnimatedBuilder重构\")]),t._v(\" \"),n(\"p\",[t._v(\"用AnimatedWidget可以从动画中分离出widget，而动画的渲染过程（即设置宽高）仍然在AnimatedWidget中，假设如果我们再添加一个widget透明度变化的动画，那么我们需要再实现一个AnimatedWidget，这样不是很优雅，如果我们能把渲染过程也抽象出来，那就会好很多，而AnimatedBuilder正是将渲染逻辑分离出来, 上面的build方法中的代码可以改为：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\nWidget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//return AnimatedImage(animation: animation,);\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedBuilder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"asset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"images/avatar.png\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext ctx\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Widget child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n              width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n              child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上面的代码中有一个迷惑的问题是，\"),n(\"code\",[t._v(\"child\")]),t._v(\"看起来像被指定了两次。但实际发生的事情是：将外部引用\"),n(\"code\",[t._v(\"child\")]),t._v(\"传递给\"),n(\"code\",[t._v(\"AnimatedBuilder\")]),t._v(\"后\"),n(\"code\",[t._v(\"AnimatedBuilder\")]),t._v(\"再将其传递给匿名构造器， 然后将该对象用作其子对象。最终的结果是\"),n(\"code\",[t._v(\"AnimatedBuilder\")]),t._v(\"返回的对象插入到widget树中。\")]),t._v(\" \"),n(\"p\",[t._v(\"也许你会说这和我们刚开始的示例差不了多少，其实它会带来三个好处：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"p\",[t._v(\"不用显式的去添加帧监听器，然后再调用\"),n(\"code\",[t._v(\"setState()\")]),t._v(\" 了，这个好处和\"),n(\"code\",[t._v(\"AnimatedWidget\")]),t._v(\"是一样的。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"动画构建的范围缩小了，如果没有\"),n(\"code\",[t._v(\"builder\")]),t._v(\"，\"),n(\"code\",[t._v(\"setState()\")]),t._v(\"将会在父组件上下文中调用，这将会导致父组件的\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法重新调用；而有了\"),n(\"code\",[t._v(\"builder\")]),t._v(\"之后，只会导致动画widget自身的\"),n(\"code\",[t._v(\"build\")]),t._v(\"重新调用，避免不必要的rebuild。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"通过\"),n(\"code\",[t._v(\"AnimatedBuilder\")]),t._v(\"可以封装常见的过渡效果来复用动画。下面我们通过封装一个\"),n(\"code\",[t._v(\"GrowTransition\")]),t._v(\"来说明，它可以对子widget实现放大动画：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"GrowTransition\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GrowTransition\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Widget child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimatedBuilder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Widget child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n                width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n                child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" child\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" child\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"这样，最初的示例就可以改为：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\nWidget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GrowTransition\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"asset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"images/avatar.png\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n    animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"strong\",[t._v(\"Flutter中正是通过这种方式封装了很多动画，如：FadeTransition、ScaleTransition、SizeTransition等，很多时候都可以复用这些预置的过渡类。\")])])])]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_9-2-2-动画状态监听\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_9-2-2-动画状态监听\"}},[t._v(\"#\")]),t._v(\" 9.2.2 动画状态监听\")]),t._v(\" \"),n(\"p\",[t._v(\"上面说过，我们可以通过\"),n(\"code\",[t._v(\"Animation\")]),t._v(\"的\"),n(\"code\",[t._v(\"addStatusListener()\")]),t._v(\"方法来添加动画状态改变监听器。Flutter中，有四种动画状态，在\"),n(\"code\",[t._v(\"AnimationStatus\")]),t._v(\"枚举类中定义，下面我们逐个说明：\")]),t._v(\" \"),n(\"table\",[n(\"thead\",[n(\"tr\",[n(\"th\",[t._v(\"枚举值\")]),t._v(\" \"),n(\"th\",[t._v(\"含义\")])])]),t._v(\" \"),n(\"tbody\",[n(\"tr\",[n(\"td\",[n(\"code\",[t._v(\"dismissed\")])]),t._v(\" \"),n(\"td\",[t._v(\"动画在起始点停止\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[n(\"code\",[t._v(\"forward\")])]),t._v(\" \"),n(\"td\",[t._v(\"动画正在正向执行\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[n(\"code\",[t._v(\"reverse\")])]),t._v(\" \"),n(\"td\",[t._v(\"动画正在反向执行\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[n(\"code\",[t._v(\"completed\")])]),t._v(\" \"),n(\"td\",[t._v(\"动画在终点停止\")])])])]),t._v(\" \"),n(\"h4\",{attrs:{id:\"示例\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),n(\"p\",[t._v(\"我们将上面图片放大的示例改为先放大再缩小再放大……这样的循环动画。要实现这种效果，我们只需要监听动画状态的改变即可，即：在动画正向执行结束时反转动画，在动画反向执行结束时再正向执行动画。代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    controller \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimationController\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        duration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" vsync\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//图片宽高从0变到300\")]),t._v(\"\\n    animation \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Tween\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"begin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" end\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"animate\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addStatusListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"status\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"status \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" AnimationStatus\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"completed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//动画执行结束时反向执行动画\")]),t._v(\"\\n        controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"reverse\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"status \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" AnimationStatus\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dismissed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//动画恢复到初始状态时执行动画（正向）\")]),t._v(\"\\n        controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"forward\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//启动动画（正向）\")]),t._v(\"\\n    controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"forward\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/7.9aacc405.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[7],{404:function(t,s){t.exports=\"data:image/png;base64,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J598cvF5NIITjs6BoyOvvPKKGYzao+zJJ59MK6+8svVj5plrAy1da5R88cUX6eCDD07ff/990e8XXnghXX311QnnlBNiIwRynuISAj5833jjjfZxU/HM+a+//rrgGsfgHF9ORhZffHHLk+acfir77rvvpg8//NBsT5zC82mnnVZ8PPXtt99Od999dzrkkEMsENBGU6R2awB8yBJScfgbbrjBgMcQbr/9dsOUSHz44Ycb6eutt545JXW33Xbb9O233yaidVlwfp4KZ5xxRlphhRWGnN58882tjdtuuy0tsMAC9kWkY4891gIM7d58883m9BgbxEtOPPHEhPOfffbZ6b777rM25p133oQxvPzyy6pWKy2jrlWnKjpz/PHHm/PvvPPO6d5777UZ2hprrGFP8vPOO8+uwEl5YuP8zOjgigC96aabps8//zwdddRRabnllrMZxDLLLGPXnHrqqfYwqbilBQLugTz00ENFlcsuu8zS48ePt6DALOOUU04x57/ooovM8QlMBAcWFf3DpGikxonaBYClllrKouiCCy6Yll56aZuyg99TTz1lMAIwK+pzzDFHOv/889NCCy2UqIvTIhiEj/qUERxOOumktMUWW1iblEkgljaY/p9++ukqtkBCuzwZttlmGyt/8cUXi/O8XjCd3GSTTdJcc81lbfCqgNQ1ABSdr3ECx4YrsD3iiCPs89a8ox944IHW6zfeeMM0dsDskM+eM2ODK3gkMCPYCAGP1zYFPpy0/Bpnlf/3R68AOLaEmQei14pLL73UHhI77rhjWmedddJ8882Xll9++UQe4ZWlSVK7eaqmZAIRYhGe7ghTMqT8vXvIxcEfe+wxCxa777671eMPBHUjCy+8cFHN90Pvdrx6SFZccUXrE1NNHP7LL7+0g/M8JUKmDwGcFWx57+fpz5Se93N9rZoAgfzwww+mCb6amlPA6x/rAXJ6q9TlH2YMCG1zH3hkLYJXDq1B8TqK8O6voESemSuCLeyyyy6WrsMf4SBd7lPtAkC5g8qLeL1zewdVHQUL3vV6IeoDbTMDYNrJKwmGQ/CQUfbi3oPUJq9a7M/j9HBKANeakHBQQKiyA63TqG63GidZd911EzO99957z5ycazX9Jw3fiBYWLeP++HULV1zbZGMCgBDU05jIXBYt9LGT0EvhP9CQ89966622a8D9LrjgAivv5b1z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\"},405:function(t,s){t.exports=\"data:image/png;base64,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\"},406:function(t,s){t.exports=\"data:image/png;base64,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\"},407:function(t,s){t.exports=\"data:image/png;base64,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\"},408:function(t,s,a){t.exports=a.p+\"assets/img/3-14.e8ad30dc.png\"},409:function(t,s){t.exports=\"data:image/png;base64,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\"},410:function(t,s){t.exports=\"data:image/png;base64,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\"},772:function(t,s,a){\"use strict\";a.r(s);var n=a(45),r=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_3-4-按钮\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-4-按钮\"}},[t._v(\"#\")]),t._v(\" 3.4 按钮\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_3-4-1-material组件库中的按钮\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-4-1-material组件库中的按钮\"}},[t._v(\"#\")]),t._v(\" 3.4.1 Material组件库中的按钮\")]),t._v(\" \"),n(\"p\",[t._v(\"Material 组件库中提供了多种按钮组件如\"),n(\"code\",[t._v(\"RaisedButton\")]),t._v(\"、\"),n(\"code\",[t._v(\"FlatButton\")]),t._v(\"、\"),n(\"code\",[t._v(\"OutlineButton\")]),t._v(\"等，它们都是直接或间接对\"),n(\"code\",[t._v(\"RawMaterialButton\")]),t._v(\"组件的包装定制，所以他们大多数属性都和\"),n(\"code\",[t._v(\"RawMaterialButton\")]),t._v(\"一样。在介绍各个按钮时我们先介绍其默认外观，而按钮的外观大都可以通过属性来自定义，我们在后面统一介绍这些属性。另外，所有Material 库中的按钮都有如下相同点：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"按下时都会有“水波动画”（又称“涟漪动画”，就是点击时按钮上会出现水波荡漾的动画）。\")]),t._v(\" \"),n(\"li\",[t._v(\"有一个\"),n(\"code\",[t._v(\"onPressed\")]),t._v(\"属性来设置点击回调，当按钮按下时会执行该回调，如果不提供该回调则按钮会处于禁用状态，禁用状态不响应用户点击。\")])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"raisedbutton\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#raisedbutton\"}},[t._v(\"#\")]),t._v(\" RaisedButton\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"RaisedButton\")]),t._v(' 即\"漂浮\"按钮，它默认带有阴影和灰色背景。按下后，阴影会变大，如图3-10所示：')]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(404),alt:\"图3-10\"}})]),t._v(\" \"),n(\"p\",[t._v(\"使用\"),n(\"code\",[t._v(\"RaisedButton\")]),t._v(\"非常简单，如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"normal\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"h3\",{attrs:{id:\"flatbutton\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#flatbutton\"}},[t._v(\"#\")]),t._v(\" FlatButton\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"FlatButton\")]),t._v(\"即扁平按钮，默认背景透明并不带阴影。按下后，会有背景色，如图3-11所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(405),alt:\"图3-11\"}})]),t._v(\" \"),n(\"p\",[t._v(\"使用FlatButton也很简单，代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"normal\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"h3\",{attrs:{id:\"outlinebutton\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#outlinebutton\"}},[t._v(\"#\")]),t._v(\" OutlineButton\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"OutlineButton\")]),t._v(\"默认有一个边框，不带阴影且背景透明。按下后，边框颜色会变亮、同时出现背景和阴影(较弱)，如图3-12所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(406),alt:\"图3-12\"}})]),t._v(\" \"),n(\"p\",[t._v(\"使用\"),n(\"code\",[t._v(\"OutlineButton\")]),t._v(\"也很简单，代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"OutlineButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"normal\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"h3\",{attrs:{id:\"iconbutton\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#iconbutton\"}},[t._v(\"#\")]),t._v(\" IconButton\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"IconButton\")]),t._v(\"是一个可点击的Icon，不包括文字，默认没有背景，点击后会出现背景，如图3-13所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(407),alt:\"图3-13\"}})]),t._v(\" \"),n(\"p\",[t._v(\"代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"IconButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  icon\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"thumb_up\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"h3\",{attrs:{id:\"带图标的按钮\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#带图标的按钮\"}},[t._v(\"#\")]),t._v(\" 带图标的按钮\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"RaisedButton\")]),t._v(\"、\"),n(\"code\",[t._v(\"FlatButton\")]),t._v(\"、\"),n(\"code\",[t._v(\"OutlineButton\")]),t._v(\"都有一个\"),n(\"code\",[t._v(\"icon\")]),t._v(\" 构造函数，通过它可以轻松创建带图标的按钮，如图3-14所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(408),alt:\"图3-14\"}})]),t._v(\" \"),n(\"p\",[t._v(\"代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"RaisedButton\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  icon\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"send\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  label\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"发送\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\nOutlineButton\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  icon\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"add\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  label\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"添加\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\nFlatButton\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  icon\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"info\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  label\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"详情\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),n(\"h2\",{attrs:{id:\"_3-4-2-自定义按钮外观\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-4-2-自定义按钮外观\"}},[t._v(\"#\")]),t._v(\" 3.4.2 自定义按钮外观\")]),t._v(\" \"),n(\"p\",[t._v(\"按钮外观可以通过其属性来定义，不同按钮属性大同小异，我们以FlatButton为例，介绍一下常见的按钮属性，详细的信息可以查看API文档。\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"  \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//按钮点击回调\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"textColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//按钮文字颜色\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"disabledTextColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//按钮禁用时的文字颜色\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//按钮背景颜色\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"disabledColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//按钮禁用时的背景颜色\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"highlightColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//按钮按下时的背景颜色\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"splashColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//点击时，水波动画中水波的颜色\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"colorBrightness\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//按钮主题，默认是浅色主题 \")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//按钮的填充\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"shape\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//外形\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//按钮的内容\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"其中大多数属性名都是自解释的，我们不赘述。下面我们通过一个示例来看看如何自定义按钮。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"示例\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),n(\"p\",[t._v(\"定义一个背景蓝色，两边圆角的按钮。效果如图3-15所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(409),alt:\"图3-15\"}})]),t._v(\" \"),n(\"p\",[t._v(\"代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlatButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  highlightColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"700\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  colorBrightness\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Brightness\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dark\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  splashColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Submit\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  shape\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RoundedRectangleBorder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"borderRadius\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BorderRadius\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"circular\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"很简单吧，在上面的代码中，我们主要通过\"),n(\"code\",[t._v(\"shape\")]),t._v(\"来指定其外形为一个圆角矩形。因为按钮背景是蓝色(深色)，我们需要指定按钮主题\"),n(\"code\",[t._v(\"colorBrightness\")]),t._v(\"为\"),n(\"code\",[t._v(\"Brightness.dark\")]),t._v(\"，这是为了保证按钮文字颜色为浅色。\")]),t._v(\" \"),n(\"p\",[t._v(\"Flutter 中没有提供去除背景的设置，假若我们需要去除背景，则可以通过将背景颜色设置为全透明来实现。对应上面的代码，便是将 \"),n(\"code\",[t._v(\"color: Colors.blue\")]),t._v(\" 替换为 \"),n(\"code\",[t._v(\"color: Color(0x000000)\")]),t._v(\"。\")]),t._v(\" \"),n(\"p\",[t._v(\"细心的读者可能会发现这个按钮没有阴影(点击之后也没有)，这样会显得没有质感。其实这也很容易，将上面的\"),n(\"code\",[t._v(\"FlatButton\")]),t._v(\"换成\"),n(\"code\",[t._v(\"RaisedButton\")]),t._v(\"就行，其它代码不用改（这里 color 也不做更改），换了之后的效果如图3-16所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(410),alt:\"图3-16\"}})]),t._v(\" \"),n(\"p\",[t._v(\"是不是有质感了！之所以会这样，是因为\"),n(\"code\",[t._v(\"RaisedButton\")]),t._v(\"默认有配置阴影：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"elevation \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//正常状态下的阴影\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"highlightElevation \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"8.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//按下时的阴影\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"disabledElevation \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 禁用时的阴影\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"值得注意的是，在Material 组件库中，我们会在很多组件中见到elevation相关的属性，它们都是用来控制阴影的，这是因为阴影在Material设计风格中是一种很重要的表现形式，以后在介绍其它组件时，便不再赘述。\")]),t._v(\" \"),n(\"p\",[t._v('如果我们想实现一个背景渐变的圆角按钮，按钮有没有相应的属性呢？答案是否定的，但是，我们可以通过其它方式来实现，我们将在后面\"自定义组件\"一章中实现。')])])}),[],!1,null,null,null);s.default=r.exports}}]);"
  },
  {
    "path": "docs/assets/js/70.a85fca0c.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[70],{527:function(t,n,s){t.exports=s.p+\"assets/img/9-3.ede423b7.png\"},528:function(t,n,s){t.exports=s.p+\"assets/img/9-4.cc3139b7.png\"},822:function(t,n,s){\"use strict\";s.r(n);var a=s(45),e=Object(a.a)({},(function(){var t=this,n=t.$createElement,a=t._self._c||n;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_9-5-交织动画\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_9-5-交织动画\"}},[t._v(\"#\")]),t._v(\" 9.5 交织动画\")]),t._v(\" \"),a(\"p\",[t._v(\"有些时候我们可能会需要一些复杂的动画，这些动画可能由一个动画序列或重叠的动画组成，比如：有一个柱状图，需要在高度增长的同时改变颜色，等到增长到最大高度后，我们需要在X轴上平移一段距离。可以发现上述场景在不同阶段包含了多种动画，要实现这种效果，使用交织动画（Stagger Animation）会非常简单。交织动画需要注意以下几点：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"要创建交织动画，需要使用多个动画对象（\"),a(\"code\",[t._v(\"Animation\")]),t._v(\"）。\")]),t._v(\" \"),a(\"li\",[t._v(\"一个\"),a(\"code\",[t._v(\"AnimationController\")]),t._v(\"控制所有的动画对象。\")]),t._v(\" \"),a(\"li\",[t._v(\"给每一个动画对象指定时间间隔（Interval）\")])]),t._v(\" \"),a(\"p\",[t._v(\"所有动画都由同一个\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/animation/AnimationController-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"AnimationController\"),a(\"OutboundLink\")],1),t._v(\"驱动，无论动画需要持续多长时间，控制器的值必须在0.0到1.0之间，而每个动画的间隔（Interval）也必须介于0.0和1.0之间。对于在间隔中设置动画的每个属性，需要分别创建一个\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/animation/Tween-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Tween\"),a(\"OutboundLink\")],1),t._v(\" 用于指定该属性的开始值和结束值。也就是说0.0到1.0代表整个动画过程，我们可以给不同动画指定不同的起始点和终止点来决定它们的开始时间和终止时间。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"示例\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),a(\"p\",[t._v(\"下面我们看一个例子，实现一个柱状图增长的动画：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"开始时高度从0增长到300像素，同时颜色由绿色渐变为红色；这个过程占据整个动画时间的60%。\")]),t._v(\" \"),a(\"li\",[t._v(\"高度增长到300后，开始沿X轴向右平移100像素；这个过程占用整个动画时间的40%。\")])]),t._v(\" \"),a(\"p\",[t._v(\"我们将执行动画的Widget分离出来：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StaggerAnimation\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"StaggerAnimation\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" Key key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"controller \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//高度动画\")]),t._v(\"\\n    height \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Tween\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      begin\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".0\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      end\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"animate\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CurvedAnimation\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        parent\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        curve\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Interval\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.6\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//间隔，前60%的动画时间\")]),t._v(\"\\n          curve\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Curves\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"ease\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    color \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ColorTween\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      begin\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"green \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      end\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"animate\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CurvedAnimation\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        parent\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        curve\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Interval\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.6\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//间隔，前60%的动画时间\")]),t._v(\"\\n          curve\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Curves\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"ease\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    padding \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Tween\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      begin\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"only\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"left\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      end\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"only\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"left\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"animate\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CurvedAnimation\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        parent\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        curve\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Interval\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.6\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//间隔，后40%的动画时间\")]),t._v(\"\\n          curve\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Curves\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"ease\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Animation\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  Animation\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  Animation\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  Animation\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Color\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_buildAnimation\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Widget child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"bottomCenter\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedBuilder\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      builder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _buildAnimation\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      animation\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[a(\"code\",[t._v(\"StaggerAnimation\")]),t._v(\"中定义了三个动画，分别是对\"),a(\"code\",[t._v(\"Container\")]),t._v(\"的\"),a(\"code\",[t._v(\"height\")]),t._v(\"、\"),a(\"code\",[t._v(\"color\")]),t._v(\"、\"),a(\"code\",[t._v(\"padding\")]),t._v(\"属性设置的动画，然后通过\"),a(\"code\",[t._v(\"Interval\")]),t._v(\"来为每个动画指定在整个动画过程中的起始点和终点。下面我们来实现启动动画的路由：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StaggerRoute\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _StaggerRouteState \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_StaggerRouteState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_StaggerRouteState\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"StaggerRoute\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"with\")]),t._v(\" TickerProviderStateMixin \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  AnimationController _controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    _controller \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimationController\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        duration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"milliseconds\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2000\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        vsync\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\\n  Future\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Null\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_playAnimation\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//先正向执行动画\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" _controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"forward\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"orCancel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//再反向执行动画\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" _controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"reverse\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"orCancel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"on\")]),t._v(\" TickerCanceled \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// the animation got canceled, probably because we were disposed\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\"  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GestureDetector\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      behavior\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" HitTestBehavior\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"opaque\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      onTap\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_playAnimation\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"black\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"withOpacity\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            border\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Border\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"  Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"black\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"withOpacity\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.5\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//调用我们定义的交织动画Widget\")]),t._v(\"\\n          child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"StaggerAnimation\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _controller\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"执行效果如图，点击图9-3灰色矩形，就可以看到整个动画效果，图9-4是动画执行过程中的一帧。\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:s(527),alt:\"图9-3\"}}),a(\"img\",{attrs:{src:s(528),alt:\"图9-4\"}})])])}),[],!1,null,null,null);n.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/71.f6151eb7.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[71],{549:function(A,a,t){A.exports=t.p+\"assets/img/12-1.456f4488.png\"},550:function(A,a){A.exports=\"data:image/png;base64,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\"},851:function(A,a,t){\"use strict\";t.r(a);var e=t(45),s=Object(e.a)({},(function(){var A=this,a=A.$createElement,e=A._self._c||a;return e(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":A.$parent.slotKey}},[e(\"h1\",{attrs:{id:\"_12-1-开发package\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_12-1-开发package\"}},[A._v(\"#\")]),A._v(\" 12.1 开发Package\")]),A._v(\" \"),e(\"p\",[A._v(\"第二章中已经讲过如何使用Package（包），我们知道通过package可以创建共享的模块化代码，本节将重点讲一下如何开发并发布我们自己的Package。一个最小的Package包括：\")]),A._v(\" \"),e(\"ul\",[e(\"li\",[A._v(\"一个\"),e(\"code\",[A._v(\"pubspec.yaml\")]),A._v(\"文件：声明了Package的名称、版本、作者等的元数据文件。\")]),A._v(\" \"),e(\"li\",[A._v(\"一个 \"),e(\"code\",[A._v(\"lib\")]),A._v(\" 文件夹：包括包中公开的(public)代码，最少应有一个\"),e(\"code\",[A._v(\"<package-name>.dart\")]),A._v(\"文件\")])]),A._v(\" \"),e(\"p\",[A._v(\"Flutter Packages分为两类：\")]),A._v(\" \"),e(\"ul\",[e(\"li\",[A._v(\"Dart包：其中一些可能包含Flutter的特定功能，因此对Flutter框架具有依赖性，这种包仅用于Flutter，例如\"),e(\"a\",{attrs:{href:\"https://pub.dartlang.org/packages/fluro\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[A._v(\"fluro\")]),e(\"OutboundLink\")],1),A._v(\"包。\")]),A._v(\" \"),e(\"li\",[A._v(\"插件包：一种专用的Dart包，其中包含用Dart代码编写的API，以及针对Android（使用Java或Kotlin）和针对iOS（使用OC或Swift）平台的特定实现，也就是说插件包括原生代码，一个具体的例子是\"),e(\"a\",{attrs:{href:\"https://pub.dartlang.org/packages/battery\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[A._v(\"battery\")]),e(\"OutboundLink\")],1),A._v(\"插件包。\")])]),A._v(\" \"),e(\"p\",[A._v(\"注意，虽然Flutter的Dart运行时和Dart VM运行时不是完全相同，但是如果Package中没有涉及这些存在差异的部分，那么这样的包可以同时支持Flutter和Dart VM，如Dart http网络库\"),e(\"a\",{attrs:{href:\"https://github.com/flutterchina/dio\",target:\"_blank\",rel:\"noopener noreferrer\"}},[A._v(\"dio\"),e(\"OutboundLink\")],1),A._v(\"。\")]),A._v(\" \"),e(\"p\",[A._v(\"下面我将带领读者一步步来开发一个Dart Package。\")]),A._v(\" \"),e(\"h3\",{attrs:{id:\"第一步-创建dart包\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#第一步-创建dart包\"}},[A._v(\"#\")]),A._v(\" 第一步：创建Dart包\")]),A._v(\" \"),e(\"p\",[A._v(\"您可以通过Android Studio：File>New>New Flutter Project 来创建一个Package工程，如图12-1所示：\")]),A._v(\" \"),e(\"p\",[e(\"img\",{attrs:{src:t(549),alt:\"图12-1\"}})]),A._v(\" \"),e(\"p\",[A._v(\"您也可以通过使用\"),e(\"code\",[A._v(\"--template=package\")]),A._v(\" 来执行 \"),e(\"code\",[A._v(\"flutter create\")]),A._v(\" 命令来创建：\")]),A._v(\" \"),e(\"div\",{staticClass:\"language-shell extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[e(\"code\",[A._v(\"flutter create --template\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[A._v(\"=\")]),A._v(\"package hello\\n\")])])]),e(\"p\",[A._v(\"这将在\"),e(\"code\",[A._v(\"hello/\")]),A._v(\"文件夹下创建一个具有以下专用内容的package工程：\")]),A._v(\" \"),e(\"ul\",[e(\"li\",[e(\"p\",[e(\"code\",[A._v(\"lib/hello.dart\")]),A._v(\"：Package的Dart代码\")])]),A._v(\" \"),e(\"li\",[e(\"p\",[e(\"code\",[A._v(\"test/hello_test.dart\")]),A._v(\"：Package的单元测试代码。\")])])]),A._v(\" \"),e(\"h3\",{attrs:{id:\"实现package\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#实现package\"}},[A._v(\"#\")]),A._v(\" 实现package\")]),A._v(\" \"),e(\"p\",[A._v(\"对于纯Dart包，只需在主\"),e(\"code\",[A._v(\"lib/<package name>.dart\")]),A._v(\"文件内或\"),e(\"code\",[A._v(\"lib\")]),A._v(\"目录中的文件中添加功能即可 。要测试软件包，请在\"),e(\"code\",[A._v(\"test\")]),A._v(\"目录中添加\"),e(\"a\",{attrs:{href:\"https://flutter.io/testing/#unit-testing\",target:\"_blank\",rel:\"noopener noreferrer\"}},[A._v(\"unit tests\"),e(\"OutboundLink\")],1),A._v(\"。下面我们看看如何组织Package包的代码，我们以shelf Package为例，它的目录结构如图12-2所示：\")]),A._v(\" \"),e(\"p\",[e(\"img\",{attrs:{src:t(550),alt:\"图12-2\"}})]),A._v(\" \"),e(\"p\",[A._v(\"在lib根目录下的“shelf.dart”中，导出了多个“lib/src”目录下的dart文件：\")]),A._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[A._v(\"export\")]),A._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[A._v(\"'src/cascade.dart'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\";\")]),A._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[A._v(\"export\")]),A._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[A._v(\"'src/handler.dart'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\";\")]),A._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[A._v(\"export\")]),A._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[A._v(\"'src/handlers/logger.dart'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\";\")]),A._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[A._v(\"export\")]),A._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[A._v(\"'src/hijack_exception.dart'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\";\")]),A._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[A._v(\"export\")]),A._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[A._v(\"'src/middleware.dart'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\";\")]),A._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[A._v(\"export\")]),A._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[A._v(\"'src/pipeline.dart'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\";\")]),A._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[A._v(\"export\")]),A._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[A._v(\"'src/request.dart'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\";\")]),A._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[A._v(\"export\")]),A._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[A._v(\"'src/response.dart'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\";\")]),A._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[A._v(\"export\")]),A._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[A._v(\"'src/server.dart'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\";\")]),A._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[A._v(\"export\")]),A._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[A._v(\"'src/server_handler.dart'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\";\")]),A._v(\"\\n\")])])]),e(\"p\",[A._v(\"而Package中主要的功能的源码都在src目录下。shelf Package也导出了一个迷你库: shelf_io，它主要是处理HttpRequest的。\")]),A._v(\" \"),e(\"h3\",{attrs:{id:\"导入包\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#导入包\"}},[A._v(\"#\")]),A._v(\" \"),e(\"strong\",[A._v(\"导入包\")])]),A._v(\" \"),e(\"p\",[A._v('当需要使用这个Package时，我们可以通过\"package:\"指令来指定包的入口文件：')]),A._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[A._v(\"import\")]),A._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[A._v(\"'package:utilities/utilities.dart'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\";\")]),A._v(\"\\n\")])])]),e(\"p\",[A._v(\"同一个包中的源码文件之间也可以使用相对路径来导入。\")]),A._v(\" \"),e(\"h3\",{attrs:{id:\"生成文档\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#生成文档\"}},[A._v(\"#\")]),A._v(\" 生成文档\")]),A._v(\" \"),e(\"p\",[A._v(\"可以使用 \"),e(\"a\",{attrs:{href:\"https://github.com/dart-lang/dartdoc#dartdoc\",target:\"_blank\",rel:\"noopener noreferrer\"}},[A._v(\"dartdoc\"),e(\"OutboundLink\")],1),A._v(' 工具来为Package生成文档，开发者需要做的就是遵守文档注释语法在代码中添加文档注释，最后使用dartdoc可以直接生成API文档（一个静态网站）。文档注释是使用三斜线\"///\"开始，如：')]),A._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[A._v(\"/// The event handler responsible for updating the badge in the UI.\")]),A._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[A._v(\"void\")]),A._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[A._v(\"updateBadge\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\")\")]),A._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\"{\")]),A._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\".\")]),A._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\"}\")]),A._v(\"\\n\")])])]),e(\"p\",[A._v(\"详细的文档语法请参考\"),e(\"a\",{attrs:{href:\"https://github.com/dart-lang/dartdoc#dartdoc\",target:\"_blank\",rel:\"noopener noreferrer\"}},[A._v(\"dartdoc\"),e(\"OutboundLink\")],1),A._v(\" 。\")]),A._v(\" \"),e(\"h3\",{attrs:{id:\"处理包的相互依赖\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#处理包的相互依赖\"}},[A._v(\"#\")]),A._v(\" 处理包的相互依赖\")]),A._v(\" \"),e(\"p\",[A._v(\"如果我们正在开发一个\"),e(\"code\",[A._v(\"hello\")]),A._v(\"包，它依赖于另一个包，则需要将该依赖包添加到\"),e(\"code\",[A._v(\"pubspec.yaml\")]),A._v(\"文件的\"),e(\"code\",[A._v(\"dependencies\")]),A._v(\"部分。 下面的代码使\"),e(\"code\",[A._v(\"url_launcher\")]),A._v(\"插件的API在\"),e(\"code\",[A._v(\"hello\")]),A._v(\"包中是可用的：\")]),A._v(\" \"),e(\"p\",[A._v(\"在 \"),e(\"code\",[A._v(\"hello/pubspec.yaml\")]),A._v(\"中:\")]),A._v(\" \"),e(\"div\",{staticClass:\"language-yaml extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[A._v(\"dependencies\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\":\")]),A._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[A._v(\"url_launcher\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\":\")]),A._v(\" ^0.4.2\\n\")])])]),e(\"p\",[A._v(\"现在可以在\"),e(\"code\",[A._v(\"hello\")]),A._v(\"中\"),e(\"code\",[A._v(\"import 'package:url_launcher/url_launcher.dart'\")]),A._v(\" 然后调用 \"),e(\"code\",[A._v(\"launch()\")]),A._v(\"方法了。\")]),A._v(\" \"),e(\"p\",[A._v(\"这与在Flutter应用程序或任何其他Dart项目中引用软件包没有什么不同。\")]),A._v(\" \"),e(\"p\",[A._v(\"但是，如果\"),e(\"code\",[A._v(\"hello\")]),A._v(\"碰巧是一个插件包，其平台特定的代码需要访问\"),e(\"code\",[A._v(\"url_launcher\")]),A._v(\"公开的特定于平台的API，那么我们还需要为特定于平台的构建文件添加合适的依赖声明，如下所示。\")]),A._v(\" \"),e(\"p\",[e(\"strong\",[A._v(\"Android\")])]),A._v(\" \"),e(\"p\",[A._v(\"在 \"),e(\"code\",[A._v(\"hello/android/build.gradle\")]),A._v(\":\")]),A._v(\" \"),e(\"div\",{staticClass:\"language-groovy extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-groovy\"}},[e(\"code\",[A._v(\"android \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\"{\")]),A._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[A._v(\"// lines skipped\")]),A._v(\"\\n    dependencies \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\"{\")]),A._v(\"\\n        provided rootProject\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[A._v(\"findProject\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string gstring\"}},[A._v('\":url_launcher\"')]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\")\")]),A._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\"}\")]),A._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\"}\")]),A._v(\"\\n\")])])]),e(\"p\",[A._v(\"您现在可以在\"),e(\"code\",[A._v(\"hello/android/src\")]),A._v(\"源码中\"),e(\"code\",[A._v(\"import io.flutter.plugins.urllauncher.UrlLauncherPlugin\")]),A._v(\"访问\"),e(\"code\",[A._v(\"UrlLauncherPlugin\")]),A._v(\"类。\")]),A._v(\" \"),e(\"p\",[e(\"strong\",[A._v(\"iOS\")])]),A._v(\" \"),e(\"p\",[A._v(\"在\"),e(\"code\",[A._v(\"hello/ios/hello.podspec\")]),A._v(\":\")]),A._v(\" \"),e(\"div\",{staticClass:\"language-ruby extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-ruby\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token constant\"}},[A._v(\"Pod\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\":\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\":\")]),e(\"span\",{pre:!0,attrs:{class:\"token constant\"}},[A._v(\"Spec\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[A._v(\"new\")]),A._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[A._v(\"do\")]),A._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[A._v(\"|\")]),A._v(\"s\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[A._v(\"|\")]),A._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[A._v(\"# lines skipped\")]),A._v(\"\\n  s\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\".\")]),A._v(\"dependency \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[A._v(\"'url_launcher'\")]),A._v(\"\\n\")])])]),e(\"p\",[A._v(\"您现在可以在\"),e(\"code\",[A._v(\"hello/ios/Classes\")]),A._v(\"源码中 \"),e(\"code\",[A._v('#import \"UrlLauncherPlugin.h\"')]),A._v(\" 然后访问 \"),e(\"code\",[A._v(\"UrlLauncherPlugin\")]),A._v(\"类。\")]),A._v(\" \"),e(\"h3\",{attrs:{id:\"解决依赖冲突\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#解决依赖冲突\"}},[A._v(\"#\")]),A._v(\" 解决依赖冲突\")]),A._v(\" \"),e(\"p\",[A._v(\"假设我们想在我们的\"),e(\"code\",[A._v(\"hello\")]),A._v(\"包中使用\"),e(\"code\",[A._v(\"some_package\")]),A._v(\"和\"),e(\"code\",[A._v(\"other_package\")]),A._v(\"，并且这两个包都依赖\"),e(\"code\",[A._v(\"url_launcher\")]),A._v(\"，但是依赖的是\"),e(\"code\",[A._v(\"url_launcher\")]),A._v(\"的不同的版本。 那我们就有潜在的冲突。避免这种情况的最好方法是在指定依赖关系时，程序包作者使用\"),e(\"a\",{attrs:{href:\"https://www.dartlang.org/tools/pub/dependencies#version-constraints\",target:\"_blank\",rel:\"noopener noreferrer\"}},[A._v(\"版本范围\"),e(\"OutboundLink\")],1),A._v(\"而不是特定版本。\")]),A._v(\" \"),e(\"div\",{staticClass:\"language-yaml extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[A._v(\"dependencies\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\":\")]),A._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[A._v(\"url_launcher\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\":\")]),A._v(\" ^0.4.2    \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[A._v(\"# 这样会较好, 任何0.4.x(x >= 2)都可.\")]),A._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[A._v(\"image_picker\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\":\")]),A._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[A._v(\"'0.1.1'\")]),A._v(\"   \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[A._v(\"# 不是很好，只有0.1.1版本.\")]),A._v(\"\\n\")])])]),e(\"p\",[A._v(\"如果\"),e(\"code\",[A._v(\"some_package\")]),A._v(\"声明了上面的依赖关系,\"),e(\"code\",[A._v(\"other_package\")]),A._v(\"声明了\"),e(\"code\",[A._v(\"url_launcher\")]),A._v(\"版本像’0.4.5’或’^0.4.0’，pub将能够自动解决问题。\")]),A._v(\" \"),e(\"p\",[A._v(\"即使\"),e(\"code\",[A._v(\"some_package\")]),A._v(\"和\"),e(\"code\",[A._v(\"other_package\")]),A._v(\"声明了不兼容的\"),e(\"code\",[A._v(\"url_launcher\")]),A._v(\"版本，它仍然可能会和\"),e(\"code\",[A._v(\"url_launcher\")]),A._v(\"以兼容的方式正常工作。 你可以通过向\"),e(\"code\",[A._v(\"hello\")]),A._v(\"包的\"),e(\"code\",[A._v(\"pubspec.yaml\")]),A._v(\"文件中添加依赖性覆盖声明来处理冲突，从而强制使用特定版本：\")]),A._v(\" \"),e(\"p\",[A._v(\"强制使用 \"),e(\"code\",[A._v(\"0.4.3\")]),A._v(\"版本的\"),e(\"code\",[A._v(\"url_launcher\")]),A._v(\"，在 \"),e(\"code\",[A._v(\"hello/pubspec.yaml\")]),A._v(\"中:\")]),A._v(\" \"),e(\"div\",{staticClass:\"language-yaml extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[A._v(\"dependencies\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\":\")]),A._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[A._v(\"some_package\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\":\")]),A._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[A._v(\"other_package\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\":\")]),A._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[A._v(\"dependency_overrides\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\":\")]),A._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[A._v(\"url_launcher\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\":\")]),A._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[A._v(\"'0.4.3'\")]),A._v(\"\\n\")])])]),e(\"p\",[A._v(\"如果冲突的依赖不是一个包，而是一个特定于Android的库，比如\"),e(\"code\",[A._v(\"guava\")]),A._v(\"，那么必须将依赖重写声明添加到Gradle构建逻辑中。\")]),A._v(\" \"),e(\"p\",[A._v(\"强制使用\"),e(\"code\",[A._v(\"23.0\")]),A._v(\"版本的\"),e(\"code\",[A._v(\"guava\")]),A._v(\"库，在\"),e(\"code\",[A._v(\"hello/android/build.gradle\")]),A._v(\"中：\")]),A._v(\" \"),e(\"div\",{staticClass:\"language-groovy extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-groovy\"}},[e(\"code\",[A._v(\"configurations\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\".\")]),A._v(\"all \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\"{\")]),A._v(\"\\n    resolutionStrategy \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\"{\")]),A._v(\"\\n        force \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[A._v(\"'com.google.guava:guava:23.0-android'\")]),A._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\"}\")]),A._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[A._v(\"}\")]),A._v(\"\\n\")])])]),e(\"p\",[A._v(\"Cocoapods目前不提供依赖覆盖功能。\")]),A._v(\" \"),e(\"h3\",{attrs:{id:\"发布package\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#发布package\"}},[A._v(\"#\")]),A._v(\" 发布Package\")]),A._v(\" \"),e(\"p\",[A._v(\"一旦实现了一个包，我们可以在\"),e(\"a\",{attrs:{href:\"https://pub.dartlang.org/\",target:\"_blank\",rel:\"noopener noreferrer\"}},[A._v(\"Pub\"),e(\"OutboundLink\")],1),A._v(\"上发布它 ，这样其他开发者就可以轻松使用它。\")]),A._v(\" \"),e(\"p\",[A._v(\"在发布之前，检查\"),e(\"code\",[A._v(\"pubspec.yaml\")]),A._v(\"、\"),e(\"code\",[A._v(\"README.md\")]),A._v(\"以及\"),e(\"code\",[A._v(\"CHANGELOG.md\")]),A._v(\"文件，以确保其内容的完整性和正确性。然后，运行 dry-run 命令以查看是否都准备OK了:\")]),A._v(\" \"),e(\"div\",{staticClass:\"language-shell extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[e(\"code\",[A._v(\"flutter packages pub publish --dry-run\\n\")])])]),e(\"p\",[A._v(\"验证无误后，我们就可以运行发布命令了：\")]),A._v(\" \"),e(\"div\",{staticClass:\"language-shell extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[e(\"code\",[A._v(\"flutter packages pub publish\\n\")])])]),e(\"blockquote\",[e(\"p\",[A._v(\"如果你遇到包发布失败的情况，先检查是否因为众所周知的网络原因，如果是网络问题，可以使用VPN，这里需要注意的是一些代理只会代理部分APP的网络请求，如浏览器的，它们可能并不能代理dart的网络请求，所以在这种情况下，即使开了代理也依然无法连接到Pub，因此，在发布Pub包时使用全局代理或全局VPN会保险些。如果网络没有问题，以管理员权限(sudo)运行发布命令重试。\"),e(\"br\"),A._v(\"\\n很多时候开启全局代理也不会让terminal中的流量打代理服务器走，以socks5为例，应该在终端下输入以下指令：\")])]),A._v(\" \"),e(\"div\",{staticClass:\"language-shell extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token builtin class-name\"}},[A._v(\"export\")]),A._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token assign-left variable\"}},[A._v(\"all_proxy\")]),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[A._v(\"=\")]),A._v(\"socks5://127.0.0.1:1080\\n\")])])]),e(\"blockquote\",[e(\"p\",[A._v(\"此时终端中的http和https流量会打代理服务器走，可以通过\"),e(\"code\",[A._v(\"curl -i https://ip.cn\")]),A._v(\"指令查看代理设置是否成功。\")])])])}),[],!1,null,null,null);a.default=s.exports}}]);"
  },
  {
    "path": "docs/assets/js/72.2c5ab8cf.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[72],{552:function(t,s,n){t.exports=n.p+\"assets/img/12-4.49c3c9bf.jpg\"},553:function(t,s){t.exports=\"data:image/jpeg;base64,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\"},856:function(t,s,n){\"use strict\";n.r(s);var a=n(45),e=Object(a.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_12-6-texture和platformview\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_12-6-texture和platformview\"}},[t._v(\"#\")]),t._v(\" 12.6 Texture和PlatformView\")]),t._v(\" \"),a(\"p\",[t._v(\"本节主要介绍原生和Flutter之间如何共享图像，以及如何在Flutter中嵌套原生组件。\")]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_12-6-1-texture-示例-使用摄像头\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_12-6-1-texture-示例-使用摄像头\"}},[t._v(\"#\")]),t._v(\" 12.6.1 Texture（示例：使用摄像头）\")]),t._v(\" \"),a(\"p\",[t._v(\"前面说过Flutter本身只是一个UI系统，对于一些系统能力的调用我们可以通过消息传送机制与原生交互。但是这种消息传送机制并不能覆盖所有的应用场景，比如我们想调用摄像头来拍照或录视频，但在拍照和录视频的过程中我们需要将预览画面显示到我们的Flutter UI中，如果我们要用Flutter定义的消息通道机制来实现这个功能，就需要将摄像头采集的每一帧图片都要从原生传递到Flutter中，这样做代价将会非常大，因为将图像或视频数据通过消息通道实时传输必然会引起内存和CPU的巨大消耗！为此，Flutter提供了一种基于Texture的图片数据共享机制。\")]),t._v(\" \"),a(\"p\",[t._v(\"Texture可以理解为GPU内保存将要绘制的图像数据的一个对象，Flutter engine会将Texture的数据在内存中直接进行映射（而无需在原生和Flutter之间再进行数据传递），Flutter会给每一个Texture分配一个id，同时Flutter中提供了一个\"),a(\"code\",[t._v(\"Texture\")]),t._v(\"组件，\"),a(\"code\",[t._v(\"Texture\")]),t._v(\"构造函数定义如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Texture\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  Key key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"textureId\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[a(\"code\",[t._v(\"Texture\")]),t._v(\" 组件正是通过\"),a(\"code\",[t._v(\"textureId\")]),t._v(\"与Texture数据关联起来；在\"),a(\"code\",[t._v(\"Texture\")]),t._v(\"组件绘制时，Flutter会自动从内存中找到相应id的Texture数据，然后进行绘制。可以总结一下整个流程：图像数据先在原生部分缓存，然后在Flutter部分再通过\"),a(\"code\",[t._v(\"textureId\")]),t._v(\"和缓存关联起来，最后绘制由Flutter完成。\")]),t._v(\" \"),a(\"p\",[t._v(\"如果我们作为一个插件开发者，我们在原生代码中分配了\"),a(\"code\",[t._v(\"textureId\")]),t._v(\"，那么在Flutter侧使用\"),a(\"code\",[t._v(\"Texture\")]),t._v(\"组件时要如何获取\"),a(\"code\",[t._v(\"textureId\")]),t._v(\"呢？这又回到了之前的内容了，\"),a(\"code\",[t._v(\"textureId\")]),t._v(\"完全可以通过MethodChannel来传递。\")]),t._v(\" \"),a(\"p\",[t._v(\"另外，值得注意的是，当原生摄像头捕获的图像发生变化时，\"),a(\"code\",[t._v(\"Texture\")]),t._v(\" 组件会自动重绘，这不需要我们写任何Dart 代码去控制。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"texture用法\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#texture用法\"}},[t._v(\"#\")]),t._v(\" Texture用法\")]),t._v(\" \"),a(\"p\",[t._v(\"如果我们要手动实现一个相机插件，和前面几节介绍的“获取剩余电量”插件的步骤一样，需要分别实现原生部分和Flutter部分。考虑到大多数读者可能并非同时既了解Android开发，又了解iOS开发，如果我们再花大量篇幅来介绍不同端的实现可能会没什么意义，另外，由于Flutter官方提供的相机（camera）插件和视频播放（video_player）插件都是使用Texture来实现的，它们本身就是Texture非常好的示例，所以在本书中将不会再介绍使用Texture的具体流程了，读者有兴趣查看camera和video_player的实现代码。下面我们重点介绍一下如何使用camera和video_player。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"相机示例\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#相机示例\"}},[t._v(\"#\")]),t._v(\" 相机示例\")]),t._v(\" \"),a(\"p\",[t._v(\"下面我们看一下camera包自带的一个示例，它包含如下功能：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"可以拍照，也可以拍视频，拍摄完成后可以保存；排号的视频可以播放预览。\")]),t._v(\" \"),a(\"li\",[t._v(\"可以切换摄像头（前置摄像头、后置摄像头、其它）\")]),t._v(\" \"),a(\"li\",[t._v(\"可以显示已经拍摄内容的预览图。\")])]),t._v(\" \"),a(\"p\",[t._v(\"下面我们看一下具体代码：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[a(\"p\",[t._v(\"首先，依赖camera插件的最新版，并下载依赖。\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-yaml extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"dependencies\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"...\")]),t._v(\"  //省略无关代码\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"camera\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ^0.5.2+2\\n\")])])])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"在\"),a(\"code\",[t._v(\"main\")]),t._v(\"方法中获取可用摄像头列表。\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"main\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 获取可用摄像头列表，cameras为全局变量\")]),t._v(\"\\n  cameras \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"availableCameras\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"runApp\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyApp\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"构建UI。现在我们构建如图12-4的测试界面：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(552),alt:\"12-4\"}}),t._v(\"\\n线面是完整的代码：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:camera/camera.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'../common.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'dart:async'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'dart:io'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:path_provider/path_provider.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:video_player/video_player.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//用于播放录制的视频\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// 获取不同摄像头的图标（前置、后置、其它）\")]),t._v(\"\\nIconData \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getCameraLensIcon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"CameraLensDirection direction\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"switch\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"direction\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" CameraLensDirection\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"back\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"camera_rear\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" CameraLensDirection\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"front\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"camera_front\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" CameraLensDirection\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"external\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"camera\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"throw\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ArgumentError\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Unknown lens direction'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"logError\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String code\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" String message\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Error: $code\\\\nError Message: $message'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 示例页面路由\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CameraExampleHome\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _CameraExampleHomeState \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_CameraExampleHomeState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_CameraExampleHomeState\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"CameraExampleHome\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"with\")]),t._v(\" WidgetsBindingObserver \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  CameraController controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  String imagePath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 图片保存路径\")]),t._v(\"\\n  String videoPath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//视频保存路径\")]),t._v(\"\\n  VideoPlayerController videoController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  VoidCallback videoPlayerListener\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  bool enableAudio \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 监听APP状态改变，是否在前台\")]),t._v(\"\\n    WidgetsBinding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"instance\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addObserver\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    WidgetsBinding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"instance\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"removeObserver\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didChangeAppLifecycleState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"AppLifecycleState state\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 如果APP不在在前台\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"state \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" AppLifecycleState\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"inactive\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      controller\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"state \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" AppLifecycleState\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"resumed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 在前台\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"controller \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onNewCameraSelected\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"description\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" GlobalKey\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ScaffoldState\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _scaffoldKey \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" GlobalKey\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ScaffoldState\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _scaffoldKey\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      appBar\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        title\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'相机示例'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      body\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Expanded\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_cameraPreviewWidget\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"black\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                border\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Border\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" controller \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\" controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isRecordingVideo\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"redAccent\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_captureControlRowWidget\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_toggleAudioWidget\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"5.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              mainAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"start\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_cameraTogglesRowWidget\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_thumbnailWidget\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// 展示预览窗口\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_cameraPreviewWidget\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"controller \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"||\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isInitialized\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'选择一个摄像头'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        style\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          fontSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"24.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          fontWeight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" FontWeight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"w900\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AspectRatio\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        aspectRatio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"aspectRatio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CameraPreview\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// 开启或关闭录音\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_toggleAudioWidget\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"only\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"left\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"25\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'开启录音:'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Switch\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" enableAudio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onChanged\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"bool value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              enableAudio \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"controller \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onNewCameraSelected\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"description\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// 显示已拍摄的图片/视频缩略图。\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_thumbnailWidget\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Expanded\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Align\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"centerRight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          mainAxisSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"min\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            videoController \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\" imagePath \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"videoController \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" Image\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"file\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"File\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"imagePath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AspectRatio\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                      aspectRatio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n                      videoController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"size \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" videoController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"aspectRatio\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"VideoPlayer\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"videoController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    border\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Border\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"pink\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"64.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"64.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// 相机工具栏\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_captureControlRowWidget\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      mainAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"spaceEvenly\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      mainAxisSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"max\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"IconButton\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          icon\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"camera_alt\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" controller \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\"\\n              controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isInitialized \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isRecordingVideo\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" onTakePictureButtonPressed\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"IconButton\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          icon\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"videocam\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" controller \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\"\\n              controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isInitialized \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isRecordingVideo\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" onVideoRecordButtonPressed\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"IconButton\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          icon\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"stop\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" controller \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\"\\n              controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isInitialized \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\"\\n              controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isRecordingVideo\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" onStopButtonPressed\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// 展示所有摄像头\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_cameraTogglesRowWidget\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" List\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" toggles \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"cameras\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isEmpty\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'没有检测到摄像头'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"for\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"CameraDescription cameraDescription \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"in\")]),t._v(\" cameras\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        toggles\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"90.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" RadioListTile\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"CameraDescription\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              title\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getCameraLensIcon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"cameraDescription\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"lensDirection\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              groupValue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" controller\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"description\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" cameraDescription\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              onChanged\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" controller \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\" controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isRecordingVideo\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" onNewCameraSelected\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" toggles\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  String \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"timestamp\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" DateTime\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"now\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"millisecondsSinceEpoch\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showInSnackBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String message\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    _scaffoldKey\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"currentState\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showSnackBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SnackBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"content\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"message\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 摄像头选中回调\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onNewCameraSelected\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"CameraDescription cameraDescription\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"controller \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    controller \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CameraController\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      cameraDescription\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      ResolutionPreset\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"high\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      enableAudio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" enableAudio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addListener\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"mounted\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"hasError\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showInSnackBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Camera error ${controller.value.errorDescription}'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initialize\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"on\")]),t._v(\" CameraException \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"catch\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_showCameraException\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"mounted\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 拍照按钮点击回调\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onTakePictureButtonPressed\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"takePicture\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String filePath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"mounted\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          imagePath \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" filePath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          videoController\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          videoController \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"filePath \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showInSnackBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'图片保存在 $filePath'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 开始录制视频\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onVideoRecordButtonPressed\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"startVideoRecording\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String filePath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"mounted\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"filePath \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showInSnackBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'正在保存视频于 $filePath'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 终止视频录制\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onStopButtonPressed\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"stopVideoRecording\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"mounted\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showInSnackBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'视频保存在: $videoPath'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  Future\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"startVideoRecording\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isInitialized\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showInSnackBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'请先选择一个摄像头'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 确定视频保存的路径\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Directory extDir \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getApplicationDocumentsDirectory\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String dirPath \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'${extDir.path}/Movies/flutter_test'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Directory\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"dirPath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"create\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"recursive\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String filePath \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'$dirPath/${timestamp()}.mp4'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isRecordingVideo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 如果正在录制，则直接返回\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      videoPath \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" filePath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"startVideoRecording\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"filePath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"on\")]),t._v(\" CameraException \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"catch\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_showCameraException\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" filePath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  Future\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"stopVideoRecording\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isRecordingVideo\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"stopVideoRecording\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"on\")]),t._v(\" CameraException \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"catch\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_showCameraException\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_startVideoPlayer\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  Future\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_startVideoPlayer\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" VideoPlayerController vcontroller \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"\\n    VideoPlayerController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"file\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"File\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"videoPath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    videoPlayerListener \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"videoController \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\" videoController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"size \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// Refreshing the state to update video player with the correct ratio.\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"mounted\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        videoController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"removeListener\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"videoPlayerListener\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    vcontroller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addListener\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"videoPlayerListener\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" vcontroller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setLooping\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" vcontroller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initialize\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" videoController\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"mounted\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        imagePath \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        videoController \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" vcontroller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" vcontroller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"play\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  Future\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"takePicture\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isInitialized\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showInSnackBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'错误: 请先选择一个相机'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Directory extDir \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getApplicationDocumentsDirectory\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String dirPath \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'${extDir.path}/Pictures/flutter_test'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Directory\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"dirPath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"create\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"recursive\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String filePath \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'$dirPath/${timestamp()}.jpg'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isTakingPicture\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// A capture is already pending, do nothing.\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"takePicture\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"filePath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"on\")]),t._v(\" CameraException \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"catch\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_showCameraException\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" filePath\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_showCameraException\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"CameraException e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"logError\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"code\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"description\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showInSnackBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Error: ${e.code}\\\\n${e.description}'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])])])]),t._v(\" \"),a(\"blockquote\",[a(\"p\",[t._v(\"如果代码运行遇到困难，请直接查看\"),a(\"a\",{attrs:{href:\"https://pub.dev/packages/camera\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"camera官方文档\"),a(\"OutboundLink\")],1),t._v(\"。\")])]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_12-6-2-platformview-示例-webview\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_12-6-2-platformview-示例-webview\"}},[t._v(\"#\")]),t._v(\" 12.6.2 PlatformView （示例：WebView）\")]),t._v(\" \"),a(\"p\",[t._v(\"如果我们在开发过程中需要使用一个原生组件，但这个原生组件在Flutter中很难实现时怎么办（如webview）？这时一个简单的方法就是将需要使用原生组件的页面全部用原生实现，在flutter中需要打开该页面时通过消息通道打开这个原生的页面。但是这种方法有一个最大的缺点，就是原生组件很难和Flutter组件进行组合。\")]),t._v(\" \"),a(\"p\",[t._v(\"在 Flutter 1.0版本中，Flutter SDK中新增了\"),a(\"code\",[t._v(\"AndroidView\")]),t._v(\"和\"),a(\"code\",[t._v(\"UIKitView\")]),t._v(\" 两个组件，这两个组件的主要功能就是将原生的Android组件和iOS组件嵌入到Flutter的组件树中，这个功能是非常重要的，尤其是对一些实现非常复杂的组件，比如webview，这些组件原生已经有了，如果Flutter中要用，重新实现的话成本将非常高，所以如果有一种机制能让Flutter共享原生组件，这将会非常有用，也正因如此，Flutter才提供了这两个组件。\")]),t._v(\" \"),a(\"p\",[t._v(\"由于\"),a(\"code\",[t._v(\"AndroidView\")]),t._v(\"和\"),a(\"code\",[t._v(\"UIKitView\")]),t._v(\" 是和具体平台相关的，所以称它们为PlatformView。需要说明的是将来Flutter支持的平台可能会增多，则相应的PlatformView也将会变多。那么如何使用Platform View呢？我们以Flutter官方提供的\"),a(\"a\",{attrs:{href:\"https://github.com/flutter/plugins/tree/master/packages/webview_flutter\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"webview_flutter插件\"),a(\"OutboundLink\")],1),t._v(\"为例：\")]),t._v(\" \"),a(\"blockquote\",[a(\"p\",[t._v(\"注意，在本书写作之时，webview_flutter仍处于预览阶段，如您想在项目中使用它，请查看一下webview_flutter插件最新版本及动态。\")])]),t._v(\" \"),a(\"ol\",[a(\"li\",[a(\"p\",[t._v(\"原生代码中注册要被Flutter嵌入的组件工厂，如webview_flutter插件中Android端注册webview插件代码：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-java extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-java\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"public\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"registerWith\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Registrar\")]),t._v(\" registrar\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   registrar\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"platformViewRegistry\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"registerViewFactory\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"webview\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n   \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"WebViewFactory\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"registrar\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"messenger\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[a(\"code\",[t._v(\"WebViewFactory\")]),t._v(\"的具体实现请参考webview_flutter插件的实现源码，在此不再赘述。\")])]),t._v(\" \"),a(\"li\",[a(\"p\",[t._v(\"在Flutter中使用；打开Flutter中文社区首页。\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"PlatformViewRoute\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"WebView\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      initialUrl\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"https://flutterchina.club\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      javascriptMode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" JavascriptMode\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"unrestricted\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"运行效果如图12-5所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(553),alt:\"12-5\"}})])])]),t._v(\" \"),a(\"p\",[t._v(\"注意，使用PlatformView的开销是非常大的，因此，如果一个原生组件用Flutter实现的难度不大时，我们应该首选Flutter实现。\")]),t._v(\" \"),a(\"p\",[t._v(\"另外，PlatformView的相关功能在作者写作时还处于预览阶段，可能还会发生变化，因此，读者如果需要在项目中使用的话，应查看一下最新的文档。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/73.dcf07497.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[73],{627:function(t,s,n){t.exports=n.p+\"assets/img/4-9.7897e012.png\"},628:function(t,s,n){t.exports=n.p+\"assets/img/4-10.9e758696.png\"},898:function(t,s,n){\"use strict\";n.r(s);var a=n(45),e=Object(a.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_4-5-层叠布局-stack、positioned\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_4-5-层叠布局-stack、positioned\"}},[t._v(\"#\")]),t._v(\" 4.5 层叠布局 Stack、Positioned\")]),t._v(\" \"),a(\"p\",[t._v(\"层叠布局和Web中的绝对定位、Android中的Frame布局是相似的，子组件可以根据距父容器四个角的位置来确定自身的位置。绝对定位允许子组件堆叠起来（按照代码中声明的顺序）。Flutter中使用\"),a(\"code\",[t._v(\"Stack\")]),t._v(\"和\"),a(\"code\",[t._v(\"Positioned\")]),t._v(\"这两个组件来配合实现绝对定位。\"),a(\"code\",[t._v(\"Stack\")]),t._v(\"允许子组件堆叠，而\"),a(\"code\",[t._v(\"Positioned\")]),t._v(\"用于根据\"),a(\"code\",[t._v(\"Stack\")]),t._v(\"的四个角来确定子组件的位置。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"stack\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#stack\"}},[t._v(\"#\")]),t._v(\" Stack\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Stack\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"alignment \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" AlignmentDirectional\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"topStart\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"textDirection\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"fit \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" StackFit\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"loose\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"overflow \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Overflow\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"clip\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  List\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" children \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"ul\",[a(\"li\",[a(\"code\",[t._v(\"alignment\")]),t._v(\"：此参数决定如何去对齐没有定位（没有使用\"),a(\"code\",[t._v(\"Positioned\")]),t._v(\"）或部分定位的子组件。所谓部分定位，在这里\"),a(\"strong\",[t._v(\"特指没有在某一个轴上定位：\")]),a(\"code\",[t._v(\"left\")]),t._v(\"、\"),a(\"code\",[t._v(\"right\")]),t._v(\"为横轴，\"),a(\"code\",[t._v(\"top\")]),t._v(\"、\"),a(\"code\",[t._v(\"bottom\")]),t._v(\"为纵轴，只要包含某个轴上的一个定位属性就算在该轴上有定位。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"textDirection\")]),t._v(\"：和\"),a(\"code\",[t._v(\"Row\")]),t._v(\"、\"),a(\"code\",[t._v(\"Wrap\")]),t._v(\"的\"),a(\"code\",[t._v(\"textDirection\")]),t._v(\"功能一样，都用于确定\"),a(\"code\",[t._v(\"alignment\")]),t._v(\"对齐的参考系，即：\"),a(\"code\",[t._v(\"textDirection\")]),t._v(\"的值为\"),a(\"code\",[t._v(\"TextDirection.ltr\")]),t._v(\"，则\"),a(\"code\",[t._v(\"alignment\")]),t._v(\"的\"),a(\"code\",[t._v(\"start\")]),t._v(\"代表左，\"),a(\"code\",[t._v(\"end\")]),t._v(\"代表右，即\"),a(\"code\",[t._v(\"从左往右\")]),t._v(\"的顺序；\"),a(\"code\",[t._v(\"textDirection\")]),t._v(\"的值为\"),a(\"code\",[t._v(\"TextDirection.rtl\")]),t._v(\"，则alignment的\"),a(\"code\",[t._v(\"start\")]),t._v(\"代表右，\"),a(\"code\",[t._v(\"end\")]),t._v(\"代表左，即\"),a(\"code\",[t._v(\"从右往左\")]),t._v(\"的顺序。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"fit\")]),t._v(\"：此参数用于确定\"),a(\"strong\",[t._v(\"没有定位\")]),t._v(\"的子组件如何去适应\"),a(\"code\",[t._v(\"Stack\")]),t._v(\"的大小。\"),a(\"code\",[t._v(\"StackFit.loose\")]),t._v(\"表示使用子组件的大小，\"),a(\"code\",[t._v(\"StackFit.expand\")]),t._v(\"表示扩伸到\"),a(\"code\",[t._v(\"Stack\")]),t._v(\"的大小。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"overflow\")]),t._v(\"：此属性决定如何显示超出\"),a(\"code\",[t._v(\"Stack\")]),t._v(\"显示空间的子组件；值为\"),a(\"code\",[t._v(\"Overflow.clip\")]),t._v(\"时，超出部分会被剪裁（隐藏），值为\"),a(\"code\",[t._v(\"Overflow.visible\")]),t._v(\" 时则不会。\")])]),t._v(\" \"),a(\"h3\",{attrs:{id:\"positioned\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#positioned\"}},[t._v(\"#\")]),t._v(\" Positioned\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Positioned\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  Key key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"left\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"top\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"right\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"bottom\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" Widget child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[a(\"code\",[t._v(\"left\")]),t._v(\"、\"),a(\"code\",[t._v(\"top\")]),t._v(\" 、\"),a(\"code\",[t._v(\"right\")]),t._v(\"、 \"),a(\"code\",[t._v(\"bottom\")]),t._v(\"分别代表离\"),a(\"code\",[t._v(\"Stack\")]),t._v(\"左、上、右、底四边的距离。\"),a(\"code\",[t._v(\"width\")]),t._v(\"和\"),a(\"code\",[t._v(\"height\")]),t._v(\"用于指定需要定位元素的宽度和高度。注意，\"),a(\"code\",[t._v(\"Positioned\")]),t._v(\"的\"),a(\"code\",[t._v(\"width\")]),t._v(\"、\"),a(\"code\",[t._v(\"height\")]),t._v(\" 和其它地方的意义稍微有点区别，此处用于配合\"),a(\"code\",[t._v(\"left\")]),t._v(\"、\"),a(\"code\",[t._v(\"top\")]),t._v(\" 、\"),a(\"code\",[t._v(\"right\")]),t._v(\"、 \"),a(\"code\",[t._v(\"bottom\")]),t._v(\"来定位组件，举个例子，在水平方向时，你只能指定\"),a(\"code\",[t._v(\"left\")]),t._v(\"、\"),a(\"code\",[t._v(\"right\")]),t._v(\"、\"),a(\"code\",[t._v(\"width\")]),t._v(\"三个属性中的两个，如指定\"),a(\"code\",[t._v(\"left\")]),t._v(\"和\"),a(\"code\",[t._v(\"width\")]),t._v(\"后，\"),a(\"code\",[t._v(\"right\")]),t._v(\"会自动算出(\"),a(\"code\",[t._v(\"left\")]),t._v(\"+\"),a(\"code\",[t._v(\"width\")]),t._v(\")，如果同时指定三个属性则会报错，垂直方向同理。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"示例\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),a(\"p\",[t._v(\"在下面的例子中，我们通过对几个\"),a(\"code\",[t._v(\"Text\")]),t._v(\"组件的定位来演示\"),a(\"code\",[t._v(\"Stack\")]),t._v(\"和\"),a(\"code\",[t._v(\"Positioned\")]),t._v(\"的特性：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//通过ConstrainedBox来确保Stack占满屏幕\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ConstrainedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  constraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BoxConstraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"expand\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Stack\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"Alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//指定未定位或部分定位widget的对齐方式\")]),t._v(\"\\n    children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hello world\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"style\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Positioned\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        left\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"18.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"I am Jack\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Positioned\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        top\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"18.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Your friend\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"        \\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"运行效果见图4-9：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(627),alt:\"图4-9\"}})]),t._v(\" \"),a(\"p\",[t._v(\"由于第一个子文本组件\"),a(\"code\",[t._v('Text(\"Hello world\")')]),t._v(\"没有指定定位，并且\"),a(\"code\",[t._v(\"alignment\")]),t._v(\"值为\"),a(\"code\",[t._v(\"Alignment.center\")]),t._v(\"，所以它会居中显示。第二个子文本组件\"),a(\"code\",[t._v('Text(\"I am Jack\")')]),t._v(\"只指定了水平方向的定位(\"),a(\"code\",[t._v(\"left\")]),t._v(\")，所以属于部分定位，即垂直方向上没有定位，那么它在垂直方向的对齐方式则会按照\"),a(\"code\",[t._v(\"alignment\")]),t._v(\"指定的对齐方式对齐，即垂直方向居中。对于第三个子文本组件\"),a(\"code\",[t._v('Text(\"Your friend\")')]),t._v(\"，和第二个\"),a(\"code\",[t._v(\"Text\")]),t._v(\"原理一样，只不过是水平方向没有定位，则水平方向居中。\")]),t._v(\" \"),a(\"p\",[t._v(\"我们给上例中的\"),a(\"code\",[t._v(\"Stack\")]),t._v(\"指定一个\"),a(\"code\",[t._v(\"fit\")]),t._v(\"属性，然后将三个子文本组件的顺序调整一下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Stack\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"Alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  fit\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" StackFit\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"expand\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//未定位widget占满Stack整个空间\")]),t._v(\"\\n  children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Positioned\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      left\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"18.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"I am Jack\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hello world\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"style\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Positioned\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      top\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"18.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Your friend\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"显示效果如图4-10所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(628),alt:\"图4-10\"}})]),t._v(\" \"),a(\"p\",[t._v(\"可以看到，由于第二个子文本组件没有定位，所以\"),a(\"code\",[t._v(\"fit\")]),t._v(\"属性会对它起作用，就会占满\"),a(\"code\",[t._v(\"Stack\")]),t._v(\"。由于\"),a(\"code\",[t._v(\"Stack\")]),t._v(\"子元素是堆叠的，所以第一个子文本组件被第二个遮住了，而第三个在最上层，所以可以正常显示。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/74.3253492f.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[74],{632:function(t,s,a){t.exports=a.p+\"assets/img/5-24.f113edc6.png\"},633:function(t,s){t.exports=\"data:image/png;base64,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\"},900:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_5-7-剪裁-clip\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_5-7-剪裁-clip\"}},[t._v(\"#\")]),t._v(\" 5.7 剪裁（Clip）\")]),t._v(\" \"),n(\"p\",[t._v(\"Flutter中提供了一些剪裁函数，用于对组件进行剪裁。\")]),t._v(\" \"),n(\"table\",[n(\"thead\",[n(\"tr\",[n(\"th\",[t._v(\"剪裁Widget\")]),t._v(\" \"),n(\"th\",[t._v(\"作用\")])])]),t._v(\" \"),n(\"tbody\",[n(\"tr\",[n(\"td\",[t._v(\"ClipOval\")]),t._v(\" \"),n(\"td\",[t._v(\"子组件为正方形时剪裁为内贴圆形，为矩形时，剪裁为内贴椭圆\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"ClipRRect\")]),t._v(\" \"),n(\"td\",[t._v(\"将子组件剪裁为圆角矩形\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"ClipRect\")]),t._v(\" \"),n(\"td\",[t._v(\"剪裁子组件到实际占用的矩形大小（溢出部分剪裁）\")])])])]),t._v(\" \"),n(\"p\",[t._v(\"下面看一个例子：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ClipTestRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 头像  \")]),t._v(\"\\n    Widget avatar \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"asset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"imgs/avatar.png\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"60.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          avatar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//不剪裁\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ClipOval\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" avatar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//剪裁为圆形\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ClipRRect\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//剪裁为圆角矩形\")]),t._v(\"\\n            borderRadius\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BorderRadius\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"circular\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"5.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" avatar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            mainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Align\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"topLeft\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                widthFactor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".5\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//宽度设为原来宽度一半，另一半会溢出\")]),t._v(\"\\n                child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" avatar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"你好世界\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"green\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Row\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            mainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ClipRect\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//将溢出部分剪裁\")]),t._v(\"\\n                child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Align\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"topLeft\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  widthFactor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".5\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//宽度设为原来宽度一半\")]),t._v(\"\\n                  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" avatar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"你好世界\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"green\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图5-24所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(632),alt:\"图5-24\"}})]),t._v(\" \"),n(\"p\",[t._v(\"上面示例代码注释比较详细，在此不再赘述。但值得一提的是最后的两个\"),n(\"code\",[t._v(\"Row\")]),t._v(\"！它们通过\"),n(\"code\",[t._v(\"Align\")]),t._v(\"设置\"),n(\"code\",[t._v(\"widthFactor\")]),t._v(\"为0.5后，图片的实际宽度等于60×0.5，即原宽度一半，但此时图片溢出部分依然会显示，所以第一个“你好世界”会和图片的另一部分重合，为了剪裁掉溢出部分，我们在第二个\"),n(\"code\",[t._v(\"Row\")]),t._v(\"中通过\"),n(\"code\",[t._v(\"ClipRect\")]),t._v(\"将溢出部分剪裁掉了。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"customclipper\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#customclipper\"}},[t._v(\"#\")]),t._v(\" CustomClipper\")]),t._v(\" \"),n(\"p\",[t._v(\"如果我们想剪裁子组件的特定区域，比如，在上面示例的图片中，如果我们只想截取图片中部40×30像素的范围应该怎么做？这时我们可以使用\"),n(\"code\",[t._v(\"CustomClipper\")]),t._v(\"来自定义剪裁区域，实现代码如下：\")]),t._v(\" \"),n(\"p\",[t._v(\"首先，自定义一个\"),n(\"code\",[t._v(\"CustomClipper\")]),t._v(\"：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyClipper\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CustomClipper\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Rect\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Rect \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getClip\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Size size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Rect\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fromLTWH\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"10.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"15.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"40.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"30.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  bool \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"shouldReclip\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"CustomClipper\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Rect\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" oldClipper\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"getClip()\")]),t._v(\"是用于获取剪裁区域的接口，由于图片大小是60×60，我们返回剪裁区域为\"),n(\"code\",[t._v(\"Rect.fromLTWH(10.0, 15.0, 40.0, 30.0)\")]),t._v(\"，即图片中部40×30像素的范围。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"shouldReclip()\")]),t._v(\" 接口决定是否重新剪裁。如果在应用中，剪裁区域始终不会发生变化时应该返回\"),n(\"code\",[t._v(\"false\")]),t._v(\"，这样就不会触发重新剪裁，避免不必要的性能开销。如果剪裁区域会发生变化（比如在对剪裁区域执行一个动画），那么变化后应该返回\"),n(\"code\",[t._v(\"true\")]),t._v(\"来重新执行剪裁。\")])]),t._v(\" \"),n(\"p\",[t._v(\"然后，我们通过\"),n(\"code\",[t._v(\"ClipRect\")]),t._v(\"来执行剪裁，为了看清图片实际所占用的位置，我们设置一个红色背景：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecoratedBox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  decoration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ClipRect\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      clipper\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyClipper\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//使用自定义的clipper\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" avatar\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图5-25所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(633),alt:\"图5-25\"}})]),t._v(\" \"),n(\"p\",[t._v(\"可以看到我们的剪裁成功了，但是图片所占用的空间大小仍然是60×60（红色区域），这是因为剪裁是在layout完成后的绘制阶段进行的，所以不会影响组件的大小，这和\"),n(\"code\",[t._v(\"Transform\")]),t._v(\"原理是相似的。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/75.bef82a69.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[75],{641:function(t,n,s){t.exports=s.p+\"assets/img/5-16.24d30a6e.png\"},642:function(t,n){t.exports=\"data:image/png;base64,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\"},902:function(t,n,s){\"use strict\";s.r(n);var a=s(45),e=Object(a.a)({},(function(){var t=this,n=t.$createElement,a=t._self._c||n;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_5-5-container\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_5-5-container\"}},[t._v(\"#\")]),t._v(\" 5.5 Container\")]),t._v(\" \"),a(\"p\",[t._v(\"我们在前面的章节示例中多次用到过\"),a(\"code\",[t._v(\"Container\")]),t._v(\"组件，本节我们就详细介绍一下\"),a(\"code\",[t._v(\"Container\")]),t._v(\"组件。\"),a(\"code\",[t._v(\"Container\")]),t._v(\"是一个组合类容器，它本身不对应具体的\"),a(\"code\",[t._v(\"RenderObject\")]),t._v(\"，它是\"),a(\"code\",[t._v(\"DecoratedBox\")]),t._v(\"、\"),a(\"code\",[t._v(\"ConstrainedBox、Transform\")]),t._v(\"、\"),a(\"code\",[t._v(\"Padding\")]),t._v(\"、\"),a(\"code\",[t._v(\"Align\")]),t._v(\"等组件组合的一个多功能容器，所以我们只需通过一个\"),a(\"code\",[t._v(\"Container\")]),t._v(\"组件可以实现同时需要装饰、变换、限制的场景。下面是\"),a(\"code\",[t._v(\"Container\")]),t._v(\"的定义：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//容器内补白，属于decoration的装饰范围\")]),t._v(\"\\n  Color color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 背景色\")]),t._v(\"\\n  Decoration decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 背景装饰\")]),t._v(\"\\n  Decoration foregroundDecoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//前景装饰\")]),t._v(\"\\n  double width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//容器的宽度\")]),t._v(\"\\n  double height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//容器的高度\")]),t._v(\"\\n  BoxConstraints constraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//容器大小的限制条件\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"margin\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//容器外补白，不属于decoration的装饰范围\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"transform\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//变换\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[a(\"code\",[t._v(\"Container\")]),t._v(\"的大多数属性在介绍其它容器时都已经介绍过了，不再赘述，但有两点需要说明：\")]),t._v(\" \"),a(\"ul\",[a(\"li\",[t._v(\"容器的大小可以通过\"),a(\"code\",[t._v(\"width\")]),t._v(\"、\"),a(\"code\",[t._v(\"height\")]),t._v(\"属性来指定，也可以通过\"),a(\"code\",[t._v(\"constraints\")]),t._v(\"来指定；如果它们同时存在时，\"),a(\"code\",[t._v(\"width\")]),t._v(\"、\"),a(\"code\",[t._v(\"height\")]),t._v(\"优先。实际上Container内部会根据\"),a(\"code\",[t._v(\"width\")]),t._v(\"、\"),a(\"code\",[t._v(\"height\")]),t._v(\"来生成一个\"),a(\"code\",[t._v(\"constraints\")]),t._v(\"。\")]),t._v(\" \"),a(\"li\",[a(\"code\",[t._v(\"color\")]),t._v(\"和\"),a(\"code\",[t._v(\"decoration\")]),t._v(\"是互斥的，如果同时设置它们则会报错！实际上，当指定\"),a(\"code\",[t._v(\"color\")]),t._v(\"时，\"),a(\"code\",[t._v(\"Container\")]),t._v(\"内会自动创建一个\"),a(\"code\",[t._v(\"decoration\")]),t._v(\"。\")])]),t._v(\" \"),a(\"h3\",{attrs:{id:\"实例\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#实例\"}},[t._v(\"#\")]),t._v(\" 实例\")]),t._v(\" \"),a(\"p\",[t._v(\"我们通过\"),a(\"code\",[t._v(\"Container\")]),t._v(\"来实现如图5-16所示的卡片：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:s(641),alt:\"图5-16\"}})]),t._v(\" \"),a(\"p\",[t._v(\"实现代码如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  margin\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"only\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"top\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" left\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"120.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//容器外填充\")]),t._v(\"\\n  constraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BoxConstraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"tightFor\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"150.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//卡片大小\")]),t._v(\"\\n  decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//背景装饰\")]),t._v(\"\\n      gradient\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RadialGradient\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//背景径向渐变\")]),t._v(\"\\n          colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"orange\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          center\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"topLeft\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".98\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      boxShadow\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//卡片阴影\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxShadow\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"black54\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            offset\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            blurRadius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"4.0\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  transform\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Matrix4\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"rotationZ\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".2\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//卡片倾斜变换\")]),t._v(\"\\n  alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//卡片内文字居中\")]),t._v(\"\\n  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//卡片文字\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"5.20\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" style\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" fontSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"40.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"可以看到\"),a(\"code\",[t._v(\"Container\")]),t._v(\"具备多种组件的功能，通过查看\"),a(\"code\",[t._v(\"Container\")]),t._v(\"源码，我们会很容易发现它正是前面我们介绍过的多种组件组合而成。在Flutter中，\"),a(\"code\",[t._v(\"Container\")]),t._v(\"组件也正是组合优先于继承的实例。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"padding和margin\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#padding和margin\"}},[t._v(\"#\")]),t._v(\" Padding和Margin\")]),t._v(\" \"),a(\"p\",[t._v(\"接下来我们来研究一下\"),a(\"code\",[t._v(\"Container\")]),t._v(\"组件\"),a(\"code\",[t._v(\"margin\")]),t._v(\"和\"),a(\"code\",[t._v(\"padding\")]),t._v(\"属性的区别:\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  margin\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//容器外补白\")]),t._v(\"\\n  color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"orange\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hello world!\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//容器内补白\")]),t._v(\"\\n  color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"orange\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hello world!\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\")])])]),a(\"p\",[a(\"img\",{attrs:{src:s(642),alt:\"图5-17\"}})]),t._v(\" \"),a(\"p\",[t._v(\"可以发现，直观的感觉就是\"),a(\"code\",[t._v(\"margin\")]),t._v(\"的留白是在容器外部，而\"),a(\"code\",[t._v(\"padding\")]),t._v(\"的留白是在容器内部，读者需要记住这个差异。事实上，\"),a(\"code\",[t._v(\"Container\")]),t._v(\"内\"),a(\"code\",[t._v(\"margin\")]),t._v(\"和\"),a(\"code\",[t._v(\"padding\")]),t._v(\"都是通过\"),a(\"code\",[t._v(\"Padding\")]),t._v(\" 组件来实现的，上面的示例代码实际上等价于：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecoratedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"orange\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hello world!\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecoratedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"orange\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Hello world!\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"    \\n\")])])])])}),[],!1,null,null,null);n.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/76.29cac87f.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[76],{657:function(t,s,n){t.exports=n.p+\"assets/img/6-12.d5a5a078.png\"},658:function(t,s,n){t.exports=n.p+\"assets/img/6-13.7ffa2d43.png\"},908:function(t,s,n){\"use strict\";n.r(s);var a=n(45),e=Object(a.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_6-5-customscrollview\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_6-5-customscrollview\"}},[t._v(\"#\")]),t._v(\" 6.5 CustomScrollView\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"CustomScrollView\")]),t._v(\"是可以使用Sliver来自定义滚动模型（效果）的组件。它可以包含多种滚动模型，举个例子，假设有一个页面，顶部需要一个\"),a(\"code\",[t._v(\"GridView\")]),t._v(\"，底部需要一个\"),a(\"code\",[t._v(\"ListView\")]),t._v(\"，而要求整个页面的滑动效果是统一的，即它们看起来是一个整体。如果使用\"),a(\"code\",[t._v(\"GridView\")]),t._v(\"+\"),a(\"code\",[t._v(\"ListView\")]),t._v('来实现的话，就不能保证一致的滑动效果，因为它们的滚动效果是分离的，所以这时就需要一个\"胶水\"，把这些彼此独立的可滚动组件\"粘\"起来，而'),a(\"code\",[t._v(\"CustomScrollView\")]),t._v(\"的功能就相当于“胶水”。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"可滚动组件的sliver版\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#可滚动组件的sliver版\"}},[t._v(\"#\")]),t._v(\" 可滚动组件的Sliver版\")]),t._v(\" \"),a(\"p\",[t._v(\"Sliver在前面讲过，有细片、薄片之意，在Flutter中，Sliver通常指可滚动组件子元素（就像一个个薄片一样）。但是在\"),a(\"code\",[t._v(\"CustomScrollView\")]),t._v(\"中，需要粘起来的可滚动组件就是\"),a(\"code\",[t._v(\"CustomScrollView\")]),t._v(\"的Sliver了，如果直接将\"),a(\"code\",[t._v(\"ListView\")]),t._v(\"、\"),a(\"code\",[t._v(\"GridView\")]),t._v(\"作为\"),a(\"code\",[t._v(\"CustomScrollView\")]),t._v(\"是不行的，因为它们本身是可滚动组件而并不是Sliver！因此，为了能让可滚动组件能和\"),a(\"code\",[t._v(\"CustomScrollView\")]),t._v(\"配合使用，Flutter提供了一些可滚动组件的Sliver版，如SliverList、SliverGrid等。实际上Sliver版的可滚动组件和非Sliver版的可滚动组件最大的区别就是\"),a(\"strong\",[t._v(\"前者不包含滚动模型（自身不能再滚动），而后者包含滚动模型\")]),t._v(\" ，也正因如此，\"),a(\"code\",[t._v(\"CustomScrollView\")]),t._v('才可以将多个Sliver\"粘\"在一起，这些Sliver共用'),a(\"code\",[t._v(\"CustomScrollView\")]),t._v(\"的\"),a(\"code\",[t._v(\"Scrollable\")]),t._v(\"，所以最终才实现了统一的滑动效果。\")]),t._v(\" \"),a(\"blockquote\",[a(\"p\",[t._v(\"Sliver系列Widget比较多，我们不会一一介绍，读者只需记住它的特点，需要时再去查看文档即可。上面之所以说“大多数”Sliver都和可滚动组件对应，是由于还有一些如SliverPadding、SliverAppBar等是和可滚动组件无关的，它们主要是为了结合CustomScrollView一起使用，这是因为\"),a(\"strong\",[t._v(\"CustomScrollView的子组件必须都是Sliver\")]),t._v(\"。\")])]),t._v(\" \"),a(\"h3\",{attrs:{id:\"示例\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CustomScrollViewTestRoute\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//因为本路由没有使用Scaffold，为了让子级Widget(如Text)使用\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//Material Design 默认的样式风格,我们使用Material作为本路由的根。\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Material\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CustomScrollView\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        slivers\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//AppBar，包含一个导航栏\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SliverAppBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            pinned\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            expandedHeight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"250.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            flexibleSpace\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlexibleSpaceBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              title\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Demo'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              background\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Image\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"asset\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"./images/avatar.png\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" fit\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BoxFit\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cover\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SliverPadding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"8.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            sliver\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"SliverGrid\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//Grid\")]),t._v(\"\\n              gridDelegate\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"SliverGridDelegateWithFixedCrossAxisCount\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                crossAxisCount\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//Grid按两列显示\")]),t._v(\"\\n                mainAxisSpacing\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"10.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                crossAxisSpacing\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"10.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                childAspectRatio\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"4.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              delegate\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"SliverChildBuilderDelegate\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int index\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//创建子widget      \")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cyan\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"index \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"%\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"9\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'grid item $index'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                childCount\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//List\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"SliverFixedExtentList\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            itemExtent\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            delegate\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"SliverChildBuilderDelegate\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int index\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//创建列表项      \")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"lightBlue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"index \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"%\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"9\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'list item $index'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                childCount\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//50个列表项\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"代码分为三部分：\")]),t._v(\" \"),a(\"ul\",[a(\"li\",[t._v(\"头部\"),a(\"code\",[t._v(\"SliverAppBar\")]),t._v(\"：\"),a(\"code\",[t._v(\"SliverAppBar\")]),t._v(\"对应\"),a(\"code\",[t._v(\"AppBar\")]),t._v(\"，两者不同之处在于\"),a(\"code\",[t._v(\"SliverAppBar\")]),t._v(\"可以集成到\"),a(\"code\",[t._v(\"CustomScrollView\")]),t._v(\"。\"),a(\"code\",[t._v(\"SliverAppBar\")]),t._v(\"可以结合\"),a(\"code\",[t._v(\"FlexibleSpaceBar\")]),t._v(\"实现Material Design中头部伸缩的模型，具体效果，读者可以运行该示例查看。\")]),t._v(\" \"),a(\"li\",[t._v(\"中间的\"),a(\"code\",[t._v(\"SliverGrid\")]),t._v(\"：它用\"),a(\"code\",[t._v(\"SliverPadding\")]),t._v(\"包裹以给\"),a(\"code\",[t._v(\"SliverGrid\")]),t._v(\"添加补白。\"),a(\"code\",[t._v(\"SliverGrid\")]),t._v(\"是一个两列，宽高比为4的网格，它有20个子组件。\")]),t._v(\" \"),a(\"li\",[t._v(\"底部\"),a(\"code\",[t._v(\"SliverFixedExtentList\")]),t._v(\"：它是一个所有子元素高度都为50像素的列表。\")])]),t._v(\" \"),a(\"p\",[t._v(\"运行效果如图：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(657),alt:\"图6-12\"}}),a(\"img\",{attrs:{src:n(658),alt:\"图6-13\"}})])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/77.8fef1f03.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[77],{673:function(t,s,a){t.exports=a.p+\"assets/img/7-8.c316cc7f.png\"},674:function(t,s,a){t.exports=a.p+\"assets/img/7-9.62d054b0.png\"},915:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_7-5-异步ui更新-futurebuilder、streambuilder\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_7-5-异步ui更新-futurebuilder、streambuilder\"}},[t._v(\"#\")]),t._v(\" 7.5 异步UI更新（FutureBuilder、StreamBuilder）\")]),t._v(\" \"),n(\"p\",[t._v(\"很多时候我们会依赖一些异步数据来动态更新UI，比如在打开一个页面时我们需要先从互联网上获取数据，在获取数据的过程中我们显示一个加载框，等获取到数据时我们再渲染页面；又比如我们想展示Stream（比如文件流、互联网数据接收流）的进度。当然，通过StatefulWidget我们完全可以实现上述这些功能。但由于在实际开发中依赖异步数据更新UI的这种场景非常常见，因此Flutter专门提供了\"),n(\"code\",[t._v(\"FutureBuilder\")]),t._v(\"和\"),n(\"code\",[t._v(\"StreamBuilder\")]),t._v(\"两个组件来快速实现这种功能。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_7-5-1-futurebuilder\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_7-5-1-futurebuilder\"}},[t._v(\"#\")]),t._v(\" 7.5.1 FutureBuilder\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"FutureBuilder\")]),t._v(\"会依赖一个\"),n(\"code\",[t._v(\"Future\")]),t._v(\"，它会根据所依赖的\"),n(\"code\",[t._v(\"Future\")]),t._v(\"的状态来动态构建自身。我们看一下\"),n(\"code\",[t._v(\"FutureBuilder\")]),t._v(\"构造函数：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FutureBuilder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"future\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"initialData\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"ul\",[n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"future\")]),t._v(\"：\"),n(\"code\",[t._v(\"FutureBuilder\")]),t._v(\"依赖的\"),n(\"code\",[t._v(\"Future\")]),t._v(\"，通常是一个异步耗时任务。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"initialData\")]),t._v(\"：初始数据，用户设置默认数据。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"builder\")]),t._v(\"：Widget构建器；该构建器会在\"),n(\"code\",[t._v(\"Future\")]),t._v(\"执行的不同阶段被多次调用，构建器签名如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"Function\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" AsyncSnapshot snapshot\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"snapshot\")]),t._v(\"会包含当前异步任务的状态信息及结果信息 ，比如我们可以通过\"),n(\"code\",[t._v(\"snapshot.connectionState\")]),t._v(\"获取异步任务的状态信息、通过\"),n(\"code\",[t._v(\"snapshot.hasError\")]),t._v(\"判断异步任务是否有错误等等，完整的定义读者可以查看\"),n(\"code\",[t._v(\"AsyncSnapshot\")]),t._v(\"类定义。\")]),t._v(\" \"),n(\"p\",[t._v(\"另外，\"),n(\"code\",[t._v(\"FutureBuilder\")]),t._v(\"的\"),n(\"code\",[t._v(\"builder\")]),t._v(\"函数签名和\"),n(\"code\",[t._v(\"StreamBuilder\")]),t._v(\"的\"),n(\"code\",[t._v(\"builder\")]),t._v(\"是相同的。\")])])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"示例\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),n(\"p\",[t._v(\"我们实现一个路由，当该路由打开时我们从网上获取数据，获取数据时弹一个加载框；获取结束时，如果成功则显示获取到的数据，如果失败则显示错误。由于我们还没有介绍在flutter中如何发起网络请求，所以在这里我们不真正去网络请求数据，而是模拟一下这个过程，隔3秒后返回一个字符串：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"mockNetworkData\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" Future\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"delayed\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"我是从互联网上获取的数据\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"FutureBuilder\")]),t._v(\"使用代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\nWidget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" FutureBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      future\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"mockNetworkData\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" AsyncSnapshot snapshot\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 请求已结束\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"snapshot\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"connectionState \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" ConnectionState\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"done\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"snapshot\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"hasError\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 请求失败，显示错误\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Error: ${snapshot.error}\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 请求成功，显示数据\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Contents: ${snapshot.data}\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 请求未结束，显示loading\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircularProgressIndicator\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行结果如图7-8、7-9所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(673),alt:\"图7-8\"}}),n(\"img\",{attrs:{src:a(674),alt:\"图7-9\"}})]),t._v(\" \"),n(\"p\",[t._v(\"上面代码中我们在\"),n(\"code\",[t._v(\"builder\")]),t._v(\"中根据当前异步任务状态\"),n(\"code\",[t._v(\"ConnectionState\")]),t._v(\"来返回不同的widget。\"),n(\"code\",[t._v(\"ConnectionState\")]),t._v(\"是一个枚举类，定义如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"enum\")]),t._v(\" ConnectionState \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// 当前没有异步任务，比如[FutureBuilder]的[future]为null时\")]),t._v(\"\\n  none\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// 异步任务处于等待状态\")]),t._v(\"\\n  waiting\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// Stream处于激活状态（流上已经有数据传递了），对于FutureBuilder没有该状态。\")]),t._v(\"\\n  active\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// 异步任务已经终止.\")]),t._v(\"\\n  done\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"注意，\"),n(\"code\",[t._v(\"ConnectionState.active\")]),t._v(\"只在\"),n(\"code\",[t._v(\"StreamBuilder\")]),t._v(\"中才会出现。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_7-5-2-streambuilder\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_7-5-2-streambuilder\"}},[t._v(\"#\")]),t._v(\" 7.5.2 StreamBuilder\")]),t._v(\" \"),n(\"p\",[t._v(\"我们知道，在Dart中\"),n(\"code\",[t._v(\"Stream\")]),t._v(\" 也是用于接收异步事件数据，和\"),n(\"code\",[t._v(\"Future\")]),t._v(\" 不同的是，它可以接收多个异步操作的结果，它常用于会多次读取数据的异步任务场景，如网络内容下载、文件读写等。\"),n(\"code\",[t._v(\"StreamBuilder\")]),t._v(\"正是用于配合\"),n(\"code\",[t._v(\"Stream\")]),t._v(\"来展示流上事件（数据）变化的UI组件。下面看一下\"),n(\"code\",[t._v(\"StreamBuilder\")]),t._v(\"的默认构造函数：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"StreamBuilder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"initialData\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  Stream\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" stream\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \\n\")])])]),n(\"p\",[t._v(\"可以看到和\"),n(\"code\",[t._v(\"FutureBuilder\")]),t._v(\"的构造函数只有一点不同：前者需要一个\"),n(\"code\",[t._v(\"future\")]),t._v(\"，而后者需要一个\"),n(\"code\",[t._v(\"stream\")]),t._v(\"。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"示例-2\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例-2\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),n(\"p\",[t._v(\"我们创建一个计时器的示例：每隔1秒，计数加1。这里，我们使用\"),n(\"code\",[t._v(\"Stream\")]),t._v(\"来实现每隔一秒生成一个数字:\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Stream\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"int\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"counter\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" Stream\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"periodic\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"i\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" i\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"StreamBuilder\")]),t._v(\"使用代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"  \\n Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" StreamBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"int\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      stream\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"counter\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//initialData: ,// a Stream<int> or null\")]),t._v(\"\\n      builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" AsyncSnapshot\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"int\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" snapshot\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"snapshot\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"hasError\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Error: ${snapshot.error}'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"switch\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"snapshot\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"connectionState\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" ConnectionState\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"none\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'没有Stream'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" ConnectionState\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"waiting\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'等待数据...'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" ConnectionState\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"active\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'active: ${snapshot.data}'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" ConnectionState\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"done\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Stream已关闭'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// unreachable\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"读者可以自己运行本示例查看运行结果。注意，本示例只是为了演示\"),n(\"code\",[t._v(\"StreamBuilder\")]),t._v(\"的使用，在实战中，凡是UI会依赖多个异步数据而发生变化的场景都可以使用\"),n(\"code\",[t._v(\"StreamBuilder\")]),t._v(\"。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/78.28647d83.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[78],{676:function(t,s,a){t.exports=a.p+\"assets/img/7-2.20458eff.png\"},677:function(t,s,a){t.exports=a.p+\"assets/img/7-3.531c5fdf.png\"},918:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_7-3-跨组件状态共享-provider\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_7-3-跨组件状态共享-provider\"}},[t._v(\"#\")]),t._v(\" 7.3 跨组件状态共享（Provider）\")]),t._v(\" \"),n(\"p\",[t._v(\"在Flutter开发中，状态管理是一个永恒的话题。一般的原则是：如果状态是组件私有的，则应该由组件自己管理；如果状态要跨组件共享，则该状态应该由各个组件共同的父元素来管理。对于组件私有的状态管理很好理解，但对于跨组件共享的状态，管理的方式就比较多了，如使用全局事件总线EventBus（将在下一章中介绍），它是一个观察者模式的实现，通过它就可以实现跨组件状态同步：状态持有方（发布者）负责更新、发布状态，状态使用方（观察者）监听状态改变事件来执行一些操作。下面我们看一个登陆状态同步的简单示例：\")]),t._v(\" \"),n(\"p\",[t._v(\"定义事件：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"enum\")]),t._v(\" Event\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  login\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略其它事件\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"登录页代码大致如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 登录状态改变后发布状态改变事件\")]),t._v(\"\\nbus\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"emit\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Event\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"login\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"依赖登录状态的页面：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onLoginChanged\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//登录状态变化处理逻辑\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//订阅登录状态改变事件\")]),t._v(\"\\n  bus\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"on\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Event\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"login\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"onLogin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//取消订阅\")]),t._v(\"\\n  bus\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"off\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Event\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"login\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"onLogin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们可以发现，通过观察者模式来实现跨组件状态共享有一些明显的缺点：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"必须显式定义各种事件，不好管理\")]),t._v(\" \"),n(\"li\",[t._v(\"订阅者必须需显式注册状态改变回调，也必须在组件销毁时手动去解绑回调以避免内存泄露。\")])]),t._v(\" \"),n(\"p\",[t._v(\"在Flutter当中有没有更好的跨组件状态管理方式了呢？答案是肯定的，那怎么做的？我们想想前面介绍的\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"，它的天生特性就是能绑定\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"与依赖它的子孙组件的依赖关系，并且当\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"数据发生变化时，可以自动更新依赖的子孙组件！利用这个特性，我们可以将需要跨组件共享的状态保存在\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"中，然后在子组件中引用\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"即可，Flutter社区著名的Provider包正是基于这个思想实现的一套跨组件状态共享解决方案，接下来我们便详细介绍一下Provider的用法及原理。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"provider\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#provider\"}},[t._v(\"#\")]),t._v(\" Provider\")]),t._v(\" \"),n(\"p\",[t._v(\"为了加强读者的理解，我们不直接去看Provider包的源代码，相反，我会带着你根据上面描述的通过\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"实现的思路来一步一步地实现一个最小功能的Provider。\")]),t._v(\" \"),n(\"p\",[t._v(\"首先，我们需要一个保存需要共享的数据\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"，由于具体业务数据类型不可预期，为了通用性，我们使用泛型，定义一个通用的\"),n(\"code\",[t._v(\"InheritedProvider\")]),t._v(\"类，它继承自\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 一个通用的InheritedWidget，保存任需要跨组件共享的状态\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"InheritedProvider\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"InheritedWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"InheritedProvider\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Widget child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//共享状态使用泛型\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" T data\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  bool \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"updateShouldNotify\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"InheritedProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" old\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//在此简单返回true，则每次更新都会调用依赖其的子孙节点的`didChangeDependencies`。\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"数据保存的地方有了，那么接下来我们需要做的就是在数据发生变化的时候来重新构建\"),n(\"code\",[t._v(\"InheritedProvider\")]),t._v(\"，那么现在就面临两个问题：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"数据发生变化怎么通知？\")]),t._v(\" \"),n(\"li\",[t._v(\"谁来重新构建\"),n(\"code\",[t._v(\"InheritedProvider\")]),t._v(\"？\")])]),t._v(\" \"),n(\"p\",[t._v(\"第一个问题其实很好解决，我们当然可以使用之前介绍的eventBus来进行事件通知，但是为了更贴近Flutter开发，我们使用Flutter SDK中提供的\"),n(\"code\",[t._v(\"ChangeNotifier\")]),t._v(\"类 ，它继承自\"),n(\"code\",[t._v(\"Listenable\")]),t._v(\"，也实现了一个Flutter风格的发布者-订阅者模式，\"),n(\"code\",[t._v(\"ChangeNotifier\")]),t._v(\"定义大致如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ChangeNotifier\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"implements\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Listenable\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  List listeners\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"VoidCallback listener\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n     \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//添加监听器\")]),t._v(\"\\n     listeners\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"listener\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"removeListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"VoidCallback listener\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//移除监听器\")]),t._v(\"\\n    listeners\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"remove\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"listener\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"notifyListeners\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//通知所有监听器，触发监听器回调 \")]),t._v(\"\\n    listeners\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"forEach\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"item\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"item\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n   \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\")])])]),n(\"p\",[t._v(\"我们可以通过调用\"),n(\"code\",[t._v(\"addListener()\")]),t._v(\"和\"),n(\"code\",[t._v(\"removeListener()\")]),t._v(\"来添加、移除监听器（订阅者）；通过调用\"),n(\"code\",[t._v(\"notifyListeners()\")]),t._v(\" 可以触发所有监听器回调。\")]),t._v(\" \"),n(\"p\",[t._v(\"现在，我们将要共享的状态放到一个Model类中，然后让它继承自\"),n(\"code\",[t._v(\"ChangeNotifier\")]),t._v(\"，这样当共享的状态改变时，我们只需要调用\"),n(\"code\",[t._v(\"notifyListeners()\")]),t._v(\" 来通知订阅者，然后由订阅者来重新构建\"),n(\"code\",[t._v(\"InheritedProvider\")]),t._v(\"，这也是第二个问题的答案！接下来我们便实现这个订阅者类：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ChangeNotifierProvider\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ChangeNotifier\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ChangeNotifierProvider\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Widget child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" T data\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//定义一个便捷方法，方便子树中的widget获取共享数据\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" T of\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" type \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _typeOf\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"InheritedProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">>\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" provider \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"  context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dependOnInheritedWidgetOfExactType\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"InheritedProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">>\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" provider\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _ChangeNotifierProviderState\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _ChangeNotifierProviderState\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"该类继承\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"，然后定义了一个\"),n(\"code\",[t._v(\"of()\")]),t._v(\"静态方法供子类方便获取Widget树中的\"),n(\"code\",[t._v(\"InheritedProvider\")]),t._v(\"中保存的共享状态(model)，下面我们实现该类对应的\"),n(\"code\",[t._v(\"_ChangeNotifierProviderState\")]),t._v(\"类：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_ChangeNotifierProviderState\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ChangeNotifier\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ChangeNotifierProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">>\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"update\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//如果数据发生变化（model类调用了notifyListeners），重新构建InheritedProvider\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didUpdateWidget\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ChangeNotifierProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" oldWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v('//当Provider更新时，如果新旧数据不\"==\"，则解绑旧数据监听，同时添加新数据监听')]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" oldWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      oldWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"removeListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"update\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"update\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didUpdateWidget\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"oldWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 给model添加监听器\")]),t._v(\"\\n    widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"update\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 移除model的监听器\")]),t._v(\"\\n    widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"removeListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"update\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" InheritedProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      data\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"可以看到\"),n(\"code\",[t._v(\"_ChangeNotifierProviderState\")]),t._v(\"类的主要作用就是监听到共享状态（model）改变时重新构建Widget树。注意，在\"),n(\"code\",[t._v(\"_ChangeNotifierProviderState\")]),t._v(\"类中调用\"),n(\"code\",[t._v(\"setState()\")]),t._v(\"方法，\"),n(\"code\",[t._v(\"widget.child\")]),t._v(\"始终是同一个，所以执行build时，\"),n(\"code\",[t._v(\"InheritedProvider\")]),t._v(\"的child引用的始终是同一个子widget，所以\"),n(\"code\",[t._v(\"widget.child\")]),t._v(\"并不会重新\"),n(\"code\",[t._v(\"build\")]),t._v(\"，这也就相当于对\"),n(\"code\",[t._v(\"child\")]),t._v(\"进行了缓存！当然如果\"),n(\"code\",[t._v(\"ChangeNotifierProvider\")]),t._v(\"父级Widget重新build时，则其传入的\"),n(\"code\",[t._v(\"child\")]),t._v(\"便有可能会发生变化。\")]),t._v(\" \"),n(\"p\",[t._v(\"现在我们所需要的各个工具类都已完成，下面我们通过一个购物车的例子来看看怎么使用上面的这些类。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"购物车示例\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#购物车示例\"}},[t._v(\"#\")]),t._v(\" 购物车示例\")]),t._v(\" \"),n(\"p\",[t._v(\"我们需要实现一个显示购物车中所有商品总价的功能：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"向购物车中添加新商品时总价更新\")])]),t._v(\" \"),n(\"p\",[t._v(\"定义一个\"),n(\"code\",[t._v(\"Item\")]),t._v(\"类，用于表示商品信息：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Item\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Item\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"price\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"count\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  double price\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//商品单价\")]),t._v(\"\\n  int count\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 商品份数\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//... 省略其它属性\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"定义一个保存购物车内商品数据的\"),n(\"code\",[t._v(\"CartModel\")]),t._v(\"类:\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CartModel\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ChangeNotifier\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 用于保存购物车中商品列表\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" List\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Item\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _items \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 禁止改变购物车里的商品信息\")]),t._v(\"\\n  UnmodifiableListView\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Item\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" items \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"UnmodifiableListView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_items\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 购物车中商品的总价\")]),t._v(\"\\n  double \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" totalPrice \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"\\n      _items\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fold\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" item\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" value \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" item\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"count \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" item\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"price\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 将 [item] 添加到购物车。这是唯一一种能从外部改变购物车的方法。\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Item item\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    _items\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"item\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 通知监听器（订阅者），重新构建InheritedProvider， 更新状态。\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"notifyListeners\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"CartModel\")]),t._v(\"即要跨组件共享的model类。最后我们构建示例页面：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ProviderRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _ProviderRouteState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_ProviderRouteState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_ProviderRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ProviderRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ChangeNotifierProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"CartModel\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        data\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CartModel\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" cart\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"ChangeNotifierProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"of\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"CartModel\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"总价: ${cart.totalPrice}\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"RaisedButton build\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//在后面优化部分会用到\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"添加商品\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//给购物车中添加商品，添加后总价会更新\")]),t._v(\"\\n                    ChangeNotifierProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"of\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"CartModel\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Item\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行示例后效果如图7-2所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(676),alt:\"provider\"}})]),t._v(\" \"),n(\"p\",[t._v(\"每次点击”添加商品“按钮，总价就会增加20，我们期望的功能实现了！可能有些读者会疑惑，我们饶了一大圈实现这么简单的功能有意义么？其实，就这个例子来看，只是更新同一个路由页中的一个状态，我们使用\"),n(\"code\",[t._v(\"ChangeNotifierProvider\")]),t._v(\"的优势并不明显，但是如果我们是做一个购物APP呢？由于购物车数据是通常是会在整个APP中共享的，比如会跨路由共享。如果我们将\"),n(\"code\",[t._v(\"ChangeNotifierProvider\")]),t._v(\"放在整个应用的Widget树的根上，那么整个APP就可以共享购物车的数据了，这时\"),n(\"code\",[t._v(\"ChangeNotifierProvider\")]),t._v(\"的优势将会非常明显。\")]),t._v(\" \"),n(\"p\",[t._v(\"虽然上面的例子比较简单，但它却将Provider的原理和流程体现的很清楚，图7-3是Provider的原理图：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(677),alt:\"图7-3\"}})]),t._v(\" \"),n(\"p\",[t._v(\"Model变化后会自动通知\"),n(\"code\",[t._v(\"ChangeNotifierProvider\")]),t._v(\"（订阅者），\"),n(\"code\",[t._v(\"ChangeNotifierProvider\")]),t._v(\"内部会重新构建\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"，而依赖该\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"的子孙Widget就会更新。\")]),t._v(\" \"),n(\"p\",[t._v(\"我们可以发现使用Provider，将会带来如下收益：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"我们的业务代码更关注数据了，只要更新Model，则UI会自动更新，而不用在状态改变后再去手动调用\"),n(\"code\",[t._v(\"setState()\")]),t._v(\"来显式更新页面。\")]),t._v(\" \"),n(\"li\",[t._v(\"数据改变的消息传递被屏蔽了，我们无需手动去处理状态改变事件的发布和订阅了，这一切都被封装在Provider中了。这真的很棒，帮我们省掉了大量的工作！\")]),t._v(\" \"),n(\"li\",[t._v(\"在大型复杂应用中，尤其是需要全局共享的状态非常多时，使用Provider将会大大简化我们的代码逻辑，降低出错的概率，提高开发效率。\")])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"优化\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#优化\"}},[t._v(\"#\")]),t._v(\" 优化\")]),t._v(\" \"),n(\"p\",[t._v(\"我们上面实现的\"),n(\"code\",[t._v(\"ChangeNotifierProvider\")]),t._v(\"是有两个明显缺点：代码组织问题和性能问题，下面我们一一讨论。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"代码组织问题\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#代码组织问题\"}},[t._v(\"#\")]),t._v(\" 代码组织问题\")]),t._v(\" \"),n(\"p\",[t._v(\"我们先看一下构建显示总价Text的代码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" cart\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"ChangeNotifierProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"of\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"CartModel\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"总价: ${cart.totalPrice}\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"这段代码有两点可以优化：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"需要显式调用\"),n(\"code\",[t._v(\"ChangeNotifierProvider.of\")]),t._v(\"，当APP内部依赖\"),n(\"code\",[t._v(\"CartModel\")]),t._v(\"很多时，这样的代码将很冗余。\")]),t._v(\" \"),n(\"li\",[t._v(\"语义不明确；由于\"),n(\"code\",[t._v(\"ChangeNotifierProvider\")]),t._v(\"是订阅者，那么依赖\"),n(\"code\",[t._v(\"CartModel\")]),t._v(\"的Widget自然就是订阅者，其实也就是状态的消费者，如果我们用\"),n(\"code\",[t._v(\"Builder\")]),t._v(\" 来构建，语义就不是很明确；如果我们能使用一个具有明确语义的Widget，比如就叫\"),n(\"code\",[t._v(\"Consumer\")]),t._v(\"，这样最终的代码语义将会很明确，只要看到\"),n(\"code\",[t._v(\"Consumer\")]),t._v(\"，我们就知道它是依赖某个跨组件或全局的状态。\")])]),t._v(\" \"),n(\"p\",[t._v(\"为了优化这两个问题，我们可以封装一个\"),n(\"code\",[t._v(\"Consumer\")]),t._v(\" Widget，实现如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 这是一个便捷类，会获得当前context和指定数据类型的Provider\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Consumer\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Consumer\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Widget child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"Function\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" T value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      ChangeNotifierProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"of\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//自动获取Model\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"Consumer\")]),t._v(\"实现非常简单，它通过指定模板参数，然后再内部自动调用\"),n(\"code\",[t._v(\"ChangeNotifierProvider.of\")]),t._v(\"获取相应的Model，并且\"),n(\"code\",[t._v(\"Consumer\")]),t._v(\"这个名字本身也是具有确切语义（消费者）。现在上面的代码块可以优化为如下这样：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Consumer\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"CartModel\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" cart\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"总价: ${cart.totalPrice}\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"是不是很优雅！\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"性能问题\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#性能问题\"}},[t._v(\"#\")]),t._v(\" 性能问题\")]),t._v(\" \"),n(\"p\",[t._v(\"上面的代码还有一个性能问题，就在构建”添加按钮“的代码处：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"RaisedButton build\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 构建时输出日志\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"添加商品\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      ChangeNotifierProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"of\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"CartModel\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Item\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v('我们点击”添加商品“按钮后，由于购物车商品总价会变化，所以显示总价的Text更新是符合预期的，但是”添加商品“按钮本身没有变化，是不应该被重新build的。但是我们运行示例，每次点击”添加商品“按钮，控制台都会输出\"RaisedButton build\"日志，也就是说”添加商品“按钮在每次点击时其自身都会重新build！这是为什么呢？如果你已经理解了'),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"的更新机制，那么答案一眼就能看出：这是因为构建\"),n(\"code\",[t._v(\"RaisedButton\")]),t._v(\"的\"),n(\"code\",[t._v(\"Builder\")]),t._v(\"中调用了\"),n(\"code\",[t._v(\"ChangeNotifierProvider.of\")]),t._v(\"，也就是说依赖了Widget树上面的\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"（即\"),n(\"code\",[t._v(\"InheritedProvider\")]),t._v(\" ）Widget，所以当添加完商品后，\"),n(\"code\",[t._v(\"CartModel\")]),t._v(\"发生变化，会通知\"),n(\"code\",[t._v(\"ChangeNotifierProvider\")]),t._v(\", 而\"),n(\"code\",[t._v(\"ChangeNotifierProvider\")]),t._v(\"则会重新构建子树，所以\"),n(\"code\",[t._v(\"InheritedProvider\")]),t._v(\"将会更新，此时依赖它的子孙Widget就会被重新构建。\")]),t._v(\" \"),n(\"p\",[t._v(\"问题的原因搞清楚了，那么我们如何避免这不必要重构呢？既然按钮重新被build是因为按钮和\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"建立了依赖关系，那么我们只要打破或解除这种依赖关系就可以了。那么如何解除按钮和\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"的依赖关系呢？我们上一节介绍\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"时已经讲过了：调用\"),n(\"code\",[t._v(\"dependOnInheritedWidgetOfExactType()\")]),t._v(\" 和 \"),n(\"code\",[t._v(\"getElementForInheritedWidgetOfExactType()\")]),t._v(\"的区别就是前者会注册依赖关系，而后者不会。所以我们只需要将\"),n(\"code\",[t._v(\"ChangeNotifierProvider.of\")]),t._v(\"的实现改为下面这样即可：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//添加一个listen参数，表示是否建立依赖关系\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" T of\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"bool listen \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" type \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _typeOf\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"InheritedProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">>\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" provider \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" listen\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dependOnInheritedWidgetOfExactType\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"InheritedProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">>\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"getElementForInheritedWidgetOfExactType\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"InheritedProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">>\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"widget\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"as\")]),t._v(\" InheritedProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" provider\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"data\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"然后我们将调用部分代码改为：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n      Consumer\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"CartModel\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" cart\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"总价: ${cart.totalPrice}\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"RaisedButton build\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"添加商品\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// listen 设为false，不建立依赖关系\")]),t._v(\"\\n            ChangeNotifierProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"of\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"CartModel\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" listen\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Item\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v('修改后再次运行上面的示例，我们会发现点击”添加商品“按钮后，控制台不会再输出\"RaisedButton build\"了，即按钮不会被重新构建了。而总价仍然会更新，这是因为'),n(\"code\",[t._v(\"Consumer\")]),t._v(\"中调用\"),n(\"code\",[t._v(\"ChangeNotifierProvider.of\")]),t._v(\"时\"),n(\"code\",[t._v(\"listen\")]),t._v(\"值为默认值true，所以还是会建立依赖关系。\")]),t._v(\" \"),n(\"p\",[t._v(\"至此我们便实现了一个迷你的Provider，它具备Pub上Provider Package中的核心功能；但是我们的迷你版功能并不全面，如只实现了一个可监听的ChangeNotifierProvider，并没有实现只用于数据共享的Provider；另外，我们的实现有些边界也没有考虑的到，比如如何保证在Widget树重新build时Model始终是单例等。所以建议读者在实战中还是使用Provider Package，而本节实现这个迷你Provider的主要目的主要是为了帮助读者了解Provider Package底层的原理。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"其它状态管理包\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#其它状态管理包\"}},[t._v(\"#\")]),t._v(\" 其它状态管理包\")]),t._v(\" \"),n(\"p\",[t._v(\"现在Flutter社区已经有很多专门用于状态管理的包了，在此我们列出几个相对评分比较高的：\")]),t._v(\" \"),n(\"table\",[n(\"thead\",[n(\"tr\",[n(\"th\",[t._v(\"包名\")]),t._v(\" \"),n(\"th\",[t._v(\"介绍\")])])]),t._v(\" \"),n(\"tbody\",[n(\"tr\",[n(\"td\",[n(\"a\",{attrs:{href:\"https://pub.flutter-io.cn/packages/provider\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Provider\"),n(\"OutboundLink\")],1),t._v(\" & \"),n(\"a\",{attrs:{href:\"https://pub.flutter-io.cn/packages/scoped_model\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Scoped Model\"),n(\"OutboundLink\")],1)]),t._v(\" \"),n(\"td\",[t._v(\"这两个包都是基于\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"的，原理相似\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[n(\"a\",{attrs:{href:\"https://pub.flutter-io.cn/packages/flutter_redux\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Redux\"),n(\"OutboundLink\")],1)]),t._v(\" \"),n(\"td\",[t._v(\"是Web开发中React生态链中Redux包的Flutter实现\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[n(\"a\",{attrs:{href:\"https://pub.dev/packages/flutter_mobx\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"MobX\"),n(\"OutboundLink\")],1)]),t._v(\" \"),n(\"td\",[t._v(\"是Web开发中React生态链中MobX包的Flutter实现\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[n(\"a\",{attrs:{href:\"https://pub.dev/packages/flutter_bloc\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"BLoC\"),n(\"OutboundLink\")],1)]),t._v(\" \"),n(\"td\",[t._v(\"是BLoC模式的Flutter实现\")])])])]),t._v(\" \"),n(\"p\",[t._v(\"在此笔者不对这些包做推荐，读者有兴趣都可以研究一下，了解它们各自的思想。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"总结\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),n(\"p\",[t._v(\"本节通过介绍事件总线在跨组件共享中的一些缺点引出了通过\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"来实现状态的共享的思想，然后基于该思想实现了一个简单的Provider，在实现的过程中也更深入的探索了\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"与其依赖项的注册机制和更新机制。通过本节的学习，读者应该达到两个目标，首先是对\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"彻底吃透，其次是Provider的设计思想。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"是Flutter中非常重要的一个Widget，像国际化、主题等都是通过它来实现，所以我们也不惜篇幅，通过好几节来介绍它的，在下一节中，我们将介绍另一个基于\"),n(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"的组件Theme(主题)。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/79.1a0eb05b.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[79],{694:function(t,a,s){t.exports=s.p+\"assets/img/9-1.67235fc3.png\"},695:function(t,a,s){t.exports=s.p+\"assets/img/9-2.53c1a9c2.png\"},928:function(t,a,s){\"use strict\";s.r(a);var n=s(45),e=Object(n.a)({},(function(){var t=this,a=t.$createElement,n=t._self._c||a;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_9-2-动画基本结构及状态监听\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_9-2-动画基本结构及状态监听\"}},[t._v(\"#\")]),t._v(\" 9.2 动画基本结构及状态监听\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_9-2-1-动画基本结构\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_9-2-1-动画基本结构\"}},[t._v(\"#\")]),t._v(\" 9.2.1 动画基本结构\")]),t._v(\" \"),n(\"p\",[t._v(\"在Flutter中我们可以通过多种方式来实现动画，下面通过一个图片逐渐放大示例的不同实现来演示Flutter中动画的不同实现方式的区别。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"基础版本\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#基础版本\"}},[t._v(\"#\")]),t._v(\" 基础版本\")]),t._v(\" \"),n(\"p\",[t._v(\"下面我们演示一下最基础的动画实现方式：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ScaleAnimationRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _ScaleAnimationRouteState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_ScaleAnimationRouteState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//需要继承TickerProvider，如果有多个AnimationController，则应该使用TickerProviderStateMixin。\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_ScaleAnimationRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ScaleAnimationRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"with\")]),t._v(\" SingleTickerProviderStateMixin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" \\n    \\n  Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  AnimationController controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    controller \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimationController\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        duration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" vsync\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//图片宽高从0变到300\")]),t._v(\"\\n    animation \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Tween\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"begin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" end\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"animate\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//启动动画(正向执行)\")]),t._v(\"\\n    controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"forward\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n       child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"asset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"imgs/avatar.png\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//路由销毁时需要释放动画资源\")]),t._v(\"\\n    controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上面代码中\"),n(\"code\",[t._v(\"addListener()\")]),t._v(\"函数调用了\"),n(\"code\",[t._v(\"setState()\")]),t._v(\"，所以每次动画生成一个新的数字时，当前帧被标记为脏(dirty)，这会导致widget的\"),n(\"code\",[t._v(\"build()\")]),t._v(\"方法再次被调用，而在\"),n(\"code\",[t._v(\"build()\")]),t._v(\"中，改变Image的宽高，因为它的高度和宽度现在使用的是\"),n(\"code\",[t._v(\"animation.value\")]),t._v(\" ，所以就会逐渐放大。值得注意的是动画完成时要释放控制器(调用\"),n(\"code\",[t._v(\"dispose()\")]),t._v(\"方法)以防止内存泄漏。\")]),t._v(\" \"),n(\"p\",[t._v(\"上面的例子中并没有指定Curve，所以放大的过程是线性的（匀速），下面我们指定一个Curve，来实现一个类似于弹簧效果的动画过程，我们只需要将\"),n(\"code\",[t._v(\"initState\")]),t._v(\"中的代码改为下面这样即可：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    controller \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimationController\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        duration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" vsync\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//使用弹性曲线\")]),t._v(\"\\n    animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CurvedAnimation\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"parent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" curve\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Curves\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"bounceIn\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//图片宽高从0变到300\")]),t._v(\"\\n    animation \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Tween\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"begin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" end\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"animate\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//启动动画\")]),t._v(\"\\n    controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"forward\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上面代码执行后截取了其中的两帧，效果如图9-1、9-2所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(694),alt:\"图9-1\"}}),n(\"img\",{attrs:{src:s(695),alt:\"图9-2\"}})]),t._v(\" \"),n(\"h3\",{attrs:{id:\"使用animatedwidget简化\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#使用animatedwidget简化\"}},[t._v(\"#\")]),t._v(\" 使用AnimatedWidget简化\")]),t._v(\" \"),n(\"p\",[t._v(\"细心的读者可能已经发现上面示例中通过\"),n(\"code\",[t._v(\"addListener()\")]),t._v(\"和\"),n(\"code\",[t._v(\"setState()\")]),t._v(\" 来更新UI这一步其实是通用的，如果每个动画中都加这么一句是比较繁琐的。\"),n(\"code\",[t._v(\"AnimatedWidget\")]),t._v(\"类封装了调用\"),n(\"code\",[t._v(\"setState()\")]),t._v(\"的细节，并允许我们将widget分离出来，重构后的代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimatedImage\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimatedWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedImage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" listenable\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" animation \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" listenable\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"asset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"imgs/avatar.png\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ScaleAnimationRoute1\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _ScaleAnimationRouteState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_ScaleAnimationRouteState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_ScaleAnimationRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ScaleAnimationRoute1\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"with\")]),t._v(\" SingleTickerProviderStateMixin \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\n  Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  AnimationController controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    controller \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimationController\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        duration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" vsync\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//图片宽高从0变到300\")]),t._v(\"\\n    animation \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Tween\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"begin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" end\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"animate\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//启动动画\")]),t._v(\"\\n    controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"forward\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedImage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//路由销毁时需要释放动画资源\")]),t._v(\"\\n    controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"h3\",{attrs:{id:\"用animatedbuilder重构\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#用animatedbuilder重构\"}},[t._v(\"#\")]),t._v(\" 用AnimatedBuilder重构\")]),t._v(\" \"),n(\"p\",[t._v(\"用AnimatedWidget可以从动画中分离出widget，而动画的渲染过程（即设置宽高）仍然在AnimatedWidget中，假设如果我们再添加一个widget透明度变化的动画，那么我们需要再实现一个AnimatedWidget，这样不是很优雅，如果我们能把渲染过程也抽象出来，那就会好很多，而AnimatedBuilder正是将渲染逻辑分离出来, 上面的build方法中的代码可以改为：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\nWidget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//return AnimatedImage(animation: animation,);\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedBuilder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"asset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"images/avatar.png\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext ctx\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Widget child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n              width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n              child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上面的代码中有一个迷惑的问题是，\"),n(\"code\",[t._v(\"child\")]),t._v(\"看起来像被指定了两次。但实际发生的事情是：将外部引用\"),n(\"code\",[t._v(\"child\")]),t._v(\"传递给\"),n(\"code\",[t._v(\"AnimatedBuilder\")]),t._v(\"后\"),n(\"code\",[t._v(\"AnimatedBuilder\")]),t._v(\"再将其传递给匿名构造器， 然后将该对象用作其子对象。最终的结果是\"),n(\"code\",[t._v(\"AnimatedBuilder\")]),t._v(\"返回的对象插入到widget树中。\")]),t._v(\" \"),n(\"p\",[t._v(\"也许你会说这和我们刚开始的示例差不了多少，其实它会带来三个好处：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"p\",[t._v(\"不用显式的去添加帧监听器，然后再调用\"),n(\"code\",[t._v(\"setState()\")]),t._v(\" 了，这个好处和\"),n(\"code\",[t._v(\"AnimatedWidget\")]),t._v(\"是一样的。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"动画构建的范围缩小了，如果没有\"),n(\"code\",[t._v(\"builder\")]),t._v(\"，\"),n(\"code\",[t._v(\"setState()\")]),t._v(\"将会在父组件上下文中调用，这将会导致父组件的\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法重新调用；而有了\"),n(\"code\",[t._v(\"builder\")]),t._v(\"之后，只会导致动画widget自身的\"),n(\"code\",[t._v(\"build\")]),t._v(\"重新调用，避免不必要的rebuild。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"通过\"),n(\"code\",[t._v(\"AnimatedBuilder\")]),t._v(\"可以封装常见的过渡效果来复用动画。下面我们通过封装一个\"),n(\"code\",[t._v(\"GrowTransition\")]),t._v(\"来说明，它可以对子widget实现放大动画：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"GrowTransition\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GrowTransition\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Widget child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Animation\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimatedBuilder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          builder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Widget child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n                width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n                child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" child\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" child\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"这样，最初的示例就可以改为：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\nWidget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GrowTransition\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"asset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"images/avatar.png\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n    animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"strong\",[t._v(\"Flutter中正是通过这种方式封装了很多动画，如：FadeTransition、ScaleTransition、SizeTransition等，很多时候都可以复用这些预置的过渡类。\")])])])]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_9-2-2-动画状态监听\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_9-2-2-动画状态监听\"}},[t._v(\"#\")]),t._v(\" 9.2.2 动画状态监听\")]),t._v(\" \"),n(\"p\",[t._v(\"上面说过，我们可以通过\"),n(\"code\",[t._v(\"Animation\")]),t._v(\"的\"),n(\"code\",[t._v(\"addStatusListener()\")]),t._v(\"方法来添加动画状态改变监听器。Flutter中，有四种动画状态，在\"),n(\"code\",[t._v(\"AnimationStatus\")]),t._v(\"枚举类中定义，下面我们逐个说明：\")]),t._v(\" \"),n(\"table\",[n(\"thead\",[n(\"tr\",[n(\"th\",[t._v(\"枚举值\")]),t._v(\" \"),n(\"th\",[t._v(\"含义\")])])]),t._v(\" \"),n(\"tbody\",[n(\"tr\",[n(\"td\",[n(\"code\",[t._v(\"dismissed\")])]),t._v(\" \"),n(\"td\",[t._v(\"动画在起始点停止\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[n(\"code\",[t._v(\"forward\")])]),t._v(\" \"),n(\"td\",[t._v(\"动画正在正向执行\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[n(\"code\",[t._v(\"reverse\")])]),t._v(\" \"),n(\"td\",[t._v(\"动画正在反向执行\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[n(\"code\",[t._v(\"completed\")])]),t._v(\" \"),n(\"td\",[t._v(\"动画在终点停止\")])])])]),t._v(\" \"),n(\"h4\",{attrs:{id:\"示例\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),n(\"p\",[t._v(\"我们将上面图片放大的示例改为先放大再缩小再放大……这样的循环动画。要实现这种效果，我们只需要监听动画状态的改变即可，即：在动画正向执行结束时反转动画，在动画反向执行结束时再正向执行动画。代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    controller \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimationController\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        duration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" vsync\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//图片宽高从0变到300\")]),t._v(\"\\n    animation \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Tween\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"begin\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" end\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"animate\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    animation\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addStatusListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"status\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"status \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" AnimationStatus\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"completed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//动画执行结束时反向执行动画\")]),t._v(\"\\n        controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"reverse\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"status \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" AnimationStatus\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dismissed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//动画恢复到初始状态时执行动画（正向）\")]),t._v(\"\\n        controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"forward\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//启动动画（正向）\")]),t._v(\"\\n    controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"forward\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/8.cb5899ca.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[8],{455:function(t,s,n){t.exports=n.p+\"assets/img/5-2.40b01667.png\"},456:function(t,s){t.exports=\"data:image/png;base64,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\"},457:function(t,s){t.exports=\"data:image/png;base64,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\"},458:function(t,s){t.exports=\"data:image/png;base64,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\"},459:function(t,s){t.exports=\"data:image/png;base64,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\"},460:function(t,s,n){t.exports=n.p+\"assets/img/5-7.27479289.png\"},461:function(t,s,n){t.exports=n.p+\"assets/img/5-8.5b913c51.png\"},789:function(t,s,n){\"use strict\";n.r(s);var a=n(45),r=Object(a.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_5-2-尺寸限制类容器\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_5-2-尺寸限制类容器\"}},[t._v(\"#\")]),t._v(\" 5.2 尺寸限制类容器\")]),t._v(\" \"),a(\"p\",[t._v(\"尺寸限制类容器用于限制容器大小，Flutter中提供了多种这样的容器，如\"),a(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"、\"),a(\"code\",[t._v(\"SizedBox\")]),t._v(\"、\"),a(\"code\",[t._v(\"UnconstrainedBox\")]),t._v(\"、\"),a(\"code\",[t._v(\"AspectRatio\")]),t._v(\"等，本节将介绍一些常用的。\")]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_5-2-1-constrainedbox\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_5-2-1-constrainedbox\"}},[t._v(\"#\")]),t._v(\" 5.2.1 ConstrainedBox\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"用于对子组件添加额外的约束。例如，如果你想让子组件的最小高度是80像素，你可以使用\"),a(\"code\",[t._v(\"const BoxConstraints(minHeight: 80.0)\")]),t._v(\"作为子组件的约束。\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"示例\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),a(\"p\",[t._v(\"我们先定义一个\"),a(\"code\",[t._v(\"redBox\")]),t._v(\"，它是一个背景颜色为红色的盒子，不指定它的宽度和高度：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[t._v(\"Widget redBox\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecoratedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"我们实现一个最小高度为50，宽度尽可能大的红色容器。\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ConstrainedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  constraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxConstraints\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    minWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" double\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"infinity\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//宽度尽可能大\")]),t._v(\"\\n    minHeight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//最小高度为50像素\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"5.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" redBox \\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"运行效果如图5-2所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(455),alt:\"图5-2\"}})]),t._v(\" \"),a(\"p\",[t._v(\"可以看到，我们虽然将Container的高度设置为5像素，但是最终却是50像素，这正是ConstrainedBox的最小高度限制生效了。如果将Container的高度设置为80像素，那么最终红色区域的高度也会是80像素，因为在此示例中，ConstrainedBox只限制了最小高度，并未限制最大高度。\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"boxconstraints\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#boxconstraints\"}},[t._v(\"#\")]),t._v(\" BoxConstraints\")]),t._v(\" \"),a(\"p\",[t._v(\"BoxConstraints用于设置限制条件，它的定义如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxConstraints\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"minWidth \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//最小宽度\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"maxWidth \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" double\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"infinity\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//最大宽度\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"minHeight \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//最小高度\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"maxHeight \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" double\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"infinity \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//最大高度\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"BoxConstraints还定义了一些便捷的构造函数，用于快速生成特定限制规则的BoxConstraints，如\"),a(\"code\",[t._v(\"BoxConstraints.tight(Size size)\")]),t._v(\"，它可以生成给定大小的限制；\"),a(\"code\",[t._v(\"const BoxConstraints.expand()\")]),t._v(\"可以生成一个尽可能大的用以填充另一个容器的BoxConstraints。除此之外还有一些其它的便捷函数，读者可以查看\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/rendering/BoxConstraints-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"API文档\"),a(\"OutboundLink\")],1),t._v(\"。\")]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_5-2-2-sizedbox\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_5-2-2-sizedbox\"}},[t._v(\"#\")]),t._v(\" 5.2.2 SizedBox\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"SizedBox\")]),t._v(\"用于给子元素指定固定的宽高，如：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" redBox\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"运行效果如图5-3所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(456),alt:\"图5-3\"}})]),t._v(\" \"),a(\"p\",[t._v(\"实际上\"),a(\"code\",[t._v(\"SizedBox\")]),t._v(\"只是\"),a(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"的一个定制，上面代码等价于：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ConstrainedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  constraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BoxConstraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"tightFor\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" redBox\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"而\"),a(\"code\",[t._v(\"BoxConstraints.tightFor(width: 80.0,height: 80.0)\")]),t._v(\"等价于：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxConstraints\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"minHeight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"maxHeight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"minWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"maxWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"而实际上\"),a(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"和\"),a(\"code\",[t._v(\"SizedBox\")]),t._v(\"都是通过\"),a(\"code\",[t._v(\"RenderConstrainedBox\")]),t._v(\"来渲染的，我们可以看到\"),a(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"和\"),a(\"code\",[t._v(\"SizedBox\")]),t._v(\"的\"),a(\"code\",[t._v(\"createRenderObject()\")]),t._v(\"方法都返回的是一个\"),a(\"code\",[t._v(\"RenderConstrainedBox\")]),t._v(\"对象：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\nRenderConstrainedBox \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createRenderObject\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"RenderConstrainedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    additionalConstraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"h2\",{attrs:{id:\"_5-2-3-多重限制\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_5-2-3-多重限制\"}},[t._v(\"#\")]),t._v(\" 5.2.3 多重限制\")]),t._v(\" \"),a(\"p\",[t._v(\"如果某一个组件有多个父级\"),a(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"限制，那么最终会是哪个生效？我们看一个例子：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ConstrainedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    constraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxConstraints\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"minWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"60.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" minHeight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"60.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//父\")]),t._v(\"\\n    child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ConstrainedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      constraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxConstraints\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"minWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"90.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" minHeight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//子\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" redBox\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"上面我们有父子两个\"),a(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"，他们的限制条件不同，运行后效果如图5-4所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(457),alt:\"图5-4\"}})]),t._v(\" \"),a(\"p\",[t._v(\"最终显示效果是宽90，高60，也就是说是子\"),a(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"的\"),a(\"code\",[t._v(\"minWidth\")]),t._v(\"生效，而\"),a(\"code\",[t._v(\"minHeight\")]),t._v(\"是父\"),a(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"生效。单凭这个例子，我们还总结不出什么规律，我们将上例中父子限制条件换一下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ConstrainedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    constraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxConstraints\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"minWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"90.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" minHeight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ConstrainedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      constraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxConstraints\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"minWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"60.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" minHeight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"60.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" redBox\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"运行效果如图5-5所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(458),alt:\"图5-5\"}})]),t._v(\" \"),a(\"p\",[t._v(\"最终的显示效果仍然是90，高60，效果相同，但意义不同，因为此时\"),a(\"code\",[t._v(\"minWidth\")]),t._v(\"生效的是父\"),a(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"，而\"),a(\"code\",[t._v(\"minHeight\")]),t._v(\"是子\"),a(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"生效。\")]),t._v(\" \"),a(\"p\",[t._v(\"通过上面示例，我们发现有多重限制时，对于\"),a(\"code\",[t._v(\"minWidth\")]),t._v(\"和\"),a(\"code\",[t._v(\"minHeight\")]),t._v(\"来说，是取父子中相应数值较大的。实际上，只有这样才能保证父限制与子限制不冲突。\")]),t._v(\" \"),a(\"blockquote\",[a(\"p\",[t._v(\"思考题：对于\"),a(\"code\",[t._v(\"maxWidth\")]),t._v(\"和\"),a(\"code\",[t._v(\"maxHeight\")]),t._v(\"，多重限制的策略是什么样的呢？\")])]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_5-2-4-unconstrainedbox\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_5-2-4-unconstrainedbox\"}},[t._v(\"#\")]),t._v(\" 5.2.4 UnconstrainedBox\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"UnconstrainedBox\")]),t._v('不会对子组件产生任何限制，它允许其子组件按照其本身大小绘制。一般情况下，我们会很少直接使用此组件，但在\"去除\"多重限制的时候也许会有帮助，我们看下下面的代码：')]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ConstrainedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    constraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxConstraints\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"minWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"60.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" minHeight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//父\")]),t._v(\"\\n    child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"UnconstrainedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//“去除”父级限制\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ConstrainedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        constraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxConstraints\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"minWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"90.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" minHeight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//子\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" redBox\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"上面代码中，如果没有中间的\"),a(\"code\",[t._v(\"UnconstrainedBox\")]),t._v(\"，那么根据上面所述的多重限制规则，那么最终将显示一个90×100的红色框。但是由于\"),a(\"code\",[t._v(\"UnconstrainedBox\")]),t._v(\" “去除”了父\"),a(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"的限制，则最终会按照子\"),a(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"的限制来绘制\"),a(\"code\",[t._v(\"redBox\")]),t._v(\"，即90×20：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(459),alt:\"图5-6\"}})]),t._v(\" \"),a(\"p\",[t._v(\"但是，读者请注意，\"),a(\"code\",[t._v(\"UnconstrainedBox\")]),t._v(\"对父组件限制的“去除”并非是真正的去除：上面例子中虽然红色区域大小是90×20，但上方仍然有80的空白空间。也就是说父限制的\"),a(\"code\",[t._v(\"minHeight\")]),t._v(\"(100.0)仍然是生效的，只不过它不影响最终子元素\"),a(\"code\",[t._v(\"redBox\")]),t._v(\"的大小，但仍然还是占有相应的空间，可以认为此时的父\"),a(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"是作用于子\"),a(\"code\",[t._v(\"UnconstrainedBox\")]),t._v(\"上，而\"),a(\"code\",[t._v(\"redBox\")]),t._v(\"只受子\"),a(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"限制，这一点请读者务必注意。\")]),t._v(\" \"),a(\"p\",[t._v(\"那么有什么方法可以彻底去除父\"),a(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"的限制吗？答案是否定的！所以在此提示读者，在定义一个通用的组件时，如果要对子组件指定限制，那么一定要注意，因为一旦指定限制条件，子组件如果要进行相关自定义大小时将可能非常困难，因为子组件在不更改父组件的代码的情况下无法彻底去除其限制条件。\")]),t._v(\" \"),a(\"p\",[t._v(\"在实际开发中，当我们发现已经使用\"),a(\"code\",[t._v(\"SizedBox\")]),t._v(\"或\"),a(\"code\",[t._v(\"ConstrainedBox\")]),t._v(\"给子元素指定了宽高，但是仍然没有效果时，几乎可以断定：已经有父元素已经设置了限制！举个例子，如Material组件库中的\"),a(\"code\",[t._v(\"AppBar\")]),t._v(\"（导航栏）的右侧菜单中，我们使用\"),a(\"code\",[t._v(\"SizedBox\")]),t._v(\"指定了loading按钮的大小，代码如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n   title\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n   actions\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n         \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n             width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n             height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n             child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircularProgressIndicator\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                 strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                 valueColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlwaysStoppedAnimation\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white70\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n             \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n         \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n   \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"上面代码运行后，效果如图5-7所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(460),alt:\"图5-6\"}})]),t._v(\" \"),a(\"p\",[t._v(\"我们会发现右侧loading按钮大小并没有发生变化！这正是因为\"),a(\"code\",[t._v(\"AppBar\")]),t._v(\"中已经指定了\"),a(\"code\",[t._v(\"actions\")]),t._v(\"按钮的限制条件，所以我们要自定义loading按钮大小，就必须通过\"),a(\"code\",[t._v(\"UnconstrainedBox\")]),t._v(\"来“去除”父元素的限制，代码如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  title\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  actions\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"UnconstrainedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircularProgressIndicator\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                valueColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AlwaysStoppedAnimation\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white70\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"运行后效果如图5-8所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(461),alt:\"图5-8\"}})]),t._v(\" \"),a(\"p\",[t._v(\"生效了！\")]),t._v(\" \"),a(\"h2\",{attrs:{id:\"_5-2-4-其它尺寸限制类容器\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_5-2-4-其它尺寸限制类容器\"}},[t._v(\"#\")]),t._v(\" 5.2.4 其它尺寸限制类容器\")]),t._v(\" \"),a(\"p\",[t._v(\"除了上面介绍的这些常用的尺寸限制类容器外，还有一些其他的尺寸限制类容器，比如\"),a(\"code\",[t._v(\"AspectRatio\")]),t._v(\"，它可以指定子组件的长宽比、\"),a(\"code\",[t._v(\"LimitedBox\")]),t._v(\" 用于指定最大宽高、\"),a(\"code\",[t._v(\"FractionallySizedBox\")]),t._v(\" 可以根据父容器宽高的百分比来设置子组件宽高等，由于这些容器使用起来都比较简单，我们便不再赘述，读者可以自行了解。\")])])}),[],!1,null,null,null);s.default=r.exports}}]);"
  },
  {
    "path": "docs/assets/js/80.8eacbc37.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[80],{696:function(t,n,s){t.exports=s.p+\"assets/img/9-3.ede423b7.png\"},697:function(t,n,s){t.exports=s.p+\"assets/img/9-4.cc3139b7.png\"},933:function(t,n,s){\"use strict\";s.r(n);var a=s(45),e=Object(a.a)({},(function(){var t=this,n=t.$createElement,a=t._self._c||n;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_9-5-交织动画\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_9-5-交织动画\"}},[t._v(\"#\")]),t._v(\" 9.5 交织动画\")]),t._v(\" \"),a(\"p\",[t._v(\"有些时候我们可能会需要一些复杂的动画，这些动画可能由一个动画序列或重叠的动画组成，比如：有一个柱状图，需要在高度增长的同时改变颜色，等到增长到最大高度后，我们需要在X轴上平移一段距离。可以发现上述场景在不同阶段包含了多种动画，要实现这种效果，使用交织动画（Stagger Animation）会非常简单。交织动画需要注意以下几点：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"要创建交织动画，需要使用多个动画对象（\"),a(\"code\",[t._v(\"Animation\")]),t._v(\"）。\")]),t._v(\" \"),a(\"li\",[t._v(\"一个\"),a(\"code\",[t._v(\"AnimationController\")]),t._v(\"控制所有的动画对象。\")]),t._v(\" \"),a(\"li\",[t._v(\"给每一个动画对象指定时间间隔（Interval）\")])]),t._v(\" \"),a(\"p\",[t._v(\"所有动画都由同一个\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/animation/AnimationController-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"AnimationController\"),a(\"OutboundLink\")],1),t._v(\"驱动，无论动画需要持续多长时间，控制器的值必须在0.0到1.0之间，而每个动画的间隔（Interval）也必须介于0.0和1.0之间。对于在间隔中设置动画的每个属性，需要分别创建一个\"),a(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/animation/Tween-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Tween\"),a(\"OutboundLink\")],1),t._v(\" 用于指定该属性的开始值和结束值。也就是说0.0到1.0代表整个动画过程，我们可以给不同动画指定不同的起始点和终止点来决定它们的开始时间和终止时间。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"示例\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),a(\"p\",[t._v(\"下面我们看一个例子，实现一个柱状图增长的动画：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"开始时高度从0增长到300像素，同时颜色由绿色渐变为红色；这个过程占据整个动画时间的60%。\")]),t._v(\" \"),a(\"li\",[t._v(\"高度增长到300后，开始沿X轴向右平移100像素；这个过程占用整个动画时间的40%。\")])]),t._v(\" \"),a(\"p\",[t._v(\"我们将执行动画的Widget分离出来：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StaggerAnimation\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"StaggerAnimation\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" Key key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"controller \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//高度动画\")]),t._v(\"\\n    height \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Tween\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      begin\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".0\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      end\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"animate\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CurvedAnimation\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        parent\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        curve\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Interval\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.6\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//间隔，前60%的动画时间\")]),t._v(\"\\n          curve\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Curves\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"ease\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    color \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ColorTween\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      begin\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"green \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      end\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"animate\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CurvedAnimation\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        parent\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        curve\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Interval\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.6\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//间隔，前60%的动画时间\")]),t._v(\"\\n          curve\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Curves\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"ease\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    padding \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Tween\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      begin\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"only\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"left\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      end\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"only\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"left\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"animate\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CurvedAnimation\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        parent\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        curve\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Interval\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.6\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//间隔，后40%的动画时间\")]),t._v(\"\\n          curve\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Curves\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"ease\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Animation\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  Animation\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  Animation\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  Animation\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Color\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_buildAnimation\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Widget child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"bottomCenter\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedBuilder\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      builder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _buildAnimation\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      animation\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[a(\"code\",[t._v(\"StaggerAnimation\")]),t._v(\"中定义了三个动画，分别是对\"),a(\"code\",[t._v(\"Container\")]),t._v(\"的\"),a(\"code\",[t._v(\"height\")]),t._v(\"、\"),a(\"code\",[t._v(\"color\")]),t._v(\"、\"),a(\"code\",[t._v(\"padding\")]),t._v(\"属性设置的动画，然后通过\"),a(\"code\",[t._v(\"Interval\")]),t._v(\"来为每个动画指定在整个动画过程中的起始点和终点。下面我们来实现启动动画的路由：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StaggerRoute\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _StaggerRouteState \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_StaggerRouteState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_StaggerRouteState\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"StaggerRoute\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"with\")]),t._v(\" TickerProviderStateMixin \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  AnimationController _controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    _controller \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimationController\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        duration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"milliseconds\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2000\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        vsync\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\\n  Future\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Null\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_playAnimation\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//先正向执行动画\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" _controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"forward\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"orCancel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//再反向执行动画\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" _controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"reverse\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"orCancel\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"on\")]),t._v(\" TickerCanceled \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// the animation got canceled, probably because we were disposed\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\"  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GestureDetector\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      behavior\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" HitTestBehavior\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"opaque\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      onTap\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_playAnimation\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"black\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"withOpacity\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            border\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Border\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"  Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"black\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"withOpacity\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.5\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//调用我们定义的交织动画Widget\")]),t._v(\"\\n          child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"StaggerAnimation\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _controller\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"执行效果如图，点击图9-3灰色矩形，就可以看到整个动画效果，图9-4是动画执行过程中的一帧。\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:s(696),alt:\"图9-3\"}}),a(\"img\",{attrs:{src:s(697),alt:\"图9-4\"}})])])}),[],!1,null,null,null);n.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/81.f8e7c85e.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[81],{360:function(t,r,a){t.exports=a.p+\"assets/img/1-1.41c572c4.png\"},721:function(t,r,a){\"use strict\";a.r(r);var e=a(45),v=Object(e.a)({},(function(){var t=this,r=t.$createElement,e=t._self._c||r;return e(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[e(\"h1\",{attrs:{id:\"_1-2-初识flutter\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-2-初识flutter\"}},[t._v(\"#\")]),t._v(\" 1.2 初识Flutter\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"_1-2-1-flutter简介\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-2-1-flutter简介\"}},[t._v(\"#\")]),t._v(\" 1.2.1 Flutter简介\")]),t._v(\" \"),e(\"p\",[t._v(\"Flutter 是 Google推出并开源的移动应用开发框架，主打跨平台、高保真、高性能。开发者可以通过 Dart语言开发 App，一套代码同时运行在 iOS 和 Android平台。 Flutter提供了丰富的组件、接口，开发者可以很快地为 Flutter添加 native扩展。同时 Flutter还使用 Native引擎渲染视图，这无疑能为用户提供良好的体验。\")]),t._v(\" \"),e(\"h4\",{attrs:{id:\"跨平台自绘引擎\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#跨平台自绘引擎\"}},[t._v(\"#\")]),t._v(\" 跨平台自绘引擎\")]),t._v(\" \"),e(\"p\",[t._v(\"Flutter与用于构建移动应用程序的其它大多数框架不同，因为Flutter既不使用WebView，也不使用操作系统的原生控件。 相反，Flutter使用自己的高性能渲染引擎来绘制widget。这样不仅可以保证在Android和iOS上UI的一致性，而且也可以避免对原生控件依赖而带来的限制及高昂的维护成本。\")]),t._v(\" \"),e(\"p\",[t._v(\"Flutter使用Skia作为其2D渲染引擎，Skia是Google的一个2D图形处理函数库，包含字型、坐标转换，以及点阵图都有高效能且简洁的表现，Skia是跨平台的，并提供了非常友好的API，目前Google Chrome浏览器和Android均采用Skia作为其绘图引擎。\")]),t._v(\" \"),e(\"p\",[t._v(\"目前Flutter默认支持iOS、Android、Fuchsia（Google新的自研操作系统）三个移动平台。但Flutter亦可支持Web开发（Flutter for web）和PC开发，本书的示例和介绍主要是基于iOS和Android平台的，其它平台读者可以自行了解。\")]),t._v(\" \"),e(\"h4\",{attrs:{id:\"高性能\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#高性能\"}},[t._v(\"#\")]),t._v(\" 高性能\")]),t._v(\" \"),e(\"p\",[t._v(\"Flutter高性能主要靠两点来保证，首先，Flutter APP采用Dart语言开发。Dart在 JIT（即时编译）模式下，速度与 JavaScript基本持平。但是 Dart支持 AOT，当以 AOT模式运行时，JavaScript便远远追不上了。速度的提升对高帧率下的视图数据计算很有帮助。其次，Flutter使用自己的渲染引擎来绘制UI，布局数据等由Dart语言直接控制，所以在布局过程中不需要像RN那样要在JavaScript和Native之间通信，这在一些滑动和拖动的场景下具有明显优势，因为在滑动和拖动过程往往都会引起布局发生变化，所以JavaScript需要和Native之间不停的同步布局信息，这和在浏览器中要JavaScript频繁操作DOM所带来的问题是相同的，都会带来比较可观的性能开销。\")]),t._v(\" \"),e(\"h4\",{attrs:{id:\"采用dart语言开发\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#采用dart语言开发\"}},[t._v(\"#\")]),t._v(\" 采用Dart语言开发\")]),t._v(\" \"),e(\"p\",[t._v(\"这是一个很有意思，但也很有争议的问题，在了解Flutter为什么选择了 Dart而不是 JavaScript之前我们先来介绍两个概念：JIT和AOT。\")]),t._v(\" \"),e(\"p\",[t._v(\"目前，程序主要有两种运行方式：静态编译与动态解释。静态编译的程序在执行前全部被翻译为机器码，通常将这种类型称为\"),e(\"strong\",[t._v(\"AOT\")]),t._v(\" （Ahead of time）即 “提前编译”；而解释执行的则是一句一句边翻译边运行，通常将这种类型称为\"),e(\"strong\",[t._v(\"JIT\")]),t._v(\"（Just-in-time）即“即时编译”。AOT程序的典型代表是用C/C++开发的应用，它们必须在执行前编译成机器码，而JIT的代表则非常多，如JavaScript、python等，事实上，所有脚本语言都支持JIT模式。但需要注意的是JIT和AOT指的是程序运行方式，和编程语言并非强关联的，有些语言既可以以JIT方式运行也可以以AOT方式运行，如Java、Python，它们可以在第一次执行时编译成中间字节码、然后在之后执行时可以直接执行字节码，也许有人会说，中间字节码并非机器码，在程序执行时仍然需要动态将字节码转为机器码，是的，这没有错，不过通常我们区分是否为AOT的标准就是看代码在执行之前是否需要编译，只要需要编译，无论其编译产物是字节码还是机器码，都属于AOT。在此，读者不必纠结于概念，概念就是为了传达精神而发明的，只要读者能够理解其原理即可，得其神忘其形。\")]),t._v(\" \"),e(\"p\",[t._v(\"现在我们看看Flutter为什么选择Dart语言？笔者根据官方解释以及自己对Flutter的理解总结了以下几条（由于其它跨平台框架都将JavaScript作为其开发语言，所以主要将Dart和JavaScript做一个对比）：\")]),t._v(\" \"),e(\"ol\",[e(\"li\",[e(\"p\",[e(\"strong\",[t._v(\"开发效率高\")])]),t._v(\" \"),e(\"p\",[t._v(\"Dart运行时和编译器支持Flutter的两个关键特性的组合：\")]),t._v(\" \"),e(\"p\",[e(\"strong\",[t._v(\"基于JIT的快速开发周期\")]),t._v(\"：Flutter在开发阶段采用，采用JIT模式，这样就避免了每次改动都要进行编译，极大的节省了开发时间；\")]),t._v(\" \"),e(\"p\",[e(\"strong\",[t._v(\"基于AOT的发布包\")]),t._v(\":  Flutter在发布时可以通过AOT生成高效的ARM代码以保证应用性能。而JavaScript则不具有这个能力。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"strong\",[t._v(\"高性能\")])]),t._v(\" \"),e(\"p\",[t._v(\"Flutter旨在提供流畅、高保真的的UI体验。为了实现这一点，Flutter中需要能够在每个动画帧中运行大量的代码。这意味着需要一种既能提供高性能的语言，而不会出现会丢帧的周期性暂停，而Dart支持AOT，在这一点上可以做的比JavaScript更好。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"strong\",[t._v(\"快速内存分配\")])]),t._v(\" \"),e(\"p\",[t._v(\"Flutter框架使用函数式流，这使得它在很大程度上依赖于底层的内存分配器。因此，拥有一个能够有效地处理琐碎任务的内存分配器将显得十分重要，在缺乏此功能的语言中，Flutter将无法有效地工作。当然Chrome V8的JavaScript引擎在内存分配上也已经做的很好，事实上Dart开发团队的很多成员都是来自Chrome团队的，所以在内存分配上Dart并不能作为超越JavaScript的优势，而对于Flutter来说，它需要这样的特性，而Dart也正好满足而已。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"strong\",[t._v(\"类型安全\")])]),t._v(\" \"),e(\"p\",[t._v(\"由于Dart是类型安全的语言，支持静态类型检测，所以可以在编译前发现一些类型的错误，并排除潜在问题，这一点对于前端开发者来说可能会更具有吸引力。与之不同的，JavaScript是一个弱类型语言，也因此前端社区出现了很多给JavaScript代码添加静态类型检测的扩展语言和工具，如：微软的TypeScript以及Facebook的Flow。相比之下，Dart本身就支持静态类型，这是它的一个重要优势。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"strong\",[t._v(\"Dart团队就在你身边\")])]),t._v(\" \"),e(\"p\",[t._v(\"看似不起眼，实则举足轻重。由于有Dart团队的积极投入，Flutter团队可以获得更多、更方便的支持，正如Flutter官网所述“我们正与Dart社区进行密切合作，以改进Dart在Flutter中的使用。例如，当我们最初采用Dart时，该语言并没有提供生成原生二进制文件的工具链（这对于实现可预测的高性能具有很大的帮助），但是现在它实现了，因为Dart团队专门为Flutter构建了它。同样，Dart VM之前已经针对吞吐量进行了优化，但团队现在正在优化VM的延迟时间，这对于Flutter的工作负载更为重要。”\")])])]),t._v(\" \"),e(\"h4\",{attrs:{id:\"总结\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),e(\"p\",[t._v(\"本节主要介绍了一下Flutter的特点，如果你感到有些点还不是很好理解，不用着急，随着日后对Flutter细节的了解，再回过头来看，相信你会有更深的体会。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"_1-2-2-flutter框架结构\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-2-2-flutter框架结构\"}},[t._v(\"#\")]),t._v(\" 1.2.2 Flutter框架结构\")]),t._v(\" \"),e(\"p\",[t._v(\"本节我们先对Flutter的框架做一个整体介绍，旨在让读者心中有一个整体的印象，这对初学者来说非常重要。如果一下子便深入到Flutter中，就会像是一个在沙漠中没有地图的人，即使可以找到一个绿洲，但是他也不会知道下一个绿洲在哪。因此，无论学什么技术，都要先有一张清晰的“地图”，而我们的学习过程就是“按图索骥”，这样我们才不会陷于细节而“目无全牛”。言归正传，我们看一下Flutter官方提供的Flutter框架图，如图1-1所示：\")]),t._v(\" \"),e(\"p\",[e(\"img\",{attrs:{src:a(360),alt:\"图1-1\"}})]),t._v(\" \"),e(\"h3\",{attrs:{id:\"flutter-framework\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#flutter-framework\"}},[t._v(\"#\")]),t._v(\" Flutter Framework\")]),t._v(\" \"),e(\"p\",[t._v(\"这是一个纯 Dart实现的 SDK，它实现了一套基础库，自底向上，我们来简单介绍一下：\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"p\",[t._v(\"底下两层（Foundation和Animation、Painting、Gestures）在Google的一些视频中被合并为一个dart UI层，对应的是Flutter中的\"),e(\"code\",[t._v(\"dart:ui\")]),t._v(\"包，它是Flutter引擎暴露的底层UI库，提供动画、手势及绘制能力。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[t._v(\"Rendering层，这一层是一个抽象的布局层，它依赖于dart UI层，Rendering层会构建一个UI树，当UI树有变化时，会计算出有变化的部分，然后更新UI树，最终将UI树绘制到屏幕上，这个过程类似于React中的虚拟DOM。Rendering层可以说是Flutter UI框架最核心的部分，它除了确定每个UI元素的位置、大小之外还要进行坐标变换、绘制(调用底层dart:ui)。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[t._v(\"Widgets层是Flutter提供的的一套基础组件库，在基础组件库之上，Flutter还提供了 Material 和Cupertino两种视觉风格的组件库。而\"),e(\"strong\",[t._v(\"我们Flutter开发的大多数场景，只是和这两层打交道\")]),t._v(\"。\")])])]),t._v(\" \"),e(\"h3\",{attrs:{id:\"flutter-engine\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#flutter-engine\"}},[t._v(\"#\")]),t._v(\" Flutter Engine\")]),t._v(\" \"),e(\"p\",[t._v(\"这是一个纯 C++实现的 SDK，其中包括了 Skia引擎、Dart运行时、文字排版引擎等。在代码调用 \"),e(\"code\",[t._v(\"dart:ui\")]),t._v(\"库时，调用最终会走到Engine层，然后实现真正的绘制逻辑。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"总结-2\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结-2\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),e(\"p\",[t._v(\"Flutter框架本身有着良好的分层设计，本节旨在让读者对Flutter整体框架有个大概的印象，相信到现在为止，读者已经对Flutter有一个初始印象，在我们正式动手之前，我们还需要了解一下Flutter的开发语言Dart。\")]),t._v(\" \"),e(\"h2\",{attrs:{id:\"_1-2-3-如何学习flutter\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_1-2-3-如何学习flutter\"}},[t._v(\"#\")]),t._v(\" 1.2.3 如何学习Flutter\")]),t._v(\" \"),e(\"p\",[t._v(\"本节给大家一些学习建议，分享一下笔者在学习Flutter中的一些心得，希望可以帮助你提高学习效率，避免不必要的坑。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"资源\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#资源\"}},[t._v(\"#\")]),t._v(\" 资源\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"p\",[e(\"strong\",[t._v(\"官网\")]),t._v(\"：阅读Flutter官网的资源是快速入门的最佳方式，同时官网也是了解最新Flutter发展动态的地方，由于目前Flutter仍然处于快速发展阶段，所以建议读者还是时不时的去官网看看有没有新的动态。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"strong\",[t._v(\"源码及注释\")]),t._v(\"：源码注释应作为学习Flutter的第一文档，Flutter SDK的源码是开源的，并且注释非常详细，也有很多示例，实际上，Flutter官方的SDK文档就是通过注释生成的。源码结合注释可以帮你解决大多数问题。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"strong\",[t._v(\"Github\")]),t._v(\"：如果遇到的问题在StackOverflow上也没有找到答案，可以去github flutter 项目下提issue。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"strong\",[t._v(\"Gallery源码\")]),t._v(\"：Gallery是Flutter官方示例APP，里面有丰富的示例，读者可以在网上下载安装。Gallery的源码在Flutter源码“examples”目录下。\")])])]),t._v(\" \"),e(\"h3\",{attrs:{id:\"社区\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#社区\"}},[t._v(\"#\")]),t._v(\" 社区\")]),t._v(\" \"),e(\"ul\",[e(\"li\",[e(\"strong\",[t._v(\"StackOverflow\")]),t._v(\"：如果你还没听过StackOverflow，这是目前全球最大的程序员问答社区，现在也是活跃度最高的Flutter问答社区。StackOverflow上面除了世界各地的Flutter使用者会在上面交流之外，Flutter开发团队的成员也经常会在上面回答问题。\")]),t._v(\" \"),e(\"li\",[e(\"strong\",[t._v(\"Flutter中文网社区\")]),t._v(\"：Flutter中文网(https://flutterchina.club)是笔者维护中文网站，目前也是最大的中文资源社区，上面提供了Flutter官网的文档翻译、开源项目、及案例，还有申请加入组织的入口哦。\")]),t._v(\" \"),e(\"li\",[e(\"strong\",[t._v(\"博客\")]),t._v(\"：随着Flutter技术的推广，相信很快网上将会有很多Flutter相关的文章、博客，读者可以多去浏览、阅读。\")])]),t._v(\" \"),e(\"h3\",{attrs:{id:\"总结-3\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结-3\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),e(\"p\",[t._v(\"有了资料和社区后，对于我们学习者自身来说，最重要的还是要多动手、多实践，在本书后面的章节中，希望读者能够亲自动手写一下示例。准备好了吗，下一章中，我们将正式进入Flutter的世界！\")])])}),[],!1,null,null,null);r.default=v.exports}}]);"
  },
  {
    "path": "docs/assets/js/82.a6b89300.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[82],{373:function(t,s,n){t.exports=n.p+\"assets/img/10-1.f2135ea6.png\"},733:function(t,s,n){\"use strict\";n.r(s);var a=n(45),e=Object(a.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_10-2-组合现有组件\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_10-2-组合现有组件\"}},[t._v(\"#\")]),t._v(\" 10.2 组合现有组件\")]),t._v(\" \"),a(\"p\",[t._v(\"在Flutter中页面UI通常都是由一些低阶别的组件组合而成，当我们需要封装一些通用组件时，应该首先考虑是否可以通过组合其它组件来实现，如果可以，则应优先使用组合，因为直接通过现有组件拼装会非常简单、灵活、高效。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"示例-自定义渐变按钮\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例-自定义渐变按钮\"}},[t._v(\"#\")]),t._v(\" 示例：自定义渐变按钮\")]),t._v(\" \"),a(\"p\",[t._v(\"Flutter Material组件库中的按钮默认不支持渐变背景，为了实现渐变背景按钮，我们自定义一个\"),a(\"code\",[t._v(\"GradientButton\")]),t._v(\"组件，它需要支持一下功能：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"背景支持渐变色\")]),t._v(\" \"),a(\"li\",[t._v(\"手指按下时有涟漪效果\")]),t._v(\" \"),a(\"li\",[t._v(\"可以支持圆角\")])]),t._v(\" \"),a(\"p\",[t._v(\"我们先来看看最终要实现的效果（图10-1）：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(373),alt:\"gradient-button\"}})]),t._v(\" \"),a(\"p\",[t._v(\"我们\"),a(\"code\",[t._v(\"DecoratedBox\")]),t._v(\"可以支持背景色渐变和圆角，\"),a(\"code\",[t._v(\"InkWell\")]),t._v(\"在手指按下有涟漪效果，所以我们可以通过组合\"),a(\"code\",[t._v(\"DecoratedBox\")]),t._v(\"和\"),a(\"code\",[t._v(\"InkWell\")]),t._v(\"来实现\"),a(\"code\",[t._v(\"GradientButton\")]),t._v(\"，代码如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"GradientButton\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GradientButton\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"borderRadius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 渐变色数组\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" List\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Color\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 按钮宽高\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" double width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" double height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Widget child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" BorderRadius borderRadius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//点击回调\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" GestureTapCallback onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    ThemeData theme \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Theme\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//确保colors数组不空\")]),t._v(\"\\n    List\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Color\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _colors \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" colors \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"theme\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"primaryColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" theme\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"primaryColorDark \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" theme\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"primaryColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecoratedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        gradient\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"LinearGradient\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        borderRadius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" borderRadius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Material\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        type\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MaterialType\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"transparency\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"InkWell\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          splashColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"last\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          highlightColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"transparent\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          borderRadius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" borderRadius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          onTap\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ConstrainedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            constraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BoxConstraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"tightFor\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"8.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DefaultTextStyle\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  style\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"fontWeight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" FontWeight\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"bold\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"可以看到\"),a(\"code\",[t._v(\"GradientButton\")]),t._v(\"是由\"),a(\"code\",[t._v(\"DecoratedBox\")]),t._v(\"、\"),a(\"code\",[t._v(\"Padding\")]),t._v(\"、\"),a(\"code\",[t._v(\"Center\")]),t._v(\"、\"),a(\"code\",[t._v(\"InkWell\")]),t._v(\"等组件组合而成。当然上面的代码只是一个示例，作为一个按钮它还并不完整，比如没有禁用状态，读者可以根据实际需要来完善。\")]),t._v(\" \"),a(\"h4\",{attrs:{id:\"使用gradientbutton\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#使用gradientbutton\"}},[t._v(\"#\")]),t._v(\" 使用GradientButton\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'../widgets/index.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"GradientButtonRoute\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _GradientButtonRouteState \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_GradientButtonRouteState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_GradientButtonRouteState\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"GradientButtonRoute\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GradientButton\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"orange\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Submit\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" onTap\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GradientButton\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"lightGreen\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"green\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"700\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Submit\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" onTap\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GradientButton\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"lightBlue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blueAccent\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Submit\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" onTap\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onTap\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"button click\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"h3\",{attrs:{id:\"总结\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),a(\"p\",[t._v(\"通过组合的方式定义组件和我们之前写界面并无差异，不过在抽离出单独的组件时我们要考虑代码规范性，如必要参数要用\"),a(\"code\",[t._v(\"@required\")]),t._v(\" 标注，对于可选参数在特定场景需要判空或设置默认值等。这是由于使用者大多时候可能不了解组件的内部细节，所以为了保证代码健壮性，我们需要在用户错误地使用组件时能够兼容或报错提示（使用\"),a(\"code\",[t._v(\"assert\")]),t._v(\"断言函数）。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/83.daf1b549.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[83],{370:function(t,s,a){t.exports=a.p+\"assets/img/10-3.3989fea9.png\"},725:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_10-4-自绘组件-custompaint与canvas\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_10-4-自绘组件-custompaint与canvas\"}},[t._v(\"#\")]),t._v(\" 10.4 自绘组件 （CustomPaint与Canvas）\")]),t._v(\" \"),n(\"p\",[t._v(\"对于一些复杂或不规则的UI，我们可能无法通过组合其它组件的方式来实现，比如我们需要一个正六边形、一个渐变的圆形进度条、一个棋盘等。当然，有时候我们可以使用图片来实现，但在一些需要动态交互的场景静态图片也是实现不了的，比如要实现一个手写输入面板，这时，我们就需要来自己绘制UI外观。\")]),t._v(\" \"),n(\"p\",[t._v(\"几乎所有的UI系统都会提供一个自绘UI的接口，这个接口通常会提供一块2D画布\"),n(\"code\",[t._v(\"Canvas\")]),t._v(\"，\"),n(\"code\",[t._v(\"Canvas\")]),t._v(\"内部封装了一些基本绘制的API，开发者可以通过\"),n(\"code\",[t._v(\"Canvas\")]),t._v(\"绘制各种自定义图形。在Flutter中，提供了一个\"),n(\"code\",[t._v(\"CustomPaint\")]),t._v(\" 组件，它可以结合画笔\"),n(\"code\",[t._v(\"CustomPainter\")]),t._v(\"来实现自定义图形绘制。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"custompaint\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#custompaint\"}},[t._v(\"#\")]),t._v(\" CustomPaint\")]),t._v(\" \"),n(\"p\",[t._v(\"我们看看\"),n(\"code\",[t._v(\"CustomPaint\")]),t._v(\"构造函数：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CustomPaint\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"painter\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"foregroundPainter\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"size \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"zero\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isComplex \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"willChange \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n  Widget child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//子节点，可以为空\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"painter\")]),t._v(\": 背景画笔，会显示在子节点后面;\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"foregroundPainter\")]),t._v(\": 前景画笔，会显示在子节点前面\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"size\")]),t._v(\"：当child为null时，代表默认绘制区域大小，如果有child则忽略此参数，画布尺寸则为child尺寸。如果有child但是想指定画布为特定大小，可以使用SizeBox包裹CustomPaint实现。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"isComplex\")]),t._v(\"：是否复杂的绘制，如果是，Flutter会应用一些缓存策略来减少重复渲染的开销。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"willChange\")]),t._v(\"：和\"),n(\"code\",[t._v(\"isComplex\")]),t._v(\"配合使用，当启用缓存时，该属性代表在下一帧中绘制是否会改变。\")])]),t._v(\" \"),n(\"p\",[t._v(\"可以看到，绘制时我们需要提供前景或背景画笔，两者也可以同时提供。我们的画笔需要继承\"),n(\"code\",[t._v(\"CustomPainter\")]),t._v(\"类，我们在画笔类中实现真正的绘制逻辑。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"注意\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#注意\"}},[t._v(\"#\")]),t._v(\" 注意\")]),t._v(\" \"),n(\"p\",[t._v(\"如果\"),n(\"code\",[t._v(\"CustomPaint\")]),t._v(\"有子节点，为了避免子节点不必要的重绘并提高性能，通常情况下都会将子节点包裹在\"),n(\"code\",[t._v(\"RepaintBoundary\")]),t._v(\"组件中，这样会在绘制时就会创建一个新的绘制层（Layer），其子组件将在新的Layer上绘制，而父组件将在原来Layer上绘制，也就是说\"),n(\"code\",[t._v(\"RepaintBoundary\")]),t._v(\" 子组件的绘制将独立于父组件的绘制，\"),n(\"code\",[t._v(\"RepaintBoundary\")]),t._v(\"会隔离其子节点和\"),n(\"code\",[t._v(\"CustomPaint\")]),t._v(\"本身的绘制边界。示例如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CustomPaint\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Size\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//指定画布大小\")]),t._v(\"\\n  painter\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyPainter\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RepaintBoundary\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"h3\",{attrs:{id:\"custompainter\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#custompainter\"}},[t._v(\"#\")]),t._v(\" CustomPainter\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"CustomPainter\")]),t._v(\"中提定义了一个虚函数\"),n(\"code\",[t._v(\"paint\")]),t._v(\"：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language- extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[n(\"code\",[t._v(\"void paint(Canvas canvas, Size size);\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"paint\")]),t._v(\"有两个参数:\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"Canvas\")]),t._v(\"：一个画布，包括各种绘制方法，我们列出一下常用的方法：\")]),t._v(\" \"),n(\"table\",[n(\"thead\",[n(\"tr\",[n(\"th\",[t._v(\"API名称\")]),t._v(\" \"),n(\"th\",[t._v(\"功能\")])])]),t._v(\" \"),n(\"tbody\",[n(\"tr\",[n(\"td\",[t._v(\"drawLine\")]),t._v(\" \"),n(\"td\",[t._v(\"画线\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"drawPoint\")]),t._v(\" \"),n(\"td\",[t._v(\"画点\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"drawPath\")]),t._v(\" \"),n(\"td\",[t._v(\"画路径\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"drawImage\")]),t._v(\" \"),n(\"td\",[t._v(\"画图像\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"drawRect\")]),t._v(\" \"),n(\"td\",[t._v(\"画矩形\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"drawCircle\")]),t._v(\" \"),n(\"td\",[t._v(\"画圆\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"drawOval\")]),t._v(\" \"),n(\"td\",[t._v(\"画椭圆\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"drawArc\")]),t._v(\" \"),n(\"td\",[t._v(\"画圆弧\")])])])])]),t._v(\" \"),n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"Size\")]),t._v(\"：当前绘制区域大小。\")])])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"画笔paint\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#画笔paint\"}},[t._v(\"#\")]),t._v(\" 画笔Paint\")]),t._v(\" \"),n(\"p\",[t._v(\"现在画布有了，我们最后还缺一个画笔，Flutter提供了\"),n(\"code\",[t._v(\"Paint\")]),t._v(\"类来实现画笔。在\"),n(\"code\",[t._v(\"Paint\")]),t._v(\"中，我们可以配置画笔的各种属性如粗细、颜色、样式等。如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" paint \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Paint\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//创建一个画笔并配置其属性\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isAntiAlias \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//是否抗锯齿\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"style \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" PaintingStyle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"fill \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//画笔样式：填充\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Color\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0x77cdb175\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//画笔颜色\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"更多的配置属性读者可以参考Paint类定义。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"示例-五子棋-盘\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例-五子棋-盘\"}},[t._v(\"#\")]),t._v(\" 示例：五子棋/盘\")]),t._v(\" \"),n(\"p\",[t._v(\"下面我们通过一个五子棋游戏中棋盘和棋子的绘制来演示自绘UI的过程，首先我们看一下我们的目标效果，如图10-3所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(370),alt:\"图10-3\"}})]),t._v(\" \"),n(\"p\",[t._v(\"代码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'dart:math'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CustomPaintRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CustomPaint\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Size\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//指定画布大小\")]),t._v(\"\\n        painter\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyPainter\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyPainter\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CustomPainter\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"paint\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Canvas canvas\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Size size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    double eWidth \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"width \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"15\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    double eHeight \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"height \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"15\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//画棋盘背景\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" paint \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Paint\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isAntiAlias \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"style \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" PaintingStyle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"fill \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//填充\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"color \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Color\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0x77cdb175\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//背景为纸黄色\")]),t._v(\"\\n    canvas\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"drawRect\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"zero \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&\")]),t._v(\" size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" paint\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//画棋盘网格\")]),t._v(\"\\n    paint\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"style \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" PaintingStyle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"stroke \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//线\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"color \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"black87\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"strokeWidth \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"for\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int i \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" i \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"15\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"++\")]),t._v(\"i\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      double dy \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" eHeight \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" i\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      canvas\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"drawLine\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" dy\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" dy\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" paint\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"for\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int i \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" i \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"15\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"++\")]),t._v(\"i\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      double dx \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" eWidth \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" i\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      canvas\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"drawLine\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"dx\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"dx\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" paint\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//画一个黑子\")]),t._v(\"\\n    paint\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"style \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" PaintingStyle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"fill\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"color \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"black\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    canvas\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"drawCircle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"width \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" eWidth \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"height \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" eHeight \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"min\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"eWidth \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" eHeight \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      paint\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//画一个白子\")]),t._v(\"\\n    paint\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"color \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    canvas\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"drawCircle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"width \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" eWidth \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"height \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" eHeight \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"min\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"eWidth \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" eHeight \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      paint\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//在实际场景中正确利用此回调可以避免重绘开销，本示例我们简单的返回true\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  bool \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"shouldRepaint\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"CustomPainter oldDelegate\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"h3\",{attrs:{id:\"性能\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#性能\"}},[t._v(\"#\")]),t._v(\" 性能\")]),t._v(\" \"),n(\"p\",[t._v(\"绘制是比较昂贵的操作，所以我们在实现自绘控件时应该考虑到性能开销，下面是两条关于性能优化的建议：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"p\",[t._v(\"尽可能的利用好\"),n(\"code\",[t._v(\"shouldRepaint\")]),t._v(\"返回值；在UI树重新build时，控件在绘制前都会先调用该方法以确定是否有必要重绘；假如我们绘制的UI不依赖外部状态，那么就应该始终返回\"),n(\"code\",[t._v(\"false\")]),t._v(\"，因为外部状态改变导致重新build时不会影响我们的UI外观；如果绘制依赖外部状态，那么我们就应该在\"),n(\"code\",[t._v(\"shouldRepaint\")]),t._v(\"中判断依赖的状态是否改变，如果已改变则应返回\"),n(\"code\",[t._v(\"true\")]),t._v(\"来重绘，反之则应返回\"),n(\"code\",[t._v(\"false\")]),t._v(\"不需要重绘。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"绘制尽可能多的分层；在上面五子棋的示例中，我们将棋盘和棋子的绘制放在了一起，这样会有一个问题：由于棋盘始终是不变的，用户每次落子时变的只是棋子，但是如果按照上面的代码来实现，每次绘制棋子时都要重新绘制一次棋盘，这是没必要的。优化的方法就是将棋盘单独抽为一个组件，并设置其\"),n(\"code\",[t._v(\"shouldRepaint\")]),t._v(\"回调值为\"),n(\"code\",[t._v(\"false\")]),t._v(\"，然后将棋盘组件作为背景。然后将棋子的绘制放到另一个组件中，这样每次落子时只需要绘制棋子。\")])])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"总结\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),n(\"p\",[t._v(\"自绘控件非常强大，理论上可以实现任何2D图形外观，实际上Flutter提供的所有组件最终都是通过调用Canvas绘制出来的，只不过绘制的逻辑被封装起来了，读者有兴趣可以查看具有外观样式的组件源码，找到其对应的\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"对象，如\"),n(\"code\",[t._v(\"Text\")]),t._v(\"对应的\"),n(\"code\",[t._v(\"RenderParagraph\")]),t._v(\"对象最终会通过\"),n(\"code\",[t._v(\"Canvas\")]),t._v(\"实现文本绘制逻辑。下一节我们会再通过一个自绘的圆形背景渐变进度条的实例来帮助读者加深印象。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/84.f330e398.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[84],{371:function(t,s,n){t.exports=n.p+\"assets/img/gradient_circular_progress.4cea87cb.png\"},726:function(t,s,n){\"use strict\";n.r(s);var a=n(45),p=Object(a.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_10-5-自绘实例-圆形背景渐变进度条\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_10-5-自绘实例-圆形背景渐变进度条\"}},[t._v(\"#\")]),t._v(\" 10.5 自绘实例：圆形背景渐变进度条\")]),t._v(\" \"),a(\"p\",[t._v(\"本节我们实现一个圆形背景渐变进度条，它支持：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"支持多种背景渐变色。\")]),t._v(\" \"),a(\"li\",[t._v(\"任意弧度；进度条可以不是整圆。\")]),t._v(\" \"),a(\"li\",[t._v(\"可以自定义粗细、两端是否圆角等样式。\")])]),t._v(\" \"),a(\"p\",[t._v(\"可以发现要实现这样的一个进度条是无法通过现有组件组合而成的，所以我们通过自绘方式实现，代码如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'dart:math'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"GradientCircularProgressIndicator\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GradientCircularProgressIndicator\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"strokeWidth \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"stops\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"strokeCapRound \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"backgroundColor \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Color\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0xFFEEEEEE\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"totalAngle \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" pi\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"///粗细\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" double strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// 圆的半径\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" double radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"///两端是否为圆角\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" bool strokeCapRound\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// 当前进度，取值范围 [0.0-1.0]\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" double value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// 进度条背景色\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Color backgroundColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// 进度条的总弧度，2*PI为整圆，小于2*PI则不是整圆\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" double totalAngle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// 渐变色数组\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" List\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Color\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// 渐变色的终止点，对应colors属性\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" List\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" stops\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    double _offset \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 如果两端为圆角，则需要对起始位置进行调整，否则圆角部分会偏离起始位置\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 下面调整的角度的计算公式是通过数学几何知识得出，读者有兴趣可以研究一下为什么是这样  \")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"strokeCapRound\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _offset \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"asin\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"strokeWidth \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"radius \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" _colors \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_colors \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      Color color \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Theme\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"accentColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      _colors \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" Transform\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"rotate\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      angle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\"pi \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2.0\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" _offset\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CustomPaint\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          size\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Size\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fromRadius\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          painter\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_GradientCircularProgressPainter\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            strokeCapRound\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" strokeCapRound\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            backgroundColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" backgroundColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            total\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" totalAngle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//实现画笔\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_GradientCircularProgressPainter\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"CustomPainter\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_GradientCircularProgressPainter\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"10.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"strokeCapRound\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"backgroundColor \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Color\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0xFFEEEEEE\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"total \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" pi\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"stops\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" double strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" bool strokeCapRound\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" double value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Color backgroundColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" List\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Color\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" double total\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" double radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" List\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"double\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" stops\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"paint\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Canvas canvas\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Size size\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"radius \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      size \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Size\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fromRadius\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    double _offset \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" strokeWidth \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    double _value \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"value \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    _value \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"clamp\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" total\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    double _start \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"strokeCapRound\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _start \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"asin\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"size\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"width \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n    Rect rect \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_offset\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" _offset\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Size\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        size\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"width \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        size\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"height \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" strokeWidth\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" paint \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Paint\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"strokeCap \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" strokeCapRound \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" StrokeCap\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"round \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" StrokeCap\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"butt\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"style \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" PaintingStyle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"stroke\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isAntiAlias \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"strokeWidth \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 先画背景\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"backgroundColor \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"transparent\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      paint\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"color \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" backgroundColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      canvas\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"drawArc\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          rect\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          _start\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          total\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          paint\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 再画前景，应用渐变\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_value \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      paint\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"shader \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SweepGradient\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        startAngle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        endAngle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        stops\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" stops\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createShader\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"rect\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n      canvas\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"drawArc\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          rect\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          _start\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          _value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          paint\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  bool \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"shouldRepaint\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"CustomPainter oldDelegate\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"下面我们来测试一下，为了尽可能多的展示\"),a(\"code\",[t._v(\"GradientCircularProgressIndicator\")]),t._v(\"的不同外观和用途，这个示例代码会比较长，并且添加了动画，建议读者将此示例运行起来观看实际效果，我们先看看其中的一帧动画的截图：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(371),alt:\"gradient_circular_progress\"}})]),t._v(\" \"),a(\"p\",[t._v(\"示例代码：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'dart:math'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'../widgets/index.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"GradientCircularProgressRoute\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  GradientCircularProgressRouteState \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"GradientCircularProgressRouteState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"GradientCircularProgressRouteState\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"GradientCircularProgressRoute\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"with\")]),t._v(\" TickerProviderStateMixin \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  AnimationController _animationController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    _animationController \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimationController\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"vsync\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" duration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    bool isForward \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    _animationController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addStatusListener\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"status\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"status \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" AnimationStatus\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"forward\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        isForward \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"status \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" AnimationStatus\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"completed \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"||\")]),t._v(\"\\n          status \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" AnimationStatus\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"dismissed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"isForward\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          _animationController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"reverse\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          _animationController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"forward\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"status \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" AnimationStatus\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"reverse\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        isForward \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    _animationController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"forward\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    _animationController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SingleChildScrollView\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          crossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" CrossAxisAlignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AnimatedBuilder\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              animation\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _animationController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              builder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Widget child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"symmetric\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"vertical\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Wrap\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                        spacing\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"10.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        runSpacing\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GradientCircularProgressIndicator\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                            \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// No gradient\")]),t._v(\"\\n                            colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _animationController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GradientCircularProgressIndicator\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                            colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"orange\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _animationController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GradientCircularProgressIndicator\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                            colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"orange\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"5.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _animationController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GradientCircularProgressIndicator\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                            colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"teal\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cyan\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"5.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            strokeCapRound\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CurvedAnimation\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                                    parent\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _animationController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                    curve\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Curves\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"decelerate\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TurnBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                            turns\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"/\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"8\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GradientCircularProgressIndicator\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                                colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"orange\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"5.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                strokeCapRound\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                backgroundColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                totalAngle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.5\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" pi\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CurvedAnimation\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                                        parent\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _animationController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                        curve\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Curves\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"ease\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                                    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RotatedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                            quarterTurns\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GradientCircularProgressIndicator\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                                colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"700\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                strokeCapRound\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                backgroundColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"transparent\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _animationController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GradientCircularProgressIndicator\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                            colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                              Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                              Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"amber\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                              Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cyan\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                              Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"green\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                              Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                              Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\\n                            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"5.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            strokeCapRound\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _animationController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GradientCircularProgressIndicator\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                        colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"700\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _animationController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                        padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"symmetric\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"vertical\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GradientCircularProgressIndicator\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                          colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"700\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _animationController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          strokeCapRound\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//剪裁半圆\")]),t._v(\"\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ClipRect\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Align\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                          alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"topCenter\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          heightFactor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".5\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                            padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"only\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"bottom\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"8.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                              \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//width: 100.0,\")]),t._v(\"\\n                              child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TurnBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                                turns\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".75\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GradientCircularProgressIndicator\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                                  colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"teal\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cyan\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"500\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                  radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                  strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"8.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                  value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _animationController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                  totalAngle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" pi\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                  strokeCapRound\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                        height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"104.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        width\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Stack\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                          alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                          children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                            \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Positioned\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                              height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                              top\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                              child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TurnBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                                turns\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".75\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"GradientCircularProgressIndicator\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                                  colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"teal\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"cyan\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"500\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                  radius\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                  strokeWidth\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"8.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                  value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _animationController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                  totalAngle\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" pi\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                  strokeCapRound\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                              padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"only\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"top\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"10.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                              child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                                \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"${(_animationController.value * 100).toInt()}%\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                style\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                                  fontSize\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"25.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                  color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blueGrey\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n                          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"怎么样，很炫酷吧！\"),a(\"code\",[t._v(\"GradientCircularProgressIndicator\")]),t._v(\"已经被添加进了笔者维护的flukit组件库中了，读者如果有需要，可以直接依赖flukit包。\")])])}),[],!1,null,null,null);s.default=p.exports}}]);"
  },
  {
    "path": "docs/assets/js/85.f647b247.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[85],{372:function(t,s,a){t.exports=a.p+\"assets/img/10-2.2a9ed156.png\"},730:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_10-3-组合实例-turnbox\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_10-3-组合实例-turnbox\"}},[t._v(\"#\")]),t._v(\" 10.3 组合实例：TurnBox\")]),t._v(\" \"),n(\"p\",[t._v(\"我们之前已经介绍过\"),n(\"code\",[t._v(\"RotatedBox\")]),t._v(\"，它可以旋转子组件，但是它有两个缺点：一是只能将其子节点以90度的倍数旋转；二是当旋转的角度发生变化时，旋转角度更新过程没有动画。\")]),t._v(\" \"),n(\"p\",[t._v(\"本节我们将实现一个\"),n(\"code\",[t._v(\"TurnBox\")]),t._v(\"组件，它不仅可以以任意角度来旋转其子节点，而且可以在角度发生变化时执行一个动画以过渡到新状态，同时，我们可以手动指定动画速度。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"TurnBox\")]),t._v(\"的完整代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/widgets.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TurnBox\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TurnBox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"turns \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//旋转的“圈”数,一圈为360度，如0.25圈即90度\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"speed \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//过渡动画执行的总时长\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" double turns\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" int speed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Widget child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _TurnBoxState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_TurnBoxState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_TurnBoxState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"TurnBox\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"with\")]),t._v(\" SingleTickerProviderStateMixin \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  AnimationController _controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    _controller \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimationController\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        vsync\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        lowerBound\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\"double\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"infinity\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        upperBound\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" double\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"infinity\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    _controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"turns\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    _controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RotationTransition\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      turns\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didUpdateWidget\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"TurnBox oldWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didUpdateWidget\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"oldWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//旋转角度发生变化时执行过渡动画  \")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"oldWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"turns \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"turns\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"animateTo\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"turns\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        duration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"milliseconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"speed\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        curve\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Curves\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"easeOut\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上面代码中：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"我们是通过组合\"),n(\"code\",[t._v(\"RotationTransition\")]),t._v(\"和child来实现的旋转效果。\")]),t._v(\" \"),n(\"li\",[t._v(\"在\"),n(\"code\",[t._v(\"didUpdateWidget\")]),t._v(\"中，我们判断要旋转的角度是否发生了变化，如果变了，则执行一个过渡动画。\")])]),t._v(\" \"),n(\"p\",[t._v(\"下面我们测试一下\"),n(\"code\",[t._v(\"TurnBox\")]),t._v(\"的功能，测试代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'../widgets/index.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TurnBoxRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _TurnBoxRouteState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_TurnBoxRouteState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_TurnBoxRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"TurnBoxRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  double _turns \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TurnBox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            turns\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _turns\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            speed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"500\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"refresh\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TurnBox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            turns\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _turns\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            speed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1000\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"refresh\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"150.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"顺时针旋转1/5圈\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                _turns \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"逆时针旋转1/5圈\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                _turns \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"测试代码运行后效果如图10-2所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(372),alt:\"图10-2\"}})]),t._v(\" \"),n(\"p\",[t._v(\"当我们点击旋转按钮时，两个图标的旋转都会旋转1/5圈，但旋转的速度是不同的，读者可以自己运行一下示例看看效果。\")]),t._v(\" \"),n(\"p\",[t._v(\"实际上本示例只组合了\"),n(\"code\",[t._v(\"RotationTransition\")]),t._v(\"一个组件，它是一个最简的组合类组件示例。另外，如果我们封装的是\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"，那么一定要注意在组件更新时是否需要同步状态。比如我们要封装一个富文本展示组件\"),n(\"code\",[t._v(\"MyRichText\")]),t._v(\" ，它可以自动处理url链接，定义如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyRichText\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyRichText\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 文本字符串\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"linkStyle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// url链接样式\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" TextStyle linkStyle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _MyRichTextState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_MyRichTextState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"接下来我们在\"),n(\"code\",[t._v(\"_MyRichTextState\")]),t._v(\"中要实现的功能有两个：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"解析文本字符串“text”，生成\"),n(\"code\",[t._v(\"TextSpan\")]),t._v(\"缓存起来；\")]),t._v(\" \"),n(\"li\",[t._v(\"在\"),n(\"code\",[t._v(\"build\")]),t._v(\"中返回最终的富文本样式；\")])]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"_MyRichTextState\")]),t._v(\" 实现的代码大致如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_MyRichTextState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"MyRichText\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\n  TextSpan _textSpan\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RichText\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _textSpan\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  TextSpan \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"parseText\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 耗时操作：解析文本字符串，构建出TextSpan。\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 省略具体实现。\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    _textSpan \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"parseText\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"由于解析文本字符串，构建出\"),n(\"code\",[t._v(\"TextSpan\")]),t._v(\"是一个耗时操作，为了不在每次build的时候都解析一次，所以我们在\"),n(\"code\",[t._v(\"initState\")]),t._v(\"中对解析的结果进行了缓存，然后再\"),n(\"code\",[t._v(\"build\")]),t._v(\"中直接使用解析的结果\"),n(\"code\",[t._v(\"_textSpan\")]),t._v(\"。这看起来很不错，但是上面的代码有一个严重的问题，就是父组件传入的\"),n(\"code\",[t._v(\"text\")]),t._v(\"发生变化时（组件树结构不变），那么\"),n(\"code\",[t._v(\"MyRichText\")]),t._v(\"显示的内容不会更新，原因就是\"),n(\"code\",[t._v(\"initState\")]),t._v(\"只会在State创建时被调用，所以在\"),n(\"code\",[t._v(\"text\")]),t._v(\"发生变化时，\"),n(\"code\",[t._v(\"parseText\")]),t._v(\"没有重新执行，导致\"),n(\"code\",[t._v(\"_textSpan\")]),t._v(\"任然是旧的解析值。要解决这个问题也很简单，我们只需添加一个\"),n(\"code\",[t._v(\"didUpdateWidget\")]),t._v(\"回调，然后再里面重新调用\"),n(\"code\",[t._v(\"parseText\")]),t._v(\"即可：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didUpdateWidget\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"MyRichText oldWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"text \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" oldWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    _textSpan \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"parseText\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didUpdateWidget\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"oldWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"有些读者可能会觉得这个点也很简单，是的，的确很简单，之所以要在这里反复强调是因为这个点在实际开发中很容易被忽略，它虽然简单，但却很重要。总之，当我们在State中会缓存某些依赖Widget参数的数据时，一定要注意在组件更新时是否需要同步状态。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/86.e82005d4.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[86],{377:function(t,s,a){t.exports=a.p+\"assets/img/11-1.dd10418c.png\"},742:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_11-2-通过httpclient发起http请求\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_11-2-通过httpclient发起http请求\"}},[t._v(\"#\")]),t._v(\" 11.2 通过HttpClient发起HTTP请求\")]),t._v(\" \"),n(\"p\",[t._v(\"Dart IO库中提供了用于发起Http请求的一些类，我们可以直接使用\"),n(\"code\",[t._v(\"HttpClient\")]),t._v(\"来发起请求。使用\"),n(\"code\",[t._v(\"HttpClient\")]),t._v(\"发起请求分为五步：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"p\",[t._v(\"创建一个\"),n(\"code\",[t._v(\"HttpClient\")]),t._v(\"：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\" HttpClient httpClient \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"HttpClient\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"打开Http连接，设置请求头：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"HttpClientRequest request \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" httpClient\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getUrl\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"uri\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"这一步可以使用任意Http Method，如\"),n(\"code\",[t._v(\"httpClient.post(...)\")]),t._v(\"、\"),n(\"code\",[t._v(\"httpClient.delete(...)\")]),t._v(\"等。如果包含Query参数，可以在构建uri时添加，如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Uri uri\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Uri\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"scheme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"https\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" host\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"flutterchina.club\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" queryParameters\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"xx\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"xx\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"yy\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"dd\"')]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"通过\"),n(\"code\",[t._v(\"HttpClientRequest\")]),t._v(\"可以设置请求header，如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"request\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"headers\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"user-agent\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"test\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"如果是post或put等可以携带请求体方法，可以通过HttpClientRequest对象发送request body，如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"String payload\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"...\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\nrequest\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"utf8\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"encode\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"payload\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//request.addStream(_inputStream); //可以直接添加输入流\")]),t._v(\"\\n\")])])])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"等待连接服务器：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"HttpClientResponse response \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" request\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"close\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"这一步完成后，请求信息就已经发送给服务器了，返回一个\"),n(\"code\",[t._v(\"HttpClientResponse\")]),t._v(\"对象，它包含响应头（header）和响应流(响应体的Stream)，接下来就可以通过读取响应流来获取响应内容。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"读取响应内容：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"String responseBody \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" response\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"transform\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"utf8\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"decoder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"join\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们通过读取响应流来获取服务器返回的数据，在读取时我们可以设置编码格式，这里是utf8。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"请求结束，关闭\"),n(\"code\",[t._v(\"HttpClient\")]),t._v(\"：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"httpClient\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"close\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"关闭client后，通过该client发起的所有请求都会中止。\")])])]),t._v(\" \"),n(\"h4\",{attrs:{id:\"示例\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),n(\"p\",[t._v(\"我们实现一个获取百度首页html的例子，示例效果如图11-1所示：\")]),t._v(\" \"),n(\"p\",[t._v(\"​    \"),n(\"img\",{attrs:{src:a(377),alt:\"图11-1\"}})]),t._v(\" \"),n(\"p\",[t._v(\"点击“获取百度首页”按钮后，会请求百度首页，请求成功后，我们将返回内容显示出来并在控制台打印响应header，代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'dart:convert'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'dart:io'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"HttpTestRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _HttpTestRouteState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_HttpTestRouteState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_HttpTestRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"HttpTestRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  bool _loading \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  String _text \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ConstrainedBox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      constraints\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BoxConstraints\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"expand\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SingleChildScrollView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"获取百度首页\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _loading \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                    _loading \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                    _text \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"正在请求...\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//创建一个HttpClient\")]),t._v(\"\\n                    HttpClient httpClient \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"HttpClient\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//打开Http连接\")]),t._v(\"\\n                    HttpClientRequest request \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" httpClient\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getUrl\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                        Uri\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"parse\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"https://www.baidu.com\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//使用iPhone的UA\")]),t._v(\"\\n                    request\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"headers\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"user-agent\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//等待连接服务器（会将请求信息发送给服务器）\")]),t._v(\"\\n                    HttpClientResponse response \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" request\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"close\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//读取响应内容\")]),t._v(\"\\n                    _text \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" response\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"transform\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"utf8\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"decoder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"join\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//输出响应头\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"response\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"headers\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//关闭client后，通过该client发起的所有请求都会中止。\")]),t._v(\"\\n                    httpClient\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"close\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"catch\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                    _text \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"请求失败：$e\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"finally\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                      _loading \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MediaQuery\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"width\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_text\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"replaceAll\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"RegExp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('r\"\\\\s\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"控制台输出：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language- extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[n(\"code\",[t._v(\"I/flutter (18545): connection: Keep-Alive\\nI/flutter (18545): cache-control: no-cache\\nI/flutter (18545): set-cookie: ....  //有多个，省略...\\nI/flutter (18545): transfer-encoding: chunked\\nI/flutter (18545): date: Tue, 30 Oct 2018 10:00:52 GMT\\nI/flutter (18545): content-encoding: gzip\\nI/flutter (18545): vary: Accept-Encoding\\nI/flutter (18545): strict-transport-security: max-age=172800\\nI/flutter (18545): content-type: text/html;charset=utf-8\\nI/flutter (18545): tracecode: 00525262401065761290103018, 00522983\\n\")])])]),n(\"h4\",{attrs:{id:\"httpclient配置\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#httpclient配置\"}},[t._v(\"#\")]),t._v(\" HttpClient配置\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"HttpClient\")]),t._v(\"有很多属性可以配置，常用的属性列表如下：\")]),t._v(\" \"),n(\"table\",[n(\"thead\",[n(\"tr\",[n(\"th\",[t._v(\"属性\")]),t._v(\" \"),n(\"th\",[t._v(\"含义\")])])]),t._v(\" \"),n(\"tbody\",[n(\"tr\",[n(\"td\",[t._v(\"idleTimeout\")]),t._v(\" \"),n(\"td\",[t._v(\"对应请求头中的keep-alive字段值，为了避免频繁建立连接，httpClient在请求结束后会保持连接一段时间，超过这个阈值后才会关闭连接。\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"connectionTimeout\")]),t._v(\" \"),n(\"td\",[t._v(\"和服务器建立连接的超时，如果超过这个值则会抛出SocketException异常。\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"maxConnectionsPerHost\")]),t._v(\" \"),n(\"td\",[t._v(\"同一个host，同时允许建立连接的最大数量。\")])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"autoUncompress\")]),t._v(\" \"),n(\"td\",[t._v('对应请求头中的Content-Encoding，如果设置为true，则请求头中Content-Encoding的值为当前HttpClient支持的压缩算法列表，目前只有\"gzip\"')])]),t._v(\" \"),n(\"tr\",[n(\"td\",[t._v(\"userAgent\")]),t._v(\" \"),n(\"td\",[t._v(\"对应请求头中的User-Agent字段。\")])])])]),t._v(\" \"),n(\"p\",[t._v(\"可以发现，有些属性只是为了更方便的设置请求头，对于这些属性，你完全可以通过\"),n(\"code\",[t._v(\"HttpClientRequest\")]),t._v(\"直接设置header，不同的是通过\"),n(\"code\",[t._v(\"HttpClient\")]),t._v(\"设置的对整个\"),n(\"code\",[t._v(\"httpClient\")]),t._v(\"都生效，而通过\"),n(\"code\",[t._v(\"HttpClientRequest\")]),t._v(\"设置的只对当前请求生效。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"http请求认证\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#http请求认证\"}},[t._v(\"#\")]),t._v(\" HTTP请求认证\")]),t._v(\" \"),n(\"p\",[t._v(\"Http协议的认证（Authentication）机制可以用于保护非公开资源。如果Http服务器开启了认证，那么用户在发起请求时就需要携带用户凭据，如果你在浏览器中访问了启用Basic认证的资源时，浏览就会弹出一个登录框，如：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:\"https://cdn.jsdelivr.net/gh/flutterchina/flutter-in-action@1.0/docs/imgs/image-20181031114207514.png\",alt:\"image-20181031114207514\"}})]),t._v(\" \"),n(\"p\",[t._v(\"我们先看看Basic认证的基本过程：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"p\",[t._v(\"客户端发送http请求给服务器，服务器验证该用户是否已经登录验证过了，如果没有的话，  服务器会返回一个401 Unauthozied给客户端，并且在响应header中添加一个 “WWW-Authenticate” 字段，例如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language- extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[n(\"code\",[t._v('WWW-Authenticate: Basic realm=\"admin\"\\n')])])]),n(\"p\",[t._v('其中\"Basic\"为认证方式，realm为用户角色的分组，可以在后台添加分组。')])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"客户端得到响应码后，将用户名和密码进行base64编码（格式为用户名:密码），设置请求头Authorization，继续访问 :\")]),t._v(\" \"),n(\"div\",{staticClass:\"language- extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[n(\"code\",[t._v(\"Authorization: Basic YXXFISDJFISJFGIJIJG\\n\")])])]),n(\"p\",[t._v(\"服务器验证用户凭据，如果通过就返回资源内容。\")])])]),t._v(\" \"),n(\"p\",[t._v(\"注意，Http的方式除了Basic认证之外还有：Digest认证、Client认证、Form Based认证等，目前Flutter的HttpClient只支持Basic和Digest两种认证方式，这两种认证方式最大的区别是发送用户凭据时，对于用户凭据的内容，前者只是简单的通过Base64编码（可逆），而后者会进行哈希运算，相对来说安全一点点，但是为了安全起见，\"),n(\"strong\",[t._v(\"无论是采用Basic认证还是Digest认证，都应该在Https协议下\")]),t._v(\"，这样可以防止抓包和中间人攻击。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"HttpClient\")]),t._v(\"关于Http认证的方法和属性：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"addCredentials(Uri url, String realm, HttpClientCredentials credentials)\")])]),t._v(\" \"),n(\"p\",[t._v(\"该方法用于添加用户凭据,如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"httpClient\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addCredentials\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_uri\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"admin\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"HttpClientBasicCredentials\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"username\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"password\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//Basic认证凭据\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"如果是Digest认证，可以创建Digest认证凭据：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"HttpClientDigestCredentials\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"username\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"password\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])])]),t._v(\" \"),n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"authenticate(Future<bool> f(Uri url, String scheme, String realm))\")])]),t._v(\" \"),n(\"p\",[t._v(\"这是一个setter，类型是一个回调，当服务器需要用户凭据且该用户凭据未被添加时，httpClient会调用此回调，在这个回调当中，一般会调用\"),n(\"code\",[t._v(\"addCredential()\")]),t._v(\"来动态添加用户凭证，例如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"httpClient\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"authenticate\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Uri url\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" String scheme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" String realm\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"url\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"host\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"xx.com\"')]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\" realm\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"admin\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    httpClient\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addCredentials\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"url\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"admin\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"HttpClientBasicCredentials\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"username\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"pwd\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"一个建议是，如果所有请求都需要认证，那么应该在HttpClient初始化时就调用\"),n(\"code\",[t._v(\"addCredentials()\")]),t._v(\"来添加全局凭证，而不是去动态添加。\")])])]),t._v(\" \"),n(\"h4\",{attrs:{id:\"代理\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#代理\"}},[t._v(\"#\")]),t._v(\" 代理\")]),t._v(\" \"),n(\"p\",[t._v(\"可以通过\"),n(\"code\",[t._v(\"findProxy\")]),t._v(\"来设置代理策略，例如，我们要将所有请求通过代理服务器（192.168.1.2:8888）发送出去：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"  client\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"findProxy \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"uri\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 如果需要过滤uri，可以手动判断\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"PROXY 192.168.1.2:8888\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"findProxy\")]),t._v(' 回调返回值是一个遵循浏览器PAC脚本格式的字符串，详情可以查看API文档，如果不需要代理，返回\"DIRECT\"即可。')]),t._v(\" \"),n(\"p\",[t._v(\"在APP开发中，很多时候我们需要抓包来调试，而抓包软件(如charles)就是一个代理，这时我们就可以将请求发送到我们的抓包软件，我们就可以在抓包软件中看到请求的数据了。\")]),t._v(\" \"),n(\"p\",[t._v(\"有时代理服务器也启用了身份验证，这和http协议的认证是相似的，HttpClient提供了对应的Proxy认证方法和属性：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"set\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"authenticateProxy\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"bool\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"f\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String host\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int port\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" String scheme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" String realm\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addProxyCredentials\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    String host\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int port\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" String realm\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" HttpClientCredentials credentials\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"他们的使用方法和上面“HTTP请求认证”一节中介绍的\"),n(\"code\",[t._v(\"addCredentials\")]),t._v(\"和\"),n(\"code\",[t._v(\"authenticate\")]),t._v(\" 相同，故不再赘述。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"证书校验\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#证书校验\"}},[t._v(\"#\")]),t._v(\" 证书校验\")]),t._v(\" \"),n(\"p\",[t._v(\"Https中为了防止通过伪造证书而发起的中间人攻击，客户端应该对自签名或非CA颁发的证书进行校验。\"),n(\"code\",[t._v(\"HttpClient\")]),t._v(\"对证书校验的逻辑如下：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"如果请求的Https证书是可信CA颁发的，并且访问host包含在证书的domain列表中(或者符合通配规则)并且证书未过期，则验证通过。\")]),t._v(\" \"),n(\"li\",[t._v(\"如果第一步验证失败，但在创建HttpClient时，已经通过SecurityContext将证书添加到证书信任链中，那么当服务器返回的证书在信任链中的话，则验证通过。\")]),t._v(\" \"),n(\"li\",[t._v(\"如果1、2验证都失败了，如果用户提供了\"),n(\"code\",[t._v(\"badCertificateCallback\")]),t._v(\"回调，则会调用它，如果回调返回\"),n(\"code\",[t._v(\"true\")]),t._v(\"，则允许继续链接，如果返回\"),n(\"code\",[t._v(\"false\")]),t._v(\"，则终止链接。\")])]),t._v(\" \"),n(\"p\",[t._v(\"综上所述，我们的证书校验其实就是提供一个\"),n(\"code\",[t._v(\"badCertificateCallback\")]),t._v(\"回调，下面通过一个示例来说明。\")]),t._v(\" \"),n(\"h5\",{attrs:{id:\"示例-2\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#示例-2\"}},[t._v(\"#\")]),t._v(\" 示例\")]),t._v(\" \"),n(\"p\",[t._v(\"假设我们的后台服务使用的是自签名证书，证书格式是PEM格式，我们将证书的内容保存在本地字符串中，那么我们的校验逻辑如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"String PEM\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"XXXXX\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//可以从文件读取\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\nhttpClient\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"badCertificateCallback\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"X509Certificate cert\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" String host\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int port\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"cert\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"pem\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\"PEM\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//证书一致，则允许发送数据\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"X509Certificate\")]),t._v(\"是证书的标准格式，包含了证书除私钥外所有信息，读者可以自行查阅文档。另外，上面的示例没有校验host，是因为只要服务器返回的证书内容和本地的保存一致就已经能证明是我们的服务器了（而不是中间人），host验证通常是为了防止证书和域名不匹配。\")]),t._v(\" \"),n(\"p\",[t._v(\"对于自签名的证书，我们也可以将其添加到本地证书信任链中，这样证书验证时就会自动通过，而不会再走到\"),n(\"code\",[t._v(\"badCertificateCallback\")]),t._v(\"回调中：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"SecurityContext sc\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"SecurityContext\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//file为证书路径\")]),t._v(\"\\nsc\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setTrustedCertificates\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"file\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//创建一个HttpClient\")]),t._v(\"\\nHttpClient httpClient \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"HttpClient\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" sc\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"注意，通过\"),n(\"code\",[t._v(\"setTrustedCertificates()\")]),t._v(\"设置的证书格式必须为PEM或PKCS12，如果证书格式为PKCS12，则需将证书密码传入，这样则会在代码中暴露证书密码，所以客户端证书校验不建议使用PKCS12格式的证书。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"总结\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),n(\"p\",[t._v(\"值得注意的是，\"),n(\"code\",[t._v(\"HttpClient\")]),t._v(\"提供的这些属性和方法最终都会作用在请求header里，我们完全可以通过手动去设置header来实现，之所以提供这些方法，只是为了方便开发者而已。另外，Http协议是一个非常重要的、使用最多的网络协议，每一个开发者都应该对http协议非常熟悉。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/87.e1f95a69.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[87],{378:function(t,s,a){t.exports=a.p+\"assets/img/11-4.b1cfbd03.png\"},744:function(t,s,a){\"use strict\";a.r(s);var n=a(45),r=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_11-7-json转dart-model类\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_11-7-json转dart-model类\"}},[t._v(\"#\")]),t._v(\" 11.7 Json转Dart Model类\")]),t._v(\" \"),n(\"p\",[t._v(\"在实战中，后台接口往往会返回一些结构化数据，如JSON、XML等，如之前我们请求Github API的示例，它返回的数据就是JSON格式的字符串，为了方便我们在代码中操作JSON，我们先将JSON格式的字符串转为Dart对象，这个可以通过\"),n(\"code\",[t._v(\"dart:convert\")]),t._v(\"中内置的JSON解码器json.decode() 来实现，该方法可以根据JSON字符串具体内容将其转为List或Map，这样我们就可以通过他们来查找所需的值，如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//一个JSON格式的用户列表字符串\")]),t._v(\"\\nString jsonStr\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\\'[{\"name\":\"Jack\"},{\"name\":\"Rose\"}]\\'')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//将JSON字符串转为Dart对象(此处是List)\")]),t._v(\"\\nList items\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"json\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"decode\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"jsonStr\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//输出第一个用户的姓名\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"items\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"name\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"通过json.decode() 将JSON字符串转为List/Map的方法比较简单，它没有外部依赖或其它的设置，对于小项目很方便。但当项目变大时，这种手动编写序列化逻辑可能变得难以管理且容易出错，例如有如下JSON:\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-json extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-json\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"name\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"John Smith\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"email\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"john@example.com\"')]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们可以通过调用\"),n(\"code\",[t._v(\"json.decode\")]),t._v(\"方法来解码JSON ，使用JSON字符串作为参数:\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Map\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" user \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" json\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"decode\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"json\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Howdy, ${user['\")]),t._v(\"name\"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"']}!'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'We sent the verification link to ${user['\")]),t._v(\"email\"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"']}.'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"由于\"),n(\"code\",[t._v(\"json.decode()\")]),t._v(\"仅返回一个\"),n(\"code\",[t._v(\"Map<String, dynamic>\")]),t._v(\"，这意味着直到运行时我们才知道值的类型。 通过这种方法，我们失去了大部分静态类型语言特性：类型安全、自动补全和最重要的编译时异常。这样一来，我们的代码可能会变得非常容易出错。例如，当我们访问\"),n(\"code\",[t._v(\"name\")]),t._v(\"或\"),n(\"code\",[t._v(\"email\")]),t._v(\"字段时，我们输入的很快，导致字段名打错了。但由于这个JSON在map结构中，所以编译器不知道这个错误的字段名，所以编译时不会报错。\")]),t._v(\" \"),n(\"p\",[t._v(\"其实，这个问题在很多平台上都会遇到，而也早就有了好的解决方法即“Json Model化”，具体做法就是，通过预定义一些与Json结构对应的Model类，然后在请求到数据后再动态根据数据创建出Model类的实例。这样一来，在开发阶段我们使用的是Model类的实例，而不再是Map/List，这样访问内部属性时就不会发生拼写错误。例如，我们可以通过引入一个简单的模型类(Model class)来解决前面提到的问题，我们称之为\"),n(\"code\",[t._v(\"User\")]),t._v(\"。在User类内部，我们有：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[t._v(\"一个\"),n(\"code\",[t._v(\"User.fromJson\")]),t._v(\" 构造函数, 用于从一个map构造出一个 \"),n(\"code\",[t._v(\"User\")]),t._v(\"实例 map structure\")]),t._v(\" \"),n(\"li\",[t._v(\"一个\"),n(\"code\",[t._v(\"toJson\")]),t._v(\" 方法, 将 \"),n(\"code\",[t._v(\"User\")]),t._v(\" 实例转化为一个map.\")])]),t._v(\" \"),n(\"p\",[t._v(\"这样，调用代码现在可以具有类型安全、自动补全字段（name和email）以及编译时异常。如果我们将拼写错误字段视为\"),n(\"code\",[t._v(\"int\")]),t._v(\"类型而不是\"),n(\"code\",[t._v(\"String\")]),t._v(\"， 那么我们的代码就不会通过编译，而不是在运行时崩溃。\")]),t._v(\" \"),n(\"p\",[n(\"strong\",[t._v(\"user.dart\")])]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"User\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String name\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String email\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"User\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"name\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"email\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  User\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fromJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Map\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" json\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" name \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" json\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'name'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        email \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" json\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'email'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  Map\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'name'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" name\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'email'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" email\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"现在，序列化逻辑移到了模型本身内部。采用这种新方法，我们可以非常容易地反序列化user.\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"Map userMap \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" json\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"decode\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"json\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" user \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"User\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"fromJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"userMap\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Howdy, ${user.name}!'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'We sent the verification link to ${user.email}.'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"要序列化一个user，我们只是将该\"),n(\"code\",[t._v(\"User\")]),t._v(\"对象传递给该\"),n(\"code\",[t._v(\"json.encode\")]),t._v(\"方法。我们不需要手动调用\"),n(\"code\",[t._v(\"toJson\")]),t._v(\"这个方法，因为`JSON.encode内部会自动调用。\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"String json \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" json\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"encode\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"user\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"这样，调用代码就不用担心JSON序列化了，但是，Model类还是必须的。在实践中，\"),n(\"code\",[t._v(\"User.fromJson\")]),t._v(\"和\"),n(\"code\",[t._v(\"User.toJson\")]),t._v(\"方法都需要单元测试到位，以验证正确的行为。\")]),t._v(\" \"),n(\"p\",[t._v(\"另外，实际场景中，JSON对象很少会这么简单，嵌套的JSON对象并不罕见，如果有什么能为我们自动处理JSON序列化，那将会非常好。幸运的是，有！\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"自动生成model\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#自动生成model\"}},[t._v(\"#\")]),t._v(\" 自动生成Model\")]),t._v(\" \"),n(\"p\",[t._v(\"尽管还有其他库可用，但在本书中，我们介绍一下官方推荐的\"),n(\"a\",{attrs:{href:\"https://pub.dartlang.org/packages/json_serializable\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"json_serializable package\"),n(\"OutboundLink\")],1),t._v(\"包。 它是一个自动化的源代码生成器，可以在开发阶段为我们生成JSON序列化模板，这样一来，由于序列化代码不再由我们手写和维护，我们将运行时产生JSON序列化异常的风险降至最低。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"在项目中设置json-serializable\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#在项目中设置json-serializable\"}},[t._v(\"#\")]),t._v(\" 在项目中设置json_serializable\")]),t._v(\" \"),n(\"p\",[t._v(\"要包含\"),n(\"code\",[t._v(\"json_serializable\")]),t._v(\"到我们的项目中，我们需要一个常规和两个\"),n(\"strong\",[t._v(\"开发依赖\")]),t._v(\"项。简而言之，\"),n(\"strong\",[t._v(\"开发依赖项\")]),t._v(\"是不包含在我们的应用程序源代码中的依赖项，它是开发过程中的一些辅助工具、脚本，和node中的开发依赖项相似。\")]),t._v(\" \"),n(\"p\",[n(\"strong\",[t._v(\"pubspec.yaml\")])]),t._v(\" \"),n(\"div\",{staticClass:\"language-yaml extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"dependencies\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"# Your other regular dependencies here\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"json_annotation\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ^2.0.0\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"dev_dependencies\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"# Your other dev_dependencies here\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"build_runner\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ^1.0.0\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"json_serializable\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ^2.0.0\\n\")])])]),n(\"p\",[t._v(\"在您的项目根文件夹中运行 \"),n(\"code\",[t._v(\"flutter packages get\")]),t._v(\" (或者在编辑器中点击 “Packages Get”) 以在项目中使用这些新的依赖项.\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"以json-serializable的方式创建model类\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#以json-serializable的方式创建model类\"}},[t._v(\"#\")]),t._v(\" 以json_serializable的方式创建model类\")]),t._v(\" \"),n(\"p\",[t._v(\"让我们看看如何将我们的\"),n(\"code\",[t._v(\"User\")]),t._v(\"类转换为一个\"),n(\"code\",[t._v(\"json_serializable\")]),t._v(\"。为了简单起见，我们使用前面示例中的简化JSON model。\")]),t._v(\" \"),n(\"p\",[n(\"strong\",[t._v(\"user.dart\")])]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:json_annotation/json_annotation.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// user.g.dart 将在我们运行生成命令后自动生成\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"part\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'user.g.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"///这个标注是告诉生成器，这个类是需要生成Model类的\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@JsonSerializable\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"User\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"User\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"name\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"email\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  String name\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  String email\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//不同的类使用不同的mixin即可\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"factory\")]),t._v(\" User\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fromJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Map\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" json\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _$\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"UserFromJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"json\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  Map\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _$\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"UserToJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"  \\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"有了上面的设置，源码生成器将生成用于序列化\"),n(\"code\",[t._v(\"name\")]),t._v(\"和\"),n(\"code\",[t._v(\"email\")]),t._v(\"字段的JSON代码。\")]),t._v(\" \"),n(\"p\",[t._v(\"如果需要，自定义命名策略也很容易。例如，如果我们正在使用的API返回带有_snake_case_的对象，但我们想在我们的模型中使用_lowerCamelCase_， 那么我们可以使用@JsonKey标注：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//显式关联JSON字段名与Model属性的对应关系 \")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@JsonKey\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"name\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'registration_date_millis'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" int registrationDateMillis\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"h3\",{attrs:{id:\"运行代码生成程序\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#运行代码生成程序\"}},[t._v(\"#\")]),t._v(\" 运行代码生成程序\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"json_serializable\")]),t._v(\"第一次创建类时，您会看到与图11-4类似的错误。\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(378),alt:\"ide_warning\"}})]),t._v(\" \"),n(\"p\",[t._v(\"这些错误是完全正常的，这是因为Model类的生成代码还不存在。为了解决这个问题，我们必须运行代码生成器来为我们生成序列化模板。有两种运行代码生成器的方法：\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"一次性生成\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#一次性生成\"}},[t._v(\"#\")]),t._v(\" 一次性生成\")]),t._v(\" \"),n(\"p\",[t._v(\"通过在我们的项目根目录下运行:\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-shell extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-shell\"}},[n(\"code\",[t._v(\"flutter packages pub run build_runner build\\n\")])])]),n(\"p\",[t._v(\"这触发了一次性构建，我们可以在需要时为我们的Model生成json序列化代码，它通过我们的源文件，找出需要生成Model类的源文件（包含@JsonSerializable标注的）来生成对应的.g.dart文件。一个好的建议是将所有Model类放在一个单独的目录下，然后在该目录下执行命令。\")]),t._v(\" \"),n(\"p\",[t._v(\"虽然这非常方便，但如果我们不需要每次在Model类中进行更改时都要手动运行构建命令的话会更好。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"持续生成\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#持续生成\"}},[t._v(\"#\")]),t._v(\" 持续生成\")]),t._v(\" \"),n(\"p\",[t._v(\"使用_watcher_可以使我们的源代码生成的过程更加方便。它会监视我们项目中文件的变化，并在需要时自动构建必要的文件，我们可以通过\"),n(\"code\",[t._v(\"flutter packages pub run build_runner watch\")]),t._v(\"在项目根目录下运行来启动_watcher_。只需启动一次观察器，然后它就会在后台运行，这是安全的。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"自动化生成模板\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#自动化生成模板\"}},[t._v(\"#\")]),t._v(\" 自动化生成模板\")]),t._v(\" \"),n(\"p\",[t._v(\"上面的方法有一个最大的问题就是要为每一个json写模板，这是比较枯燥的。如果有一个工具可以直接根据JSON文本生成模板，那我们就能彻底解放双手了。笔者自己用dart实现了一个脚本，它可以自动生成模板，并直接将JSON转为Model类，下面我们看看怎么做：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"p\",[t._v('定义一个\"模板的模板\"，名为\"template.dart\"：')]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:json_annotation/json_annotation.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"%\")]),t._v(\"t\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"part\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'%s.g.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@JsonSerializable\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"%\")]),t._v(\"s \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"%\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"s\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"%\")]),t._v(\"s\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"factory\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"%\")]),t._v(\"s\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fromJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Map\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" json\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _$\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"%\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"sFromJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"json\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    Map\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _$\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"%\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"sToJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"模板中的“%t”、“%s”为占位符，将在脚本运行时动态被替换为合适的导入头和类名。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"写一个自动生成模板的脚本(mo.dart)，它可以根据指定的JSON目录，遍历生成模板，在生成时我们定义一些规则：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[t._v(\"如果JSON文件名以下划线“_”开始，则忽略此JSON文件。\")]),t._v(\" \"),n(\"li\",[t._v(\"复杂的JSON对象往往会出现嵌套，我们可以通过一个特殊标志来手动指定嵌套的对象（后面举例）。\")])]),t._v(\" \"),n(\"p\",[t._v(\"脚本我们通过Dart来写，源码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'dart:convert'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'dart:io'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:path/path.dart'\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"as\")]),t._v(\" path\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" TAG\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"\\\\$\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" SRC\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"./json\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//JSON 目录\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" DIST\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"lib/models/\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//输出model目录\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"walk\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//遍历JSON目录生成模板\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" src \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Directory\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"SRC\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" list \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" src\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"listSync\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" template\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"File\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"./template.dart\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"readAsStringSync\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  File file\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  list\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"forEach\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"f\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"FileSystemEntity\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"isFileSync\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"f\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"path\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      file \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"File\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"f\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"path\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" paths\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"path\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"basename\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"f\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"path\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"split\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\".\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      String name\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"paths\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"first\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"paths\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"last\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toLowerCase\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"json\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"||\")]),t._v(\"name\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"startsWith\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"_\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"name\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"startsWith\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"_\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//下面生成模板\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" map \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" json\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"decode\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"file\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"readAsStringSync\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//为了避免重复导入相同的包，我们用Set来保存生成的import语句。\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"set\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Set\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      StringBuffer attrs\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StringBuffer\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"map \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"as\")]),t._v(\" Map\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"forEach\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" v\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"startsWith\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"_\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          attrs\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"write\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getType\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"v\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"set\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"name\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          attrs\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"write\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\" \"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          attrs\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"write\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          attrs\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"writeln\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\";\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          attrs\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"write\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"    \"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      String  className\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"name\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toUpperCase\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\"name\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"substring\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" dist\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"format\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"template\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"name\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"className\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"className\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"attrs\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                                className\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"className\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"className\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" _import\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"set\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"join\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\";\\\\r\\\\n\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      _import\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+=\")]),t._v(\"_import\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isEmpty\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\";\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      dist\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"dist\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"replaceFirst\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"%t\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"_import \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//将生成的模板输出\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"File\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$DIST$name.dart\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"writeAsStringSync\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"dist\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\nString \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"changeFirstChar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String str\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"bool upper\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"upper\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\"str\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toUpperCase\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"str\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toLowerCase\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\"str\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"substring\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//将JSON类型转为对应的dart类型\")]),t._v(\"\\n String \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getType\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"v\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"Set\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"set\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"String current\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  current\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"current\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toLowerCase\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"v \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"is\")]),t._v(\" bool\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"bool\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"v \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"is\")]),t._v(\" num\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"num\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"v \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"is\")]),t._v(\" Map\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Map<String,dynamic>\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"v \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"is\")]),t._v(\" List\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"List\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"v \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"is\")]),t._v(\" String\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//处理特殊标志\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"v\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"startsWith\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$TAG[]\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" className\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"changeFirstChar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"v\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"substring\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"className\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toLowerCase\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\"current\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"set\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'import \\\"$className.dart\\\"'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"List<${changeFirstChar(className)}>\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"v\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"startsWith\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"TAG\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" fileName\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"changeFirstChar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"v\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"substring\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"fileName\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toLowerCase\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\"current\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"set\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'import \\\"$fileName.dart\\\"'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"changeFirstChar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"fileName\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"String\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"String\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//替换模板占位符\")]),t._v(\"\\nString \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"format\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String fmt\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" List\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Object\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" params\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  int matchIndex \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  String \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"replace\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Match m\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"matchIndex \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\" params\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"length\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"switch\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"m\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"%s\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" params\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"matchIndex\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"++\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"throw\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Exception\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Missing parameter for string format\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"throw\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Exception\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Invalid format string: \"')]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" m\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" fmt\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"replaceAllMapped\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"%s\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" replace\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"main\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"walk\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"写一个shell(mo.sh)，将生成模板和生成model串起来：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-sh extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-sh\"}},[n(\"code\",[t._v(\"dart mo.dart\\nflutter packages pub run build_runner build --delete-conflicting-outputs\\n\")])])])])]),t._v(\" \"),n(\"p\",[t._v(\"至此，我们的脚本写好了，我们在根目录下新建一个json目录，然后把user.json移进去，然后在lib目录下创建一个models目录，用于保存最终生成的Model类。现在我们只需要一句命令即可生成Model类了:\")]),t._v(\" \"),n(\"div\",{staticClass:\"language- extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[n(\"code\",[t._v(\"./mo.sh  \\n\")])])]),n(\"p\",[t._v(\"运行后，一切都将自动执行，现在好多了，不是吗？\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"嵌套json\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#嵌套json\"}},[t._v(\"#\")]),t._v(\" 嵌套JSON\")]),t._v(\" \"),n(\"p\",[t._v(\"我们定义一个person.json内容修改为：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-json extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-json\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"name\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"John Smith\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"email\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"john@example.com\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"mother\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"name\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Alice\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"email\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"alice@example.com\"')]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"friends\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"name\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Jack\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"email\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Jack@example.com\"')]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"name\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Nancy\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"email\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Nancy@example.com\"')]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"每个Person都有\"),n(\"code\",[t._v(\"name\")]),t._v(\" 、\"),n(\"code\",[t._v(\"email\")]),t._v(\" 、 \"),n(\"code\",[t._v(\"mother\")]),t._v(\"和\"),n(\"code\",[t._v(\"friends\")]),t._v(\"四个字段，由于\"),n(\"code\",[t._v(\"mother\")]),t._v(\"也是一个Person，朋友是多个Person(数组)，所以我们期望生成的Model是下面这样：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:json_annotation/json_annotation.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"part\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'person.g.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@JsonSerializable\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Person\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Person\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \\n    String name\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    String email\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    Person mother\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    List\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Person\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" friends\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"factory\")]),t._v(\" Person\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fromJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Map\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" json\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _$\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"PersonFromJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"json\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    Map\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _$\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"PersonToJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\")])])]),n(\"p\",[t._v(\"这时，我们只需要简单修改一下JSON，添加一些特殊标志，重新运行mo.sh即可：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-json extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-json\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"name\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"John Smith\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"email\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"john@example.com\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"mother\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$person\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"friends\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$[]person\"')]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们使用美元符“$”作为特殊标志符(如果与内容冲突，可以修改mo.dart中的\"),n(\"code\",[t._v(\"TAG\")]),t._v(\"常量，自定义标志符)，脚本在遇到特殊标志符后会先把相应字段转为相应的对象或对象数组，对象数组需要在标志符后面添加数组符“[]”，符号后面接具体的类型名，此例中是person。其它类型同理，加入我们给User添加一个Person类型的 \"),n(\"code\",[t._v(\"boss\")]),t._v(\"字段：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-json extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-json\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"name\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"John Smith\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"email\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"john@example.com\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token property\"}},[t._v('\"boss\"')]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$person\"')]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"重新运行mo.sh，生成的user.dart如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:json_annotation/json_annotation.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"person.dart\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"part\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'user.g.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@JsonSerializable\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"User\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"User\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n    String name\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    String email\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    Person boss\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"factory\")]),t._v(\" User\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fromJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Map\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" json\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _$\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"UserFromJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"json\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    Map\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _$\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"UserToJson\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"可以看到，\"),n(\"code\",[t._v(\"boss\")]),t._v(\"字段已自动添加，并自动导入了“person.dart”。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"json-model-包\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#json-model-包\"}},[t._v(\"#\")]),t._v(\" Json_model 包\")]),t._v(\" \"),n(\"p\",[t._v(\"如果每个项目都要构建一个上面这样的脚本显然很麻烦，为此，我们将上面脚本和生成模板封装了一个包,已经发布到了Pub上，包名为\"),n(\"a\",{attrs:{href:\"https://github.com/flutterchina/json_model\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Json_model\"),n(\"OutboundLink\")],1),t._v(\"，开发者把该包加入开发依赖后，便可以用一条命令，根据Json文件生成Dart类。另外\"),n(\"a\",{attrs:{href:\"https://github.com/flutterchina/json_model\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Json_model\"),n(\"OutboundLink\")],1),t._v(\" 处于迭代中，功能会逐渐完善，所以建议读者直接使用该包（而不是手动复制上面的代码）。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"使用ide插件生成model\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#使用ide插件生成model\"}},[t._v(\"#\")]),t._v(\" 使用IDE插件生成model\")]),t._v(\" \"),n(\"p\",[t._v(\"目前Android Studio(或IntelliJ)有几个插件，可以将json文件转成Model类，但插件质量参差不齐，甚至还有一些沾染上了抄袭风波，故笔者在此不做优先推荐，读者有兴趣可以自行了解。但是，我们还是要了解一下IDE插件和\"),n(\"a\",{attrs:{href:\"https://link.juejin.im?target=https%3A%2F%2Fgithub.com%2Fflutterchina%2Fjson_model\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Json_model\"),n(\"OutboundLink\")],1),t._v(\"的优劣：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"a\",{attrs:{href:\"https://link.juejin.im?target=https%3A%2F%2Fgithub.com%2Fflutterchina%2Fjson_model\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Json_model\"),n(\"OutboundLink\")],1),t._v(\"需要单独维护一个存放Json文件的文件夹，如果有改动，只需修改Json文件便可重新生成Model类；而IDE插件一般需要用户手动将Json内容拷贝复制到一个输入框中，这样生成之后Json文件没有存档的化，之后要改动就需要手动。\")]),t._v(\" \"),n(\"li\",[n(\"a\",{attrs:{href:\"https://link.juejin.im?target=https%3A%2F%2Fgithub.com%2Fflutterchina%2Fjson_model\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Json_model\"),n(\"OutboundLink\")],1),t._v(\"可以手动指定某个字段引用的其它Model类，可以避免生成重复的类；而IDE插件一般会为每一个Json文件中所有嵌套对象都单独生成一个Model类，即使这些嵌套对象可能在其它Model类中已经生成过。\")]),t._v(\" \"),n(\"li\",[n(\"a\",{attrs:{href:\"https://link.juejin.im?target=https%3A%2F%2Fgithub.com%2Fflutterchina%2Fjson_model\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Json_model\"),n(\"OutboundLink\")],1),t._v(\" 提供了命令行转化方式，可以方便集成到CI等非UI环境的场景。\")])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"faq\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#faq\"}},[t._v(\"#\")]),t._v(\" FAQ\")]),t._v(\" \"),n(\"p\",[t._v(\"很多人可能会问Flutter中有没有像Java开发中的Gson/Jackson一样的Json序列化类库？答案是没有！因为这样的库需要使用运行时反射，这在Flutter中是禁用的。运行时反射会干扰Dart的_tree shaking_，使用_tree shaking_，可以在release版中“去除”未使用的代码，这可以显著优化应用程序的大小。由于反射会默认应用到所有代码，因此_tree shaking_会很难工作，因为在启用反射时很难知道哪些代码未被使用，因此冗余代码很难剥离，所以Flutter中禁用了Dart的反射功能，而正因如此也就无法实现动态转化Model的功能。\")])])}),[],!1,null,null,null);s.default=r.exports}}]);"
  },
  {
    "path": "docs/assets/js/88.9709422a.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[88],{374:function(t,s,n){t.exports=n.p+\"assets/img/11-2.f480cac6.png\"},737:function(t,s,n){\"use strict\";n.r(s);var a=n(45),e=Object(a.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_11-6-使用socket-api\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_11-6-使用socket-api\"}},[t._v(\"#\")]),t._v(\" 11.6 使用Socket API\")]),t._v(\" \"),a(\"p\",[t._v(\"我们之前介绍的Http协议和WebSocket协议都属于应用层协议，除了它们，应用层协议还有很多如：SMTP、FTP等，这些应用层协议的实现都是通过Socket API来实现的。其实，操作系统中提供的原生网络请求API是标准的，在C语言的Socket库中，它主要提供了端到端建立链接和发送数据的基础API，而高级编程语言中的Socket库其实都是对操作系统的socket API的一个封装。所以，如果我们需要自定义协议或者想直接来控制管理网络链接、又或者我们觉得自带的HttpClient不好用想重新实现一个，这时我们就需要使用Socket。Flutter的Socket API在dart：io包中，下面我们看一个使用Socket实现简单http请求的示例，以请求百度首页为例：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_request\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//建立连接\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" socket\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" Socket\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"connect\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"baidu.com\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//根据http协议，发送请求头\")]),t._v(\"\\n  socket\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"writeln\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"GET / HTTP/1.1\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  socket\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"writeln\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Host:baidu.com\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  socket\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"writeln\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Connection:close\"')]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  socket\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"writeln\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" socket\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"flush\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//发送\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//读取返回内容\")]),t._v(\"\\n  _response \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" socket\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"transform\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"utf8\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"decoder\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"join\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" socket\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"close\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"可以看到，使用Socket需要我们自己实现Http协议（需要自己实现和服务器的通信过程），本例只是一个简单示例，没有处理重定向、cookie等。本示例完整代码参考示例demo，运行后效果如图11-2所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(374),alt:\"图11-2\"}})]),t._v(\" \"),a(\"p\",[t._v(\"可以看到响应内容分两个部分，第一部分是响应头，第二部分是响应体，服务端可以根据请求信息动态来输出响应体。由于本示例请求头比较简单，所以响应体和浏览器中访问的会有差别，读者可以补充一些请求头(如user-agent)来看看输出的变化。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/89.4ff5c882.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[89],{379:function(t,a,e){t.exports=e.p+\"assets/img/12-3.2326714a.png\"},745:function(t,a,e){\"use strict\";e.r(a);var r=e(45),n=Object(r.a)({},(function(){var t=this,a=t.$createElement,r=t._self._c||a;return r(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[r(\"h1\",{attrs:{id:\"_12-2-插件开发-平台通道简介\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_12-2-插件开发-平台通道简介\"}},[t._v(\"#\")]),t._v(\" 12.2 插件开发：平台通道简介\")]),t._v(\" \"),r(\"p\",[t._v(\"“平台特定”或“特定平台”中的平台指的就是Flutter应用程序运行的平台，如Android或IOS。我们知道一个完整的Flutter应用程序实际上包括原生代码和Flutter代码两部分。由于Flutter本身只是一个UI系统，它本身是无法提供一些系统能力，比如使用蓝牙、相机、GPS等，因此要在Flutter APP中调用这些能力就必须和原生平台进行通信。为此，Flutter中提供了一个平台通道（platform channel），用于Flutter和原生平台的通信。平台通道正是Flutter和原生之间通信的桥梁，它也是Flutter插件的底层基础设施。\")]),t._v(\" \"),r(\"p\",[t._v(\"Flutter使用了一个灵活的系统，允许您调用特定平台的API，无论在Android上的Java或Kotlin代码中，还是iOS上的ObjectiveC或Swift代码中均可用。\")]),t._v(\" \"),r(\"p\",[t._v(\"Flutter与原生之间的通信依赖灵活的消息传递方式：\")]),t._v(\" \"),r(\"ul\",[r(\"li\",[t._v(\"应用的Flutter部分通过平台通道（platform channel）将消息发送到其应用程序的所在的宿主（iOS或Android）应用（原生应用）。\")]),t._v(\" \"),r(\"li\",[t._v(\"宿主监听平台通道，并接收该消息。然后它会调用该平台的API，并将响应发送回客户端，即应用程序的Flutter部分。\")])]),t._v(\" \"),r(\"h3\",{attrs:{id:\"平台通道\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#平台通道\"}},[t._v(\"#\")]),t._v(\" 平台通道\")]),t._v(\" \"),r(\"p\",[t._v(\"使用平台通道在Flutter(client)和原生(host)之间传递消息，如下图所示：\")]),t._v(\" \"),r(\"p\",[r(\"img\",{attrs:{src:e(379),alt:\"平台通道\"}})]),t._v(\" \"),r(\"p\",[t._v(\"当在Flutter中调用原生方法时，调用信息通过平台通道传递到原生，原生收到调用信息后方可执行指定的操作，如需返回数据，则原生会将数据再通过平台通道传递给Flutter。值得注意的是消息传递是异步的，这确保了用户界面在消息传递时不会被挂起。\")]),t._v(\" \"),r(\"p\",[t._v(\"在客户端，\"),r(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/services/MethodChannel-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"MethodChannel  API\"),r(\"OutboundLink\")],1),t._v(\" 可以发送与方法调用相对应的消息。 在宿主平台上，\"),r(\"code\",[t._v(\"MethodChannel\")]),t._v(\" 在\"),r(\"a\",{attrs:{href:\"https://docs.flutter.io/javadoc/io/flutter/plugin/common/MethodChannel.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Android API\"),r(\"OutboundLink\")],1),t._v(\" 和 \"),r(\"a\",{attrs:{href:\"https://docs.flutter.io/objcdoc/Classes/FlutterMethodChannel.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"FlutterMethodChannel iOS API\"),r(\"OutboundLink\")],1),t._v(\"可以接收方法调用并返回结果。这些类可以帮助我们用很少的代码就能开发平台插件。\")]),t._v(\" \"),r(\"blockquote\",[r(\"p\",[r(\"strong\",[t._v(\"注意\")]),t._v(\": 如果需要，方法调用(消息传递)可以是反向的，即宿主作为客户端调用Dart中实现的API。 \"),r(\"a\",{attrs:{href:\"https://pub.dartlang.org/packages/quick_actions\",target:\"_blank\",rel:\"noopener noreferrer\"}},[r(\"code\",[t._v(\"quick_actions\")]),r(\"OutboundLink\")],1),t._v(\"插件就是一个具体的例子。\")])]),t._v(\" \"),r(\"h3\",{attrs:{id:\"平台通道数据类型支持\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#平台通道数据类型支持\"}},[t._v(\"#\")]),t._v(\" 平台通道数据类型支持\")]),t._v(\" \"),r(\"p\",[t._v(\"平台通道使用标准消息编/解码器对消息进行编解码，它可以高效的对消息进行二进制序列化与反序列化。由于Dart与原生平台之间数据类型有所差异，下面我们列出数据类型之间的映射关系。\")]),t._v(\" \"),r(\"table\",[r(\"thead\",[r(\"tr\",[r(\"th\",[t._v(\"Dart\")]),t._v(\" \"),r(\"th\",[t._v(\"Android\")]),t._v(\" \"),r(\"th\",[t._v(\"iOS\")])])]),t._v(\" \"),r(\"tbody\",[r(\"tr\",[r(\"td\",[t._v(\"null\")]),t._v(\" \"),r(\"td\",[t._v(\"null\")]),t._v(\" \"),r(\"td\",[t._v(\"nil (NSNull when nested)\")])]),t._v(\" \"),r(\"tr\",[r(\"td\",[t._v(\"bool\")]),t._v(\" \"),r(\"td\",[t._v(\"java.lang.Boolean\")]),t._v(\" \"),r(\"td\",[t._v(\"NSNumber numberWithBool:\")])]),t._v(\" \"),r(\"tr\",[r(\"td\",[t._v(\"int\")]),t._v(\" \"),r(\"td\",[t._v(\"java.lang.Integer\")]),t._v(\" \"),r(\"td\",[t._v(\"NSNumber numberWithInt:\")])]),t._v(\" \"),r(\"tr\",[r(\"td\",[t._v(\"int, 如果不足32位\")]),t._v(\" \"),r(\"td\",[t._v(\"java.lang.Long\")]),t._v(\" \"),r(\"td\",[t._v(\"NSNumber numberWithLong:\")])]),t._v(\" \"),r(\"tr\",[r(\"td\",[t._v(\"int, 如果不足64位\")]),t._v(\" \"),r(\"td\",[t._v(\"java.math.BigInteger\")]),t._v(\" \"),r(\"td\",[t._v(\"FlutterStandardBigInteger\")])]),t._v(\" \"),r(\"tr\",[r(\"td\",[t._v(\"double\")]),t._v(\" \"),r(\"td\",[t._v(\"java.lang.Double\")]),t._v(\" \"),r(\"td\",[t._v(\"NSNumber numberWithDouble:\")])]),t._v(\" \"),r(\"tr\",[r(\"td\",[t._v(\"String\")]),t._v(\" \"),r(\"td\",[t._v(\"java.lang.String\")]),t._v(\" \"),r(\"td\",[t._v(\"NSString\")])]),t._v(\" \"),r(\"tr\",[r(\"td\",[t._v(\"Uint8List\")]),t._v(\" \"),r(\"td\",[t._v(\"byte[]\")]),t._v(\" \"),r(\"td\",[t._v(\"FlutterStandardTypedData typedDataWithBytes:\")])]),t._v(\" \"),r(\"tr\",[r(\"td\",[t._v(\"Int32List\")]),t._v(\" \"),r(\"td\",[t._v(\"int[]\")]),t._v(\" \"),r(\"td\",[t._v(\"FlutterStandardTypedData typedDataWithInt32:\")])]),t._v(\" \"),r(\"tr\",[r(\"td\",[t._v(\"Int64List\")]),t._v(\" \"),r(\"td\",[t._v(\"long[]\")]),t._v(\" \"),r(\"td\",[t._v(\"FlutterStandardTypedData typedDataWithInt64:\")])]),t._v(\" \"),r(\"tr\",[r(\"td\",[t._v(\"Float64List\")]),t._v(\" \"),r(\"td\",[t._v(\"double[]\")]),t._v(\" \"),r(\"td\",[t._v(\"FlutterStandardTypedData typedDataWithFloat64:\")])]),t._v(\" \"),r(\"tr\",[r(\"td\",[t._v(\"List\")]),t._v(\" \"),r(\"td\",[t._v(\"java.util.ArrayList\")]),t._v(\" \"),r(\"td\",[t._v(\"NSArray\")])]),t._v(\" \"),r(\"tr\",[r(\"td\",[t._v(\"Map\")]),t._v(\" \"),r(\"td\",[t._v(\"java.util.HashMap\")]),t._v(\" \"),r(\"td\",[t._v(\"NSDictionary\")])])])]),t._v(\" \"),r(\"p\",[t._v(\"当在发送和接收值时，这些值在消息中的序列化和反序列化会自动进行。\")]),t._v(\" \"),r(\"h3\",{attrs:{id:\"自定义编解码器\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#自定义编解码器\"}},[t._v(\"#\")]),t._v(\" 自定义编解码器\")]),t._v(\" \"),r(\"p\",[t._v(\"除了上面提到的\"),r(\"code\",[t._v(\"MethodChannel\")]),t._v(\"，还可以使用\"),r(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/services/BasicMessageChannel-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[r(\"code\",[t._v(\"BasicMessageChannel\")]),r(\"OutboundLink\")],1),t._v(\"，它支持使用自定义消息编解码器进行基本的异步消息传递。 此外，可以使用专门的\"),r(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/services/BinaryCodec-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[r(\"code\",[t._v(\"BinaryCodec\")]),r(\"OutboundLink\")],1),t._v(\"、\"),r(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/services/StringCodec-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[r(\"code\",[t._v(\"StringCodec\")]),r(\"OutboundLink\")],1),t._v(\"和 \"),r(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/services/JSONMessageCodec-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[r(\"code\",[t._v(\"JSONMessageCodec\")]),r(\"OutboundLink\")],1),t._v(\"类，或创建自己的编解码器。\")]),t._v(\" \"),r(\"h3\",{attrs:{id:\"如何获取平台信息\"}},[r(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#如何获取平台信息\"}},[t._v(\"#\")]),t._v(\" 如何获取平台信息\")]),t._v(\" \"),r(\"p\",[t._v(\"Flutter 中提供了一个全局变量\"),r(\"code\",[t._v(\"defaultTargetPlatform\")]),t._v(\"来获取当前应用的平台信息，\"),r(\"code\",[t._v(\"defaultTargetPlatform\")]),t._v('定义在\"platform.dart\"中，它的类型是'),r(\"code\",[t._v(\"TargetPlatform\")]),t._v(\"，这是一个枚举类，定义如下：\")]),t._v(\" \"),r(\"div\",{staticClass:\"language-dart extra-class\"},[r(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[r(\"code\",[r(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"enum\")]),t._v(\" TargetPlatform \"),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  android\"),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  fuchsia\"),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  iOS\"),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),r(\"p\",[t._v(\"可以看到目前Flutter只支持这三个平台。我们可以通过如下代码判断平台：\")]),t._v(\" \"),r(\"div\",{staticClass:\"language-dart extra-class\"},[r(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[r(\"code\",[r(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"defaultTargetPlatform\"),r(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\"TargetPlatform\"),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"android\"),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),r(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 是安卓系统，do something\")]),t._v(\"\\n  \"),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\")])])]),r(\"p\",[t._v(\"由于不同平台有它们各自的交互规范，Flutter Material库中的一些组件都针对相应的平台做了一些适配，比如路由组件\"),r(\"code\",[t._v(\"MaterialPageRoute\")]),t._v(\"，它在android和ios中会应用各自平台规范的切换动画。那如果我们想让我们的APP在所有平台都表现一致，比如希望在所有平台路由切换动画都按照ios平台一致的左右滑动切换风格该怎么做？Flutter中提供了一种覆盖默认平台的机制，我们可以通过显式指定\"),r(\"code\",[t._v(\"debugDefaultTargetPlatformOverride\")]),t._v(\"全局变量的值来指定应用平台。比如：\")]),t._v(\" \"),r(\"div\",{staticClass:\"language-dart extra-class\"},[r(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[r(\"code\",[t._v(\"debugDefaultTargetPlatformOverride\"),r(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"TargetPlatform\"),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"iOS\"),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),r(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"defaultTargetPlatform\"),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),r(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),r(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 会输出TargetPlatform.iOS\")]),t._v(\"\\n\")])])]),r(\"p\",[t._v(\"上面代码即在Android中运行后，Flutter APP就会认为是当前系统是iOS，Material组件库中所有组件交互方式都会和iOS平台对齐，\"),r(\"code\",[t._v(\"defaultTargetPlatform\")]),t._v(\"的值也会变为\"),r(\"code\",[t._v(\"TargetPlatform.iOS\")]),t._v(\"。\")])])}),[],!1,null,null,null);a.default=n.exports}}]);"
  },
  {
    "path": "docs/assets/js/9.f995bbe8.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[9],{484:function(t,s,a){t.exports=a.p+\"assets/img/6-2.1d35c6fc.png\"},485:function(t,s,a){t.exports=a.p+\"assets/img/6-3.e023acf1.png\"},486:function(t,s,a){t.exports=a.p+\"assets/img/6-4.288d3ff1.png\"},487:function(t,s,a){t.exports=a.p+\"assets/img/6-5.2947b7f7.png\"},488:function(t,s,a){t.exports=a.p+\"assets/img/6-6.e48bfae5.png\"},489:function(t,s,a){t.exports=a.p+\"assets/img/6-7.c443522f.png\"},490:function(t,s,a){t.exports=a.p+\"assets/img/6-8.e48bfae5.png\"},801:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_6-3-listview\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_6-3-listview\"}},[t._v(\"#\")]),t._v(\" 6.3 ListView\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"ListView\")]),t._v(\"是最常用的可滚动组件之一，它可以沿一个方向线性排布所有子组件，并且它也支持基于Sliver的延迟构建模型。我们看看ListView的默认构造函数定义：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"  \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//可滚动widget公共参数\")]),t._v(\"\\n  Axis scrollDirection \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Axis\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"vertical\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  bool reverse \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  ScrollController controller\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  bool primary\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  ScrollPhysics physics\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  EdgeInsetsGeometry padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//ListView各个构造函数的共同参数  \")]),t._v(\"\\n  double itemExtent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  bool shrinkWrap \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  bool addAutomaticKeepAlives \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  bool addRepaintBoundaries \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  double cacheExtent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//子widget列表\")]),t._v(\"\\n  List\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" children \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上面参数分为两组：第一组是可滚动组件的公共参数，本章第一节中已经介绍过，不再赘述；第二组是\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"各个构造函数（\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"有多个构造函数）的共同参数，我们重点来看看这些参数，：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"itemExtent\")]),t._v(\"：该参数如果不为\"),n(\"code\",[t._v(\"null\")]),t._v(\"，则会强制\"),n(\"code\",[t._v(\"children\")]),t._v(\"的“长度”为\"),n(\"code\",[t._v(\"itemExtent\")]),t._v(\"的值；这里的“长度”是指滚动方向上子组件的长度，也就是说如果滚动方向是垂直方向，则\"),n(\"code\",[t._v(\"itemExtent\")]),t._v(\"代表子组件的高度；如果滚动方向为水平方向，则\"),n(\"code\",[t._v(\"itemExtent\")]),t._v(\"就代表子组件的宽度。在\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"中，指定\"),n(\"code\",[t._v(\"itemExtent\")]),t._v(\"比让子组件自己决定自身长度会更高效，这是因为指定\"),n(\"code\",[t._v(\"itemExtent\")]),t._v(\"后，滚动系统可以提前知道列表的长度，而无需每次构建子组件时都去再计算一下，尤其是在滚动位置频繁变化时（滚动系统需要频繁去计算列表高度）。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"shrinkWrap\")]),t._v(\"：该属性表示是否根据子组件的总长度来设置\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"的长度，默认值为\"),n(\"code\",[t._v(\"false\")]),t._v(\" 。默认情况下，\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"的会在滚动方向尽可能多的占用空间。当\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"在一个无边界(滚动方向上)的容器中时，\"),n(\"code\",[t._v(\"shrinkWrap\")]),t._v(\"必须为\"),n(\"code\",[t._v(\"true\")]),t._v(\"。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"addAutomaticKeepAlives\")]),t._v(\"：该属性表示是否将列表项（子组件）包裹在\"),n(\"code\",[t._v(\"AutomaticKeepAlive\")]),t._v(\" 组件中；典型地，在一个懒加载列表中，如果将列表项包裹在\"),n(\"code\",[t._v(\"AutomaticKeepAlive\")]),t._v(\"中，在该列表项滑出视口时它也不会被GC（垃圾回收），它会使用\"),n(\"code\",[t._v(\"KeepAliveNotification\")]),t._v(\"来保存其状态。如果列表项自己维护其\"),n(\"code\",[t._v(\"KeepAlive\")]),t._v(\"状态，那么此参数必须置为\"),n(\"code\",[t._v(\"false\")]),t._v(\"。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"addRepaintBoundaries\")]),t._v(\"：该属性表示是否将列表项（子组件）包裹在\"),n(\"code\",[t._v(\"RepaintBoundary\")]),t._v(\"组件中。当可滚动组件滚动时，将列表项包裹在\"),n(\"code\",[t._v(\"RepaintBoundary\")]),t._v(\"中可以避免列表项重绘，但是当列表项重绘的开销非常小（如一个颜色块，或者一个较短的文本）时，不添加\"),n(\"code\",[t._v(\"RepaintBoundary\")]),t._v(\"反而会更高效。和\"),n(\"code\",[t._v(\"addAutomaticKeepAlive\")]),t._v(\"一样，如果列表项自己维护其\"),n(\"code\",[t._v(\"KeepAlive\")]),t._v(\"状态，那么此参数必须置为\"),n(\"code\",[t._v(\"false\")]),t._v(\"。\")])]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[t._v(\"注意：上面这些参数并非\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"特有，在本章后面介绍的其它可滚动组件也可能会拥有这些参数，它们的含义是相同的。\")])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"默认构造函数\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#默认构造函数\"}},[t._v(\"#\")]),t._v(\" 默认构造函数\")]),t._v(\" \"),n(\"p\",[t._v(\"默认构造函数有一个\"),n(\"code\",[t._v(\"children\")]),t._v(\"参数，它接受一个Widget列表（List\"),n(\"Widget\",[t._v(\"）。这种方式适合只有少量的子组件的情况，因为这种方式需要将所有\"),n(\"code\",[t._v(\"children\")]),t._v(\"都提前创建好（这需要做大量工作），而不是等到子widget真正显示的时候再创建，也就是说通过默认构造函数构建的ListView没有应用基于Sliver的懒加载模型。实际上通过此方式创建的\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"和使用\"),n(\"code\",[t._v(\"SingleChildScrollView\")]),t._v(\"+\"),n(\"code\",[t._v(\"Column\")]),t._v(\"的方式没有本质的区别。下面是一个例子：\")])],1),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListView\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  shrinkWrap\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n  padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'I\\\\'m dedicating every day to you'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Domestic life was never quite my style'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'When you smile, you knock me out, I fall apart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'And I thought I was so smart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"blockquote\",[n(\"p\",[t._v(\"再次强调，可滚动组件通过一个List\"),n(\"Widget\",[t._v(\"来作为其children属性时，只适用于子组件较少的情况，这是一个通用规律，并非\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"自己的特性，像\"),n(\"code\",[t._v(\"GridView\")]),t._v(\"也是如此。\")])],1)]),t._v(\" \"),n(\"h3\",{attrs:{id:\"listview-builder\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#listview-builder\"}},[t._v(\"#\")]),t._v(\" ListView.builder\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"ListView.builder\")]),t._v(\"适合列表项比较多（或者无限）的情况，因为只有当子组件真正显示的时候才会被创建，也就说通过该构造函数创建的\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"是支持基于Sliver的懒加载模型的。下面看一下\"),n(\"code\",[t._v(\"ListView.builder\")]),t._v(\"的核心参数列表：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"ListView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// ListView公共参数已省略  \")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" IndexedWidgetBuilder itemBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  int itemCount\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"itemBuilder\")]),t._v(\"：它是列表项的构建器，类型为\"),n(\"code\",[t._v(\"IndexedWidgetBuilder\")]),t._v(\"，返回值为一个widget。当列表滚动到具体的\"),n(\"code\",[t._v(\"index\")]),t._v(\"位置时，会调用该构建器构建列表项。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"itemCount\")]),t._v(\"：列表项的数量，如果为\"),n(\"code\",[t._v(\"null\")]),t._v(\"，则为无限列表。\")])]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[t._v(\"可滚动组件的构造函数如果需要一个列表项Builder，那么通过该构造函数构建的可滚动组件通常就是支持基于Sliver的懒加载模型的，反之则不支持，这是个一般规律。我们在后面在介绍可滚动组件的构造函数时将不再专门说明其是否支持基于Sliver的懒加载模型了。\")])]),t._v(\" \"),n(\"p\",[t._v(\"下面看一个例子：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"ListView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    itemCount\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    itemExtent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"50.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//强制高度为50.0\")]),t._v(\"\\n    itemBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$index\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图6-2所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(484),alt:\"图6-2\"}})]),t._v(\" \"),n(\"h3\",{attrs:{id:\"listview-separated\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#listview-separated\"}},[t._v(\"#\")]),t._v(\" ListView.separated\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"ListView.separated\")]),t._v(\"可以在生成的列表项之间添加一个分割组件，它比\"),n(\"code\",[t._v(\"ListView.builder\")]),t._v(\"多了一个\"),n(\"code\",[t._v(\"separatorBuilder\")]),t._v(\"参数，该参数是一个分割组件生成器。\")]),t._v(\" \"),n(\"p\",[t._v(\"下面我们看一个例子：奇数行添加一条蓝色下划线，偶数行添加一条绿色下划线。\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ListView3\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//下划线widget预定义以供复用。  \")]),t._v(\"\\n    Widget divider1\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Divider\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    Widget divider2\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Divider\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"green\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" ListView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"separated\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        itemCount\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//列表项构造器\")]),t._v(\"\\n        itemBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$index\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//分割器构造器\")]),t._v(\"\\n        separatorBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" index\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"%\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\"divider1\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"divider2\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"img\",{attrs:{src:a(485),alt:\"图6-3\"}})]),t._v(\" \"),n(\"h3\",{attrs:{id:\"实例-无限加载列表\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#实例-无限加载列表\"}},[t._v(\"#\")]),t._v(\" 实例：无限加载列表\")]),t._v(\" \"),n(\"p\",[t._v(\"假设我们要从数据源异步分批拉取一些数据，然后用\"),n(\"code\",[t._v(\"ListView\")]),t._v('展示，当我们滑动到列表末尾时，判断是否需要再去拉取数据，如果是，则去拉取，拉取过程中在表尾显示一个loading，拉取成功后将数据插入列表；如果不需要再去拉取，则在表尾提示\"没有更多\"。代码如下：')]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"InfiniteListView\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _InfiniteListViewState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_InfiniteListViewState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_InfiniteListViewState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"InfiniteListView\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" loadingTag \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"##loading##\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//表尾标记\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" _words \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"String\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"loadingTag\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_retrieveData\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" ListView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"separated\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      itemCount\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _words\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"length\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      itemBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//如果到了表尾\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_words\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" loadingTag\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//不足100条，继续获取数据\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_words\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"length \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//获取数据\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_retrieveData\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//加载时显示loading\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"24.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"24.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CircularProgressIndicator\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"strokeWidth\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//已经加载了100条数据，不再获取数据。\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Alignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                padding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"没有更多了\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//显示单词列表项\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_words\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      separatorBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Divider\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\".0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_retrieveData\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Future\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"delayed\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//重新构建列表\")]),t._v(\"\\n\\t\\t_words\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"insertAll\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_words\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"length \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//每次生成20个单词\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"generateWordPairs\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"take\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"map\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"asPascalCase\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toList\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \\t\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行后效果如图6-4、6-5所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(486),alt:\"图6-4\"}}),n(\"img\",{attrs:{src:a(487),alt:\"图6-5\"}})]),t._v(\" \"),n(\"p\",[t._v(\"代码比较简单，读者可以参照代码中的注释理解，故不再赘述。需要说明的是，\"),n(\"code\",[t._v(\"_retrieveData()\")]),t._v(\"的功能是模拟从数据源异步获取数据，我们使用english_words包的\"),n(\"code\",[t._v(\"generateWordPairs()\")]),t._v(\"方法每次生成20个单词。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"添加固定列表头\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#添加固定列表头\"}},[t._v(\"#\")]),t._v(\" 添加固定列表头\")]),t._v(\" \"),n(\"p\",[t._v(\"很多时候我们需要给列表添加一个固定表头，比如我们想实现一个商品列表，需要在列表顶部添加一个“商品列表”标题，期望的效果如图6-6所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(488),alt:\"图6-6\"}})]),t._v(\" \"),n(\"p\",[t._v(\"我们按照之前经验，写出如下代码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\nWidget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"商品列表\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    ListView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"itemBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$index\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"然后运行，发现并没有出现我们期望的效果，相反触发了一个异常；\")]),t._v(\" \"),n(\"div\",{staticClass:\"language- extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-text\"}},[n(\"code\",[t._v(\"Error caught by rendering library, thrown during performResize()。\\nVertical viewport was given unbounded height ...\\n\")])])]),n(\"p\",[t._v(\"从异常信息中我们可以看到是因为\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"高度边界无法确定引起，所以解决的办法也很明显，我们需要给\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"指定边界，我们通过\"),n(\"code\",[t._v(\"SizedBox\")]),t._v(\"指定一个列表高度看看是否生效：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"400\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//指定列表高度为400\")]),t._v(\"\\n    child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ListView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"itemBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$index\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图6-7所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(489),alt:\"图6-7\"}})]),t._v(\" \"),n(\"p\",[t._v(\"可以看到，现在没有触发异常并且列表已经显示出来了，但是我们的手机屏幕高度要大于400，所以底部会有一些空白。那如果我们要实现列表铺满除表头以外的屏幕空间应该怎么做？直观的方法是我们去动态计算，用屏幕高度减去状态栏、导航栏、表头的高度即为剩余屏幕高度，代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//Material设计规范中状态栏、导航栏、ListTile高度分别为24、56、56 \")]),t._v(\"\\n  height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MediaQuery\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"height\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"24\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"56\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"56\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ListView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"itemBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$index\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"    \\n\")])])]),n(\"p\",[t._v(\"运行效果如下图6-8所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(490),alt:\"图6-8\"}})]),t._v(\" \"),n(\"p\",[t._v(\"可以看到，我们期望的效果实现了，但是这种方法并不优雅，如果页面布局发生变化，比如表头布局调整导致表头高度改变，那么剩余空间的高度就得重新计算。那么有什么方法可以自动拉伸\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"以填充屏幕剩余空间的方法吗？当然有！答案就是\"),n(\"code\",[t._v(\"Flex\")]),t._v(\"。前面已经介绍过在弹性布局中，可以使用\"),n(\"code\",[t._v(\"Expanded\")]),t._v(\"自动拉伸组件大小，并且我们也说过\"),n(\"code\",[t._v(\"Column\")]),t._v(\"是继承自\"),n(\"code\",[t._v(\"Flex\")]),t._v(\"的，所以我们可以直接使用\"),n(\"code\",[t._v(\"Column\")]),t._v(\"+\"),n(\"code\",[t._v(\"Expanded\")]),t._v(\"来实现，代码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\nWidget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"商品列表\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Expanded\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" ListView\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"itemBuilder\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int index\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ListTile\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"$index\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行后，和上图一样，完美实现了！\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"总结\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),n(\"p\",[t._v(\"本节主要介绍了\"),n(\"code\",[t._v(\"ListView\")]),t._v(\"的一些公共参数以及常用的构造函数。不同的构造函数对应了不同的列表项生成模型，如果需要自定义列表项生成模型，可以通过\"),n(\"code\",[t._v(\"ListView.custom\")]),t._v(\"来自定义，它需要实现一个\"),n(\"code\",[t._v(\"SliverChildDelegate\")]),t._v(\"用来给ListView生成列表项组件，更多详情请参考API文档。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/90.fff47bcf.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[90],{382:function(t,a,s){t.exports=s.p+\"assets/img/13-1.fcda4f48.jpeg\"},751:function(t,a,s){\"use strict\";s.r(a);var e=s(45),n=Object(e.a)({},(function(){var t=this,a=t.$createElement,e=t._self._c||a;return e(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[e(\"h1\",{attrs:{id:\"_13-1-让app支持多语言\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_13-1-让app支持多语言\"}},[t._v(\"#\")]),t._v(\" 13.1 让App支持多语言\")]),t._v(\" \"),e(\"p\",[t._v(\"如果我们的应用要支持多种语言，那么我们需要“国际化”它。这意味着我们在开发时需要为应用程序支持的每种语言环境设置“本地化”的一些值，如文本和布局。Flutter SDK已经提供了一些组件和类来帮助我们实现国际化，下面我们来介绍一下Flutter中实现国际化的步骤。\")]),t._v(\" \"),e(\"p\",[t._v(\"接下来我们以\"),e(\"code\",[t._v(\"MaterialApp\")]),t._v(\"类为入口的应用来说明如何支持国际化。\")]),t._v(\" \"),e(\"blockquote\",[e(\"p\",[t._v(\"大多数应用程序都是通过\"),e(\"code\",[t._v(\"MaterialApp\")]),t._v(\"为入口，但根据低级别的\"),e(\"code\",[t._v(\"WidgetsApp\")]),t._v(\"类为入口编写的应用程序也可以使用相同的类和逻辑进行国际化。\"),e(\"code\",[t._v(\"MaterialApp\")]),t._v(\"实际上也是\"),e(\"code\",[t._v(\"WidgetsApp\")]),t._v(\"的一个包装。\")])]),t._v(\" \"),e(\"p\",[t._v(\"注意，”本地化的值和资源“是指我们针对不同语言准备的不同资源，这些资源一般是指文案（字符串），当然也会有一些其他的资源会根据不同语言地区而不同，比如我们需要显示一个APP上架地的国旗图片，那么不同Locale区域我们就需要提供不同的的国旗图片。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"支持国际化\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#支持国际化\"}},[t._v(\"#\")]),t._v(\" 支持国际化\")]),t._v(\" \"),e(\"p\",[t._v(\"默认情况下，Flutter SDK中的组件仅提供美国英语本地化资源（主要是文本）。要添加对其他语言的支持，应用程序须添加一个名为“flutter_localizations”的包依赖，然后还需要在\"),e(\"code\",[t._v(\"MaterialApp\")]),t._v(\"中进行一些配置。 要使用\"),e(\"code\",[t._v(\"flutter_localizations\")]),t._v(\"包，首先需要添加依赖到\"),e(\"code\",[t._v(\"pubspec.yaml\")]),t._v(\"文件中：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-yaml extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-yaml\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"dependencies\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"flutter\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"sdk\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" flutter\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"flutter_localizations\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token key atrule\"}},[t._v(\"sdk\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" flutter\\n\")])])]),e(\"p\",[t._v(\"接下来，下载\"),e(\"code\",[t._v(\"flutter_localizations\")]),t._v(\"库，然后指定\"),e(\"code\",[t._v(\"MaterialApp\")]),t._v(\"的\"),e(\"code\",[t._v(\"localizationsDelegates\")]),t._v(\"和\"),e(\"code\",[t._v(\"supportedLocales\")]),t._v(\"：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter_localizations/flutter_localizations.dart'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MaterialApp\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n localizationsDelegates\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n   \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 本地化的代理类\")]),t._v(\"\\n   GlobalMaterialLocalizations\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"delegate\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n   GlobalWidgetsLocalizations\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"delegate\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n supportedLocales\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Locale\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'en'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'US'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 美国英语\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Locale\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'zh'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'CN'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 中文简体\")]),t._v(\"\\n    \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//其它Locales\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// ...\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),e(\"blockquote\",[e(\"p\",[t._v(\"与\"),e(\"code\",[t._v(\"MaterialApp\")]),t._v(\"类为入口的应用不同, 对基于\"),e(\"code\",[t._v(\"WidgetsApp\")]),t._v(\"类为入口的应用程序进行国际化时,不需要\"),e(\"code\",[t._v(\"GlobalMaterialLocalizations.delegate\")]),t._v(\"。\")])]),t._v(\" \"),e(\"p\",[e(\"code\",[t._v(\"localizationsDelegates\")]),t._v(\"列表中的元素是生成本地化值集合的工厂。\"),e(\"code\",[t._v(\"GlobalMaterialLocalizations.delegate\")]),t._v(\" 为Material 组件库提供的本地化的字符串和其他值，它可以使Material 组件支持多语言。 \"),e(\"code\",[t._v(\"GlobalWidgetsLocalizations.delegate\")]),t._v(\"定义组件默认的文本方向，从左到右或从右到左，这是因为有些语言的阅读习惯并不是从左到右，比如如阿拉伯语就是从右向左的。\")]),t._v(\" \"),e(\"p\",[e(\"code\",[t._v(\"supportedLocales\")]),t._v(\"也接收一个Locale数组，表示我们的应用支持的语言列表，在本例中我们的应用只支持美国英语和中文简体两种语言。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"获取当前区域locale\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#获取当前区域locale\"}},[t._v(\"#\")]),t._v(\" 获取当前区域Locale\")]),t._v(\" \"),e(\"p\",[e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/dart-ui/Locale-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"Locale\")]),e(\"OutboundLink\")],1),t._v(\"类是用来标识用户的语言环境的，它包括语言和国家两个标志如：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Locale\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'zh'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'CN'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 中文简体\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"我们始终可以通过以下方式来获取应用的当前区域Locale：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[t._v(\"Locale myLocale \"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Localizations\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"localeOf\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),e(\"p\",[e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/Localizations-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"Localizations\")]),e(\"OutboundLink\")],1),t._v(\" 组件一般位于widget树中其它业务组件的顶部，它的作用是定义区域Locale以及设置子树依赖的本地化资源。 如果系统的语言环境发生变化，\"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/WidgetsApp-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"WidgetsApp\"),e(\"OutboundLink\")],1),t._v(\"将创建一个新的Localizations 组件并重建它，这样子树中通过\"),e(\"code\",[t._v(\"Localizations.localeOf(context)\")]),t._v(\" 获取的Locale就会更新。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"监听系统语言切换\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#监听系统语言切换\"}},[t._v(\"#\")]),t._v(\" 监听系统语言切换\")]),t._v(\" \"),e(\"p\",[t._v(\"当我们更改系统语言设置时，APP中的Localizations组件会重新构建，\"),e(\"code\",[t._v(\"Localizations.localeOf(context)\")]),t._v(\" 获取的Locale就会更新，最终界面会重新build达到切换语言的效果。但是这个过程是隐式完成的，我们并没有主动去监听系统语言切换，但是有时我们需要在系统语言发生改变时做一些事，比如系统语言切换为一种我们APP不支持的语言时，我们需要设置一个默认的语言，这时我们就需要监听locale改变事件。\")]),t._v(\" \"),e(\"p\",[t._v(\"我们可以通过\"),e(\"code\",[t._v(\"localeResolutionCallback\")]),t._v(\"或\"),e(\"code\",[t._v(\"localeListResolutionCallback\")]),t._v(\"回调来监听locale改变的事件，我们先看看\"),e(\"code\",[t._v(\"localeResolutionCallback\")]),t._v(\"的回调函数签名：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[t._v(\"Locale \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"Function\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Locale locale\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Iterable\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Locale\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" supportedLocales\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),e(\"ul\",[e(\"li\",[e(\"p\",[t._v(\"参数\"),e(\"code\",[t._v(\"locale\")]),t._v(\"的值为当前的当前的系统语言设置，当应用启动时或用户动态改变系统语言设置时此locale即为系统的当前locale。当开发者手动指定APP的locale时，那么此locale参数代表开发者指定的locale，此时将忽略系统locale如：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MaterialApp\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n locale\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Locale\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'en'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'US'\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//手动指定locale\")]),t._v(\"\\n \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"上面的例子中手动指定了应用locale为美国英语，指定后即使设备当前语言是中文简体，应用中的locale也依然是美国英语。如果\"),e(\"code\",[t._v(\"locale\")]),t._v(\"为\"),e(\"code\",[t._v(\"null\")]),t._v(\"，则表示Flutter未能获取到设备的Locale信息，所以我们在使用\"),e(\"code\",[t._v(\"locale\")]),t._v(\"之前一定要先判空。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[e(\"code\",[t._v(\"supportedLocales\")]),t._v(\" 为当前应用支持的locale列表，是开发者在MaterialApp中通过\"),e(\"code\",[t._v(\"supportedLocales\")]),t._v(\"属性注册的。\")])]),t._v(\" \"),e(\"li\",[e(\"p\",[t._v(\"返回值是一个\"),e(\"code\",[t._v(\"Locale\")]),t._v(\"，此\"),e(\"code\",[t._v(\"Locale\")]),t._v(\"为Flutter APP最终使用的\"),e(\"code\",[t._v(\"Locale\")]),t._v(\"。通常在不支持的语言区域时返回一个默认的\"),e(\"code\",[t._v(\"Locale\")]),t._v(\"。\")])])]),t._v(\" \"),e(\"p\",[e(\"code\",[t._v(\"localeListResolutionCallback\")]),t._v(\"和\"),e(\"code\",[t._v(\"localeResolutionCallback\")]),t._v(\"唯一的不同就在第一个参数类型，前者接收的是一个\"),e(\"code\",[t._v(\"Locale\")]),t._v(\"列表，而后者接收的是单个\"),e(\"code\",[t._v(\"Locale\")]),t._v(\"。\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[t._v(\"Locale \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"Function\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"List\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Locale\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" locales\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Iterable\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Locale\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" supportedLocales\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"在较新的Android系统中，用户可以设置一个语言列表，这样一来，支持多语言的应用就会得到这个列表，应用通常的处理方式就是按照列表的顺序依次尝试加载相应的Locale，如果某一种语言加载成功则会停止。图13-1是Android系统中设置语言列表的截图：\")]),t._v(\" \"),e(\"p\",[e(\"img\",{attrs:{src:s(382),alt:\"设置语言列表\"}})]),t._v(\" \"),e(\"p\",[t._v(\"在Flutter中，应该优先使用\"),e(\"code\",[t._v(\"localeListResolutionCallback\")]),t._v(\"，当然你不必担心Android系统的差异性，如果在低版本的Android系统中，Flutter会自动处理这种情况，这时Locale列表只会包含一项。\")]),t._v(\" \"),e(\"h3\",{attrs:{id:\"localization-组件\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#localization-组件\"}},[t._v(\"#\")]),t._v(\" Localization 组件\")]),t._v(\" \"),e(\"p\",[t._v(\"Localizations组件用于加载和查找应用当前语言下的本地化值或资源。应用程序通过\"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/Localizations/of.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[e(\"code\",[t._v(\"Localizations.of(context,type)\")]),e(\"OutboundLink\")],1),t._v(\"来引用这些对象。 如果设备的Locale区域设置发生更改，则Localizations 组件会自动加载新区域的Locale值，然后重新build使用（依赖）了它们的组件，之所以会这样，是因为\"),e(\"code\",[t._v(\"Localizations\")]),t._v(\"内部使用了\"),e(\"a\",{attrs:{href:\"https://book.flutterchina.club/chapter7/inherited_widget.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"InheritedWidget\"),e(\"OutboundLink\")],1),t._v(\" ，我们在介绍该组件时讲过：当子组件的\"),e(\"code\",[t._v(\"build\")]),t._v(\"函数引用了\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"时，会创建对\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"的隐式依赖关系。因此，当\"),e(\"code\",[t._v(\"InheritedWidget\")]),t._v(\"发生更改时，即\"),e(\"code\",[t._v(\"Localizations\")]),t._v(\"的Locale设置发生更改时，将重建所有依赖它的子组件。\")]),t._v(\" \"),e(\"p\",[t._v(\"本地化值由\"),e(\"code\",[t._v(\"Localizations\")]),t._v(\"的 \"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/widgets/LocalizationsDelegate-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"LocalizationsDelegates\"),e(\"OutboundLink\")],1),t._v(\" 列表加载 。 \"),e(\"strong\",[t._v(\"每个委托必须定义一个异步load() 方法\")]),t._v(\"，以生成封装了一系列本地化值的对象。通常这些对象为每个本地化值定义一个方法。\")]),t._v(\" \"),e(\"p\",[t._v(\"在大型应用程序中，不同模块或Package可能会与自己的本地化值捆绑在一起。 这就是为什么要用\"),e(\"code\",[t._v(\"Localizations\")]),t._v(\" 管理对象表的原因。 要使用由\"),e(\"code\",[t._v(\"LocalizationsDelegate\")]),t._v(\"的\"),e(\"code\",[t._v(\"load\")]),t._v(\"方法之一产生的对象，可以指定一个\"),e(\"code\",[t._v(\"BuildContext\")]),t._v(\"和对象的类型来找到它。例如，Material 组件库的本地化字符串由\"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/material/MaterialLocalizations-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"MaterialLocalizations\"),e(\"OutboundLink\")],1),t._v(\"类定义，此类的实例由\"),e(\"a\",{attrs:{href:\"https://docs.flutter.io/flutter/material/MaterialApp-class.html\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"MaterialApp\"),e(\"OutboundLink\")],1),t._v(\"类提供的\"),e(\"code\",[t._v(\"LocalizationDelegate\")]),t._v(\"创建， 它们可以如下方式获取到：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[t._v(\"Localizations\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"of\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"MaterialLocalizations\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" MaterialLocalizations\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),e(\"p\",[t._v(\"这个特殊的\"),e(\"code\",[t._v(\"Localizations.of()\")]),t._v(\"表达式会经常使用，所以MaterialLocalizations类提供了一个便捷方法：\")]),t._v(\" \"),e(\"div\",{staticClass:\"language-dart extra-class\"},[e(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[e(\"code\",[e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" MaterialLocalizations \"),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),e(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" Localizations\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"of\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"MaterialLocalizations\"),e(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" MaterialLocalizations\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),e(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 可以直接调用便捷方法\")]),t._v(\"\\ntooltip\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MaterialLocalizations\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),e(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"backButtonTooltip\"),e(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),e(\"h3\",{attrs:{id:\"使用打包好的localizationsdelegates\"}},[e(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#使用打包好的localizationsdelegates\"}},[t._v(\"#\")]),t._v(\" 使用打包好的LocalizationsDelegates\")]),t._v(\" \"),e(\"p\",[t._v(\"为了尽可能小而且简单，flutter软件包中仅提供美国英语值的\"),e(\"code\",[t._v(\"MaterialLocalizations\")]),t._v(\"和\"),e(\"code\",[t._v(\"WidgetsLocalizations\")]),t._v(\"接口的实现。 这些实现类分别称为\"),e(\"code\",[t._v(\"DefaultMaterialLocalizations\")]),t._v(\"和\"),e(\"code\",[t._v(\"DefaultWidgetsLocalizations\")]),t._v(\"。flutter_localizations 包包含\"),e(\"code\",[t._v(\"GlobalMaterialLocalizations\")]),t._v(\"和\"),e(\"code\",[t._v(\"GlobalWidgetsLocalizations\")]),t._v(\"的本地化接口的多语言实现， 国际化的应用程序必须按照本节开头说明的那样为这些类指定本地化Delegate。\")]),t._v(\" \"),e(\"p\",[t._v(\"上述的\"),e(\"code\",[t._v(\"GlobalMaterialLocalizations\")]),t._v(\"和\"),e(\"code\",[t._v(\"GlobalWidgetsLocalizations\")]),t._v(\"只是Material组件库的本地化实现，如果我们要让自己的布局支持多语言，那么就需要实现在即的\"),e(\"code\",[t._v(\"Localizations\")]),t._v(\"，我们将在下一节介绍其具体的实现方式。\")])])}),[],!1,null,null,null);a.default=n.exports}}]);"
  },
  {
    "path": "docs/assets/js/91.9eb501f7.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[91],{386:function(t,a,s){t.exports=s.p+\"assets/img/14-4.4a6d698c.png\"},755:function(t,a,s){\"use strict\";s.r(a);var n=s(45),e=Object(n.a)({},(function(){var t=this,a=t.$createElement,n=t._self._c||a;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_14-5-图片加载原理与缓存\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_14-5-图片加载原理与缓存\"}},[t._v(\"#\")]),t._v(\" 14.5 图片加载原理与缓存\")]),t._v(\" \"),n(\"p\",[t._v(\"在本书前面章节已经介绍过\"),n(\"code\",[t._v(\"Image\")]),t._v(\" 组件，并提到Flutter框架对加载过的图片是有缓存的（内存），默认最大缓存数量是1000，最大缓存空间为100M。本节便详细介绍Image的原理及图片缓存机制，下面我们先看看\"),n(\"code\",[t._v(\"ImageProvider\")]),t._v(\" 类。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_14-5-1-imageprovider\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_14-5-1-imageprovider\"}},[t._v(\"#\")]),t._v(\" 14.5.1 ImageProvider\")]),t._v(\" \"),n(\"p\",[t._v(\"我们已经知道\"),n(\"code\",[t._v(\"Image\")]),t._v(\" 组件的\"),n(\"code\",[t._v(\"image\")]),t._v(\" 参数是一个必选参数，它是\"),n(\"code\",[t._v(\"ImageProvider\")]),t._v(\"类型。下面我们便详细介绍一下\"),n(\"code\",[t._v(\"ImageProvider\")]),t._v(\"，\"),n(\"code\",[t._v(\"ImageProvider\")]),t._v(\"是一个抽象类，定义了图片数据获取和加载的相关接口。它的主要职责有两个：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"提供图片数据源\")]),t._v(\" \"),n(\"li\",[t._v(\"缓存图片\")])]),t._v(\" \"),n(\"p\",[t._v(\"我们看看\"),n(\"code\",[t._v(\"ImageProvider\")]),t._v(\"抽象类的详细定义：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"abstract\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ImageProvider\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\n  ImageStream \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"resolve\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ImageConfiguration configuration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 实现代码省略\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"bool\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"evict\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" ImageCache cache\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      ImageConfiguration configuration \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" ImageConfiguration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"empty \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 实现代码省略\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"obtainKey\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ImageConfiguration configuration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@protected\")]),t._v(\"\\n  ImageStreamCompleter \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"load\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"T key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 需子类实现\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"h4\",{attrs:{id:\"load-t-key-方法\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#load-t-key-方法\"}},[t._v(\"#\")]),t._v(\" \"),n(\"code\",[t._v(\"load(T key)\")]),t._v(\"方法\")]),t._v(\" \"),n(\"p\",[t._v(\"加载图片数据源的接口，不同的数据源的加载方法不同，每个\"),n(\"code\",[t._v(\"ImageProvider\")]),t._v(\"的子类必须实现它。比如\"),n(\"code\",[t._v(\"NetworkImage\")]),t._v(\"类和\"),n(\"code\",[t._v(\"AssetImage\")]),t._v(\"类，它们都是\"),n(\"code\",[t._v(\"ImageProvider\")]),t._v(\"的子类，但它们需要从不同的数据源来加载图片数据：\"),n(\"code\",[t._v(\"NetworkImage\")]),t._v(\"是从网络来加载图片数据，而\"),n(\"code\",[t._v(\"AssetImage\")]),t._v(\"则是从最终的应用包里来加载（加载打到应用安装包里的资源图片）。 我们以\"),n(\"code\",[t._v(\"NetworkImage\")]),t._v(\"为例，看看其load方法的实现：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\nImageStreamCompleter \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"load\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"image_provider\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"NetworkImage key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" StreamController\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ImageChunkEvent\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" chunkEvents \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" StreamController\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ImageChunkEvent\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MultiFrameImageStreamCompleter\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    codec\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_loadAsync\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" chunkEvents\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//调用\")]),t._v(\"\\n    chunkEvents\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" chunkEvents\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"stream\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    scale\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"scale\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们看到，\"),n(\"code\",[t._v(\"load\")]),t._v(\"方法的返回值类型是\"),n(\"code\",[t._v(\"ImageStreamCompleter\")]),t._v(\" ，它是一个抽象类，定义了管理图片加载过程的一些接口，\"),n(\"code\",[t._v(\"Image\")]),t._v(\" Widget中正是通过它来监听图片加载状态的（我们将在下面介绍\"),n(\"code\",[t._v(\"Image\")]),t._v(\" 原理时详细介绍）。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"MultiFrameImageStreamCompleter\")]),t._v(\" 是 \"),n(\"code\",[t._v(\"ImageStreamCompleter\")]),t._v(\"的一个子类，是flutter sdk预置的类，通过该类，我们以方便、轻松地创建出一个\"),n(\"code\",[t._v(\"ImageStreamCompleter\")]),t._v(\"实例来做为\"),n(\"code\",[t._v(\"load\")]),t._v(\"方法的返回值。\")]),t._v(\" \"),n(\"p\",[t._v(\"我们可以看到，\"),n(\"code\",[t._v(\"MultiFrameImageStreamCompleter\")]),t._v(\" 需要一个\"),n(\"code\",[t._v(\"codec\")]),t._v(\"参数，该参数类型为\"),n(\"code\",[t._v(\"Future<ui.Codec>\")]),t._v(\"。\"),n(\"code\",[t._v(\"Codec\")]),t._v(\" 是处理图片编解码的类的一个handler，实际上，它只是一个flutter engine API 的包装类，也就是说图片的编解码逻辑不是在Dart 代码部分实现，而是在flutter engine中实现的。\"),n(\"code\",[t._v(\"Codec\")]),t._v(\"类部分定义如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@pragma\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'vm:entry-point'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Codec\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"NativeFieldWrapperClass2\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 此类由flutter engine创建，不应该手动实例化此类或直接继承此类。\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@pragma\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'vm:entry-point'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n  Codec\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// 图片中的帧数(动态图会有多帧)\")]),t._v(\"\\n  int \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" frameCount native \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Codec_frameCount'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// 动画重复的次数\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// * 0 表示只执行一次\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// * -1 表示循环执行\")]),t._v(\"\\n  int \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" repetitionCount native \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Codec_repetitionCount'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// 获取下一个动画帧\")]),t._v(\"\\n  Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"FrameInfo\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getNextFrame\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_futurize\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_getNextFrame\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  String \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_getNextFrame\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_Callback\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"FrameInfo\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" callback\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" native \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Codec_getNextFrame'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们可以看到\"),n(\"code\",[t._v(\"Codec\")]),t._v(\"最终的结果是一个或多个（动图）帧，而这些帧最终会绘制到屏幕上。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"MultiFrameImageStreamCompleter 的\")]),t._v(\" \"),n(\"code\",[t._v(\"codec\")]),t._v(\"参数值为\"),n(\"code\",[t._v(\"_loadAsync\")]),t._v(\"方法的返回值，我们继续看\"),n(\"code\",[t._v(\"_loadAsync\")]),t._v(\"方法的实现：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"\\n Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ui\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"Codec\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_loadAsync\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    NetworkImage key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    StreamController\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ImageChunkEvent\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" chunkEvents\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//下载图片\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Uri resolved \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Uri\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"base\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"resolve\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"url\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" HttpClientRequest request \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" _httpClient\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"getUrl\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"resolved\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      headers\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"forEach\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String name\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" String value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        request\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"headers\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"name\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" HttpClientResponse response \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" request\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"close\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"response\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"statusCode \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" HttpStatus\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"ok\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"throw\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Exception\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 接收图片数据 \")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Uint8List bytes \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"consolidateHttpClientResponseBytes\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        response\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        onBytesReceived\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int cumulative\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int total\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          chunkEvents\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ImageChunkEvent\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            cumulativeBytesLoaded\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" cumulative\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            expectedTotalBytes\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" total\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"bytes\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"lengthInBytes \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"throw\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Exception\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'NetworkImage is an empty file: $resolved'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 对图片数据进行解码\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" PaintingBinding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"instance\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"instantiateImageCodec\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"bytes\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"finally\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      chunkEvents\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"close\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"可以看到\"),n(\"code\",[t._v(\"_loadAsync\")]),t._v(\"方法主要做了两件事：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"下载图片。\")]),t._v(\" \"),n(\"li\",[t._v(\"对下载的图片数据进行解码。\")])]),t._v(\" \"),n(\"p\",[t._v(\"下载逻辑比较简单：通过\"),n(\"code\",[t._v(\"HttpClient\")]),t._v(\"从网上下载图片，另外下载请求会设置一些自定义的header，开发者可以通过\"),n(\"code\",[t._v(\"NetworkImage\")]),t._v(\"的\"),n(\"code\",[t._v(\"headers\")]),t._v(\"命名参数来传递。\")]),t._v(\" \"),n(\"p\",[t._v(\"在图片下载完成后调用了\"),n(\"code\",[t._v(\"PaintingBinding.instance.instantiateImageCodec(bytes)\")]),t._v(\"对图片进行解码，值得注意的是\"),n(\"code\",[t._v(\"instantiateImageCodec(...)\")]),t._v(\"也是一个Native API的包装，实际上会调用Flutter engine的\"),n(\"code\",[t._v(\"instantiateImageCodec\")]),t._v(\"方法，源码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"String \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_instantiateImageCodec\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Uint8List list\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" _Callback\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Codec\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" callback\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" _ImageInfo imageInfo\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int targetWidth\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" int targetHeight\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n  native \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'instantiateImageCodec'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"h4\",{attrs:{id:\"obtainkey-imageconfiguration-方法\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#obtainkey-imageconfiguration-方法\"}},[t._v(\"#\")]),t._v(\" \"),n(\"code\",[t._v(\"obtainKey(ImageConfiguration)\")]),t._v(\"方法\")]),t._v(\" \"),n(\"p\",[t._v(\"该接口主要是为了配合实现图片缓存，\"),n(\"code\",[t._v(\"ImageProvider\")]),t._v(\"从数据源加载完数据后，会在全局的\"),n(\"code\",[t._v(\"ImageCache\")]),t._v(\"中缓存图片数据，而图片数据缓存是一个Map，而Map的key便是调用此方法的返回值，不同的key代表不同的图片数据缓存。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"resolve-imageconfiguration-方法\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#resolve-imageconfiguration-方法\"}},[t._v(\"#\")]),t._v(\" \"),n(\"code\",[t._v(\"resolve(ImageConfiguration)\")]),t._v(\" 方法\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"resolve\")]),t._v(\"方法是\"),n(\"code\",[t._v(\"ImageProvider\")]),t._v(\"的暴露的给\"),n(\"code\",[t._v(\"Image\")]),t._v(\"的主入口方法，它接受一个\"),n(\"code\",[t._v(\"ImageConfiguration\")]),t._v(\"参数，返回\"),n(\"code\",[t._v(\"ImageStream\")]),t._v(\"，即图片数据流。我们重点看一下\"),n(\"code\",[t._v(\"resolve\")]),t._v(\"执行流程：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"ImageStream \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"resolve\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ImageConfiguration configuration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" ImageStream stream \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ImageStream\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  T obtainedKey\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//定义错误处理函数\")]),t._v(\"\\n  Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"handleError\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),t._v(\" exception\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" StackTrace stack\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n    stream\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setCompleter\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"imageCompleter\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    imageCompleter\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setError\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 创建一个新Zone，主要是为了当发生错误时不会干扰MainZone\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Zone dangerZone \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Zone\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"current\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fork\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \\n  dangerZone\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"runGuarded\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Future\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"T\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 先验证是否已经有缓存\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 生成缓存key，后面会根据此key来检测是否有缓存\")]),t._v(\"\\n      key \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"obtainKey\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"configuration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"catch\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"error\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" stackTrace\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"handleError\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"error\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" stackTrace\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"then\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"T key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      obtainedKey \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 缓存的处理逻辑在这里，记为A，下面详细介绍\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" ImageStreamCompleter completer \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" PaintingBinding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"instance\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"imageCache\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"putIfAbsent\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"load\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" onError\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" handleError\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"completer \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        stream\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setCompleter\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"completer\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"catchError\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"handleError\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" stream\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"ImageConfiguration\")]),t._v(\"  包含图片和设备的相关信息，如图片的大小、所在的\"),n(\"code\",[t._v(\"AssetBundle\")]),t._v(\"(只有打到安装包的图片存在)以及当前的设备平台、devicePixelRatio（设备像素比等）。Flutter SDK提供了一个便捷函数\"),n(\"code\",[t._v(\"createLocalImageConfiguration\")]),t._v(\"来创建\"),n(\"code\",[t._v(\"ImageConfiguration\")]),t._v(\"  对象：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"ImageConfiguration \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createLocalImageConfiguration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" Size size \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ImageConfiguration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    bundle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" DefaultAssetBundle\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    devicePixelRatio\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MediaQuery\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" nullOk\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"devicePixelRatio \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    locale\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Localizations\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"localeOf\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" nullOk\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    textDirection\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Directionality\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    platform\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" defaultTargetPlatform\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们可以发现这些信息基本都是通过\"),n(\"code\",[t._v(\"Context\")]),t._v(\"来获取。\")]),t._v(\" \"),n(\"p\",[t._v(\"上面代码A处就是处理缓存的主要代码，这里的\"),n(\"code\",[t._v(\"PaintingBinding.instance.imageCache\")]),t._v(\" 是 \"),n(\"code\",[t._v(\"ImageCache\")]),t._v(\"的一个实例，它是\"),n(\"code\",[t._v(\"PaintingBinding\")]),t._v(\"的一个属性，而Flutter框架中的\"),n(\"code\",[t._v(\"PaintingBinding.instance\")]),t._v(\"是一个单例，\"),n(\"code\",[t._v(\"imageCache\")]),t._v(\"事实上也是一个单例，也就是说图片缓存是全局的，统一由\"),n(\"code\",[t._v(\"PaintingBinding.instance.imageCache\")]),t._v(\" 来管理。\")]),t._v(\" \"),n(\"p\",[t._v(\"下面我们看看\"),n(\"code\",[t._v(\"ImageCache\")]),t._v(\"类定义：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" int _kDefaultSize \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1000\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" int _kDefaultSizeBytes \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<<\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 100 MiB\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ImageCache\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 正在加载中的图片队列\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Map\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Object\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" _PendingImage\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _pendingImages \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Object\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" _PendingImage\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 缓存队列\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Map\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Object\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" _CachedImage\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _cache \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Object\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" _CachedImage\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 缓存数量上限(1000)\")]),t._v(\"\\n  int _maximumSize \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _kDefaultSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 缓存容量上限 (100 MB)\")]),t._v(\"\\n  int _maximumSizeBytes \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _kDefaultSizeBytes\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 缓存上限设置的setter\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"set\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"maximumSize\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"set\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"maximumSizeBytes\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"int value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 省略部分定义\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 清除所有缓存\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"clear\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// ...省略具体实现代码\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 清除指定key对应的图片缓存\")]),t._v(\"\\n  bool \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"evict\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Object key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// ...省略具体实现代码\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n \\n  ImageStreamCompleter \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"putIfAbsent\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Object key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" ImageStreamCompleter \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"loader\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" ImageErrorListener onError \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"loader \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    ImageStreamCompleter result \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _pendingImages\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"completer\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 图片还未加载成功，直接返回\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"result \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" result\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n \\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 有缓存，继续往下走\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 先移除缓存，后再添加，可以让最新使用过的缓存在_map中的位置更近一些，清理时会LRU来清除\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" _CachedImage image \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _cache\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"remove\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"image \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _cache\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"completer\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      result \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"loader\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"catch\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"error\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" stackTrace\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"onError \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onError\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"error\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" stackTrace\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"rethrow\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"listener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ImageInfo info\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" bool syncCall\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" int imageSize \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" info\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"image \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" info\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"height \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" info\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"width \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"*\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"4\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" _CachedImage image \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_CachedImage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"result\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" imageSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 下面是缓存处理的逻辑\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"maximumSizeBytes \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\" imageSize \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" maximumSizeBytes\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        _maximumSizeBytes \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" imageSize \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1000\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n      _currentSizeBytes \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+=\")]),t._v(\" imageSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" _PendingImage pendingImage \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _pendingImages\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"remove\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"pendingImage \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        pendingImage\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"removeListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n      _cache\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_checkCacheSize\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"maximumSize \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\" maximumSizeBytes \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" ImageStreamListener streamListener \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ImageStreamListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"listener\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      _pendingImages\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_PendingImage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"result\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" streamListener\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// Listener is removed in [_PendingImage.removeListener].\")]),t._v(\"\\n      result\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"streamListener\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" result\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 当缓存数量超过最大值或缓存的大小超过最大缓存容量，会调用此方法清理到缓存上限以内\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_checkCacheSize\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"while\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_currentSizeBytes \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _maximumSizeBytes \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"||\")]),t._v(\" _cache\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"length \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" _maximumSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" Object key \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _cache\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"keys\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"first\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" _CachedImage image \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _cache\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      _currentSizeBytes \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"-=\")]),t._v(\" image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"sizeBytes\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      _cache\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"remove\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"有缓存则使用缓存，没有缓存则调用load方法加载图片，加载成功后:\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"先判断图片数据有没有缓存，如果有，则直接返回\"),n(\"code\",[t._v(\"ImageStream\")]),t._v(\"。\")]),t._v(\" \"),n(\"li\",[t._v(\"如果没有缓存，则调用\"),n(\"code\",[t._v(\"load(T key)\")]),t._v(\"方法从数据源加载图片数据，加载成功后先缓存，然后返回ImageStream。\")])]),t._v(\" \"),n(\"p\",[t._v(\"另外，我们可以看到\"),n(\"code\",[t._v(\"ImageCache\")]),t._v(\"类中有设置缓存上限的setter，所以，如果我们可以自定义缓存上限：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\" PaintingBinding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"instance\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"imageCache\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"maximumSize\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2000\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//最多2000张\")]),t._v(\"\\n PaintingBinding\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"instance\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"imageCache\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"maximumSizeBytes \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<<\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//最大200M\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"现在我们看一下缓存的key，因为Map中相同key的值会被覆盖，也就是说key是图片缓存的一个唯一标识，只要是不同key，那么图片数据就会分别缓存（即使事实上是同一张图片）。那么图片的唯一标识是什么呢？跟踪源码，很容易发现key正是\"),n(\"code\",[t._v(\"ImageProvider.obtainKey()\")]),t._v(\"方法的返回值，而此方法需要\"),n(\"code\",[t._v(\"ImageProvider\")]),t._v(\"子类去重写，这也就意味着不同的\"),n(\"code\",[t._v(\"ImageProvider\")]),t._v(\"对key的定义逻辑会不同。其实也很好理解，比如对于\"),n(\"code\",[t._v(\"NetworkImage\")]),t._v(\"，将图片的url作为key会很合适，而对于\"),n(\"code\",[t._v(\"AssetImage\")]),t._v(\"，则应该将“包名+路径”作为唯一的key。下面我们以\"),n(\"code\",[t._v(\"NetworkImage\")]),t._v(\"为例，看一下它的\"),n(\"code\",[t._v(\"obtainKey()\")]),t._v(\"实现：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\nFuture\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"NetworkImage\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"obtainKey\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"image_provider\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"ImageConfiguration configuration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" SynchronousFuture\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"NetworkImage\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"代码很简单，创建了一个同步的future，然后直接将自身做为key返回。因为Map中在判断key（此时是\"),n(\"code\",[t._v(\"NetworkImage\")]),t._v(\"对象）是否相等时会使用“==”运算符，那么定义key的逻辑就是\"),n(\"code\",[t._v(\"NetworkImage\")]),t._v(\"的“==”运算符：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\nbool \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"operator\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),t._v(\" other\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//省略无关代码\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" NetworkImage typedOther \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" other\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" url \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" typedOther\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"url\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&&\")]),t._v(\" scale \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" typedOther\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"scale\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"很清晰，对于网络图片来说，会将其“url+缩放比例”作为缓存的key。也就是说\"),n(\"strong\",[t._v(\"如果两张图片的url或scale只要有一个不同，便会重新下载并分别缓存\")]),t._v(\"。\")]),t._v(\" \"),n(\"p\",[t._v(\"另外，我们需要注意的是，图片缓存是在内存中，并没有进行本地文件持久化存储，这也是为什么网络图片在应用重启后需要重新联网下载的原因。\")]),t._v(\" \"),n(\"p\",[t._v(\"同时也意味着在应用生命周期内，如果缓存没有超过上限，相同的图片只会被下载一次。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"总结\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),n(\"p\",[t._v(\"上面主要结合源码，探索了\"),n(\"code\",[t._v(\"ImageProvider\")]),t._v(\"的主要功能和原理，如果要用一句话来总结\"),n(\"code\",[t._v(\"ImageProvider\")]),t._v(\"功能，那么应该是：加载图片数据并进行缓存、解码。在此再次提醒读者，Flutter的源码是非常好的第一手资料，建议读者多多探索，另外，在阅读源码学习的同时一定要有总结，这样才不至于在源码中迷失。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_14-5-2-image组件原理\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_14-5-2-image组件原理\"}},[t._v(\"#\")]),t._v(\" 14.5.2 Image组件原理\")]),t._v(\" \"),n(\"p\",[t._v(\"前面章节中我们介绍过\"),n(\"code\",[t._v(\"Image\")]),t._v(\"的基础用法，现在我们更深入一些，研究一下\"),n(\"code\",[t._v(\"Image\")]),t._v(\"是如何和\"),n(\"code\",[t._v(\"ImageProvider\")]),t._v(\"配合来获取最终解码后的数据，然后又如何将图片绘制到屏幕上的。\")]),t._v(\" \"),n(\"p\",[t._v(\"本节换一个思路，我们先不去直接看\"),n(\"code\",[t._v(\"Image\")]),t._v(\"的源码，而根据已经掌握的知识来实现一个简版的“\"),n(\"code\",[t._v(\"Image\")]),t._v(\"组件” \"),n(\"code\",[t._v(\"MyImage\")]),t._v(\"，代码大致如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyImage\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyImage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"imageProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"imageProvider \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" ImageProvider imageProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _MyImageState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_MyImageState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_MyImageState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"MyImage\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  ImageStream _imageStream\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  ImageInfo _imageInfo\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didChangeDependencies\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didChangeDependencies\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 依赖改变时，图片的配置信息可能会发生改变\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_getImage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didUpdateWidget\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"MyImage oldWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"didUpdateWidget\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"oldWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"imageProvider \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" oldWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"imageProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_getImage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_getImage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" ImageStream oldImageStream \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" _imageStream\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 调用imageProvider.resolve方法，获得ImageStream。\")]),t._v(\"\\n    _imageStream \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"\\n        widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"imageProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"resolve\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createLocalImageConfiguration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//判断新旧ImageStream是否相同，如果不同，则需要调整流的监听器\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_imageStream\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"key \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" oldImageStream\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" ImageStreamListener listener \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ImageStreamListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_updateImage\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      oldImageStream\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"removeListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"listener\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      _imageStream\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"addListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"listener\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_updateImage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"ImageInfo imageInfo\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" bool synchronousCall\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// Trigger a build whenever the image changes.\")]),t._v(\"\\n      _imageInfo \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" imageInfo\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    _imageStream\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"removeListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ImageStreamListener\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_updateImage\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"dispose\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RawImage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _imageInfo\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"image\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// this is a dart:ui Image object\")]),t._v(\"\\n      scale\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _imageInfo\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"scale \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上面代码流程如下：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"通过\"),n(\"code\",[t._v(\"imageProvider.resolve\")]),t._v(\"方法可以得到一个\"),n(\"code\",[t._v(\"ImageStream\")]),t._v(\"（图片数据流），然后监听\"),n(\"code\",[t._v(\"ImageStream\")]),t._v(\"的变化。当图片数据源发生变化时，\"),n(\"code\",[t._v(\"ImageStream\")]),t._v(\"会触发相应的事件，而本例中我们只设置了图片成功的监听器\"),n(\"code\",[t._v(\"_updateImage\")]),t._v(\"，而\"),n(\"code\",[t._v(\"_updateImage\")]),t._v(\"中只更新了\"),n(\"code\",[t._v(\"_imageInfo\")]),t._v(\"。值得注意的是，如果是静态图，\"),n(\"code\",[t._v(\"ImageStream\")]),t._v(\"只会触发一次时间，如果是动态图，则会触发多次事件，每一次都会有一个解码后的图片帧。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"_imageInfo\")]),t._v(\" 更新后会rebuild，此时会创建一个\"),n(\"code\",[t._v(\"RawImage\")]),t._v(\" Widget。\"),n(\"code\",[t._v(\"RawImage\")]),t._v(\"最终会通过\"),n(\"code\",[t._v(\"RenderImage\")]),t._v(\"来将图片绘制在屏幕上。如果继续跟进\"),n(\"code\",[t._v(\"RenderImage\")]),t._v(\"类，我们会发现\"),n(\"code\",[t._v(\"RenderImage\")]),t._v(\"的\"),n(\"code\",[t._v(\"paint\")]),t._v(\" 方法中调用了\"),n(\"code\",[t._v(\"paintImage\")]),t._v(\"方法，而\"),n(\"code\",[t._v(\"paintImage\")]),t._v(\"方法中通过\"),n(\"code\",[t._v(\"Canvas\")]),t._v(\"的\"),n(\"code\",[t._v(\"drawImageRect(…)\")]),t._v(\"、\"),n(\"code\",[t._v(\"drawImageNine(...)\")]),t._v(\"等方法来完成最终的绘制。\")]),t._v(\" \"),n(\"li\",[t._v(\"最终的绘制由\"),n(\"code\",[t._v(\"RawImage\")]),t._v(\"来完成。\")])]),t._v(\" \"),n(\"p\",[t._v(\"下面测试一下\"),n(\"code\",[t._v(\"MyImage\")]),t._v(\"：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ImageInternalTestRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyImage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          imageProvider\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"NetworkImage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"https://avatars2.githubusercontent.com/u/20411648?s=460&v=4\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"运行效果如图14-4所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(386),alt:\"图14-4\"}})]),t._v(\" \"),n(\"p\",[t._v(\"成功了！ 现在，想必\"),n(\"code\",[t._v(\"Image\")]),t._v(\" Widget的源码已经没必要在花费篇章去介绍了，读者有兴趣可以自行去阅读。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"总结-2\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结-2\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),n(\"p\",[t._v(\"本节主要介绍了Flutter 图片的加载、缓存和绘制流程。其中\"),n(\"code\",[t._v(\"ImageProvider\")]),t._v(\"主要负责图片数据的加载和缓存，而绘制部分逻辑主要是由\"),n(\"code\",[t._v(\"RawImage\")]),t._v(\"来完成。 而\"),n(\"code\",[t._v(\"Image\")]),t._v(\"正是连接起\"),n(\"code\",[t._v(\"ImageProvider\")]),t._v(\"和\"),n(\"code\",[t._v(\"RawImage\")]),t._v(\" 的桥梁。\")])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/92.7b12896a.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[92],{387:function(t,a){t.exports=\"data:image/png;base64,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\"},757:function(t,a,s){\"use strict\";s.r(a);var n=s(45),e=Object(n.a)({},(function(){var t=this,a=t.$createElement,n=t._self._c||a;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_14-3-renderobject和renderbox\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_14-3-renderobject和renderbox\"}},[t._v(\"#\")]),t._v(\" 14.3 RenderObject和RenderBox\")]),t._v(\" \"),n(\"p\",[t._v(\"在上一节我们说过每个\"),n(\"code\",[t._v(\"Element\")]),t._v(\"都对应一个\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"，我们可以通过\"),n(\"code\",[t._v(\"Element.renderObject\")]),t._v(\" 来获取。并且我们也说过\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"的主要职责是Layout和绘制，所有的\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"会组成一棵渲染树Render Tree。本节我们将重点介绍一下\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"的作用。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"RenderObject\")]),t._v(\"就是渲染树中的一个对象，它拥有一个\"),n(\"code\",[t._v(\"parent\")]),t._v(\"和一个\"),n(\"code\",[t._v(\"parentData\")]),t._v(\" 插槽（slot），所谓插槽，就是指预留的一个接口或位置，这个接口和位置是由其它对象来接入或占据的，这个接口或位置在软件中通常用预留变量来表示，而\"),n(\"code\",[t._v(\"parentData\")]),t._v(\"正是一个预留变量，它正是由\"),n(\"code\",[t._v(\"parent\")]),t._v(\" 来赋值的，\"),n(\"code\",[t._v(\"parent\")]),t._v(\"通常会通过子\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"的\"),n(\"code\",[t._v(\"parentData\")]),t._v(\"存储一些和子元素相关的数据，如在Stack布局中，\"),n(\"code\",[t._v(\"RenderStack\")]),t._v(\"就会将子元素的偏移数据存储在子元素的\"),n(\"code\",[t._v(\"parentData\")]),t._v(\"中（具体可以查看\"),n(\"code\",[t._v(\"Positioned\")]),t._v(\"实现）。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"RenderObject\")]),t._v(\"类本身实现了一套基础的layout和绘制协议，但是并没有定义子节点模型（如一个节点可以有几个子节点，没有子节点？一个？两个？或者更多？）。 它也没有定义坐标系统（如子节点定位是在笛卡尔坐标中还是极坐标？）和具体的布局协议（是通过宽高还是通过constraint和size?，或者是否由父节点在子节点布局之前或之后设置子节点的大小和位置等）。为此，Flutter提供了一个\"),n(\"code\",[t._v(\"RenderBox\")]),t._v(\"类，它继承自``RenderObject\"),n(\"code\",[t._v(\"，布局坐标系统采用笛卡尔坐标系，这和Android和iOS原生坐标系是一致的，都是屏幕的top、left是原点，然后分宽高两个轴，大多数情况下，我们直接使用\")]),t._v(\"RenderBox\"),n(\"code\",[t._v(\"就可以了，除非遇到要自定义布局模型或坐标系统的情况，下面我们重点介绍一下\")]),t._v(\"RenderBox`。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_14-3-1-布局过程\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_14-3-1-布局过程\"}},[t._v(\"#\")]),t._v(\" 14.3.1 布局过程\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"constraints\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#constraints\"}},[t._v(\"#\")]),t._v(\" Constraints\")]),t._v(\" \"),n(\"p\",[t._v(\"在\"),n(\"code\",[t._v(\"RenderBox\")]),t._v(\" 中，有个\"),n(\"code\",[t._v(\"size\")]),t._v(\"属性用来保存控件的宽和高。\"),n(\"code\",[t._v(\"RenderBox\")]),t._v(\"的layout是通过在组件树中从上往下传递\"),n(\"code\",[t._v(\"BoxConstraints\")]),t._v(\"对象的实现的。\"),n(\"code\",[t._v(\"BoxConstraints\")]),t._v(\"对象可以限制子节点的最大和最小宽高，子节点必须遵守父节点给定的限制条件。\")]),t._v(\" \"),n(\"p\",[t._v(\"在布局阶段，父节点会调用子节点的\"),n(\"code\",[t._v(\"layout()\")]),t._v(\"方法，下面我们看看\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"中\"),n(\"code\",[t._v(\"layout()\")]),t._v(\"方法的大致实现（删掉了一些无关代码和异常捕获）:\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"layout\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Constraints constraints\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" bool parentUsesSize \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n   \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n   RenderObject relayoutBoundary\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"parentUsesSize \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"||\")]),t._v(\" sizedByParent \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"||\")]),t._v(\" constraints\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isTight \\n    \\t\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"||\")]),t._v(\" parent \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"is!\")]),t._v(\" RenderObject\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      relayoutBoundary \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" RenderObject parent \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"parent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      relayoutBoundary \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" parent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"_relayoutBoundary\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"sizedByParent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"performResize\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"performLayout\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"可以看到\"),n(\"code\",[t._v(\"layout\")]),t._v(\"方法需要传入两个参数，第一个为\"),n(\"code\",[t._v(\"constraints\")]),t._v(\"，即 父节点对子节点大小的限制，该值根据父节点的布局逻辑确定。另外一个参数是 \"),n(\"code\",[t._v(\"parentUsesSize\")]),t._v(\"，该值用于确定 \"),n(\"code\",[t._v(\"relayoutBoundary\")]),t._v(\"，该参数表示子节点布局变化是否影响父节点，如果为\"),n(\"code\",[t._v(\"true\")]),t._v(\"，当子节点布局发生变化时父节点都会标记为需要重新布局，如果为\"),n(\"code\",[t._v(\"false\")]),t._v(\"，则子节点布局发生变化后不会影响父节点。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"relayoutboundary\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#relayoutboundary\"}},[t._v(\"#\")]),t._v(\" relayoutBoundary\")]),t._v(\" \"),n(\"p\",[t._v(\"上面\"),n(\"code\",[t._v(\"layout()\")]),t._v(\"源码中定义了一个\"),n(\"code\",[t._v(\"relayoutBoundary\")]),t._v(\"变量，什么是 \"),n(\"code\",[t._v(\"relayoutBoundary\")]),t._v(\"？在前面介绍\"),n(\"code\",[t._v(\"Element\")]),t._v(\"时，我们讲过当一个\"),n(\"code\",[t._v(\"Element\")]),t._v(\"标记为 dirty 时便会重新build，这时\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"便会重新布局，我们是通过调用 \"),n(\"code\",[t._v(\"markNeedsBuild()\")]),t._v(\" 来标记\"),n(\"code\",[t._v(\"Element\")]),t._v(\"为dirty的。在\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"中有一个类似的\"),n(\"code\",[t._v(\"markNeedsLayout()\")]),t._v(\"方法，它会将\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"的布局状态标记为 dirty，这样在下一个frame中便会重新layout，我们看看\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"的\"),n(\"code\",[t._v(\"markNeedsLayout()\")]),t._v(\"的部分源码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"markNeedsLayout\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_relayoutBoundary \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_relayoutBoundary \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"markParentNeedsLayout\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    _needsLayout \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"owner \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n      owner\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"_nodesNeedingLayout\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      owner\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"requestVisualUpdate\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"代码大致逻辑是先判断自身是不是\"),n(\"code\",[t._v(\"relayoutBoundary\")]),t._v(\"，如果不是就继续向parent 查找，一直向上查找到是 \"),n(\"code\",[t._v(\"relayoutBoundary\")]),t._v(\" 的 \"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"为止，然后再将其标记为 dirty 的。这样来看它的作用就比较明显了，意思就是当一个控件的大小被改变时可能会影响到它的 parent，因此 parent 也需要被重新布局，那么到什么时候是个头呢？答案就是 \"),n(\"code\",[t._v(\"relayoutBoundary\")]),t._v(\"，如果一个 \"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\" 是 \"),n(\"code\",[t._v(\"relayoutBoundary\")]),t._v(\"，就表示它的大小变化不会再影响到 parent 的大小了，于是 parent 也就不用重新布局了。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"performresize-和-performlayout\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#performresize-和-performlayout\"}},[t._v(\"#\")]),t._v(\" performResize 和 performLayout\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"RenderBox\")]),t._v(\"实际的测量和布局逻辑是在\"),n(\"code\",[t._v(\"performResize()\")]),t._v(\" 和 \"),n(\"code\",[t._v(\"performLayout()\")]),t._v(\"两个方法中，RenderBox子类需要实现这两个方法来定制自身的布局逻辑。根据\"),n(\"code\",[t._v(\"layout()\")]),t._v(\" 源码可以看出只有 \"),n(\"code\",[t._v(\"sizedByParent\")]),t._v(\" 为 \"),n(\"code\",[t._v(\"true\")]),t._v(\" 时，\"),n(\"code\",[t._v(\"performResize()\")]),t._v(\" 才会被调用，而 \"),n(\"code\",[t._v(\"performLayout()\")]),t._v(\" 是每次布局都会被调用的。\"),n(\"code\",[t._v(\"sizedByParent\")]),t._v(\" 意为该节点的大小是否仅通过 parent 传给它的 constraints 就可以确定了，即该节点的大小与它自身的属性和其子节点无关，比如如果一个控件永远充满 parent 的大小，那么 \"),n(\"code\",[t._v(\"sizedByParent\")]),t._v(\"就应该返回\"),n(\"code\",[t._v(\"true\")]),t._v(\"，此时其大小在 \"),n(\"code\",[t._v(\"performResize()\")]),t._v(\" 中就确定了，在后面的 \"),n(\"code\",[t._v(\"performLayout()\")]),t._v(\" 方法中将不会再被修改了，这种情况下 \"),n(\"code\",[t._v(\"performLayout()\")]),t._v(\" 只负责布局子节点。\")]),t._v(\" \"),n(\"p\",[t._v(\"在 \"),n(\"code\",[t._v(\"performLayout()\")]),t._v(\" 方法中除了完成自身布局，也必须完成子节点的布局，这是因为只有父子节点全部完成后布局流程才算真正完成。所以最终的调用栈将会变成：\"),n(\"em\",[t._v(\"layout() > performResize()/performLayout() > child.layout() > ...\")]),t._v(\"  ，如此递归完成整个UI的布局。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"RenderBox\")]),t._v(\"子类要定制布局算法不应该重写\"),n(\"code\",[t._v(\"layout()\")]),t._v(\"方法，因为对于任何RenderBox的子类来说，它的layout流程基本是相同的，不同之处只在具体的布局算法，而具体的布局算法子类应该通过重写\"),n(\"code\",[t._v(\"performResize()\")]),t._v(\" 和 \"),n(\"code\",[t._v(\"performLayout()\")]),t._v(\"两个方法来实现，他们会在\"),n(\"code\",[t._v(\"layout()\")]),t._v(\"中被调用。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"parentdata\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#parentdata\"}},[t._v(\"#\")]),t._v(\" ParentData\")]),t._v(\" \"),n(\"p\",[t._v(\"当layout结束后，每个节点的位置（相对于父节点的偏移）就已经确定了，\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"就可以根据位置信息来进行最终的绘制。但是在layout过程中，节点的位置信息怎么保存？对于大多数\"),n(\"code\",[t._v(\"RenderBox\")]),t._v(\"子类来说如果子类只有一个子节点，那么子节点偏移一般都是\"),n(\"code\",[t._v(\"Offset.zero\")]),t._v(\" ，如果有多个子节点，则每个子节点的偏移就可能不同。而子节点在父节点的偏移数据正是通过\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"的\"),n(\"code\",[t._v(\"parentData\")]),t._v(\"属性来保存的。在\"),n(\"code\",[t._v(\"RenderBox\")]),t._v(\"中，其\"),n(\"code\",[t._v(\"parentData\")]),t._v(\"属性默认是一个\"),n(\"code\",[t._v(\"BoxParentData\")]),t._v(\"对象，该属性只能通过父节点的\"),n(\"code\",[t._v(\"setupParentData()\")]),t._v(\"方法来设置：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"abstract\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"RenderBox\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"RenderObject\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setupParentData\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"covariant\")]),t._v(\" RenderObject child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"parentData \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"is!\")]),t._v(\" BoxParentData\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"parentData \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxParentData\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"BoxParentData\")]),t._v(\"定义如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// Parentdata 会被RenderBox和它的子类使用.\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"BoxParentData\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ParentData\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// offset表示在子节点在父节点坐标系中的绘制偏移  \")]),t._v(\"\\n  Offset offset \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"zero\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  String \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'offset=$offset'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"blockquote\",[n(\"p\",[t._v(\"一定要注意，\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"的\"),n(\"code\",[t._v(\"parentData\")]),t._v(\" 只能通过父元素设置.\")])]),t._v(\" \"),n(\"p\",[t._v(\"当然，\"),n(\"code\",[t._v(\"ParentData\")]),t._v(\"并不仅仅可以用来存储偏移信息，通常所有和子节点特定的数据都可以存储到子节点的\"),n(\"code\",[t._v(\"ParentData\")]),t._v(\"中，如\"),n(\"code\",[t._v(\"ContainerBox\")]),t._v(\"的\"),n(\"code\",[t._v(\"ParentData\")]),t._v(\"就保存了指向兄弟节点的\"),n(\"code\",[t._v(\"previousSibling\")]),t._v(\"和\"),n(\"code\",[t._v(\"nextSibling\")]),t._v(\"，\"),n(\"code\",[t._v(\"Element.visitChildren()\")]),t._v(\"方法也正是通过它们来实现对子节点的遍历。再比如\"),n(\"code\",[t._v(\"KeepAlive\")]),t._v(\" 组件，它使用\"),n(\"code\",[t._v(\"KeepAliveParentDataMixin\")]),t._v(\"（继承自\"),n(\"code\",[t._v(\"ParentData\")]),t._v(\"） 来保存子节的\"),n(\"code\",[t._v(\"keepAlive\")]),t._v(\"状态。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_14-3-2-绘制过程\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_14-3-2-绘制过程\"}},[t._v(\"#\")]),t._v(\" 14.3.2 绘制过程\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"RenderObject\")]),t._v(\"可以通过\"),n(\"code\",[t._v(\"paint()\")]),t._v(\"方法来完成具体绘制逻辑，流程和布局流程相似，子类可以实现\"),n(\"code\",[t._v(\"paint()\")]),t._v(\"方法来完成自身的绘制逻辑，\"),n(\"code\",[t._v(\"paint()\")]),t._v(\"签名如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"paint\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"PaintingContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Offset offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"通过\"),n(\"code\",[t._v(\"context.canvas\")]),t._v(\"可以取到\"),n(\"code\",[t._v(\"Canvas\")]),t._v(\"对象，接下来就可以调用\"),n(\"code\",[t._v(\"Canvas\")]),t._v(\" API来实现具体的绘制逻辑。\")]),t._v(\" \"),n(\"p\",[t._v(\"如果节点有子节点，它除了完成自身绘制逻辑之外，还要调用子节点的绘制方法。我们以\"),n(\"code\",[t._v(\"RenderFlex\")]),t._v(\"对象为例说明：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"paint\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"PaintingContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Offset offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 如果子元素未超出当前边界，则绘制子元素  \")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_overflow \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"defaultPaint\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 如果size为空，则无需绘制\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isEmpty\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 剪裁掉溢出边界的部分\")]),t._v(\"\\n  context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pushClipRect\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"needsCompositing\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"zero \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&\")]),t._v(\" size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" defaultPaint\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String debugOverflowHints \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'...'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//溢出提示内容，省略\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 绘制溢出部分的错误提示样式\")]),t._v(\"\\n    Rect overflowChildRect\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"switch\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_direction\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" Axis\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"horizontal\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n        overflowChildRect \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Rect\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fromLTWH\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"width \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" _overflow\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"break\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"case\")]),t._v(\" Axis\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"vertical\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"\\n        overflowChildRect \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Rect\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"fromLTWH\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"height \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" _overflow\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"break\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"  \\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"paintOverflowIndicator\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"zero \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"&\")]),t._v(\" size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                           overflowChildRect\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" overflowHints\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" debugOverflowHints\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"代码很简单，首先判断有无溢出，如果没有则调用\"),n(\"code\",[t._v(\"defaultPaint(context, offset)\")]),t._v(\"来完成绘制，该方法源码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"defaultPaint\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"PaintingContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Offset offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  ChildType child \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" firstChild\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"while\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" ParentDataType childParentData \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"parentData\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//绘制子节点， \")]),t._v(\"\\n    context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"paintChild\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" childParentData\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"offset \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"+\")]),t._v(\" offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    child \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" childParentData\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"nextSibling\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"很明显，由于Flex本身没有需要绘制的东西，所以直接遍历其子节点，然后调用\"),n(\"code\",[t._v(\"paintChild()\")]),t._v(\"来绘制子节点，同时将子节点\"),n(\"code\",[t._v(\"ParentData\")]),t._v(\"中在layout阶段保存的offset加上自身偏移作为第二个参数传递给\"),n(\"code\",[t._v(\"paintChild()\")]),t._v(\"。而如果子节点还有子节点时，\"),n(\"code\",[t._v(\"paintChild()\")]),t._v(\"方法还会调用子节点的\"),n(\"code\",[t._v(\"paint()\")]),t._v(\"方法，如此递归完成整个节点树的绘制，最终调用栈为： \"),n(\"em\",[t._v(\"paint() > paintChild() > paint() ...\")]),t._v(\" 。\")]),t._v(\" \"),n(\"p\",[t._v(\"当需要绘制的内容大小溢出当前空间时，将会执行\"),n(\"code\",[t._v(\"paintOverflowIndicator()\")]),t._v(\" 来绘制溢出部分提示，这个就是我们经常看到的溢出提示，如图14-3所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(387),alt:\"overflow\"}})]),t._v(\" \"),n(\"h3\",{attrs:{id:\"repaintboundary\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#repaintboundary\"}},[t._v(\"#\")]),t._v(\" RepaintBoundary\")]),t._v(\" \"),n(\"p\",[t._v(\"我们已经在\"),n(\"code\",[t._v(\"CustomPaint\")]),t._v(\"一节中介绍过\"),n(\"code\",[t._v(\"RepaintBoundary\")]),t._v(\"，现在我们深入的了解一些。与 \"),n(\"code\",[t._v(\"RelayoutBoundary\")]),t._v(\" 相似，\"),n(\"code\",[t._v(\"RepaintBoundary\")]),t._v(\"是用于在确定重绘边界的，与\"),n(\"code\",[t._v(\"RelayoutBoundary\")]),t._v(\"不同的是，这个绘制边界需要由开发者通过\"),n(\"code\",[t._v(\"RepaintBoundary\")]),t._v(\" 组件自己指定，如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"CustomPaint\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Size\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"300\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//指定画布大小\")]),t._v(\"\\n  painter\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyPainter\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RepaintBoundary\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"下面我们看看\"),n(\"code\",[t._v(\"RepaintBoundary\")]),t._v(\"的原理，\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"有一个\"),n(\"code\",[t._v(\"isRepaintBoundary\")]),t._v(\"属性，该属性决定这个\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"重绘时是否独立于其父元素，如果该属性值为\"),n(\"code\",[t._v(\"true\")]),t._v(\" ，则独立绘制，反之则一起绘制。那独立绘制是怎么实现的呢？ 答案就在\"),n(\"code\",[t._v(\"paintChild()\")]),t._v(\"源码中：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"paintChild\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"RenderObject child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Offset offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isRepaintBoundary\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"stopRecordingIfNeeded\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_compositeChild\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_paintWithContext\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们可以看到，在绘制子节点时，如果\"),n(\"code\",[t._v(\"child.isRepaintBoundary\")]),t._v(\" 为 \"),n(\"code\",[t._v(\"true\")]),t._v(\"则会调用\"),n(\"code\",[t._v(\"_compositeChild()\")]),t._v(\"方法，\"),n(\"code\",[t._v(\"_compositeChild()\")]),t._v(\"源码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_compositeChild\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"RenderObject child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Offset offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 给子节点创建一个layer ，然后再上面绘制子节点 \")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"_needsPaint\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"repaintCompositedChild\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" debugAlsoPaintedParent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"_layer \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"_layer\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"offset \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" offset\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"appendLayer\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"_layer\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"很明显了，独立绘制是通过在不同的layer（层）上绘制的。所以，很明显，正确使用\"),n(\"code\",[t._v(\"isRepaintBoundary\")]),t._v(\"属性可以提高绘制效率，避免不必要的重绘。具体原理是：和触发重新build和layout类似，\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"也提供了一个\"),n(\"code\",[t._v(\"markNeedsPaint()\")]),t._v(\"方法，其源码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"markNeedsPaint\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//如果RenderObject.isRepaintBoundary 为true,则该RenderObject拥有layer，直接绘制  \")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"isRepaintBoundary\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"owner \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//找到最近的layer，绘制  \")]),t._v(\"\\n      owner\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"_nodesNeedingPaint\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      owner\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"requestVisualUpdate\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"parent \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"is\")]),t._v(\" RenderObject\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 没有自己的layer, 会和一个祖先节点共用一个layer  \")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_layer \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" RenderObject parent \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"parent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 向父级递归查找  \")]),t._v(\"\\n    parent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"markNeedsPaint\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"parent \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"parent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 如果直到根节点也没找到一个Layer，那么便需要绘制自身，因为没有其它节点可以绘制根节点。  \")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"owner \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      owner\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"requestVisualUpdate\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"可以看出，当调用 \"),n(\"code\",[t._v(\"markNeedsPaint()\")]),t._v(\" 方法时，会从当前 \"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\" 开始一直向父节点查找，直到找到 一个\"),n(\"code\",[t._v(\"isRepaintBoundary\")]),t._v(\" 为 \"),n(\"code\",[t._v(\"true\")]),t._v(\"的\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\" 时，才会触发重绘，这样便可以实现局部重绘。当 有\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\" 绘制的很频繁或很复杂时，可以通过RepaintBoundary Widget来指定\"),n(\"code\",[t._v(\"isRepaintBoundary\")]),t._v(\" 为 \"),n(\"code\",[t._v(\"true\")]),t._v(\"，这样在绘制时仅会重绘自身而无需重绘它的 parent，如此便可提高性能。\")]),t._v(\" \"),n(\"p\",[t._v(\"还有一个问题，通过\"),n(\"code\",[t._v(\"RepaintBoundary\")]),t._v(\" 如何设置\"),n(\"code\",[t._v(\"isRepaintBoundary\")]),t._v(\"属性呢？其实，如果使用了\"),n(\"code\",[t._v(\"RepaintBoundary\")]),t._v(\"，其对应的\"),n(\"code\",[t._v(\"RenderRepaintBoundary\")]),t._v(\"会自动将\"),n(\"code\",[t._v(\"isRepaintBoundary\")]),t._v(\"设为\"),n(\"code\",[t._v(\"true\")]),t._v(\"的：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"RenderRepaintBoundary\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"RenderProxyBox\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"/// Creates a repaint boundary around [child].\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RenderRepaintBoundary\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" RenderBox child \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  bool \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"get\")]),t._v(\" isRepaintBoundary \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"h2\",{attrs:{id:\"_14-3-3-命中测试\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_14-3-3-命中测试\"}},[t._v(\"#\")]),t._v(\" 14.3.3 命中测试\")]),t._v(\" \"),n(\"p\",[t._v(\"我们在“事件处理与通知”一章中已经讲过Flutter事件机制和命中测试流程，本节我们看一下其内部实现原理。\")]),t._v(\" \"),n(\"p\",[t._v(\"一个对象是否可以响应事件，取决于其对命中测试的返回，当发生用户事件时，会从根节点（\"),n(\"code\",[t._v(\"RenderView\")]),t._v(\"）开始进行命中测试，下面是\"),n(\"code\",[t._v(\"RenderView\")]),t._v(\"的\"),n(\"code\",[t._v(\"hitTest()\")]),t._v(\"源码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"bool \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"hitTest\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"HitTestResult result\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" Offset position \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"child \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"hitTest\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"result\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//递归子RenderBox进行命中测试\")]),t._v(\"\\n  result\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"HitTestEntry\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//将测试结果添加到result中\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们再看看\"),n(\"code\",[t._v(\"RenderBox\")]),t._v(\"默认的\"),n(\"code\",[t._v(\"hitTest()\")]),t._v(\"实现：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"bool \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"hitTest\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"HitTestResult result\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" Offset position \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"  \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_size\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"contains\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"hitTestChildren\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"result\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"||\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"hitTestSelf\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      result\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"add\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxHitTestEntry\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们看到默认的实现里调用了\"),n(\"code\",[t._v(\"hitTestSelf()\")]),t._v(\"和\"),n(\"code\",[t._v(\"hitTestChildren()\")]),t._v(\"两个方法，这两个方法默认实现如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\" \\n\"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@protected\")]),t._v(\"\\nbool \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"hitTestSelf\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Offset position\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n \\n\"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@protected\")]),t._v(\"\\nbool \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"hitTestChildren\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"HitTestResult result\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" Offset position \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"hitTest\")]),t._v(\" 方法用来判断该\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\" 是否在被点击的范围内，同时负责将被点击的 \"),n(\"code\",[t._v(\"RenderBox\")]),t._v(\" 添加到 \"),n(\"code\",[t._v(\"HitTestResult\")]),t._v(\" 列表中，参数 \"),n(\"code\",[t._v(\"position\")]),t._v(\" 为事件触发的坐标（如果有的话），返回 true 则表示有\"),n(\"code\",[t._v(\"RenderBox\")]),t._v(\" 通过了命中测试，需要响应事件，反之则认为当前\"),n(\"code\",[t._v(\"RenderBox\")]),t._v(\"没有命中。在继承\"),n(\"code\",[t._v(\"RenderBox\")]),t._v(\"时，可以直接重写\"),n(\"code\",[t._v(\"hitTest()\")]),t._v(\"方法，也可以重写 \"),n(\"code\",[t._v(\"hitTestSelf()\")]),t._v(\" 或 \"),n(\"code\",[t._v(\"hitTestChildren()\")]),t._v(\", 唯一不同的是 \"),n(\"code\",[t._v(\"hitTest()\")]),t._v(\"中需要将通过命中测试的节点信息添加到命中测试结果列表中，而 \"),n(\"code\",[t._v(\"hitTestSelf()\")]),t._v(\" 和 \"),n(\"code\",[t._v(\"hitTestChildren()\")]),t._v(\"则只需要简单的返回\"),n(\"code\",[t._v(\"true\")]),t._v(\"或\"),n(\"code\",[t._v(\"false\")]),t._v(\"。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_14-3-4-语义化\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_14-3-4-语义化\"}},[t._v(\"#\")]),t._v(\" 14.3.4 语义化\")]),t._v(\" \"),n(\"p\",[t._v(\"语义化即Semantics，主要是提供给读屏软件的接口，也是实现辅助功能的基础，通过语义化接口可以让机器理解页面上的内容，对于有视力障碍用户可以使用读屏软件来理解UI内容。如果一个\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"要支持语义化接口，可以实现 \"),n(\"code\",[t._v(\"describeApproximatePaintClip\")]),t._v(\"和 \"),n(\"code\",[t._v(\"visitChildrenForSemantics\")]),t._v(\"方法和\"),n(\"code\",[t._v(\"semanticsAnnotator\")]),t._v(\" getter。更多关于语义化的信息可以查看API文档。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_14-3-5-总结\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_14-3-5-总结\"}},[t._v(\"#\")]),t._v(\" 14.3.5 总结\")]),t._v(\" \"),n(\"p\",[t._v(\"本节我们介绍了\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"主要的功能和方法，理解这些内容可以帮助我们更好的理解Flutter UI底层原理。我们也可以看到，如果要从头到尾实现一个\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"是比较麻烦的，我们必须去实现layout、绘制和命中测试逻辑，但是值得庆幸的是，大多数时候我们可以直接在Widget层通过组合或者\"),n(\"code\",[t._v(\"CustomPaint\")]),t._v(\"完成自定义UI。如果遇到只能定义一个新\"),n(\"code\",[t._v(\"RenderObject\")]),t._v(\"的场景时（如要实现一个新的layout算法的布局容器），可以直接继承自\"),n(\"code\",[t._v(\"RenderBox\")]),t._v(\"，这样可以帮我们减少一部分工作。\")])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/93.54d0e6b6.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[93],{392:function(t,s,n){t.exports=n.p+\"assets/img/15-5.bcaa565a.png\"},762:function(t,s,n){\"use strict\";n.r(s);var a=n(45),p=Object(a.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_15-7-登录页\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_15-7-登录页\"}},[t._v(\"#\")]),t._v(\" 15.7 登录页\")]),t._v(\" \"),a(\"p\",[t._v(\"我们说过Github有多种登录方式，为了简单起见，我们只实现通过用户名和密码登录。在实现登录页时有四点需要注意：\")]),t._v(\" \"),a(\"ol\",[a(\"li\",[t._v(\"可以自动填充上次登录的用户名（如果有）。\")]),t._v(\" \"),a(\"li\",[t._v(\"为了防止密码输入错误，密码框应该有开关可以看明文。\")]),t._v(\" \"),a(\"li\",[t._v(\"用户名或密码字段在调用登录接口前有本地合法性校验（比如不能为空）。\")]),t._v(\" \"),a(\"li\",[t._v(\"登录成功后需更新用户信息。\")])]),t._v(\" \"),a(\"p\",[t._v(\"实现代码如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'../index.dart'\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"LoginRoute\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _LoginRouteState \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_LoginRouteState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_LoginRouteState\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"LoginRoute\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  TextEditingController _unameController \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TextEditingController\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  TextEditingController _pwdController \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TextEditingController\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  bool pwdShow \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//密码是否显示明文\")]),t._v(\"\\n  GlobalKey _formKey \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"GlobalKey\")]),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"FormState\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  bool _nameAutoFocus \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 自动填充上次登录的用户名，填充后将焦点定位到密码输入框\")]),t._v(\"\\n    _unameController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"text \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" Global\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"profile\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"lastLogin\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_unameController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"text \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _nameAutoFocus \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"initState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" gm \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" GmLocalizations\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Scaffold\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      appBar\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"AppBar\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"gm\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"login\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      body\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"all\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"16.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Form\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _formKey\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          autovalidate\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextFormField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  autofocus\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _nameAutoFocus\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _unameController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"InputDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    labelText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" gm\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"userName\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    hintText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" gm\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"userNameOrEmail\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    prefixIcon\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"person\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 校验用户名（不能为空）\")]),t._v(\"\\n                  validator\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"v\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" v\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"trim\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isNotEmpty \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" gm\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"userNameRequired\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextFormField\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                controller\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _pwdController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                autofocus\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"_nameAutoFocus\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                decoration\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"InputDecoration\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    labelText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" gm\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"password\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    hintText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" gm\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"password\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    prefixIcon\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"lock\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    suffixIcon\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"IconButton\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                      icon\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Icon\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                          pwdShow \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"visibility_off \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Icons\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"visibility\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                      onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                          pwdShow \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"pwdShow\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                obscureText\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"pwdShow\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//校验密码（不能为空）\")]),t._v(\"\\n                validator\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"v\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" v\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"trim\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"isNotEmpty \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" gm\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"passwordRequired\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"only\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"top\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"25\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ConstrainedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  constraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BoxConstraints\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"expand\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"55.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"RaisedButton\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Theme\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"primaryColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    onPressed\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _onLogin\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    textColor\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"gm\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"login\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_onLogin\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"async\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 提交前，先验证各个表单字段是否合法\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_formKey\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"currentState \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"as\")]),t._v(\" FormState\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"validate\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showLoading\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      User user\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        user \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"await\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Git\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"login\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_unameController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"text\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" _pwdController\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"text\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 因为登录页返回后，首页会build，所以我们传false，更新user后不触发更新\")]),t._v(\"\\n        Provider\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"of\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"UserModel\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" listen\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"user \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" user\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"catch\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//登录失败则提示\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"response\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"statusCode \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"==\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"401\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showToast\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"GmLocalizations\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"userNameOrPasswordWrong\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"else\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"showToast\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"toString\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"finally\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 隐藏loading框\")]),t._v(\"\\n        Navigator\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"user \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 返回\")]),t._v(\"\\n        Navigator\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"pop\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"代码很简单，关键地方都有注释，不再赘述，下面我们看一下运行效果，如图15-5所示。\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(392),alt:\"图15-5\"}})])])}),[],!1,null,null,null);s.default=p.exports}}]);"
  },
  {
    "path": "docs/assets/js/94.c6e7a2f7.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[94],{393:function(t,a,s){t.exports=s.p+\"assets/img/2-1.801e91b2.png\"},765:function(t,a,s){\"use strict\";s.r(a);var n=s(45),e=Object(n.a)({},(function(){var t=this,a=t.$createElement,n=t._self._c||a;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_2-1-计数器应用示例\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_2-1-计数器应用示例\"}},[t._v(\"#\")]),t._v(\" 2.1 计数器应用示例\")]),t._v(\" \"),n(\"p\",[t._v(\"用Android Studio和VS Code创建的Flutter应用模板默认是一个简单的计数器示例。本节先仔细讲解一下这个计数器Demo的源码，让读者对Flutter应用程序结构有个基本了解，然后在随后的小节中将会基于此示例，一步一步添加一些新的功能来介绍Flutter应用的其它概念与技术。\")]),t._v(\" \"),n(\"p\",[t._v(\"对于接下来的示例，希望读者可以跟着笔者一起亲自动手来写一下，这样不仅可以加深印象，而且也会对介绍的概念与技术有一个真切的体会。如果你还不是很熟悉Dart语言或者没有移动开发经验，不用担心，只要你熟悉面向对象和基本编程概念（如变量、循环和条件控制），则可以完成本示例。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_2-1-1-创建flutter应用模板\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_2-1-1-创建flutter应用模板\"}},[t._v(\"#\")]),t._v(\" 2.1.1 创建Flutter应用模板\")]),t._v(\" \"),n(\"p\",[t._v('通过Android Studio或VS Code创建一个新的Flutter工程，命名为\"first_flutter_app\"。创建好后，就会得到一个计数器应用的Demo。')]),t._v(\" \"),n(\"blockquote\",[n(\"p\",[t._v(\"注意，默认Demo示例可能随着编辑器Flutter插件的版本变化而变化，本例中会介绍计数器示例的全部代码，所以不会对本示例产生影响。\")])]),t._v(\" \"),n(\"p\",[t._v(\"我们先运行创建的工程，效果如图2-1所示：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(393),alt:\"图2-1\"}})]),t._v(\" \"),n(\"p\",[t._v(\"该计数器示例中，每点击一次右下角带“+”号的悬浮按钮，屏幕中央的数字就会加1。\")]),t._v(\" \"),n(\"p\",[t._v(\"在这个示例中，主要Dart代码是在 \"),n(\"strong\",[t._v(\"lib/main.dart\")]),t._v(\" 文件中，下面是它的源码：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"main\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"runApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyApp\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MaterialApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Flutter Demo'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      theme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ThemeData\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        primarySwatch\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      home\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyHomePage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Flutter Demo Home Page'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyHomePage\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyHomePage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _MyHomePageState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_MyHomePageState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_MyHomePageState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"MyHomePage\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  int _counter \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_incrementCounter\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _counter\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"++\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Scaffold\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      appBar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AppBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      body\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          mainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'You have pushed the button this many times:'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'$_counter'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Theme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"textTheme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"headline4\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      floatingActionButton\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FloatingActionButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _incrementCounter\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        tooltip\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Increment'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"add\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// This trailing comma makes auto-formatting nicer for build methods.\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\")])])]),n(\"h3\",{attrs:{id:\"分析\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#分析\"}},[t._v(\"#\")]),t._v(\" 分析\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"p\",[t._v(\"导入包。\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"import\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'package:flutter/material.dart'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"此行代码作用是导入了Material UI组件库。\"),n(\"a\",{attrs:{href:\"https://material.io/guidelines/\",target:\"_blank\",rel:\"noopener noreferrer\"}},[t._v(\"Material\"),n(\"OutboundLink\")],1),t._v(\"是一种标准的移动端和web端的视觉设计语言， Flutter默认提供了一套丰富的Material风格的UI组件。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"应用入口。\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"main\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"runApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"ul\",[n(\"li\",[t._v(\"与C/C++、Java类似，Flutter 应用中\"),n(\"code\",[t._v(\"main\")]),t._v(\"函数为应用程序的入口。\"),n(\"code\",[t._v(\"main\")]),t._v(\"函数中调用了\"),n(\"code\",[t._v(\"runApp\")]),t._v(\" 方法，它的功能是启动Flutter应用。\"),n(\"code\",[t._v(\"runApp\")]),t._v(\"它接受一个\"),n(\"code\",[t._v(\"Widget\")]),t._v(\"参数，在本示例中它是一个\"),n(\"code\",[t._v(\"MyApp\")]),t._v(\"对象，\"),n(\"code\",[t._v(\"MyApp()\")]),t._v(\"是Flutter应用的根组件。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"main\")]),t._v(\"函数使用了(\"),n(\"code\",[t._v(\"=>\")]),t._v(\")符号，这是Dart中单行函数或方法的简写。\")])])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"应用结构。\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyApp\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MaterialApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//应用名称  \")]),t._v(\"\\n      title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Flutter Demo'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n      theme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ThemeData\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//蓝色主题  \")]),t._v(\"\\n        primarySwatch\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"blue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//应用首页路由  \")]),t._v(\"\\n      home\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyHomePage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Flutter Demo Home Page'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"ul\",[n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"MyApp\")]),t._v(\"类代表Flutter应用，它继承了 \"),n(\"code\",[t._v(\"StatelessWidget\")]),t._v(\"类，这也就意味着应用本身也是一个widget。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"在Flutter中，大多数东西都是widget（后同“组件”或“部件”），包括对齐(alignment)、填充(padding)和布局(layout)等，它们都是以widget的形式提供。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"Flutter在构建页面时，会调用组件的\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法，widget的主要工作是提供一个build()方法来描述如何构建UI界面（通常是通过组合、拼装其它基础widget）。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"MaterialApp\")]),t._v(\" 是Material库中提供的Flutter APP框架，通过它可以设置应用的名称、主题、语言、首页及路由列表等。\"),n(\"code\",[t._v(\"MaterialApp\")]),t._v(\"也是一个widget。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"home\")]),t._v(\" 为Flutter应用的首页，它也是一个widget。\")])])])])]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_2-1-2-首页\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_2-1-2-首页\"}},[t._v(\"#\")]),t._v(\" 2.1.2 首页\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyHomePage\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyHomePage\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" String title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _MyHomePageState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_MyHomePageState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_MyHomePageState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"MyHomePage\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"MyHomePage\")]),t._v(\" 是Flutter应用的首页，它继承自\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"类，表示它是一个有状态的组件（Stateful widget）。关于Stateful widget我们将在第三章“Widget简介”一节仔细介绍，现在我们只需简单认为有状态的组件（Stateful widget） 和无状态的组件（Stateless widget）有两点不同：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"p\",[t._v(\"Stateful widget可以拥有状态，这些状态在widget生命周期中是可以变的，而Stateless widget是不可变的。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"Stateful widget至少由两个类组成：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[t._v(\"一个\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"类。\")]),t._v(\" \"),n(\"li\",[t._v(\"一个 \"),n(\"code\",[t._v(\"State\")]),t._v(\"类； \"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"类本身是不变的，但是\"),n(\"code\",[t._v(\"State\")]),t._v(\"类中持有的状态在widget生命周期中可能会发生变化。\")])]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"_MyHomePageState\")]),t._v(\"类是\"),n(\"code\",[t._v(\"MyHomePage\")]),t._v(\"类对应的状态类。看到这里，读者可能已经发现：和\"),n(\"code\",[t._v(\"MyApp\")]),t._v(\" 类不同， \"),n(\"code\",[t._v(\"MyHomePage\")]),t._v(\"类中并没有\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法，取而代之的是，\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法被挪到了\"),n(\"code\",[t._v(\"_MyHomePageState\")]),t._v(\"方法中，至于为什么这么做，先留个疑问，在分析完完整代码后再来解答。\")])])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"state类\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#state类\"}},[t._v(\"#\")]),t._v(\" State类\")]),t._v(\" \"),n(\"p\",[t._v(\"接下来，我们看看\"),n(\"code\",[t._v(\"_MyHomePageState\")]),t._v(\"中都包含哪些东西：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"p\",[t._v(\"该组件的状态。由于我们只需要维护一个点击次数计数器，所以定义一个\"),n(\"code\",[t._v(\"_counter\")]),t._v(\"状态：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"int _counter \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//用于记录按钮点击的总次数\")]),t._v(\"\\n\")])])]),n(\"p\",[n(\"code\",[t._v(\"_counter\")]),t._v(\" 为保存屏幕右下角带“+”号按钮点击次数的状态。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"设置状态的自增函数。\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_incrementCounter\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n     _counter\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"++\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"当按钮点击时，会调用此函数，该函数的作用是先自增\"),n(\"code\",[t._v(\"_counter\")]),t._v(\"，然后调用\"),n(\"code\",[t._v(\"setState\")]),t._v(\" 方法。\"),n(\"code\",[t._v(\"setState\")]),t._v(\"方法的作用是通知Flutter框架，有状态发生了改变，Flutter框架收到通知后，会执行\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法来根据新的状态重新构建界面， Flutter 对此方法做了优化，使重新执行变的很快，所以你可以重新构建任何需要更新的东西，而无需分别去修改各个widget。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"构建UI界面\")]),t._v(\" \"),n(\"p\",[t._v(\"构建UI界面的逻辑在\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法中，当\"),n(\"code\",[t._v(\"MyHomePage\")]),t._v(\"第一次创建时，\"),n(\"code\",[t._v(\"_MyHomePageState\")]),t._v(\"类会被创建，当初始化完成后，Flutter框架会调用Widget的\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法来构建widget树，最终将widget树渲染到设备屏幕上。所以，我们看看\"),n(\"code\",[t._v(\"_MyHomePageState\")]),t._v(\"的\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法中都干了什么事：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Scaffold\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      appBar\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AppBar\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"title\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      body\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          mainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" MainAxisAlignment\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"center\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'You have pushed the button this many times:'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'$_counter'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Theme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"of\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"textTheme\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"headline4\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      floatingActionButton\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FloatingActionButton\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        onPressed\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _incrementCounter\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        tooltip\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Increment'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Icon\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Icons\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"add\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"ul\",[n(\"li\",[n(\"code\",[t._v(\"Scaffold\")]),t._v(\" 是 Material 库中提供的页面脚手架，它提供了默认的导航栏、标题和包含主屏幕widget树（后同“组件树”或“部件树”）的\"),n(\"code\",[t._v(\"body\")]),t._v(\"属性，组件树可以很复杂。本书后面示例中，路由默认都是通过\"),n(\"code\",[t._v(\"Scaffold\")]),t._v(\"创建。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"body\")]),t._v(\"的组件树中包含了一个\"),n(\"code\",[t._v(\"Center\")]),t._v(\" 组件，\"),n(\"code\",[t._v(\"Center\")]),t._v(\" 可以将其子组件树对齐到屏幕中心。此例中， \"),n(\"code\",[t._v(\"Center\")]),t._v(\" 子组件是一个\"),n(\"code\",[t._v(\"Column\")]),t._v(\" 组件，\"),n(\"code\",[t._v(\"Column\")]),t._v(\"的作用是将其所有子组件沿屏幕垂直方向依次排列； 此例中\"),n(\"code\",[t._v(\"Column\")]),t._v(\"子组件是两个 \"),n(\"code\",[t._v(\"Text\")]),t._v(\"，第一个\"),n(\"code\",[t._v(\"Text\")]),t._v(\" 显示固定文本 “You have pushed the button this many times:”，第二个\"),n(\"code\",[t._v(\"Text\")]),t._v(\" 显示\"),n(\"code\",[t._v(\"_counter\")]),t._v(\"状态的数值。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"floatingActionButton\")]),t._v(\"是页面右下角的带“+”的悬浮按钮，它的\"),n(\"code\",[t._v(\"onPressed\")]),t._v(\"属性接受一个回调函数，代表它被点击后的处理器，本例中直接将\"),n(\"code\",[t._v(\"_incrementCounter\")]),t._v(\"方法作为其处理函数。\")])])])]),t._v(\" \"),n(\"p\",[t._v(\"现在，我们将整个计数器执行流程串起来：当右下角的\"),n(\"code\",[t._v(\"floatingActionButton\")]),t._v(\"按钮被点击之后，会调用\"),n(\"code\",[t._v(\"_incrementCounter\")]),t._v(\"方法。在\"),n(\"code\",[t._v(\"_incrementCounter\")]),t._v(\"方法中，首先会自增\"),n(\"code\",[t._v(\"_counter\")]),t._v(\"计数器（状态），然后\"),n(\"code\",[t._v(\"setState\")]),t._v(\"会通知Flutter框架状态发生变化，接着，Flutter框架会调用\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法以新的状态重新构建UI，最终显示在设备屏幕上。\")]),t._v(\" \"),n(\"h4\",{attrs:{id:\"为什么要将build方法放在state中-而不是放在statefulwidget中\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#为什么要将build方法放在state中-而不是放在statefulwidget中\"}},[t._v(\"#\")]),t._v(\" 为什么要将build方法放在State中，而不是放在StatefulWidget中？\")]),t._v(\" \"),n(\"p\",[t._v(\"现在，我们回答之前提出的问题，为什么\"),n(\"code\",[t._v(\"build()\")]),t._v(\"方法放在State（而不是\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"）中 ？这主要是为了提高开发的灵活性。如果将\"),n(\"code\",[t._v(\"build()\")]),t._v(\"方法放在\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"中则会有两个问题：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[n(\"p\",[t._v(\"状态访问不便\")]),t._v(\" \"),n(\"p\",[t._v(\"试想一下，如果我们的\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"有很多状态，而每次状态改变都要调用\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法，由于状态是保存在State中的，如果\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法在\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"中，那么\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法和状态分别在两个类中，那么构建时读取状态将会很不方便！试想一下，如果真的将\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法放在StatefulWidget中的话，由于构建用户界面过程需要依赖State，所以\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法将必须加一个\"),n(\"code\",[t._v(\"State\")]),t._v(\"参数，大概是下面这样：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" State state\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//state.counter\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"这样的话就只能将State的所有状态声明为公开的状态，这样才能在State类外部访问状态！但是，将状态设置为公开后，状态将不再具有私密性，这就会导致对状态的修改将会变的不可控。但如果将\"),n(\"code\",[t._v(\"build()\")]),t._v(\"方法放在State中的话，构建过程不仅可以直接访问状态，而且也无需公开私有状态，这会非常方便。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[t._v(\"继承\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"不便\")]),t._v(\" \"),n(\"p\",[t._v(\"例如，Flutter中有一个动画widget的基类\"),n(\"code\",[t._v(\"AnimatedWidget\")]),t._v(\"，它继承自\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"类。\"),n(\"code\",[t._v(\"AnimatedWidget\")]),t._v(\"中引入了一个抽象方法\"),n(\"code\",[t._v(\"build(BuildContext context)\")]),t._v(\"，继承自\"),n(\"code\",[t._v(\"AnimatedWidget\")]),t._v(\"的动画widget都要实现这个\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法。现在设想一下，如果\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\" 类中已经有了一个\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法，正如上面所述，此时\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法需要接收一个state对象，这就意味着\"),n(\"code\",[t._v(\"AnimatedWidget\")]),t._v(\"必须将自己的State对象(记为_animatedWidgetState)提供给其子类，因为子类需要在其\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法中调用父类的\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法，代码可能如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"MyAnimationWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"AnimatedWidget\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n    Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" State state\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//由于子类要用到AnimatedWidget的状态对象_animatedWidgetState，\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//所以AnimatedWidget必须通过某种方式将其状态对象_animatedWidgetState\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//暴露给其子类   \")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" _animatedWidgetState\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"这样很显然是不合理的，因为\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[n(\"code\",[t._v(\"AnimatedWidget\")]),t._v(\"的状态对象是\"),n(\"code\",[t._v(\"AnimatedWidget\")]),t._v(\"内部实现细节，不应该暴露给外部。\")]),t._v(\" \"),n(\"li\",[t._v(\"如果要将父类状态暴露给子类，那么必须得有一种传递机制，而做这一套传递机制是无意义的，因为父子类之间状态的传递和子类本身逻辑是无关的。\")])])])]),t._v(\" \"),n(\"p\",[t._v(\"综上所述，可以发现，对于\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"，将\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法放在State中，可以给开发带来很大的灵活性。\")])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/95.bf70fc45.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[95],{464:function(t,a,s){t.exports=s.p+\"assets/img/2-12.eb7484c9.png\"},791:function(t,a,s){\"use strict\";s.r(a);var n=s(45),r=Object(n.a)({},(function(){var t=this,a=t.$createElement,n=t._self._c||a;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_2-6-flutter异常捕获\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_2-6-flutter异常捕获\"}},[t._v(\"#\")]),t._v(\" 2.6 Flutter异常捕获\")]),t._v(\" \"),n(\"p\",[t._v(\"在介绍Flutter异常捕获之前必须先了解一下Dart单线程模型，只有了解了Dart的代码执行流程，我们才能知道该在什么地方去捕获异常。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_2-6-1-dart单线程模型\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_2-6-1-dart单线程模型\"}},[t._v(\"#\")]),t._v(\" 2.6.1 Dart单线程模型\")]),t._v(\" \"),n(\"p\",[t._v(\"在Java和Objective-C（以下简称“OC”）中，如果程序发生异常且没有被捕获，那么程序将会终止，但是这在Dart或JavaScript中则不会！究其原因，这和它们的运行机制有关系。Java和OC都是多线程模型的编程语言，任意一个线程触发异常且该异常未被捕获时，就会导致整个进程退出。但Dart和JavaScript不会，它们都是单线程模型，运行机制很相似(但有区别)，下面我们通过Dart官方提供的一张图来看看Dart大致运行原理：\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:s(464),alt:\"图2-12\"}})]),t._v(\" \"),n(\"p\",[t._v(\"Dart 在单线程中是以消息循环机制来运行的，其中包含两个任务队列，一个是“微任务队列”  \"),n(\"strong\",[t._v(\"microtask queue\")]),t._v(\"，另一个叫做“事件队列” \"),n(\"strong\",[t._v(\"event queue\")]),t._v(\"。从图中可以发现，微任务队列的执行优先级高于事件队列。\")]),t._v(\" \"),n(\"p\",[t._v(\"现在我们来介绍一下Dart线程运行过程，如上图中所示，入口函数 main() 执行完后，消息循环机制便启动了。首先会按照先进先出的顺序逐个执行微任务队列中的任务，事件任务执行完毕后程序便会退出，但是，在事件任务执行的过程中也可以插入新的微任务和事件任务，在这种情况下，整个线程的执行过程便是一直在循环，不会退出，而Flutter中，主线程的执行过程正是如此，永不终止。\")]),t._v(\" \"),n(\"p\",[t._v(\"在Dart中，所有的外部事件任务都在事件队列中，如IO、计时器、点击、以及绘制事件等，而微任务通常来源于Dart内部，并且微任务非常少，之所以如此，是因为微任务队列优先级高，如果微任务太多，执行时间总和就越久，事件队列任务的延迟也就越久，对于GUI应用来说最直观的表现就是比较卡，所以必须得保证微任务队列不会太长。值得注意的是，我们可以通过\"),n(\"code\",[t._v(\"Future.microtask(…)\")]),t._v(\"方法向微任务队列插入一个任务。\")]),t._v(\" \"),n(\"p\",[t._v(\"在事件循环中，当某个任务发生异常并没有被捕获时，程序并不会退出，而直接导致的结果是\"),n(\"strong\",[t._v(\"当前任务\")]),t._v(\"的后续代码就不会被执行了，也就是说一个任务中的异常是不会影响其它任务执行的。\")]),t._v(\" \"),n(\"h2\",{attrs:{id:\"_2-6-2-flutter异常捕获\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_2-6-2-flutter异常捕获\"}},[t._v(\"#\")]),t._v(\" 2.6.2 Flutter异常捕获\")]),t._v(\" \"),n(\"p\",[t._v(\"Dart中可以通过\"),n(\"code\",[t._v(\"try/catch/finally\")]),t._v(\"来捕获代码块异常，这个和其它编程语言类似，如果读者不清楚，可以查看Dart语言文档，不再赘述，下面我们看看Flutter中的异常捕获。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"flutter框架异常捕获\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#flutter框架异常捕获\"}},[t._v(\"#\")]),t._v(\" Flutter框架异常捕获\")]),t._v(\" \"),n(\"p\",[t._v(\"Flutter 框架为我们在很多关键的方法进行了异常捕获。这里举一个例子，当我们布局发生越界或不合规范时，Flutter就会自动弹出一个错误界面，这是因为Flutter已经在执行build方法时添加了异常捕获，最终的源码如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"performRebuild\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//执行build方法  \")]),t._v(\"\\n    built \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"catch\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" stack\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 有异常时则弹出错误提示  \")]),t._v(\"\\n    built \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" ErrorWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"builder\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_debugReportException\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'building $this'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" stack\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"      \\n\")])])]),n(\"p\",[t._v(\"可以看到，在发生异常时，Flutter默认的处理方式是弹一个ErrorWidget，但如果我们想自己捕获异常并上报到报警平台的话应该怎么做？我们进入\"),n(\"code\",[t._v(\"_debugReportException()\")]),t._v(\"方法看看：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"FlutterErrorDetails \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_debugReportException\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n  String context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"dynamic\")]),t._v(\" exception\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  StackTrace stack\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  InformationCollector informationCollector\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//构建错误详情对象  \")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" FlutterErrorDetails details \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"FlutterErrorDetails\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    exception\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" exception\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    stack\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" stack\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"library\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'widgets library'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    informationCollector\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" informationCollector\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//报告错误 \")]),t._v(\"\\n  FlutterError\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"reportError\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们发现，错误是通过\"),n(\"code\",[t._v(\"FlutterError.reportError\")]),t._v(\"方法上报的，继续跟踪：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"static\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"reportError\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"FlutterErrorDetails details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"if\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"onError \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onError\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//调用了onError回调\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"我们发现\"),n(\"code\",[t._v(\"onError\")]),t._v(\"是\"),n(\"code\",[t._v(\"FlutterError\")]),t._v(\"的一个静态属性，它有一个默认的处理方法 \"),n(\"code\",[t._v(\"dumpErrorToConsole\")]),t._v(\"，到这里就清晰了，如果我们想自己上报异常，只需要提供一个自定义的错误处理回调即可，如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"main\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  FlutterError\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onError \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"FlutterErrorDetails details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"reportError\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"这样我们就可以处理那些Flutter为我们捕获的异常了，接下来我们看看如何捕获其它异常。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"其它异常捕获与日志收集\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#其它异常捕获与日志收集\"}},[t._v(\"#\")]),t._v(\" 其它异常捕获与日志收集\")]),t._v(\" \"),n(\"p\",[t._v(\"在Flutter中，还有一些Flutter没有为我们捕获的异常，如调用空对象方法异常、Future中的异常。在Dart中，异常分两类：同步异常和异步异常，同步异常可以通过\"),n(\"code\",[t._v(\"try/catch\")]),t._v(\"捕获，而异步异常则比较麻烦，如下面的代码是捕获不了\"),n(\"code\",[t._v(\"Future\")]),t._v(\"的异常的：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"try\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Future\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"delayed\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Duration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"seconds\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" Future\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"error\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"xxx\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"catch\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"Dart中有一个\"),n(\"code\",[t._v(\"runZoned(...)\")]),t._v(\" 方法，可以给执行对象指定一个Zone。Zone表示一个代码执行的环境范围，为了方便理解，读者可以将Zone类比为一个代码执行沙箱，不同沙箱的之间是隔离的，沙箱可以捕获、拦截或修改一些代码行为，如Zone中可以捕获日志输出、Timer创建、微任务调度的行为，同时Zone也可以捕获所有未处理的异常。下面我们看看\"),n(\"code\",[t._v(\"runZoned(...)\")]),t._v(\"方法定义：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[t._v(\"R runZoned\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"R\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"R \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"body\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    Map zoneValues\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n    ZoneSpecification zoneSpecification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"Function\")]),t._v(\" onError\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \\n\")])])]),n(\"ul\",[n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"zoneValues\")]),t._v(\": Zone 的私有数据，可以通过实例\"),n(\"code\",[t._v(\"zone[key]\")]),t._v(\"获取，可以理解为每个“沙箱”的私有数据。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"zoneSpecification\")]),t._v(\"：Zone的一些配置，可以自定义一些代码行为，比如拦截日志输出行为等，举个例子：\")]),t._v(\" \"),n(\"p\",[t._v(\"下面是拦截应用中所有调用\"),n(\"code\",[t._v(\"print\")]),t._v(\"输出日志的行为。\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"main\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"runZoned\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"runApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" zoneSpecification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ZoneSpecification\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      print\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Zone self\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" ZoneDelegate parent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Zone zone\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" String line\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        parent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"zone\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Intercepted: $line\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"这样一来，我们APP中所有调用\"),n(\"code\",[t._v(\"print\")]),t._v(\"方法输出日志的行为都会被拦截，通过这种方式，我们也可以在应用中记录日志，等到应用触发未捕获的异常时，将异常信息和日志统一上报。ZoneSpecification还可以自定义一些其他行为，读者可以查看API文档。\")])]),t._v(\" \"),n(\"li\",[n(\"p\",[n(\"code\",[t._v(\"onError\")]),t._v(\"：Zone中未捕获异常处理回调，如果开发者提供了onError回调或者通过\"),n(\"code\",[t._v(\"ZoneSpecification.handleUncaughtError\")]),t._v(\"指定了错误处理回调，那么这个zone将会变成一个error-zone，该error-zone中发生未捕获异常(无论同步还是异步)时都会调用开发者提供的回调，如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"runZoned\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"runApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" onError\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Object obj\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" StackTrace stack\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" details\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"makeDetails\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"obj\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"stack\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"reportError\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"这样一来，结合上面的\"),n(\"code\",[t._v(\"FlutterError.onError\")]),t._v(\"我们就可以捕获我们Flutter应用中全部错误了！需要注意的是，error-zone内部发生的错误是不会跨越当前error-zone的边界的，如果想跨越error-zone边界去捕获异常，可以通过共同的“源”zone来捕获，如：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" future \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Future\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"value\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"499\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"runZoned\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\t\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" future2 \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" future\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"then\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"_\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"throw\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"error in first error-zone\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\t\"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"runZoned\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n\\t\\t\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" future3 \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" future2\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"catchError\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Never reached!\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\t\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" onError\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"unused error handler\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" onError\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"e\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"print\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"catches error of first error-zone.\"')]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n\")])])])])]),t._v(\" \"),n(\"h3\",{attrs:{id:\"总结\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),n(\"p\",[t._v(\"我们最终的异常捕获和上报代码大致如下：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"collectLog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"String line\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//收集日志\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"reportErrorAndLog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"FlutterErrorDetails details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//上报错误和日志逻辑\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\nFlutterErrorDetails \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"makeDetails\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Object obj\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" StackTrace stack\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 构建错误信息\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"main\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  FlutterError\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onError \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"FlutterErrorDetails details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"reportErrorAndLog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"runZoned\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"runApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"MyApp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    zoneSpecification\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"ZoneSpecification\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      print\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Zone self\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" ZoneDelegate parent\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" Zone zone\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" String line\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"collectLog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"line\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 收集日志\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    onError\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"Object obj\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" StackTrace stack\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"var\")]),t._v(\" details \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"makeDetails\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"obj\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" stack\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"reportErrorAndLog\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])])])}),[],!1,null,null,null);a.default=r.exports}}]);"
  },
  {
    "path": "docs/assets/js/96.b382caf8.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[96],{432:function(t,s,a){t.exports=a.p+\"assets/img/3-23.a249df4c.png\"},778:function(t,s,a){\"use strict\";a.r(s);var n=a(45),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_3-6-单选开关和复选框\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-6-单选开关和复选框\"}},[t._v(\"#\")]),t._v(\" 3.6 单选开关和复选框\")]),t._v(\" \"),n(\"p\",[t._v(\"Material 组件库中提供了Material风格的单选开关\"),n(\"code\",[t._v(\"Switch\")]),t._v(\"和复选框\"),n(\"code\",[t._v(\"Checkbox\")]),t._v(\"，虽然它们都是继承自\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"，但它们本身不会保存当前选中状态，选中状态都是由父组件来管理的。当\"),n(\"code\",[t._v(\"Switch\")]),t._v(\"或\"),n(\"code\",[t._v(\"Checkbox\")]),t._v(\"被点击时，会触发它们的\"),n(\"code\",[t._v(\"onChanged\")]),t._v(\"回调，我们可以在此回调中处理选中状态改变逻辑。下面看一个简单的例子：\")]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"SwitchAndCheckBoxTestRoute\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _SwitchAndCheckBoxTestRouteState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_SwitchAndCheckBoxTestRouteState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_SwitchAndCheckBoxTestRouteState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"SwitchAndCheckBoxTestRoute\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  bool _switchSelected\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//维护单选开关状态\")]),t._v(\"\\n  bool _checkboxSelected\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//维护复选框状态\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      children\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Switch\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _switchSelected\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//当前状态\")]),t._v(\"\\n          onChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//重新构建页面  \")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              _switchSelected\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Checkbox\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _checkboxSelected\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          activeColor\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//选中时的颜色\")]),t._v(\"\\n          onChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n              _checkboxSelected\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\"value\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n            \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"上面代码中，由于需要维护\"),n(\"code\",[t._v(\"Switch\")]),t._v(\"和\"),n(\"code\",[t._v(\"Checkbox\")]),t._v(\"的选中状态，所以\"),n(\"code\",[t._v(\"SwitchAndCheckBoxTestRoute\")]),t._v(\"继承自\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\" 。在其\"),n(\"code\",[t._v(\"build\")]),t._v(\"方法中分别构建了一个\"),n(\"code\",[t._v(\"Switch\")]),t._v(\"和\"),n(\"code\",[t._v(\"Checkbox\")]),t._v(\"，初始状态都为选中状态，当用户点击时，会将状态置反，然后回调用\"),n(\"code\",[t._v(\"setState()\")]),t._v(\"通知Flutter framework重新构建UI。\")]),t._v(\" \"),n(\"p\",[n(\"img\",{attrs:{src:a(432),alt:\"图3-23\"}})]),t._v(\" \"),n(\"h3\",{attrs:{id:\"属性及外观\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#属性及外观\"}},[t._v(\"#\")]),t._v(\" 属性及外观\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"Switch\")]),t._v(\"和\"),n(\"code\",[t._v(\"Checkbox\")]),t._v(\"属性比较简单，读者可以查看API文档，它们都有一个\"),n(\"code\",[t._v(\"activeColor\")]),t._v(\"属性，用于设置激活态的颜色。至于大小，到目前为止，\"),n(\"code\",[t._v(\"Checkbox\")]),t._v(\"的大小是固定的，无法自定义，而\"),n(\"code\",[t._v(\"Switch\")]),t._v(\"只能定义宽度，高度也是固定的。值得一提的是\"),n(\"code\",[t._v(\"Checkbox\")]),t._v(\"有一个属性\"),n(\"code\",[t._v(\"tristate\")]),t._v(\" ，表示是否为三态，其默认值为\"),n(\"code\",[t._v(\"false\")]),t._v(\" ，这时Checkbox有两种状态即“选中”和“不选中”，对应的value值为\"),n(\"code\",[t._v(\"true\")]),t._v(\"和\"),n(\"code\",[t._v(\"false\")]),t._v(\" 。如果\"),n(\"code\",[t._v(\"tristate\")]),t._v(\"值为\"),n(\"code\",[t._v(\"true\")]),t._v(\"时，value的值会增加一个状态\"),n(\"code\",[t._v(\"null\")]),t._v(\"，读者可以自行了解。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"总结\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#总结\"}},[t._v(\"#\")]),t._v(\" 总结\")]),t._v(\" \"),n(\"p\",[t._v(\"通过\"),n(\"code\",[t._v(\"Switch\")]),t._v(\"和\"),n(\"code\",[t._v(\"Checkbox\")]),t._v(\"我们可以看到，虽然它们本身是与状态（是否选中）关联的，但它们却不是自己来维护状态，而是需要父组件来管理状态，然后当用户点击时，再通过事件通知给父组件，这样是合理的，因为\"),n(\"code\",[t._v(\"Switch\")]),t._v(\"和\"),n(\"code\",[t._v(\"Checkbox\")]),t._v(\"是否选中本就和用户数据关联，而这些用户数据也不可能是它们的私有状态。我们在自定义组件时也应该思考一下哪种状态的管理方式最为合理。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/97.14909b27.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[97],{433:function(t,a,s){t.exports=s.p+\"assets/img/3-4.8e70e140.png\"},779:function(t,a,s){\"use strict\";s.r(a);var n=s(45),e=Object(n.a)({},(function(){var t=this,a=t.$createElement,n=t._self._c||a;return n(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[n(\"h1\",{attrs:{id:\"_3-2-状态管理\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-2-状态管理\"}},[t._v(\"#\")]),t._v(\" 3.2 状态管理\")]),t._v(\" \"),n(\"p\",[t._v(\"响应式的编程框架中都会有一个永恒的主题——“状态(State)管理”，无论是在React/Vue（两者都是支持响应式编程的Web开发框架）还是Flutter中，他们讨论的问题和解决的思想都是一致的。所以，如果你对React/Vue的状态管理有了解，可以跳过本节。言归正传，我们想一个问题，\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"的状态应该被谁管理？Widget本身？父Widget？都会？还是另一个对象？答案是取决于实际情况！以下是管理状态的最常见的方法：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[t._v(\"Widget管理自己的状态。\")]),t._v(\" \"),n(\"li\",[t._v(\"Widget管理子Widget状态。\")]),t._v(\" \"),n(\"li\",[t._v(\"混合管理（父Widget和子Widget都管理状态）。\")])]),t._v(\" \"),n(\"p\",[t._v(\"如何决定使用哪种管理方法？下面是官方给出的一些原则可以帮助你做决定：\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[t._v(\"如果状态是用户数据，如复选框的选中状态、滑块的位置，则该状态最好由父Widget管理。\")]),t._v(\" \"),n(\"li\",[t._v(\"如果状态是有关界面外观效果的，例如颜色、动画，那么状态最好由Widget本身来管理。\")]),t._v(\" \"),n(\"li\",[t._v(\"如果某一个状态是不同Widget共享的则最好由它们共同的父Widget管理。\")])]),t._v(\" \"),n(\"p\",[t._v(\"在Widget内部管理状态封装性会好一些，而在父Widget中管理会比较灵活。有些时候，如果不确定到底该怎么管理状态，那么推荐的首选是在父widget中管理（灵活会显得更重要一些）。\")]),t._v(\" \"),n(\"p\",[t._v(\"接下来，我们将通过创建三个简单示例TapboxA、TapboxB和TapboxC来说明管理状态的不同方式。 这些例子功能是相似的 ——创建一个盒子，当点击它时，盒子背景会在绿色与灰色之间切换。状态 \"),n(\"code\",[t._v(\"_active\")]),t._v(\"确定颜色：绿色为\"),n(\"code\",[t._v(\"true\")]),t._v(\" ，灰色为\"),n(\"code\",[t._v(\"false\")]),t._v(\"，如图3-4所示。\"),n(\"img\",{attrs:{src:s(433),alt:\"a large grey box with the text, 'Inactive'\"}})]),t._v(\" \"),n(\"p\",[t._v(\"下面的例子将使用\"),n(\"code\",[t._v(\"GestureDetector\")]),t._v(\"来识别点击事件，关于该\"),n(\"code\",[t._v(\"GestureDetector\")]),t._v(\"的详细内容我们将在后面“事件处理”一章中介绍。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"_3-2-1-widget管理自身状态\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-2-1-widget管理自身状态\"}},[t._v(\"#\")]),t._v(\" 3.2.1 Widget管理自身状态\")]),t._v(\" \"),n(\"p\",[t._v(\"_TapboxAState 类:\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[t._v(\"管理TapboxA的状态。\")]),t._v(\" \"),n(\"li\",[t._v(\"定义\"),n(\"code\",[t._v(\"_active\")]),t._v(\"：确定盒子的当前颜色的布尔值。\")]),t._v(\" \"),n(\"li\",[t._v(\"定义\"),n(\"code\",[t._v(\"_handleTap()\")]),t._v(\"函数，该函数在点击该盒子时更新\"),n(\"code\",[t._v(\"_active\")]),t._v(\"，并调用\"),n(\"code\",[t._v(\"setState()\")]),t._v(\"更新UI。\")]),t._v(\" \"),n(\"li\",[t._v(\"实现widget的所有交互式行为。\")])]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// TapboxA 管理自身状态.\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//------------------------- TapboxA ----------------------------------\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TapboxA\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TapboxA\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _TapboxAState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_TapboxAState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_TapboxAState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"TapboxA\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  bool _active \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_handleTap\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _active \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"_active\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"GestureDetector\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      onTap\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _handleTap\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            _active \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Active'\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Inactive'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"fontSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"32.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        decoration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"BoxDecoration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _active \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"lightGreen\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"700\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"600\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\")])])]),n(\"h3\",{attrs:{id:\"_3-2-2-父widget管理子widget的状态\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-2-2-父widget管理子widget的状态\"}},[t._v(\"#\")]),t._v(\" 3.2.2 父Widget管理子Widget的状态\")]),t._v(\" \"),n(\"p\",[t._v(\"对于父Widget来说，管理状态并告诉其子Widget何时更新通常是比较好的方式。 例如，\"),n(\"code\",[t._v(\"IconButton\")]),t._v(\"是一个图标按钮，但它是一个无状态的Widget，因为我们认为父Widget需要知道该按钮是否被点击来采取相应的处理。\")]),t._v(\" \"),n(\"p\",[t._v(\"在以下示例中，TapboxB通过回调将其状态导出到其父组件，状态由父组件管理，因此它的父组件为\"),n(\"code\",[t._v(\"StatefulWidget\")]),t._v(\"。但是由于TapboxB不管理任何状态，所以\"),n(\"code\",[t._v(\"TapboxB\")]),t._v(\"为\"),n(\"code\",[t._v(\"StatelessWidget\")]),t._v(\"。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"ParentWidgetState\")]),t._v(\" 类:\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[t._v(\"为TapboxB 管理\"),n(\"code\",[t._v(\"_active\")]),t._v(\"状态。\")]),t._v(\" \"),n(\"li\",[t._v(\"实现\"),n(\"code\",[t._v(\"_handleTapboxChanged()\")]),t._v(\"，当盒子被点击时调用的方法。\")]),t._v(\" \"),n(\"li\",[t._v(\"当状态改变时，调用\"),n(\"code\",[t._v(\"setState()\")]),t._v(\"更新UI。\")])]),t._v(\" \"),n(\"p\",[t._v(\"TapboxB 类:\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[t._v(\"继承\"),n(\"code\",[t._v(\"StatelessWidget\")]),t._v(\"类，因为所有状态都由其父组件处理。\")]),t._v(\" \"),n(\"li\",[t._v(\"当检测到点击时，它会通知父组件。\")])]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// ParentWidget 为 TapboxB 管理状态.\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//------------------------ ParentWidget --------------------------------\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ParentWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _ParentWidgetState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_ParentWidgetState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_ParentWidgetState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ParentWidget\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  bool _active \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_handleTapboxChanged\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"bool newValue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _active \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" newValue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TapboxB\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        active\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _active\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        onChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _handleTapboxChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//------------------------- TapboxB ----------------------------------\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TapboxB\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TapboxB\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"active\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" bool active\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" ValueChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"bool\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" onChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_handleTap\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onChanged\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"active\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"GestureDetector\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      onTap\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _handleTap\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            active \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Active'\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Inactive'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"fontSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"32.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        decoration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"BoxDecoration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" active \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"lightGreen\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"700\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"600\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"h3\",{attrs:{id:\"_3-2-3-混合状态管理\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-2-3-混合状态管理\"}},[t._v(\"#\")]),t._v(\" 3.2.3 混合状态管理\")]),t._v(\" \"),n(\"p\",[t._v(\"对于一些组件来说，混合管理的方式会非常有用。在这种情况下，组件自身管理一些内部状态，而父组件管理一些其他外部状态。\")]),t._v(\" \"),n(\"p\",[t._v(\"在下面TapboxC示例中，手指按下时，盒子的周围会出现一个深绿色的边框，抬起时，边框消失。点击完成后，盒子的颜色改变。 TapboxC将其\"),n(\"code\",[t._v(\"_active\")]),t._v(\"状态导出到其父组件中，但在内部管理其\"),n(\"code\",[t._v(\"_highlight\")]),t._v(\"状态。这个例子有两个状态对象\"),n(\"code\",[t._v(\"_ParentWidgetState\")]),t._v(\"和\"),n(\"code\",[t._v(\"_TapboxCState\")]),t._v(\"。\")]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"_ParentWidgetStateC\")]),t._v(\"类:\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[t._v(\"管理\"),n(\"code\",[t._v(\"_active\")]),t._v(\" 状态。\")]),t._v(\" \"),n(\"li\",[t._v(\"实现 \"),n(\"code\",[t._v(\"_handleTapboxChanged()\")]),t._v(\" ，当盒子被点击时调用。\")]),t._v(\" \"),n(\"li\",[t._v(\"当点击盒子并且\"),n(\"code\",[t._v(\"_active\")]),t._v(\"状态改变时调用\"),n(\"code\",[t._v(\"setState()\")]),t._v(\"更新UI。\")])]),t._v(\" \"),n(\"p\",[n(\"code\",[t._v(\"_TapboxCState\")]),t._v(\" 对象:\")]),t._v(\" \"),n(\"ul\",[n(\"li\",[t._v(\"管理\"),n(\"code\",[t._v(\"_highlight\")]),t._v(\" 状态。\")]),t._v(\" \"),n(\"li\",[n(\"code\",[t._v(\"GestureDetector\")]),t._v(\"监听所有tap事件。当用户点下时，它添加高亮（深绿色边框）；当用户释放时，会移除高亮。\")]),t._v(\" \"),n(\"li\",[t._v(\"当按下、抬起、或者取消点击时更新\"),n(\"code\",[t._v(\"_highlight\")]),t._v(\"状态，调用\"),n(\"code\",[t._v(\"setState()\")]),t._v(\"更新UI。\")]),t._v(\" \"),n(\"li\",[t._v(\"当点击时，将状态的改变传递给父组件。\")])]),t._v(\" \"),n(\"div\",{staticClass:\"language-dart extra-class\"},[n(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[n(\"code\",[n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//---------------------------- ParentWidget ----------------------------\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"ParentWidgetC\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _ParentWidgetCState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_ParentWidgetCState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_ParentWidgetCState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"ParentWidgetC\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  bool _active \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_handleTapboxChanged\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"bool newValue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _active \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" newValue\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TapboxC\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        active\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _active\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        onChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _handleTapboxChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//----------------------------- TapboxC ------------------------------\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TapboxC\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatefulWidget\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TapboxC\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"Key key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"active\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"onChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" bool active\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" ValueChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"bool\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" onChanged\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  _TapboxCState \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"createState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_TapboxCState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"_TapboxCState\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"State\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"TapboxC\"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  bool _highlight \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_handleTapDown\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"TapDownDetails details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _highlight \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"true\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_handleTapUp\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"TapUpDetails details\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _highlight \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_handleTapCancel\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"setState\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n      _highlight \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token boolean\"}},[t._v(\"false\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"void\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"_handleTap\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"onChanged\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!\")]),t._v(\"widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"active\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),n(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 在按下时添加绿色边框，当抬起时，取消高亮  \")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"GestureDetector\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      onTapDown\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _handleTapDown\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 处理按下事件\")]),t._v(\"\\n      onTapUp\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _handleTapUp\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"// 处理抬起事件\")]),t._v(\"\\n      onTap\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _handleTap\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      onTapCancel\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _handleTapCancel\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Container\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n        child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Center\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          child\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Text\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"active \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Active'\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v(\"'Inactive'\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              style\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"TextStyle\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"fontSize\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"32.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        height\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"200.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        decoration\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"BoxDecoration\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" widget\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"active \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"lightGreen\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"700\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"grey\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"600\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          border\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" _highlight\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"?\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"new\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Border\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"all\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  color\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"teal\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"700\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  width\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"10.0\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n              \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),n(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),n(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),n(\"p\",[t._v(\"另一种实现可能会将高亮状态导出到父组件，但同时保持\"),n(\"code\",[t._v(\"_active\")]),t._v(\"状态为内部状态，但如果你要将该TapBox给其它人使用，可能没有什么意义。 开发人员只会关心该框是否处于Active状态，而不在乎高亮显示是如何管理的，所以应该让TapBox内部处理这些细节。\")]),t._v(\" \"),n(\"h3\",{attrs:{id:\"_3-2-4-全局状态管理\"}},[n(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_3-2-4-全局状态管理\"}},[t._v(\"#\")]),t._v(\" 3.2.4 全局状态管理\")]),t._v(\" \"),n(\"p\",[t._v(\"当应用中需要一些跨组件（包括跨路由）的状态需要同步时，上面介绍的方法便很难胜任了。比如，我们有一个设置页，里面可以设置应用的语言，我们为了让设置实时生效，我们期望在语言状态发生改变时，APP中依赖应用语言的组件能够重新build一下，但这些依赖应用语言的组件和设置页并不在一起，所以这种情况用上面的方法很难管理。这时，正确的做法是通过一个全局状态管理器来处理这种相距较远的组件之间的通信。目前主要有两种办法：\")]),t._v(\" \"),n(\"ol\",[n(\"li\",[t._v(\"实现一个全局的事件总线，将语言状态改变对应为一个事件，然后在APP中依赖应用语言的组件的\"),n(\"code\",[t._v(\"initState\")]),t._v(\" 方法中订阅语言改变的事件。当用户在设置页切换语言后，我们发布语言改变事件，而订阅了此事件的组件就会收到通知，收到通知后调用\"),n(\"code\",[t._v(\"setState(...)\")]),t._v(\"方法重新\"),n(\"code\",[t._v(\"build\")]),t._v(\"一下自身即可。\")]),t._v(\" \"),n(\"li\",[t._v(\"使用一些专门用于状态管理的包，如Provider、Redux，读者可以在pub上查看其详细信息。\")])]),t._v(\" \"),n(\"p\",[t._v('本书将在\"功能型组件\"一章中介绍Provider包的实现原理及用法，同时也将会在\"事件处理与通知\"一章中实现一个全局事件总线，读者有需要可以直接翻看。')])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/98.8023c3ac.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[98],{443:function(t,s,n){t.exports=n.p+\"assets/img/4-5.67110f64.png\"},782:function(t,s,n){\"use strict\";n.r(s);var a=n(45),e=Object(a.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[a(\"h1\",{attrs:{id:\"_4-3-弹性布局-flex\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_4-3-弹性布局-flex\"}},[t._v(\"#\")]),t._v(\" 4.3 弹性布局（Flex）\")]),t._v(\" \"),a(\"p\",[t._v(\"弹性布局允许子组件按照一定比例来分配父容器空间。弹性布局的概念在其它UI系统中也都存在，如H5中的弹性盒子布局，Android中的\"),a(\"code\",[t._v(\"FlexboxLayout\")]),t._v(\"等。Flutter中的弹性布局主要通过\"),a(\"code\",[t._v(\"Flex\")]),t._v(\"和\"),a(\"code\",[t._v(\"Expanded\")]),t._v(\"来配合实现。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"flex\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#flex\"}},[t._v(\"#\")]),t._v(\" Flex\")]),t._v(\" \"),a(\"p\",[a(\"code\",[t._v(\"Flex\")]),t._v(\"组件可以沿着水平或垂直方向排列子组件，如果你知道主轴方向，使用\"),a(\"code\",[t._v(\"Row\")]),t._v(\"或\"),a(\"code\",[t._v(\"Column\")]),t._v(\"会方便一些，因为\"),a(\"code\",[t._v(\"Row\")]),t._v(\"和\"),a(\"code\",[t._v(\"Column\")]),t._v(\"都继承自\"),a(\"code\",[t._v(\"Flex\")]),t._v(\"，参数基本相同，所以能使用Flex的地方基本上都可以使用\"),a(\"code\",[t._v(\"Row\")]),t._v(\"或\"),a(\"code\",[t._v(\"Column\")]),t._v(\"。\"),a(\"code\",[t._v(\"Flex\")]),t._v(\"本身功能是很强大的，它也可以和\"),a(\"code\",[t._v(\"Expanded\")]),t._v(\"组件配合实现弹性布局。接下来我们只讨论\"),a(\"code\",[t._v(\"Flex\")]),t._v(\"和弹性布局相关的属性(其它属性已经在介绍\"),a(\"code\",[t._v(\"Row\")]),t._v(\"和\"),a(\"code\",[t._v(\"Column\")]),t._v(\"时介绍过了)。\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Flex\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"direction\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//弹性布局的方向, Row默认为水平方向，Column默认为垂直方向\")]),t._v(\"\\n  List\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" children \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[a(\"code\",[t._v(\"Flex\")]),t._v(\"继承自\"),a(\"code\",[t._v(\"MultiChildRenderObjectWidget\")]),t._v(\"，对应的\"),a(\"code\",[t._v(\"RenderObject\")]),t._v(\"为\"),a(\"code\",[t._v(\"RenderFlex\")]),t._v(\"，\"),a(\"code\",[t._v(\"RenderFlex\")]),t._v(\"中实现了其布局算法。\")]),t._v(\" \"),a(\"h3\",{attrs:{id:\"expanded\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#expanded\"}},[t._v(\"#\")]),t._v(\" Expanded\")]),t._v(\" \"),a(\"p\",[t._v(\"可以按比例“扩伸” \"),a(\"code\",[t._v(\"Row\")]),t._v(\"、\"),a(\"code\",[t._v(\"Column\")]),t._v(\"和\"),a(\"code\",[t._v(\"Flex\")]),t._v(\"子组件所占用的空间。\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Expanded\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  int flex \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@required\")]),t._v(\" Widget child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),a(\"p\",[a(\"code\",[t._v(\"flex\")]),t._v(\"参数为弹性系数，如果为0或\"),a(\"code\",[t._v(\"null\")]),t._v(\"，则\"),a(\"code\",[t._v(\"child\")]),t._v(\"是没有弹性的，即不会被扩伸占用的空间。如果大于0，所有的\"),a(\"code\",[t._v(\"Expanded\")]),t._v(\"按照其flex的比例来分割主轴的全部空闲空间。下面我们看一个例子：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"FlexLayoutTestRoute\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Column\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//Flex的两个子widget按1：2来占据水平空间  \")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Flex\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          direction\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Axis\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"horizontal\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Expanded\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              flex\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"30.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Expanded\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              flex\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"30.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"green\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n          padding\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" EdgeInsets\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"only\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"top\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"20.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"SizedBox\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"100.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//Flex的三个子widget，在垂直方向按2：1：1来占用100像素的空间  \")]),t._v(\"\\n            child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Flex\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n              direction\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Axis\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"vertical\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              children\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"Widget\"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Expanded\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  flex\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"30.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Spacer\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  flex\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Expanded\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                  flex\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Container\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n                    height\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"30.0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                    color\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"green\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n                \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n              \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n          \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n        \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"p\",[t._v(\"运行效果如图4-5所示：\")]),t._v(\" \"),a(\"p\",[a(\"img\",{attrs:{src:n(443),alt:\"弹性布局\"}})]),t._v(\" \"),a(\"p\",[t._v(\"示例中的\"),a(\"code\",[t._v(\"Spacer\")]),t._v(\"的功能是占用指定比例的空间，实际上它只是\"),a(\"code\",[t._v(\"Expanded\")]),t._v(\"的一个包装类，\"),a(\"code\",[t._v(\"Spacer\")]),t._v(\"的源码如下：\")]),t._v(\" \"),a(\"div\",{staticClass:\"language-dart extra-class\"},[a(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[a(\"code\",[a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"class\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"Spacer\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"extends\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token class-name\"}},[t._v(\"StatelessWidget\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Spacer\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"Key key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"this\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"flex \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"1\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"flex \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"!=\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"null\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"assert\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"flex \"),a(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"0\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"super\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" key\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"final\")]),t._v(\" int flex\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token metadata symbol\"}},[t._v(\"@override\")]),t._v(\"\\n  Widget \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"build\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"BuildContext context\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"return\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Expanded\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      flex\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" flex\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n      child\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),a(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" SizedBox\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),a(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"shrink\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\";\")]),t._v(\"\\n  \"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\"),a(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),t._v(\"\\n\")])])]),a(\"h3\",{attrs:{id:\"小结\"}},[a(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#小结\"}},[t._v(\"#\")]),t._v(\" 小结\")]),t._v(\" \"),a(\"p\",[t._v(\"弹性布局比较简单，唯一需要注意的就是\"),a(\"code\",[t._v(\"Row\")]),t._v(\"、\"),a(\"code\",[t._v(\"Column\")]),t._v(\"以及\"),a(\"code\",[t._v(\"Flex\")]),t._v(\"的关系。\")])])}),[],!1,null,null,null);s.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/99.3db30893.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[99],{465:function(t,a,n){t.exports=n.p+\"assets/img/5-9.47017753.png\"},792:function(t,a,n){\"use strict\";n.r(a);var s=n(45),e=Object(s.a)({},(function(){var t=this,a=t.$createElement,s=t._self._c||a;return s(\"ContentSlotsDistributor\",{attrs:{\"slot-key\":t.$parent.slotKey}},[s(\"h1\",{attrs:{id:\"_5-3-装饰容器decoratedbox\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#_5-3-装饰容器decoratedbox\"}},[t._v(\"#\")]),t._v(\" 5.3 装饰容器DecoratedBox\")]),t._v(\" \"),s(\"p\",[s(\"code\",[t._v(\"DecoratedBox\")]),t._v(\"可以在其子组件绘制前(或后)绘制一些装饰（Decoration），如背景、边框、渐变等。\"),s(\"code\",[t._v(\"DecoratedBox\")]),t._v(\"定义如下：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token keyword\"}},[t._v(\"const\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecoratedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  Decoration decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  DecorationPosition position \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" DecorationPosition\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"background\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  Widget child\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),s(\"ul\",[s(\"li\",[s(\"code\",[t._v(\"decoration\")]),t._v(\"：代表将要绘制的装饰，它的类型为\"),s(\"code\",[t._v(\"Decoration\")]),t._v(\"。\"),s(\"code\",[t._v(\"Decoration\")]),t._v(\"是一个抽象类，它定义了一个接口 \"),s(\"code\",[t._v(\"createBoxPainter()\")]),t._v(\"，子类的主要职责是需要通过实现它来创建一个画笔，该画笔用于绘制装饰。\")]),t._v(\" \"),s(\"li\",[s(\"code\",[t._v(\"position\")]),t._v(\"：此属性决定在哪里绘制\"),s(\"code\",[t._v(\"Decoration\")]),t._v(\"，它接收\"),s(\"code\",[t._v(\"DecorationPosition\")]),t._v(\"的枚举类型，该枚举类有两个值：\\n\"),s(\"ul\",[s(\"li\",[s(\"code\",[t._v(\"background\")]),t._v(\"：在子组件之后绘制，即背景装饰。\")]),t._v(\" \"),s(\"li\",[s(\"code\",[t._v(\"foreground\")]),t._v(\"：在子组件之上绘制，即前景。\")])])])]),t._v(\" \"),s(\"h4\",{attrs:{id:\"boxdecoration\"}},[s(\"a\",{staticClass:\"header-anchor\",attrs:{href:\"#boxdecoration\"}},[t._v(\"#\")]),t._v(\" BoxDecoration\")]),t._v(\" \"),s(\"p\",[t._v(\"我们通常会直接使用\"),s(\"code\",[t._v(\"BoxDecoration\")]),t._v(\"类，它是一个Decoration的子类，实现了常用的装饰元素的绘制。\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"{\")]),t._v(\"\\n  Color color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//颜色\")]),t._v(\"\\n  DecorationImage image\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//图片\")]),t._v(\"\\n  BoxBorder border\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//边框\")]),t._v(\"\\n  BorderRadiusGeometry borderRadius\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//圆角\")]),t._v(\"\\n  List\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"<\")]),t._v(\"BoxShadow\"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\">\")]),t._v(\" boxShadow\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//阴影,可以指定多个\")]),t._v(\"\\n  Gradient gradient\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//渐变\")]),t._v(\"\\n  BlendMode backgroundBlendMode\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//背景混合模式\")]),t._v(\"\\n  BoxShape shape \"),s(\"span\",{pre:!0,attrs:{class:\"token operator\"}},[t._v(\"=\")]),t._v(\" BoxShape\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"rectangle\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//形状\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"}\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"各个属性名都是自解释的，详情读者可以查看API文档。下面我们实现一个带阴影的背景色渐变的按钮：\")]),t._v(\" \"),s(\"div\",{staticClass:\"language-dart extra-class\"},[s(\"pre\",{pre:!0,attrs:{class:\"language-dart\"}},[s(\"code\",[t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"DecoratedBox\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n    decoration\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxDecoration\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n      gradient\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"LinearGradient\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\"Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"red\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"orange\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"700\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//背景渐变\")]),t._v(\"\\n      borderRadius\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" BorderRadius\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"circular\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"3.0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//3像素圆角\")]),t._v(\"\\n      boxShadow\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"[\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token comment\"}},[t._v(\"//阴影\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"BoxShadow\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"\\n            color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\"Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"black54\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            offset\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Offset\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2.0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"2.0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n            blurRadius\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"4.0\")]),t._v(\"\\n        \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n      \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"]\")]),t._v(\"\\n    \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Padding\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"padding\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" EdgeInsets\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"symmetric\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"horizontal\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"80.0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" vertical\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token number\"}},[t._v(\"18.0\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n    child\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"Text\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),s(\"span\",{pre:!0,attrs:{class:\"token string\"}},[t._v('\"Login\"')]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\" style\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" \"),s(\"span\",{pre:!0,attrs:{class:\"token function\"}},[t._v(\"TextStyle\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\"(\")]),t._v(\"color\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\":\")]),t._v(\" Colors\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\".\")]),t._v(\"white\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\",\")]),t._v(\"\\n  \"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\"),s(\"span\",{pre:!0,attrs:{class:\"token punctuation\"}},[t._v(\")\")]),t._v(\"\\n\")])])]),s(\"p\",[t._v(\"运行后效果如图5-9所示：\")]),t._v(\" \"),s(\"p\",[s(\"img\",{attrs:{src:n(465),alt:\"图5-9\"}})]),t._v(\" \"),s(\"p\",[t._v(\"怎么样，通过\"),s(\"code\",[t._v(\"BoxDecoration\")]),t._v(\"我们实现了一个渐变按钮的外观，但此示例还不是一个标准的按钮，因为它还不能响应点击事件，我们将在后面“自定义组件”一章中实现一个完整功能的\"),s(\"code\",[t._v(\"GradientButton\")]),t._v(\"。另外，上面的例子中使用了\"),s(\"code\",[t._v(\"LinearGradient\")]),t._v(\"类，它用于定义线性渐变的类，Flutter中还提供了其它渐变配置类，如\"),s(\"code\",[t._v(\"RadialGradient\")]),t._v(\"、\"),s(\"code\",[t._v(\"SweepGradient\")]),t._v(\"，读者若有需要可以自行查看API文档。\")])])}),[],!1,null,null,null);a.default=e.exports}}]);"
  },
  {
    "path": "docs/assets/js/app.189f52e4.js",
    "content": "(window.webpackJsonp=window.webpackJsonp||[]).push([[0],[]]);!function(t){function e(e){for(var r,i,l=e[0],c=e[1],u=e[2],h=0,f=[];h<l.length;h++)i=l[h],Object.prototype.hasOwnProperty.call(a,i)&&a[i]&&f.push(a[i][0]),a[i]=0;for(r in c)Object.prototype.hasOwnProperty.call(c,r)&&(t[r]=c[r]);for(s&&s(e);f.length;)f.shift()();return o.push.apply(o,u||[]),n()}function n(){for(var t,e=0;e<o.length;e++){for(var n=o[e],r=!0,l=1;l<n.length;l++){var c=n[l];0!==a[c]&&(r=!1)}r&&(o.splice(e--,1),t=i(i.s=n[0]))}return t}var r={},a={1:0},o=[];function i(e){if(r[e])return r[e].exports;var n=r[e]={i:e,l:!1,exports:{}};return t[e].call(n.exports,n,n.exports,i),n.l=!0,n.exports}i.e=function(t){var e=[],n=a[t];if(0!==n)if(n)e.push(n[2]);else{var r=new Promise((function(e,r){n=a[t]=[e,r]}));e.push(n[2]=r);var o,l=document.createElement(\"script\");l.charset=\"utf-8\",l.timeout=120,i.nc&&l.setAttribute(\"nonce\",i.nc),l.src=function(t){return i.p+\"assets/js/\"+({}[t]||t)+\".\"+{2:\"f4fe4405\",3:\"cc1736a7\",4:\"2eb3fd02\",5:\"0941abdb\",6:\"15c5e328\",7:\"9aacc405\",8:\"cb5899ca\",9:\"f995bbe8\",10:\"5f481ee1\",11:\"2026a71f\",12:\"ef586fcc\",13:\"e93e35ca\",14:\"4d0ac363\",15:\"7f957f4b\",16:\"2d42d5bc\",17:\"59a459c4\",18:\"318a79e1\",19:\"175563ad\",20:\"6c5c8986\",21:\"c0363ec5\",22:\"4fdaa56f\",23:\"87c6db58\",24:\"bf1834df\",25:\"869317d7\",26:\"e656381b\",27:\"d1fa65c2\",28:\"fbba6f6f\",29:\"e809e77a\",30:\"b84a8f21\",31:\"dca209c1\",32:\"c0efa5ac\",33:\"8b948e2d\",34:\"9dff7b9d\",35:\"a9c7aa47\",36:\"d9eeb78f\",37:\"7ecb6ef5\",38:\"260a994c\",39:\"c3d0c942\",40:\"31216541\",41:\"7b70eaf0\",42:\"057d8df6\",43:\"b17a6aae\",44:\"5f61dab7\",45:\"381b04c9\",46:\"1c911d7c\",47:\"692e2e27\",48:\"6747a87a\",49:\"e636869b\",50:\"57b758f4\",51:\"4c0d2270\",52:\"61d5b4f1\",53:\"56438d06\",54:\"4afb8008\",55:\"a7c27448\",56:\"8a9849da\",57:\"371d9d12\",58:\"3d2501b6\",59:\"ab37b38d\",60:\"7043cbf5\",61:\"470fa853\",62:\"b51637d5\",63:\"ff70c9ee\",64:\"4621481d\",65:\"2d58a524\",66:\"5df03e62\",67:\"73c671d9\",68:\"4de1c271\",69:\"46ed47d5\",70:\"a85fca0c\",71:\"f6151eb7\",72:\"2c5ab8cf\",73:\"dcf07497\",74:\"3253492f\",75:\"bef82a69\",76:\"29cac87f\",77:\"8fef1f03\",78:\"28647d83\",79:\"1a0eb05b\",80:\"8eacbc37\",81:\"f8e7c85e\",82:\"a6b89300\",83:\"daf1b549\",84:\"f330e398\",85:\"f647b247\",86:\"e82005d4\",87:\"e1f95a69\",88:\"9709422a\",89:\"4ff5c882\",90:\"fff47bcf\",91:\"9eb501f7\",92:\"7b12896a\",93:\"54d0e6b6\",94:\"c6e7a2f7\",95:\"bf70fc45\",96:\"b382caf8\",97:\"14909b27\",98:\"8023c3ac\",99:\"3db30893\",100:\"ce32ed9e\",101:\"60ad385d\",102:\"53edb23b\",103:\"21193c45\",104:\"02d5761d\",105:\"b6044eeb\",106:\"9b857fc8\",107:\"188f42e4\",108:\"47fd6022\",109:\"9119c4a8\",110:\"fec0c84f\",111:\"95623244\",112:\"e8e91632\",113:\"d0f44add\",114:\"df1f86a1\",115:\"374ee72b\",116:\"5453b788\",117:\"f7002db2\",118:\"d4908451\",119:\"baead276\",120:\"681a706e\",121:\"aef1325d\",122:\"59b7f284\",123:\"b83bac47\",124:\"7a950a7c\",125:\"275c37b1\",126:\"a62ac904\",127:\"76aaf7da\",128:\"c75c7015\",129:\"88b4391d\",130:\"f1922c51\",131:\"7f51717c\",132:\"4b82358d\",133:\"85003caa\",134:\"68a25fe9\",135:\"765bad57\",136:\"46f11ea2\",137:\"0345664d\",138:\"1a184d5a\",139:\"94803f35\",140:\"c76b4d30\",141:\"592ee9d6\",142:\"d7d8dbaf\",143:\"77583b74\",144:\"2ce6c614\",145:\"8fe852a4\",146:\"86bb1e0f\",147:\"e9009fb7\",148:\"0c341180\",149:\"46210223\",150:\"220fd8cf\",151:\"a1cacc6d\",152:\"5323fd98\",153:\"26e3ed31\",154:\"40b97c4a\",155:\"b55f4193\",156:\"f4128841\",157:\"fb299d51\",158:\"49064f28\",159:\"277037a1\",160:\"0664832e\",161:\"38ea1a1a\",162:\"f96e9c80\",163:\"cfb15292\",164:\"46dd9498\",165:\"86c8b8c1\",166:\"3938fc5f\",167:\"09cbb683\",168:\"aab68e4f\",169:\"b01d210b\",170:\"e68bf3e7\",171:\"4f0e7d6b\",172:\"ae542eee\",173:\"c985ca5c\",174:\"ece20a6a\",175:\"891c0a2f\",176:\"93a74e23\",177:\"20c492a7\",178:\"a9c56f1f\",179:\"b42bb139\",180:\"d048122a\",181:\"e3633acb\",182:\"d30b9f69\",183:\"802b20e7\",184:\"90e5f242\",185:\"5cca4d69\",186:\"56dca3cc\",187:\"405078e0\",188:\"766e2a5e\",189:\"c927c79a\",190:\"92378aeb\",191:\"167ff3f3\",192:\"53c8ab5d\",193:\"05fa90e3\",194:\"df254afb\",195:\"1c40c74b\",196:\"3dbd36a7\",197:\"7fd8856f\",198:\"2a531f59\",199:\"7dc1f153\",200:\"92165c5b\",201:\"2953b2e7\",202:\"de6ee6b8\",203:\"ac627436\",204:\"fb9289cb\",205:\"90d0284c\",206:\"2dbac1ab\",207:\"d052f90b\",208:\"0818aeb7\",209:\"95753ceb\",210:\"0c9a3892\",211:\"43a19344\",212:\"f9dee082\",213:\"f55d441e\",214:\"0836f39e\",215:\"807d3ade\",216:\"a7c3d830\",217:\"ad95582b\",218:\"059e8703\",219:\"70d1d263\",220:\"69e2bb54\",221:\"51aa50d8\",222:\"8f955066\",223:\"3230e791\",224:\"35c9623f\",225:\"8687c2ec\",226:\"d982aa8d\",227:\"c297d2cb\",228:\"4dcdd69c\",229:\"91061a53\"}[t]+\".js\"}(t);var c=new Error;o=function(e){l.onerror=l.onload=null,clearTimeout(u);var n=a[t];if(0!==n){if(n){var r=e&&(\"load\"===e.type?\"missing\":e.type),o=e&&e.target&&e.target.src;c.message=\"Loading chunk \"+t+\" failed.\\n(\"+r+\": \"+o+\")\",c.name=\"ChunkLoadError\",c.type=r,c.request=o,n[1](c)}a[t]=void 0}};var u=setTimeout((function(){o({type:\"timeout\",target:l})}),12e4);l.onerror=l.onload=o,document.head.appendChild(l)}return Promise.all(e)},i.m=t,i.c=r,i.d=function(t,e,n){i.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},i.r=function(t){\"undefined\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:\"Module\"}),Object.defineProperty(t,\"__esModule\",{value:!0})},i.t=function(t,e){if(1&e&&(t=i(t)),8&e)return t;if(4&e&&\"object\"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(i.r(n),Object.defineProperty(n,\"default\",{enumerable:!0,value:t}),2&e&&\"string\"!=typeof t)for(var r in t)i.d(n,r,function(e){return t[e]}.bind(null,r));return n},i.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return i.d(e,\"a\",e),e},i.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},i.p=\"/\",i.oe=function(t){throw console.error(t),t};var l=window.webpackJsonp=window.webpackJsonp||[],c=l.push.bind(l);l.push=e,l=l.slice();for(var u=0;u<l.length;u++)e(l[u]);var s=c;o.push([178,0]),n()}([function(t,e,n){var r=n(3),a=n(24).f,o=n(10),i=n(18),l=n(71),c=n(111),u=n(98);t.exports=function(t,e){var n,s,h,f,p,v=t.target,d=t.global,m=t.stat;if(n=d?r:m?r[v]||l(v,{}):(r[v]||{}).prototype)for(s in e){if(f=e[s],h=t.noTargetGet?(p=a(n,s))&&p.value:n[s],!u(d?s:v+(m?\".\":\"#\")+s,t.forced)&&void 0!==h){if(typeof f==typeof h)continue;c(f,h)}(t.sham||h&&h.sham)&&o(f,\"sham\",!0),i(n,s,f,t)}}},function(t,e,n){var r=n(3),a=n(69),o=n(8),i=n(48),l=n(73),c=n(106),u=a(\"wks\"),s=r.Symbol,h=c?s:s&&s.withoutSetter||i;t.exports=function(t){return o(u,t)&&(l||\"string\"==typeof u[t])||(l&&o(s,t)?u[t]=s[t]:u[t]=h(\"Symbol.\"+t)),u[t]}},function(t,e){t.exports=function(t){try{return!!t()}catch(t){return!0}}},function(t,e){var n=function(t){return t&&t.Math==Math&&t};t.exports=n(\"object\"==typeof globalThis&&globalThis)||n(\"object\"==typeof window&&window)||n(\"object\"==typeof self&&self)||n(\"object\"==typeof global&&global)||function(){return this}()||Function(\"return this\")()},function(t,e){t.exports=function(t){return\"object\"==typeof t?null!==t:\"function\"==typeof t}},function(t,e,n){var r=n(4);t.exports=function(t){if(!r(t))throw TypeError(String(t)+\" is not an object\");return t}},function(t,e,n){var r=n(2);t.exports=!r((function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]}))},function(t,e,n){var r=n(6),a=n(104),o=n(5),i=n(47),l=Object.defineProperty;e.f=r?l:function(t,e,n){if(o(t),e=i(e,!0),o(n),a)try{return l(t,e,n)}catch(t){}if(\"get\"in n||\"set\"in n)throw TypeError(\"Accessors not supported\");return\"value\"in n&&(t[e]=n.value),t}},function(t,e){var n={}.hasOwnProperty;t.exports=function(t,e){return n.call(t,e)}},function(t,e,n){var r=n(81),a=n(18),o=n(194);r||a(Object.prototype,\"toString\",o,{unsafe:!0})},function(t,e,n){var r=n(6),a=n(7),o=n(32);t.exports=r?function(t,e,n){return a.f(t,e,o(1,n))}:function(t,e,n){return t[e]=n,t}},function(t,e,n){var r=n(23);t.exports=function(t){return Object(r(t))}},function(t,e,n){\"use strict\";var r=n(126).charAt,a=n(28),o=n(110),i=a.set,l=a.getterFor(\"String Iterator\");o(String,\"String\",(function(t){i(this,{type:\"String Iterator\",string:String(t),index:0})}),(function(){var t,e=l(this),n=e.string,a=e.index;return a>=n.length?{value:void 0,done:!0}:(t=r(n,a),e.index+=t.length,{value:t,done:!1})}))},function(t,e,n){var r=n(50),a=Math.min;t.exports=function(t){return t>0?a(r(t),9007199254740991):0}},function(t,e){var n=Array.isArray;t.exports=n},function(t,e,n){var r=n(31),a=n(23);t.exports=function(t){return r(a(t))}},function(t,e,n){var r=n(3),a=n(127),o=n(103),i=n(10),l=n(1),c=l(\"iterator\"),u=l(\"toStringTag\"),s=o.values;for(var h in a){var f=r[h],p=f&&f.prototype;if(p){if(p[c]!==s)try{i(p,c,s)}catch(t){p[c]=s}if(p[u]||i(p,u,h),a[h])for(var v in o)if(p[v]!==o[v])try{i(p,v,o[v])}catch(t){p[v]=o[v]}}}},function(t,e,n){var r=n(137),a=\"object\"==typeof self&&self&&self.Object===Object&&self,o=r||a||Function(\"return this\")();t.exports=o},function(t,e,n){var r=n(3),a=n(10),o=n(8),i=n(71),l=n(77),c=n(28),u=c.get,s=c.enforce,h=String(String).split(\"String\");(t.exports=function(t,e,n,l){var c,u=!!l&&!!l.unsafe,f=!!l&&!!l.enumerable,p=!!l&&!!l.noTargetGet;\"function\"==typeof n&&(\"string\"!=typeof e||o(n,\"name\")||a(n,\"name\",e),(c=s(n)).source||(c.source=h.join(\"string\"==typeof e?e:\"\"))),t!==r?(u?!p&&t[e]&&(f=!0):delete t[e],f?t[e]=n:a(t,e,n)):f?t[e]=n:i(e,n)})(Function.prototype,\"toString\",(function(){return\"function\"==typeof this&&u(this).source||l(this)}))},function(t,e,n){var r=n(105),a=n(3),o=function(t){return\"function\"==typeof t?t:void 0};t.exports=function(t,e){return arguments.length<2?o(r[t])||o(a[t]):r[t]&&r[t][e]||a[t]&&a[t][e]}},function(t,e){t.exports=!1},function(t,e){t.exports=function(t){if(\"function\"!=typeof t)throw TypeError(String(t)+\" is not a function\");return t}},function(t,e,n){var r=n(223),a=n(226);t.exports=function(t,e){var n=a(t,e);return r(n)?n:void 0}},function(t,e){t.exports=function(t){if(null==t)throw TypeError(\"Can't call method on \"+t);return t}},function(t,e,n){var r=n(6),a=n(78),o=n(32),i=n(15),l=n(47),c=n(8),u=n(104),s=Object.getOwnPropertyDescriptor;e.f=r?s:function(t,e){if(t=i(t),e=l(e,!0),u)try{return s(t,e)}catch(t){}if(c(t,e))return o(!a.f.call(t,e),t[e])}},function(t,e){t.exports=function(t){return null!=t&&\"object\"==typeof t}},function(t,e){var n={}.toString;t.exports=function(t){return n.call(t).slice(8,-1)}},function(t,e,n){\"use strict\";var r=n(0),a=n(29).filter;r({target:\"Array\",proto:!0,forced:!n(56)(\"filter\")},{filter:function(t){return a(this,t,arguments.length>1?arguments[1]:void 0)}})},function(t,e,n){var r,a,o,i=n(180),l=n(3),c=n(4),u=n(10),s=n(8),h=n(70),f=n(51),p=n(36),v=l.WeakMap;if(i){var d=h.state||(h.state=new v),m=d.get,g=d.has,b=d.set;r=function(t,e){return e.facade=t,b.call(d,t,e),e},a=function(t){return m.call(d,t)||{}},o=function(t){return g.call(d,t)}}else{var y=f(\"state\");p[y]=!0,r=function(t,e){return e.facade=t,u(t,y,e),e},a=function(t){return s(t,y)?t[y]:{}},o=function(t){return s(t,y)}}t.exports={set:r,get:a,has:o,enforce:function(t){return o(t)?a(t):r(t,{})},getterFor:function(t){return function(e){var n;if(!c(e)||(n=a(e)).type!==t)throw TypeError(\"Incompatible receiver, \"+t+\" required\");return n}}}},function(t,e,n){var r=n(53),a=n(31),o=n(11),i=n(13),l=n(128),c=[].push,u=function(t){var e=1==t,n=2==t,u=3==t,s=4==t,h=6==t,f=7==t,p=5==t||h;return function(v,d,m,g){for(var b,y,_=o(v),x=a(_),w=r(d,m,3),P=i(x.length),k=0,E=g||l,S=e?E(v,P):n||f?E(v,0):void 0;P>k;k++)if((p||k in x)&&(y=w(b=x[k],k,_),t))if(e)S[k]=y;else if(y)switch(t){case 3:return!0;case 5:return b;case 6:return k;case 2:c.call(S,b)}else switch(t){case 4:return!1;case 7:c.call(S,b)}return h?-1:u||s?s:S}};t.exports={forEach:u(0),map:u(1),filter:u(2),some:u(3),every:u(4),find:u(5),findIndex:u(6),filterOut:u(7)}},function(t,e,n){var r=n(42),a=n(208),o=n(209),i=r?r.toStringTag:void 0;t.exports=function(t){return null==t?void 0===t?\"[object Undefined]\":\"[object Null]\":i&&i in Object(t)?a(t):o(t)}},function(t,e,n){var r=n(2),a=n(26),o=\"\".split;t.exports=r((function(){return!Object(\"z\").propertyIsEnumerable(0)}))?function(t){return\"String\"==a(t)?o.call(t,\"\"):Object(t)}:Object},function(t,e){t.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},function(t,e,n){var r=n(26),a=n(3);t.exports=\"process\"==r(a.process)},function(t,e,n){var r,a,o=n(3),i=n(74),l=o.process,c=l&&l.versions,u=c&&c.v8;u?a=(r=u.split(\".\"))[0]+r[1]:i&&(!(r=i.match(/Edge\\/(\\d+)/))||r[1]>=74)&&(r=i.match(/Chrome\\/(\\d+)/))&&(a=r[1]),t.exports=a&&+a},function(t,e,n){var r,a=n(5),o=n(179),i=n(76),l=n(36),c=n(109),u=n(72),s=n(51),h=s(\"IE_PROTO\"),f=function(){},p=function(t){return\"<script>\"+t+\"<\\/script>\"},v=function(){try{r=document.domain&&new ActiveXObject(\"htmlfile\")}catch(t){}var t,e;v=r?function(t){t.write(p(\"\")),t.close();var e=t.parentWindow.Object;return t=null,e}(r):((e=u(\"iframe\")).style.display=\"none\",c.appendChild(e),e.src=String(\"javascript:\"),(t=e.contentWindow.document).open(),t.write(p(\"document.F=Object\")),t.close(),t.F);for(var n=i.length;n--;)delete v.prototype[i[n]];return v()};l[h]=!0,t.exports=Object.create||function(t,e){var n;return null!==t?(f.prototype=a(t),n=new f,f.prototype=null,n[h]=t):n=v(),void 0===e?n:o(n,e)}},function(t,e){t.exports={}},function(t,e){t.exports={}},function(t,e,n){var r=n(26);t.exports=Array.isArray||function(t){return\"Array\"==r(t)}},function(t,e,n){\"use strict\";var r=n(2);t.exports=function(t,e){var n=[][t];return!!n&&r((function(){n.call(null,e||function(){throw 1},1)}))}},function(t,e,n){\"use strict\";var r=n(0),a=n(3),o=n(19),i=n(20),l=n(6),c=n(73),u=n(106),s=n(2),h=n(8),f=n(38),p=n(4),v=n(5),d=n(11),m=n(15),g=n(47),b=n(32),y=n(35),_=n(49),x=n(67),w=n(201),P=n(79),k=n(24),E=n(7),S=n(78),O=n(10),j=n(18),L=n(69),A=n(51),C=n(36),$=n(48),T=n(1),I=n(132),R=n(133),F=n(52),D=n(28),M=n(29).forEach,N=A(\"hidden\"),B=T(\"toPrimitive\"),z=D.set,U=D.getterFor(\"Symbol\"),W=Object.prototype,V=a.Symbol,q=o(\"JSON\",\"stringify\"),H=k.f,G=E.f,K=w.f,J=S.f,X=L(\"symbols\"),Q=L(\"op-symbols\"),Y=L(\"string-to-symbol-registry\"),Z=L(\"symbol-to-string-registry\"),tt=L(\"wks\"),et=a.QObject,nt=!et||!et.prototype||!et.prototype.findChild,rt=l&&s((function(){return 7!=y(G({},\"a\",{get:function(){return G(this,\"a\",{value:7}).a}})).a}))?function(t,e,n){var r=H(W,e);r&&delete W[e],G(t,e,n),r&&t!==W&&G(W,e,r)}:G,at=function(t,e){var n=X[t]=y(V.prototype);return z(n,{type:\"Symbol\",tag:t,description:e}),l||(n.description=e),n},ot=u?function(t){return\"symbol\"==typeof t}:function(t){return Object(t)instanceof V},it=function(t,e,n){t===W&&it(Q,e,n),v(t);var r=g(e,!0);return v(n),h(X,r)?(n.enumerable?(h(t,N)&&t[N][r]&&(t[N][r]=!1),n=y(n,{enumerable:b(0,!1)})):(h(t,N)||G(t,N,b(1,{})),t[N][r]=!0),rt(t,r,n)):G(t,r,n)},lt=function(t,e){v(t);var n=m(e),r=_(n).concat(ht(n));return M(r,(function(e){l&&!ct.call(n,e)||it(t,e,n[e])})),t},ct=function(t){var e=g(t,!0),n=J.call(this,e);return!(this===W&&h(X,e)&&!h(Q,e))&&(!(n||!h(this,e)||!h(X,e)||h(this,N)&&this[N][e])||n)},ut=function(t,e){var n=m(t),r=g(e,!0);if(n!==W||!h(X,r)||h(Q,r)){var a=H(n,r);return!a||!h(X,r)||h(n,N)&&n[N][r]||(a.enumerable=!0),a}},st=function(t){var e=K(m(t)),n=[];return M(e,(function(t){h(X,t)||h(C,t)||n.push(t)})),n},ht=function(t){var e=t===W,n=K(e?Q:m(t)),r=[];return M(n,(function(t){!h(X,t)||e&&!h(W,t)||r.push(X[t])})),r};(c||(j((V=function(){if(this instanceof V)throw TypeError(\"Symbol is not a constructor\");var t=arguments.length&&void 0!==arguments[0]?String(arguments[0]):void 0,e=$(t),n=function(t){this===W&&n.call(Q,t),h(this,N)&&h(this[N],e)&&(this[N][e]=!1),rt(this,e,b(1,t))};return l&&nt&&rt(W,e,{configurable:!0,set:n}),at(e,t)}).prototype,\"toString\",(function(){return U(this).tag})),j(V,\"withoutSetter\",(function(t){return at($(t),t)})),S.f=ct,E.f=it,k.f=ut,x.f=w.f=st,P.f=ht,I.f=function(t){return at(T(t),t)},l&&(G(V.prototype,\"description\",{configurable:!0,get:function(){return U(this).description}}),i||j(W,\"propertyIsEnumerable\",ct,{unsafe:!0}))),r({global:!0,wrap:!0,forced:!c,sham:!c},{Symbol:V}),M(_(tt),(function(t){R(t)})),r({target:\"Symbol\",stat:!0,forced:!c},{for:function(t){var e=String(t);if(h(Y,e))return Y[e];var n=V(e);return Y[e]=n,Z[n]=e,n},keyFor:function(t){if(!ot(t))throw TypeError(t+\" is not a symbol\");if(h(Z,t))return Z[t]},useSetter:function(){nt=!0},useSimple:function(){nt=!1}}),r({target:\"Object\",stat:!0,forced:!c,sham:!l},{create:function(t,e){return void 0===e?y(t):lt(y(t),e)},defineProperty:it,defineProperties:lt,getOwnPropertyDescriptor:ut}),r({target:\"Object\",stat:!0,forced:!c},{getOwnPropertyNames:st,getOwnPropertySymbols:ht}),r({target:\"Object\",stat:!0,forced:s((function(){P.f(1)}))},{getOwnPropertySymbols:function(t){return P.f(d(t))}}),q)&&r({target:\"JSON\",stat:!0,forced:!c||s((function(){var t=V();return\"[null]\"!=q([t])||\"{}\"!=q({a:t})||\"{}\"!=q(Object(t))}))},{stringify:function(t,e,n){for(var r,a=[t],o=1;arguments.length>o;)a.push(arguments[o++]);if(r=e,(p(e)||void 0!==t)&&!ot(t))return f(e)||(e=function(t,e){if(\"function\"==typeof r&&(e=r.call(this,t,e)),!ot(e))return e}),a[1]=e,q.apply(null,a)}});V.prototype[B]||O(V.prototype,B,V.prototype.valueOf),F(V,\"Symbol\"),C[N]=!0},function(t,e,n){\"use strict\";var r=n(0),a=n(6),o=n(3),i=n(8),l=n(4),c=n(7).f,u=n(111),s=o.Symbol;if(a&&\"function\"==typeof s&&(!(\"description\"in s.prototype)||void 0!==s().description)){var h={},f=function(){var t=arguments.length<1||void 0===arguments[0]?void 0:String(arguments[0]),e=this instanceof f?new s(t):void 0===t?s():s(t);return\"\"===t&&(h[e]=!0),e};u(f,s);var p=f.prototype=s.prototype;p.constructor=f;var v=p.toString,d=\"Symbol(test)\"==String(s(\"test\")),m=/^Symbol\\((.*)\\)[^)]+$/;c(p,\"description\",{configurable:!0,get:function(){var t=l(this)?this.valueOf():this,e=v.call(t);if(i(h,t))return\"\";var n=d?e.slice(7,-1):e.replace(m,\"$1\");return\"\"===n?void 0:n}}),r({global:!0,forced:!0},{Symbol:f})}},function(t,e,n){var r=n(17).Symbol;t.exports=r},function(t,e,n){\"use strict\";n.d(e,\"a\",(function(){return o}));n(96);var r=n(44);n(40),n(41),n(9),n(57),n(12),n(16),n(134);var a=n(63);function o(t){return function(t){if(Array.isArray(t))return Object(r.a)(t)}(t)||function(t){if(\"undefined\"!=typeof Symbol&&Symbol.iterator in Object(t))return Array.from(t)}(t)||Object(a.a)(t)||function(){throw new TypeError(\"Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\")}()}},function(t,e,n){\"use strict\";function r(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,r=new Array(e);n<e;n++)r[n]=t[n];return r}n.d(e,\"a\",(function(){return r}))},function(t,e,n){\"use strict\";function r(t,e,n,r,a,o,i,l){var c,u=\"function\"==typeof t?t.options:t;if(e&&(u.render=e,u.staticRenderFns=n,u._compiled=!0),r&&(u.functional=!0),o&&(u._scopeId=\"data-v-\"+o),i?(c=function(t){(t=t||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||\"undefined\"==typeof __VUE_SSR_CONTEXT__||(t=__VUE_SSR_CONTEXT__),a&&a.call(this,t),t&&t._registeredComponents&&t._registeredComponents.add(i)},u._ssrRegister=c):a&&(c=l?function(){a.call(this,(u.functional?this.parent:this).$root.$options.shadowRoot)}:a),c)if(u.functional){u._injectStyles=c;var s=u.render;u.render=function(t,e){return c.call(e),s(t,e)}}else{var h=u.beforeCreate;u.beforeCreate=h?[].concat(h,c):[c]}return{exports:t,options:u}}n.d(e,\"a\",(function(){return r}))},function(t,e,n){\"use strict\";var r=n(0),a=n(68);r({target:\"RegExp\",proto:!0,forced:/./.exec!==a},{exec:a})},function(t,e,n){var r=n(4);t.exports=function(t,e){if(!r(t))return t;var n,a;if(e&&\"function\"==typeof(n=t.toString)&&!r(a=n.call(t)))return a;if(\"function\"==typeof(n=t.valueOf)&&!r(a=n.call(t)))return a;if(!e&&\"function\"==typeof(n=t.toString)&&!r(a=n.call(t)))return a;throw TypeError(\"Can't convert object to primitive value\")}},function(t,e){var n=0,r=Math.random();t.exports=function(t){return\"Symbol(\"+String(void 0===t?\"\":t)+\")_\"+(++n+r).toString(36)}},function(t,e,n){var r=n(107),a=n(76);t.exports=Object.keys||function(t){return r(t,a)}},function(t,e){var n=Math.ceil,r=Math.floor;t.exports=function(t){return isNaN(t=+t)?0:(t>0?r:n)(t)}},function(t,e,n){var r=n(69),a=n(48),o=r(\"keys\");t.exports=function(t){return o[t]||(o[t]=a(t))}},function(t,e,n){var r=n(7).f,a=n(8),o=n(1)(\"toStringTag\");t.exports=function(t,e,n){t&&!a(t=n?t:t.prototype,o)&&r(t,o,{configurable:!0,value:e})}},function(t,e,n){var r=n(21);t.exports=function(t,e,n){if(r(t),void 0===e)return t;switch(n){case 0:return function(){return t.call(e)};case 1:return function(n){return t.call(e,n)};case 2:return function(n,r){return t.call(e,n,r)};case 3:return function(n,r,a){return t.call(e,n,r,a)}}return function(){return t.apply(e,arguments)}}},function(t,e,n){\"use strict\";var r=n(0),a=n(4),o=n(38),i=n(108),l=n(13),c=n(15),u=n(55),s=n(1),h=n(56)(\"slice\"),f=s(\"species\"),p=[].slice,v=Math.max;r({target:\"Array\",proto:!0,forced:!h},{slice:function(t,e){var n,r,s,h=c(this),d=l(h.length),m=i(t,d),g=i(void 0===e?d:e,d);if(o(h)&&(\"function\"!=typeof(n=h.constructor)||n!==Array&&!o(n.prototype)?a(n)&&null===(n=n[f])&&(n=void 0):n=void 0,n===Array||void 0===n))return p.call(h,m,g);for(r=new(void 0===n?Array:n)(v(g-m,0)),s=0;m<g;m++,s++)m in h&&u(r,s,h[m]);return r.length=s,r}})},function(t,e,n){\"use strict\";var r=n(47),a=n(7),o=n(32);t.exports=function(t,e,n){var i=r(e);i in t?a.f(t,i,o(0,n)):t[i]=n}},function(t,e,n){var r=n(2),a=n(1),o=n(34),i=a(\"species\");t.exports=function(t){return o>=51||!r((function(){var e=[];return(e.constructor={})[i]=function(){return{foo:1}},1!==e[t](Boolean).foo}))}},function(t,e,n){n(133)(\"iterator\")},function(t,e,n){var r=n(213),a=n(214),o=n(215),i=n(216),l=n(217);function c(t){var e=-1,n=null==t?0:t.length;for(this.clear();++e<n;){var r=t[e];this.set(r[0],r[1])}}c.prototype.clear=r,c.prototype.delete=a,c.prototype.get=o,c.prototype.has=i,c.prototype.set=l,t.exports=c},function(t,e,n){var r=n(139);t.exports=function(t,e){for(var n=t.length;n--;)if(r(t[n][0],e))return n;return-1}},function(t,e,n){var r=n(22)(Object,\"create\");t.exports=r},function(t,e,n){var r=n(235);t.exports=function(t,e){var n=t.__data__;return r(e)?n[\"string\"==typeof e?\"string\":\"hash\"]:n.map}},function(t,e,n){var r=n(90);t.exports=function(t){if(\"string\"==typeof t||r(t))return t;var e=t+\"\";return\"0\"==e&&1/t==-1/0?\"-0\":e}},function(t,e,n){\"use strict\";n.d(e,\"a\",(function(){return a}));n(54),n(9),n(82),n(134),n(12);var r=n(44);function a(t,e){if(t){if(\"string\"==typeof t)return Object(r.a)(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);return\"Object\"===n&&t.constructor&&(n=t.constructor.name),\"Map\"===n||\"Set\"===n?Array.from(t):\"Arguments\"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?Object(r.a)(t,e):void 0}}},function(t,e,n){var r,a;\n/* NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress\n * @license MIT */void 0===(a=\"function\"==typeof(r=function(){var t,e,n={version:\"0.2.0\"},r=n.settings={minimum:.08,easing:\"ease\",positionUsing:\"\",speed:200,trickle:!0,trickleRate:.02,trickleSpeed:800,showSpinner:!0,barSelector:'[role=\"bar\"]',spinnerSelector:'[role=\"spinner\"]',parent:\"body\",template:'<div class=\"bar\" role=\"bar\"><div class=\"peg\"></div></div><div class=\"spinner\" role=\"spinner\"><div class=\"spinner-icon\"></div></div>'};function a(t,e,n){return t<e?e:t>n?n:t}function o(t){return 100*(-1+t)}n.configure=function(t){var e,n;for(e in t)void 0!==(n=t[e])&&t.hasOwnProperty(e)&&(r[e]=n);return this},n.status=null,n.set=function(t){var e=n.isStarted();t=a(t,r.minimum,1),n.status=1===t?null:t;var c=n.render(!e),u=c.querySelector(r.barSelector),s=r.speed,h=r.easing;return c.offsetWidth,i((function(e){\"\"===r.positionUsing&&(r.positionUsing=n.getPositioningCSS()),l(u,function(t,e,n){var a;return(a=\"translate3d\"===r.positionUsing?{transform:\"translate3d(\"+o(t)+\"%,0,0)\"}:\"translate\"===r.positionUsing?{transform:\"translate(\"+o(t)+\"%,0)\"}:{\"margin-left\":o(t)+\"%\"}).transition=\"all \"+e+\"ms \"+n,a}(t,s,h)),1===t?(l(c,{transition:\"none\",opacity:1}),c.offsetWidth,setTimeout((function(){l(c,{transition:\"all \"+s+\"ms linear\",opacity:0}),setTimeout((function(){n.remove(),e()}),s)}),s)):setTimeout(e,s)})),this},n.isStarted=function(){return\"number\"==typeof n.status},n.start=function(){n.status||n.set(0);var t=function(){setTimeout((function(){n.status&&(n.trickle(),t())}),r.trickleSpeed)};return r.trickle&&t(),this},n.done=function(t){return t||n.status?n.inc(.3+.5*Math.random()).set(1):this},n.inc=function(t){var e=n.status;return e?(\"number\"!=typeof t&&(t=(1-e)*a(Math.random()*e,.1,.95)),e=a(e+t,0,.994),n.set(e)):n.start()},n.trickle=function(){return n.inc(Math.random()*r.trickleRate)},t=0,e=0,n.promise=function(r){return r&&\"resolved\"!==r.state()?(0===e&&n.start(),t++,e++,r.always((function(){0==--e?(t=0,n.done()):n.set((t-e)/t)})),this):this},n.render=function(t){if(n.isRendered())return document.getElementById(\"nprogress\");u(document.documentElement,\"nprogress-busy\");var e=document.createElement(\"div\");e.id=\"nprogress\",e.innerHTML=r.template;var a,i=e.querySelector(r.barSelector),c=t?\"-100\":o(n.status||0),s=document.querySelector(r.parent);return l(i,{transition:\"all 0 linear\",transform:\"translate3d(\"+c+\"%,0,0)\"}),r.showSpinner||(a=e.querySelector(r.spinnerSelector))&&f(a),s!=document.body&&u(s,\"nprogress-custom-parent\"),s.appendChild(e),e},n.remove=function(){s(document.documentElement,\"nprogress-busy\"),s(document.querySelector(r.parent),\"nprogress-custom-parent\");var t=document.getElementById(\"nprogress\");t&&f(t)},n.isRendered=function(){return!!document.getElementById(\"nprogress\")},n.getPositioningCSS=function(){var t=document.body.style,e=\"WebkitTransform\"in t?\"Webkit\":\"MozTransform\"in t?\"Moz\":\"msTransform\"in t?\"ms\":\"OTransform\"in t?\"O\":\"\";return e+\"Perspective\"in t?\"translate3d\":e+\"Transform\"in t?\"translate\":\"margin\"};var i=function(){var t=[];function e(){var n=t.shift();n&&n(e)}return function(n){t.push(n),1==t.length&&e()}}(),l=function(){var t=[\"Webkit\",\"O\",\"Moz\",\"ms\"],e={};function n(n){return n=n.replace(/^-ms-/,\"ms-\").replace(/-([\\da-z])/gi,(function(t,e){return e.toUpperCase()})),e[n]||(e[n]=function(e){var n=document.body.style;if(e in n)return e;for(var r,a=t.length,o=e.charAt(0).toUpperCase()+e.slice(1);a--;)if((r=t[a]+o)in n)return r;return e}(n))}function r(t,e,r){e=n(e),t.style[e]=r}return function(t,e){var n,a,o=arguments;if(2==o.length)for(n in e)void 0!==(a=e[n])&&e.hasOwnProperty(n)&&r(t,n,a);else r(t,o[1],o[2])}}();function c(t,e){return(\"string\"==typeof t?t:h(t)).indexOf(\" \"+e+\" \")>=0}function u(t,e){var n=h(t),r=n+e;c(n,e)||(t.className=r.substring(1))}function s(t,e){var n,r=h(t);c(t,e)&&(n=r.replace(\" \"+e+\" \",\" \"),t.className=n.substring(1,n.length-1))}function h(t){return(\" \"+(t.className||\"\")+\" \").replace(/\\s+/gi,\" \")}function f(t){t&&t.parentNode&&t.parentNode.removeChild(t)}return n})?r.call(e,n,e,t):r)||(t.exports=a)},function(t,e,n){\"use strict\";var r=n(0),a=n(29).map;r({target:\"Array\",proto:!0,forced:!n(56)(\"map\")},{map:function(t){return a(this,t,arguments.length>1?arguments[1]:void 0)}})},function(t,e,n){\"use strict\";var r=n(166),a=n(5),o=n(13),i=n(50),l=n(23),c=n(168),u=n(204),s=n(169),h=Math.max,f=Math.min;r(\"replace\",2,(function(t,e,n,r){var p=r.REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE,v=r.REPLACE_KEEPS_$0,d=p?\"$\":\"$0\";return[function(n,r){var a=l(this),o=null==n?void 0:n[t];return void 0!==o?o.call(n,a,r):e.call(String(a),n,r)},function(t,r){if(!p&&v||\"string\"==typeof r&&-1===r.indexOf(d)){var l=n(e,t,this,r);if(l.done)return l.value}var m=a(t),g=String(this),b=\"function\"==typeof r;b||(r=String(r));var y=m.global;if(y){var _=m.unicode;m.lastIndex=0}for(var x=[];;){var w=s(m,g);if(null===w)break;if(x.push(w),!y)break;\"\"===String(w[0])&&(m.lastIndex=c(g,o(m.lastIndex),_))}for(var P,k=\"\",E=0,S=0;S<x.length;S++){w=x[S];for(var O=String(w[0]),j=h(f(i(w.index),g.length),0),L=[],A=1;A<w.length;A++)L.push(void 0===(P=w[A])?P:String(P));var C=w.groups;if(b){var $=[O].concat(L,j,g);void 0!==C&&$.push(C);var T=String(r.apply(void 0,$))}else T=u(O,g,j,L,C,r);j>=E&&(k+=g.slice(E,j)+T,E=j+O.length)}return k+g.slice(E)}]}))},function(t,e,n){var r=n(107),a=n(76).concat(\"length\",\"prototype\");e.f=Object.getOwnPropertyNames||function(t){return r(t,a)}},function(t,e,n){\"use strict\";var r,a,o=n(167),i=n(172),l=RegExp.prototype.exec,c=String.prototype.replace,u=l,s=(r=/a/,a=/b*/g,l.call(r,\"a\"),l.call(a,\"a\"),0!==r.lastIndex||0!==a.lastIndex),h=i.UNSUPPORTED_Y||i.BROKEN_CARET,f=void 0!==/()??/.exec(\"\")[1];(s||f||h)&&(u=function(t){var e,n,r,a,i=this,u=h&&i.sticky,p=o.call(i),v=i.source,d=0,m=t;return u&&(-1===(p=p.replace(\"y\",\"\")).indexOf(\"g\")&&(p+=\"g\"),m=String(t).slice(i.lastIndex),i.lastIndex>0&&(!i.multiline||i.multiline&&\"\\n\"!==t[i.lastIndex-1])&&(v=\"(?: \"+v+\")\",m=\" \"+m,d++),n=new RegExp(\"^(?:\"+v+\")\",p)),f&&(n=new RegExp(\"^\"+v+\"$(?!\\\\s)\",p)),s&&(e=i.lastIndex),r=l.call(u?n:i,m),u?r?(r.input=r.input.slice(d),r[0]=r[0].slice(d),r.index=i.lastIndex,i.lastIndex+=r[0].length):i.lastIndex=0:s&&r&&(i.lastIndex=i.global?r.index+r[0].length:e),f&&r&&r.length>1&&c.call(r[0],n,(function(){for(a=1;a<arguments.length-2;a++)void 0===arguments[a]&&(r[a]=void 0)})),r}),t.exports=u},function(t,e,n){var r=n(20),a=n(70);(t.exports=function(t,e){return a[t]||(a[t]=void 0!==e?e:{})})(\"versions\",[]).push({version:\"3.9.1\",mode:r?\"pure\":\"global\",copyright:\"© 2021 Denis Pushkarev (zloirock.ru)\"})},function(t,e,n){var r=n(3),a=n(71),o=r[\"__core-js_shared__\"]||a(\"__core-js_shared__\",{});t.exports=o},function(t,e,n){var r=n(3),a=n(10);t.exports=function(t,e){try{a(r,t,e)}catch(n){r[t]=e}return e}},function(t,e,n){var r=n(3),a=n(4),o=r.document,i=a(o)&&a(o.createElement);t.exports=function(t){return i?o.createElement(t):{}}},function(t,e,n){var r=n(33),a=n(34),o=n(2);t.exports=!!Object.getOwnPropertySymbols&&!o((function(){return!Symbol.sham&&(r?38===a:a>37&&a<41)}))},function(t,e,n){var r=n(19);t.exports=r(\"navigator\",\"userAgent\")||\"\"},function(t,e,n){var r=n(15),a=n(13),o=n(108),i=function(t){return function(e,n,i){var l,c=r(e),u=a(c.length),s=o(i,u);if(t&&n!=n){for(;u>s;)if((l=c[s++])!=l)return!0}else for(;u>s;s++)if((t||s in c)&&c[s]===n)return t||s||0;return!t&&-1}};t.exports={includes:i(!0),indexOf:i(!1)}},function(t,e){t.exports=[\"constructor\",\"hasOwnProperty\",\"isPrototypeOf\",\"propertyIsEnumerable\",\"toLocaleString\",\"toString\",\"valueOf\"]},function(t,e,n){var r=n(70),a=Function.toString;\"function\"!=typeof r.inspectSource&&(r.inspectSource=function(t){return a.call(t)}),t.exports=r.inspectSource},function(t,e,n){\"use strict\";var r={}.propertyIsEnumerable,a=Object.getOwnPropertyDescriptor,o=a&&!r.call({1:2},1);e.f=o?function(t){var e=a(this,t);return!!e&&e.enumerable}:r},function(t,e){e.f=Object.getOwnPropertySymbols},function(t,e,n){var r=n(8),a=n(11),o=n(51),i=n(114),l=o(\"IE_PROTO\"),c=Object.prototype;t.exports=i?Object.getPrototypeOf:function(t){return t=a(t),r(t,l)?t[l]:\"function\"==typeof t.constructor&&t instanceof t.constructor?t.constructor.prototype:t instanceof Object?c:null}},function(t,e,n){var r={};r[n(1)(\"toStringTag\")]=\"z\",t.exports=\"[object z]\"===String(r)},function(t,e,n){var r=n(6),a=n(7).f,o=Function.prototype,i=o.toString,l=/^\\s*function ([^ (]*)/;r&&!(\"name\"in o)&&a(o,\"name\",{configurable:!0,get:function(){try{return i.call(this).match(l)[1]}catch(t){return\"\"}}})},function(t,e,n){var r=n(207),a=n(25),o=Object.prototype,i=o.hasOwnProperty,l=o.propertyIsEnumerable,c=r(function(){return arguments}())?r:function(t){return a(t)&&i.call(t,\"callee\")&&!l.call(t,\"callee\")};t.exports=c},function(t,e,n){var r=n(22)(n(17),\"Map\");t.exports=r},function(t,e){t.exports=function(t){var e=typeof t;return null!=t&&(\"object\"==e||\"function\"==e)}},function(t,e,n){var r=n(227),a=n(234),o=n(236),i=n(237),l=n(238);function c(t){var e=-1,n=null==t?0:t.length;for(this.clear();++e<n;){var r=t[e];this.set(r[0],r[1])}}c.prototype.clear=r,c.prototype.delete=a,c.prototype.get=o,c.prototype.has=i,c.prototype.set=l,t.exports=c},function(t,e){t.exports=function(t){var e=-1,n=Array(t.size);return t.forEach((function(t){n[++e]=t})),n}},function(t,e){t.exports=function(t){return\"number\"==typeof t&&t>-1&&t%1==0&&t<=9007199254740991}},function(t,e,n){var r=n(14),a=n(90),o=/\\.|\\[(?:[^[\\]]*|([\"'])(?:(?!\\1)[^\\\\]|\\\\.)*?\\1)\\]/,i=/^\\w*$/;t.exports=function(t,e){if(r(t))return!1;var n=typeof t;return!(\"number\"!=n&&\"symbol\"!=n&&\"boolean\"!=n&&null!=t&&!a(t))||(i.test(t)||!o.test(t)||null!=e&&t in Object(e))}},function(t,e,n){var r=n(30),a=n(25);t.exports=function(t){return\"symbol\"==typeof t||a(t)&&\"[object Symbol]\"==r(t)}},function(t,e){t.exports=function(t){return t}},function(t,e,n){\"use strict\";var r=n(0),a=n(29).some;r({target:\"Array\",proto:!0,forced:!n(39)(\"some\")},{some:function(t){return a(this,t,arguments.length>1?arguments[1]:void 0)}})},function(t,e,n){var r=n(0),a=n(11),o=n(49);r({target:\"Object\",stat:!0,forced:n(2)((function(){o(1)}))},{keys:function(t){return o(a(t))}})},function(t,e,n){\"use strict\";var r=n(0),a=n(130);r({target:\"Array\",proto:!0,forced:[].forEach!=a},{forEach:a})},function(t,e,n){var r=n(3),a=n(127),o=n(130),i=n(10);for(var l in a){var c=r[l],u=c&&c.prototype;if(u&&u.forEach!==o)try{i(u,\"forEach\",o)}catch(t){u.forEach=o}}},function(t,e,n){n(0)({target:\"Array\",stat:!0},{isArray:n(38)})},function(t,e,n){var r=n(1),a=n(35),o=n(7),i=r(\"unscopables\"),l=Array.prototype;null==l[i]&&o.f(l,i,{configurable:!0,value:a(null)}),t.exports=function(t){l[i][t]=!0}},function(t,e,n){var r=n(2),a=/#|\\.prototype\\./,o=function(t,e){var n=l[i(t)];return n==u||n!=c&&(\"function\"==typeof e?r(e):!!e)},i=o.normalize=function(t){return String(t).replace(a,\".\").toLowerCase()},l=o.data={},c=o.NATIVE=\"N\",u=o.POLYFILL=\"P\";t.exports=o},function(t,e,n){var r=n(5),a=n(182);t.exports=Object.setPrototypeOf||(\"__proto__\"in{}?function(){var t,e=!1,n={};try{(t=Object.getOwnPropertyDescriptor(Object.prototype,\"__proto__\").set).call(n,[]),e=n instanceof Array}catch(t){}return function(n,o){return r(n),a(o),e?t.call(n,o):n.__proto__=o,n}}():void 0)},function(t,e,n){var r=n(5),a=n(21),o=n(1)(\"species\");t.exports=function(t,e){var n,i=r(t).constructor;return void 0===i||null==(n=r(i)[o])?e:a(n)}},function(t,e,n){var r=n(165);t.exports=function(t){if(r(t))throw TypeError(\"The method doesn't accept regular expressions\");return t}},function(t,e,n){var r=n(1)(\"match\");t.exports=function(t){var e=/./;try{\"/./\"[t](e)}catch(n){try{return e[r]=!1,\"/./\"[t](e)}catch(t){}}return!1}},function(t,e,n){\"use strict\";var r=n(15),a=n(97),o=n(37),i=n(28),l=n(110),c=i.set,u=i.getterFor(\"Array Iterator\");t.exports=l(Array,\"Array\",(function(t,e){c(this,{type:\"Array Iterator\",target:r(t),index:0,kind:e})}),(function(){var t=u(this),e=t.target,n=t.kind,r=t.index++;return!e||r>=e.length?(t.target=void 0,{value:void 0,done:!0}):\"keys\"==n?{value:r,done:!1}:\"values\"==n?{value:e[r],done:!1}:{value:[r,e[r]],done:!1}}),\"values\"),o.Arguments=o.Array,a(\"keys\"),a(\"values\"),a(\"entries\")},function(t,e,n){var r=n(6),a=n(2),o=n(72);t.exports=!r&&!a((function(){return 7!=Object.defineProperty(o(\"div\"),\"a\",{get:function(){return 7}}).a}))},function(t,e,n){var r=n(3);t.exports=r},function(t,e,n){var r=n(73);t.exports=r&&!Symbol.sham&&\"symbol\"==typeof Symbol.iterator},function(t,e,n){var r=n(8),a=n(15),o=n(75).indexOf,i=n(36);t.exports=function(t,e){var n,l=a(t),c=0,u=[];for(n in l)!r(i,n)&&r(l,n)&&u.push(n);for(;e.length>c;)r(l,n=e[c++])&&(~o(u,n)||u.push(n));return u}},function(t,e,n){var r=n(50),a=Math.max,o=Math.min;t.exports=function(t,e){var n=r(t);return n<0?a(n+e,0):o(n,e)}},function(t,e,n){var r=n(19);t.exports=r(\"document\",\"documentElement\")},function(t,e,n){\"use strict\";var r=n(0),a=n(181),o=n(80),i=n(99),l=n(52),c=n(10),u=n(18),s=n(1),h=n(20),f=n(37),p=n(113),v=p.IteratorPrototype,d=p.BUGGY_SAFARI_ITERATORS,m=s(\"iterator\"),g=function(){return this};t.exports=function(t,e,n,s,p,b,y){a(n,e,s);var _,x,w,P=function(t){if(t===p&&j)return j;if(!d&&t in S)return S[t];switch(t){case\"keys\":case\"values\":case\"entries\":return function(){return new n(this,t)}}return function(){return new n(this)}},k=e+\" Iterator\",E=!1,S=t.prototype,O=S[m]||S[\"@@iterator\"]||p&&S[p],j=!d&&O||P(p),L=\"Array\"==e&&S.entries||O;if(L&&(_=o(L.call(new t)),v!==Object.prototype&&_.next&&(h||o(_)===v||(i?i(_,v):\"function\"!=typeof _[m]&&c(_,m,g)),l(_,k,!0,!0),h&&(f[k]=g))),\"values\"==p&&O&&\"values\"!==O.name&&(E=!0,j=function(){return O.call(this)}),h&&!y||S[m]===j||c(S,m,j),f[e]=j,p)if(x={values:P(\"values\"),keys:b?j:P(\"keys\"),entries:P(\"entries\")},y)for(w in x)(d||E||!(w in S))&&u(S,w,x[w]);else r({target:e,proto:!0,forced:d||E},x);return x}},function(t,e,n){var r=n(8),a=n(112),o=n(24),i=n(7);t.exports=function(t,e){for(var n=a(e),l=i.f,c=o.f,u=0;u<n.length;u++){var s=n[u];r(t,s)||l(t,s,c(e,s))}}},function(t,e,n){var r=n(19),a=n(67),o=n(79),i=n(5);t.exports=r(\"Reflect\",\"ownKeys\")||function(t){var e=a.f(i(t)),n=o.f;return n?e.concat(n(t)):e}},function(t,e,n){\"use strict\";var r,a,o,i=n(2),l=n(80),c=n(10),u=n(8),s=n(1),h=n(20),f=s(\"iterator\"),p=!1;[].keys&&(\"next\"in(o=[].keys())?(a=l(l(o)))!==Object.prototype&&(r=a):p=!0);var v=null==r||i((function(){var t={};return r[f].call(t)!==t}));v&&(r={}),h&&!v||u(r,f)||c(r,f,(function(){return this})),t.exports={IteratorPrototype:r,BUGGY_SAFARI_ITERATORS:p}},function(t,e,n){var r=n(2);t.exports=!r((function(){function t(){}return t.prototype.constructor=null,Object.getPrototypeOf(new t)!==t.prototype}))},function(t,e,n){var r=n(3);t.exports=r.Promise},function(t,e,n){var r=n(1),a=n(37),o=r(\"iterator\"),i=Array.prototype;t.exports=function(t){return void 0!==t&&(a.Array===t||i[o]===t)}},function(t,e,n){var r=n(118),a=n(37),o=n(1)(\"iterator\");t.exports=function(t){if(null!=t)return t[o]||t[\"@@iterator\"]||a[r(t)]}},function(t,e,n){var r=n(81),a=n(26),o=n(1)(\"toStringTag\"),i=\"Arguments\"==a(function(){return arguments}());t.exports=r?a:function(t){var e,n,r;return void 0===t?\"Undefined\":null===t?\"Null\":\"string\"==typeof(n=function(t,e){try{return t[e]}catch(t){}}(e=Object(t),o))?n:i?a(e):\"Object\"==(r=a(e))&&\"function\"==typeof e.callee?\"Arguments\":r}},function(t,e,n){var r=n(5);t.exports=function(t){var e=t.return;if(void 0!==e)return r(e.call(t)).value}},function(t,e,n){var r=n(1)(\"iterator\"),a=!1;try{var o=0,i={next:function(){return{done:!!o++}},return:function(){a=!0}};i[r]=function(){return this},Array.from(i,(function(){throw 2}))}catch(t){}t.exports=function(t,e){if(!e&&!a)return!1;var n=!1;try{var o={};o[r]=function(){return{next:function(){return{done:n=!0}}}},t(o)}catch(t){}return n}},function(t,e,n){var r,a,o,i=n(3),l=n(2),c=n(53),u=n(109),s=n(72),h=n(122),f=n(33),p=i.location,v=i.setImmediate,d=i.clearImmediate,m=i.process,g=i.MessageChannel,b=i.Dispatch,y=0,_={},x=function(t){if(_.hasOwnProperty(t)){var e=_[t];delete _[t],e()}},w=function(t){return function(){x(t)}},P=function(t){x(t.data)},k=function(t){i.postMessage(t+\"\",p.protocol+\"//\"+p.host)};v&&d||(v=function(t){for(var e=[],n=1;arguments.length>n;)e.push(arguments[n++]);return _[++y]=function(){(\"function\"==typeof t?t:Function(t)).apply(void 0,e)},r(y),y},d=function(t){delete _[t]},f?r=function(t){m.nextTick(w(t))}:b&&b.now?r=function(t){b.now(w(t))}:g&&!h?(o=(a=new g).port2,a.port1.onmessage=P,r=c(o.postMessage,o,1)):i.addEventListener&&\"function\"==typeof postMessage&&!i.importScripts&&p&&\"file:\"!==p.protocol&&!l(k)?(r=k,i.addEventListener(\"message\",P,!1)):r=\"onreadystatechange\"in s(\"script\")?function(t){u.appendChild(s(\"script\")).onreadystatechange=function(){u.removeChild(this),x(t)}}:function(t){setTimeout(w(t),0)}),t.exports={set:v,clear:d}},function(t,e,n){var r=n(74);t.exports=/(iphone|ipod|ipad).*applewebkit/i.test(r)},function(t,e,n){var r=n(5),a=n(4),o=n(124);t.exports=function(t,e){if(r(t),a(e)&&e.constructor===t)return e;var n=o.f(t);return(0,n.resolve)(e),n.promise}},function(t,e,n){\"use strict\";var r=n(21),a=function(t){var e,n;this.promise=new t((function(t,r){if(void 0!==e||void 0!==n)throw TypeError(\"Bad Promise constructor\");e=t,n=r})),this.resolve=r(e),this.reject=r(n)};t.exports.f=function(t){return new a(t)}},function(t,e,n){var r=function(t){\"use strict\";var e=Object.prototype,n=e.hasOwnProperty,r=\"function\"==typeof Symbol?Symbol:{},a=r.iterator||\"@@iterator\",o=r.asyncIterator||\"@@asyncIterator\",i=r.toStringTag||\"@@toStringTag\";function l(t,e,n){return Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}),t[e]}try{l({},\"\")}catch(t){l=function(t,e,n){return t[e]=n}}function c(t,e,n,r){var a=e&&e.prototype instanceof h?e:h,o=Object.create(a.prototype),i=new P(r||[]);return o._invoke=function(t,e,n){var r=\"suspendedStart\";return function(a,o){if(\"executing\"===r)throw new Error(\"Generator is already running\");if(\"completed\"===r){if(\"throw\"===a)throw o;return E()}for(n.method=a,n.arg=o;;){var i=n.delegate;if(i){var l=_(i,n);if(l){if(l===s)continue;return l}}if(\"next\"===n.method)n.sent=n._sent=n.arg;else if(\"throw\"===n.method){if(\"suspendedStart\"===r)throw r=\"completed\",n.arg;n.dispatchException(n.arg)}else\"return\"===n.method&&n.abrupt(\"return\",n.arg);r=\"executing\";var c=u(t,e,n);if(\"normal\"===c.type){if(r=n.done?\"completed\":\"suspendedYield\",c.arg===s)continue;return{value:c.arg,done:n.done}}\"throw\"===c.type&&(r=\"completed\",n.method=\"throw\",n.arg=c.arg)}}}(t,n,i),o}function u(t,e,n){try{return{type:\"normal\",arg:t.call(e,n)}}catch(t){return{type:\"throw\",arg:t}}}t.wrap=c;var s={};function h(){}function f(){}function p(){}var v={};v[a]=function(){return this};var d=Object.getPrototypeOf,m=d&&d(d(k([])));m&&m!==e&&n.call(m,a)&&(v=m);var g=p.prototype=h.prototype=Object.create(v);function b(t){[\"next\",\"throw\",\"return\"].forEach((function(e){l(t,e,(function(t){return this._invoke(e,t)}))}))}function y(t,e){var r;this._invoke=function(a,o){function i(){return new e((function(r,i){!function r(a,o,i,l){var c=u(t[a],t,o);if(\"throw\"!==c.type){var s=c.arg,h=s.value;return h&&\"object\"==typeof h&&n.call(h,\"__await\")?e.resolve(h.__await).then((function(t){r(\"next\",t,i,l)}),(function(t){r(\"throw\",t,i,l)})):e.resolve(h).then((function(t){s.value=t,i(s)}),(function(t){return r(\"throw\",t,i,l)}))}l(c.arg)}(a,o,r,i)}))}return r=r?r.then(i,i):i()}}function _(t,e){var n=t.iterator[e.method];if(void 0===n){if(e.delegate=null,\"throw\"===e.method){if(t.iterator.return&&(e.method=\"return\",e.arg=void 0,_(t,e),\"throw\"===e.method))return s;e.method=\"throw\",e.arg=new TypeError(\"The iterator does not provide a 'throw' method\")}return s}var r=u(n,t.iterator,e.arg);if(\"throw\"===r.type)return e.method=\"throw\",e.arg=r.arg,e.delegate=null,s;var a=r.arg;return a?a.done?(e[t.resultName]=a.value,e.next=t.nextLoc,\"return\"!==e.method&&(e.method=\"next\",e.arg=void 0),e.delegate=null,s):a:(e.method=\"throw\",e.arg=new TypeError(\"iterator result is not an object\"),e.delegate=null,s)}function x(t){var e={tryLoc:t[0]};1 in t&&(e.catchLoc=t[1]),2 in t&&(e.finallyLoc=t[2],e.afterLoc=t[3]),this.tryEntries.push(e)}function w(t){var e=t.completion||{};e.type=\"normal\",delete e.arg,t.completion=e}function P(t){this.tryEntries=[{tryLoc:\"root\"}],t.forEach(x,this),this.reset(!0)}function k(t){if(t){var e=t[a];if(e)return e.call(t);if(\"function\"==typeof t.next)return t;if(!isNaN(t.length)){var r=-1,o=function e(){for(;++r<t.length;)if(n.call(t,r))return e.value=t[r],e.done=!1,e;return e.value=void 0,e.done=!0,e};return o.next=o}}return{next:E}}function E(){return{value:void 0,done:!0}}return f.prototype=g.constructor=p,p.constructor=f,f.displayName=l(p,i,\"GeneratorFunction\"),t.isGeneratorFunction=function(t){var e=\"function\"==typeof t&&t.constructor;return!!e&&(e===f||\"GeneratorFunction\"===(e.displayName||e.name))},t.mark=function(t){return Object.setPrototypeOf?Object.setPrototypeOf(t,p):(t.__proto__=p,l(t,i,\"GeneratorFunction\")),t.prototype=Object.create(g),t},t.awrap=function(t){return{__await:t}},b(y.prototype),y.prototype[o]=function(){return this},t.AsyncIterator=y,t.async=function(e,n,r,a,o){void 0===o&&(o=Promise);var i=new y(c(e,n,r,a),o);return t.isGeneratorFunction(n)?i:i.next().then((function(t){return t.done?t.value:i.next()}))},b(g),l(g,i,\"Generator\"),g[a]=function(){return this},g.toString=function(){return\"[object Generator]\"},t.keys=function(t){var e=[];for(var n in t)e.push(n);return e.reverse(),function n(){for(;e.length;){var r=e.pop();if(r in t)return n.value=r,n.done=!1,n}return n.done=!0,n}},t.values=k,P.prototype={constructor:P,reset:function(t){if(this.prev=0,this.next=0,this.sent=this._sent=void 0,this.done=!1,this.delegate=null,this.method=\"next\",this.arg=void 0,this.tryEntries.forEach(w),!t)for(var e in this)\"t\"===e.charAt(0)&&n.call(this,e)&&!isNaN(+e.slice(1))&&(this[e]=void 0)},stop:function(){this.done=!0;var t=this.tryEntries[0].completion;if(\"throw\"===t.type)throw t.arg;return this.rval},dispatchException:function(t){if(this.done)throw t;var e=this;function r(n,r){return i.type=\"throw\",i.arg=t,e.next=n,r&&(e.method=\"next\",e.arg=void 0),!!r}for(var a=this.tryEntries.length-1;a>=0;--a){var o=this.tryEntries[a],i=o.completion;if(\"root\"===o.tryLoc)return r(\"end\");if(o.tryLoc<=this.prev){var l=n.call(o,\"catchLoc\"),c=n.call(o,\"finallyLoc\");if(l&&c){if(this.prev<o.catchLoc)return r(o.catchLoc,!0);if(this.prev<o.finallyLoc)return r(o.finallyLoc)}else if(l){if(this.prev<o.catchLoc)return r(o.catchLoc,!0)}else{if(!c)throw new Error(\"try statement without catch or finally\");if(this.prev<o.finallyLoc)return r(o.finallyLoc)}}}},abrupt:function(t,e){for(var r=this.tryEntries.length-1;r>=0;--r){var a=this.tryEntries[r];if(a.tryLoc<=this.prev&&n.call(a,\"finallyLoc\")&&this.prev<a.finallyLoc){var o=a;break}}o&&(\"break\"===t||\"continue\"===t)&&o.tryLoc<=e&&e<=o.finallyLoc&&(o=null);var i=o?o.completion:{};return i.type=t,i.arg=e,o?(this.method=\"next\",this.next=o.finallyLoc,s):this.complete(i)},complete:function(t,e){if(\"throw\"===t.type)throw t.arg;return\"break\"===t.type||\"continue\"===t.type?this.next=t.arg:\"return\"===t.type?(this.rval=this.arg=t.arg,this.method=\"return\",this.next=\"end\"):\"normal\"===t.type&&e&&(this.next=e),s},finish:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var n=this.tryEntries[e];if(n.finallyLoc===t)return this.complete(n.completion,n.afterLoc),w(n),s}},catch:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var n=this.tryEntries[e];if(n.tryLoc===t){var r=n.completion;if(\"throw\"===r.type){var a=r.arg;w(n)}return a}}throw new Error(\"illegal catch attempt\")},delegateYield:function(t,e,n){return this.delegate={iterator:k(t),resultName:e,nextLoc:n},\"next\"===this.method&&(this.arg=void 0),s}},t}(t.exports);try{regeneratorRuntime=r}catch(t){Function(\"r\",\"regeneratorRuntime = r\")(r)}},function(t,e,n){var r=n(50),a=n(23),o=function(t){return function(e,n){var o,i,l=String(a(e)),c=r(n),u=l.length;return c<0||c>=u?t?\"\":void 0:(o=l.charCodeAt(c))<55296||o>56319||c+1===u||(i=l.charCodeAt(c+1))<56320||i>57343?t?l.charAt(c):o:t?l.slice(c,c+2):i-56320+(o-55296<<10)+65536}};t.exports={codeAt:o(!1),charAt:o(!0)}},function(t,e){t.exports={CSSRuleList:0,CSSStyleDeclaration:0,CSSValueList:0,ClientRectList:0,DOMRectList:0,DOMStringList:0,DOMTokenList:1,DataTransferItemList:0,FileList:0,HTMLAllCollection:0,HTMLCollection:0,HTMLFormElement:0,HTMLSelectElement:0,MediaList:0,MimeTypeArray:0,NamedNodeMap:0,NodeList:1,PaintRequestList:0,Plugin:0,PluginArray:0,SVGLengthList:0,SVGNumberList:0,SVGPathSegList:0,SVGPointList:0,SVGStringList:0,SVGTransformList:0,SourceBufferList:0,StyleSheetList:0,TextTrackCueList:0,TextTrackList:0,TouchList:0}},function(t,e,n){var r=n(4),a=n(38),o=n(1)(\"species\");t.exports=function(t,e){var n;return a(t)&&(\"function\"!=typeof(n=t.constructor)||n!==Array&&!a(n.prototype)?r(n)&&null===(n=n[o])&&(n=void 0):n=void 0),new(void 0===n?Array:n)(0===e?0:e)}},function(t,e,n){var r=n(0),a=n(2),o=n(11),i=n(80),l=n(114);r({target:\"Object\",stat:!0,forced:a((function(){i(1)})),sham:!l},{getPrototypeOf:function(t){return i(o(t))}})},function(t,e,n){\"use strict\";var r=n(29).forEach,a=n(39)(\"forEach\");t.exports=a?[].forEach:function(t){return r(this,t,arguments.length>1?arguments[1]:void 0)}},function(t,e,n){var r=n(2);t.exports=!r((function(){return Object.isExtensible(Object.preventExtensions({}))}))},function(t,e,n){var r=n(1);e.f=r},function(t,e,n){var r=n(105),a=n(8),o=n(132),i=n(7).f;t.exports=function(t){var e=r.Symbol||(r.Symbol={});a(e,t)||i(e,t,{value:o.f(t)})}},function(t,e,n){var r=n(0),a=n(202);r({target:\"Array\",stat:!0,forced:!n(120)((function(t){Array.from(t)}))},{from:a})},function(t,e,n){n(0)({target:\"Object\",stat:!0,sham:!n(6)},{create:n(35)})},function(t,e){t.exports=function(t,e){for(var n=-1,r=e.length,a=t.length;++n<r;)t[a+n]=e[n];return t}},function(t,e){var n=\"object\"==typeof global&&global&&global.Object===Object&&global;t.exports=n},function(t,e,n){var r=n(58),a=n(218),o=n(219),i=n(220),l=n(221),c=n(222);function u(t){var e=this.__data__=new r(t);this.size=e.size}u.prototype.clear=a,u.prototype.delete=o,u.prototype.get=i,u.prototype.has=l,u.prototype.set=c,t.exports=u},function(t,e){t.exports=function(t,e){return t===e||t!=t&&e!=e}},function(t,e,n){var r=n(30),a=n(85);t.exports=function(t){if(!a(t))return!1;var e=r(t);return\"[object Function]\"==e||\"[object GeneratorFunction]\"==e||\"[object AsyncFunction]\"==e||\"[object Proxy]\"==e}},function(t,e){var n=Function.prototype.toString;t.exports=function(t){if(null!=t){try{return n.call(t)}catch(t){}try{return t+\"\"}catch(t){}}return\"\"}},function(t,e,n){var r=n(239),a=n(25);t.exports=function t(e,n,o,i,l){return e===n||(null==e||null==n||!a(e)&&!a(n)?e!=e&&n!=n:r(e,n,o,i,t,l))}},function(t,e,n){var r=n(144),a=n(242),o=n(145);t.exports=function(t,e,n,i,l,c){var u=1&n,s=t.length,h=e.length;if(s!=h&&!(u&&h>s))return!1;var f=c.get(t),p=c.get(e);if(f&&p)return f==e&&p==t;var v=-1,d=!0,m=2&n?new r:void 0;for(c.set(t,e),c.set(e,t);++v<s;){var g=t[v],b=e[v];if(i)var y=u?i(b,g,v,e,t,c):i(g,b,v,t,e,c);if(void 0!==y){if(y)continue;d=!1;break}if(m){if(!a(e,(function(t,e){if(!o(m,e)&&(g===t||l(g,t,n,i,c)))return m.push(e)}))){d=!1;break}}else if(g!==b&&!l(g,b,n,i,c)){d=!1;break}}return c.delete(t),c.delete(e),d}},function(t,e,n){var r=n(86),a=n(240),o=n(241);function i(t){var e=-1,n=null==t?0:t.length;for(this.__data__=new r;++e<n;)this.add(t[e])}i.prototype.add=i.prototype.push=a,i.prototype.has=o,t.exports=i},function(t,e){t.exports=function(t,e){return t.has(e)}},function(t,e,n){var r=n(252),a=n(258),o=n(151);t.exports=function(t){return o(t)?r(t):a(t)}},function(t,e,n){(function(t){var r=n(17),a=n(254),o=e&&!e.nodeType&&e,i=o&&\"object\"==typeof t&&t&&!t.nodeType&&t,l=i&&i.exports===o?r.Buffer:void 0,c=(l?l.isBuffer:void 0)||a;t.exports=c}).call(this,n(148)(t))},function(t,e){t.exports=function(t){return t.webpackPolyfill||(t.deprecate=function(){},t.paths=[],t.children||(t.children=[]),Object.defineProperty(t,\"loaded\",{enumerable:!0,get:function(){return t.l}}),Object.defineProperty(t,\"id\",{enumerable:!0,get:function(){return t.i}}),t.webpackPolyfill=1),t}},function(t,e){var n=/^(?:0|[1-9]\\d*)$/;t.exports=function(t,e){var r=typeof t;return!!(e=null==e?9007199254740991:e)&&(\"number\"==r||\"symbol\"!=r&&n.test(t))&&t>-1&&t%1==0&&t<e}},function(t,e,n){var r=n(255),a=n(256),o=n(257),i=o&&o.isTypedArray,l=i?a(i):r;t.exports=l},function(t,e,n){var r=n(140),a=n(88);t.exports=function(t){return null!=t&&a(t.length)&&!r(t)}},function(t,e,n){var r=n(22)(n(17),\"Set\");t.exports=r},function(t,e,n){var r=n(85);t.exports=function(t){return t==t&&!r(t)}},function(t,e){t.exports=function(t,e){return function(n){return null!=n&&(n[t]===e&&(void 0!==e||t in Object(n)))}}},function(t,e,n){var r=n(156),a=n(62);t.exports=function(t,e){for(var n=0,o=(e=r(e,t)).length;null!=t&&n<o;)t=t[a(e[n++])];return n&&n==o?t:void 0}},function(t,e,n){var r=n(14),a=n(89),o=n(268),i=n(271);t.exports=function(t,e){return r(t)?t:a(t,e)?[t]:o(i(t))}},function(t,e,n){n(0)({target:\"Object\",stat:!0},{setPrototypeOf:n(99)})},function(t,e,n){var r=n(0),a=n(19),o=n(21),i=n(5),l=n(4),c=n(35),u=n(301),s=n(2),h=a(\"Reflect\",\"construct\"),f=s((function(){function t(){}return!(h((function(){}),[],t)instanceof t)})),p=!s((function(){h((function(){}))})),v=f||p;r({target:\"Reflect\",stat:!0,forced:v,sham:v},{construct:function(t,e){o(t),i(e);var n=arguments.length<3?t:o(arguments[2]);if(p&&!f)return h(t,e,n);if(t==n){switch(e.length){case 0:return new t;case 1:return new t(e[0]);case 2:return new t(e[0],e[1]);case 3:return new t(e[0],e[1],e[2]);case 4:return new t(e[0],e[1],e[2],e[3])}var r=[null];return r.push.apply(r,e),new(u.apply(t,r))}var a=n.prototype,s=c(l(a)?a:Object.prototype),v=Function.apply.call(t,s,e);return l(v)?v:s}})},function(t,e,n){},function(t,e,n){},function(t,e,n){var r=n(205),a=n(210),o=n(280),i=n(288),l=n(297),c=n(177),u=o((function(t){var e=c(t);return l(e)&&(e=void 0),i(r(t,1,l,!0),a(e,2))}));t.exports=u},function(t,e){var n=/^\\s+|\\s+$/g,r=/^[-+]0x[0-9a-f]+$/i,a=/^0b[01]+$/i,o=/^0o[0-7]+$/i,i=parseInt,l=\"object\"==typeof global&&global&&global.Object===Object&&global,c=\"object\"==typeof self&&self&&self.Object===Object&&self,u=l||c||Function(\"return this\")(),s=Object.prototype.toString,h=Math.max,f=Math.min,p=function(){return u.Date.now()};function v(t){var e=typeof t;return!!t&&(\"object\"==e||\"function\"==e)}function d(t){if(\"number\"==typeof t)return t;if(function(t){return\"symbol\"==typeof t||function(t){return!!t&&\"object\"==typeof t}(t)&&\"[object Symbol]\"==s.call(t)}(t))return NaN;if(v(t)){var e=\"function\"==typeof t.valueOf?t.valueOf():t;t=v(e)?e+\"\":e}if(\"string\"!=typeof t)return 0===t?t:+t;t=t.replace(n,\"\");var l=a.test(t);return l||o.test(t)?i(t.slice(2),l?2:8):r.test(t)?NaN:+t}t.exports=function(t,e,n){var r,a,o,i,l,c,u=0,s=!1,m=!1,g=!0;if(\"function\"!=typeof t)throw new TypeError(\"Expected a function\");function b(e){var n=r,o=a;return r=a=void 0,u=e,i=t.apply(o,n)}function y(t){return u=t,l=setTimeout(x,e),s?b(t):i}function _(t){var n=t-c;return void 0===c||n>=e||n<0||m&&t-u>=o}function x(){var t=p();if(_(t))return w(t);l=setTimeout(x,function(t){var n=e-(t-c);return m?f(n,o-(t-u)):n}(t))}function w(t){return l=void 0,g&&r?b(t):(r=a=void 0,i)}function P(){var t=p(),n=_(t);if(r=arguments,a=this,c=t,n){if(void 0===l)return y(c);if(m)return l=setTimeout(x,e),b(c)}return void 0===l&&(l=setTimeout(x,e)),i}return e=d(e)||0,v(n)&&(s=!!n.leading,o=(m=\"maxWait\"in n)?h(d(n.maxWait)||0,e):o,g=\"trailing\"in n?!!n.trailing:g),P.cancel=function(){void 0!==l&&clearTimeout(l),u=0,r=c=a=l=void 0},P.flush=function(){return void 0===l?i:w(p())},P}},function(t,e,n){function r(e){return\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?(t.exports=r=function(t){return typeof t},t.exports.default=t.exports,t.exports.__esModule=!0):(t.exports=r=function(t){return t&&\"function\"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?\"symbol\":typeof t},t.exports.default=t.exports,t.exports.__esModule=!0),r(e)}n(40),n(41),n(9),n(57),n(12),n(16),t.exports=r,t.exports.default=t.exports,t.exports.__esModule=!0},function(t,e,n){\"use strict\";var r=n(0),a=n(75).indexOf,o=n(39),i=[].indexOf,l=!!i&&1/[1].indexOf(1,-0)<0,c=o(\"indexOf\");r({target:\"Array\",proto:!0,forced:l||!c},{indexOf:function(t){return l?i.apply(this,arguments)||0:a(this,t,arguments.length>1?arguments[1]:void 0)}})},function(t,e,n){var r=n(4),a=n(26),o=n(1)(\"match\");t.exports=function(t){var e;return r(t)&&(void 0!==(e=t[o])?!!e:\"RegExp\"==a(t))}},function(t,e,n){\"use strict\";n(46);var r=n(18),a=n(2),o=n(1),i=n(68),l=n(10),c=o(\"species\"),u=!a((function(){var t=/./;return t.exec=function(){var t=[];return t.groups={a:\"7\"},t},\"7\"!==\"\".replace(t,\"$<a>\")})),s=\"$0\"===\"a\".replace(/./,\"$0\"),h=o(\"replace\"),f=!!/./[h]&&\"\"===/./[h](\"a\",\"$0\"),p=!a((function(){var t=/(?:)/,e=t.exec;t.exec=function(){return e.apply(this,arguments)};var n=\"ab\".split(t);return 2!==n.length||\"a\"!==n[0]||\"b\"!==n[1]}));t.exports=function(t,e,n,h){var v=o(t),d=!a((function(){var e={};return e[v]=function(){return 7},7!=\"\"[t](e)})),m=d&&!a((function(){var e=!1,n=/a/;return\"split\"===t&&((n={}).constructor={},n.constructor[c]=function(){return n},n.flags=\"\",n[v]=/./[v]),n.exec=function(){return e=!0,null},n[v](\"\"),!e}));if(!d||!m||\"replace\"===t&&(!u||!s||f)||\"split\"===t&&!p){var g=/./[v],b=n(v,\"\"[t],(function(t,e,n,r,a){return e.exec===i?d&&!a?{done:!0,value:g.call(e,n,r)}:{done:!0,value:t.call(n,e,r)}:{done:!1}}),{REPLACE_KEEPS_$0:s,REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE:f}),y=b[0],_=b[1];r(String.prototype,t,y),r(RegExp.prototype,v,2==e?function(t,e){return _.call(t,this,e)}:function(t){return _.call(t,this)})}h&&l(RegExp.prototype[v],\"sham\",!0)}},function(t,e,n){\"use strict\";var r=n(5);t.exports=function(){var t=r(this),e=\"\";return t.global&&(e+=\"g\"),t.ignoreCase&&(e+=\"i\"),t.multiline&&(e+=\"m\"),t.dotAll&&(e+=\"s\"),t.unicode&&(e+=\"u\"),t.sticky&&(e+=\"y\"),e}},function(t,e,n){\"use strict\";var r=n(126).charAt;t.exports=function(t,e,n){return e+(n?r(t,e).length:1)}},function(t,e,n){var r=n(26),a=n(68);t.exports=function(t,e){var n=t.exec;if(\"function\"==typeof n){var o=n.call(t,e);if(\"object\"!=typeof o)throw TypeError(\"RegExp exec method returned something other than an Object or null\");return o}if(\"RegExp\"!==r(t))throw TypeError(\"RegExp#exec called on incompatible receiver\");return a.call(t,e)}},function(t,e,n){\"use strict\";var r=n(0),a=n(31),o=n(15),i=n(39),l=[].join,c=a!=Object,u=i(\"join\",\",\");r({target:\"Array\",proto:!0,forced:c||!u},{join:function(t){return l.call(o(this),void 0===t?\",\":t)}})},function(t,e,n){\"use strict\";var r=n(19),a=n(7),o=n(1),i=n(6),l=o(\"species\");t.exports=function(t){var e=r(t),n=a.f;i&&e&&!e[l]&&n(e,l,{configurable:!0,get:function(){return this}})}},function(t,e,n){\"use strict\";var r=n(2);function a(t,e){return RegExp(t,e)}e.UNSUPPORTED_Y=r((function(){var t=a(\"a\",\"y\");return t.lastIndex=2,null!=t.exec(\"abcd\")})),e.BROKEN_CARET=r((function(){var t=a(\"^r\",\"gy\");return t.lastIndex=2,null!=t.exec(\"str\")}))},function(t,e,n){\"use strict\";var r=n(0),a=n(75).includes,o=n(97);r({target:\"Array\",proto:!0},{includes:function(t){return a(this,t,arguments.length>1?arguments[1]:void 0)}}),o(\"includes\")},function(t,e,n){\"use strict\";var r=n(0),a=n(101),o=n(23);r({target:\"String\",proto:!0,forced:!n(102)(\"includes\")},{includes:function(t){return!!~String(o(this)).indexOf(a(t),arguments.length>1?arguments[1]:void 0)}})},function(t,e,n){\"use strict\";var r=n(0),a=n(2),o=n(38),i=n(4),l=n(11),c=n(13),u=n(55),s=n(128),h=n(56),f=n(1),p=n(34),v=f(\"isConcatSpreadable\"),d=p>=51||!a((function(){var t=[];return t[v]=!1,t.concat()[0]!==t})),m=h(\"concat\"),g=function(t){if(!i(t))return!1;var e=t[v];return void 0!==e?!!e:o(t)};r({target:\"Array\",proto:!0,forced:!d||!m},{concat:function(t){var e,n,r,a,o,i=l(this),h=s(i,0),f=0;for(e=-1,r=arguments.length;e<r;e++)if(g(o=-1===e?i:arguments[e])){if(f+(a=c(o.length))>9007199254740991)throw TypeError(\"Maximum allowed index exceeded\");for(n=0;n<a;n++,f++)n in o&&u(h,f,o[n])}else{if(f>=9007199254740991)throw TypeError(\"Maximum allowed index exceeded\");u(h,f++,o)}return h.length=f,h}})},function(t,e,n){var r=n(155);t.exports=function(t,e,n){var a=null==t?void 0:r(t,e);return void 0===a?n:a}},function(t,e){t.exports=function(t){var e=null==t?0:t.length;return e?t[e-1]:void 0}},function(t,e,n){t.exports=n(304)},function(t,e,n){var r=n(6),a=n(7),o=n(5),i=n(49);t.exports=r?Object.defineProperties:function(t,e){o(t);for(var n,r=i(e),l=r.length,c=0;l>c;)a.f(t,n=r[c++],e[n]);return t}},function(t,e,n){var r=n(3),a=n(77),o=r.WeakMap;t.exports=\"function\"==typeof o&&/native code/.test(a(o))},function(t,e,n){\"use strict\";var r=n(113).IteratorPrototype,a=n(35),o=n(32),i=n(52),l=n(37),c=function(){return this};t.exports=function(t,e,n){var u=e+\" Iterator\";return t.prototype=a(r,{next:o(1,n)}),i(t,u,!1,!0),l[u]=c,t}},function(t,e,n){var r=n(4);t.exports=function(t){if(!r(t)&&null!==t)throw TypeError(\"Can't set \"+String(t)+\" as a prototype\");return t}},function(t,e,n){\"use strict\";var r,a,o,i,l=n(0),c=n(20),u=n(3),s=n(19),h=n(115),f=n(18),p=n(184),v=n(52),d=n(171),m=n(4),g=n(21),b=n(185),y=n(77),_=n(186),x=n(120),w=n(100),P=n(121).set,k=n(187),E=n(123),S=n(189),O=n(124),j=n(190),L=n(28),A=n(98),C=n(1),$=n(33),T=n(34),I=C(\"species\"),R=\"Promise\",F=L.get,D=L.set,M=L.getterFor(R),N=h,B=u.TypeError,z=u.document,U=u.process,W=s(\"fetch\"),V=O.f,q=V,H=!!(z&&z.createEvent&&u.dispatchEvent),G=\"function\"==typeof PromiseRejectionEvent,K=A(R,(function(){if(!(y(N)!==String(N))){if(66===T)return!0;if(!$&&!G)return!0}if(c&&!N.prototype.finally)return!0;if(T>=51&&/native code/.test(N))return!1;var t=N.resolve(1),e=function(t){t((function(){}),(function(){}))};return(t.constructor={})[I]=e,!(t.then((function(){}))instanceof e)})),J=K||!x((function(t){N.all(t).catch((function(){}))})),X=function(t){var e;return!(!m(t)||\"function\"!=typeof(e=t.then))&&e},Q=function(t,e){if(!t.notified){t.notified=!0;var n=t.reactions;k((function(){for(var r=t.value,a=1==t.state,o=0;n.length>o;){var i,l,c,u=n[o++],s=a?u.ok:u.fail,h=u.resolve,f=u.reject,p=u.domain;try{s?(a||(2===t.rejection&&et(t),t.rejection=1),!0===s?i=r:(p&&p.enter(),i=s(r),p&&(p.exit(),c=!0)),i===u.promise?f(B(\"Promise-chain cycle\")):(l=X(i))?l.call(i,h,f):h(i)):f(r)}catch(t){p&&!c&&p.exit(),f(t)}}t.reactions=[],t.notified=!1,e&&!t.rejection&&Z(t)}))}},Y=function(t,e,n){var r,a;H?((r=z.createEvent(\"Event\")).promise=e,r.reason=n,r.initEvent(t,!1,!0),u.dispatchEvent(r)):r={promise:e,reason:n},!G&&(a=u[\"on\"+t])?a(r):\"unhandledrejection\"===t&&S(\"Unhandled promise rejection\",n)},Z=function(t){P.call(u,(function(){var e,n=t.facade,r=t.value;if(tt(t)&&(e=j((function(){$?U.emit(\"unhandledRejection\",r,n):Y(\"unhandledrejection\",n,r)})),t.rejection=$||tt(t)?2:1,e.error))throw e.value}))},tt=function(t){return 1!==t.rejection&&!t.parent},et=function(t){P.call(u,(function(){var e=t.facade;$?U.emit(\"rejectionHandled\",e):Y(\"rejectionhandled\",e,t.value)}))},nt=function(t,e,n){return function(r){t(e,r,n)}},rt=function(t,e,n){t.done||(t.done=!0,n&&(t=n),t.value=e,t.state=2,Q(t,!0))},at=function(t,e,n){if(!t.done){t.done=!0,n&&(t=n);try{if(t.facade===e)throw B(\"Promise can't be resolved itself\");var r=X(e);r?k((function(){var n={done:!1};try{r.call(e,nt(at,n,t),nt(rt,n,t))}catch(e){rt(n,e,t)}})):(t.value=e,t.state=1,Q(t,!1))}catch(e){rt({done:!1},e,t)}}};K&&(N=function(t){b(this,N,R),g(t),r.call(this);var e=F(this);try{t(nt(at,e),nt(rt,e))}catch(t){rt(e,t)}},(r=function(t){D(this,{type:R,done:!1,notified:!1,parent:!1,reactions:[],rejection:!1,state:0,value:void 0})}).prototype=p(N.prototype,{then:function(t,e){var n=M(this),r=V(w(this,N));return r.ok=\"function\"!=typeof t||t,r.fail=\"function\"==typeof e&&e,r.domain=$?U.domain:void 0,n.parent=!0,n.reactions.push(r),0!=n.state&&Q(n,!1),r.promise},catch:function(t){return this.then(void 0,t)}}),a=function(){var t=new r,e=F(t);this.promise=t,this.resolve=nt(at,e),this.reject=nt(rt,e)},O.f=V=function(t){return t===N||t===o?new a(t):q(t)},c||\"function\"!=typeof h||(i=h.prototype.then,f(h.prototype,\"then\",(function(t,e){var n=this;return new N((function(t,e){i.call(n,t,e)})).then(t,e)}),{unsafe:!0}),\"function\"==typeof W&&l({global:!0,enumerable:!0,forced:!0},{fetch:function(t){return E(N,W.apply(u,arguments))}}))),l({global:!0,wrap:!0,forced:K},{Promise:N}),v(N,R,!1,!0),d(R),o=s(R),l({target:R,stat:!0,forced:K},{reject:function(t){var e=V(this);return e.reject.call(void 0,t),e.promise}}),l({target:R,stat:!0,forced:c||K},{resolve:function(t){return E(c&&this===o?N:this,t)}}),l({target:R,stat:!0,forced:J},{all:function(t){var e=this,n=V(e),r=n.resolve,a=n.reject,o=j((function(){var n=g(e.resolve),o=[],i=0,l=1;_(t,(function(t){var c=i++,u=!1;o.push(void 0),l++,n.call(e,t).then((function(t){u||(u=!0,o[c]=t,--l||r(o))}),a)})),--l||r(o)}));return o.error&&a(o.value),n.promise},race:function(t){var e=this,n=V(e),r=n.reject,a=j((function(){var a=g(e.resolve);_(t,(function(t){a.call(e,t).then(n.resolve,r)}))}));return a.error&&r(a.value),n.promise}})},function(t,e,n){var r=n(18);t.exports=function(t,e,n){for(var a in e)r(t,a,e[a],n);return t}},function(t,e){t.exports=function(t,e,n){if(!(t instanceof e))throw TypeError(\"Incorrect \"+(n?n+\" \":\"\")+\"invocation\");return t}},function(t,e,n){var r=n(5),a=n(116),o=n(13),i=n(53),l=n(117),c=n(119),u=function(t,e){this.stopped=t,this.result=e};t.exports=function(t,e,n){var s,h,f,p,v,d,m,g=n&&n.that,b=!(!n||!n.AS_ENTRIES),y=!(!n||!n.IS_ITERATOR),_=!(!n||!n.INTERRUPTED),x=i(e,g,1+b+_),w=function(t){return s&&c(s),new u(!0,t)},P=function(t){return b?(r(t),_?x(t[0],t[1],w):x(t[0],t[1])):_?x(t,w):x(t)};if(y)s=t;else{if(\"function\"!=typeof(h=l(t)))throw TypeError(\"Target is not iterable\");if(a(h)){for(f=0,p=o(t.length);p>f;f++)if((v=P(t[f]))&&v instanceof u)return v;return new u(!1)}s=h.call(t)}for(d=s.next;!(m=d.call(s)).done;){try{v=P(m.value)}catch(t){throw c(s),t}if(\"object\"==typeof v&&v&&v instanceof u)return v}return new u(!1)}},function(t,e,n){var r,a,o,i,l,c,u,s,h=n(3),f=n(24).f,p=n(121).set,v=n(122),d=n(188),m=n(33),g=h.MutationObserver||h.WebKitMutationObserver,b=h.document,y=h.process,_=h.Promise,x=f(h,\"queueMicrotask\"),w=x&&x.value;w||(r=function(){var t,e;for(m&&(t=y.domain)&&t.exit();a;){e=a.fn,a=a.next;try{e()}catch(t){throw a?i():o=void 0,t}}o=void 0,t&&t.enter()},v||m||d||!g||!b?_&&_.resolve?(u=_.resolve(void 0),s=u.then,i=function(){s.call(u,r)}):i=m?function(){y.nextTick(r)}:function(){p.call(h,r)}:(l=!0,c=b.createTextNode(\"\"),new g(r).observe(c,{characterData:!0}),i=function(){c.data=l=!l})),t.exports=w||function(t){var e={fn:t,next:void 0};o&&(o.next=e),a||(a=e,i()),o=e}},function(t,e,n){var r=n(74);t.exports=/web0s(?!.*chrome)/i.test(r)},function(t,e,n){var r=n(3);t.exports=function(t,e){var n=r.console;n&&n.error&&(1===arguments.length?n.error(t):n.error(t,e))}},function(t,e){t.exports=function(t){try{return{error:!1,value:t()}}catch(t){return{error:!0,value:t}}}},function(t,e,n){var r=n(0),a=n(192);r({target:\"Object\",stat:!0,forced:Object.assign!==a},{assign:a})},function(t,e,n){\"use strict\";var r=n(6),a=n(2),o=n(49),i=n(79),l=n(78),c=n(11),u=n(31),s=Object.assign,h=Object.defineProperty;t.exports=!s||a((function(){if(r&&1!==s({b:1},s(h({},\"a\",{enumerable:!0,get:function(){h(this,\"b\",{value:3,enumerable:!1})}}),{b:2})).b)return!0;var t={},e={},n=Symbol();return t[n]=7,\"abcdefghijklmnopqrst\".split(\"\").forEach((function(t){e[t]=t})),7!=s({},t)[n]||\"abcdefghijklmnopqrst\"!=o(s({},e)).join(\"\")}))?function(t,e){for(var n=c(t),a=arguments.length,s=1,h=i.f,f=l.f;a>s;)for(var p,v=u(arguments[s++]),d=h?o(v).concat(h(v)):o(v),m=d.length,g=0;m>g;)p=d[g++],r&&!f.call(v,p)||(n[p]=v[p]);return n}:s},function(t,e,n){\"use strict\";var r=n(0),a=n(20),o=n(115),i=n(2),l=n(19),c=n(100),u=n(123),s=n(18);r({target:\"Promise\",proto:!0,real:!0,forced:!!o&&i((function(){o.prototype.finally.call({then:function(){}},(function(){}))}))},{finally:function(t){var e=c(this,l(\"Promise\")),n=\"function\"==typeof t;return this.then(n?function(n){return u(e,t()).then((function(){return n}))}:t,n?function(n){return u(e,t()).then((function(){throw n}))}:t)}}),a||\"function\"!=typeof o||o.prototype.finally||s(o.prototype,\"finally\",l(\"Promise\").prototype.finally)},function(t,e,n){\"use strict\";var r=n(81),a=n(118);t.exports=r?{}.toString:function(){return\"[object \"+a(this)+\"]\"}},function(t,e,n){var r=n(0),a=n(6),o=n(112),i=n(15),l=n(24),c=n(55);r({target:\"Object\",stat:!0,sham:!a},{getOwnPropertyDescriptors:function(t){for(var e,n,r=i(t),a=l.f,u=o(r),s={},h=0;u.length>h;)void 0!==(n=a(r,e=u[h++]))&&c(s,e,n);return s}})},function(t,e,n){\"use strict\";var r=n(0),a=n(197).left,o=n(39),i=n(34),l=n(33);r({target:\"Array\",proto:!0,forced:!o(\"reduce\")||!l&&i>79&&i<83},{reduce:function(t){return a(this,t,arguments.length,arguments.length>1?arguments[1]:void 0)}})},function(t,e,n){var r=n(21),a=n(11),o=n(31),i=n(13),l=function(t){return function(e,n,l,c){r(n);var u=a(e),s=o(u),h=i(u.length),f=t?h-1:0,p=t?-1:1;if(l<2)for(;;){if(f in s){c=s[f],f+=p;break}if(f+=p,t?f<0:h<=f)throw TypeError(\"Reduce of empty array with no initial value\")}for(;t?f>=0:h>f;f+=p)f in s&&(c=n(c,s[f],f,u));return c}};t.exports={left:l(!1),right:l(!0)}},function(t,e,n){\"use strict\";var r,a=n(0),o=n(24).f,i=n(13),l=n(101),c=n(23),u=n(102),s=n(20),h=\"\".startsWith,f=Math.min,p=u(\"startsWith\");a({target:\"String\",proto:!0,forced:!!(s||p||(r=o(String.prototype,\"startsWith\"),!r||r.writable))&&!p},{startsWith:function(t){var e=String(c(this));l(t);var n=i(f(arguments.length>1?arguments[1]:void 0,e.length)),r=String(t);return h?h.call(e,r,n):e.slice(n,n+r.length)===r}})},function(t,e,n){var r=n(0),a=n(131),o=n(2),i=n(4),l=n(200).onFreeze,c=Object.freeze;r({target:\"Object\",stat:!0,forced:o((function(){c(1)})),sham:!a},{freeze:function(t){return c&&i(t)?c(l(t)):t}})},function(t,e,n){var r=n(36),a=n(4),o=n(8),i=n(7).f,l=n(48),c=n(131),u=l(\"meta\"),s=0,h=Object.isExtensible||function(){return!0},f=function(t){i(t,u,{value:{objectID:\"O\"+ ++s,weakData:{}}})},p=t.exports={REQUIRED:!1,fastKey:function(t,e){if(!a(t))return\"symbol\"==typeof t?t:(\"string\"==typeof t?\"S\":\"P\")+t;if(!o(t,u)){if(!h(t))return\"F\";if(!e)return\"E\";f(t)}return t[u].objectID},getWeakData:function(t,e){if(!o(t,u)){if(!h(t))return!0;if(!e)return!1;f(t)}return t[u].weakData},onFreeze:function(t){return c&&p.REQUIRED&&h(t)&&!o(t,u)&&f(t),t}};r[u]=!0},function(t,e,n){var r=n(15),a=n(67).f,o={}.toString,i=\"object\"==typeof window&&window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[];t.exports.f=function(t){return i&&\"[object Window]\"==o.call(t)?function(t){try{return a(t)}catch(t){return i.slice()}}(t):a(r(t))}},function(t,e,n){\"use strict\";var r=n(53),a=n(11),o=n(203),i=n(116),l=n(13),c=n(55),u=n(117);t.exports=function(t){var e,n,s,h,f,p,v=a(t),d=\"function\"==typeof this?this:Array,m=arguments.length,g=m>1?arguments[1]:void 0,b=void 0!==g,y=u(v),_=0;if(b&&(g=r(g,m>2?arguments[2]:void 0,2)),null==y||d==Array&&i(y))for(n=new d(e=l(v.length));e>_;_++)p=b?g(v[_],_):v[_],c(n,_,p);else for(f=(h=y.call(v)).next,n=new d;!(s=f.call(h)).done;_++)p=b?o(h,g,[s.value,_],!0):s.value,c(n,_,p);return n.length=_,n}},function(t,e,n){var r=n(5),a=n(119);t.exports=function(t,e,n,o){try{return o?e(r(n)[0],n[1]):e(n)}catch(e){throw a(t),e}}},function(t,e,n){var r=n(11),a=Math.floor,o=\"\".replace,i=/\\$([$&'`]|\\d{1,2}|<[^>]*>)/g,l=/\\$([$&'`]|\\d{1,2})/g;t.exports=function(t,e,n,c,u,s){var h=n+t.length,f=c.length,p=l;return void 0!==u&&(u=r(u),p=i),o.call(s,p,(function(r,o){var i;switch(o.charAt(0)){case\"$\":return\"$\";case\"&\":return t;case\"`\":return e.slice(0,n);case\"'\":return e.slice(h);case\"<\":i=u[o.slice(1,-1)];break;default:var l=+o;if(0===l)return r;if(l>f){var s=a(l/10);return 0===s?r:s<=f?void 0===c[s-1]?o.charAt(1):c[s-1]+o.charAt(1):r}i=c[l-1]}return void 0===i?\"\":i}))}},function(t,e,n){var r=n(136),a=n(206);t.exports=function t(e,n,o,i,l){var c=-1,u=e.length;for(o||(o=a),l||(l=[]);++c<u;){var s=e[c];n>0&&o(s)?n>1?t(s,n-1,o,i,l):r(l,s):i||(l[l.length]=s)}return l}},function(t,e,n){var r=n(42),a=n(83),o=n(14),i=r?r.isConcatSpreadable:void 0;t.exports=function(t){return o(t)||a(t)||!!(i&&t&&t[i])}},function(t,e,n){var r=n(30),a=n(25);t.exports=function(t){return a(t)&&\"[object Arguments]\"==r(t)}},function(t,e,n){var r=n(42),a=Object.prototype,o=a.hasOwnProperty,i=a.toString,l=r?r.toStringTag:void 0;t.exports=function(t){var e=o.call(t,l),n=t[l];try{t[l]=void 0;var r=!0}catch(t){}var a=i.call(t);return r&&(e?t[l]=n:delete t[l]),a}},function(t,e){var n=Object.prototype.toString;t.exports=function(t){return n.call(t)}},function(t,e,n){var r=n(211),a=n(267),o=n(91),i=n(14),l=n(277);t.exports=function(t){return\"function\"==typeof t?t:null==t?o:\"object\"==typeof t?i(t)?a(t[0],t[1]):r(t):l(t)}},function(t,e,n){var r=n(212),a=n(266),o=n(154);t.exports=function(t){var e=a(t);return 1==e.length&&e[0][2]?o(e[0][0],e[0][1]):function(n){return n===t||r(n,t,e)}}},function(t,e,n){var r=n(138),a=n(142);t.exports=function(t,e,n,o){var i=n.length,l=i,c=!o;if(null==t)return!l;for(t=Object(t);i--;){var u=n[i];if(c&&u[2]?u[1]!==t[u[0]]:!(u[0]in t))return!1}for(;++i<l;){var s=(u=n[i])[0],h=t[s],f=u[1];if(c&&u[2]){if(void 0===h&&!(s in t))return!1}else{var p=new r;if(o)var v=o(h,f,s,t,e,p);if(!(void 0===v?a(f,h,3,o,p):v))return!1}}return!0}},function(t,e){t.exports=function(){this.__data__=[],this.size=0}},function(t,e,n){var r=n(59),a=Array.prototype.splice;t.exports=function(t){var e=this.__data__,n=r(e,t);return!(n<0)&&(n==e.length-1?e.pop():a.call(e,n,1),--this.size,!0)}},function(t,e,n){var r=n(59);t.exports=function(t){var e=this.__data__,n=r(e,t);return n<0?void 0:e[n][1]}},function(t,e,n){var r=n(59);t.exports=function(t){return r(this.__data__,t)>-1}},function(t,e,n){var r=n(59);t.exports=function(t,e){var n=this.__data__,a=r(n,t);return a<0?(++this.size,n.push([t,e])):n[a][1]=e,this}},function(t,e,n){var r=n(58);t.exports=function(){this.__data__=new r,this.size=0}},function(t,e){t.exports=function(t){var e=this.__data__,n=e.delete(t);return this.size=e.size,n}},function(t,e){t.exports=function(t){return this.__data__.get(t)}},function(t,e){t.exports=function(t){return this.__data__.has(t)}},function(t,e,n){var r=n(58),a=n(84),o=n(86);t.exports=function(t,e){var n=this.__data__;if(n instanceof r){var i=n.__data__;if(!a||i.length<199)return i.push([t,e]),this.size=++n.size,this;n=this.__data__=new o(i)}return n.set(t,e),this.size=n.size,this}},function(t,e,n){var r=n(140),a=n(224),o=n(85),i=n(141),l=/^\\[object .+?Constructor\\]$/,c=Function.prototype,u=Object.prototype,s=c.toString,h=u.hasOwnProperty,f=RegExp(\"^\"+s.call(h).replace(/[\\\\^$.*+?()[\\]{}|]/g,\"\\\\$&\").replace(/hasOwnProperty|(function).*?(?=\\\\\\()| for .+?(?=\\\\\\])/g,\"$1.*?\")+\"$\");t.exports=function(t){return!(!o(t)||a(t))&&(r(t)?f:l).test(i(t))}},function(t,e,n){var r,a=n(225),o=(r=/[^.]+$/.exec(a&&a.keys&&a.keys.IE_PROTO||\"\"))?\"Symbol(src)_1.\"+r:\"\";t.exports=function(t){return!!o&&o in t}},function(t,e,n){var r=n(17)[\"__core-js_shared__\"];t.exports=r},function(t,e){t.exports=function(t,e){return null==t?void 0:t[e]}},function(t,e,n){var r=n(228),a=n(58),o=n(84);t.exports=function(){this.size=0,this.__data__={hash:new r,map:new(o||a),string:new r}}},function(t,e,n){var r=n(229),a=n(230),o=n(231),i=n(232),l=n(233);function c(t){var e=-1,n=null==t?0:t.length;for(this.clear();++e<n;){var r=t[e];this.set(r[0],r[1])}}c.prototype.clear=r,c.prototype.delete=a,c.prototype.get=o,c.prototype.has=i,c.prototype.set=l,t.exports=c},function(t,e,n){var r=n(60);t.exports=function(){this.__data__=r?r(null):{},this.size=0}},function(t,e){t.exports=function(t){var e=this.has(t)&&delete this.__data__[t];return this.size-=e?1:0,e}},function(t,e,n){var r=n(60),a=Object.prototype.hasOwnProperty;t.exports=function(t){var e=this.__data__;if(r){var n=e[t];return\"__lodash_hash_undefined__\"===n?void 0:n}return a.call(e,t)?e[t]:void 0}},function(t,e,n){var r=n(60),a=Object.prototype.hasOwnProperty;t.exports=function(t){var e=this.__data__;return r?void 0!==e[t]:a.call(e,t)}},function(t,e,n){var r=n(60);t.exports=function(t,e){var n=this.__data__;return this.size+=this.has(t)?0:1,n[t]=r&&void 0===e?\"__lodash_hash_undefined__\":e,this}},function(t,e,n){var r=n(61);t.exports=function(t){var e=r(this,t).delete(t);return this.size-=e?1:0,e}},function(t,e){t.exports=function(t){var e=typeof t;return\"string\"==e||\"number\"==e||\"symbol\"==e||\"boolean\"==e?\"__proto__\"!==t:null===t}},function(t,e,n){var r=n(61);t.exports=function(t){return r(this,t).get(t)}},function(t,e,n){var r=n(61);t.exports=function(t){return r(this,t).has(t)}},function(t,e,n){var r=n(61);t.exports=function(t,e){var n=r(this,t),a=n.size;return n.set(t,e),this.size+=n.size==a?0:1,this}},function(t,e,n){var r=n(138),a=n(143),o=n(243),i=n(246),l=n(262),c=n(14),u=n(147),s=n(150),h=\"[object Object]\",f=Object.prototype.hasOwnProperty;t.exports=function(t,e,n,p,v,d){var m=c(t),g=c(e),b=m?\"[object Array]\":l(t),y=g?\"[object Array]\":l(e),_=(b=\"[object Arguments]\"==b?h:b)==h,x=(y=\"[object Arguments]\"==y?h:y)==h,w=b==y;if(w&&u(t)){if(!u(e))return!1;m=!0,_=!1}if(w&&!_)return d||(d=new r),m||s(t)?a(t,e,n,p,v,d):o(t,e,b,n,p,v,d);if(!(1&n)){var P=_&&f.call(t,\"__wrapped__\"),k=x&&f.call(e,\"__wrapped__\");if(P||k){var E=P?t.value():t,S=k?e.value():e;return d||(d=new r),v(E,S,n,p,d)}}return!!w&&(d||(d=new r),i(t,e,n,p,v,d))}},function(t,e){t.exports=function(t){return this.__data__.set(t,\"__lodash_hash_undefined__\"),this}},function(t,e){t.exports=function(t){return this.__data__.has(t)}},function(t,e){t.exports=function(t,e){for(var n=-1,r=null==t?0:t.length;++n<r;)if(e(t[n],n,t))return!0;return!1}},function(t,e,n){var r=n(42),a=n(244),o=n(139),i=n(143),l=n(245),c=n(87),u=r?r.prototype:void 0,s=u?u.valueOf:void 0;t.exports=function(t,e,n,r,u,h,f){switch(n){case\"[object DataView]\":if(t.byteLength!=e.byteLength||t.byteOffset!=e.byteOffset)return!1;t=t.buffer,e=e.buffer;case\"[object ArrayBuffer]\":return!(t.byteLength!=e.byteLength||!h(new a(t),new a(e)));case\"[object Boolean]\":case\"[object Date]\":case\"[object Number]\":return o(+t,+e);case\"[object Error]\":return t.name==e.name&&t.message==e.message;case\"[object RegExp]\":case\"[object String]\":return t==e+\"\";case\"[object Map]\":var p=l;case\"[object Set]\":var v=1&r;if(p||(p=c),t.size!=e.size&&!v)return!1;var d=f.get(t);if(d)return d==e;r|=2,f.set(t,e);var m=i(p(t),p(e),r,u,h,f);return f.delete(t),m;case\"[object Symbol]\":if(s)return s.call(t)==s.call(e)}return!1}},function(t,e,n){var r=n(17).Uint8Array;t.exports=r},function(t,e){t.exports=function(t){var e=-1,n=Array(t.size);return t.forEach((function(t,r){n[++e]=[r,t]})),n}},function(t,e,n){var r=n(247),a=Object.prototype.hasOwnProperty;t.exports=function(t,e,n,o,i,l){var c=1&n,u=r(t),s=u.length;if(s!=r(e).length&&!c)return!1;for(var h=s;h--;){var f=u[h];if(!(c?f in e:a.call(e,f)))return!1}var p=l.get(t),v=l.get(e);if(p&&v)return p==e&&v==t;var d=!0;l.set(t,e),l.set(e,t);for(var m=c;++h<s;){var g=t[f=u[h]],b=e[f];if(o)var y=c?o(b,g,f,e,t,l):o(g,b,f,t,e,l);if(!(void 0===y?g===b||i(g,b,n,o,l):y)){d=!1;break}m||(m=\"constructor\"==f)}if(d&&!m){var _=t.constructor,x=e.constructor;_==x||!(\"constructor\"in t)||!(\"constructor\"in e)||\"function\"==typeof _&&_ instanceof _&&\"function\"==typeof x&&x instanceof x||(d=!1)}return l.delete(t),l.delete(e),d}},function(t,e,n){var r=n(248),a=n(249),o=n(146);t.exports=function(t){return r(t,o,a)}},function(t,e,n){var r=n(136),a=n(14);t.exports=function(t,e,n){var o=e(t);return a(t)?o:r(o,n(t))}},function(t,e,n){var r=n(250),a=n(251),o=Object.prototype.propertyIsEnumerable,i=Object.getOwnPropertySymbols,l=i?function(t){return null==t?[]:(t=Object(t),r(i(t),(function(e){return o.call(t,e)})))}:a;t.exports=l},function(t,e){t.exports=function(t,e){for(var n=-1,r=null==t?0:t.length,a=0,o=[];++n<r;){var i=t[n];e(i,n,t)&&(o[a++]=i)}return o}},function(t,e){t.exports=function(){return[]}},function(t,e,n){var r=n(253),a=n(83),o=n(14),i=n(147),l=n(149),c=n(150),u=Object.prototype.hasOwnProperty;t.exports=function(t,e){var n=o(t),s=!n&&a(t),h=!n&&!s&&i(t),f=!n&&!s&&!h&&c(t),p=n||s||h||f,v=p?r(t.length,String):[],d=v.length;for(var m in t)!e&&!u.call(t,m)||p&&(\"length\"==m||h&&(\"offset\"==m||\"parent\"==m)||f&&(\"buffer\"==m||\"byteLength\"==m||\"byteOffset\"==m)||l(m,d))||v.push(m);return v}},function(t,e){t.exports=function(t,e){for(var n=-1,r=Array(t);++n<t;)r[n]=e(n);return r}},function(t,e){t.exports=function(){return!1}},function(t,e,n){var r=n(30),a=n(88),o=n(25),i={};i[\"[object Float32Array]\"]=i[\"[object Float64Array]\"]=i[\"[object Int8Array]\"]=i[\"[object Int16Array]\"]=i[\"[object Int32Array]\"]=i[\"[object Uint8Array]\"]=i[\"[object Uint8ClampedArray]\"]=i[\"[object Uint16Array]\"]=i[\"[object Uint32Array]\"]=!0,i[\"[object Arguments]\"]=i[\"[object Array]\"]=i[\"[object ArrayBuffer]\"]=i[\"[object Boolean]\"]=i[\"[object DataView]\"]=i[\"[object Date]\"]=i[\"[object Error]\"]=i[\"[object Function]\"]=i[\"[object Map]\"]=i[\"[object Number]\"]=i[\"[object Object]\"]=i[\"[object RegExp]\"]=i[\"[object Set]\"]=i[\"[object String]\"]=i[\"[object WeakMap]\"]=!1,t.exports=function(t){return o(t)&&a(t.length)&&!!i[r(t)]}},function(t,e){t.exports=function(t){return function(e){return t(e)}}},function(t,e,n){(function(t){var r=n(137),a=e&&!e.nodeType&&e,o=a&&\"object\"==typeof t&&t&&!t.nodeType&&t,i=o&&o.exports===a&&r.process,l=function(){try{var t=o&&o.require&&o.require(\"util\").types;return t||i&&i.binding&&i.binding(\"util\")}catch(t){}}();t.exports=l}).call(this,n(148)(t))},function(t,e,n){var r=n(259),a=n(260),o=Object.prototype.hasOwnProperty;t.exports=function(t){if(!r(t))return a(t);var e=[];for(var n in Object(t))o.call(t,n)&&\"constructor\"!=n&&e.push(n);return e}},function(t,e){var n=Object.prototype;t.exports=function(t){var e=t&&t.constructor;return t===(\"function\"==typeof e&&e.prototype||n)}},function(t,e,n){var r=n(261)(Object.keys,Object);t.exports=r},function(t,e){t.exports=function(t,e){return function(n){return t(e(n))}}},function(t,e,n){var r=n(263),a=n(84),o=n(264),i=n(152),l=n(265),c=n(30),u=n(141),s=u(r),h=u(a),f=u(o),p=u(i),v=u(l),d=c;(r&&\"[object DataView]\"!=d(new r(new ArrayBuffer(1)))||a&&\"[object Map]\"!=d(new a)||o&&\"[object Promise]\"!=d(o.resolve())||i&&\"[object Set]\"!=d(new i)||l&&\"[object WeakMap]\"!=d(new l))&&(d=function(t){var e=c(t),n=\"[object Object]\"==e?t.constructor:void 0,r=n?u(n):\"\";if(r)switch(r){case s:return\"[object DataView]\";case h:return\"[object Map]\";case f:return\"[object Promise]\";case p:return\"[object Set]\";case v:return\"[object WeakMap]\"}return e}),t.exports=d},function(t,e,n){var r=n(22)(n(17),\"DataView\");t.exports=r},function(t,e,n){var r=n(22)(n(17),\"Promise\");t.exports=r},function(t,e,n){var r=n(22)(n(17),\"WeakMap\");t.exports=r},function(t,e,n){var r=n(153),a=n(146);t.exports=function(t){for(var e=a(t),n=e.length;n--;){var o=e[n],i=t[o];e[n]=[o,i,r(i)]}return e}},function(t,e,n){var r=n(142),a=n(176),o=n(274),i=n(89),l=n(153),c=n(154),u=n(62);t.exports=function(t,e){return i(t)&&l(e)?c(u(t),e):function(n){var i=a(n,t);return void 0===i&&i===e?o(n,t):r(e,i,3)}}},function(t,e,n){var r=n(269),a=/[^.[\\]]+|\\[(?:(-?\\d+(?:\\.\\d+)?)|([\"'])((?:(?!\\2)[^\\\\]|\\\\.)*?)\\2)\\]|(?=(?:\\.|\\[\\])(?:\\.|\\[\\]|$))/g,o=/\\\\(\\\\)?/g,i=r((function(t){var e=[];return 46===t.charCodeAt(0)&&e.push(\"\"),t.replace(a,(function(t,n,r,a){e.push(r?a.replace(o,\"$1\"):n||t)})),e}));t.exports=i},function(t,e,n){var r=n(270);t.exports=function(t){var e=r(t,(function(t){return 500===n.size&&n.clear(),t})),n=e.cache;return e}},function(t,e,n){var r=n(86);function a(t,e){if(\"function\"!=typeof t||null!=e&&\"function\"!=typeof e)throw new TypeError(\"Expected a function\");var n=function(){var r=arguments,a=e?e.apply(this,r):r[0],o=n.cache;if(o.has(a))return o.get(a);var i=t.apply(this,r);return n.cache=o.set(a,i)||o,i};return n.cache=new(a.Cache||r),n}a.Cache=r,t.exports=a},function(t,e,n){var r=n(272);t.exports=function(t){return null==t?\"\":r(t)}},function(t,e,n){var r=n(42),a=n(273),o=n(14),i=n(90),l=r?r.prototype:void 0,c=l?l.toString:void 0;t.exports=function t(e){if(\"string\"==typeof e)return e;if(o(e))return a(e,t)+\"\";if(i(e))return c?c.call(e):\"\";var n=e+\"\";return\"0\"==n&&1/e==-1/0?\"-0\":n}},function(t,e){t.exports=function(t,e){for(var n=-1,r=null==t?0:t.length,a=Array(r);++n<r;)a[n]=e(t[n],n,t);return a}},function(t,e,n){var r=n(275),a=n(276);t.exports=function(t,e){return null!=t&&a(t,e,r)}},function(t,e){t.exports=function(t,e){return null!=t&&e in Object(t)}},function(t,e,n){var r=n(156),a=n(83),o=n(14),i=n(149),l=n(88),c=n(62);t.exports=function(t,e,n){for(var u=-1,s=(e=r(e,t)).length,h=!1;++u<s;){var f=c(e[u]);if(!(h=null!=t&&n(t,f)))break;t=t[f]}return h||++u!=s?h:!!(s=null==t?0:t.length)&&l(s)&&i(f,s)&&(o(t)||a(t))}},function(t,e,n){var r=n(278),a=n(279),o=n(89),i=n(62);t.exports=function(t){return o(t)?r(i(t)):a(t)}},function(t,e){t.exports=function(t){return function(e){return null==e?void 0:e[t]}}},function(t,e,n){var r=n(155);t.exports=function(t){return function(e){return r(e,t)}}},function(t,e,n){var r=n(91),a=n(281),o=n(283);t.exports=function(t,e){return o(a(t,e,r),t+\"\")}},function(t,e,n){var r=n(282),a=Math.max;t.exports=function(t,e,n){return e=a(void 0===e?t.length-1:e,0),function(){for(var o=arguments,i=-1,l=a(o.length-e,0),c=Array(l);++i<l;)c[i]=o[e+i];i=-1;for(var u=Array(e+1);++i<e;)u[i]=o[i];return u[e]=n(c),r(t,this,u)}}},function(t,e){t.exports=function(t,e,n){switch(n.length){case 0:return t.call(e);case 1:return t.call(e,n[0]);case 2:return t.call(e,n[0],n[1]);case 3:return t.call(e,n[0],n[1],n[2])}return t.apply(e,n)}},function(t,e,n){var r=n(284),a=n(287)(r);t.exports=a},function(t,e,n){var r=n(285),a=n(286),o=n(91),i=a?function(t,e){return a(t,\"toString\",{configurable:!0,enumerable:!1,value:r(e),writable:!0})}:o;t.exports=i},function(t,e){t.exports=function(t){return function(){return t}}},function(t,e,n){var r=n(22),a=function(){try{var t=r(Object,\"defineProperty\");return t({},\"\",{}),t}catch(t){}}();t.exports=a},function(t,e){var n=Date.now;t.exports=function(t){var e=0,r=0;return function(){var a=n(),o=16-(a-r);if(r=a,o>0){if(++e>=800)return arguments[0]}else e=0;return t.apply(void 0,arguments)}}},function(t,e,n){var r=n(144),a=n(289),o=n(294),i=n(145),l=n(295),c=n(87);t.exports=function(t,e,n){var u=-1,s=a,h=t.length,f=!0,p=[],v=p;if(n)f=!1,s=o;else if(h>=200){var d=e?null:l(t);if(d)return c(d);f=!1,s=i,v=new r}else v=e?[]:p;t:for(;++u<h;){var m=t[u],g=e?e(m):m;if(m=n||0!==m?m:0,f&&g==g){for(var b=v.length;b--;)if(v[b]===g)continue t;e&&v.push(g),p.push(m)}else s(v,g,n)||(v!==p&&v.push(g),p.push(m))}return p}},function(t,e,n){var r=n(290);t.exports=function(t,e){return!!(null==t?0:t.length)&&r(t,e,0)>-1}},function(t,e,n){var r=n(291),a=n(292),o=n(293);t.exports=function(t,e,n){return e==e?o(t,e,n):r(t,a,n)}},function(t,e){t.exports=function(t,e,n,r){for(var a=t.length,o=n+(r?1:-1);r?o--:++o<a;)if(e(t[o],o,t))return o;return-1}},function(t,e){t.exports=function(t){return t!=t}},function(t,e){t.exports=function(t,e,n){for(var r=n-1,a=t.length;++r<a;)if(t[r]===e)return r;return-1}},function(t,e){t.exports=function(t,e,n){for(var r=-1,a=null==t?0:t.length;++r<a;)if(n(e,t[r]))return!0;return!1}},function(t,e,n){var r=n(152),a=n(296),o=n(87),i=r&&1/o(new r([,-0]))[1]==1/0?function(t){return new r(t)}:a;t.exports=i},function(t,e){t.exports=function(){}},function(t,e,n){var r=n(151),a=n(25);t.exports=function(t){return a(t)&&r(t)}},function(t,e,n){},function(t,e,n){},function(t,e,n){var r=n(0),a=n(6);r({target:\"Object\",stat:!0,forced:!a,sham:!a},{defineProperty:n(7).f})},function(t,e,n){\"use strict\";var r=n(21),a=n(4),o=[].slice,i={},l=function(t,e,n){if(!(e in i)){for(var r=[],a=0;a<e;a++)r[a]=\"a[\"+a+\"]\";i[e]=Function(\"C,a\",\"return new C(\"+r.join(\",\")+\")\")}return i[e](t,n)};t.exports=Function.bind||function(t){var e=r(this),n=o.call(arguments,1),i=function(){var r=n.concat(o.call(arguments));return this instanceof i?l(e,r.length,r):e.apply(t,r)};return a(e.prototype)&&(i.prototype=e.prototype),i}},function(t,e,n){\"use strict\";n(159)},function(t,e,n){\"use strict\";n(160)},function(t,e,n){\"use strict\";n.r(e);n(103),n(183),n(191),n(193),n(9);function r(t,e,n,r,a,o,i){try{var l=t[o](i),c=l.value}catch(t){return void n(t)}l.done?e(c):Promise.resolve(c).then(r,a)}function a(t){return function(){var e=this,n=arguments;return new Promise((function(a,o){var i=t.apply(e,n);function l(t){r(i,a,o,l,c,\"next\",t)}function c(t){r(i,a,o,l,c,\"throw\",t)}l(void 0)}))}}n(125),n(54),n(12),n(16),n(65),n(27);var o=Object.freeze({});function i(t){return null==t}function l(t){return null!=t}function c(t){return!0===t}function u(t){return\"string\"==typeof t||\"number\"==typeof t||\"symbol\"==typeof t||\"boolean\"==typeof t}function s(t){return null!==t&&\"object\"==typeof t}var h=Object.prototype.toString;function f(t){return\"[object Object]\"===h.call(t)}function p(t){return\"[object RegExp]\"===h.call(t)}function v(t){var e=parseFloat(String(t));return e>=0&&Math.floor(e)===e&&isFinite(t)}function d(t){return l(t)&&\"function\"==typeof t.then&&\"function\"==typeof t.catch}function m(t){return null==t?\"\":Array.isArray(t)||f(t)&&t.toString===h?JSON.stringify(t,null,2):String(t)}function g(t){var e=parseFloat(t);return isNaN(e)?t:e}function b(t,e){for(var n=Object.create(null),r=t.split(\",\"),a=0;a<r.length;a++)n[r[a]]=!0;return e?function(t){return n[t.toLowerCase()]}:function(t){return n[t]}}b(\"slot,component\",!0);var y=b(\"key,ref,slot,slot-scope,is\");function _(t,e){if(t.length){var n=t.indexOf(e);if(n>-1)return t.splice(n,1)}}var x=Object.prototype.hasOwnProperty;function w(t,e){return x.call(t,e)}function P(t){var e=Object.create(null);return function(n){return e[n]||(e[n]=t(n))}}var k=/-(\\w)/g,E=P((function(t){return t.replace(k,(function(t,e){return e?e.toUpperCase():\"\"}))})),S=P((function(t){return t.charAt(0).toUpperCase()+t.slice(1)})),O=/\\B([A-Z])/g,j=P((function(t){return t.replace(O,\"-$1\").toLowerCase()}));var L=Function.prototype.bind?function(t,e){return t.bind(e)}:function(t,e){function n(n){var r=arguments.length;return r?r>1?t.apply(e,arguments):t.call(e,n):t.call(e)}return n._length=t.length,n};function A(t,e){e=e||0;for(var n=t.length-e,r=new Array(n);n--;)r[n]=t[n+e];return r}function C(t,e){for(var n in e)t[n]=e[n];return t}function $(t){for(var e={},n=0;n<t.length;n++)t[n]&&C(e,t[n]);return e}function T(t,e,n){}var I=function(t,e,n){return!1},R=function(t){return t};function F(t,e){if(t===e)return!0;var n=s(t),r=s(e);if(!n||!r)return!n&&!r&&String(t)===String(e);try{var a=Array.isArray(t),o=Array.isArray(e);if(a&&o)return t.length===e.length&&t.every((function(t,n){return F(t,e[n])}));if(t instanceof Date&&e instanceof Date)return t.getTime()===e.getTime();if(a||o)return!1;var i=Object.keys(t),l=Object.keys(e);return i.length===l.length&&i.every((function(n){return F(t[n],e[n])}))}catch(t){return!1}}function D(t,e){for(var n=0;n<t.length;n++)if(F(t[n],e))return n;return-1}function M(t){var e=!1;return function(){e||(e=!0,t.apply(this,arguments))}}var N=[\"component\",\"directive\",\"filter\"],B=[\"beforeCreate\",\"created\",\"beforeMount\",\"mounted\",\"beforeUpdate\",\"updated\",\"beforeDestroy\",\"destroyed\",\"activated\",\"deactivated\",\"errorCaptured\",\"serverPrefetch\"],z={optionMergeStrategies:Object.create(null),silent:!1,productionTip:!1,devtools:!1,performance:!1,errorHandler:null,warnHandler:null,ignoredElements:[],keyCodes:Object.create(null),isReservedTag:I,isReservedAttr:I,isUnknownElement:I,getTagNamespace:T,parsePlatformTagName:R,mustUseProp:I,async:!0,_lifecycleHooks:B},U=/a-zA-Z\\u00B7\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u203F-\\u2040\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD/;function W(t,e,n,r){Object.defineProperty(t,e,{value:n,enumerable:!!r,writable:!0,configurable:!0})}var V=new RegExp(\"[^\"+U.source+\".$_\\\\d]\");var q,H=\"__proto__\"in{},G=\"undefined\"!=typeof window,K=\"undefined\"!=typeof WXEnvironment&&!!WXEnvironment.platform,J=K&&WXEnvironment.platform.toLowerCase(),X=G&&window.navigator.userAgent.toLowerCase(),Q=X&&/msie|trident/.test(X),Y=X&&X.indexOf(\"msie 9.0\")>0,Z=X&&X.indexOf(\"edge/\")>0,tt=(X&&X.indexOf(\"android\"),X&&/iphone|ipad|ipod|ios/.test(X)||\"ios\"===J),et=(X&&/chrome\\/\\d+/.test(X),X&&/phantomjs/.test(X),X&&X.match(/firefox\\/(\\d+)/)),nt={}.watch,rt=!1;if(G)try{var at={};Object.defineProperty(at,\"passive\",{get:function(){rt=!0}}),window.addEventListener(\"test-passive\",null,at)}catch(t){}var ot=function(){return void 0===q&&(q=!G&&!K&&\"undefined\"!=typeof global&&(global.process&&\"server\"===global.process.env.VUE_ENV)),q},it=G&&window.__VUE_DEVTOOLS_GLOBAL_HOOK__;function lt(t){return\"function\"==typeof t&&/native code/.test(t.toString())}var ct,ut=\"undefined\"!=typeof Symbol&&lt(Symbol)&&\"undefined\"!=typeof Reflect&&lt(Reflect.ownKeys);ct=\"undefined\"!=typeof Set&&lt(Set)?Set:function(){function t(){this.set=Object.create(null)}return t.prototype.has=function(t){return!0===this.set[t]},t.prototype.add=function(t){this.set[t]=!0},t.prototype.clear=function(){this.set=Object.create(null)},t}();var st=T,ht=0,ft=function(){this.id=ht++,this.subs=[]};ft.prototype.addSub=function(t){this.subs.push(t)},ft.prototype.removeSub=function(t){_(this.subs,t)},ft.prototype.depend=function(){ft.target&&ft.target.addDep(this)},ft.prototype.notify=function(){var t=this.subs.slice();for(var e=0,n=t.length;e<n;e++)t[e].update()},ft.target=null;var pt=[];function vt(t){pt.push(t),ft.target=t}function dt(){pt.pop(),ft.target=pt[pt.length-1]}var mt=function(t,e,n,r,a,o,i,l){this.tag=t,this.data=e,this.children=n,this.text=r,this.elm=a,this.ns=void 0,this.context=o,this.fnContext=void 0,this.fnOptions=void 0,this.fnScopeId=void 0,this.key=e&&e.key,this.componentOptions=i,this.componentInstance=void 0,this.parent=void 0,this.raw=!1,this.isStatic=!1,this.isRootInsert=!0,this.isComment=!1,this.isCloned=!1,this.isOnce=!1,this.asyncFactory=l,this.asyncMeta=void 0,this.isAsyncPlaceholder=!1},gt={child:{configurable:!0}};gt.child.get=function(){return this.componentInstance},Object.defineProperties(mt.prototype,gt);var bt=function(t){void 0===t&&(t=\"\");var e=new mt;return e.text=t,e.isComment=!0,e};function yt(t){return new mt(void 0,void 0,void 0,String(t))}function _t(t){var e=new mt(t.tag,t.data,t.children&&t.children.slice(),t.text,t.elm,t.context,t.componentOptions,t.asyncFactory);return e.ns=t.ns,e.isStatic=t.isStatic,e.key=t.key,e.isComment=t.isComment,e.fnContext=t.fnContext,e.fnOptions=t.fnOptions,e.fnScopeId=t.fnScopeId,e.asyncMeta=t.asyncMeta,e.isCloned=!0,e}var xt=Array.prototype,wt=Object.create(xt);[\"push\",\"pop\",\"shift\",\"unshift\",\"splice\",\"sort\",\"reverse\"].forEach((function(t){var e=xt[t];W(wt,t,(function(){for(var n=[],r=arguments.length;r--;)n[r]=arguments[r];var a,o=e.apply(this,n),i=this.__ob__;switch(t){case\"push\":case\"unshift\":a=n;break;case\"splice\":a=n.slice(2)}return a&&i.observeArray(a),i.dep.notify(),o}))}));var Pt=Object.getOwnPropertyNames(wt),kt=!0;function Et(t){kt=t}var St=function(t){this.value=t,this.dep=new ft,this.vmCount=0,W(t,\"__ob__\",this),Array.isArray(t)?(H?function(t,e){t.__proto__=e}(t,wt):function(t,e,n){for(var r=0,a=n.length;r<a;r++){var o=n[r];W(t,o,e[o])}}(t,wt,Pt),this.observeArray(t)):this.walk(t)};function Ot(t,e){var n;if(s(t)&&!(t instanceof mt))return w(t,\"__ob__\")&&t.__ob__ instanceof St?n=t.__ob__:kt&&!ot()&&(Array.isArray(t)||f(t))&&Object.isExtensible(t)&&!t._isVue&&(n=new St(t)),e&&n&&n.vmCount++,n}function jt(t,e,n,r,a){var o=new ft,i=Object.getOwnPropertyDescriptor(t,e);if(!i||!1!==i.configurable){var l=i&&i.get,c=i&&i.set;l&&!c||2!==arguments.length||(n=t[e]);var u=!a&&Ot(n);Object.defineProperty(t,e,{enumerable:!0,configurable:!0,get:function(){var e=l?l.call(t):n;return ft.target&&(o.depend(),u&&(u.dep.depend(),Array.isArray(e)&&Ct(e))),e},set:function(e){var r=l?l.call(t):n;e===r||e!=e&&r!=r||l&&!c||(c?c.call(t,e):n=e,u=!a&&Ot(e),o.notify())}})}}function Lt(t,e,n){if(Array.isArray(t)&&v(e))return t.length=Math.max(t.length,e),t.splice(e,1,n),n;if(e in t&&!(e in Object.prototype))return t[e]=n,n;var r=t.__ob__;return t._isVue||r&&r.vmCount?n:r?(jt(r.value,e,n),r.dep.notify(),n):(t[e]=n,n)}function At(t,e){if(Array.isArray(t)&&v(e))t.splice(e,1);else{var n=t.__ob__;t._isVue||n&&n.vmCount||w(t,e)&&(delete t[e],n&&n.dep.notify())}}function Ct(t){for(var e=void 0,n=0,r=t.length;n<r;n++)(e=t[n])&&e.__ob__&&e.__ob__.dep.depend(),Array.isArray(e)&&Ct(e)}St.prototype.walk=function(t){for(var e=Object.keys(t),n=0;n<e.length;n++)jt(t,e[n])},St.prototype.observeArray=function(t){for(var e=0,n=t.length;e<n;e++)Ot(t[e])};var $t=z.optionMergeStrategies;function Tt(t,e){if(!e)return t;for(var n,r,a,o=ut?Reflect.ownKeys(e):Object.keys(e),i=0;i<o.length;i++)\"__ob__\"!==(n=o[i])&&(r=t[n],a=e[n],w(t,n)?r!==a&&f(r)&&f(a)&&Tt(r,a):Lt(t,n,a));return t}function It(t,e,n){return n?function(){var r=\"function\"==typeof e?e.call(n,n):e,a=\"function\"==typeof t?t.call(n,n):t;return r?Tt(r,a):a}:e?t?function(){return Tt(\"function\"==typeof e?e.call(this,this):e,\"function\"==typeof t?t.call(this,this):t)}:e:t}function Rt(t,e){var n=e?t?t.concat(e):Array.isArray(e)?e:[e]:t;return n?function(t){for(var e=[],n=0;n<t.length;n++)-1===e.indexOf(t[n])&&e.push(t[n]);return e}(n):n}function Ft(t,e,n,r){var a=Object.create(t||null);return e?C(a,e):a}$t.data=function(t,e,n){return n?It(t,e,n):e&&\"function\"!=typeof e?t:It(t,e)},B.forEach((function(t){$t[t]=Rt})),N.forEach((function(t){$t[t+\"s\"]=Ft})),$t.watch=function(t,e,n,r){if(t===nt&&(t=void 0),e===nt&&(e=void 0),!e)return Object.create(t||null);if(!t)return e;var a={};for(var o in C(a,t),e){var i=a[o],l=e[o];i&&!Array.isArray(i)&&(i=[i]),a[o]=i?i.concat(l):Array.isArray(l)?l:[l]}return a},$t.props=$t.methods=$t.inject=$t.computed=function(t,e,n,r){if(!t)return e;var a=Object.create(null);return C(a,t),e&&C(a,e),a},$t.provide=It;var Dt=function(t,e){return void 0===e?t:e};function Mt(t,e,n){if(\"function\"==typeof e&&(e=e.options),function(t,e){var n=t.props;if(n){var r,a,o={};if(Array.isArray(n))for(r=n.length;r--;)\"string\"==typeof(a=n[r])&&(o[E(a)]={type:null});else if(f(n))for(var i in n)a=n[i],o[E(i)]=f(a)?a:{type:a};else 0;t.props=o}}(e),function(t,e){var n=t.inject;if(n){var r=t.inject={};if(Array.isArray(n))for(var a=0;a<n.length;a++)r[n[a]]={from:n[a]};else if(f(n))for(var o in n){var i=n[o];r[o]=f(i)?C({from:o},i):{from:i}}else 0}}(e),function(t){var e=t.directives;if(e)for(var n in e){var r=e[n];\"function\"==typeof r&&(e[n]={bind:r,update:r})}}(e),!e._base&&(e.extends&&(t=Mt(t,e.extends,n)),e.mixins))for(var r=0,a=e.mixins.length;r<a;r++)t=Mt(t,e.mixins[r],n);var o,i={};for(o in t)l(o);for(o in e)w(t,o)||l(o);function l(r){var a=$t[r]||Dt;i[r]=a(t[r],e[r],n,r)}return i}function Nt(t,e,n,r){if(\"string\"==typeof n){var a=t[e];if(w(a,n))return a[n];var o=E(n);if(w(a,o))return a[o];var i=S(o);return w(a,i)?a[i]:a[n]||a[o]||a[i]}}function Bt(t,e,n,r){var a=e[t],o=!w(n,t),i=n[t],l=Wt(Boolean,a.type);if(l>-1)if(o&&!w(a,\"default\"))i=!1;else if(\"\"===i||i===j(t)){var c=Wt(String,a.type);(c<0||l<c)&&(i=!0)}if(void 0===i){i=function(t,e,n){if(!w(e,\"default\"))return;var r=e.default;0;if(t&&t.$options.propsData&&void 0===t.$options.propsData[n]&&void 0!==t._props[n])return t._props[n];return\"function\"==typeof r&&\"Function\"!==zt(e.type)?r.call(t):r}(r,a,t);var u=kt;Et(!0),Ot(i),Et(u)}return i}function zt(t){var e=t&&t.toString().match(/^\\s*function (\\w+)/);return e?e[1]:\"\"}function Ut(t,e){return zt(t)===zt(e)}function Wt(t,e){if(!Array.isArray(e))return Ut(e,t)?0:-1;for(var n=0,r=e.length;n<r;n++)if(Ut(e[n],t))return n;return-1}function Vt(t,e,n){vt();try{if(e)for(var r=e;r=r.$parent;){var a=r.$options.errorCaptured;if(a)for(var o=0;o<a.length;o++)try{if(!1===a[o].call(r,t,e,n))return}catch(t){Ht(t,r,\"errorCaptured hook\")}}Ht(t,e,n)}finally{dt()}}function qt(t,e,n,r,a){var o;try{(o=n?t.apply(e,n):t.call(e))&&!o._isVue&&d(o)&&!o._handled&&(o.catch((function(t){return Vt(t,r,a+\" (Promise/async)\")})),o._handled=!0)}catch(t){Vt(t,r,a)}return o}function Ht(t,e,n){if(z.errorHandler)try{return z.errorHandler.call(null,t,e,n)}catch(e){e!==t&&Gt(e,null,\"config.errorHandler\")}Gt(t,e,n)}function Gt(t,e,n){if(!G&&!K||\"undefined\"==typeof console)throw t;console.error(t)}var Kt,Jt=!1,Xt=[],Qt=!1;function Yt(){Qt=!1;var t=Xt.slice(0);Xt.length=0;for(var e=0;e<t.length;e++)t[e]()}if(\"undefined\"!=typeof Promise&&lt(Promise)){var Zt=Promise.resolve();Kt=function(){Zt.then(Yt),tt&&setTimeout(T)},Jt=!0}else if(Q||\"undefined\"==typeof MutationObserver||!lt(MutationObserver)&&\"[object MutationObserverConstructor]\"!==MutationObserver.toString())Kt=\"undefined\"!=typeof setImmediate&&lt(setImmediate)?function(){setImmediate(Yt)}:function(){setTimeout(Yt,0)};else{var te=1,ee=new MutationObserver(Yt),ne=document.createTextNode(String(te));ee.observe(ne,{characterData:!0}),Kt=function(){te=(te+1)%2,ne.data=String(te)},Jt=!0}function re(t,e){var n;if(Xt.push((function(){if(t)try{t.call(e)}catch(t){Vt(t,e,\"nextTick\")}else n&&n(e)})),Qt||(Qt=!0,Kt()),!t&&\"undefined\"!=typeof Promise)return new Promise((function(t){n=t}))}var ae=new ct;function oe(t){!function t(e,n){var r,a,o=Array.isArray(e);if(!o&&!s(e)||Object.isFrozen(e)||e instanceof mt)return;if(e.__ob__){var i=e.__ob__.dep.id;if(n.has(i))return;n.add(i)}if(o)for(r=e.length;r--;)t(e[r],n);else for(a=Object.keys(e),r=a.length;r--;)t(e[a[r]],n)}(t,ae),ae.clear()}var ie=P((function(t){var e=\"&\"===t.charAt(0),n=\"~\"===(t=e?t.slice(1):t).charAt(0),r=\"!\"===(t=n?t.slice(1):t).charAt(0);return{name:t=r?t.slice(1):t,once:n,capture:r,passive:e}}));function le(t,e){function n(){var t=arguments,r=n.fns;if(!Array.isArray(r))return qt(r,null,arguments,e,\"v-on handler\");for(var a=r.slice(),o=0;o<a.length;o++)qt(a[o],null,t,e,\"v-on handler\")}return n.fns=t,n}function ce(t,e,n,r,a,o){var l,u,s,h;for(l in t)u=t[l],s=e[l],h=ie(l),i(u)||(i(s)?(i(u.fns)&&(u=t[l]=le(u,o)),c(h.once)&&(u=t[l]=a(h.name,u,h.capture)),n(h.name,u,h.capture,h.passive,h.params)):u!==s&&(s.fns=u,t[l]=s));for(l in e)i(t[l])&&r((h=ie(l)).name,e[l],h.capture)}function ue(t,e,n){var r;t instanceof mt&&(t=t.data.hook||(t.data.hook={}));var a=t[e];function o(){n.apply(this,arguments),_(r.fns,o)}i(a)?r=le([o]):l(a.fns)&&c(a.merged)?(r=a).fns.push(o):r=le([a,o]),r.merged=!0,t[e]=r}function se(t,e,n,r,a){if(l(e)){if(w(e,n))return t[n]=e[n],a||delete e[n],!0;if(w(e,r))return t[n]=e[r],a||delete e[r],!0}return!1}function he(t){return u(t)?[yt(t)]:Array.isArray(t)?function t(e,n){var r,a,o,s,h=[];for(r=0;r<e.length;r++)i(a=e[r])||\"boolean\"==typeof a||(o=h.length-1,s=h[o],Array.isArray(a)?a.length>0&&(fe((a=t(a,(n||\"\")+\"_\"+r))[0])&&fe(s)&&(h[o]=yt(s.text+a[0].text),a.shift()),h.push.apply(h,a)):u(a)?fe(s)?h[o]=yt(s.text+a):\"\"!==a&&h.push(yt(a)):fe(a)&&fe(s)?h[o]=yt(s.text+a.text):(c(e._isVList)&&l(a.tag)&&i(a.key)&&l(n)&&(a.key=\"__vlist\"+n+\"_\"+r+\"__\"),h.push(a)));return h}(t):void 0}function fe(t){return l(t)&&l(t.text)&&!1===t.isComment}function pe(t,e){if(t){for(var n=Object.create(null),r=ut?Reflect.ownKeys(t):Object.keys(t),a=0;a<r.length;a++){var o=r[a];if(\"__ob__\"!==o){for(var i=t[o].from,l=e;l;){if(l._provided&&w(l._provided,i)){n[o]=l._provided[i];break}l=l.$parent}if(!l)if(\"default\"in t[o]){var c=t[o].default;n[o]=\"function\"==typeof c?c.call(e):c}else 0}}return n}}function ve(t,e){if(!t||!t.length)return{};for(var n={},r=0,a=t.length;r<a;r++){var o=t[r],i=o.data;if(i&&i.attrs&&i.attrs.slot&&delete i.attrs.slot,o.context!==e&&o.fnContext!==e||!i||null==i.slot)(n.default||(n.default=[])).push(o);else{var l=i.slot,c=n[l]||(n[l]=[]);\"template\"===o.tag?c.push.apply(c,o.children||[]):c.push(o)}}for(var u in n)n[u].every(de)&&delete n[u];return n}function de(t){return t.isComment&&!t.asyncFactory||\" \"===t.text}function me(t,e,n){var r,a=Object.keys(e).length>0,i=t?!!t.$stable:!a,l=t&&t.$key;if(t){if(t._normalized)return t._normalized;if(i&&n&&n!==o&&l===n.$key&&!a&&!n.$hasNormal)return n;for(var c in r={},t)t[c]&&\"$\"!==c[0]&&(r[c]=ge(e,c,t[c]))}else r={};for(var u in e)u in r||(r[u]=be(e,u));return t&&Object.isExtensible(t)&&(t._normalized=r),W(r,\"$stable\",i),W(r,\"$key\",l),W(r,\"$hasNormal\",a),r}function ge(t,e,n){var r=function(){var t=arguments.length?n.apply(null,arguments):n({});return(t=t&&\"object\"==typeof t&&!Array.isArray(t)?[t]:he(t))&&(0===t.length||1===t.length&&t[0].isComment)?void 0:t};return n.proxy&&Object.defineProperty(t,e,{get:r,enumerable:!0,configurable:!0}),r}function be(t,e){return function(){return t[e]}}function ye(t,e){var n,r,a,o,i;if(Array.isArray(t)||\"string\"==typeof t)for(n=new Array(t.length),r=0,a=t.length;r<a;r++)n[r]=e(t[r],r);else if(\"number\"==typeof t)for(n=new Array(t),r=0;r<t;r++)n[r]=e(r+1,r);else if(s(t))if(ut&&t[Symbol.iterator]){n=[];for(var c=t[Symbol.iterator](),u=c.next();!u.done;)n.push(e(u.value,n.length)),u=c.next()}else for(o=Object.keys(t),n=new Array(o.length),r=0,a=o.length;r<a;r++)i=o[r],n[r]=e(t[i],i,r);return l(n)||(n=[]),n._isVList=!0,n}function _e(t,e,n,r){var a,o=this.$scopedSlots[t];o?(n=n||{},r&&(n=C(C({},r),n)),a=o(n)||e):a=this.$slots[t]||e;var i=n&&n.slot;return i?this.$createElement(\"template\",{slot:i},a):a}function xe(t){return Nt(this.$options,\"filters\",t)||R}function we(t,e){return Array.isArray(t)?-1===t.indexOf(e):t!==e}function Pe(t,e,n,r,a){var o=z.keyCodes[e]||n;return a&&r&&!z.keyCodes[e]?we(a,r):o?we(o,t):r?j(r)!==e:void 0}function ke(t,e,n,r,a){if(n)if(s(n)){var o;Array.isArray(n)&&(n=$(n));var i=function(i){if(\"class\"===i||\"style\"===i||y(i))o=t;else{var l=t.attrs&&t.attrs.type;o=r||z.mustUseProp(e,l,i)?t.domProps||(t.domProps={}):t.attrs||(t.attrs={})}var c=E(i),u=j(i);c in o||u in o||(o[i]=n[i],a&&((t.on||(t.on={}))[\"update:\"+i]=function(t){n[i]=t}))};for(var l in n)i(l)}else;return t}function Ee(t,e){var n=this._staticTrees||(this._staticTrees=[]),r=n[t];return r&&!e||Oe(r=n[t]=this.$options.staticRenderFns[t].call(this._renderProxy,null,this),\"__static__\"+t,!1),r}function Se(t,e,n){return Oe(t,\"__once__\"+e+(n?\"_\"+n:\"\"),!0),t}function Oe(t,e,n){if(Array.isArray(t))for(var r=0;r<t.length;r++)t[r]&&\"string\"!=typeof t[r]&&je(t[r],e+\"_\"+r,n);else je(t,e,n)}function je(t,e,n){t.isStatic=!0,t.key=e,t.isOnce=n}function Le(t,e){if(e)if(f(e)){var n=t.on=t.on?C({},t.on):{};for(var r in e){var a=n[r],o=e[r];n[r]=a?[].concat(a,o):o}}else;return t}function Ae(t,e,n,r){e=e||{$stable:!n};for(var a=0;a<t.length;a++){var o=t[a];Array.isArray(o)?Ae(o,e,n):o&&(o.proxy&&(o.fn.proxy=!0),e[o.key]=o.fn)}return r&&(e.$key=r),e}function Ce(t,e){for(var n=0;n<e.length;n+=2){var r=e[n];\"string\"==typeof r&&r&&(t[e[n]]=e[n+1])}return t}function $e(t,e){return\"string\"==typeof t?e+t:t}function Te(t){t._o=Se,t._n=g,t._s=m,t._l=ye,t._t=_e,t._q=F,t._i=D,t._m=Ee,t._f=xe,t._k=Pe,t._b=ke,t._v=yt,t._e=bt,t._u=Ae,t._g=Le,t._d=Ce,t._p=$e}function Ie(t,e,n,r,a){var i,l=this,u=a.options;w(r,\"_uid\")?(i=Object.create(r))._original=r:(i=r,r=r._original);var s=c(u._compiled),h=!s;this.data=t,this.props=e,this.children=n,this.parent=r,this.listeners=t.on||o,this.injections=pe(u.inject,r),this.slots=function(){return l.$slots||me(t.scopedSlots,l.$slots=ve(n,r)),l.$slots},Object.defineProperty(this,\"scopedSlots\",{enumerable:!0,get:function(){return me(t.scopedSlots,this.slots())}}),s&&(this.$options=u,this.$slots=this.slots(),this.$scopedSlots=me(t.scopedSlots,this.$slots)),u._scopeId?this._c=function(t,e,n,a){var o=ze(i,t,e,n,a,h);return o&&!Array.isArray(o)&&(o.fnScopeId=u._scopeId,o.fnContext=r),o}:this._c=function(t,e,n,r){return ze(i,t,e,n,r,h)}}function Re(t,e,n,r,a){var o=_t(t);return o.fnContext=n,o.fnOptions=r,e.slot&&((o.data||(o.data={})).slot=e.slot),o}function Fe(t,e){for(var n in e)t[E(n)]=e[n]}Te(Ie.prototype);var De={init:function(t,e){if(t.componentInstance&&!t.componentInstance._isDestroyed&&t.data.keepAlive){var n=t;De.prepatch(n,n)}else{(t.componentInstance=function(t,e){var n={_isComponent:!0,_parentVnode:t,parent:e},r=t.data.inlineTemplate;l(r)&&(n.render=r.render,n.staticRenderFns=r.staticRenderFns);return new t.componentOptions.Ctor(n)}(t,Qe)).$mount(e?t.elm:void 0,e)}},prepatch:function(t,e){var n=e.componentOptions;!function(t,e,n,r,a){0;var i=r.data.scopedSlots,l=t.$scopedSlots,c=!!(i&&!i.$stable||l!==o&&!l.$stable||i&&t.$scopedSlots.$key!==i.$key),u=!!(a||t.$options._renderChildren||c);t.$options._parentVnode=r,t.$vnode=r,t._vnode&&(t._vnode.parent=r);if(t.$options._renderChildren=a,t.$attrs=r.data.attrs||o,t.$listeners=n||o,e&&t.$options.props){Et(!1);for(var s=t._props,h=t.$options._propKeys||[],f=0;f<h.length;f++){var p=h[f],v=t.$options.props;s[p]=Bt(p,v,e,t)}Et(!0),t.$options.propsData=e}n=n||o;var d=t.$options._parentListeners;t.$options._parentListeners=n,Xe(t,n,d),u&&(t.$slots=ve(a,r.context),t.$forceUpdate());0}(e.componentInstance=t.componentInstance,n.propsData,n.listeners,e,n.children)},insert:function(t){var e,n=t.context,r=t.componentInstance;r._isMounted||(r._isMounted=!0,en(r,\"mounted\")),t.data.keepAlive&&(n._isMounted?((e=r)._inactive=!1,rn.push(e)):tn(r,!0))},destroy:function(t){var e=t.componentInstance;e._isDestroyed||(t.data.keepAlive?function t(e,n){if(n&&(e._directInactive=!0,Ze(e)))return;if(!e._inactive){e._inactive=!0;for(var r=0;r<e.$children.length;r++)t(e.$children[r]);en(e,\"deactivated\")}}(e,!0):e.$destroy())}},Me=Object.keys(De);function Ne(t,e,n,r,a){if(!i(t)){var u=n.$options._base;if(s(t)&&(t=u.extend(t)),\"function\"==typeof t){var h;if(i(t.cid)&&void 0===(t=function(t,e){if(c(t.error)&&l(t.errorComp))return t.errorComp;if(l(t.resolved))return t.resolved;var n=We;n&&l(t.owners)&&-1===t.owners.indexOf(n)&&t.owners.push(n);if(c(t.loading)&&l(t.loadingComp))return t.loadingComp;if(n&&!l(t.owners)){var r=t.owners=[n],a=!0,o=null,u=null;n.$on(\"hook:destroyed\",(function(){return _(r,n)}));var h=function(t){for(var e=0,n=r.length;e<n;e++)r[e].$forceUpdate();t&&(r.length=0,null!==o&&(clearTimeout(o),o=null),null!==u&&(clearTimeout(u),u=null))},f=M((function(n){t.resolved=Ve(n,e),a?r.length=0:h(!0)})),p=M((function(e){l(t.errorComp)&&(t.error=!0,h(!0))})),v=t(f,p);return s(v)&&(d(v)?i(t.resolved)&&v.then(f,p):d(v.component)&&(v.component.then(f,p),l(v.error)&&(t.errorComp=Ve(v.error,e)),l(v.loading)&&(t.loadingComp=Ve(v.loading,e),0===v.delay?t.loading=!0:o=setTimeout((function(){o=null,i(t.resolved)&&i(t.error)&&(t.loading=!0,h(!1))}),v.delay||200)),l(v.timeout)&&(u=setTimeout((function(){u=null,i(t.resolved)&&p(null)}),v.timeout)))),a=!1,t.loading?t.loadingComp:t.resolved}}(h=t,u)))return function(t,e,n,r,a){var o=bt();return o.asyncFactory=t,o.asyncMeta={data:e,context:n,children:r,tag:a},o}(h,e,n,r,a);e=e||{},kn(t),l(e.model)&&function(t,e){var n=t.model&&t.model.prop||\"value\",r=t.model&&t.model.event||\"input\";(e.attrs||(e.attrs={}))[n]=e.model.value;var a=e.on||(e.on={}),o=a[r],i=e.model.callback;l(o)?(Array.isArray(o)?-1===o.indexOf(i):o!==i)&&(a[r]=[i].concat(o)):a[r]=i}(t.options,e);var f=function(t,e,n){var r=e.options.props;if(!i(r)){var a={},o=t.attrs,c=t.props;if(l(o)||l(c))for(var u in r){var s=j(u);se(a,c,u,s,!0)||se(a,o,u,s,!1)}return a}}(e,t);if(c(t.options.functional))return function(t,e,n,r,a){var i=t.options,c={},u=i.props;if(l(u))for(var s in u)c[s]=Bt(s,u,e||o);else l(n.attrs)&&Fe(c,n.attrs),l(n.props)&&Fe(c,n.props);var h=new Ie(n,c,a,r,t),f=i.render.call(null,h._c,h);if(f instanceof mt)return Re(f,n,h.parent,i,h);if(Array.isArray(f)){for(var p=he(f)||[],v=new Array(p.length),d=0;d<p.length;d++)v[d]=Re(p[d],n,h.parent,i,h);return v}}(t,f,e,n,r);var p=e.on;if(e.on=e.nativeOn,c(t.options.abstract)){var v=e.slot;e={},v&&(e.slot=v)}!function(t){for(var e=t.hook||(t.hook={}),n=0;n<Me.length;n++){var r=Me[n],a=e[r],o=De[r];a===o||a&&a._merged||(e[r]=a?Be(o,a):o)}}(e);var m=t.options.name||a;return new mt(\"vue-component-\"+t.cid+(m?\"-\"+m:\"\"),e,void 0,void 0,void 0,n,{Ctor:t,propsData:f,listeners:p,tag:a,children:r},h)}}}function Be(t,e){var n=function(n,r){t(n,r),e(n,r)};return n._merged=!0,n}function ze(t,e,n,r,a,o){return(Array.isArray(n)||u(n))&&(a=r,r=n,n=void 0),c(o)&&(a=2),function(t,e,n,r,a){if(l(n)&&l(n.__ob__))return bt();l(n)&&l(n.is)&&(e=n.is);if(!e)return bt();0;Array.isArray(r)&&\"function\"==typeof r[0]&&((n=n||{}).scopedSlots={default:r[0]},r.length=0);2===a?r=he(r):1===a&&(r=function(t){for(var e=0;e<t.length;e++)if(Array.isArray(t[e]))return Array.prototype.concat.apply([],t);return t}(r));var o,u;if(\"string\"==typeof e){var h;u=t.$vnode&&t.$vnode.ns||z.getTagNamespace(e),o=z.isReservedTag(e)?new mt(z.parsePlatformTagName(e),n,r,void 0,void 0,t):n&&n.pre||!l(h=Nt(t.$options,\"components\",e))?new mt(e,n,r,void 0,void 0,t):Ne(h,n,t,r,e)}else o=Ne(e,n,t,r);return Array.isArray(o)?o:l(o)?(l(u)&&function t(e,n,r){e.ns=n,\"foreignObject\"===e.tag&&(n=void 0,r=!0);if(l(e.children))for(var a=0,o=e.children.length;a<o;a++){var u=e.children[a];l(u.tag)&&(i(u.ns)||c(r)&&\"svg\"!==u.tag)&&t(u,n,r)}}(o,u),l(n)&&function(t){s(t.style)&&oe(t.style);s(t.class)&&oe(t.class)}(n),o):bt()}(t,e,n,r,a)}var Ue,We=null;function Ve(t,e){return(t.__esModule||ut&&\"Module\"===t[Symbol.toStringTag])&&(t=t.default),s(t)?e.extend(t):t}function qe(t){return t.isComment&&t.asyncFactory}function He(t){if(Array.isArray(t))for(var e=0;e<t.length;e++){var n=t[e];if(l(n)&&(l(n.componentOptions)||qe(n)))return n}}function Ge(t,e){Ue.$on(t,e)}function Ke(t,e){Ue.$off(t,e)}function Je(t,e){var n=Ue;return function r(){var a=e.apply(null,arguments);null!==a&&n.$off(t,r)}}function Xe(t,e,n){Ue=t,ce(e,n||{},Ge,Ke,Je,t),Ue=void 0}var Qe=null;function Ye(t){var e=Qe;return Qe=t,function(){Qe=e}}function Ze(t){for(;t&&(t=t.$parent);)if(t._inactive)return!0;return!1}function tn(t,e){if(e){if(t._directInactive=!1,Ze(t))return}else if(t._directInactive)return;if(t._inactive||null===t._inactive){t._inactive=!1;for(var n=0;n<t.$children.length;n++)tn(t.$children[n]);en(t,\"activated\")}}function en(t,e){vt();var n=t.$options[e],r=e+\" hook\";if(n)for(var a=0,o=n.length;a<o;a++)qt(n[a],t,null,t,r);t._hasHookEvent&&t.$emit(\"hook:\"+e),dt()}var nn=[],rn=[],an={},on=!1,ln=!1,cn=0;var un=0,sn=Date.now;if(G&&!Q){var hn=window.performance;hn&&\"function\"==typeof hn.now&&sn()>document.createEvent(\"Event\").timeStamp&&(sn=function(){return hn.now()})}function fn(){var t,e;for(un=sn(),ln=!0,nn.sort((function(t,e){return t.id-e.id})),cn=0;cn<nn.length;cn++)(t=nn[cn]).before&&t.before(),e=t.id,an[e]=null,t.run();var n=rn.slice(),r=nn.slice();cn=nn.length=rn.length=0,an={},on=ln=!1,function(t){for(var e=0;e<t.length;e++)t[e]._inactive=!0,tn(t[e],!0)}(n),function(t){var e=t.length;for(;e--;){var n=t[e],r=n.vm;r._watcher===n&&r._isMounted&&!r._isDestroyed&&en(r,\"updated\")}}(r),it&&z.devtools&&it.emit(\"flush\")}var pn=0,vn=function(t,e,n,r,a){this.vm=t,a&&(t._watcher=this),t._watchers.push(this),r?(this.deep=!!r.deep,this.user=!!r.user,this.lazy=!!r.lazy,this.sync=!!r.sync,this.before=r.before):this.deep=this.user=this.lazy=this.sync=!1,this.cb=n,this.id=++pn,this.active=!0,this.dirty=this.lazy,this.deps=[],this.newDeps=[],this.depIds=new ct,this.newDepIds=new ct,this.expression=\"\",\"function\"==typeof e?this.getter=e:(this.getter=function(t){if(!V.test(t)){var e=t.split(\".\");return function(t){for(var n=0;n<e.length;n++){if(!t)return;t=t[e[n]]}return t}}}(e),this.getter||(this.getter=T)),this.value=this.lazy?void 0:this.get()};vn.prototype.get=function(){var t;vt(this);var e=this.vm;try{t=this.getter.call(e,e)}catch(t){if(!this.user)throw t;Vt(t,e,'getter for watcher \"'+this.expression+'\"')}finally{this.deep&&oe(t),dt(),this.cleanupDeps()}return t},vn.prototype.addDep=function(t){var e=t.id;this.newDepIds.has(e)||(this.newDepIds.add(e),this.newDeps.push(t),this.depIds.has(e)||t.addSub(this))},vn.prototype.cleanupDeps=function(){for(var t=this.deps.length;t--;){var e=this.deps[t];this.newDepIds.has(e.id)||e.removeSub(this)}var n=this.depIds;this.depIds=this.newDepIds,this.newDepIds=n,this.newDepIds.clear(),n=this.deps,this.deps=this.newDeps,this.newDeps=n,this.newDeps.length=0},vn.prototype.update=function(){this.lazy?this.dirty=!0:this.sync?this.run():function(t){var e=t.id;if(null==an[e]){if(an[e]=!0,ln){for(var n=nn.length-1;n>cn&&nn[n].id>t.id;)n--;nn.splice(n+1,0,t)}else nn.push(t);on||(on=!0,re(fn))}}(this)},vn.prototype.run=function(){if(this.active){var t=this.get();if(t!==this.value||s(t)||this.deep){var e=this.value;if(this.value=t,this.user)try{this.cb.call(this.vm,t,e)}catch(t){Vt(t,this.vm,'callback for watcher \"'+this.expression+'\"')}else this.cb.call(this.vm,t,e)}}},vn.prototype.evaluate=function(){this.value=this.get(),this.dirty=!1},vn.prototype.depend=function(){for(var t=this.deps.length;t--;)this.deps[t].depend()},vn.prototype.teardown=function(){if(this.active){this.vm._isBeingDestroyed||_(this.vm._watchers,this);for(var t=this.deps.length;t--;)this.deps[t].removeSub(this);this.active=!1}};var dn={enumerable:!0,configurable:!0,get:T,set:T};function mn(t,e,n){dn.get=function(){return this[e][n]},dn.set=function(t){this[e][n]=t},Object.defineProperty(t,n,dn)}function gn(t){t._watchers=[];var e=t.$options;e.props&&function(t,e){var n=t.$options.propsData||{},r=t._props={},a=t.$options._propKeys=[];t.$parent&&Et(!1);var o=function(o){a.push(o);var i=Bt(o,e,n,t);jt(r,o,i),o in t||mn(t,\"_props\",o)};for(var i in e)o(i);Et(!0)}(t,e.props),e.methods&&function(t,e){t.$options.props;for(var n in e)t[n]=\"function\"!=typeof e[n]?T:L(e[n],t)}(t,e.methods),e.data?function(t){var e=t.$options.data;f(e=t._data=\"function\"==typeof e?function(t,e){vt();try{return t.call(e,e)}catch(t){return Vt(t,e,\"data()\"),{}}finally{dt()}}(e,t):e||{})||(e={});var n=Object.keys(e),r=t.$options.props,a=(t.$options.methods,n.length);for(;a--;){var o=n[a];0,r&&w(r,o)||(i=void 0,36!==(i=(o+\"\").charCodeAt(0))&&95!==i&&mn(t,\"_data\",o))}var i;Ot(e,!0)}(t):Ot(t._data={},!0),e.computed&&function(t,e){var n=t._computedWatchers=Object.create(null),r=ot();for(var a in e){var o=e[a],i=\"function\"==typeof o?o:o.get;0,r||(n[a]=new vn(t,i||T,T,bn)),a in t||yn(t,a,o)}}(t,e.computed),e.watch&&e.watch!==nt&&function(t,e){for(var n in e){var r=e[n];if(Array.isArray(r))for(var a=0;a<r.length;a++)wn(t,n,r[a]);else wn(t,n,r)}}(t,e.watch)}var bn={lazy:!0};function yn(t,e,n){var r=!ot();\"function\"==typeof n?(dn.get=r?_n(e):xn(n),dn.set=T):(dn.get=n.get?r&&!1!==n.cache?_n(e):xn(n.get):T,dn.set=n.set||T),Object.defineProperty(t,e,dn)}function _n(t){return function(){var e=this._computedWatchers&&this._computedWatchers[t];if(e)return e.dirty&&e.evaluate(),ft.target&&e.depend(),e.value}}function xn(t){return function(){return t.call(this,this)}}function wn(t,e,n,r){return f(n)&&(r=n,n=n.handler),\"string\"==typeof n&&(n=t[n]),t.$watch(e,n,r)}var Pn=0;function kn(t){var e=t.options;if(t.super){var n=kn(t.super);if(n!==t.superOptions){t.superOptions=n;var r=function(t){var e,n=t.options,r=t.sealedOptions;for(var a in n)n[a]!==r[a]&&(e||(e={}),e[a]=n[a]);return e}(t);r&&C(t.extendOptions,r),(e=t.options=Mt(n,t.extendOptions)).name&&(e.components[e.name]=t)}}return e}function En(t){this._init(t)}function Sn(t){t.cid=0;var e=1;t.extend=function(t){t=t||{};var n=this,r=n.cid,a=t._Ctor||(t._Ctor={});if(a[r])return a[r];var o=t.name||n.options.name;var i=function(t){this._init(t)};return(i.prototype=Object.create(n.prototype)).constructor=i,i.cid=e++,i.options=Mt(n.options,t),i.super=n,i.options.props&&function(t){var e=t.options.props;for(var n in e)mn(t.prototype,\"_props\",n)}(i),i.options.computed&&function(t){var e=t.options.computed;for(var n in e)yn(t.prototype,n,e[n])}(i),i.extend=n.extend,i.mixin=n.mixin,i.use=n.use,N.forEach((function(t){i[t]=n[t]})),o&&(i.options.components[o]=i),i.superOptions=n.options,i.extendOptions=t,i.sealedOptions=C({},i.options),a[r]=i,i}}function On(t){return t&&(t.Ctor.options.name||t.tag)}function jn(t,e){return Array.isArray(t)?t.indexOf(e)>-1:\"string\"==typeof t?t.split(\",\").indexOf(e)>-1:!!p(t)&&t.test(e)}function Ln(t,e){var n=t.cache,r=t.keys,a=t._vnode;for(var o in n){var i=n[o];if(i){var l=On(i.componentOptions);l&&!e(l)&&An(n,o,r,a)}}}function An(t,e,n,r){var a=t[e];!a||r&&a.tag===r.tag||a.componentInstance.$destroy(),t[e]=null,_(n,e)}En.prototype._init=function(t){var e=this;e._uid=Pn++,e._isVue=!0,t&&t._isComponent?function(t,e){var n=t.$options=Object.create(t.constructor.options),r=e._parentVnode;n.parent=e.parent,n._parentVnode=r;var a=r.componentOptions;n.propsData=a.propsData,n._parentListeners=a.listeners,n._renderChildren=a.children,n._componentTag=a.tag,e.render&&(n.render=e.render,n.staticRenderFns=e.staticRenderFns)}(e,t):e.$options=Mt(kn(e.constructor),t||{},e),e._renderProxy=e,e._self=e,function(t){var e=t.$options,n=e.parent;if(n&&!e.abstract){for(;n.$options.abstract&&n.$parent;)n=n.$parent;n.$children.push(t)}t.$parent=n,t.$root=n?n.$root:t,t.$children=[],t.$refs={},t._watcher=null,t._inactive=null,t._directInactive=!1,t._isMounted=!1,t._isDestroyed=!1,t._isBeingDestroyed=!1}(e),function(t){t._events=Object.create(null),t._hasHookEvent=!1;var e=t.$options._parentListeners;e&&Xe(t,e)}(e),function(t){t._vnode=null,t._staticTrees=null;var e=t.$options,n=t.$vnode=e._parentVnode,r=n&&n.context;t.$slots=ve(e._renderChildren,r),t.$scopedSlots=o,t._c=function(e,n,r,a){return ze(t,e,n,r,a,!1)},t.$createElement=function(e,n,r,a){return ze(t,e,n,r,a,!0)};var a=n&&n.data;jt(t,\"$attrs\",a&&a.attrs||o,null,!0),jt(t,\"$listeners\",e._parentListeners||o,null,!0)}(e),en(e,\"beforeCreate\"),function(t){var e=pe(t.$options.inject,t);e&&(Et(!1),Object.keys(e).forEach((function(n){jt(t,n,e[n])})),Et(!0))}(e),gn(e),function(t){var e=t.$options.provide;e&&(t._provided=\"function\"==typeof e?e.call(t):e)}(e),en(e,\"created\"),e.$options.el&&e.$mount(e.$options.el)},function(t){var e={get:function(){return this._data}},n={get:function(){return this._props}};Object.defineProperty(t.prototype,\"$data\",e),Object.defineProperty(t.prototype,\"$props\",n),t.prototype.$set=Lt,t.prototype.$delete=At,t.prototype.$watch=function(t,e,n){if(f(e))return wn(this,t,e,n);(n=n||{}).user=!0;var r=new vn(this,t,e,n);if(n.immediate)try{e.call(this,r.value)}catch(t){Vt(t,this,'callback for immediate watcher \"'+r.expression+'\"')}return function(){r.teardown()}}}(En),function(t){var e=/^hook:/;t.prototype.$on=function(t,n){var r=this;if(Array.isArray(t))for(var a=0,o=t.length;a<o;a++)r.$on(t[a],n);else(r._events[t]||(r._events[t]=[])).push(n),e.test(t)&&(r._hasHookEvent=!0);return r},t.prototype.$once=function(t,e){var n=this;function r(){n.$off(t,r),e.apply(n,arguments)}return r.fn=e,n.$on(t,r),n},t.prototype.$off=function(t,e){var n=this;if(!arguments.length)return n._events=Object.create(null),n;if(Array.isArray(t)){for(var r=0,a=t.length;r<a;r++)n.$off(t[r],e);return n}var o,i=n._events[t];if(!i)return n;if(!e)return n._events[t]=null,n;for(var l=i.length;l--;)if((o=i[l])===e||o.fn===e){i.splice(l,1);break}return n},t.prototype.$emit=function(t){var e=this,n=e._events[t];if(n){n=n.length>1?A(n):n;for(var r=A(arguments,1),a='event handler for \"'+t+'\"',o=0,i=n.length;o<i;o++)qt(n[o],e,r,e,a)}return e}}(En),function(t){t.prototype._update=function(t,e){var n=this,r=n.$el,a=n._vnode,o=Ye(n);n._vnode=t,n.$el=a?n.__patch__(a,t):n.__patch__(n.$el,t,e,!1),o(),r&&(r.__vue__=null),n.$el&&(n.$el.__vue__=n),n.$vnode&&n.$parent&&n.$vnode===n.$parent._vnode&&(n.$parent.$el=n.$el)},t.prototype.$forceUpdate=function(){this._watcher&&this._watcher.update()},t.prototype.$destroy=function(){var t=this;if(!t._isBeingDestroyed){en(t,\"beforeDestroy\"),t._isBeingDestroyed=!0;var e=t.$parent;!e||e._isBeingDestroyed||t.$options.abstract||_(e.$children,t),t._watcher&&t._watcher.teardown();for(var n=t._watchers.length;n--;)t._watchers[n].teardown();t._data.__ob__&&t._data.__ob__.vmCount--,t._isDestroyed=!0,t.__patch__(t._vnode,null),en(t,\"destroyed\"),t.$off(),t.$el&&(t.$el.__vue__=null),t.$vnode&&(t.$vnode.parent=null)}}}(En),function(t){Te(t.prototype),t.prototype.$nextTick=function(t){return re(t,this)},t.prototype._render=function(){var t,e=this,n=e.$options,r=n.render,a=n._parentVnode;a&&(e.$scopedSlots=me(a.data.scopedSlots,e.$slots,e.$scopedSlots)),e.$vnode=a;try{We=e,t=r.call(e._renderProxy,e.$createElement)}catch(n){Vt(n,e,\"render\"),t=e._vnode}finally{We=null}return Array.isArray(t)&&1===t.length&&(t=t[0]),t instanceof mt||(t=bt()),t.parent=a,t}}(En);var Cn=[String,RegExp,Array],$n={KeepAlive:{name:\"keep-alive\",abstract:!0,props:{include:Cn,exclude:Cn,max:[String,Number]},created:function(){this.cache=Object.create(null),this.keys=[]},destroyed:function(){for(var t in this.cache)An(this.cache,t,this.keys)},mounted:function(){var t=this;this.$watch(\"include\",(function(e){Ln(t,(function(t){return jn(e,t)}))})),this.$watch(\"exclude\",(function(e){Ln(t,(function(t){return!jn(e,t)}))}))},render:function(){var t=this.$slots.default,e=He(t),n=e&&e.componentOptions;if(n){var r=On(n),a=this.include,o=this.exclude;if(a&&(!r||!jn(a,r))||o&&r&&jn(o,r))return e;var i=this.cache,l=this.keys,c=null==e.key?n.Ctor.cid+(n.tag?\"::\"+n.tag:\"\"):e.key;i[c]?(e.componentInstance=i[c].componentInstance,_(l,c),l.push(c)):(i[c]=e,l.push(c),this.max&&l.length>parseInt(this.max)&&An(i,l[0],l,this._vnode)),e.data.keepAlive=!0}return e||t&&t[0]}}};!function(t){var e={get:function(){return z}};Object.defineProperty(t,\"config\",e),t.util={warn:st,extend:C,mergeOptions:Mt,defineReactive:jt},t.set=Lt,t.delete=At,t.nextTick=re,t.observable=function(t){return Ot(t),t},t.options=Object.create(null),N.forEach((function(e){t.options[e+\"s\"]=Object.create(null)})),t.options._base=t,C(t.options.components,$n),function(t){t.use=function(t){var e=this._installedPlugins||(this._installedPlugins=[]);if(e.indexOf(t)>-1)return this;var n=A(arguments,1);return n.unshift(this),\"function\"==typeof t.install?t.install.apply(t,n):\"function\"==typeof t&&t.apply(null,n),e.push(t),this}}(t),function(t){t.mixin=function(t){return this.options=Mt(this.options,t),this}}(t),Sn(t),function(t){N.forEach((function(e){t[e]=function(t,n){return n?(\"component\"===e&&f(n)&&(n.name=n.name||t,n=this.options._base.extend(n)),\"directive\"===e&&\"function\"==typeof n&&(n={bind:n,update:n}),this.options[e+\"s\"][t]=n,n):this.options[e+\"s\"][t]}}))}(t)}(En),Object.defineProperty(En.prototype,\"$isServer\",{get:ot}),Object.defineProperty(En.prototype,\"$ssrContext\",{get:function(){return this.$vnode&&this.$vnode.ssrContext}}),Object.defineProperty(En,\"FunctionalRenderContext\",{value:Ie}),En.version=\"2.6.12\";var Tn=b(\"style,class\"),In=b(\"input,textarea,option,select,progress\"),Rn=b(\"contenteditable,draggable,spellcheck\"),Fn=b(\"events,caret,typing,plaintext-only\"),Dn=b(\"allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,default,defaultchecked,defaultmuted,defaultselected,defer,disabled,enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,required,reversed,scoped,seamless,selected,sortable,translate,truespeed,typemustmatch,visible\"),Mn=\"http://www.w3.org/1999/xlink\",Nn=function(t){return\":\"===t.charAt(5)&&\"xlink\"===t.slice(0,5)},Bn=function(t){return Nn(t)?t.slice(6,t.length):\"\"},zn=function(t){return null==t||!1===t};function Un(t){for(var e=t.data,n=t,r=t;l(r.componentInstance);)(r=r.componentInstance._vnode)&&r.data&&(e=Wn(r.data,e));for(;l(n=n.parent);)n&&n.data&&(e=Wn(e,n.data));return function(t,e){if(l(t)||l(e))return Vn(t,qn(e));return\"\"}(e.staticClass,e.class)}function Wn(t,e){return{staticClass:Vn(t.staticClass,e.staticClass),class:l(t.class)?[t.class,e.class]:e.class}}function Vn(t,e){return t?e?t+\" \"+e:t:e||\"\"}function qn(t){return Array.isArray(t)?function(t){for(var e,n=\"\",r=0,a=t.length;r<a;r++)l(e=qn(t[r]))&&\"\"!==e&&(n&&(n+=\" \"),n+=e);return n}(t):s(t)?function(t){var e=\"\";for(var n in t)t[n]&&(e&&(e+=\" \"),e+=n);return e}(t):\"string\"==typeof t?t:\"\"}var Hn={svg:\"http://www.w3.org/2000/svg\",math:\"http://www.w3.org/1998/Math/MathML\"},Gn=b(\"html,body,base,head,link,meta,style,title,address,article,aside,footer,header,h1,h2,h3,h4,h5,h6,hgroup,nav,section,div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,rtc,ruby,s,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,embed,object,param,source,canvas,script,noscript,del,ins,caption,col,colgroup,table,thead,tbody,td,th,tr,button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,output,progress,select,textarea,details,dialog,menu,menuitem,summary,content,element,shadow,template,blockquote,iframe,tfoot\"),Kn=b(\"svg,animate,circle,clippath,cursor,defs,desc,ellipse,filter,font-face,foreignObject,g,glyph,image,line,marker,mask,missing-glyph,path,pattern,polygon,polyline,rect,switch,symbol,text,textpath,tspan,use,view\",!0),Jn=function(t){return Gn(t)||Kn(t)};var Xn=Object.create(null);var Qn=b(\"text,number,password,search,email,tel,url\");var Yn=Object.freeze({createElement:function(t,e){var n=document.createElement(t);return\"select\"!==t||e.data&&e.data.attrs&&void 0!==e.data.attrs.multiple&&n.setAttribute(\"multiple\",\"multiple\"),n},createElementNS:function(t,e){return document.createElementNS(Hn[t],e)},createTextNode:function(t){return document.createTextNode(t)},createComment:function(t){return document.createComment(t)},insertBefore:function(t,e,n){t.insertBefore(e,n)},removeChild:function(t,e){t.removeChild(e)},appendChild:function(t,e){t.appendChild(e)},parentNode:function(t){return t.parentNode},nextSibling:function(t){return t.nextSibling},tagName:function(t){return t.tagName},setTextContent:function(t,e){t.textContent=e},setStyleScope:function(t,e){t.setAttribute(e,\"\")}}),Zn={create:function(t,e){tr(e)},update:function(t,e){t.data.ref!==e.data.ref&&(tr(t,!0),tr(e))},destroy:function(t){tr(t,!0)}};function tr(t,e){var n=t.data.ref;if(l(n)){var r=t.context,a=t.componentInstance||t.elm,o=r.$refs;e?Array.isArray(o[n])?_(o[n],a):o[n]===a&&(o[n]=void 0):t.data.refInFor?Array.isArray(o[n])?o[n].indexOf(a)<0&&o[n].push(a):o[n]=[a]:o[n]=a}}var er=new mt(\"\",{},[]),nr=[\"create\",\"activate\",\"update\",\"remove\",\"destroy\"];function rr(t,e){return t.key===e.key&&(t.tag===e.tag&&t.isComment===e.isComment&&l(t.data)===l(e.data)&&function(t,e){if(\"input\"!==t.tag)return!0;var n,r=l(n=t.data)&&l(n=n.attrs)&&n.type,a=l(n=e.data)&&l(n=n.attrs)&&n.type;return r===a||Qn(r)&&Qn(a)}(t,e)||c(t.isAsyncPlaceholder)&&t.asyncFactory===e.asyncFactory&&i(e.asyncFactory.error))}function ar(t,e,n){var r,a,o={};for(r=e;r<=n;++r)l(a=t[r].key)&&(o[a]=r);return o}var or={create:ir,update:ir,destroy:function(t){ir(t,er)}};function ir(t,e){(t.data.directives||e.data.directives)&&function(t,e){var n,r,a,o=t===er,i=e===er,l=cr(t.data.directives,t.context),c=cr(e.data.directives,e.context),u=[],s=[];for(n in c)r=l[n],a=c[n],r?(a.oldValue=r.value,a.oldArg=r.arg,sr(a,\"update\",e,t),a.def&&a.def.componentUpdated&&s.push(a)):(sr(a,\"bind\",e,t),a.def&&a.def.inserted&&u.push(a));if(u.length){var h=function(){for(var n=0;n<u.length;n++)sr(u[n],\"inserted\",e,t)};o?ue(e,\"insert\",h):h()}s.length&&ue(e,\"postpatch\",(function(){for(var n=0;n<s.length;n++)sr(s[n],\"componentUpdated\",e,t)}));if(!o)for(n in l)c[n]||sr(l[n],\"unbind\",t,t,i)}(t,e)}var lr=Object.create(null);function cr(t,e){var n,r,a=Object.create(null);if(!t)return a;for(n=0;n<t.length;n++)(r=t[n]).modifiers||(r.modifiers=lr),a[ur(r)]=r,r.def=Nt(e.$options,\"directives\",r.name);return a}function ur(t){return t.rawName||t.name+\".\"+Object.keys(t.modifiers||{}).join(\".\")}function sr(t,e,n,r,a){var o=t.def&&t.def[e];if(o)try{o(n.elm,t,n,r,a)}catch(r){Vt(r,n.context,\"directive \"+t.name+\" \"+e+\" hook\")}}var hr=[Zn,or];function fr(t,e){var n=e.componentOptions;if(!(l(n)&&!1===n.Ctor.options.inheritAttrs||i(t.data.attrs)&&i(e.data.attrs))){var r,a,o=e.elm,c=t.data.attrs||{},u=e.data.attrs||{};for(r in l(u.__ob__)&&(u=e.data.attrs=C({},u)),u)a=u[r],c[r]!==a&&pr(o,r,a);for(r in(Q||Z)&&u.value!==c.value&&pr(o,\"value\",u.value),c)i(u[r])&&(Nn(r)?o.removeAttributeNS(Mn,Bn(r)):Rn(r)||o.removeAttribute(r))}}function pr(t,e,n){t.tagName.indexOf(\"-\")>-1?vr(t,e,n):Dn(e)?zn(n)?t.removeAttribute(e):(n=\"allowfullscreen\"===e&&\"EMBED\"===t.tagName?\"true\":e,t.setAttribute(e,n)):Rn(e)?t.setAttribute(e,function(t,e){return zn(e)||\"false\"===e?\"false\":\"contenteditable\"===t&&Fn(e)?e:\"true\"}(e,n)):Nn(e)?zn(n)?t.removeAttributeNS(Mn,Bn(e)):t.setAttributeNS(Mn,e,n):vr(t,e,n)}function vr(t,e,n){if(zn(n))t.removeAttribute(e);else{if(Q&&!Y&&\"TEXTAREA\"===t.tagName&&\"placeholder\"===e&&\"\"!==n&&!t.__ieph){var r=function(e){e.stopImmediatePropagation(),t.removeEventListener(\"input\",r)};t.addEventListener(\"input\",r),t.__ieph=!0}t.setAttribute(e,n)}}var dr={create:fr,update:fr};function mr(t,e){var n=e.elm,r=e.data,a=t.data;if(!(i(r.staticClass)&&i(r.class)&&(i(a)||i(a.staticClass)&&i(a.class)))){var o=Un(e),c=n._transitionClasses;l(c)&&(o=Vn(o,qn(c))),o!==n._prevClass&&(n.setAttribute(\"class\",o),n._prevClass=o)}}var gr,br={create:mr,update:mr};function yr(t,e,n){var r=gr;return function a(){var o=e.apply(null,arguments);null!==o&&wr(t,a,n,r)}}var _r=Jt&&!(et&&Number(et[1])<=53);function xr(t,e,n,r){if(_r){var a=un,o=e;e=o._wrapper=function(t){if(t.target===t.currentTarget||t.timeStamp>=a||t.timeStamp<=0||t.target.ownerDocument!==document)return o.apply(this,arguments)}}gr.addEventListener(t,e,rt?{capture:n,passive:r}:n)}function wr(t,e,n,r){(r||gr).removeEventListener(t,e._wrapper||e,n)}function Pr(t,e){if(!i(t.data.on)||!i(e.data.on)){var n=e.data.on||{},r=t.data.on||{};gr=e.elm,function(t){if(l(t.__r)){var e=Q?\"change\":\"input\";t[e]=[].concat(t.__r,t[e]||[]),delete t.__r}l(t.__c)&&(t.change=[].concat(t.__c,t.change||[]),delete t.__c)}(n),ce(n,r,xr,wr,yr,e.context),gr=void 0}}var kr,Er={create:Pr,update:Pr};function Sr(t,e){if(!i(t.data.domProps)||!i(e.data.domProps)){var n,r,a=e.elm,o=t.data.domProps||{},c=e.data.domProps||{};for(n in l(c.__ob__)&&(c=e.data.domProps=C({},c)),o)n in c||(a[n]=\"\");for(n in c){if(r=c[n],\"textContent\"===n||\"innerHTML\"===n){if(e.children&&(e.children.length=0),r===o[n])continue;1===a.childNodes.length&&a.removeChild(a.childNodes[0])}if(\"value\"===n&&\"PROGRESS\"!==a.tagName){a._value=r;var u=i(r)?\"\":String(r);Or(a,u)&&(a.value=u)}else if(\"innerHTML\"===n&&Kn(a.tagName)&&i(a.innerHTML)){(kr=kr||document.createElement(\"div\")).innerHTML=\"<svg>\"+r+\"</svg>\";for(var s=kr.firstChild;a.firstChild;)a.removeChild(a.firstChild);for(;s.firstChild;)a.appendChild(s.firstChild)}else if(r!==o[n])try{a[n]=r}catch(t){}}}}function Or(t,e){return!t.composing&&(\"OPTION\"===t.tagName||function(t,e){var n=!0;try{n=document.activeElement!==t}catch(t){}return n&&t.value!==e}(t,e)||function(t,e){var n=t.value,r=t._vModifiers;if(l(r)){if(r.number)return g(n)!==g(e);if(r.trim)return n.trim()!==e.trim()}return n!==e}(t,e))}var jr={create:Sr,update:Sr},Lr=P((function(t){var e={},n=/:(.+)/;return t.split(/;(?![^(]*\\))/g).forEach((function(t){if(t){var r=t.split(n);r.length>1&&(e[r[0].trim()]=r[1].trim())}})),e}));function Ar(t){var e=Cr(t.style);return t.staticStyle?C(t.staticStyle,e):e}function Cr(t){return Array.isArray(t)?$(t):\"string\"==typeof t?Lr(t):t}var $r,Tr=/^--/,Ir=/\\s*!important$/,Rr=function(t,e,n){if(Tr.test(e))t.style.setProperty(e,n);else if(Ir.test(n))t.style.setProperty(j(e),n.replace(Ir,\"\"),\"important\");else{var r=Dr(e);if(Array.isArray(n))for(var a=0,o=n.length;a<o;a++)t.style[r]=n[a];else t.style[r]=n}},Fr=[\"Webkit\",\"Moz\",\"ms\"],Dr=P((function(t){if($r=$r||document.createElement(\"div\").style,\"filter\"!==(t=E(t))&&t in $r)return t;for(var e=t.charAt(0).toUpperCase()+t.slice(1),n=0;n<Fr.length;n++){var r=Fr[n]+e;if(r in $r)return r}}));function Mr(t,e){var n=e.data,r=t.data;if(!(i(n.staticStyle)&&i(n.style)&&i(r.staticStyle)&&i(r.style))){var a,o,c=e.elm,u=r.staticStyle,s=r.normalizedStyle||r.style||{},h=u||s,f=Cr(e.data.style)||{};e.data.normalizedStyle=l(f.__ob__)?C({},f):f;var p=function(t,e){var n,r={};if(e)for(var a=t;a.componentInstance;)(a=a.componentInstance._vnode)&&a.data&&(n=Ar(a.data))&&C(r,n);(n=Ar(t.data))&&C(r,n);for(var o=t;o=o.parent;)o.data&&(n=Ar(o.data))&&C(r,n);return r}(e,!0);for(o in h)i(p[o])&&Rr(c,o,\"\");for(o in p)(a=p[o])!==h[o]&&Rr(c,o,null==a?\"\":a)}}var Nr={create:Mr,update:Mr},Br=/\\s+/;function zr(t,e){if(e&&(e=e.trim()))if(t.classList)e.indexOf(\" \")>-1?e.split(Br).forEach((function(e){return t.classList.add(e)})):t.classList.add(e);else{var n=\" \"+(t.getAttribute(\"class\")||\"\")+\" \";n.indexOf(\" \"+e+\" \")<0&&t.setAttribute(\"class\",(n+e).trim())}}function Ur(t,e){if(e&&(e=e.trim()))if(t.classList)e.indexOf(\" \")>-1?e.split(Br).forEach((function(e){return t.classList.remove(e)})):t.classList.remove(e),t.classList.length||t.removeAttribute(\"class\");else{for(var n=\" \"+(t.getAttribute(\"class\")||\"\")+\" \",r=\" \"+e+\" \";n.indexOf(r)>=0;)n=n.replace(r,\" \");(n=n.trim())?t.setAttribute(\"class\",n):t.removeAttribute(\"class\")}}function Wr(t){if(t){if(\"object\"==typeof t){var e={};return!1!==t.css&&C(e,Vr(t.name||\"v\")),C(e,t),e}return\"string\"==typeof t?Vr(t):void 0}}var Vr=P((function(t){return{enterClass:t+\"-enter\",enterToClass:t+\"-enter-to\",enterActiveClass:t+\"-enter-active\",leaveClass:t+\"-leave\",leaveToClass:t+\"-leave-to\",leaveActiveClass:t+\"-leave-active\"}})),qr=G&&!Y,Hr=\"transition\",Gr=\"transitionend\",Kr=\"animation\",Jr=\"animationend\";qr&&(void 0===window.ontransitionend&&void 0!==window.onwebkittransitionend&&(Hr=\"WebkitTransition\",Gr=\"webkitTransitionEnd\"),void 0===window.onanimationend&&void 0!==window.onwebkitanimationend&&(Kr=\"WebkitAnimation\",Jr=\"webkitAnimationEnd\"));var Xr=G?window.requestAnimationFrame?window.requestAnimationFrame.bind(window):setTimeout:function(t){return t()};function Qr(t){Xr((function(){Xr(t)}))}function Yr(t,e){var n=t._transitionClasses||(t._transitionClasses=[]);n.indexOf(e)<0&&(n.push(e),zr(t,e))}function Zr(t,e){t._transitionClasses&&_(t._transitionClasses,e),Ur(t,e)}function ta(t,e,n){var r=na(t,e),a=r.type,o=r.timeout,i=r.propCount;if(!a)return n();var l=\"transition\"===a?Gr:Jr,c=0,u=function(){t.removeEventListener(l,s),n()},s=function(e){e.target===t&&++c>=i&&u()};setTimeout((function(){c<i&&u()}),o+1),t.addEventListener(l,s)}var ea=/\\b(transform|all)(,|$)/;function na(t,e){var n,r=window.getComputedStyle(t),a=(r[Hr+\"Delay\"]||\"\").split(\", \"),o=(r[Hr+\"Duration\"]||\"\").split(\", \"),i=ra(a,o),l=(r[Kr+\"Delay\"]||\"\").split(\", \"),c=(r[Kr+\"Duration\"]||\"\").split(\", \"),u=ra(l,c),s=0,h=0;return\"transition\"===e?i>0&&(n=\"transition\",s=i,h=o.length):\"animation\"===e?u>0&&(n=\"animation\",s=u,h=c.length):h=(n=(s=Math.max(i,u))>0?i>u?\"transition\":\"animation\":null)?\"transition\"===n?o.length:c.length:0,{type:n,timeout:s,propCount:h,hasTransform:\"transition\"===n&&ea.test(r[Hr+\"Property\"])}}function ra(t,e){for(;t.length<e.length;)t=t.concat(t);return Math.max.apply(null,e.map((function(e,n){return aa(e)+aa(t[n])})))}function aa(t){return 1e3*Number(t.slice(0,-1).replace(\",\",\".\"))}function oa(t,e){var n=t.elm;l(n._leaveCb)&&(n._leaveCb.cancelled=!0,n._leaveCb());var r=Wr(t.data.transition);if(!i(r)&&!l(n._enterCb)&&1===n.nodeType){for(var a=r.css,o=r.type,c=r.enterClass,u=r.enterToClass,h=r.enterActiveClass,f=r.appearClass,p=r.appearToClass,v=r.appearActiveClass,d=r.beforeEnter,m=r.enter,b=r.afterEnter,y=r.enterCancelled,_=r.beforeAppear,x=r.appear,w=r.afterAppear,P=r.appearCancelled,k=r.duration,E=Qe,S=Qe.$vnode;S&&S.parent;)E=S.context,S=S.parent;var O=!E._isMounted||!t.isRootInsert;if(!O||x||\"\"===x){var j=O&&f?f:c,L=O&&v?v:h,A=O&&p?p:u,C=O&&_||d,$=O&&\"function\"==typeof x?x:m,T=O&&w||b,I=O&&P||y,R=g(s(k)?k.enter:k);0;var F=!1!==a&&!Y,D=ca($),N=n._enterCb=M((function(){F&&(Zr(n,A),Zr(n,L)),N.cancelled?(F&&Zr(n,j),I&&I(n)):T&&T(n),n._enterCb=null}));t.data.show||ue(t,\"insert\",(function(){var e=n.parentNode,r=e&&e._pending&&e._pending[t.key];r&&r.tag===t.tag&&r.elm._leaveCb&&r.elm._leaveCb(),$&&$(n,N)})),C&&C(n),F&&(Yr(n,j),Yr(n,L),Qr((function(){Zr(n,j),N.cancelled||(Yr(n,A),D||(la(R)?setTimeout(N,R):ta(n,o,N)))}))),t.data.show&&(e&&e(),$&&$(n,N)),F||D||N()}}}function ia(t,e){var n=t.elm;l(n._enterCb)&&(n._enterCb.cancelled=!0,n._enterCb());var r=Wr(t.data.transition);if(i(r)||1!==n.nodeType)return e();if(!l(n._leaveCb)){var a=r.css,o=r.type,c=r.leaveClass,u=r.leaveToClass,h=r.leaveActiveClass,f=r.beforeLeave,p=r.leave,v=r.afterLeave,d=r.leaveCancelled,m=r.delayLeave,b=r.duration,y=!1!==a&&!Y,_=ca(p),x=g(s(b)?b.leave:b);0;var w=n._leaveCb=M((function(){n.parentNode&&n.parentNode._pending&&(n.parentNode._pending[t.key]=null),y&&(Zr(n,u),Zr(n,h)),w.cancelled?(y&&Zr(n,c),d&&d(n)):(e(),v&&v(n)),n._leaveCb=null}));m?m(P):P()}function P(){w.cancelled||(!t.data.show&&n.parentNode&&((n.parentNode._pending||(n.parentNode._pending={}))[t.key]=t),f&&f(n),y&&(Yr(n,c),Yr(n,h),Qr((function(){Zr(n,c),w.cancelled||(Yr(n,u),_||(la(x)?setTimeout(w,x):ta(n,o,w)))}))),p&&p(n,w),y||_||w())}}function la(t){return\"number\"==typeof t&&!isNaN(t)}function ca(t){if(i(t))return!1;var e=t.fns;return l(e)?ca(Array.isArray(e)?e[0]:e):(t._length||t.length)>1}function ua(t,e){!0!==e.data.show&&oa(e)}var sa=function(t){var e,n,r={},a=t.modules,o=t.nodeOps;for(e=0;e<nr.length;++e)for(r[nr[e]]=[],n=0;n<a.length;++n)l(a[n][nr[e]])&&r[nr[e]].push(a[n][nr[e]]);function s(t){var e=o.parentNode(t);l(e)&&o.removeChild(e,t)}function h(t,e,n,a,i,u,s){if(l(t.elm)&&l(u)&&(t=u[s]=_t(t)),t.isRootInsert=!i,!function(t,e,n,a){var o=t.data;if(l(o)){var i=l(t.componentInstance)&&o.keepAlive;if(l(o=o.hook)&&l(o=o.init)&&o(t,!1),l(t.componentInstance))return f(t,e),p(n,t.elm,a),c(i)&&function(t,e,n,a){var o,i=t;for(;i.componentInstance;)if(i=i.componentInstance._vnode,l(o=i.data)&&l(o=o.transition)){for(o=0;o<r.activate.length;++o)r.activate[o](er,i);e.push(i);break}p(n,t.elm,a)}(t,e,n,a),!0}}(t,e,n,a)){var h=t.data,d=t.children,b=t.tag;l(b)?(t.elm=t.ns?o.createElementNS(t.ns,b):o.createElement(b,t),g(t),v(t,d,e),l(h)&&m(t,e),p(n,t.elm,a)):c(t.isComment)?(t.elm=o.createComment(t.text),p(n,t.elm,a)):(t.elm=o.createTextNode(t.text),p(n,t.elm,a))}}function f(t,e){l(t.data.pendingInsert)&&(e.push.apply(e,t.data.pendingInsert),t.data.pendingInsert=null),t.elm=t.componentInstance.$el,d(t)?(m(t,e),g(t)):(tr(t),e.push(t))}function p(t,e,n){l(t)&&(l(n)?o.parentNode(n)===t&&o.insertBefore(t,e,n):o.appendChild(t,e))}function v(t,e,n){if(Array.isArray(e)){0;for(var r=0;r<e.length;++r)h(e[r],n,t.elm,null,!0,e,r)}else u(t.text)&&o.appendChild(t.elm,o.createTextNode(String(t.text)))}function d(t){for(;t.componentInstance;)t=t.componentInstance._vnode;return l(t.tag)}function m(t,n){for(var a=0;a<r.create.length;++a)r.create[a](er,t);l(e=t.data.hook)&&(l(e.create)&&e.create(er,t),l(e.insert)&&n.push(t))}function g(t){var e;if(l(e=t.fnScopeId))o.setStyleScope(t.elm,e);else for(var n=t;n;)l(e=n.context)&&l(e=e.$options._scopeId)&&o.setStyleScope(t.elm,e),n=n.parent;l(e=Qe)&&e!==t.context&&e!==t.fnContext&&l(e=e.$options._scopeId)&&o.setStyleScope(t.elm,e)}function y(t,e,n,r,a,o){for(;r<=a;++r)h(n[r],o,t,e,!1,n,r)}function _(t){var e,n,a=t.data;if(l(a))for(l(e=a.hook)&&l(e=e.destroy)&&e(t),e=0;e<r.destroy.length;++e)r.destroy[e](t);if(l(e=t.children))for(n=0;n<t.children.length;++n)_(t.children[n])}function x(t,e,n){for(;e<=n;++e){var r=t[e];l(r)&&(l(r.tag)?(w(r),_(r)):s(r.elm))}}function w(t,e){if(l(e)||l(t.data)){var n,a=r.remove.length+1;for(l(e)?e.listeners+=a:e=function(t,e){function n(){0==--n.listeners&&s(t)}return n.listeners=e,n}(t.elm,a),l(n=t.componentInstance)&&l(n=n._vnode)&&l(n.data)&&w(n,e),n=0;n<r.remove.length;++n)r.remove[n](t,e);l(n=t.data.hook)&&l(n=n.remove)?n(t,e):e()}else s(t.elm)}function P(t,e,n,r){for(var a=n;a<r;a++){var o=e[a];if(l(o)&&rr(t,o))return a}}function k(t,e,n,a,u,s){if(t!==e){l(e.elm)&&l(a)&&(e=a[u]=_t(e));var f=e.elm=t.elm;if(c(t.isAsyncPlaceholder))l(e.asyncFactory.resolved)?O(t.elm,e,n):e.isAsyncPlaceholder=!0;else if(c(e.isStatic)&&c(t.isStatic)&&e.key===t.key&&(c(e.isCloned)||c(e.isOnce)))e.componentInstance=t.componentInstance;else{var p,v=e.data;l(v)&&l(p=v.hook)&&l(p=p.prepatch)&&p(t,e);var m=t.children,g=e.children;if(l(v)&&d(e)){for(p=0;p<r.update.length;++p)r.update[p](t,e);l(p=v.hook)&&l(p=p.update)&&p(t,e)}i(e.text)?l(m)&&l(g)?m!==g&&function(t,e,n,r,a){var c,u,s,f=0,p=0,v=e.length-1,d=e[0],m=e[v],g=n.length-1,b=n[0],_=n[g],w=!a;for(0;f<=v&&p<=g;)i(d)?d=e[++f]:i(m)?m=e[--v]:rr(d,b)?(k(d,b,r,n,p),d=e[++f],b=n[++p]):rr(m,_)?(k(m,_,r,n,g),m=e[--v],_=n[--g]):rr(d,_)?(k(d,_,r,n,g),w&&o.insertBefore(t,d.elm,o.nextSibling(m.elm)),d=e[++f],_=n[--g]):rr(m,b)?(k(m,b,r,n,p),w&&o.insertBefore(t,m.elm,d.elm),m=e[--v],b=n[++p]):(i(c)&&(c=ar(e,f,v)),i(u=l(b.key)?c[b.key]:P(b,e,f,v))?h(b,r,t,d.elm,!1,n,p):rr(s=e[u],b)?(k(s,b,r,n,p),e[u]=void 0,w&&o.insertBefore(t,s.elm,d.elm)):h(b,r,t,d.elm,!1,n,p),b=n[++p]);f>v?y(t,i(n[g+1])?null:n[g+1].elm,n,p,g,r):p>g&&x(e,f,v)}(f,m,g,n,s):l(g)?(l(t.text)&&o.setTextContent(f,\"\"),y(f,null,g,0,g.length-1,n)):l(m)?x(m,0,m.length-1):l(t.text)&&o.setTextContent(f,\"\"):t.text!==e.text&&o.setTextContent(f,e.text),l(v)&&l(p=v.hook)&&l(p=p.postpatch)&&p(t,e)}}}function E(t,e,n){if(c(n)&&l(t.parent))t.parent.data.pendingInsert=e;else for(var r=0;r<e.length;++r)e[r].data.hook.insert(e[r])}var S=b(\"attrs,class,staticClass,staticStyle,key\");function O(t,e,n,r){var a,o=e.tag,i=e.data,u=e.children;if(r=r||i&&i.pre,e.elm=t,c(e.isComment)&&l(e.asyncFactory))return e.isAsyncPlaceholder=!0,!0;if(l(i)&&(l(a=i.hook)&&l(a=a.init)&&a(e,!0),l(a=e.componentInstance)))return f(e,n),!0;if(l(o)){if(l(u))if(t.hasChildNodes())if(l(a=i)&&l(a=a.domProps)&&l(a=a.innerHTML)){if(a!==t.innerHTML)return!1}else{for(var s=!0,h=t.firstChild,p=0;p<u.length;p++){if(!h||!O(h,u[p],n,r)){s=!1;break}h=h.nextSibling}if(!s||h)return!1}else v(e,u,n);if(l(i)){var d=!1;for(var g in i)if(!S(g)){d=!0,m(e,n);break}!d&&i.class&&oe(i.class)}}else t.data!==e.text&&(t.data=e.text);return!0}return function(t,e,n,a){if(!i(e)){var u,s=!1,f=[];if(i(t))s=!0,h(e,f);else{var p=l(t.nodeType);if(!p&&rr(t,e))k(t,e,f,null,null,a);else{if(p){if(1===t.nodeType&&t.hasAttribute(\"data-server-rendered\")&&(t.removeAttribute(\"data-server-rendered\"),n=!0),c(n)&&O(t,e,f))return E(e,f,!0),t;u=t,t=new mt(o.tagName(u).toLowerCase(),{},[],void 0,u)}var v=t.elm,m=o.parentNode(v);if(h(e,f,v._leaveCb?null:m,o.nextSibling(v)),l(e.parent))for(var g=e.parent,b=d(e);g;){for(var y=0;y<r.destroy.length;++y)r.destroy[y](g);if(g.elm=e.elm,b){for(var w=0;w<r.create.length;++w)r.create[w](er,g);var P=g.data.hook.insert;if(P.merged)for(var S=1;S<P.fns.length;S++)P.fns[S]()}else tr(g);g=g.parent}l(m)?x([t],0,0):l(t.tag)&&_(t)}}return E(e,f,s),e.elm}l(t)&&_(t)}}({nodeOps:Yn,modules:[dr,br,Er,jr,Nr,G?{create:ua,activate:ua,remove:function(t,e){!0!==t.data.show?ia(t,e):e()}}:{}].concat(hr)});Y&&document.addEventListener(\"selectionchange\",(function(){var t=document.activeElement;t&&t.vmodel&&ba(t,\"input\")}));var ha={inserted:function(t,e,n,r){\"select\"===n.tag?(r.elm&&!r.elm._vOptions?ue(n,\"postpatch\",(function(){ha.componentUpdated(t,e,n)})):fa(t,e,n.context),t._vOptions=[].map.call(t.options,da)):(\"textarea\"===n.tag||Qn(t.type))&&(t._vModifiers=e.modifiers,e.modifiers.lazy||(t.addEventListener(\"compositionstart\",ma),t.addEventListener(\"compositionend\",ga),t.addEventListener(\"change\",ga),Y&&(t.vmodel=!0)))},componentUpdated:function(t,e,n){if(\"select\"===n.tag){fa(t,e,n.context);var r=t._vOptions,a=t._vOptions=[].map.call(t.options,da);if(a.some((function(t,e){return!F(t,r[e])})))(t.multiple?e.value.some((function(t){return va(t,a)})):e.value!==e.oldValue&&va(e.value,a))&&ba(t,\"change\")}}};function fa(t,e,n){pa(t,e,n),(Q||Z)&&setTimeout((function(){pa(t,e,n)}),0)}function pa(t,e,n){var r=e.value,a=t.multiple;if(!a||Array.isArray(r)){for(var o,i,l=0,c=t.options.length;l<c;l++)if(i=t.options[l],a)o=D(r,da(i))>-1,i.selected!==o&&(i.selected=o);else if(F(da(i),r))return void(t.selectedIndex!==l&&(t.selectedIndex=l));a||(t.selectedIndex=-1)}}function va(t,e){return e.every((function(e){return!F(e,t)}))}function da(t){return\"_value\"in t?t._value:t.value}function ma(t){t.target.composing=!0}function ga(t){t.target.composing&&(t.target.composing=!1,ba(t.target,\"input\"))}function ba(t,e){var n=document.createEvent(\"HTMLEvents\");n.initEvent(e,!0,!0),t.dispatchEvent(n)}function ya(t){return!t.componentInstance||t.data&&t.data.transition?t:ya(t.componentInstance._vnode)}var _a={model:ha,show:{bind:function(t,e,n){var r=e.value,a=(n=ya(n)).data&&n.data.transition,o=t.__vOriginalDisplay=\"none\"===t.style.display?\"\":t.style.display;r&&a?(n.data.show=!0,oa(n,(function(){t.style.display=o}))):t.style.display=r?o:\"none\"},update:function(t,e,n){var r=e.value;!r!=!e.oldValue&&((n=ya(n)).data&&n.data.transition?(n.data.show=!0,r?oa(n,(function(){t.style.display=t.__vOriginalDisplay})):ia(n,(function(){t.style.display=\"none\"}))):t.style.display=r?t.__vOriginalDisplay:\"none\")},unbind:function(t,e,n,r,a){a||(t.style.display=t.__vOriginalDisplay)}}},xa={name:String,appear:Boolean,css:Boolean,mode:String,type:String,enterClass:String,leaveClass:String,enterToClass:String,leaveToClass:String,enterActiveClass:String,leaveActiveClass:String,appearClass:String,appearActiveClass:String,appearToClass:String,duration:[Number,String,Object]};function wa(t){var e=t&&t.componentOptions;return e&&e.Ctor.options.abstract?wa(He(e.children)):t}function Pa(t){var e={},n=t.$options;for(var r in n.propsData)e[r]=t[r];var a=n._parentListeners;for(var o in a)e[E(o)]=a[o];return e}function ka(t,e){if(/\\d-keep-alive$/.test(e.tag))return t(\"keep-alive\",{props:e.componentOptions.propsData})}var Ea=function(t){return t.tag||qe(t)},Sa=function(t){return\"show\"===t.name},Oa={name:\"transition\",props:xa,abstract:!0,render:function(t){var e=this,n=this.$slots.default;if(n&&(n=n.filter(Ea)).length){0;var r=this.mode;0;var a=n[0];if(function(t){for(;t=t.parent;)if(t.data.transition)return!0}(this.$vnode))return a;var o=wa(a);if(!o)return a;if(this._leaving)return ka(t,a);var i=\"__transition-\"+this._uid+\"-\";o.key=null==o.key?o.isComment?i+\"comment\":i+o.tag:u(o.key)?0===String(o.key).indexOf(i)?o.key:i+o.key:o.key;var l=(o.data||(o.data={})).transition=Pa(this),c=this._vnode,s=wa(c);if(o.data.directives&&o.data.directives.some(Sa)&&(o.data.show=!0),s&&s.data&&!function(t,e){return e.key===t.key&&e.tag===t.tag}(o,s)&&!qe(s)&&(!s.componentInstance||!s.componentInstance._vnode.isComment)){var h=s.data.transition=C({},l);if(\"out-in\"===r)return this._leaving=!0,ue(h,\"afterLeave\",(function(){e._leaving=!1,e.$forceUpdate()})),ka(t,a);if(\"in-out\"===r){if(qe(o))return c;var f,p=function(){f()};ue(l,\"afterEnter\",p),ue(l,\"enterCancelled\",p),ue(h,\"delayLeave\",(function(t){f=t}))}}return a}}},ja=C({tag:String,moveClass:String},xa);function La(t){t.elm._moveCb&&t.elm._moveCb(),t.elm._enterCb&&t.elm._enterCb()}function Aa(t){t.data.newPos=t.elm.getBoundingClientRect()}function Ca(t){var e=t.data.pos,n=t.data.newPos,r=e.left-n.left,a=e.top-n.top;if(r||a){t.data.moved=!0;var o=t.elm.style;o.transform=o.WebkitTransform=\"translate(\"+r+\"px,\"+a+\"px)\",o.transitionDuration=\"0s\"}}delete ja.mode;var $a={Transition:Oa,TransitionGroup:{props:ja,beforeMount:function(){var t=this,e=this._update;this._update=function(n,r){var a=Ye(t);t.__patch__(t._vnode,t.kept,!1,!0),t._vnode=t.kept,a(),e.call(t,n,r)}},render:function(t){for(var e=this.tag||this.$vnode.data.tag||\"span\",n=Object.create(null),r=this.prevChildren=this.children,a=this.$slots.default||[],o=this.children=[],i=Pa(this),l=0;l<a.length;l++){var c=a[l];if(c.tag)if(null!=c.key&&0!==String(c.key).indexOf(\"__vlist\"))o.push(c),n[c.key]=c,(c.data||(c.data={})).transition=i;else;}if(r){for(var u=[],s=[],h=0;h<r.length;h++){var f=r[h];f.data.transition=i,f.data.pos=f.elm.getBoundingClientRect(),n[f.key]?u.push(f):s.push(f)}this.kept=t(e,null,u),this.removed=s}return t(e,null,o)},updated:function(){var t=this.prevChildren,e=this.moveClass||(this.name||\"v\")+\"-move\";t.length&&this.hasMove(t[0].elm,e)&&(t.forEach(La),t.forEach(Aa),t.forEach(Ca),this._reflow=document.body.offsetHeight,t.forEach((function(t){if(t.data.moved){var n=t.elm,r=n.style;Yr(n,e),r.transform=r.WebkitTransform=r.transitionDuration=\"\",n.addEventListener(Gr,n._moveCb=function t(r){r&&r.target!==n||r&&!/transform$/.test(r.propertyName)||(n.removeEventListener(Gr,t),n._moveCb=null,Zr(n,e))})}})))},methods:{hasMove:function(t,e){if(!qr)return!1;if(this._hasMove)return this._hasMove;var n=t.cloneNode();t._transitionClasses&&t._transitionClasses.forEach((function(t){Ur(n,t)})),zr(n,e),n.style.display=\"none\",this.$el.appendChild(n);var r=na(n);return this.$el.removeChild(n),this._hasMove=r.hasTransform}}}};En.config.mustUseProp=function(t,e,n){return\"value\"===n&&In(t)&&\"button\"!==e||\"selected\"===n&&\"option\"===t||\"checked\"===n&&\"input\"===t||\"muted\"===n&&\"video\"===t},En.config.isReservedTag=Jn,En.config.isReservedAttr=Tn,En.config.getTagNamespace=function(t){return Kn(t)?\"svg\":\"math\"===t?\"math\":void 0},En.config.isUnknownElement=function(t){if(!G)return!0;if(Jn(t))return!1;if(t=t.toLowerCase(),null!=Xn[t])return Xn[t];var e=document.createElement(t);return t.indexOf(\"-\")>-1?Xn[t]=e.constructor===window.HTMLUnknownElement||e.constructor===window.HTMLElement:Xn[t]=/HTMLUnknownElement/.test(e.toString())},C(En.options.directives,_a),C(En.options.components,$a),En.prototype.__patch__=G?sa:T,En.prototype.$mount=function(t,e){return function(t,e,n){var r;return t.$el=e,t.$options.render||(t.$options.render=bt),en(t,\"beforeMount\"),r=function(){t._update(t._render(),n)},new vn(t,r,T,{before:function(){t._isMounted&&!t._isDestroyed&&en(t,\"beforeUpdate\")}},!0),n=!1,null==t.$vnode&&(t._isMounted=!0,en(t,\"mounted\")),t}(this,t=t&&G?function(t){if(\"string\"==typeof t){var e=document.querySelector(t);return e||document.createElement(\"div\")}return t}(t):void 0,e)},G&&setTimeout((function(){z.devtools&&it&&it.emit(\"init\",En)}),0);var Ta=En;\n/*!\n  * vue-router v3.5.1\n  * (c) 2021 Evan You\n  * @license MIT\n  */function Ia(t,e){for(var n in e)t[n]=e[n];return t}var Ra=/[!'()*]/g,Fa=function(t){return\"%\"+t.charCodeAt(0).toString(16)},Da=/%2C/g,Ma=function(t){return encodeURIComponent(t).replace(Ra,Fa).replace(Da,\",\")};function Na(t){try{return decodeURIComponent(t)}catch(t){0}return t}var Ba=function(t){return null==t||\"object\"==typeof t?t:String(t)};function za(t){var e={};return(t=t.trim().replace(/^(\\?|#|&)/,\"\"))?(t.split(\"&\").forEach((function(t){var n=t.replace(/\\+/g,\" \").split(\"=\"),r=Na(n.shift()),a=n.length>0?Na(n.join(\"=\")):null;void 0===e[r]?e[r]=a:Array.isArray(e[r])?e[r].push(a):e[r]=[e[r],a]})),e):e}function Ua(t){var e=t?Object.keys(t).map((function(e){var n=t[e];if(void 0===n)return\"\";if(null===n)return Ma(e);if(Array.isArray(n)){var r=[];return n.forEach((function(t){void 0!==t&&(null===t?r.push(Ma(e)):r.push(Ma(e)+\"=\"+Ma(t)))})),r.join(\"&\")}return Ma(e)+\"=\"+Ma(n)})).filter((function(t){return t.length>0})).join(\"&\"):null;return e?\"?\"+e:\"\"}var Wa=/\\/?$/;function Va(t,e,n,r){var a=r&&r.options.stringifyQuery,o=e.query||{};try{o=qa(o)}catch(t){}var i={name:e.name||t&&t.name,meta:t&&t.meta||{},path:e.path||\"/\",hash:e.hash||\"\",query:o,params:e.params||{},fullPath:Ka(e,a),matched:t?Ga(t):[]};return n&&(i.redirectedFrom=Ka(n,a)),Object.freeze(i)}function qa(t){if(Array.isArray(t))return t.map(qa);if(t&&\"object\"==typeof t){var e={};for(var n in t)e[n]=qa(t[n]);return e}return t}var Ha=Va(null,{path:\"/\"});function Ga(t){for(var e=[];t;)e.unshift(t),t=t.parent;return e}function Ka(t,e){var n=t.path,r=t.query;void 0===r&&(r={});var a=t.hash;return void 0===a&&(a=\"\"),(n||\"/\")+(e||Ua)(r)+a}function Ja(t,e,n){return e===Ha?t===e:!!e&&(t.path&&e.path?t.path.replace(Wa,\"\")===e.path.replace(Wa,\"\")&&(n||t.hash===e.hash&&Xa(t.query,e.query)):!(!t.name||!e.name)&&(t.name===e.name&&(n||t.hash===e.hash&&Xa(t.query,e.query)&&Xa(t.params,e.params))))}function Xa(t,e){if(void 0===t&&(t={}),void 0===e&&(e={}),!t||!e)return t===e;var n=Object.keys(t).sort(),r=Object.keys(e).sort();return n.length===r.length&&n.every((function(n,a){var o=t[n];if(r[a]!==n)return!1;var i=e[n];return null==o||null==i?o===i:\"object\"==typeof o&&\"object\"==typeof i?Xa(o,i):String(o)===String(i)}))}function Qa(t){for(var e=0;e<t.matched.length;e++){var n=t.matched[e];for(var r in n.instances){var a=n.instances[r],o=n.enteredCbs[r];if(a&&o){delete n.enteredCbs[r];for(var i=0;i<o.length;i++)a._isBeingDestroyed||o[i](a)}}}}var Ya={name:\"RouterView\",functional:!0,props:{name:{type:String,default:\"default\"}},render:function(t,e){var n=e.props,r=e.children,a=e.parent,o=e.data;o.routerView=!0;for(var i=a.$createElement,l=n.name,c=a.$route,u=a._routerViewCache||(a._routerViewCache={}),s=0,h=!1;a&&a._routerRoot!==a;){var f=a.$vnode?a.$vnode.data:{};f.routerView&&s++,f.keepAlive&&a._directInactive&&a._inactive&&(h=!0),a=a.$parent}if(o.routerViewDepth=s,h){var p=u[l],v=p&&p.component;return v?(p.configProps&&Za(v,o,p.route,p.configProps),i(v,o,r)):i()}var d=c.matched[s],m=d&&d.components[l];if(!d||!m)return u[l]=null,i();u[l]={component:m},o.registerRouteInstance=function(t,e){var n=d.instances[l];(e&&n!==t||!e&&n===t)&&(d.instances[l]=e)},(o.hook||(o.hook={})).prepatch=function(t,e){d.instances[l]=e.componentInstance},o.hook.init=function(t){t.data.keepAlive&&t.componentInstance&&t.componentInstance!==d.instances[l]&&(d.instances[l]=t.componentInstance),Qa(c)};var g=d.props&&d.props[l];return g&&(Ia(u[l],{route:c,configProps:g}),Za(m,o,c,g)),i(m,o,r)}};function Za(t,e,n,r){var a=e.props=function(t,e){switch(typeof e){case\"undefined\":return;case\"object\":return e;case\"function\":return e(t);case\"boolean\":return e?t.params:void 0;default:0}}(n,r);if(a){a=e.props=Ia({},a);var o=e.attrs=e.attrs||{};for(var i in a)t.props&&i in t.props||(o[i]=a[i],delete a[i])}}function to(t,e,n){var r=t.charAt(0);if(\"/\"===r)return t;if(\"?\"===r||\"#\"===r)return e+t;var a=e.split(\"/\");n&&a[a.length-1]||a.pop();for(var o=t.replace(/^\\//,\"\").split(\"/\"),i=0;i<o.length;i++){var l=o[i];\"..\"===l?a.pop():\".\"!==l&&a.push(l)}return\"\"!==a[0]&&a.unshift(\"\"),a.join(\"/\")}function eo(t){return t.replace(/\\/\\//g,\"/\")}var no=Array.isArray||function(t){return\"[object Array]\"==Object.prototype.toString.call(t)},ro=bo,ao=uo,oo=function(t,e){return ho(uo(t,e),e)},io=ho,lo=go,co=new RegExp([\"(\\\\\\\\.)\",\"([\\\\/.])?(?:(?:\\\\:(\\\\w+)(?:\\\\(((?:\\\\\\\\.|[^\\\\\\\\()])+)\\\\))?|\\\\(((?:\\\\\\\\.|[^\\\\\\\\()])+)\\\\))([+*?])?|(\\\\*))\"].join(\"|\"),\"g\");function uo(t,e){for(var n,r=[],a=0,o=0,i=\"\",l=e&&e.delimiter||\"/\";null!=(n=co.exec(t));){var c=n[0],u=n[1],s=n.index;if(i+=t.slice(o,s),o=s+c.length,u)i+=u[1];else{var h=t[o],f=n[2],p=n[3],v=n[4],d=n[5],m=n[6],g=n[7];i&&(r.push(i),i=\"\");var b=null!=f&&null!=h&&h!==f,y=\"+\"===m||\"*\"===m,_=\"?\"===m||\"*\"===m,x=n[2]||l,w=v||d;r.push({name:p||a++,prefix:f||\"\",delimiter:x,optional:_,repeat:y,partial:b,asterisk:!!g,pattern:w?po(w):g?\".*\":\"[^\"+fo(x)+\"]+?\"})}}return o<t.length&&(i+=t.substr(o)),i&&r.push(i),r}function so(t){return encodeURI(t).replace(/[\\/?#]/g,(function(t){return\"%\"+t.charCodeAt(0).toString(16).toUpperCase()}))}function ho(t,e){for(var n=new Array(t.length),r=0;r<t.length;r++)\"object\"==typeof t[r]&&(n[r]=new RegExp(\"^(?:\"+t[r].pattern+\")$\",mo(e)));return function(e,r){for(var a=\"\",o=e||{},i=(r||{}).pretty?so:encodeURIComponent,l=0;l<t.length;l++){var c=t[l];if(\"string\"!=typeof c){var u,s=o[c.name];if(null==s){if(c.optional){c.partial&&(a+=c.prefix);continue}throw new TypeError('Expected \"'+c.name+'\" to be defined')}if(no(s)){if(!c.repeat)throw new TypeError('Expected \"'+c.name+'\" to not repeat, but received `'+JSON.stringify(s)+\"`\");if(0===s.length){if(c.optional)continue;throw new TypeError('Expected \"'+c.name+'\" to not be empty')}for(var h=0;h<s.length;h++){if(u=i(s[h]),!n[l].test(u))throw new TypeError('Expected all \"'+c.name+'\" to match \"'+c.pattern+'\", but received `'+JSON.stringify(u)+\"`\");a+=(0===h?c.prefix:c.delimiter)+u}}else{if(u=c.asterisk?encodeURI(s).replace(/[?#]/g,(function(t){return\"%\"+t.charCodeAt(0).toString(16).toUpperCase()})):i(s),!n[l].test(u))throw new TypeError('Expected \"'+c.name+'\" to match \"'+c.pattern+'\", but received \"'+u+'\"');a+=c.prefix+u}}else a+=c}return a}}function fo(t){return t.replace(/([.+*?=^!:${}()[\\]|\\/\\\\])/g,\"\\\\$1\")}function po(t){return t.replace(/([=!:$\\/()])/g,\"\\\\$1\")}function vo(t,e){return t.keys=e,t}function mo(t){return t&&t.sensitive?\"\":\"i\"}function go(t,e,n){no(e)||(n=e||n,e=[]);for(var r=(n=n||{}).strict,a=!1!==n.end,o=\"\",i=0;i<t.length;i++){var l=t[i];if(\"string\"==typeof l)o+=fo(l);else{var c=fo(l.prefix),u=\"(?:\"+l.pattern+\")\";e.push(l),l.repeat&&(u+=\"(?:\"+c+u+\")*\"),o+=u=l.optional?l.partial?c+\"(\"+u+\")?\":\"(?:\"+c+\"(\"+u+\"))?\":c+\"(\"+u+\")\"}}var s=fo(n.delimiter||\"/\"),h=o.slice(-s.length)===s;return r||(o=(h?o.slice(0,-s.length):o)+\"(?:\"+s+\"(?=$))?\"),o+=a?\"$\":r&&h?\"\":\"(?=\"+s+\"|$)\",vo(new RegExp(\"^\"+o,mo(n)),e)}function bo(t,e,n){return no(e)||(n=e||n,e=[]),n=n||{},t instanceof RegExp?function(t,e){var n=t.source.match(/\\((?!\\?)/g);if(n)for(var r=0;r<n.length;r++)e.push({name:r,prefix:null,delimiter:null,optional:!1,repeat:!1,partial:!1,asterisk:!1,pattern:null});return vo(t,e)}(t,e):no(t)?function(t,e,n){for(var r=[],a=0;a<t.length;a++)r.push(bo(t[a],e,n).source);return vo(new RegExp(\"(?:\"+r.join(\"|\")+\")\",mo(n)),e)}(t,e,n):function(t,e,n){return go(uo(t,n),e,n)}(t,e,n)}ro.parse=ao,ro.compile=oo,ro.tokensToFunction=io,ro.tokensToRegExp=lo;var yo=Object.create(null);function _o(t,e,n){e=e||{};try{var r=yo[t]||(yo[t]=ro.compile(t));return\"string\"==typeof e.pathMatch&&(e[0]=e.pathMatch),r(e,{pretty:!0})}catch(t){return\"\"}finally{delete e[0]}}function xo(t,e,n,r){var a=\"string\"==typeof t?{path:t}:t;if(a._normalized)return a;if(a.name){var o=(a=Ia({},t)).params;return o&&\"object\"==typeof o&&(a.params=Ia({},o)),a}if(!a.path&&a.params&&e){(a=Ia({},a))._normalized=!0;var i=Ia(Ia({},e.params),a.params);if(e.name)a.name=e.name,a.params=i;else if(e.matched.length){var l=e.matched[e.matched.length-1].path;a.path=_o(l,i,e.path)}else 0;return a}var c=function(t){var e=\"\",n=\"\",r=t.indexOf(\"#\");r>=0&&(e=t.slice(r),t=t.slice(0,r));var a=t.indexOf(\"?\");return a>=0&&(n=t.slice(a+1),t=t.slice(0,a)),{path:t,query:n,hash:e}}(a.path||\"\"),u=e&&e.path||\"/\",s=c.path?to(c.path,u,n||a.append):u,h=function(t,e,n){void 0===e&&(e={});var r,a=n||za;try{r=a(t||\"\")}catch(t){r={}}for(var o in e){var i=e[o];r[o]=Array.isArray(i)?i.map(Ba):Ba(i)}return r}(c.query,a.query,r&&r.options.parseQuery),f=a.hash||c.hash;return f&&\"#\"!==f.charAt(0)&&(f=\"#\"+f),{_normalized:!0,path:s,query:h,hash:f}}var wo,Po=function(){},ko={name:\"RouterLink\",props:{to:{type:[String,Object],required:!0},tag:{type:String,default:\"a\"},custom:Boolean,exact:Boolean,exactPath:Boolean,append:Boolean,replace:Boolean,activeClass:String,exactActiveClass:String,ariaCurrentValue:{type:String,default:\"page\"},event:{type:[String,Array],default:\"click\"}},render:function(t){var e=this,n=this.$router,r=this.$route,a=n.resolve(this.to,r,this.append),o=a.location,i=a.route,l=a.href,c={},u=n.options.linkActiveClass,s=n.options.linkExactActiveClass,h=null==u?\"router-link-active\":u,f=null==s?\"router-link-exact-active\":s,p=null==this.activeClass?h:this.activeClass,v=null==this.exactActiveClass?f:this.exactActiveClass,d=i.redirectedFrom?Va(null,xo(i.redirectedFrom),null,n):i;c[v]=Ja(r,d,this.exactPath),c[p]=this.exact||this.exactPath?c[v]:function(t,e){return 0===t.path.replace(Wa,\"/\").indexOf(e.path.replace(Wa,\"/\"))&&(!e.hash||t.hash===e.hash)&&function(t,e){for(var n in e)if(!(n in t))return!1;return!0}(t.query,e.query)}(r,d);var m=c[v]?this.ariaCurrentValue:null,g=function(t){Eo(t)&&(e.replace?n.replace(o,Po):n.push(o,Po))},b={click:Eo};Array.isArray(this.event)?this.event.forEach((function(t){b[t]=g})):b[this.event]=g;var y={class:c},_=!this.$scopedSlots.$hasNormal&&this.$scopedSlots.default&&this.$scopedSlots.default({href:l,route:i,navigate:g,isActive:c[p],isExactActive:c[v]});if(_){if(1===_.length)return _[0];if(_.length>1||!_.length)return 0===_.length?t():t(\"span\",{},_)}if(\"a\"===this.tag)y.on=b,y.attrs={href:l,\"aria-current\":m};else{var x=function t(e){var n;if(e)for(var r=0;r<e.length;r++){if(\"a\"===(n=e[r]).tag)return n;if(n.children&&(n=t(n.children)))return n}}(this.$slots.default);if(x){x.isStatic=!1;var w=x.data=Ia({},x.data);for(var P in w.on=w.on||{},w.on){var k=w.on[P];P in b&&(w.on[P]=Array.isArray(k)?k:[k])}for(var E in b)E in w.on?w.on[E].push(b[E]):w.on[E]=g;var S=x.data.attrs=Ia({},x.data.attrs);S.href=l,S[\"aria-current\"]=m}else y.on=b}return t(this.tag,y,this.$slots.default)}};function Eo(t){if(!(t.metaKey||t.altKey||t.ctrlKey||t.shiftKey||t.defaultPrevented||void 0!==t.button&&0!==t.button)){if(t.currentTarget&&t.currentTarget.getAttribute){var e=t.currentTarget.getAttribute(\"target\");if(/\\b_blank\\b/i.test(e))return}return t.preventDefault&&t.preventDefault(),!0}}var So=\"undefined\"!=typeof window;function Oo(t,e,n,r,a){var o=e||[],i=n||Object.create(null),l=r||Object.create(null);t.forEach((function(t){!function t(e,n,r,a,o,i){var l=a.path,c=a.name;0;var u=a.pathToRegexpOptions||{},s=function(t,e,n){n||(t=t.replace(/\\/$/,\"\"));if(\"/\"===t[0])return t;if(null==e)return t;return eo(e.path+\"/\"+t)}(l,o,u.strict);\"boolean\"==typeof a.caseSensitive&&(u.sensitive=a.caseSensitive);var h={path:s,regex:jo(s,u),components:a.components||{default:a.component},alias:a.alias?\"string\"==typeof a.alias?[a.alias]:a.alias:[],instances:{},enteredCbs:{},name:c,parent:o,matchAs:i,redirect:a.redirect,beforeEnter:a.beforeEnter,meta:a.meta||{},props:null==a.props?{}:a.components?a.props:{default:a.props}};a.children&&a.children.forEach((function(a){var o=i?eo(i+\"/\"+a.path):void 0;t(e,n,r,a,h,o)}));n[h.path]||(e.push(h.path),n[h.path]=h);if(void 0!==a.alias)for(var f=Array.isArray(a.alias)?a.alias:[a.alias],p=0;p<f.length;++p){0;var v={path:f[p],children:a.children};t(e,n,r,v,o,h.path||\"/\")}c&&(r[c]||(r[c]=h))}(o,i,l,t,a)}));for(var c=0,u=o.length;c<u;c++)\"*\"===o[c]&&(o.push(o.splice(c,1)[0]),u--,c--);return{pathList:o,pathMap:i,nameMap:l}}function jo(t,e){return ro(t,[],e)}function Lo(t,e){var n=Oo(t),r=n.pathList,a=n.pathMap,o=n.nameMap;function i(t,n,i){var l=xo(t,n,!1,e),u=l.name;if(u){var s=o[u];if(!s)return c(null,l);var h=s.regex.keys.filter((function(t){return!t.optional})).map((function(t){return t.name}));if(\"object\"!=typeof l.params&&(l.params={}),n&&\"object\"==typeof n.params)for(var f in n.params)!(f in l.params)&&h.indexOf(f)>-1&&(l.params[f]=n.params[f]);return l.path=_o(s.path,l.params),c(s,l,i)}if(l.path){l.params={};for(var p=0;p<r.length;p++){var v=r[p],d=a[v];if(Ao(d.regex,l.path,l.params))return c(d,l,i)}}return c(null,l)}function l(t,n){var r=t.redirect,a=\"function\"==typeof r?r(Va(t,n,null,e)):r;if(\"string\"==typeof a&&(a={path:a}),!a||\"object\"!=typeof a)return c(null,n);var l=a,u=l.name,s=l.path,h=n.query,f=n.hash,p=n.params;if(h=l.hasOwnProperty(\"query\")?l.query:h,f=l.hasOwnProperty(\"hash\")?l.hash:f,p=l.hasOwnProperty(\"params\")?l.params:p,u){o[u];return i({_normalized:!0,name:u,query:h,hash:f,params:p},void 0,n)}if(s){var v=function(t,e){return to(t,e.parent?e.parent.path:\"/\",!0)}(s,t);return i({_normalized:!0,path:_o(v,p),query:h,hash:f},void 0,n)}return c(null,n)}function c(t,n,r){return t&&t.redirect?l(t,r||n):t&&t.matchAs?function(t,e,n){var r=i({_normalized:!0,path:_o(n,e.params)});if(r){var a=r.matched,o=a[a.length-1];return e.params=r.params,c(o,e)}return c(null,e)}(0,n,t.matchAs):Va(t,n,r,e)}return{match:i,addRoute:function(t,e){var n=\"object\"!=typeof t?o[t]:void 0;Oo([e||t],r,a,o,n),n&&Oo(n.alias.map((function(t){return{path:t,children:[e]}})),r,a,o,n)},getRoutes:function(){return r.map((function(t){return a[t]}))},addRoutes:function(t){Oo(t,r,a,o)}}}function Ao(t,e,n){var r=e.match(t);if(!r)return!1;if(!n)return!0;for(var a=1,o=r.length;a<o;++a){var i=t.keys[a-1];i&&(n[i.name||\"pathMatch\"]=\"string\"==typeof r[a]?Na(r[a]):r[a])}return!0}var Co=So&&window.performance&&window.performance.now?window.performance:Date;function $o(){return Co.now().toFixed(3)}var To=$o();function Io(){return To}function Ro(t){return To=t}var Fo=Object.create(null);function Do(){\"scrollRestoration\"in window.history&&(window.history.scrollRestoration=\"manual\");var t=window.location.protocol+\"//\"+window.location.host,e=window.location.href.replace(t,\"\"),n=Ia({},window.history.state);return n.key=Io(),window.history.replaceState(n,\"\",e),window.addEventListener(\"popstate\",Bo),function(){window.removeEventListener(\"popstate\",Bo)}}function Mo(t,e,n,r){if(t.app){var a=t.options.scrollBehavior;a&&t.app.$nextTick((function(){var o=function(){var t=Io();if(t)return Fo[t]}(),i=a.call(t,e,n,r?o:null);i&&(\"function\"==typeof i.then?i.then((function(t){qo(t,o)})).catch((function(t){0})):qo(i,o))}))}}function No(){var t=Io();t&&(Fo[t]={x:window.pageXOffset,y:window.pageYOffset})}function Bo(t){No(),t.state&&t.state.key&&Ro(t.state.key)}function zo(t){return Wo(t.x)||Wo(t.y)}function Uo(t){return{x:Wo(t.x)?t.x:window.pageXOffset,y:Wo(t.y)?t.y:window.pageYOffset}}function Wo(t){return\"number\"==typeof t}var Vo=/^#\\d/;function qo(t,e){var n,r=\"object\"==typeof t;if(r&&\"string\"==typeof t.selector){var a=Vo.test(t.selector)?document.getElementById(t.selector.slice(1)):document.querySelector(t.selector);if(a){var o=t.offset&&\"object\"==typeof t.offset?t.offset:{};e=function(t,e){var n=document.documentElement.getBoundingClientRect(),r=t.getBoundingClientRect();return{x:r.left-n.left-e.x,y:r.top-n.top-e.y}}(a,o={x:Wo((n=o).x)?n.x:0,y:Wo(n.y)?n.y:0})}else zo(t)&&(e=Uo(t))}else r&&zo(t)&&(e=Uo(t));e&&(\"scrollBehavior\"in document.documentElement.style?window.scrollTo({left:e.x,top:e.y,behavior:t.behavior}):window.scrollTo(e.x,e.y))}var Ho,Go=So&&((-1===(Ho=window.navigator.userAgent).indexOf(\"Android 2.\")&&-1===Ho.indexOf(\"Android 4.0\")||-1===Ho.indexOf(\"Mobile Safari\")||-1!==Ho.indexOf(\"Chrome\")||-1!==Ho.indexOf(\"Windows Phone\"))&&window.history&&\"function\"==typeof window.history.pushState);function Ko(t,e){No();var n=window.history;try{if(e){var r=Ia({},n.state);r.key=Io(),n.replaceState(r,\"\",t)}else n.pushState({key:Ro($o())},\"\",t)}catch(n){window.location[e?\"replace\":\"assign\"](t)}}function Jo(t){Ko(t,!0)}function Xo(t,e,n){var r=function(a){a>=t.length?n():t[a]?e(t[a],(function(){r(a+1)})):r(a+1)};r(0)}var Qo={redirected:2,aborted:4,cancelled:8,duplicated:16};function Yo(t,e){return ti(t,e,Qo.redirected,'Redirected when going from \"'+t.fullPath+'\" to \"'+function(t){if(\"string\"==typeof t)return t;if(\"path\"in t)return t.path;var e={};return ei.forEach((function(n){n in t&&(e[n]=t[n])})),JSON.stringify(e,null,2)}(e)+'\" via a navigation guard.')}function Zo(t,e){return ti(t,e,Qo.cancelled,'Navigation cancelled from \"'+t.fullPath+'\" to \"'+e.fullPath+'\" with a new navigation.')}function ti(t,e,n,r){var a=new Error(r);return a._isRouter=!0,a.from=t,a.to=e,a.type=n,a}var ei=[\"params\",\"query\",\"hash\"];function ni(t){return Object.prototype.toString.call(t).indexOf(\"Error\")>-1}function ri(t,e){return ni(t)&&t._isRouter&&(null==e||t.type===e)}function ai(t){return function(e,n,r){var a=!1,o=0,i=null;oi(t,(function(t,e,n,l){if(\"function\"==typeof t&&void 0===t.cid){a=!0,o++;var c,u=ci((function(e){var a;((a=e).__esModule||li&&\"Module\"===a[Symbol.toStringTag])&&(e=e.default),t.resolved=\"function\"==typeof e?e:wo.extend(e),n.components[l]=e,--o<=0&&r()})),s=ci((function(t){var e=\"Failed to resolve async component \"+l+\": \"+t;i||(i=ni(t)?t:new Error(e),r(i))}));try{c=t(u,s)}catch(t){s(t)}if(c)if(\"function\"==typeof c.then)c.then(u,s);else{var h=c.component;h&&\"function\"==typeof h.then&&h.then(u,s)}}})),a||r()}}function oi(t,e){return ii(t.map((function(t){return Object.keys(t.components).map((function(n){return e(t.components[n],t.instances[n],t,n)}))})))}function ii(t){return Array.prototype.concat.apply([],t)}var li=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.toStringTag;function ci(t){var e=!1;return function(){for(var n=[],r=arguments.length;r--;)n[r]=arguments[r];if(!e)return e=!0,t.apply(this,n)}}var ui=function(t,e){this.router=t,this.base=function(t){if(!t)if(So){var e=document.querySelector(\"base\");t=(t=e&&e.getAttribute(\"href\")||\"/\").replace(/^https?:\\/\\/[^\\/]+/,\"\")}else t=\"/\";\"/\"!==t.charAt(0)&&(t=\"/\"+t);return t.replace(/\\/$/,\"\")}(e),this.current=Ha,this.pending=null,this.ready=!1,this.readyCbs=[],this.readyErrorCbs=[],this.errorCbs=[],this.listeners=[]};function si(t,e,n,r){var a=oi(t,(function(t,r,a,o){var i=function(t,e){\"function\"!=typeof t&&(t=wo.extend(t));return t.options[e]}(t,e);if(i)return Array.isArray(i)?i.map((function(t){return n(t,r,a,o)})):n(i,r,a,o)}));return ii(r?a.reverse():a)}function hi(t,e){if(e)return function(){return t.apply(e,arguments)}}ui.prototype.listen=function(t){this.cb=t},ui.prototype.onReady=function(t,e){this.ready?t():(this.readyCbs.push(t),e&&this.readyErrorCbs.push(e))},ui.prototype.onError=function(t){this.errorCbs.push(t)},ui.prototype.transitionTo=function(t,e,n){var r,a=this;try{r=this.router.match(t,this.current)}catch(t){throw this.errorCbs.forEach((function(e){e(t)})),t}var o=this.current;this.confirmTransition(r,(function(){a.updateRoute(r),e&&e(r),a.ensureURL(),a.router.afterHooks.forEach((function(t){t&&t(r,o)})),a.ready||(a.ready=!0,a.readyCbs.forEach((function(t){t(r)})))}),(function(t){n&&n(t),t&&!a.ready&&(ri(t,Qo.redirected)&&o===Ha||(a.ready=!0,a.readyErrorCbs.forEach((function(e){e(t)}))))}))},ui.prototype.confirmTransition=function(t,e,n){var r=this,a=this.current;this.pending=t;var o,i,l=function(t){!ri(t)&&ni(t)&&(r.errorCbs.length?r.errorCbs.forEach((function(e){e(t)})):console.error(t)),n&&n(t)},c=t.matched.length-1,u=a.matched.length-1;if(Ja(t,a)&&c===u&&t.matched[c]===a.matched[u])return this.ensureURL(),l(((i=ti(o=a,t,Qo.duplicated,'Avoided redundant navigation to current location: \"'+o.fullPath+'\".')).name=\"NavigationDuplicated\",i));var s=function(t,e){var n,r=Math.max(t.length,e.length);for(n=0;n<r&&t[n]===e[n];n++);return{updated:e.slice(0,n),activated:e.slice(n),deactivated:t.slice(n)}}(this.current.matched,t.matched),h=s.updated,f=s.deactivated,p=s.activated,v=[].concat(function(t){return si(t,\"beforeRouteLeave\",hi,!0)}(f),this.router.beforeHooks,function(t){return si(t,\"beforeRouteUpdate\",hi)}(h),p.map((function(t){return t.beforeEnter})),ai(p)),d=function(e,n){if(r.pending!==t)return l(Zo(a,t));try{e(t,a,(function(e){!1===e?(r.ensureURL(!0),l(function(t,e){return ti(t,e,Qo.aborted,'Navigation aborted from \"'+t.fullPath+'\" to \"'+e.fullPath+'\" via a navigation guard.')}(a,t))):ni(e)?(r.ensureURL(!0),l(e)):\"string\"==typeof e||\"object\"==typeof e&&(\"string\"==typeof e.path||\"string\"==typeof e.name)?(l(Yo(a,t)),\"object\"==typeof e&&e.replace?r.replace(e):r.push(e)):n(e)}))}catch(t){l(t)}};Xo(v,d,(function(){Xo(function(t){return si(t,\"beforeRouteEnter\",(function(t,e,n,r){return function(t,e,n){return function(r,a,o){return t(r,a,(function(t){\"function\"==typeof t&&(e.enteredCbs[n]||(e.enteredCbs[n]=[]),e.enteredCbs[n].push(t)),o(t)}))}}(t,n,r)}))}(p).concat(r.router.resolveHooks),d,(function(){if(r.pending!==t)return l(Zo(a,t));r.pending=null,e(t),r.router.app&&r.router.app.$nextTick((function(){Qa(t)}))}))}))},ui.prototype.updateRoute=function(t){this.current=t,this.cb&&this.cb(t)},ui.prototype.setupListeners=function(){},ui.prototype.teardown=function(){this.listeners.forEach((function(t){t()})),this.listeners=[],this.current=Ha,this.pending=null};var fi=function(t){function e(e,n){t.call(this,e,n),this._startLocation=pi(this.base)}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.setupListeners=function(){var t=this;if(!(this.listeners.length>0)){var e=this.router,n=e.options.scrollBehavior,r=Go&&n;r&&this.listeners.push(Do());var a=function(){var n=t.current,a=pi(t.base);t.current===Ha&&a===t._startLocation||t.transitionTo(a,(function(t){r&&Mo(e,t,n,!0)}))};window.addEventListener(\"popstate\",a),this.listeners.push((function(){window.removeEventListener(\"popstate\",a)}))}},e.prototype.go=function(t){window.history.go(t)},e.prototype.push=function(t,e,n){var r=this,a=this.current;this.transitionTo(t,(function(t){Ko(eo(r.base+t.fullPath)),Mo(r.router,t,a,!1),e&&e(t)}),n)},e.prototype.replace=function(t,e,n){var r=this,a=this.current;this.transitionTo(t,(function(t){Jo(eo(r.base+t.fullPath)),Mo(r.router,t,a,!1),e&&e(t)}),n)},e.prototype.ensureURL=function(t){if(pi(this.base)!==this.current.fullPath){var e=eo(this.base+this.current.fullPath);t?Ko(e):Jo(e)}},e.prototype.getCurrentLocation=function(){return pi(this.base)},e}(ui);function pi(t){var e=window.location.pathname;return t&&0===e.toLowerCase().indexOf(t.toLowerCase())&&(e=e.slice(t.length)),(e||\"/\")+window.location.search+window.location.hash}var vi=function(t){function e(e,n,r){t.call(this,e,n),r&&function(t){var e=pi(t);if(!/^\\/#/.test(e))return window.location.replace(eo(t+\"/#\"+e)),!0}(this.base)||di()}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.setupListeners=function(){var t=this;if(!(this.listeners.length>0)){var e=this.router.options.scrollBehavior,n=Go&&e;n&&this.listeners.push(Do());var r=function(){var e=t.current;di()&&t.transitionTo(mi(),(function(r){n&&Mo(t.router,r,e,!0),Go||yi(r.fullPath)}))},a=Go?\"popstate\":\"hashchange\";window.addEventListener(a,r),this.listeners.push((function(){window.removeEventListener(a,r)}))}},e.prototype.push=function(t,e,n){var r=this,a=this.current;this.transitionTo(t,(function(t){bi(t.fullPath),Mo(r.router,t,a,!1),e&&e(t)}),n)},e.prototype.replace=function(t,e,n){var r=this,a=this.current;this.transitionTo(t,(function(t){yi(t.fullPath),Mo(r.router,t,a,!1),e&&e(t)}),n)},e.prototype.go=function(t){window.history.go(t)},e.prototype.ensureURL=function(t){var e=this.current.fullPath;mi()!==e&&(t?bi(e):yi(e))},e.prototype.getCurrentLocation=function(){return mi()},e}(ui);function di(){var t=mi();return\"/\"===t.charAt(0)||(yi(\"/\"+t),!1)}function mi(){var t=window.location.href,e=t.indexOf(\"#\");return e<0?\"\":t=t.slice(e+1)}function gi(t){var e=window.location.href,n=e.indexOf(\"#\");return(n>=0?e.slice(0,n):e)+\"#\"+t}function bi(t){Go?Ko(gi(t)):window.location.hash=t}function yi(t){Go?Jo(gi(t)):window.location.replace(gi(t))}var _i=function(t){function e(e,n){t.call(this,e,n),this.stack=[],this.index=-1}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.push=function(t,e,n){var r=this;this.transitionTo(t,(function(t){r.stack=r.stack.slice(0,r.index+1).concat(t),r.index++,e&&e(t)}),n)},e.prototype.replace=function(t,e,n){var r=this;this.transitionTo(t,(function(t){r.stack=r.stack.slice(0,r.index).concat(t),e&&e(t)}),n)},e.prototype.go=function(t){var e=this,n=this.index+t;if(!(n<0||n>=this.stack.length)){var r=this.stack[n];this.confirmTransition(r,(function(){var t=e.current;e.index=n,e.updateRoute(r),e.router.afterHooks.forEach((function(e){e&&e(r,t)}))}),(function(t){ri(t,Qo.duplicated)&&(e.index=n)}))}},e.prototype.getCurrentLocation=function(){var t=this.stack[this.stack.length-1];return t?t.fullPath:\"/\"},e.prototype.ensureURL=function(){},e}(ui),xi=function(t){void 0===t&&(t={}),this.app=null,this.apps=[],this.options=t,this.beforeHooks=[],this.resolveHooks=[],this.afterHooks=[],this.matcher=Lo(t.routes||[],this);var e=t.mode||\"hash\";switch(this.fallback=\"history\"===e&&!Go&&!1!==t.fallback,this.fallback&&(e=\"hash\"),So||(e=\"abstract\"),this.mode=e,e){case\"history\":this.history=new fi(this,t.base);break;case\"hash\":this.history=new vi(this,t.base,this.fallback);break;case\"abstract\":this.history=new _i(this,t.base);break;default:0}},wi={currentRoute:{configurable:!0}};function Pi(t,e){return t.push(e),function(){var n=t.indexOf(e);n>-1&&t.splice(n,1)}}xi.prototype.match=function(t,e,n){return this.matcher.match(t,e,n)},wi.currentRoute.get=function(){return this.history&&this.history.current},xi.prototype.init=function(t){var e=this;if(this.apps.push(t),t.$once(\"hook:destroyed\",(function(){var n=e.apps.indexOf(t);n>-1&&e.apps.splice(n,1),e.app===t&&(e.app=e.apps[0]||null),e.app||e.history.teardown()})),!this.app){this.app=t;var n=this.history;if(n instanceof fi||n instanceof vi){var r=function(t){n.setupListeners(),function(t){var r=n.current,a=e.options.scrollBehavior;Go&&a&&\"fullPath\"in t&&Mo(e,t,r,!1)}(t)};n.transitionTo(n.getCurrentLocation(),r,r)}n.listen((function(t){e.apps.forEach((function(e){e._route=t}))}))}},xi.prototype.beforeEach=function(t){return Pi(this.beforeHooks,t)},xi.prototype.beforeResolve=function(t){return Pi(this.resolveHooks,t)},xi.prototype.afterEach=function(t){return Pi(this.afterHooks,t)},xi.prototype.onReady=function(t,e){this.history.onReady(t,e)},xi.prototype.onError=function(t){this.history.onError(t)},xi.prototype.push=function(t,e,n){var r=this;if(!e&&!n&&\"undefined\"!=typeof Promise)return new Promise((function(e,n){r.history.push(t,e,n)}));this.history.push(t,e,n)},xi.prototype.replace=function(t,e,n){var r=this;if(!e&&!n&&\"undefined\"!=typeof Promise)return new Promise((function(e,n){r.history.replace(t,e,n)}));this.history.replace(t,e,n)},xi.prototype.go=function(t){this.history.go(t)},xi.prototype.back=function(){this.go(-1)},xi.prototype.forward=function(){this.go(1)},xi.prototype.getMatchedComponents=function(t){var e=t?t.matched?t:this.resolve(t).route:this.currentRoute;return e?[].concat.apply([],e.matched.map((function(t){return Object.keys(t.components).map((function(e){return t.components[e]}))}))):[]},xi.prototype.resolve=function(t,e,n){var r=xo(t,e=e||this.history.current,n,this),a=this.match(r,e),o=a.redirectedFrom||a.fullPath;return{location:r,route:a,href:function(t,e,n){var r=\"hash\"===n?\"#\"+e:e;return t?eo(t+\"/\"+r):r}(this.history.base,o,this.mode),normalizedTo:r,resolved:a}},xi.prototype.getRoutes=function(){return this.matcher.getRoutes()},xi.prototype.addRoute=function(t,e){this.matcher.addRoute(t,e),this.history.current!==Ha&&this.history.transitionTo(this.history.getCurrentLocation())},xi.prototype.addRoutes=function(t){this.matcher.addRoutes(t),this.history.current!==Ha&&this.history.transitionTo(this.history.getCurrentLocation())},Object.defineProperties(xi.prototype,wi),xi.install=function t(e){if(!t.installed||wo!==e){t.installed=!0,wo=e;var n=function(t){return void 0!==t},r=function(t,e){var r=t.$options._parentVnode;n(r)&&n(r=r.data)&&n(r=r.registerRouteInstance)&&r(t,e)};e.mixin({beforeCreate:function(){n(this.$options.router)?(this._routerRoot=this,this._router=this.$options.router,this._router.init(this),e.util.defineReactive(this,\"_route\",this._router.history.current)):this._routerRoot=this.$parent&&this.$parent._routerRoot||this,r(this,this)},destroyed:function(){r(this)}}),Object.defineProperty(e.prototype,\"$router\",{get:function(){return this._routerRoot._router}}),Object.defineProperty(e.prototype,\"$route\",{get:function(){return this._routerRoot._route}}),e.component(\"RouterView\",Ya),e.component(\"RouterLink\",ko);var a=e.config.optionMergeStrategies;a.beforeRouteEnter=a.beforeRouteLeave=a.beforeRouteUpdate=a.created}},xi.version=\"3.5.1\",xi.isNavigationFailure=ri,xi.NavigationFailureType=Qo,xi.START_LOCATION=Ha,So&&window.Vue&&window.Vue.use(xi);var ki=xi;n(195),n(129),n(196),n(93),n(198),n(94),n(95),n(199);function Ei(t){t.locales&&Object.keys(t.locales).forEach((function(e){t.locales[e].path=e})),Object.freeze(t)}n(40),n(41),n(57);var Si=n(43),Oi=(n(135),n(66),n(46),n(173),n(174),{NotFound:function(){return n.e(129).then(n.bind(null,717))},Layout:function(){return Promise.all([n.e(0),n.e(2),n.e(39)]).then(n.bind(null,718))}}),ji={\"v-60cb9dee\":function(){return n.e(81).then(n.bind(null,721))},\"v-03ef47d5\":function(){return n.e(131).then(n.bind(null,722))},\"v-7248ea50\":function(){return n.e(5).then(n.bind(null,723))},\"v-16a4b1be\":function(){return n.e(130).then(n.bind(null,724))},\"v-2c033278\":function(){return n.e(83).then(n.bind(null,725))},\"v-66e8855e\":function(){return n.e(84).then(n.bind(null,726))},\"v-ed117e52\":function(){return n.e(133).then(n.bind(null,727))},\"v-6672113e\":function(){return n.e(132).then(n.bind(null,728))},\"v-2dcc24c4\":function(){return n.e(134).then(n.bind(null,729))},\"v-1da3f35c\":function(){return n.e(85).then(n.bind(null,730))},\"v-d694e792\":function(){return n.e(173).then(n.bind(null,731))},\"v-2382c404\":function(){return n.e(135).then(n.bind(null,732))},\"v-6f12817e\":function(){return n.e(82).then(n.bind(null,733))},\"v-272d95d4\":function(){return n.e(136).then(n.bind(null,734))},\"v-b0364844\":function(){return n.e(137).then(n.bind(null,735))},\"v-d3df9312\":function(){return n.e(138).then(n.bind(null,736))},\"v-11c7b800\":function(){return n.e(88).then(n.bind(null,737))},\"v-1e1dbec4\":function(){return n.e(139).then(n.bind(null,738))},\"v-5eb39c1e\":function(){return n.e(61).then(n.bind(null,739))},\"v-c5918b84\":function(){return n.e(141).then(n.bind(null,740))},\"v-4b295d84\":function(){return n.e(140).then(n.bind(null,741))},\"v-2fe89fd4\":function(){return n.e(86).then(n.bind(null,742))},\"v-baada7d2\":function(){return n.e(142).then(n.bind(null,743))},\"v-1ff609fc\":function(){return n.e(87).then(n.bind(null,744))},\"v-5f7ec496\":function(){return n.e(89).then(n.bind(null,745))},\"v-3225283e\":function(){return n.e(144).then(n.bind(null,746))},\"v-a17bbc92\":function(){return n.e(145).then(n.bind(null,747))},\"v-55e5e020\":function(){return n.e(62).then(n.bind(null,748))},\"v-7e5c7f20\":function(){return n.e(146).then(n.bind(null,749))},\"v-1dbbca9e\":function(){return n.e(147).then(n.bind(null,750))},\"v-484f9fde\":function(){return n.e(90).then(n.bind(null,751))},\"v-3ab0e9b8\":function(){return n.e(40).then(n.bind(null,752))},\"v-2537f144\":function(){return n.e(148).then(n.bind(null,753))},\"v-0d5219fe\":function(){return n.e(149).then(n.bind(null,754))},\"v-680b68c4\":function(){return n.e(91).then(n.bind(null,755))},\"v-8849d152\":function(){return n.e(150).then(n.bind(null,756))},\"v-36ef989e\":function(){return n.e(92).then(n.bind(null,757))},\"v-818c75a8\":function(){return n.e(151).then(n.bind(null,758))},\"v-114a03be\":function(){return n.e(152).then(n.bind(null,759))},\"v-781e11be\":function(){return n.e(153).then(n.bind(null,760))},\"v-3cac337e\":function(){return n.e(25).then(n.bind(null,761))},\"v-1b6582b8\":function(){return n.e(93).then(n.bind(null,762))},\"v-57124e5a\":function(){return n.e(154).then(n.bind(null,763))},\"v-4d0690fe\":function(){return n.e(155).then(n.bind(null,764))},\"v-31ab8fa0\":function(){return n.e(94).then(n.bind(null,765))},\"v-379da24c\":function(){return n.e(156).then(n.bind(null,766))},\"v-1f7d17c4\":function(){return n.e(26).then(n.bind(null,767))},\"v-525634fe\":function(){return n.e(143).then(n.bind(null,768))},\"v-f93817f4\":function(){return n.e(42).then(n.bind(null,769))},\"v-28ddc35a\":function(){return n.e(157).then(n.bind(null,770))},\"v-5b708650\":function(){return n.e(41).then(n.bind(null,771))},\"v-0e5c6f0c\":function(){return n.e(7).then(n.bind(null,772))},\"v-5f3e175e\":function(){return n.e(21).then(n.bind(null,773))},\"v-2a46503e\":function(){return n.e(13).then(n.bind(null,774))},\"v-599a165e\":function(){return n.e(158).then(n.bind(null,775))},\"v-2ea81584\":function(){return n.e(14).then(n.bind(null,776))},\"v-73fa813e\":function(){return n.e(27).then(n.bind(null,777))},\"v-4017c5fe\":function(){return n.e(96).then(n.bind(null,778))},\"v-7ce47104\":function(){return n.e(97).then(n.bind(null,779))},\"v-59a0ec3e\":function(){return n.e(22).then(n.bind(null,780))},\"v-85e0b698\":function(){return n.e(28).then(n.bind(null,781))},\"v-a6d3b644\":function(){return n.e(98).then(n.bind(null,782))},\"v-8a566962\":function(){return n.e(159).then(n.bind(null,783))},\"v-634b70bc\":function(){return n.e(160).then(n.bind(null,784))},\"v-73253dde\":function(){return n.e(29).then(n.bind(null,785))},\"v-2a3e3d2c\":function(){return n.e(63).then(n.bind(null,786))},\"v-133d43b8\":function(){return n.e(44).then(n.bind(null,787))},\"v-57439bc4\":function(){return n.e(64).then(n.bind(null,788))},\"v-0520e844\":function(){return n.e(8).then(n.bind(null,789))},\"v-791109f6\":function(){return n.e(65).then(n.bind(null,790))},\"v-0761e3c2\":function(){return n.e(95).then(n.bind(null,791))},\"v-e06c6ac4\":function(){return n.e(99).then(n.bind(null,792))},\"v-bb12bc66\":function(){return n.e(161).then(n.bind(null,793))},\"v-279be048\":function(){return n.e(15).then(n.bind(null,794))},\"v-699b86d6\":function(){return n.e(100).then(n.bind(null,795))},\"v-7bbb6c20\":function(){return n.e(16).then(n.bind(null,796))},\"v-ee1d3314\":function(){return n.e(66).then(n.bind(null,797))},\"v-00179e5e\":function(){return n.e(45).then(n.bind(null,798))},\"v-ebcf0f6a\":function(){return n.e(162).then(n.bind(null,799))},\"v-41d22cc4\":function(){return n.e(163).then(n.bind(null,800))},\"v-43339144\":function(){return n.e(9).then(n.bind(null,801))},\"v-1250ccda\":function(){return n.e(46).then(n.bind(null,802))},\"v-0adf6844\":function(){return n.e(101).then(n.bind(null,803))},\"v-289bee1e\":function(){return n.e(3).then(n.bind(null,804))},\"v-ccf61948\":function(){return n.e(67).then(n.bind(null,805))},\"v-71ba4ec9\":function(){return n.e(164).then(n.bind(null,806))},\"v-67ed443e\":function(){return n.e(102).then(n.bind(null,807))},\"v-1d9fd93e\":function(){return n.e(68).then(n.bind(null,808))},\"v-35993562\":function(){return n.e(30).then(n.bind(null,809))},\"v-6c1d3c84\":function(){return n.e(165).then(n.bind(null,810))},\"v-e01bc684\":function(){return n.e(166).then(n.bind(null,811))},\"v-6ca53fa0\":function(){return n.e(31).then(n.bind(null,812))},\"v-595c2547\":function(){return n.e(167).then(n.bind(null,813))},\"v-45707ffe\":function(){return n.e(103).then(n.bind(null,814))},\"v-5075d41e\":function(){return n.e(104).then(n.bind(null,815))},\"v-5ac2e73e\":function(){return n.e(48).then(n.bind(null,816))},\"v-124b8fc0\":function(){return n.e(69).then(n.bind(null,817))},\"v-5ce18d44\":function(){return n.e(168).then(n.bind(null,818))},\"v-40fdfbc5\":function(){return n.e(169).then(n.bind(null,819))},\"v-0f9c46d0\":function(){return n.e(170).then(n.bind(null,820))},\"v-4dc3043e\":function(){return n.e(171).then(n.bind(null,821))},\"v-3863f70c\":function(){return n.e(70).then(n.bind(null,822))},\"v-a07b1986\":function(){return n.e(172).then(n.bind(null,823))},\"v-b83d4d04\":function(){return n.e(174).then(n.bind(null,824))},\"v-3d16ae44\":function(){return n.e(175).then(n.bind(null,825))},\"v-6e4f5d44\":function(){return n.e(176).then(n.bind(null,826))},\"v-c87a9a04\":function(){return n.e(177).then(n.bind(null,827))},\"v-e52985c4\":function(){return n.e(178).then(n.bind(null,828))},\"v-5b7acebe\":function(){return n.e(179).then(n.bind(null,829))},\"v-d420ae14\":function(){return n.e(47).then(n.bind(null,830))},\"v-4d5040b6\":function(){return n.e(223).then(n.bind(null,831))},\"v-7f132d5e\":function(){return n.e(105).then(n.bind(null,832))},\"v-32a6c837\":function(){return n.e(181).then(n.bind(null,833))},\"v-a39e7204\":function(){return n.e(6).then(n.bind(null,834))},\"v-64933c64\":function(){return n.e(182).then(n.bind(null,835))},\"v-784cb4d0\":function(){return n.e(106).then(n.bind(null,836))},\"v-171f753e\":function(){return n.e(107).then(n.bind(null,837))},\"v-3830054e\":function(){return n.e(108).then(n.bind(null,838))},\"v-eb28c6aa\":function(){return n.e(183).then(n.bind(null,839))},\"v-f1d5da04\":function(){return n.e(184).then(n.bind(null,840))},\"v-79c6bbc4\":function(){return n.e(109).then(n.bind(null,841))},\"v-d082d700\":function(){return n.e(185).then(n.bind(null,842))},\"v-21d3a8de\":function(){return n.e(186).then(n.bind(null,843))},\"v-1bff661e\":function(){return n.e(187).then(n.bind(null,844))},\"v-f4fe1a44\":function(){return n.e(110).then(n.bind(null,845))},\"v-f325a8ae\":function(){return n.e(188).then(n.bind(null,846))},\"v-179e5f5e\":function(){return n.e(111).then(n.bind(null,847))},\"v-5777c3fe\":function(){return n.e(112).then(n.bind(null,848))},\"v-53cfafc6\":function(){return n.e(189).then(n.bind(null,849))},\"v-26e93cac\":function(){return n.e(190).then(n.bind(null,850))},\"v-bc88e5ec\":function(){return n.e(71).then(n.bind(null,851))},\"v-19a9ffc4\":function(){return n.e(191).then(n.bind(null,852))},\"v-fb228ab2\":function(){return n.e(192).then(n.bind(null,853))},\"v-c86e7620\":function(){return n.e(193).then(n.bind(null,854))},\"v-4868f75e\":function(){return n.e(113).then(n.bind(null,855))},\"v-43fefcfe\":function(){return n.e(72).then(n.bind(null,856))},\"v-6f1da438\":function(){return n.e(194).then(n.bind(null,857))},\"v-7e7049a5\":function(){return n.e(195).then(n.bind(null,858))},\"v-015de4fe\":function(){return n.e(196).then(n.bind(null,859))},\"v-bcb3c97c\":function(){return n.e(197).then(n.bind(null,860))},\"v-4413f806\":function(){return n.e(114).then(n.bind(null,861))},\"v-e1d14584\":function(){return n.e(49).then(n.bind(null,862))},\"v-3bc635d6\":function(){return n.e(198).then(n.bind(null,863))},\"v-31686cc0\":function(){return n.e(199).then(n.bind(null,864))},\"v-7f0d8c62\":function(){return n.e(115).then(n.bind(null,865))},\"v-7a71d8a3\":function(){return n.e(200).then(n.bind(null,866))},\"v-1d0c97be\":function(){return n.e(116).then(n.bind(null,867))},\"v-1ee4ed84\":function(){return n.e(201).then(n.bind(null,868))},\"v-66801e68\":function(){return n.e(32).then(n.bind(null,869))},\"v-ff5605a8\":function(){return n.e(202).then(n.bind(null,870))},\"v-41a7bb18\":function(){return n.e(203).then(n.bind(null,871))},\"v-61f2fc7e\":function(){return n.e(50).then(n.bind(null,872))},\"v-2f91d4be\":function(){return n.e(117).then(n.bind(null,873))},\"v-721e795e\":function(){return n.e(204).then(n.bind(null,874))},\"v-3ddc62b0\":function(){return n.e(205).then(n.bind(null,875))},\"v-b4243004\":function(){return n.e(118).then(n.bind(null,876))},\"v-558e1b9e\":function(){return n.e(206).then(n.bind(null,877))},\"v-0e69043e\":function(){return n.e(33).then(n.bind(null,878))},\"v-33c1091e\":function(){return n.e(51).then(n.bind(null,879))},\"v-18aa58be\":function(){return n.e(43).then(n.bind(null,880))},\"v-7cd86197\":function(){return n.e(207).then(n.bind(null,881))},\"v-44f99264\":function(){return n.e(52).then(n.bind(null,882))},\"v-bc70f0c4\":function(){return n.e(119).then(n.bind(null,883))},\"v-21677144\":function(){return n.e(10).then(n.bind(null,884))},\"v-71ec0a12\":function(){return n.e(208).then(n.bind(null,885))},\"v-b54d4c78\":function(){return n.e(17).then(n.bind(null,886))},\"v-61c665a4\":function(){return n.e(34).then(n.bind(null,887))},\"v-75ff76d6\":function(){return n.e(23).then(n.bind(null,888))},\"v-1bfa063c\":function(){return n.e(18).then(n.bind(null,889))},\"v-2d7d46e0\":function(){return n.e(120).then(n.bind(null,890))},\"v-5fbad350\":function(){return n.e(121).then(n.bind(null,891))},\"v-0bc353b8\":function(){return n.e(24).then(n.bind(null,892))},\"v-113b9457\":function(){return n.e(209).then(n.bind(null,893))},\"v-c42c9184\":function(){return n.e(35).then(n.bind(null,894))},\"v-da668ee4\":function(){return n.e(122).then(n.bind(null,895))},\"v-2ef33ac4\":function(){return n.e(210).then(n.bind(null,896))},\"v-5c09bcfa\":function(){return n.e(36).then(n.bind(null,897))},\"v-4cab2dc4\":function(){return n.e(73).then(n.bind(null,898))},\"v-c47fd984\":function(){return n.e(53).then(n.bind(null,899))},\"v-bf6de9bc\":function(){return n.e(74).then(n.bind(null,900))},\"v-ccad755c\":function(){return n.e(11).then(n.bind(null,901))},\"v-a9cc6b44\":function(){return n.e(75).then(n.bind(null,902))},\"v-d752c44c\":function(){return n.e(123).then(n.bind(null,903))},\"v-5b6d2db7\":function(){return n.e(211).then(n.bind(null,904))},\"v-b98538c4\":function(){return n.e(124).then(n.bind(null,905))},\"v-5b676bbe\":function(){return n.e(19).then(n.bind(null,906))},\"v-5c06b7fe\":function(){return n.e(20).then(n.bind(null,907))},\"v-e7c1d544\":function(){return n.e(76).then(n.bind(null,908))},\"v-b4c271d2\":function(){return n.e(212).then(n.bind(null,909))},\"v-4517d02e\":function(){return n.e(54).then(n.bind(null,910))},\"v-18cdb544\":function(){return n.e(213).then(n.bind(null,911))},\"v-3f1fd904\":function(){return n.e(12).then(n.bind(null,912))},\"v-1f7008c4\":function(){return n.e(55).then(n.bind(null,913))},\"v-67fac5c4\":function(){return n.e(125).then(n.bind(null,914))},\"v-1cc8923e\":function(){return n.e(77).then(n.bind(null,915))},\"v-205f3f12\":function(){return n.e(214).then(n.bind(null,916))},\"v-63db8374\":function(){return n.e(126).then(n.bind(null,917))},\"v-339f6524\":function(){return n.e(78).then(n.bind(null,918))},\"v-581622c4\":function(){return n.e(37).then(n.bind(null,919))},\"v-f1d8fec8\":function(){return n.e(215).then(n.bind(null,920))},\"v-541c787c\":function(){return n.e(216).then(n.bind(null,921))},\"v-8c0e8c04\":function(){return n.e(38).then(n.bind(null,922))},\"v-3a01f9d7\":function(){return n.e(217).then(n.bind(null,923))},\"v-d28cf440\":function(){return n.e(127).then(n.bind(null,924))},\"v-62746032\":function(){return n.e(128).then(n.bind(null,925))},\"v-8bd98544\":function(){return n.e(56).then(n.bind(null,926))},\"v-72651364\":function(){return n.e(57).then(n.bind(null,927))},\"v-8de95804\":function(){return n.e(79).then(n.bind(null,928))},\"v-6ae8ab56\":function(){return n.e(218).then(n.bind(null,929))},\"v-f798d992\":function(){return n.e(219).then(n.bind(null,930))},\"v-f7956d04\":function(){return n.e(220).then(n.bind(null,931))},\"v-d4f6e118\":function(){return n.e(221).then(n.bind(null,932))},\"v-b4184284\":function(){return n.e(80).then(n.bind(null,933))},\"v-fde52892\":function(){return n.e(222).then(n.bind(null,934))},\"v-5ade8610\":function(){return n.e(224).then(n.bind(null,935))},\"v-0a03b62c\":function(){return n.e(225).then(n.bind(null,936))},\"v-7c80aebe\":function(){return n.e(226).then(n.bind(null,937))},\"v-49c18490\":function(){return n.e(227).then(n.bind(null,938))},\"v-77333b7a\":function(){return n.e(228).then(n.bind(null,939))},\"v-5a6d2e44\":function(){return n.e(229).then(n.bind(null,940))},\"v-55ace208\":function(){return n.e(180).then(n.bind(null,941))},\"v-03050454\":function(){return n.e(4).then(n.bind(null,942))}};function Li(t){var e=Object.create(null);return function(n){return e[n]||(e[n]=t(n))}}var Ai=/-(\\w)/g,Ci=Li((function(t){return t.replace(Ai,(function(t,e){return e?e.toUpperCase():\"\"}))})),$i=/\\B([A-Z])/g,Ti=Li((function(t){return t.replace($i,\"-$1\").toLowerCase()})),Ii=Li((function(t){return t.charAt(0).toUpperCase()+t.slice(1)}));function Ri(t,e){if(e)return t(e)?t(e):e.includes(\"-\")?t(Ii(Ci(e))):t(Ii(e))||t(Ti(e))}var Fi=Object.assign({},Oi,ji),Di=function(t){return Fi[t]},Mi=function(t){return ji[t]},Ni=function(t){return Oi[t]},Bi=function(t){return Ta.component(t)};function zi(t){return Ri(Mi,t)}function Ui(t){return Ri(Ni,t)}function Wi(t){return Ri(Di,t)}function Vi(t){return Ri(Bi,t)}function qi(){for(var t=arguments.length,e=new Array(t),n=0;n<t;n++)e[n]=arguments[n];return Promise.all(e.filter((function(t){return t})).map(function(){var t=a(regeneratorRuntime.mark((function t(e){var n;return regeneratorRuntime.wrap((function(t){for(;;)switch(t.prev=t.next){case 0:if(Vi(e)||!Wi(e)){t.next=5;break}return t.next=3,Wi(e)();case 3:n=t.sent,Ta.component(e,n.default);case 5:case\"end\":return t.stop()}}),t)})));return function(e){return t.apply(this,arguments)}}()))}function Hi(t,e){\"undefined\"!=typeof window&&window.__VUEPRESS__&&(window.__VUEPRESS__[t]=e)}n(96);var Gi=n(63);function Ki(t,e){return function(t){if(Array.isArray(t))return t}(t)||function(t,e){if(\"undefined\"!=typeof Symbol&&Symbol.iterator in Object(t)){var n=[],r=!0,a=!1,o=void 0;try{for(var i,l=t[Symbol.iterator]();!(r=(i=l.next()).done)&&(n.push(i.value),!e||n.length!==e);r=!0);}catch(t){a=!0,o=t}finally{try{r||null==l.return||l.return()}finally{if(a)throw o}}return n}}(t,e)||Object(Gi.a)(t,e)||function(){throw new TypeError(\"Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\")}()}n(170),n(175);var Ji=n(161),Xi=n.n(Ji),Qi={created:function(){if(this.siteMeta=this.$site.headTags.filter((function(t){return\"meta\"===Ki(t,1)[0]})).map((function(t){var e=Ki(t,2);e[0];return e[1]})),this.$ssrContext){var t=this.getMergedMetaTags();this.$ssrContext.title=this.$title,this.$ssrContext.lang=this.$lang,this.$ssrContext.pageMeta=(e=t)?e.map((function(t){var e=\"<meta\";return Object.keys(t).forEach((function(n){e+=\" \".concat(n,'=\"').concat(t[n],'\"')})),e+\">\"})).join(\"\\n    \"):\"\",this.$ssrContext.canonicalLink=Zi(this.$canonicalUrl)}var e},mounted:function(){this.currentMetaTags=Object(Si.a)(document.querySelectorAll(\"meta\")),this.updateMeta(),this.updateCanonicalLink()},methods:{updateMeta:function(){document.title=this.$title,document.documentElement.lang=this.$lang;var t=this.getMergedMetaTags();this.currentMetaTags=tl(t,this.currentMetaTags)},getMergedMetaTags:function(){var t=this.$page.frontmatter.meta||[];return Xi()([{name:\"description\",content:this.$description}],t,this.siteMeta,el)},updateCanonicalLink:function(){Yi(),this.$canonicalUrl&&document.head.insertAdjacentHTML(\"beforeend\",Zi(this.$canonicalUrl))}},watch:{$page:function(){this.updateMeta(),this.updateCanonicalLink()}},beforeDestroy:function(){tl(null,this.currentMetaTags),Yi()}};function Yi(){var t=document.querySelector(\"link[rel='canonical']\");t&&t.remove()}function Zi(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:\"\";return t?'<link href=\"'.concat(t,'\" rel=\"canonical\" />'):\"\"}function tl(t,e){if(e&&Object(Si.a)(e).filter((function(t){return t.parentNode===document.head})).forEach((function(t){return document.head.removeChild(t)})),t)return t.map((function(t){var e=document.createElement(\"meta\");return Object.keys(t).forEach((function(n){e.setAttribute(n,t[n])})),document.head.appendChild(e),e}))}function el(t){for(var e=0,n=[\"name\",\"property\",\"itemprop\"];e<n.length;e++){var r=n[e];if(t.hasOwnProperty(r))return t[r]+r}return JSON.stringify(t)}n(92);var nl=n(162),rl={mounted:function(){window.addEventListener(\"scroll\",this.onScroll)},methods:{onScroll:n.n(nl)()((function(){this.setActiveHash()}),300),setActiveHash:function(){for(var t=this,e=[].slice.call(document.querySelectorAll(\".sidebar-link\")),n=[].slice.call(document.querySelectorAll(\".header-anchor\")).filter((function(t){return e.some((function(e){return e.hash===t.hash}))})),r=Math.max(window.pageYOffset,document.documentElement.scrollTop,document.body.scrollTop),a=Math.max(document.documentElement.scrollHeight,document.body.scrollHeight),o=window.innerHeight+r,i=0;i<n.length;i++){var l=n[i],c=n[i+1],u=0===i&&0===r||r>=l.parentElement.offsetTop+10&&(!c||r<c.parentElement.offsetTop-10),s=decodeURIComponent(this.$route.hash);if(u&&s!==decodeURIComponent(l.hash)){var h=l;if(o===a)for(var f=i+1;f<n.length;f++)if(s===decodeURIComponent(n[f].hash))return;return this.$vuepress.$set(\"disableScrollBehavior\",!0),void this.$router.replace(decodeURIComponent(h.hash),(function(){t.$nextTick((function(){t.$vuepress.$set(\"disableScrollBehavior\",!1)}))}))}}}},beforeDestroy:function(){window.removeEventListener(\"scroll\",this.onScroll)}},al=(n(82),n(64)),ol=n.n(al),il=[Qi,rl,{mounted:function(){var t=this;ol.a.configure({showSpinner:!1}),this.$router.beforeEach((function(t,e,n){t.path===e.path||Ta.component(t.name)||ol.a.start(),n()})),this.$router.afterEach((function(){ol.a.done(),t.isSidebarOpen=!1}))}}],ll={name:\"GlobalLayout\",computed:{layout:function(){var t=this.getLayout();return Hi(\"layout\",t),Ta.component(t)}},methods:{getLayout:function(){if(this.$page.path){var t=this.$page.frontmatter.layout;return t&&(this.$vuepress.getLayoutAsyncComponent(t)||this.$vuepress.getVueComponent(t))?t:\"Layout\"}return\"NotFound\"}}},cl=n(45),ul=Object(cl.a)(ll,(function(){var t=this.$createElement;return(this._self._c||t)(this.layout,{tag:\"component\"})}),[],!1,null,null,null).exports;!function(t,e,n){var r;switch(e){case\"components\":t[e]||(t[e]={}),Object.assign(t[e],n);break;case\"mixins\":t[e]||(t[e]=[]),(r=t[e]).push.apply(r,Object(Si.a)(n));break;default:throw new Error(\"Unknown option name.\")}}(ul,\"mixins\",il);var sl=[{name:\"v-60cb9dee\",path:\"/chapter1/flutter_intro.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-60cb9dee\").then(n)}},{name:\"v-03ef47d5\",path:\"/chapter1/\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-03ef47d5\").then(n)}},{path:\"/chapter1/index.html\",redirect:\"/chapter1/\"},{name:\"v-7248ea50\",path:\"/chapter1/install_flutter.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-7248ea50\").then(n)}},{name:\"v-16a4b1be\",path:\"/chapter1/dart.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-16a4b1be\").then(n)}},{name:\"v-2c033278\",path:\"/chapter10/custom_paint.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-2c033278\").then(n)}},{name:\"v-66e8855e\",path:\"/chapter10/gradient_circular_progress_demo.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-66e8855e\").then(n)}},{name:\"v-ed117e52\",path:\"/chapter10/\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-ed117e52\").then(n)}},{path:\"/chapter10/index.html\",redirect:\"/chapter10/\"},{name:\"v-6672113e\",path:\"/chapter1/mobile_development_intro.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-6672113e\").then(n)}},{name:\"v-2dcc24c4\",path:\"/chapter10/intro.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-2dcc24c4\").then(n)}},{name:\"v-1da3f35c\",path:\"/chapter10/turn_box.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-1da3f35c\").then(n)}},{name:\"v-d694e792\",path:\"/\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-d694e792\").then(n)}},{path:\"/index.html\",redirect:\"/\"},{name:\"v-2382c404\",path:\"/chapter11/dio.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-2382c404\").then(n)}},{name:\"v-6f12817e\",path:\"/chapter10/combine.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-6f12817e\").then(n)}},{name:\"v-272d95d4\",path:\"/chapter11/download_with_chunks.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-272d95d4\").then(n)}},{name:\"v-b0364844\",path:\"/chapter11/file_operation.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-b0364844\").then(n)}},{name:\"v-d3df9312\",path:\"/chapter11/\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-d3df9312\").then(n)}},{path:\"/chapter11/index.html\",redirect:\"/chapter11/\"},{name:\"v-11c7b800\",path:\"/chapter11/socket.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-11c7b800\").then(n)}},{name:\"v-1e1dbec4\",path:\"/chapter11/websocket.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-1e1dbec4\").then(n)}},{name:\"v-5eb39c1e\",path:\"/chapter12/develop_package.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-5eb39c1e\").then(n)}},{name:\"v-c5918b84\",path:\"/chapter12/develop_plugin.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-c5918b84\").then(n)}},{name:\"v-4b295d84\",path:\"/chapter12/android_implement.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-4b295d84\").then(n)}},{name:\"v-2fe89fd4\",path:\"/chapter11/http.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-2fe89fd4\").then(n)}},{name:\"v-baada7d2\",path:\"/chapter12/\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-baada7d2\").then(n)}},{path:\"/chapter12/index.html\",redirect:\"/chapter12/\"},{name:\"v-1ff609fc\",path:\"/chapter11/json_model.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-1ff609fc\").then(n)}},{name:\"v-5f7ec496\",path:\"/chapter12/platform-channel.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-5f7ec496\").then(n)}},{name:\"v-3225283e\",path:\"/chapter13/faq.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-3225283e\").then(n)}},{name:\"v-a17bbc92\",path:\"/chapter13/\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-a17bbc92\").then(n)}},{path:\"/chapter13/index.html\",redirect:\"/chapter13/\"},{name:\"v-55e5e020\",path:\"/chapter12/texture_platformview.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-55e5e020\").then(n)}},{name:\"v-7e5c7f20\",path:\"/chapter13/intl.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-7e5c7f20\").then(n)}},{name:\"v-1dbbca9e\",path:\"/chapter13/locallization_implement.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-1dbbca9e\").then(n)}},{name:\"v-484f9fde\",path:\"/chapter13/multi_languages_support.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-484f9fde\").then(n)}},{name:\"v-3ab0e9b8\",path:\"/chapter14/element_buildcontext.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-3ab0e9b8\").then(n)}},{name:\"v-2537f144\",path:\"/chapter14/flutter_app_startup.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-2537f144\").then(n)}},{name:\"v-0d5219fe\",path:\"/chapter14/flutter_ui_system.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-0d5219fe\").then(n)}},{name:\"v-680b68c4\",path:\"/chapter14/image_and_cache.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-680b68c4\").then(n)}},{name:\"v-8849d152\",path:\"/chapter14/\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-8849d152\").then(n)}},{path:\"/chapter14/index.html\",redirect:\"/chapter14/\"},{name:\"v-36ef989e\",path:\"/chapter14/render_object.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-36ef989e\").then(n)}},{name:\"v-818c75a8\",path:\"/chapter15/code_structure.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-818c75a8\").then(n)}},{name:\"v-114a03be\",path:\"/chapter15/globals.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-114a03be\").then(n)}},{name:\"v-781e11be\",path:\"/chapter15/intro.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-781e11be\").then(n)}},{name:\"v-3cac337e\",path:\"/chapter15/entry.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-3cac337e\").then(n)}},{name:\"v-1b6582b8\",path:\"/chapter15/login_page.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-1b6582b8\").then(n)}},{name:\"v-57124e5a\",path:\"/chapter15/models.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-57124e5a\").then(n)}},{name:\"v-4d0690fe\",path:\"/chapter15/network.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-4d0690fe\").then(n)}},{name:\"v-31ab8fa0\",path:\"/chapter2/first_flutter_app.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-31ab8fa0\").then(n)}},{name:\"v-379da24c\",path:\"/chapter2/flutter_app_debug.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-379da24c\").then(n)}},{name:\"v-1f7d17c4\",path:\"/chapter2/flutter_assets_mgr.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-1f7d17c4\").then(n)}},{name:\"v-525634fe\",path:\"/chapter12/ios_implement.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-525634fe\").then(n)}},{name:\"v-f93817f4\",path:\"/chapter2/flutter_package_mgr.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-f93817f4\").then(n)}},{name:\"v-28ddc35a\",path:\"/chapter2/\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-28ddc35a\").then(n)}},{path:\"/chapter2/index.html\",redirect:\"/chapter2/\"},{name:\"v-5b708650\",path:\"/chapter15/language_and_theme_setting.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-5b708650\").then(n)}},{name:\"v-0e5c6f0c\",path:\"/chapter3/buttons.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-0e5c6f0c\").then(n)}},{name:\"v-5f3e175e\",path:\"/chapter3/flutter_widget_intro.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-5f3e175e\").then(n)}},{name:\"v-2a46503e\",path:\"/chapter3/img_and_icon.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-2a46503e\").then(n)}},{name:\"v-599a165e\",path:\"/chapter3/\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-599a165e\").then(n)}},{path:\"/chapter3/index.html\",redirect:\"/chapter3/\"},{name:\"v-2ea81584\",path:\"/chapter3/input_and_form.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-2ea81584\").then(n)}},{name:\"v-73fa813e\",path:\"/chapter3/progress.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-73fa813e\").then(n)}},{name:\"v-4017c5fe\",path:\"/chapter3/radio_and_checkbox.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-4017c5fe\").then(n)}},{name:\"v-7ce47104\",path:\"/chapter3/state_manage.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-7ce47104\").then(n)}},{name:\"v-59a0ec3e\",path:\"/chapter3/text.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-59a0ec3e\").then(n)}},{name:\"v-85e0b698\",path:\"/chapter4/alignment.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-85e0b698\").then(n)}},{name:\"v-a6d3b644\",path:\"/chapter4/flex.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-a6d3b644\").then(n)}},{name:\"v-8a566962\",path:\"/chapter4/\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-8a566962\").then(n)}},{path:\"/chapter4/index.html\",redirect:\"/chapter4/\"},{name:\"v-634b70bc\",path:\"/chapter4/intro.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-634b70bc\").then(n)}},{name:\"v-73253dde\",path:\"/chapter4/row_and_column.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-73253dde\").then(n)}},{name:\"v-2a3e3d2c\",path:\"/chapter4/stack.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-2a3e3d2c\").then(n)}},{name:\"v-133d43b8\",path:\"/chapter4/wrap_and_flow.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-133d43b8\").then(n)}},{name:\"v-57439bc4\",path:\"/chapter5/clip.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-57439bc4\").then(n)}},{name:\"v-0520e844\",path:\"/chapter5/constrainedbox_and_sizebox.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-0520e844\").then(n)}},{name:\"v-791109f6\",path:\"/chapter5/container.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-791109f6\").then(n)}},{name:\"v-0761e3c2\",path:\"/chapter2/thread_model_and_error_report.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-0761e3c2\").then(n)}},{name:\"v-e06c6ac4\",path:\"/chapter5/decoratedbox.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-e06c6ac4\").then(n)}},{name:\"v-bb12bc66\",path:\"/chapter5/\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-bb12bc66\").then(n)}},{path:\"/chapter5/index.html\",redirect:\"/chapter5/\"},{name:\"v-279be048\",path:\"/chapter5/material_scaffold.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-279be048\").then(n)}},{name:\"v-699b86d6\",path:\"/chapter5/padding.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-699b86d6\").then(n)}},{name:\"v-7bbb6c20\",path:\"/chapter5/transform.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-7bbb6c20\").then(n)}},{name:\"v-ee1d3314\",path:\"/chapter6/custom_scrollview.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-ee1d3314\").then(n)}},{name:\"v-00179e5e\",path:\"/chapter6/gridview.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-00179e5e\").then(n)}},{name:\"v-ebcf0f6a\",path:\"/chapter6/\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-ebcf0f6a\").then(n)}},{path:\"/chapter6/index.html\",redirect:\"/chapter6/\"},{name:\"v-41d22cc4\",path:\"/chapter6/intro.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-41d22cc4\").then(n)}},{name:\"v-43339144\",path:\"/chapter6/listview.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-43339144\").then(n)}},{name:\"v-1250ccda\",path:\"/chapter6/scroll_controller.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-1250ccda\").then(n)}},{name:\"v-0adf6844\",path:\"/chapter6/single_child_scrollview.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-0adf6844\").then(n)}},{name:\"v-289bee1e\",path:\"/chapter7/dailog.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-289bee1e\").then(n)}},{name:\"v-ccf61948\",path:\"/chapter7/futurebuilder_and_streambuilder.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-ccf61948\").then(n)}},{name:\"v-71ba4ec9\",path:\"/chapter7/\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-71ba4ec9\").then(n)}},{path:\"/chapter7/index.html\",redirect:\"/chapter7/\"},{name:\"v-67ed443e\",path:\"/chapter7/inherited_widget.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-67ed443e\").then(n)}},{name:\"v-1d9fd93e\",path:\"/chapter7/provider.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-1d9fd93e\").then(n)}},{name:\"v-35993562\",path:\"/chapter7/theme.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-35993562\").then(n)}},{name:\"v-6c1d3c84\",path:\"/chapter7/willpopscope.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-6c1d3c84\").then(n)}},{name:\"v-e01bc684\",path:\"/chapter8/eventbus.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-e01bc684\").then(n)}},{name:\"v-6ca53fa0\",path:\"/chapter8/gesture.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-6ca53fa0\").then(n)}},{name:\"v-595c2547\",path:\"/chapter8/\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-595c2547\").then(n)}},{path:\"/chapter8/index.html\",redirect:\"/chapter8/\"},{name:\"v-45707ffe\",path:\"/chapter8/listener.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-45707ffe\").then(n)}},{name:\"v-5075d41e\",path:\"/chapter8/notification.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-5075d41e\").then(n)}},{name:\"v-5ac2e73e\",path:\"/chapter9/animated_widgets.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-5ac2e73e\").then(n)}},{name:\"v-124b8fc0\",path:\"/chapter9/animation_structure.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-124b8fc0\").then(n)}},{name:\"v-5ce18d44\",path:\"/chapter9/hero.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-5ce18d44\").then(n)}},{name:\"v-40fdfbc5\",path:\"/chapter9/\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-40fdfbc5\").then(n)}},{path:\"/chapter9/index.html\",redirect:\"/chapter9/\"},{name:\"v-0f9c46d0\",path:\"/chapter9/intro.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-0f9c46d0\").then(n)}},{name:\"v-4dc3043e\",path:\"/chapter9/route_transition.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-4dc3043e\").then(n)}},{name:\"v-3863f70c\",path:\"/chapter9/stagger_animation.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-3863f70c\").then(n)}},{name:\"v-a07b1986\",path:\"/imgs/\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-a07b1986\").then(n)}},{path:\"/imgs/index.html\",redirect:\"/imgs/\"},{name:\"v-b83d4d04\",path:\"/intro.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-b83d4d04\").then(n)}},{name:\"v-3d16ae44\",path:\"/join_us.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-3d16ae44\").then(n)}},{name:\"v-6e4f5d44\",path:\"/next.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-6e4f5d44\").then(n)}},{name:\"v-c87a9a04\",path:\"/preface.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-c87a9a04\").then(n)}},{name:\"v-e52985c4\",path:\"/reference.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-e52985c4\").then(n)}},{name:\"v-5b7acebe\",path:\"/summary.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-5b7acebe\").then(n)}},{name:\"v-d420ae14\",path:\"/chapter9/animated_switcher.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-d420ae14\").then(n)}},{name:\"v-4d5040b6\",path:\"/v2/\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-4d5040b6\").then(n)}},{path:\"/v2/index.html\",redirect:\"/v2/\"},{name:\"v-7f132d5e\",path:\"/v2/chapter1/flutter_intro.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-7f132d5e\").then(n)}},{name:\"v-32a6c837\",path:\"/v2/chapter1/\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-32a6c837\").then(n)}},{path:\"/v2/chapter1/index.html\",redirect:\"/v2/chapter1/\"},{name:\"v-a39e7204\",path:\"/v2/chapter1/install_flutter.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-a39e7204\").then(n)}},{name:\"v-64933c64\",path:\"/v2/chapter1/mobile_development_intro.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-64933c64\").then(n)}},{name:\"v-784cb4d0\",path:\"/v2/chapter10/combine.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-784cb4d0\").then(n)}},{name:\"v-171f753e\",path:\"/v2/chapter10/custom_paint.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-171f753e\").then(n)}},{name:\"v-3830054e\",path:\"/v2/chapter10/gradient_circular_progress_demo.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-3830054e\").then(n)}},{name:\"v-eb28c6aa\",path:\"/v2/chapter10/\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-eb28c6aa\").then(n)}},{path:\"/v2/chapter10/index.html\",redirect:\"/v2/chapter10/\"},{name:\"v-f1d5da04\",path:\"/v2/chapter10/intro.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-f1d5da04\").then(n)}},{name:\"v-79c6bbc4\",path:\"/v2/chapter10/turn_box.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-79c6bbc4\").then(n)}},{name:\"v-d082d700\",path:\"/v2/chapter11/dio.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-d082d700\").then(n)}},{name:\"v-21d3a8de\",path:\"/v2/chapter11/download_with_chunks.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-21d3a8de\").then(n)}},{name:\"v-1bff661e\",path:\"/v2/chapter11/file_operation.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-1bff661e\").then(n)}},{name:\"v-f4fe1a44\",path:\"/v2/chapter11/http.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-f4fe1a44\").then(n)}},{name:\"v-f325a8ae\",path:\"/v2/chapter11/\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-f325a8ae\").then(n)}},{path:\"/v2/chapter11/index.html\",redirect:\"/v2/chapter11/\"},{name:\"v-179e5f5e\",path:\"/v2/chapter11/json_model.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-179e5f5e\").then(n)}},{name:\"v-5777c3fe\",path:\"/v2/chapter11/socket.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-5777c3fe\").then(n)}},{name:\"v-53cfafc6\",path:\"/v2/chapter11/websocket.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-53cfafc6\").then(n)}},{name:\"v-26e93cac\",path:\"/v2/chapter12/android_implement.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-26e93cac\").then(n)}},{name:\"v-bc88e5ec\",path:\"/v2/chapter12/develop_package.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-bc88e5ec\").then(n)}},{name:\"v-19a9ffc4\",path:\"/v2/chapter12/develop_plugin.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-19a9ffc4\").then(n)}},{name:\"v-fb228ab2\",path:\"/v2/chapter12/\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-fb228ab2\").then(n)}},{path:\"/v2/chapter12/index.html\",redirect:\"/v2/chapter12/\"},{name:\"v-c86e7620\",path:\"/v2/chapter12/ios_implement.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-c86e7620\").then(n)}},{name:\"v-4868f75e\",path:\"/v2/chapter12/platform-channel.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-4868f75e\").then(n)}},{name:\"v-43fefcfe\",path:\"/v2/chapter12/texture_platformview.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-43fefcfe\").then(n)}},{name:\"v-6f1da438\",path:\"/v2/chapter13/faq.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-6f1da438\").then(n)}},{name:\"v-7e7049a5\",path:\"/v2/chapter13/\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-7e7049a5\").then(n)}},{path:\"/v2/chapter13/index.html\",redirect:\"/v2/chapter13/\"},{name:\"v-015de4fe\",path:\"/v2/chapter13/intl.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-015de4fe\").then(n)}},{name:\"v-bcb3c97c\",path:\"/v2/chapter13/locallization_implement.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-bcb3c97c\").then(n)}},{name:\"v-4413f806\",path:\"/v2/chapter13/multi_languages_support.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-4413f806\").then(n)}},{name:\"v-e1d14584\",path:\"/v2/chapter14/element_buildcontext.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-e1d14584\").then(n)}},{name:\"v-3bc635d6\",path:\"/v2/chapter14/flutter_app_startup.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-3bc635d6\").then(n)}},{name:\"v-31686cc0\",path:\"/v2/chapter14/flutter_ui_system.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-31686cc0\").then(n)}},{name:\"v-7f0d8c62\",path:\"/v2/chapter14/image_and_cache.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-7f0d8c62\").then(n)}},{name:\"v-7a71d8a3\",path:\"/v2/chapter14/\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-7a71d8a3\").then(n)}},{path:\"/v2/chapter14/index.html\",redirect:\"/v2/chapter14/\"},{name:\"v-1d0c97be\",path:\"/v2/chapter14/render_object.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-1d0c97be\").then(n)}},{name:\"v-1ee4ed84\",path:\"/v2/chapter15/code_structure.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-1ee4ed84\").then(n)}},{name:\"v-66801e68\",path:\"/v2/chapter15/entry.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-66801e68\").then(n)}},{name:\"v-ff5605a8\",path:\"/v2/chapter15/globals.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-ff5605a8\").then(n)}},{name:\"v-41a7bb18\",path:\"/v2/chapter15/intro.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-41a7bb18\").then(n)}},{name:\"v-61f2fc7e\",path:\"/v2/chapter15/language_and_theme_setting.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-61f2fc7e\").then(n)}},{name:\"v-2f91d4be\",path:\"/v2/chapter15/login_page.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-2f91d4be\").then(n)}},{name:\"v-721e795e\",path:\"/v2/chapter15/models.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-721e795e\").then(n)}},{name:\"v-3ddc62b0\",path:\"/v2/chapter15/network.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-3ddc62b0\").then(n)}},{name:\"v-b4243004\",path:\"/v2/chapter2/first_flutter_app.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-b4243004\").then(n)}},{name:\"v-558e1b9e\",path:\"/v2/chapter2/flutter_app_debug.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-558e1b9e\").then(n)}},{name:\"v-0e69043e\",path:\"/v2/chapter2/flutter_assets_mgr.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-0e69043e\").then(n)}},{name:\"v-33c1091e\",path:\"/v2/chapter2/flutter_package_mgr.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-33c1091e\").then(n)}},{name:\"v-18aa58be\",path:\"/chapter2/flutter_router.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-18aa58be\").then(n)}},{name:\"v-7cd86197\",path:\"/v2/chapter2/\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-7cd86197\").then(n)}},{path:\"/v2/chapter2/index.html\",redirect:\"/v2/chapter2/\"},{name:\"v-44f99264\",path:\"/v2/chapter2/flutter_router.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-44f99264\").then(n)}},{name:\"v-bc70f0c4\",path:\"/v2/chapter2/thread_model_and_error_report.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-bc70f0c4\").then(n)}},{name:\"v-21677144\",path:\"/v2/chapter3/buttons.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-21677144\").then(n)}},{name:\"v-71ec0a12\",path:\"/v2/chapter3/\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-71ec0a12\").then(n)}},{path:\"/v2/chapter3/index.html\",redirect:\"/v2/chapter3/\"},{name:\"v-b54d4c78\",path:\"/v2/chapter3/img_and_icon.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-b54d4c78\").then(n)}},{name:\"v-61c665a4\",path:\"/v2/chapter3/progress.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-61c665a4\").then(n)}},{name:\"v-75ff76d6\",path:\"/v2/chapter3/flutter_widget_intro.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-75ff76d6\").then(n)}},{name:\"v-1bfa063c\",path:\"/v2/chapter3/input_and_form.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-1bfa063c\").then(n)}},{name:\"v-2d7d46e0\",path:\"/v2/chapter3/radio_and_checkbox.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-2d7d46e0\").then(n)}},{name:\"v-5fbad350\",path:\"/v2/chapter3/state_manage.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-5fbad350\").then(n)}},{name:\"v-0bc353b8\",path:\"/v2/chapter3/text.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-0bc353b8\").then(n)}},{name:\"v-113b9457\",path:\"/v2/chapter4/\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-113b9457\").then(n)}},{path:\"/v2/chapter4/index.html\",redirect:\"/v2/chapter4/\"},{name:\"v-c42c9184\",path:\"/v2/chapter4/alignment.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-c42c9184\").then(n)}},{name:\"v-da668ee4\",path:\"/v2/chapter4/flex.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-da668ee4\").then(n)}},{name:\"v-2ef33ac4\",path:\"/v2/chapter4/intro.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-2ef33ac4\").then(n)}},{name:\"v-5c09bcfa\",path:\"/v2/chapter4/row_and_column.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-5c09bcfa\").then(n)}},{name:\"v-4cab2dc4\",path:\"/v2/chapter4/stack.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-4cab2dc4\").then(n)}},{name:\"v-c47fd984\",path:\"/v2/chapter4/wrap_and_flow.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-c47fd984\").then(n)}},{name:\"v-bf6de9bc\",path:\"/v2/chapter5/clip.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-bf6de9bc\").then(n)}},{name:\"v-ccad755c\",path:\"/v2/chapter5/constrainedbox_and_sizebox.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-ccad755c\").then(n)}},{name:\"v-a9cc6b44\",path:\"/v2/chapter5/container.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-a9cc6b44\").then(n)}},{name:\"v-d752c44c\",path:\"/v2/chapter5/decoratedbox.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-d752c44c\").then(n)}},{name:\"v-5b6d2db7\",path:\"/v2/chapter5/\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-5b6d2db7\").then(n)}},{path:\"/v2/chapter5/index.html\",redirect:\"/v2/chapter5/\"},{name:\"v-b98538c4\",path:\"/v2/chapter5/padding.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-b98538c4\").then(n)}},{name:\"v-5b676bbe\",path:\"/v2/chapter5/material_scaffold.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-5b676bbe\").then(n)}},{name:\"v-5c06b7fe\",path:\"/v2/chapter5/transform.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-5c06b7fe\").then(n)}},{name:\"v-e7c1d544\",path:\"/v2/chapter6/custom_scrollview.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-e7c1d544\").then(n)}},{name:\"v-b4c271d2\",path:\"/v2/chapter6/\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-b4c271d2\").then(n)}},{path:\"/v2/chapter6/index.html\",redirect:\"/v2/chapter6/\"},{name:\"v-4517d02e\",path:\"/v2/chapter6/gridview.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-4517d02e\").then(n)}},{name:\"v-18cdb544\",path:\"/v2/chapter6/intro.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-18cdb544\").then(n)}},{name:\"v-3f1fd904\",path:\"/v2/chapter6/listview.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-3f1fd904\").then(n)}},{name:\"v-1f7008c4\",path:\"/v2/chapter6/scroll_controller.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-1f7008c4\").then(n)}},{name:\"v-67fac5c4\",path:\"/v2/chapter6/single_child_scrollview.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-67fac5c4\").then(n)}},{name:\"v-1cc8923e\",path:\"/v2/chapter7/futurebuilder_and_streambuilder.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-1cc8923e\").then(n)}},{name:\"v-205f3f12\",path:\"/v2/chapter7/\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-205f3f12\").then(n)}},{path:\"/v2/chapter7/index.html\",redirect:\"/v2/chapter7/\"},{name:\"v-63db8374\",path:\"/v2/chapter7/inherited_widget.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-63db8374\").then(n)}},{name:\"v-339f6524\",path:\"/v2/chapter7/provider.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-339f6524\").then(n)}},{name:\"v-581622c4\",path:\"/v2/chapter7/theme.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-581622c4\").then(n)}},{name:\"v-f1d8fec8\",path:\"/v2/chapter7/willpopscope.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-f1d8fec8\").then(n)}},{name:\"v-541c787c\",path:\"/v2/chapter8/eventbus.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-541c787c\").then(n)}},{name:\"v-8c0e8c04\",path:\"/v2/chapter8/gesture.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-8c0e8c04\").then(n)}},{name:\"v-3a01f9d7\",path:\"/v2/chapter8/\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-3a01f9d7\").then(n)}},{path:\"/v2/chapter8/index.html\",redirect:\"/v2/chapter8/\"},{name:\"v-d28cf440\",path:\"/v2/chapter8/listener.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-d28cf440\").then(n)}},{name:\"v-62746032\",path:\"/v2/chapter8/notification.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-62746032\").then(n)}},{name:\"v-8bd98544\",path:\"/v2/chapter9/animated_switcher.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-8bd98544\").then(n)}},{name:\"v-72651364\",path:\"/v2/chapter9/animated_widgets.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-72651364\").then(n)}},{name:\"v-8de95804\",path:\"/v2/chapter9/animation_structure.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-8de95804\").then(n)}},{name:\"v-6ae8ab56\",path:\"/v2/chapter9/hero.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-6ae8ab56\").then(n)}},{name:\"v-f798d992\",path:\"/v2/chapter9/\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-f798d992\").then(n)}},{path:\"/v2/chapter9/index.html\",redirect:\"/v2/chapter9/\"},{name:\"v-f7956d04\",path:\"/v2/chapter9/intro.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-f7956d04\").then(n)}},{name:\"v-d4f6e118\",path:\"/v2/chapter9/route_transition.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-d4f6e118\").then(n)}},{name:\"v-b4184284\",path:\"/v2/chapter9/stagger_animation.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-b4184284\").then(n)}},{name:\"v-fde52892\",path:\"/v2/imgs/\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-fde52892\").then(n)}},{path:\"/v2/imgs/index.html\",redirect:\"/v2/imgs/\"},{name:\"v-5ade8610\",path:\"/v2/intro.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-5ade8610\").then(n)}},{name:\"v-0a03b62c\",path:\"/v2/join_us.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-0a03b62c\").then(n)}},{name:\"v-7c80aebe\",path:\"/v2/next.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-7c80aebe\").then(n)}},{name:\"v-49c18490\",path:\"/v2/preface.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-49c18490\").then(n)}},{name:\"v-77333b7a\",path:\"/v2/reference.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-77333b7a\").then(n)}},{name:\"v-5a6d2e44\",path:\"/v2/summary.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-5a6d2e44\").then(n)}},{name:\"v-55ace208\",path:\"/v2/chapter1/dart.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-55ace208\").then(n)}},{name:\"v-03050454\",path:\"/v2/chapter7/dailog.html\",component:ul,beforeEnter:function(t,e,n){qi(\"Layout\",\"v-03050454\").then(n)}},{path:\"*\",component:ul}],hl={title:\"《Flutter实战》电子书\",description:\"\",base:\"/\",headTags:[[\"link\",{rel:\"icon\",href:\"/logo.png\"}]],pages:[{title:\"1.2 初识Flutter\",frontmatter:{},regularPath:\"/chapter1/flutter_intro.html\",relativePath:\"chapter1/flutter_intro.md\",key:\"v-60cb9dee\",path:\"/chapter1/flutter_intro.html\",headers:[{level:2,title:\"1.2.1 Flutter简介\",slug:\"_1-2-1-flutter简介\"},{level:2,title:\"1.2.2 Flutter框架结构\",slug:\"_1-2-2-flutter框架结构\"},{level:3,title:\"Flutter Framework\",slug:\"flutter-framework\"},{level:3,title:\"Flutter Engine\",slug:\"flutter-engine\"},{level:3,title:\"总结\",slug:\"总结-2\"},{level:2,title:\"1.2.3 如何学习Flutter\",slug:\"_1-2-3-如何学习flutter\"},{level:3,title:\"资源\",slug:\"资源\"},{level:3,title:\"社区\",slug:\"社区\"},{level:3,title:\"总结\",slug:\"总结-3\"}]},{title:\"本章目录\",frontmatter:{},regularPath:\"/chapter1/\",relativePath:\"chapter1/index.md\",key:\"v-03ef47d5\",path:\"/chapter1/\",headers:[{level:2,title:\"本章目录\",slug:\"本章目录\"}]},{title:\"1.3 搭建Flutter开发环境\",frontmatter:{},regularPath:\"/chapter1/install_flutter.html\",relativePath:\"chapter1/install_flutter.md\",key:\"v-7248ea50\",path:\"/chapter1/install_flutter.html\",headers:[{level:2,title:\"1.3.1 安装Flutter\",slug:\"_1-3-1-安装flutter\"},{level:3,title:\"使用镜像\",slug:\"使用镜像\"},{level:3,title:\"在Windows上搭建Flutter开发环境\",slug:\"在windows上搭建flutter开发环境\"},{level:3,title:\"在macOS上搭建Flutter开发环境\",slug:\"在macos上搭建flutter开发环境\"},{level:3,title:\"升级 Flutter\",slug:\"升级-flutter\"},{level:2,title:\"1.3.2 IDE配置与使用\",slug:\"_1-3-2-ide配置与使用\"},{level:3,title:\"Android Studio 配置与使用\",slug:\"android-studio-配置与使用\"},{level:3,title:\"VS Code的配置与使用\",slug:\"vs-code的配置与使用\"},{level:2,title:\"1.3.3 连接设备运行Flutter应用\",slug:\"_1-3-3-连接设备运行flutter应用\"},{level:3,title:\"连接Android模拟器\",slug:\"连接android模拟器\"},{level:3,title:\"连接Android真机设备\",slug:\"连接android真机设备\"},{level:3,title:\"连接iOS模拟器\",slug:\"连接ios模拟器\"},{level:3,title:\"连接iOS真机设备\",slug:\"连接ios真机设备\"},{level:2,title:\"1.3.4 常见配置问题\",slug:\"_1-3-4-常见配置问题\"},{level:3,title:\"Android Studio问题\",slug:\"android-studio问题\"}]},{title:\"1.4 Dart语言简介\",frontmatter:{},regularPath:\"/chapter1/dart.html\",relativePath:\"chapter1/dart.md\",key:\"v-16a4b1be\",path:\"/chapter1/dart.html\",headers:[{level:2,title:\"1.4.1 变量声明\",slug:\"_1-4-1-变量声明\"},{level:2,title:\"1.4.2 函数\",slug:\"_1-4-2-函数\"},{level:2,title:\"1.4.3 异步支持\",slug:\"_1-4-3-异步支持\"},{level:3,title:\"Future\",slug:\"future\"},{level:3,title:\"Async/await\",slug:\"async-await\"},{level:2,title:\"1.4.4 Stream\",slug:\"_1-4-4-stream\"},{level:2,title:\"1.4.5 Dart和Java及JavaScript对比\",slug:\"_1-4-5-dart和java及javascript对比\"},{level:3,title:\"Dart vs Java\",slug:\"dart-vs-java\"},{level:3,title:\"Dart vs JavaScript\",slug:\"dart-vs-javascript\"}]},{title:\"10.4 自绘组件 （CustomPaint与Canvas）\",frontmatter:{},regularPath:\"/chapter10/custom_paint.html\",relativePath:\"chapter10/custom_paint.md\",key:\"v-2c033278\",path:\"/chapter10/custom_paint.html\",headers:[{level:3,title:\"CustomPaint\",slug:\"custompaint\"},{level:3,title:\"CustomPainter\",slug:\"custompainter\"},{level:3,title:\"画笔Paint\",slug:\"画笔paint\"},{level:3,title:\"示例：五子棋/盘\",slug:\"示例-五子棋-盘\"},{level:3,title:\"性能\",slug:\"性能\"},{level:3,title:\"总结\",slug:\"总结\"}]},{title:\"10.5 自绘实例：圆形背景渐变进度条\",frontmatter:{},regularPath:\"/chapter10/gradient_circular_progress_demo.html\",relativePath:\"chapter10/gradient_circular_progress_demo.md\",key:\"v-66e8855e\",path:\"/chapter10/gradient_circular_progress_demo.html\"},{title:\"本章目录\",frontmatter:{},regularPath:\"/chapter10/\",relativePath:\"chapter10/index.md\",key:\"v-ed117e52\",path:\"/chapter10/\"},{title:\"1.1 移动开发技术简介\",frontmatter:{},regularPath:\"/chapter1/mobile_development_intro.html\",relativePath:\"chapter1/mobile_development_intro.md\",key:\"v-6672113e\",path:\"/chapter1/mobile_development_intro.html\",headers:[{level:2,title:\"1.1.1 原生开发与跨平台技术\",slug:\"_1-1-1-原生开发与跨平台技术\"},{level:3,title:\"原生开发\",slug:\"原生开发\"},{level:3,title:\"跨平台技术简介\",slug:\"跨平台技术简介\"},{level:2,title:\"1.1.2 Hybrid技术简介\",slug:\"_1-1-2-hybrid技术简介\"},{level:3,title:\"H5+原生混合开发\",slug:\"h5-原生混合开发\"},{level:3,title:\"混合开发技术点\",slug:\"混合开发技术点\"},{level:3,title:\"总结\",slug:\"总结\"},{level:2,title:\"1.1.3 React Native、Weex及快应用\",slug:\"_1-1-3-react-native、weex及快应用\"},{level:3,title:\"DOM树与控件树\",slug:\"dom树与控件树\"},{level:3,title:\"响应式编程\",slug:\"响应式编程\"},{level:3,title:\"React Native\",slug:\"react-native\"},{level:3,title:\"Weex\",slug:\"weex\"},{level:3,title:\"快应用\",slug:\"快应用\"},{level:3,title:\"总结\",slug:\"总结-2\"},{level:2,title:\"1.1.4 QT Mobile\",slug:\"_1-1-4-qt-mobile\"},{level:3,title:\"QT简介\",slug:\"qt简介\"},{level:2,title:\"1.1.5 Flutter出世\",slug:\"_1-1-5-flutter出世\"},{level:2,title:\"1.1.6 小结\",slug:\"_1-1-6-小结\"}]},{title:\"10.1 自定义组件方法简介\",frontmatter:{},regularPath:\"/chapter10/intro.html\",relativePath:\"chapter10/intro.md\",key:\"v-2dcc24c4\",path:\"/chapter10/intro.html\",headers:[{level:3,title:\"组合其它Widget\",slug:\"组合其它widget\"},{level:3,title:\"自绘\",slug:\"自绘\"},{level:3,title:\"实现RenderObject\",slug:\"实现renderobject\"},{level:3,title:\"总结\",slug:\"总结\"}]},{title:\"10.3 组合实例：TurnBox\",frontmatter:{},regularPath:\"/chapter10/turn_box.html\",relativePath:\"chapter10/turn_box.md\",key:\"v-1da3f35c\",path:\"/chapter10/turn_box.html\"},{title:\"前言\",frontmatter:{},regularPath:\"/\",relativePath:\"index.md\",key:\"v-d694e792\",path:\"/\",headers:[{level:2,title:\"缘起\",slug:\"缘起\"},{level:3,title:\"本书组织结构\",slug:\"本书组织结构\"},{level:2,title:\"本书特色\",slug:\"本书特色\"},{level:2,title:\"本书读者对象\",slug:\"本书读者对象\"},{level:2,title:\"关于随书源码\",slug:\"关于随书源码\"},{level:2,title:\"致谢\",slug:\"致谢\"},{level:2,title:\"权益\",slug:\"权益\"},{level:3,title:\"勘误\",slug:\"勘误\"}]},{title:\"11.3 Http请求-Dio http库\",frontmatter:{},regularPath:\"/chapter11/dio.html\",relativePath:\"chapter11/dio.md\",key:\"v-2382c404\",path:\"/chapter11/dio.html\",headers:[{level:3,title:\"引入\",slug:\"引入\"},{level:3,title:\"示例\",slug:\"示例\"},{level:3,title:\"实例\",slug:\"实例\"}]},{title:\"10.2 组合现有组件\",frontmatter:{},regularPath:\"/chapter10/combine.html\",relativePath:\"chapter10/combine.md\",key:\"v-6f12817e\",path:\"/chapter10/combine.html\",headers:[{level:3,title:\"示例：自定义渐变按钮\",slug:\"示例-自定义渐变按钮\"},{level:3,title:\"总结\",slug:\"总结\"}]},{title:\"11.4 实例：Http分块下载\",frontmatter:{},regularPath:\"/chapter11/download_with_chunks.html\",relativePath:\"chapter11/download_with_chunks.md\",key:\"v-272d95d4\",path:\"/chapter11/download_with_chunks.html\",headers:[{level:3,title:\"原理\",slug:\"原理\"},{level:3,title:\"实现\",slug:\"实现\"},{level:3,title:\"思考\",slug:\"思考\"}]},{title:\"11.1 文件操作\",frontmatter:{},regularPath:\"/chapter11/file_operation.html\",relativePath:\"chapter11/file_operation.md\",key:\"v-b0364844\",path:\"/chapter11/file_operation.html\"},{title:\"本章目录\",frontmatter:{},regularPath:\"/chapter11/\",relativePath:\"chapter11/index.md\",key:\"v-d3df9312\",path:\"/chapter11/\"},{title:\"11.6 使用Socket API\",frontmatter:{},regularPath:\"/chapter11/socket.html\",relativePath:\"chapter11/socket.md\",key:\"v-11c7b800\",path:\"/chapter11/socket.html\"},{title:\"使用WebSockets\",frontmatter:{},regularPath:\"/chapter11/websocket.html\",relativePath:\"chapter11/websocket.md\",key:\"v-1e1dbec4\",path:\"/chapter11/websocket.html\",headers:[{level:3,title:\"步骤\",slug:\"步骤\"},{level:3,title:\"1. 连接到WebSocket服务器\",slug:\"_1-连接到websocket服务器\"},{level:3,title:\"2. 监听来自服务器的消息\",slug:\"_2-监听来自服务器的消息\"},{level:3,title:\"3. 将数据发送到服务器\",slug:\"_3-将数据发送到服务器\"},{level:3,title:\"4. 关闭WebSocket连接\",slug:\"_4-关闭websocket连接\"},{level:3,title:\"完整的例子\",slug:\"完整的例子\"}]},{title:\"12.1 开发Package\",frontmatter:{},regularPath:\"/chapter12/develop_package.html\",relativePath:\"chapter12/develop_package.md\",key:\"v-5eb39c1e\",path:\"/chapter12/develop_package.html\",headers:[{level:3,title:\"第一步：创建Dart包\",slug:\"第一步-创建dart包\"},{level:3,title:\"实现package\",slug:\"实现package\"},{level:3,title:\"导入包\",slug:\"导入包\"},{level:3,title:\"生成文档\",slug:\"生成文档\"},{level:3,title:\"处理包的相互依赖\",slug:\"处理包的相互依赖\"},{level:3,title:\"解决依赖冲突\",slug:\"解决依赖冲突\"},{level:3,title:\"发布Package\",slug:\"发布package\"}]},{title:\"12.3 开发Flutter插件\",frontmatter:{},regularPath:\"/chapter12/develop_plugin.html\",relativePath:\"chapter12/develop_plugin.md\",key:\"v-c5918b84\",path:\"/chapter12/develop_plugin.html\",headers:[{level:3,title:\"创建一个新的应用程序项目\",slug:\"创建一个新的应用程序项目\"},{level:3,title:\"创建Flutter平台客户端\",slug:\"创建flutter平台客户端\"}]},{title:\"12.4 插件开发：Android端API实现\",frontmatter:{},regularPath:\"/chapter12/android_implement.html\",relativePath:\"chapter12/android_implement.md\",key:\"v-4b295d84\",path:\"/chapter12/android_implement.html\",headers:[{level:3,title:\"使用Kotlin添加Android平台特定的实现\",slug:\"使用kotlin添加android平台特定的实现\"}]},{title:\"11.2 通过HttpClient发起HTTP请求\",frontmatter:{},regularPath:\"/chapter11/http.html\",relativePath:\"chapter11/http.md\",key:\"v-2fe89fd4\",path:\"/chapter11/http.html\"},{title:\"包与插件\",frontmatter:{},regularPath:\"/chapter12/\",relativePath:\"chapter12/index.md\",key:\"v-baada7d2\",path:\"/chapter12/\"},{title:\"11.7 Json转Dart Model类\",frontmatter:{},regularPath:\"/chapter11/json_model.html\",relativePath:\"chapter11/json_model.md\",key:\"v-1ff609fc\",path:\"/chapter11/json_model.html\",headers:[{level:3,title:\"自动生成Model\",slug:\"自动生成model\"},{level:3,title:\"在项目中设置json_serializable\",slug:\"在项目中设置json-serializable\"},{level:3,title:\"以json_serializable的方式创建model类\",slug:\"以json-serializable的方式创建model类\"},{level:3,title:\"运行代码生成程序\",slug:\"运行代码生成程序\"},{level:3,title:\"自动化生成模板\",slug:\"自动化生成模板\"},{level:3,title:\"Json_model 包\",slug:\"json-model-包\"},{level:3,title:\"使用IDE插件生成model\",slug:\"使用ide插件生成model\"},{level:3,title:\"FAQ\",slug:\"faq\"}]},{title:\"12.2 插件开发：平台通道简介\",frontmatter:{},regularPath:\"/chapter12/platform-channel.html\",relativePath:\"chapter12/platform-channel.md\",key:\"v-5f7ec496\",path:\"/chapter12/platform-channel.html\",headers:[{level:3,title:\"平台通道\",slug:\"平台通道\"},{level:3,title:\"平台通道数据类型支持\",slug:\"平台通道数据类型支持\"},{level:3,title:\"自定义编解码器\",slug:\"自定义编解码器\"},{level:3,title:\"如何获取平台信息\",slug:\"如何获取平台信息\"}]},{title:\"13.4 国际化常见问题\",frontmatter:{},regularPath:\"/chapter13/faq.html\",relativePath:\"chapter13/faq.md\",key:\"v-3225283e\",path:\"/chapter13/faq.html\",headers:[{level:3,title:\"默认语言区域不对\",slug:\"默认语言区域不对\"},{level:3,title:\"如何对应用标题进行国际化\",slug:\"如何对应用标题进行国际化\"},{level:3,title:\"如何为英语系的国家指定同一个locale\",slug:\"如何为英语系的国家指定同一个locale\"}]},{title:\"本章目录\",frontmatter:{},regularPath:\"/chapter13/\",relativePath:\"chapter13/index.md\",key:\"v-a17bbc92\",path:\"/chapter13/\",headers:[{level:2,title:\"本章目录\",slug:\"本章目录\"}]},{title:\"12.6 Texture和PlatformView\",frontmatter:{},regularPath:\"/chapter12/texture_platformview.html\",relativePath:\"chapter12/texture_platformview.md\",key:\"v-55e5e020\",path:\"/chapter12/texture_platformview.html\",headers:[{level:2,title:\"12.6.1 Texture（示例：使用摄像头）\",slug:\"_12-6-1-texture-示例-使用摄像头\"},{level:3,title:\"Texture用法\",slug:\"texture用法\"},{level:3,title:\"相机示例\",slug:\"相机示例\"},{level:2,title:\"12.6.2 PlatformView （示例：WebView）\",slug:\"_12-6-2-platformview-示例-webview\"}]},{title:\"使用Intl包\",frontmatter:{},regularPath:\"/chapter13/intl.html\",relativePath:\"chapter13/intl.md\",key:\"v-7e5c7f20\",path:\"/chapter13/intl.html\",headers:[{level:3,title:\"第一步：创建必要目录\",slug:\"第一步-创建必要目录\"},{level:3,title:\"第二步：实现Localizations和Delegate类\",slug:\"第二步-实现localizations和delegate类\"},{level:3,title:\"第三步：添加需要国际化的属性\",slug:\"第三步-添加需要国际化的属性\"},{level:3,title:\"第四步：生成arb文件\",slug:\"第四步-生成arb文件\"},{level:3,title:\"第五步：生成dart代码\",slug:\"第五步-生成dart代码\"},{level:3,title:\"总结\",slug:\"总结\"}]},{title:\"13.2 实现Localizations\",frontmatter:{},regularPath:\"/chapter13/locallization_implement.html\",relativePath:\"chapter13/locallization_implement.md\",key:\"v-1dbbca9e\",path:\"/chapter13/locallization_implement.html\",headers:[{level:3,title:\"实现Localizations类\",slug:\"实现localizations类\"},{level:3,title:\"实现Delegate类\",slug:\"实现delegate类\"},{level:3,title:\"最后一步：添加多语言支持\",slug:\"最后一步-添加多语言支持\"},{level:3,title:\"总结\",slug:\"总结\"}]},{title:\"13.1 让App支持多语言\",frontmatter:{},regularPath:\"/chapter13/multi_languages_support.html\",relativePath:\"chapter13/multi_languages_support.md\",key:\"v-484f9fde\",path:\"/chapter13/multi_languages_support.html\",headers:[{level:3,title:\"支持国际化\",slug:\"支持国际化\"},{level:3,title:\"获取当前区域Locale\",slug:\"获取当前区域locale\"},{level:3,title:\"监听系统语言切换\",slug:\"监听系统语言切换\"},{level:3,title:\"Localization 组件\",slug:\"localization-组件\"},{level:3,title:\"使用打包好的LocalizationsDelegates\",slug:\"使用打包好的localizationsdelegates\"}]},{title:\"14.2 Element与BuildContext\",frontmatter:{},regularPath:\"/chapter14/element_buildcontext.html\",relativePath:\"chapter14/element_buildcontext.md\",key:\"v-3ab0e9b8\",path:\"/chapter14/element_buildcontext.html\",headers:[{level:2,title:\"14.2.1 Element\",slug:\"_14-2-1-element\"},{level:2,title:\"14.2.2 BuildContext\",slug:\"_14-2-2-buildcontext\"},{level:3,title:\"进阶\",slug:\"进阶\"},{level:3,title:\"总结\",slug:\"总结\"}]},{title:\"14.4 Flutter运行机制-从启动到显示\",frontmatter:{},regularPath:\"/chapter14/flutter_app_startup.html\",relativePath:\"chapter14/flutter_app_startup.md\",key:\"v-2537f144\",path:\"/chapter14/flutter_app_startup.html\",headers:[{level:3,title:\"启动\",slug:\"启动\"},{level:3,title:\"渲染\",slug:\"渲染\"},{level:3,title:\"绘制\",slug:\"绘制\"},{level:3,title:\"总结\",slug:\"总结\"}]},{title:\"14.1 Flutter UI系统\",frontmatter:{},regularPath:\"/chapter14/flutter_ui_system.html\",relativePath:\"chapter14/flutter_ui_system.md\",key:\"v-0d5219fe\",path:\"/chapter14/flutter_ui_system.html\",headers:[{level:3,title:\"硬件绘图基本原理\",slug:\"硬件绘图基本原理\"},{level:3,title:\"操作系统绘制API的封装\",slug:\"操作系统绘制api的封装\"},{level:3,title:\"Flutter UI系统\",slug:\"flutter-ui系统\"}]},{title:\"14.5 图片加载原理与缓存\",frontmatter:{},regularPath:\"/chapter14/image_and_cache.html\",relativePath:\"chapter14/image_and_cache.md\",key:\"v-680b68c4\",path:\"/chapter14/image_and_cache.html\",headers:[{level:2,title:\"14.5.1 ImageProvider\",slug:\"_14-5-1-imageprovider\"},{level:3,title:\"总结\",slug:\"总结\"},{level:2,title:\"14.5.2 Image组件原理\",slug:\"_14-5-2-image组件原理\"},{level:2,title:\"总结\",slug:\"总结-2\"}]},{title:\"本章目录\",frontmatter:{},regularPath:\"/chapter14/\",relativePath:\"chapter14/index.md\",key:\"v-8849d152\",path:\"/chapter14/\"},{title:\"14.3 RenderObject和RenderBox\",frontmatter:{},regularPath:\"/chapter14/render_object.html\",relativePath:\"chapter14/render_object.md\",key:\"v-36ef989e\",path:\"/chapter14/render_object.html\",headers:[{level:2,title:\"14.3.1 布局过程\",slug:\"_14-3-1-布局过程\"},{level:3,title:\"Constraints\",slug:\"constraints\"},{level:2,title:\"14.3.2 绘制过程\",slug:\"_14-3-2-绘制过程\"},{level:3,title:\"RepaintBoundary\",slug:\"repaintboundary\"},{level:2,title:\"14.3.3 命中测试\",slug:\"_14-3-3-命中测试\"},{level:2,title:\"14.3.4 语义化\",slug:\"_14-3-4-语义化\"},{level:2,title:\"14.3.5 总结\",slug:\"_14-3-5-总结\"}]},{title:\"15.2 Flutter APP代码结构\",frontmatter:{},regularPath:\"/chapter15/code_structure.html\",relativePath:\"chapter15/code_structure.md\",key:\"v-818c75a8\",path:\"/chapter15/code_structure.html\"},{title:\"15.4 全局变量及共享状态\",frontmatter:{},regularPath:\"/chapter15/globals.html\",relativePath:\"chapter15/globals.md\",key:\"v-114a03be\",path:\"/chapter15/globals.html\",headers:[{level:2,title:\"15.4.1 全局变量-Global类\",slug:\"_15-4-1-全局变量-global类\"},{level:2,title:\"15.4.2 共享状态\",slug:\"_15-4-2-共享状态\"},{level:3,title:\"用户状态\",slug:\"用户状态\"},{level:3,title:\"APP主题状态\",slug:\"app主题状态\"},{level:3,title:\"APP语言状态\",slug:\"app语言状态\"}]},{title:\"15.1 Github客户端示例\",frontmatter:{},regularPath:\"/chapter15/intro.html\",relativePath:\"chapter15/intro.md\",key:\"v-781e11be\",path:\"/chapter15/intro.html\"},{title:\"15.6 APP入口及主页\",frontmatter:{},regularPath:\"/chapter15/entry.html\",relativePath:\"chapter15/entry.md\",key:\"v-3cac337e\",path:\"/chapter15/entry.html\",headers:[{level:2,title:\"15.6.1 APP入口\",slug:\"_15-6-1-app入口\"},{level:2,title:\"15.6.2 主页\",slug:\"_15-6-2-主页\"},{level:2,title:\"15.6.3 抽屉菜单\",slug:\"_15-6-3-抽屉菜单\"}]},{title:\"15.7 登录页\",frontmatter:{},regularPath:\"/chapter15/login_page.html\",relativePath:\"chapter15/login_page.md\",key:\"v-1b6582b8\",path:\"/chapter15/login_page.html\"},{title:\"15.3 Model类定义\",frontmatter:{},regularPath:\"/chapter15/models.html\",relativePath:\"chapter15/models.md\",key:\"v-57124e5a\",path:\"/chapter15/models.html\",headers:[{level:3,title:\"Github账号信息\",slug:\"github账号信息\"},{level:3,title:\"API缓存策略信息\",slug:\"api缓存策略信息\"},{level:3,title:\"用户信息\",slug:\"用户信息\"},{level:3,title:\"项目信息\",slug:\"项目信息\"},{level:3,title:\"生成Dart Model类\",slug:\"生成dart-model类\"},{level:3,title:\"数据持久化\",slug:\"数据持久化\"}]},{title:\"15.5 网络请求封装\",frontmatter:{},regularPath:\"/chapter15/network.html\",relativePath:\"chapter15/network.md\",key:\"v-4d0690fe\",path:\"/chapter15/network.html\",headers:[{level:2,title:\"15.5.1 网络接口缓存\",slug:\"_15-5-1-网络接口缓存\"},{level:2,title:\"15.5.2 封装网络请求\",slug:\"_15-5-2-封装网络请求\"}]},{title:\"2.1 计数器应用示例\",frontmatter:{},regularPath:\"/chapter2/first_flutter_app.html\",relativePath:\"chapter2/first_flutter_app.md\",key:\"v-31ab8fa0\",path:\"/chapter2/first_flutter_app.html\",headers:[{level:2,title:\"2.1.1 创建Flutter应用模板\",slug:\"_2-1-1-创建flutter应用模板\"},{level:3,title:\"分析\",slug:\"分析\"},{level:2,title:\"2.1.2 首页\",slug:\"_2-1-2-首页\"},{level:3,title:\"State类\",slug:\"state类\"}]},{title:\"2.5 调试Flutter应用\",frontmatter:{},regularPath:\"/chapter2/flutter_app_debug.html\",relativePath:\"chapter2/flutter_app_debug.md\",key:\"v-379da24c\",path:\"/chapter2/flutter_app_debug.html\",headers:[{level:3,title:\"Dart 分析器\",slug:\"dart-分析器\"},{level:3,title:\"Dart Observatory (语句级的单步调试和分析器)\",slug:\"dart-observatory-语句级的单步调试和分析器\"},{level:3,title:\"debugger() 声明\",slug:\"debugger-声明\"},{level:3,title:\"print、debugPrint、flutter logs\",slug:\"print、debugprint、flutter-logs\"},{level:3,title:\"调试模式断言\",slug:\"调试模式断言\"},{level:3,title:\"调试应用程序层\",slug:\"调试应用程序层\"},{level:3,title:\"语义\",slug:\"语义\"},{level:3,title:\"调度\",slug:\"调度\"},{level:3,title:\"可视化调试\",slug:\"可视化调试\"},{level:3,title:\"调试动画\",slug:\"调试动画\"},{level:3,title:\"调试性能问题\",slug:\"调试性能问题\"},{level:3,title:\"统计应用启动时间\",slug:\"统计应用启动时间\"},{level:3,title:\"跟踪Dart代码性能\",slug:\"跟踪dart代码性能\"}]},{title:\"2.4 资源管理\",frontmatter:{},regularPath:\"/chapter2/flutter_assets_mgr.html\",relativePath:\"chapter2/flutter_assets_mgr.md\",key:\"v-1f7d17c4\",path:\"/chapter2/flutter_assets_mgr.html\",headers:[{level:2,title:\"指定 assets\",slug:\"指定-assets\"},{level:2,title:\"Asset 变体（variant）\",slug:\"asset-变体-variant\"},{level:2,title:\"加载 assets\",slug:\"加载-assets\"},{level:3,title:\"加载文本assets\",slug:\"加载文本assets\"},{level:3,title:\"加载图片\",slug:\"加载图片\"},{level:3,title:\"特定平台 assets\",slug:\"特定平台-assets\"}]},{title:\"12.5 插件开发：iOS端API实现\",frontmatter:{},regularPath:\"/chapter12/ios_implement.html\",relativePath:\"chapter12/ios_implement.md\",key:\"v-525634fe\",path:\"/chapter12/ios_implement.html\",headers:[{level:3,title:\"使用Swift实现iOS API\",slug:\"使用swift实现ios-api\"}]},{title:\"2.3 包管理\",frontmatter:{},regularPath:\"/chapter2/flutter_package_mgr.html\",relativePath:\"chapter2/flutter_package_mgr.md\",key:\"v-f93817f4\",path:\"/chapter2/flutter_package_mgr.html\",headers:[{level:2,title:\"Pub仓库\",slug:\"pub仓库\"},{level:2,title:\"示例\",slug:\"示例\"},{level:2,title:\"其它依赖方式\",slug:\"其它依赖方式\"},{level:2,title:\"总结\",slug:\"总结\"}]},{title:\"简介\",frontmatter:{},regularPath:\"/chapter2/\",relativePath:\"chapter2/index.md\",key:\"v-28ddc35a\",path:\"/chapter2/\",headers:[{level:2,title:\"简介\",slug:\"简介\"},{level:2,title:\"本章目录\",slug:\"本章目录\"}]},{title:\"15.8 多语言和多主题\",frontmatter:{},regularPath:\"/chapter15/language_and_theme_setting.html\",relativePath:\"chapter15/language_and_theme_setting.md\",key:\"v-5b708650\",path:\"/chapter15/language_and_theme_setting.html\",headers:[{level:2,title:\"15.8.1 语言选择页\",slug:\"_15-8-1-语言选择页\"},{level:2,title:\"15.8.2 主题选择页\",slug:\"_15-8-2-主题选择页\"}]},{title:\"3.4 按钮\",frontmatter:{},regularPath:\"/chapter3/buttons.html\",relativePath:\"chapter3/buttons.md\",key:\"v-0e5c6f0c\",path:\"/chapter3/buttons.html\",headers:[{level:2,title:\"3.4.1 Material组件库中的按钮\",slug:\"_3-4-1-material组件库中的按钮\"},{level:3,title:\"RaisedButton\",slug:\"raisedbutton\"},{level:3,title:\"FlatButton\",slug:\"flatbutton\"},{level:3,title:\"OutlineButton\",slug:\"outlinebutton\"},{level:3,title:\"IconButton\",slug:\"iconbutton\"},{level:3,title:\"带图标的按钮\",slug:\"带图标的按钮\"},{level:2,title:\"3.4.2 自定义按钮外观\",slug:\"_3-4-2-自定义按钮外观\"}]},{title:\"3.1 Widget简介\",frontmatter:{},regularPath:\"/chapter3/flutter_widget_intro.html\",relativePath:\"chapter3/flutter_widget_intro.md\",key:\"v-5f3e175e\",path:\"/chapter3/flutter_widget_intro.html\",headers:[{level:2,title:\"3.1.1 概念\",slug:\"_3-1-1-概念\"},{level:2,title:\"3.1.2 Widget与Element\",slug:\"_3-1-2-widget与element\"},{level:2,title:\"3.1.3 Widget主要接口\",slug:\"_3-1-3-widget主要接口\"},{level:2,title:\"3.1.4 StatelessWidget\",slug:\"_3-1-4-statelesswidget\"},{level:3,title:\"Context\",slug:\"context\"},{level:2,title:\"3.1.5 StatefulWidget\",slug:\"_3-1-5-statefulwidget\"},{level:2,title:\"3.1.6 State\",slug:\"_3-1-6-state\"},{level:3,title:\"为什么要将build方法放在State中，而不是放在StatefulWidget中？\",slug:\"为什么要将build方法放在state中-而不是放在statefulwidget中\"},{level:2,title:\"3.1.7 在Widget树中获取State对象\",slug:\"_3-1-7-在widget树中获取state对象\"},{level:3,title:\"通过Context获取\",slug:\"通过context获取\"},{level:3,title:\"通过GlobalKey\",slug:\"通过globalkey\"},{level:2,title:\"3.1.8 Flutter SDK内置组件库介绍\",slug:\"_3-1-8-flutter-sdk内置组件库介绍\"},{level:3,title:\"关于示例\",slug:\"关于示例\"},{level:3,title:\"总结\",slug:\"总结\"}]},{title:\"3.5 图片及ICON\",frontmatter:{},regularPath:\"/chapter3/img_and_icon.html\",relativePath:\"chapter3/img_and_icon.md\",key:\"v-2a46503e\",path:\"/chapter3/img_and_icon.html\",headers:[{level:2,title:\"3.5 图片及ICON\",slug:\"_3-5-图片及icon\"},{level:2,title:\"3.5.1 图片\",slug:\"_3-5-1-图片\"},{level:3,title:\"ImageProvider\",slug:\"imageprovider\"},{level:3,title:\"Image\",slug:\"image\"},{level:3,title:\"Image缓存\",slug:\"image缓存\"},{level:2,title:\"3.5.2 ICON\",slug:\"_3-5-2-icon\"}]},{title:\"基础Widget\",frontmatter:{},regularPath:\"/chapter3/\",relativePath:\"chapter3/index.md\",key:\"v-599a165e\",path:\"/chapter3/\",headers:[{level:2,title:\"本章目录\",slug:\"本章目录\"}]},{title:\"3.7 输入框及表单\",frontmatter:{},regularPath:\"/chapter3/input_and_form.html\",relativePath:\"chapter3/input_and_form.md\",key:\"v-2ea81584\",path:\"/chapter3/input_and_form.html\",headers:[{level:2,title:\"3.7.1 TextField\",slug:\"_3-7-1-textfield\"},{level:2,title:\"3.7.2 表单Form\",slug:\"_3-7-2-表单form\"}]},{title:\"3.8 进度指示器\",frontmatter:{},regularPath:\"/chapter3/progress.html\",relativePath:\"chapter3/progress.md\",key:\"v-73fa813e\",path:\"/chapter3/progress.html\",headers:[{level:3,title:\"LinearProgressIndicator\",slug:\"linearprogressindicator\"},{level:3,title:\"示例\",slug:\"示例\"},{level:3,title:\"CircularProgressIndicator\",slug:\"circularprogressindicator\"},{level:3,title:\"自定义尺寸\",slug:\"自定义尺寸\"},{level:3,title:\"进度色动画\",slug:\"进度色动画\"},{level:3,title:\"自定义进度指示器样式\",slug:\"自定义进度指示器样式\"}]},{title:\"3.6 单选开关和复选框\",frontmatter:{},regularPath:\"/chapter3/radio_and_checkbox.html\",relativePath:\"chapter3/radio_and_checkbox.md\",key:\"v-4017c5fe\",path:\"/chapter3/radio_and_checkbox.html\",headers:[{level:3,title:\"属性及外观\",slug:\"属性及外观\"},{level:3,title:\"总结\",slug:\"总结\"}]},{title:\"3.2 状态管理\",frontmatter:{},regularPath:\"/chapter3/state_manage.html\",relativePath:\"chapter3/state_manage.md\",key:\"v-7ce47104\",path:\"/chapter3/state_manage.html\",headers:[{level:3,title:\"3.2.1 Widget管理自身状态\",slug:\"_3-2-1-widget管理自身状态\"},{level:3,title:\"3.2.2 父Widget管理子Widget的状态\",slug:\"_3-2-2-父widget管理子widget的状态\"},{level:3,title:\"3.2.3 混合状态管理\",slug:\"_3-2-3-混合状态管理\"},{level:3,title:\"3.2.4 全局状态管理\",slug:\"_3-2-4-全局状态管理\"}]},{title:\"3.3 文本及样式\",frontmatter:{},regularPath:\"/chapter3/text.html\",relativePath:\"chapter3/text.md\",key:\"v-59a0ec3e\",path:\"/chapter3/text.html\",headers:[{level:2,title:\"3.3 文本及样式\",slug:\"_3-3-文本及样式\"},{level:3,title:\"3.3.1 Text\",slug:\"_3-3-1-text\"},{level:3,title:\"3.3.2 TextStyle\",slug:\"_3-3-2-textstyle\"},{level:3,title:\"3.3.3 TextSpan\",slug:\"_3-3-3-textspan\"},{level:3,title:\"3.3.4 DefaultTextStyle\",slug:\"_3-3-4-defaulttextstyle\"},{level:3,title:\"3.3.5 字体\",slug:\"_3-3-5-字体\"}]},{title:\"4.6 对齐与相对定位（Align）\",frontmatter:{},regularPath:\"/chapter4/alignment.html\",relativePath:\"chapter4/alignment.md\",key:\"v-85e0b698\",path:\"/chapter4/alignment.html\",headers:[{level:2,title:\"4.6.1 Align\",slug:\"_4-6-1-align\"},{level:3,title:\"示例\",slug:\"示例\"},{level:3,title:\"Alignment\",slug:\"alignment\"},{level:3,title:\"FractionalOffset\",slug:\"fractionaloffset\"},{level:2,title:\"4.6.2 Align和Stack对比\",slug:\"_4-6-2-align和stack对比\"},{level:2,title:\"4.6.3 Center组件\",slug:\"_4-6-3-center组件\"},{level:2,title:\"总结\",slug:\"总结\"}]},{title:\"4.3 弹性布局（Flex）\",frontmatter:{},regularPath:\"/chapter4/flex.html\",relativePath:\"chapter4/flex.md\",key:\"v-a6d3b644\",path:\"/chapter4/flex.html\",headers:[{level:3,title:\"Flex\",slug:\"flex\"},{level:3,title:\"Expanded\",slug:\"expanded\"},{level:3,title:\"小结\",slug:\"小结\"}]},{title:\"本章目录\",frontmatter:{},regularPath:\"/chapter4/\",relativePath:\"chapter4/index.md\",key:\"v-8a566962\",path:\"/chapter4/\"},{title:\"4.1 布局类组件简介\",frontmatter:{},regularPath:\"/chapter4/intro.html\",relativePath:\"chapter4/intro.md\",key:\"v-634b70bc\",path:\"/chapter4/intro.html\"},{title:\"4.2 线性布局（Row和Column）\",frontmatter:{},regularPath:\"/chapter4/row_and_column.html\",relativePath:\"chapter4/row_and_column.md\",key:\"v-73253dde\",path:\"/chapter4/row_and_column.html\",headers:[{level:3,title:\"主轴和纵轴\",slug:\"主轴和纵轴\"},{level:3,title:\"Row\",slug:\"row\"},{level:3,title:\"示例\",slug:\"示例\"},{level:3,title:\"Column\",slug:\"column\"},{level:3,title:\"特殊情况\",slug:\"特殊情况\"}]},{title:\"4.5 层叠布局 Stack、Positioned\",frontmatter:{},regularPath:\"/chapter4/stack.html\",relativePath:\"chapter4/stack.md\",key:\"v-2a3e3d2c\",path:\"/chapter4/stack.html\",headers:[{level:3,title:\"Stack\",slug:\"stack\"},{level:3,title:\"Positioned\",slug:\"positioned\"},{level:3,title:\"示例\",slug:\"示例\"}]},{title:\"4.4 流式布局\",frontmatter:{},regularPath:\"/chapter4/wrap_and_flow.html\",relativePath:\"chapter4/wrap_and_flow.md\",key:\"v-133d43b8\",path:\"/chapter4/wrap_and_flow.html\",headers:[{level:2,title:\"4.4.1 Wrap\",slug:\"_4-4-1-wrap\"},{level:2,title:\"4.4.2 Flow\",slug:\"_4-4-2-flow\"}]},{title:\"5.7 剪裁（Clip）\",frontmatter:{},regularPath:\"/chapter5/clip.html\",relativePath:\"chapter5/clip.md\",key:\"v-57439bc4\",path:\"/chapter5/clip.html\",headers:[{level:3,title:\"CustomClipper\",slug:\"customclipper\"}]},{title:\"5.2 尺寸限制类容器\",frontmatter:{},regularPath:\"/chapter5/constrainedbox_and_sizebox.html\",relativePath:\"chapter5/constrainedbox_and_sizebox.md\",key:\"v-0520e844\",path:\"/chapter5/constrainedbox_and_sizebox.html\",headers:[{level:2,title:\"5.2.1 ConstrainedBox\",slug:\"_5-2-1-constrainedbox\"},{level:2,title:\"5.2.2 SizedBox\",slug:\"_5-2-2-sizedbox\"},{level:2,title:\"5.2.3 多重限制\",slug:\"_5-2-3-多重限制\"},{level:2,title:\"5.2.4 UnconstrainedBox\",slug:\"_5-2-4-unconstrainedbox\"},{level:2,title:\"5.2.4 其它尺寸限制类容器\",slug:\"_5-2-4-其它尺寸限制类容器\"}]},{title:\"5.5 Container\",frontmatter:{},regularPath:\"/chapter5/container.html\",relativePath:\"chapter5/container.md\",key:\"v-791109f6\",path:\"/chapter5/container.html\",headers:[{level:3,title:\"实例\",slug:\"实例\"},{level:3,title:\"Padding和Margin\",slug:\"padding和margin\"}]},{title:\"2.6 Flutter异常捕获\",frontmatter:{},regularPath:\"/chapter2/thread_model_and_error_report.html\",relativePath:\"chapter2/thread_model_and_error_report.md\",key:\"v-0761e3c2\",path:\"/chapter2/thread_model_and_error_report.html\",headers:[{level:2,title:\"2.6.1 Dart单线程模型\",slug:\"_2-6-1-dart单线程模型\"},{level:2,title:\"2.6.2 Flutter异常捕获\",slug:\"_2-6-2-flutter异常捕获\"},{level:3,title:\"Flutter框架异常捕获\",slug:\"flutter框架异常捕获\"},{level:3,title:\"其它异常捕获与日志收集\",slug:\"其它异常捕获与日志收集\"},{level:3,title:\"总结\",slug:\"总结\"}]},{title:\"5.3 装饰容器DecoratedBox\",frontmatter:{},regularPath:\"/chapter5/decoratedbox.html\",relativePath:\"chapter5/decoratedbox.md\",key:\"v-e06c6ac4\",path:\"/chapter5/decoratedbox.html\"},{title:\"容器类Widget\",frontmatter:{},regularPath:\"/chapter5/\",relativePath:\"chapter5/index.md\",key:\"v-bb12bc66\",path:\"/chapter5/\",headers:[{level:2,title:\"本章目录\",slug:\"本章目录\"}]},{title:\"5.6 Scaffold、TabBar、底部导航\",frontmatter:{},regularPath:\"/chapter5/material_scaffold.html\",relativePath:\"chapter5/material_scaffold.md\",key:\"v-279be048\",path:\"/chapter5/material_scaffold.html\",headers:[{level:2,title:\"5.6.1 Scaffold\",slug:\"_5-6-1-scaffold\"},{level:3,title:\"示例\",slug:\"示例\"},{level:2,title:\"5.6.2 AppBar\",slug:\"_5-6-2-appbar\"},{level:3,title:\"TabBar\",slug:\"tabbar\"},{level:3,title:\"TabBarView\",slug:\"tabbarview\"},{level:2,title:\"5.6.3 抽屉菜单Drawer\",slug:\"_5-6-3-抽屉菜单drawer\"},{level:2,title:\"5.6.4 FloatingActionButton\",slug:\"_5-6-4-floatingactionbutton\"},{level:2,title:\"5.6.5  底部Tab导航栏\",slug:\"_5-6-5-底部tab导航栏\"}]},{title:\"5.1 填充（Padding）\",frontmatter:{},regularPath:\"/chapter5/padding.html\",relativePath:\"chapter5/padding.md\",key:\"v-699b86d6\",path:\"/chapter5/padding.html\",headers:[{level:2,title:\"5.1 填充（Padding）\",slug:\"_5-1-填充-padding\"},{level:3,title:\"EdgeInsets\",slug:\"edgeinsets\"},{level:3,title:\"示例\",slug:\"示例\"}]},{title:\"5.4 变换（Transform）\",frontmatter:{},regularPath:\"/chapter5/transform.html\",relativePath:\"chapter5/transform.md\",key:\"v-7bbb6c20\",path:\"/chapter5/transform.html\",headers:[{level:3,title:\"平移\",slug:\"平移\"},{level:3,title:\"旋转\",slug:\"旋转\"},{level:3,title:\"缩放\",slug:\"缩放\"},{level:3,title:\"注意\",slug:\"注意\"},{level:3,title:\"RotatedBox\",slug:\"rotatedbox\"}]},{title:\"6.5 CustomScrollView\",frontmatter:{},regularPath:\"/chapter6/custom_scrollview.html\",relativePath:\"chapter6/custom_scrollview.md\",key:\"v-ee1d3314\",path:\"/chapter6/custom_scrollview.html\",headers:[{level:3,title:\"可滚动组件的Sliver版\",slug:\"可滚动组件的sliver版\"},{level:3,title:\"示例\",slug:\"示例\"}]},{title:\"6.4 GridView\",frontmatter:{},regularPath:\"/chapter6/gridview.html\",relativePath:\"chapter6/gridview.md\",key:\"v-00179e5e\",path:\"/chapter6/gridview.html\",headers:[{level:3,title:\"SliverGridDelegateWithFixedCrossAxisCount\",slug:\"slivergriddelegatewithfixedcrossaxiscount\"},{level:3,title:\"SliverGridDelegateWithMaxCrossAxisExtent\",slug:\"slivergriddelegatewithmaxcrossaxisextent\"},{level:3,title:\"GridView.builder\",slug:\"gridview-builder\"},{level:3,title:\"更多\",slug:\"更多\"}]},{title:\"本章目录\",frontmatter:{},regularPath:\"/chapter6/\",relativePath:\"chapter6/index.md\",key:\"v-ebcf0f6a\",path:\"/chapter6/\"},{title:\"6.1 可滚动组件简介\",frontmatter:{},regularPath:\"/chapter6/intro.html\",relativePath:\"chapter6/intro.md\",key:\"v-41d22cc4\",path:\"/chapter6/intro.html\",headers:[{level:3,title:\"Scrollbar\",slug:\"scrollbar\"},{level:3,title:\"ViewPort视口\",slug:\"viewport视口\"},{level:3,title:\"基于Sliver的延迟构建\",slug:\"基于sliver的延迟构建\"},{level:3,title:\"主轴和纵轴\",slug:\"主轴和纵轴\"}]},{title:\"6.3 ListView\",frontmatter:{},regularPath:\"/chapter6/listview.html\",relativePath:\"chapter6/listview.md\",key:\"v-43339144\",path:\"/chapter6/listview.html\",headers:[{level:3,title:\"默认构造函数\",slug:\"默认构造函数\"},{level:3,title:\"ListView.builder\",slug:\"listview-builder\"},{level:3,title:\"ListView.separated\",slug:\"listview-separated\"},{level:3,title:\"实例：无限加载列表\",slug:\"实例-无限加载列表\"},{level:3,title:\"添加固定列表头\",slug:\"添加固定列表头\"},{level:3,title:\"总结\",slug:\"总结\"}]},{title:\"6.6 滚动监听及控制\",frontmatter:{},regularPath:\"/chapter6/scroll_controller.html\",relativePath:\"chapter6/scroll_controller.md\",key:\"v-1250ccda\",path:\"/chapter6/scroll_controller.html\",headers:[{level:2,title:\"6.6.1 ScrollController\",slug:\"_6-6-1-scrollcontroller\"},{level:3,title:\"示例\",slug:\"示例\"},{level:3,title:\"滚动位置恢复\",slug:\"滚动位置恢复\"},{level:3,title:\"ScrollPosition\",slug:\"scrollposition\"},{level:3,title:\"ScrollController控制原理\",slug:\"scrollcontroller控制原理\"},{level:2,title:\"6.6.2 滚动监听\",slug:\"_6-6-2-滚动监听\"},{level:3,title:\"示例\",slug:\"示例-2\"}]},{title:\"6.2 SingleChildScrollView\",frontmatter:{},regularPath:\"/chapter6/single_child_scrollview.html\",relativePath:\"chapter6/single_child_scrollview.md\",key:\"v-0adf6844\",path:\"/chapter6/single_child_scrollview.html\",headers:[{level:3,title:\"示例\",slug:\"示例\"}]},{title:\"7.6 对话框详解\",frontmatter:{},regularPath:\"/chapter7/dailog.html\",relativePath:\"chapter7/dailog.md\",key:\"v-289bee1e\",path:\"/chapter7/dailog.html\",headers:[{level:2,title:\"7.6.1 使用对话框\",slug:\"_7-6-1-使用对话框\"},{level:3,title:\"AlertDialog\",slug:\"alertdialog\"},{level:3,title:\"SimpleDialog\",slug:\"simpledialog\"},{level:3,title:\"Dialog\",slug:\"dialog\"},{level:2,title:\"7.6.2 对话框打开动画及遮罩\",slug:\"_7-6-2-对话框打开动画及遮罩\"},{level:2,title:\"7.6.3 对话框实现原理\",slug:\"_7-6-3-对话框实现原理\"},{level:2,title:\"7.6.4 对话框状态管理\",slug:\"_7-6-4-对话框状态管理\"},{level:3,title:\"单独抽离出StatefulWidget\",slug:\"单独抽离出statefulwidget\"},{level:3,title:\"使用StatefulBuilder方法\",slug:\"使用statefulbuilder方法\"},{level:3,title:\"精妙的解法\",slug:\"精妙的解法\"},{level:2,title:\"7.6.5 其它类型的对话框\",slug:\"_7-6-5-其它类型的对话框\"},{level:3,title:\"底部菜单列表\",slug:\"底部菜单列表\"},{level:3,title:\"Loading框\",slug:\"loading框\"},{level:3,title:\"日历选择\",slug:\"日历选择\"}]},{title:\"7.5 异步UI更新（FutureBuilder、StreamBuilder）\",frontmatter:{},regularPath:\"/chapter7/futurebuilder_and_streambuilder.html\",relativePath:\"chapter7/futurebuilder_and_streambuilder.md\",key:\"v-ccf61948\",path:\"/chapter7/futurebuilder_and_streambuilder.html\",headers:[{level:2,title:\"7.5.1 FutureBuilder\",slug:\"_7-5-1-futurebuilder\"},{level:3,title:\"示例\",slug:\"示例\"},{level:2,title:\"7.5.2 StreamBuilder\",slug:\"_7-5-2-streambuilder\"},{level:3,title:\"示例\",slug:\"示例-2\"}]},{title:\"功能型Widget简介\",frontmatter:{},regularPath:\"/chapter7/\",relativePath:\"chapter7/index.md\",key:\"v-71ba4ec9\",path:\"/chapter7/\",headers:[{level:2,title:\"功能型Widget简介\",slug:\"功能型widget简介\"},{level:2,title:\"本章目录\",slug:\"本章目录\"}]},{title:\"7.2 数据共享（InheritedWidget）\",frontmatter:{},regularPath:\"/chapter7/inherited_widget.html\",relativePath:\"chapter7/inherited_widget.md\",key:\"v-67ed443e\",path:\"/chapter7/inherited_widget.html\",headers:[{level:3,title:\"didChangeDependencies\",slug:\"didchangedependencies\"},{level:3,title:\"深入了解InheritedWidget\",slug:\"深入了解inheritedwidget\"}]},{title:\"7.3 跨组件状态共享（Provider）\",frontmatter:{},regularPath:\"/chapter7/provider.html\",relativePath:\"chapter7/provider.md\",key:\"v-1d9fd93e\",path:\"/chapter7/provider.html\",headers:[{level:2,title:\"Provider\",slug:\"provider\"},{level:3,title:\"购物车示例\",slug:\"购物车示例\"},{level:3,title:\"优化\",slug:\"优化\"},{level:3,title:\"其它状态管理包\",slug:\"其它状态管理包\"},{level:3,title:\"总结\",slug:\"总结\"}]},{title:\"7.4 颜色和主题\",frontmatter:{},regularPath:\"/chapter7/theme.html\",relativePath:\"chapter7/theme.md\",key:\"v-35993562\",path:\"/chapter7/theme.html\",headers:[{level:2,title:\"7.4.1 颜色\",slug:\"_7-4-1-颜色\"},{level:3,title:\"如何将颜色字符串转成Color对象\",slug:\"如何将颜色字符串转成color对象\"},{level:3,title:\"颜色亮度\",slug:\"颜色亮度\"},{level:3,title:\"MaterialColor\",slug:\"materialcolor\"},{level:2,title:\"7.4.2 Theme\",slug:\"_7-4-2-theme\"},{level:3,title:\"ThemeData\",slug:\"themedata\"},{level:3,title:\"示例\",slug:\"示例\"}]},{title:\"7.1 导航返回拦截（WillPopScope）\",frontmatter:{},regularPath:\"/chapter7/willpopscope.html\",relativePath:\"chapter7/willpopscope.md\",key:\"v-6c1d3c84\",path:\"/chapter7/willpopscope.html\",headers:[{level:3,title:\"示例\",slug:\"示例\"}]},{title:\"8.3 事件总线\",frontmatter:{},regularPath:\"/chapter8/eventbus.html\",relativePath:\"chapter8/eventbus.md\",key:\"v-e01bc684\",path:\"/chapter8/eventbus.html\"},{title:\"8.2 手势识别\",frontmatter:{},regularPath:\"/chapter8/gesture.html\",relativePath:\"chapter8/gesture.md\",key:\"v-6ca53fa0\",path:\"/chapter8/gesture.html\",headers:[{level:2,title:\"8.2.1 GestureDetector\",slug:\"_8-2-1-gesturedetector\"},{level:3,title:\"点击、双击、长按\",slug:\"点击、双击、长按\"},{level:3,title:\"拖动、滑动\",slug:\"拖动、滑动\"},{level:3,title:\"单一方向拖动\",slug:\"单一方向拖动\"},{level:3,title:\"缩放\",slug:\"缩放\"},{level:2,title:\"8.2.2 GestureRecognizer\",slug:\"_8-2-2-gesturerecognizer\"},{level:2,title:\"8.2.3 手势竞争与冲突\",slug:\"_8-2-3-手势竞争与冲突\"},{level:3,title:\"竞争\",slug:\"竞争\"},{level:3,title:\"示例\",slug:\"示例-2\"},{level:3,title:\"手势冲突\",slug:\"手势冲突\"}]},{title:\"事件处理与通知\",frontmatter:{},regularPath:\"/chapter8/\",relativePath:\"chapter8/index.md\",key:\"v-595c2547\",path:\"/chapter8/\",headers:[{level:2,title:\"事件处理与通知\",slug:\"事件处理与通知\"},{level:2,title:\"本章目录\",slug:\"本章目录\"}]},{title:\"8.1 原始指针事件处理\",frontmatter:{},regularPath:\"/chapter8/listener.html\",relativePath:\"chapter8/listener.md\",key:\"v-45707ffe\",path:\"/chapter8/listener.html\",headers:[{level:3,title:\"忽略PointerEvent\",slug:\"忽略pointerevent\"}]},{title:\"8.4 Notification\",frontmatter:{},regularPath:\"/chapter8/notification.html\",relativePath:\"chapter8/notification.md\",key:\"v-5075d41e\",path:\"/chapter8/notification.html\",headers:[{level:3,title:\"阻止冒泡\",slug:\"阻止冒泡\"},{level:3,title:\"通知冒泡原理\",slug:\"通知冒泡原理\"},{level:3,title:\"总结\",slug:\"总结\"}]},{title:\"9.7 动画过渡组件\",frontmatter:{},regularPath:\"/chapter9/animated_widgets.html\",relativePath:\"chapter9/animated_widgets.md\",key:\"v-5ac2e73e\",path:\"/chapter9/animated_widgets.html\",headers:[{level:2,title:\"9.7.1 自定义动画过渡组件\",slug:\"_9-7-1-自定义动画过渡组件\"},{level:3,title:\"动画过渡组件的反向动画\",slug:\"动画过渡组件的反向动画\"},{level:2,title:\"9.7.2 Flutter预置的动画过渡组件\",slug:\"_9-7-2-flutter预置的动画过渡组件\"}]},{title:\"9.2 动画基本结构及状态监听\",frontmatter:{},regularPath:\"/chapter9/animation_structure.html\",relativePath:\"chapter9/animation_structure.md\",key:\"v-124b8fc0\",path:\"/chapter9/animation_structure.html\",headers:[{level:2,title:\"9.2.1 动画基本结构\",slug:\"_9-2-1-动画基本结构\"},{level:3,title:\"基础版本\",slug:\"基础版本\"},{level:3,title:\"使用AnimatedWidget简化\",slug:\"使用animatedwidget简化\"},{level:3,title:\"用AnimatedBuilder重构\",slug:\"用animatedbuilder重构\"},{level:2,title:\"9.2.2 动画状态监听\",slug:\"_9-2-2-动画状态监听\"}]},{title:\"9.4 Hero动画\",frontmatter:{},regularPath:\"/chapter9/hero.html\",relativePath:\"chapter9/hero.md\",key:\"v-5ce18d44\",path:\"/chapter9/hero.html\"},{title:\"简介\",frontmatter:{},regularPath:\"/chapter9/\",relativePath:\"chapter9/index.md\",key:\"v-40fdfbc5\",path:\"/chapter9/\",headers:[{level:2,title:\"简介\",slug:\"简介\"},{level:2,title:\"本章目录\",slug:\"本章目录\"}]},{title:\"9.1 Flutter动画简介\",frontmatter:{},regularPath:\"/chapter9/intro.html\",relativePath:\"chapter9/intro.md\",key:\"v-0f9c46d0\",path:\"/chapter9/intro.html\",headers:[{level:3,title:\"Flutter中动画抽象\",slug:\"flutter中动画抽象\"},{level:3,title:\"Animation\",slug:\"animation\"},{level:3,title:\"Curve\",slug:\"curve\"},{level:3,title:\"AnimationController\",slug:\"animationcontroller\"},{level:3,title:\"Tween\",slug:\"tween\"}]},{title:\"9.3 自定义路由切换动画\",frontmatter:{},regularPath:\"/chapter9/route_transition.html\",relativePath:\"chapter9/route_transition.md\",key:\"v-4dc3043e\",path:\"/chapter9/route_transition.html\"},{title:\"9.5 交织动画\",frontmatter:{},regularPath:\"/chapter9/stagger_animation.html\",relativePath:\"chapter9/stagger_animation.md\",key:\"v-3863f70c\",path:\"/chapter9/stagger_animation.html\",headers:[{level:3,title:\"示例\",slug:\"示例\"}]},{title:\"前言\",frontmatter:{},regularPath:\"/imgs/\",relativePath:\"imgs/index.md\",key:\"v-a07b1986\",path:\"/imgs/\",headers:[{level:2,title:\"缘起\",slug:\"缘起\"},{level:3,title:\"本书组织结构\",slug:\"本书组织结构\"},{level:2,title:\"本书特色\",slug:\"本书特色\"},{level:2,title:\"本书读者对象\",slug:\"本书读者对象\"},{level:2,title:\"关于随书源码\",slug:\"关于随书源码\"},{level:2,title:\"致谢\",slug:\"致谢\"},{level:2,title:\"权益\",slug:\"权益\"},{level:3,title:\"勘误\",slug:\"勘误\"}]},{title:\"前言\",frontmatter:{},regularPath:\"/intro.html\",relativePath:\"intro.md\",key:\"v-b83d4d04\",path:\"/intro.html\",headers:[{level:2,title:\"缘起\",slug:\"缘起\"},{level:3,title:\"本书组织结构\",slug:\"本书组织结构\"},{level:2,title:\"本书特色\",slug:\"本书特色\"},{level:2,title:\"本书读者对象\",slug:\"本书读者对象\"},{level:2,title:\"关于随书源码\",slug:\"关于随书源码\"},{level:2,title:\"致谢\",slug:\"致谢\"},{level:2,title:\"权益\",slug:\"权益\"},{level:3,title:\"勘误\",slug:\"勘误\"}]},{title:\"字节跳动-内推\",frontmatter:{sidebar:\"auto\"},regularPath:\"/join_us.html\",relativePath:\"join_us.md\",key:\"v-3d16ae44\",path:\"/join_us.html\",headers:[{level:2,title:\"前端团队介绍\",slug:\"前端团队介绍\"},{level:3,title:\"业务线介绍\",slug:\"业务线介绍\"},{level:3,title:\"团队福利\",slug:\"团队福利\"},{level:2,title:\"职位介绍\",slug:\"职位介绍\"},{level:3,title:\"职位一：前端开发工程师(校招/社招)急\",slug:\"职位一-前端开发工程师-校招-社招-急\"},{level:3,title:\"职位二：前端开发实习生(可转正)\",slug:\"职位二-前端开发实习生-可转正\"},{level:3,title:\"职位三：web3D开发工程师（急）\",slug:\"职位三-web3d开发工程师-急\"},{level:2,title:\"其它职位（实习/校招/社招均可）\",slug:\"其它职位-实习-校招-社招均可\"},{level:3,title:\"Android开发工程师\",slug:\"android开发工程师\"},{level:3,title:\"iOS开发工程师\",slug:\"ios开发工程师\"},{level:3,title:\"后端开发工程师\",slug:\"后端开发工程师\"},{level:2,title:\"返回书籍菜单列表\",slug:\"返回书籍菜单列表\"}]},{title:\"下一步\",frontmatter:{},regularPath:\"/next.html\",relativePath:\"next.md\",key:\"v-6e4f5d44\",path:\"/next.html\",headers:[{level:3,title:\"其它平台\",slug:\"其它平台\"}]},{title:\"前言\",frontmatter:{},regularPath:\"/preface.html\",relativePath:\"preface.md\",key:\"v-c87a9a04\",path:\"/preface.html\",headers:[{level:3,title:\"缘起\",slug:\"缘起\"},{level:3,title:\"本书组织结构\",slug:\"本书组织结构\"},{level:3,title:\"本书特色\",slug:\"本书特色\"},{level:3,title:\"本书读者对象\",slug:\"本书读者对象\"},{level:3,title:\"关于随书源码\",slug:\"关于随书源码\"},{level:3,title:\"勘误\",slug:\"勘误\"},{level:3,title:\"致谢\",slug:\"致谢\"}]},{title:\"参考文献\",frontmatter:{},regularPath:\"/reference.html\",relativePath:\"reference.md\",key:\"v-e52985c4\",path:\"/reference.html\"},{title:\"Summary\",frontmatter:{},regularPath:\"/summary.html\",relativePath:\"summary.md\",key:\"v-5b7acebe\",path:\"/summary.html\",headers:[{level:2,title:\"入门篇\",slug:\"入门篇\"},{level:2,title:\"进阶篇\",slug:\"进阶篇\"},{level:2,title:\"实例篇\",slug:\"实例篇\"}]},{title:\"9.6 通用“动画切换”组件（AnimatedSwitcher）\",frontmatter:{},regularPath:\"/chapter9/animated_switcher.html\",relativePath:\"chapter9/animated_switcher.md\",key:\"v-d420ae14\",path:\"/chapter9/animated_switcher.html\",headers:[{level:2,title:\"9.6.1 AnimatedSwitcher\",slug:\"_9-6-1-animatedswitcher\"},{level:3,title:\"例子\",slug:\"例子\"},{level:3,title:\"AnimatedSwitcher实现原理\",slug:\"animatedswitcher实现原理\"},{level:2,title:\"9.6.2 AnimatedSwitcher高级用法\",slug:\"_9-6-2-animatedswitcher高级用法\"},{level:3,title:\"SlideTransitionX\",slug:\"slidetransitionx\"},{level:2,title:\"总结\",slug:\"总结\"}]},{title:\"前言\",frontmatter:{},regularPath:\"/v2/\",relativePath:\"v2/index.md\",key:\"v-4d5040b6\",path:\"/v2/\",headers:[{level:2,title:\"缘起\",slug:\"缘起\"},{level:3,title:\"本书组织结构\",slug:\"本书组织结构\"},{level:2,title:\"本书特色\",slug:\"本书特色\"},{level:2,title:\"本书读者对象\",slug:\"本书读者对象\"},{level:2,title:\"关于随书源码\",slug:\"关于随书源码\"},{level:2,title:\"致谢\",slug:\"致谢\"},{level:2,title:\"权益\",slug:\"权益\"},{level:3,title:\"勘误\",slug:\"勘误\"}]},{title:\"1.2 初识Flutter\",frontmatter:{},regularPath:\"/v2/chapter1/flutter_intro.html\",relativePath:\"v2/chapter1/flutter_intro.md\",key:\"v-7f132d5e\",path:\"/v2/chapter1/flutter_intro.html\",headers:[{level:2,title:\"1.2.1 Flutter简介\",slug:\"_1-2-1-flutter简介\"},{level:2,title:\"1.2.2 Flutter框架结构\",slug:\"_1-2-2-flutter框架结构\"},{level:3,title:\"Flutter Framework\",slug:\"flutter-framework\"},{level:3,title:\"Flutter Engine\",slug:\"flutter-engine\"},{level:3,title:\"总结\",slug:\"总结-2\"},{level:2,title:\"1.2.3 如何学习Flutter\",slug:\"_1-2-3-如何学习flutter\"},{level:3,title:\"资源\",slug:\"资源\"},{level:3,title:\"社区\",slug:\"社区\"},{level:3,title:\"总结\",slug:\"总结-3\"}]},{title:\"本章目录\",frontmatter:{},regularPath:\"/v2/chapter1/\",relativePath:\"v2/chapter1/index.md\",key:\"v-32a6c837\",path:\"/v2/chapter1/\",headers:[{level:2,title:\"本章目录\",slug:\"本章目录\"}]},{title:\"1.3 搭建Flutter开发环境\",frontmatter:{},regularPath:\"/v2/chapter1/install_flutter.html\",relativePath:\"v2/chapter1/install_flutter.md\",key:\"v-a39e7204\",path:\"/v2/chapter1/install_flutter.html\",headers:[{level:2,title:\"1.3.1 安装Flutter\",slug:\"_1-3-1-安装flutter\"},{level:3,title:\"使用镜像\",slug:\"使用镜像\"},{level:3,title:\"在Windows上搭建Flutter开发环境\",slug:\"在windows上搭建flutter开发环境\"},{level:3,title:\"在macOS上搭建Flutter开发环境\",slug:\"在macos上搭建flutter开发环境\"},{level:3,title:\"升级 Flutter\",slug:\"升级-flutter\"},{level:2,title:\"1.3.2 IDE配置与使用\",slug:\"_1-3-2-ide配置与使用\"},{level:3,title:\"Android Studio 配置与使用\",slug:\"android-studio-配置与使用\"},{level:3,title:\"VS Code的配置与使用\",slug:\"vs-code的配置与使用\"},{level:2,title:\"1.3.3 连接设备运行Flutter应用\",slug:\"_1-3-3-连接设备运行flutter应用\"},{level:3,title:\"连接Android模拟器\",slug:\"连接android模拟器\"},{level:3,title:\"连接Android真机设备\",slug:\"连接android真机设备\"},{level:3,title:\"连接iOS模拟器\",slug:\"连接ios模拟器\"},{level:3,title:\"连接iOS真机设备\",slug:\"连接ios真机设备\"},{level:2,title:\"1.3.4 常见配置问题\",slug:\"_1-3-4-常见配置问题\"},{level:3,title:\"Android Studio问题\",slug:\"android-studio问题\"}]},{title:\"1.1 移动开发技术简介\",frontmatter:{},regularPath:\"/v2/chapter1/mobile_development_intro.html\",relativePath:\"v2/chapter1/mobile_development_intro.md\",key:\"v-64933c64\",path:\"/v2/chapter1/mobile_development_intro.html\",headers:[{level:2,title:\"1.1.1 原生开发与跨平台技术\",slug:\"_1-1-1-原生开发与跨平台技术\"},{level:3,title:\"原生开发\",slug:\"原生开发\"},{level:3,title:\"跨平台技术简介\",slug:\"跨平台技术简介\"},{level:2,title:\"1.1.2 Hybrid技术简介\",slug:\"_1-1-2-hybrid技术简介\"},{level:3,title:\"H5+原生混合开发\",slug:\"h5-原生混合开发\"},{level:3,title:\"混合开发技术点\",slug:\"混合开发技术点\"},{level:3,title:\"总结\",slug:\"总结\"},{level:2,title:\"1.1.3 React Native、Weex及快应用\",slug:\"_1-1-3-react-native、weex及快应用\"},{level:3,title:\"DOM树与控件树\",slug:\"dom树与控件树\"},{level:3,title:\"响应式编程\",slug:\"响应式编程\"},{level:3,title:\"React Native\",slug:\"react-native\"},{level:3,title:\"Weex\",slug:\"weex\"},{level:3,title:\"快应用\",slug:\"快应用\"},{level:3,title:\"总结\",slug:\"总结-2\"},{level:2,title:\"1.1.4 QT Mobile\",slug:\"_1-1-4-qt-mobile\"},{level:3,title:\"QT简介\",slug:\"qt简介\"},{level:2,title:\"1.1.5 Flutter出世\",slug:\"_1-1-5-flutter出世\"},{level:2,title:\"1.1.6 小结\",slug:\"_1-1-6-小结\"}]},{title:\"10.2 组合现有组件\",frontmatter:{},regularPath:\"/v2/chapter10/combine.html\",relativePath:\"v2/chapter10/combine.md\",key:\"v-784cb4d0\",path:\"/v2/chapter10/combine.html\",headers:[{level:3,title:\"示例：自定义渐变按钮\",slug:\"示例-自定义渐变按钮\"},{level:3,title:\"总结\",slug:\"总结\"}]},{title:\"10.4 自绘组件 （CustomPaint与Canvas）\",frontmatter:{},regularPath:\"/v2/chapter10/custom_paint.html\",relativePath:\"v2/chapter10/custom_paint.md\",key:\"v-171f753e\",path:\"/v2/chapter10/custom_paint.html\",headers:[{level:3,title:\"CustomPaint\",slug:\"custompaint\"},{level:3,title:\"CustomPainter\",slug:\"custompainter\"},{level:3,title:\"画笔Paint\",slug:\"画笔paint\"},{level:3,title:\"示例：五子棋/盘\",slug:\"示例-五子棋-盘\"},{level:3,title:\"性能\",slug:\"性能\"},{level:3,title:\"总结\",slug:\"总结\"}]},{title:\"10.5 自绘实例：圆形背景渐变进度条\",frontmatter:{},regularPath:\"/v2/chapter10/gradient_circular_progress_demo.html\",relativePath:\"v2/chapter10/gradient_circular_progress_demo.md\",key:\"v-3830054e\",path:\"/v2/chapter10/gradient_circular_progress_demo.html\"},{title:\"本章目录\",frontmatter:{},regularPath:\"/v2/chapter10/\",relativePath:\"v2/chapter10/index.md\",key:\"v-eb28c6aa\",path:\"/v2/chapter10/\"},{title:\"10.1 自定义组件方法简介\",frontmatter:{},regularPath:\"/v2/chapter10/intro.html\",relativePath:\"v2/chapter10/intro.md\",key:\"v-f1d5da04\",path:\"/v2/chapter10/intro.html\",headers:[{level:3,title:\"组合其它Widget\",slug:\"组合其它widget\"},{level:3,title:\"自绘\",slug:\"自绘\"},{level:3,title:\"实现RenderObject\",slug:\"实现renderobject\"},{level:3,title:\"总结\",slug:\"总结\"}]},{title:\"10.3 组合实例：TurnBox\",frontmatter:{},regularPath:\"/v2/chapter10/turn_box.html\",relativePath:\"v2/chapter10/turn_box.md\",key:\"v-79c6bbc4\",path:\"/v2/chapter10/turn_box.html\"},{title:\"11.3 Http请求-Dio http库\",frontmatter:{},regularPath:\"/v2/chapter11/dio.html\",relativePath:\"v2/chapter11/dio.md\",key:\"v-d082d700\",path:\"/v2/chapter11/dio.html\",headers:[{level:3,title:\"引入\",slug:\"引入\"},{level:3,title:\"示例\",slug:\"示例\"},{level:3,title:\"实例\",slug:\"实例\"}]},{title:\"11.4 实例：Http分块下载\",frontmatter:{},regularPath:\"/v2/chapter11/download_with_chunks.html\",relativePath:\"v2/chapter11/download_with_chunks.md\",key:\"v-21d3a8de\",path:\"/v2/chapter11/download_with_chunks.html\",headers:[{level:3,title:\"原理\",slug:\"原理\"},{level:3,title:\"实现\",slug:\"实现\"},{level:3,title:\"思考\",slug:\"思考\"}]},{title:\"11.1 文件操作\",frontmatter:{},regularPath:\"/v2/chapter11/file_operation.html\",relativePath:\"v2/chapter11/file_operation.md\",key:\"v-1bff661e\",path:\"/v2/chapter11/file_operation.html\"},{title:\"11.2 通过HttpClient发起HTTP请求\",frontmatter:{},regularPath:\"/v2/chapter11/http.html\",relativePath:\"v2/chapter11/http.md\",key:\"v-f4fe1a44\",path:\"/v2/chapter11/http.html\"},{title:\"本章目录\",frontmatter:{},regularPath:\"/v2/chapter11/\",relativePath:\"v2/chapter11/index.md\",key:\"v-f325a8ae\",path:\"/v2/chapter11/\"},{title:\"11.7 Json转Dart Model类\",frontmatter:{},regularPath:\"/v2/chapter11/json_model.html\",relativePath:\"v2/chapter11/json_model.md\",key:\"v-179e5f5e\",path:\"/v2/chapter11/json_model.html\",headers:[{level:3,title:\"自动生成Model\",slug:\"自动生成model\"},{level:3,title:\"在项目中设置json_serializable\",slug:\"在项目中设置json-serializable\"},{level:3,title:\"以json_serializable的方式创建model类\",slug:\"以json-serializable的方式创建model类\"},{level:3,title:\"运行代码生成程序\",slug:\"运行代码生成程序\"},{level:3,title:\"自动化生成模板\",slug:\"自动化生成模板\"},{level:3,title:\"Json_model 包\",slug:\"json-model-包\"},{level:3,title:\"使用IDE插件生成model\",slug:\"使用ide插件生成model\"},{level:3,title:\"FAQ\",slug:\"faq\"}]},{title:\"11.6 使用Socket API\",frontmatter:{},regularPath:\"/v2/chapter11/socket.html\",relativePath:\"v2/chapter11/socket.md\",key:\"v-5777c3fe\",path:\"/v2/chapter11/socket.html\"},{title:\"使用WebSockets\",frontmatter:{},regularPath:\"/v2/chapter11/websocket.html\",relativePath:\"v2/chapter11/websocket.md\",key:\"v-53cfafc6\",path:\"/v2/chapter11/websocket.html\",headers:[{level:3,title:\"步骤\",slug:\"步骤\"},{level:3,title:\"1. 连接到WebSocket服务器\",slug:\"_1-连接到websocket服务器\"},{level:3,title:\"2. 监听来自服务器的消息\",slug:\"_2-监听来自服务器的消息\"},{level:3,title:\"3. 将数据发送到服务器\",slug:\"_3-将数据发送到服务器\"},{level:3,title:\"4. 关闭WebSocket连接\",slug:\"_4-关闭websocket连接\"},{level:3,title:\"完整的例子\",slug:\"完整的例子\"}]},{title:\"12.4 插件开发：Android端API实现\",frontmatter:{},regularPath:\"/v2/chapter12/android_implement.html\",relativePath:\"v2/chapter12/android_implement.md\",key:\"v-26e93cac\",path:\"/v2/chapter12/android_implement.html\",headers:[{level:3,title:\"使用Kotlin添加Android平台特定的实现\",slug:\"使用kotlin添加android平台特定的实现\"}]},{title:\"12.1 开发Package\",frontmatter:{},regularPath:\"/v2/chapter12/develop_package.html\",relativePath:\"v2/chapter12/develop_package.md\",key:\"v-bc88e5ec\",path:\"/v2/chapter12/develop_package.html\",headers:[{level:3,title:\"第一步：创建Dart包\",slug:\"第一步-创建dart包\"},{level:3,title:\"实现package\",slug:\"实现package\"},{level:3,title:\"导入包\",slug:\"导入包\"},{level:3,title:\"生成文档\",slug:\"生成文档\"},{level:3,title:\"处理包的相互依赖\",slug:\"处理包的相互依赖\"},{level:3,title:\"解决依赖冲突\",slug:\"解决依赖冲突\"},{level:3,title:\"发布Package\",slug:\"发布package\"}]},{title:\"12.3 开发Flutter插件\",frontmatter:{},regularPath:\"/v2/chapter12/develop_plugin.html\",relativePath:\"v2/chapter12/develop_plugin.md\",key:\"v-19a9ffc4\",path:\"/v2/chapter12/develop_plugin.html\",headers:[{level:3,title:\"创建一个新的应用程序项目\",slug:\"创建一个新的应用程序项目\"},{level:3,title:\"创建Flutter平台客户端\",slug:\"创建flutter平台客户端\"}]},{title:\"包与插件\",frontmatter:{},regularPath:\"/v2/chapter12/\",relativePath:\"v2/chapter12/index.md\",key:\"v-fb228ab2\",path:\"/v2/chapter12/\"},{title:\"12.5 插件开发：iOS端API实现\",frontmatter:{},regularPath:\"/v2/chapter12/ios_implement.html\",relativePath:\"v2/chapter12/ios_implement.md\",key:\"v-c86e7620\",path:\"/v2/chapter12/ios_implement.html\",headers:[{level:3,title:\"使用Swift实现iOS API\",slug:\"使用swift实现ios-api\"}]},{title:\"12.2 插件开发：平台通道简介\",frontmatter:{},regularPath:\"/v2/chapter12/platform-channel.html\",relativePath:\"v2/chapter12/platform-channel.md\",key:\"v-4868f75e\",path:\"/v2/chapter12/platform-channel.html\",headers:[{level:3,title:\"平台通道\",slug:\"平台通道\"},{level:3,title:\"平台通道数据类型支持\",slug:\"平台通道数据类型支持\"},{level:3,title:\"自定义编解码器\",slug:\"自定义编解码器\"},{level:3,title:\"如何获取平台信息\",slug:\"如何获取平台信息\"}]},{title:\"12.6 Texture和PlatformView\",frontmatter:{},regularPath:\"/v2/chapter12/texture_platformview.html\",relativePath:\"v2/chapter12/texture_platformview.md\",key:\"v-43fefcfe\",path:\"/v2/chapter12/texture_platformview.html\",headers:[{level:2,title:\"12.6.1 Texture（示例：使用摄像头）\",slug:\"_12-6-1-texture-示例-使用摄像头\"},{level:3,title:\"Texture用法\",slug:\"texture用法\"},{level:3,title:\"相机示例\",slug:\"相机示例\"},{level:2,title:\"12.6.2 PlatformView （示例：WebView）\",slug:\"_12-6-2-platformview-示例-webview\"}]},{title:\"13.4 国际化常见问题\",frontmatter:{},regularPath:\"/v2/chapter13/faq.html\",relativePath:\"v2/chapter13/faq.md\",key:\"v-6f1da438\",path:\"/v2/chapter13/faq.html\",headers:[{level:3,title:\"默认语言区域不对\",slug:\"默认语言区域不对\"},{level:3,title:\"如何对应用标题进行国际化\",slug:\"如何对应用标题进行国际化\"},{level:3,title:\"如何为英语系的国家指定同一个locale\",slug:\"如何为英语系的国家指定同一个locale\"}]},{title:\"本章目录\",frontmatter:{},regularPath:\"/v2/chapter13/\",relativePath:\"v2/chapter13/index.md\",key:\"v-7e7049a5\",path:\"/v2/chapter13/\",headers:[{level:2,title:\"本章目录\",slug:\"本章目录\"}]},{title:\"使用Intl包\",frontmatter:{},regularPath:\"/v2/chapter13/intl.html\",relativePath:\"v2/chapter13/intl.md\",key:\"v-015de4fe\",path:\"/v2/chapter13/intl.html\",headers:[{level:3,title:\"第一步：创建必要目录\",slug:\"第一步-创建必要目录\"},{level:3,title:\"第二步：实现Localizations和Delegate类\",slug:\"第二步-实现localizations和delegate类\"},{level:3,title:\"第三步：添加需要国际化的属性\",slug:\"第三步-添加需要国际化的属性\"},{level:3,title:\"第四步：生成arb文件\",slug:\"第四步-生成arb文件\"},{level:3,title:\"第五步：生成dart代码\",slug:\"第五步-生成dart代码\"},{level:3,title:\"总结\",slug:\"总结\"}]},{title:\"13.2 实现Localizations\",frontmatter:{},regularPath:\"/v2/chapter13/locallization_implement.html\",relativePath:\"v2/chapter13/locallization_implement.md\",key:\"v-bcb3c97c\",path:\"/v2/chapter13/locallization_implement.html\",headers:[{level:3,title:\"实现Localizations类\",slug:\"实现localizations类\"},{level:3,title:\"实现Delegate类\",slug:\"实现delegate类\"},{level:3,title:\"最后一步：添加多语言支持\",slug:\"最后一步-添加多语言支持\"},{level:3,title:\"总结\",slug:\"总结\"}]},{title:\"13.1 让App支持多语言\",frontmatter:{},regularPath:\"/v2/chapter13/multi_languages_support.html\",relativePath:\"v2/chapter13/multi_languages_support.md\",key:\"v-4413f806\",path:\"/v2/chapter13/multi_languages_support.html\",headers:[{level:3,title:\"支持国际化\",slug:\"支持国际化\"},{level:3,title:\"获取当前区域Locale\",slug:\"获取当前区域locale\"},{level:3,title:\"监听系统语言切换\",slug:\"监听系统语言切换\"},{level:3,title:\"Localization 组件\",slug:\"localization-组件\"},{level:3,title:\"使用打包好的LocalizationsDelegates\",slug:\"使用打包好的localizationsdelegates\"}]},{title:\"14.2 Element与BuildContext\",frontmatter:{},regularPath:\"/v2/chapter14/element_buildcontext.html\",relativePath:\"v2/chapter14/element_buildcontext.md\",key:\"v-e1d14584\",path:\"/v2/chapter14/element_buildcontext.html\",headers:[{level:2,title:\"14.2.1 Element\",slug:\"_14-2-1-element\"},{level:2,title:\"14.2.2 BuildContext\",slug:\"_14-2-2-buildcontext\"},{level:3,title:\"进阶\",slug:\"进阶\"},{level:3,title:\"总结\",slug:\"总结\"}]},{title:\"14.4 Flutter运行机制-从启动到显示\",frontmatter:{},regularPath:\"/v2/chapter14/flutter_app_startup.html\",relativePath:\"v2/chapter14/flutter_app_startup.md\",key:\"v-3bc635d6\",path:\"/v2/chapter14/flutter_app_startup.html\",headers:[{level:3,title:\"启动\",slug:\"启动\"},{level:3,title:\"渲染\",slug:\"渲染\"},{level:3,title:\"绘制\",slug:\"绘制\"},{level:3,title:\"总结\",slug:\"总结\"}]},{title:\"14.1 Flutter UI系统\",frontmatter:{},regularPath:\"/v2/chapter14/flutter_ui_system.html\",relativePath:\"v2/chapter14/flutter_ui_system.md\",key:\"v-31686cc0\",path:\"/v2/chapter14/flutter_ui_system.html\",headers:[{level:3,title:\"硬件绘图基本原理\",slug:\"硬件绘图基本原理\"},{level:3,title:\"操作系统绘制API的封装\",slug:\"操作系统绘制api的封装\"},{level:3,title:\"Flutter UI系统\",slug:\"flutter-ui系统\"}]},{title:\"14.5 图片加载原理与缓存\",frontmatter:{},regularPath:\"/v2/chapter14/image_and_cache.html\",relativePath:\"v2/chapter14/image_and_cache.md\",key:\"v-7f0d8c62\",path:\"/v2/chapter14/image_and_cache.html\",headers:[{level:2,title:\"14.5.1 ImageProvider\",slug:\"_14-5-1-imageprovider\"},{level:3,title:\"总结\",slug:\"总结\"},{level:2,title:\"14.5.2 Image组件原理\",slug:\"_14-5-2-image组件原理\"},{level:2,title:\"总结\",slug:\"总结-2\"}]},{title:\"本章目录\",frontmatter:{},regularPath:\"/v2/chapter14/\",relativePath:\"v2/chapter14/index.md\",key:\"v-7a71d8a3\",path:\"/v2/chapter14/\"},{title:\"14.3 RenderObject和RenderBox\",frontmatter:{},regularPath:\"/v2/chapter14/render_object.html\",relativePath:\"v2/chapter14/render_object.md\",key:\"v-1d0c97be\",path:\"/v2/chapter14/render_object.html\",headers:[{level:2,title:\"14.3.1 布局过程\",slug:\"_14-3-1-布局过程\"},{level:3,title:\"Constraints\",slug:\"constraints\"},{level:2,title:\"14.3.2 绘制过程\",slug:\"_14-3-2-绘制过程\"},{level:3,title:\"RepaintBoundary\",slug:\"repaintboundary\"},{level:2,title:\"14.3.3 命中测试\",slug:\"_14-3-3-命中测试\"},{level:2,title:\"14.3.4 语义化\",slug:\"_14-3-4-语义化\"},{level:2,title:\"14.3.5 总结\",slug:\"_14-3-5-总结\"}]},{title:\"15.2 Flutter APP代码结构\",frontmatter:{},regularPath:\"/v2/chapter15/code_structure.html\",relativePath:\"v2/chapter15/code_structure.md\",key:\"v-1ee4ed84\",path:\"/v2/chapter15/code_structure.html\"},{title:\"15.6 APP入口及主页\",frontmatter:{},regularPath:\"/v2/chapter15/entry.html\",relativePath:\"v2/chapter15/entry.md\",key:\"v-66801e68\",path:\"/v2/chapter15/entry.html\",headers:[{level:2,title:\"15.6.1 APP入口\",slug:\"_15-6-1-app入口\"},{level:2,title:\"15.6.2 主页\",slug:\"_15-6-2-主页\"},{level:2,title:\"15.6.3 抽屉菜单\",slug:\"_15-6-3-抽屉菜单\"}]},{title:\"15.4 全局变量及共享状态\",frontmatter:{},regularPath:\"/v2/chapter15/globals.html\",relativePath:\"v2/chapter15/globals.md\",key:\"v-ff5605a8\",path:\"/v2/chapter15/globals.html\",headers:[{level:2,title:\"15.4.1 全局变量-Global类\",slug:\"_15-4-1-全局变量-global类\"},{level:2,title:\"15.4.2 共享状态\",slug:\"_15-4-2-共享状态\"},{level:3,title:\"用户状态\",slug:\"用户状态\"},{level:3,title:\"APP主题状态\",slug:\"app主题状态\"},{level:3,title:\"APP语言状态\",slug:\"app语言状态\"}]},{title:\"15.1 Github客户端示例\",frontmatter:{},regularPath:\"/v2/chapter15/intro.html\",relativePath:\"v2/chapter15/intro.md\",key:\"v-41a7bb18\",path:\"/v2/chapter15/intro.html\"},{title:\"15.8 多语言和多主题\",frontmatter:{},regularPath:\"/v2/chapter15/language_and_theme_setting.html\",relativePath:\"v2/chapter15/language_and_theme_setting.md\",key:\"v-61f2fc7e\",path:\"/v2/chapter15/language_and_theme_setting.html\",headers:[{level:2,title:\"15.8.1 语言选择页\",slug:\"_15-8-1-语言选择页\"},{level:2,title:\"15.8.2 主题选择页\",slug:\"_15-8-2-主题选择页\"}]},{title:\"15.7 登录页\",frontmatter:{},regularPath:\"/v2/chapter15/login_page.html\",relativePath:\"v2/chapter15/login_page.md\",key:\"v-2f91d4be\",path:\"/v2/chapter15/login_page.html\"},{title:\"15.3 Model类定义\",frontmatter:{},regularPath:\"/v2/chapter15/models.html\",relativePath:\"v2/chapter15/models.md\",key:\"v-721e795e\",path:\"/v2/chapter15/models.html\",headers:[{level:3,title:\"Github账号信息\",slug:\"github账号信息\"},{level:3,title:\"API缓存策略信息\",slug:\"api缓存策略信息\"},{level:3,title:\"用户信息\",slug:\"用户信息\"},{level:3,title:\"项目信息\",slug:\"项目信息\"},{level:3,title:\"生成Dart Model类\",slug:\"生成dart-model类\"},{level:3,title:\"数据持久化\",slug:\"数据持久化\"}]},{title:\"15.5 网络请求封装\",frontmatter:{},regularPath:\"/v2/chapter15/network.html\",relativePath:\"v2/chapter15/network.md\",key:\"v-3ddc62b0\",path:\"/v2/chapter15/network.html\",headers:[{level:2,title:\"15.5.1 网络接口缓存\",slug:\"_15-5-1-网络接口缓存\"},{level:2,title:\"15.5.2 封装网络请求\",slug:\"_15-5-2-封装网络请求\"}]},{title:\"2.1 计数器应用示例\",frontmatter:{},regularPath:\"/v2/chapter2/first_flutter_app.html\",relativePath:\"v2/chapter2/first_flutter_app.md\",key:\"v-b4243004\",path:\"/v2/chapter2/first_flutter_app.html\",headers:[{level:2,title:\"2.1.1 创建Flutter应用模板\",slug:\"_2-1-1-创建flutter应用模板\"},{level:3,title:\"分析\",slug:\"分析\"},{level:2,title:\"2.1.2 首页\",slug:\"_2-1-2-首页\"},{level:3,title:\"State类\",slug:\"state类\"}]},{title:\"2.5 调试Flutter应用\",frontmatter:{},regularPath:\"/v2/chapter2/flutter_app_debug.html\",relativePath:\"v2/chapter2/flutter_app_debug.md\",key:\"v-558e1b9e\",path:\"/v2/chapter2/flutter_app_debug.html\",headers:[{level:3,title:\"Dart 分析器\",slug:\"dart-分析器\"},{level:3,title:\"Dart Observatory (语句级的单步调试和分析器)\",slug:\"dart-observatory-语句级的单步调试和分析器\"},{level:3,title:\"debugger() 声明\",slug:\"debugger-声明\"},{level:3,title:\"print、debugPrint、flutter logs\",slug:\"print、debugprint、flutter-logs\"},{level:3,title:\"调试模式断言\",slug:\"调试模式断言\"},{level:3,title:\"调试应用程序层\",slug:\"调试应用程序层\"},{level:3,title:\"语义\",slug:\"语义\"},{level:3,title:\"调度\",slug:\"调度\"},{level:3,title:\"可视化调试\",slug:\"可视化调试\"},{level:3,title:\"调试动画\",slug:\"调试动画\"},{level:3,title:\"调试性能问题\",slug:\"调试性能问题\"},{level:3,title:\"统计应用启动时间\",slug:\"统计应用启动时间\"},{level:3,title:\"跟踪Dart代码性能\",slug:\"跟踪dart代码性能\"}]},{title:\"2.4 资源管理\",frontmatter:{},regularPath:\"/v2/chapter2/flutter_assets_mgr.html\",relativePath:\"v2/chapter2/flutter_assets_mgr.md\",key:\"v-0e69043e\",path:\"/v2/chapter2/flutter_assets_mgr.html\",headers:[{level:2,title:\"指定 assets\",slug:\"指定-assets\"},{level:2,title:\"Asset 变体（variant）\",slug:\"asset-变体-variant\"},{level:2,title:\"加载 assets\",slug:\"加载-assets\"},{level:3,title:\"加载文本assets\",slug:\"加载文本assets\"},{level:3,title:\"加载图片\",slug:\"加载图片\"},{level:3,title:\"特定平台 assets\",slug:\"特定平台-assets\"}]},{title:\"2.3 包管理\",frontmatter:{},regularPath:\"/v2/chapter2/flutter_package_mgr.html\",relativePath:\"v2/chapter2/flutter_package_mgr.md\",key:\"v-33c1091e\",path:\"/v2/chapter2/flutter_package_mgr.html\",headers:[{level:2,title:\"Pub仓库\",slug:\"pub仓库\"},{level:2,title:\"示例\",slug:\"示例\"},{level:2,title:\"其它依赖方式\",slug:\"其它依赖方式\"},{level:2,title:\"总结\",slug:\"总结\"}]},{title:\"2.2 路由管理\",frontmatter:{},regularPath:\"/chapter2/flutter_router.html\",relativePath:\"chapter2/flutter_router.md\",key:\"v-18aa58be\",path:\"/chapter2/flutter_router.html\",headers:[{level:2,title:\"2.2.1 一个简单示例\",slug:\"_2-2-1-一个简单示例\"},{level:2,title:\"2.2.2 MaterialPageRoute\",slug:\"_2-2-2-materialpageroute\"},{level:2,title:\"2.2.3 Navigator\",slug:\"_2-2-3-navigator\"},{level:3,title:\"Future  push(BuildContext context, Route route)\",slug:\"future-push-buildcontext-context-route-route\"},{level:3,title:\"bool  pop(BuildContext context, [ result ])\",slug:\"bool-pop-buildcontext-context-result\"},{level:3,title:\"实例方法\",slug:\"实例方法\"},{level:2,title:\"2.2.4 路由传值\",slug:\"_2-2-4-路由传值\"},{level:3,title:\"示例\",slug:\"示例\"},{level:2,title:\"2.2.5 命名路由\",slug:\"_2-2-5-命名路由\"},{level:3,title:\"路由表\",slug:\"路由表\"},{level:3,title:\"注册路由表\",slug:\"注册路由表\"},{level:3,title:\"通过路由名打开新路由页\",slug:\"通过路由名打开新路由页\"},{level:3,title:\"命名路由参数传递\",slug:\"命名路由参数传递\"},{level:3,title:\"适配\",slug:\"适配\"},{level:2,title:\"2.2.6 路由生成钩子\",slug:\"_2-2-6-路由生成钩子\"},{level:2,title:\"2.2.7 总结\",slug:\"_2-2-7-总结\"}]},{title:\"简介\",frontmatter:{},regularPath:\"/v2/chapter2/\",relativePath:\"v2/chapter2/index.md\",key:\"v-7cd86197\",path:\"/v2/chapter2/\",headers:[{level:2,title:\"简介\",slug:\"简介\"},{level:2,title:\"本章目录\",slug:\"本章目录\"}]},{title:\"2.2 路由管理\",frontmatter:{},regularPath:\"/v2/chapter2/flutter_router.html\",relativePath:\"v2/chapter2/flutter_router.md\",key:\"v-44f99264\",path:\"/v2/chapter2/flutter_router.html\",headers:[{level:2,title:\"2.2.1 一个简单示例\",slug:\"_2-2-1-一个简单示例\"},{level:2,title:\"2.2.2 MaterialPageRoute\",slug:\"_2-2-2-materialpageroute\"},{level:2,title:\"2.2.3 Navigator\",slug:\"_2-2-3-navigator\"},{level:3,title:\"Future  push(BuildContext context, Route route)\",slug:\"future-push-buildcontext-context-route-route\"},{level:3,title:\"bool  pop(BuildContext context, [ result ])\",slug:\"bool-pop-buildcontext-context-result\"},{level:3,title:\"实例方法\",slug:\"实例方法\"},{level:2,title:\"2.2.4 路由传值\",slug:\"_2-2-4-路由传值\"},{level:3,title:\"示例\",slug:\"示例\"},{level:2,title:\"2.2.5 命名路由\",slug:\"_2-2-5-命名路由\"},{level:3,title:\"路由表\",slug:\"路由表\"},{level:3,title:\"注册路由表\",slug:\"注册路由表\"},{level:3,title:\"通过路由名打开新路由页\",slug:\"通过路由名打开新路由页\"},{level:3,title:\"命名路由参数传递\",slug:\"命名路由参数传递\"},{level:3,title:\"适配\",slug:\"适配\"},{level:2,title:\"2.2.6 路由生成钩子\",slug:\"_2-2-6-路由生成钩子\"},{level:2,title:\"2.2.7 总结\",slug:\"_2-2-7-总结\"}]},{title:\"2.6 Flutter异常捕获\",frontmatter:{},regularPath:\"/v2/chapter2/thread_model_and_error_report.html\",relativePath:\"v2/chapter2/thread_model_and_error_report.md\",key:\"v-bc70f0c4\",path:\"/v2/chapter2/thread_model_and_error_report.html\",headers:[{level:2,title:\"2.6.1 Dart单线程模型\",slug:\"_2-6-1-dart单线程模型\"},{level:2,title:\"2.6.2 Flutter异常捕获\",slug:\"_2-6-2-flutter异常捕获\"},{level:3,title:\"Flutter框架异常捕获\",slug:\"flutter框架异常捕获\"},{level:3,title:\"其它异常捕获与日志收集\",slug:\"其它异常捕获与日志收集\"},{level:3,title:\"总结\",slug:\"总结\"}]},{title:\"3.4 按钮\",frontmatter:{},regularPath:\"/v2/chapter3/buttons.html\",relativePath:\"v2/chapter3/buttons.md\",key:\"v-21677144\",path:\"/v2/chapter3/buttons.html\",headers:[{level:2,title:\"3.4.1 Material组件库中的按钮\",slug:\"_3-4-1-material组件库中的按钮\"},{level:3,title:\"RaisedButton\",slug:\"raisedbutton\"},{level:3,title:\"FlatButton\",slug:\"flatbutton\"},{level:3,title:\"OutlineButton\",slug:\"outlinebutton\"},{level:3,title:\"IconButton\",slug:\"iconbutton\"},{level:3,title:\"带图标的按钮\",slug:\"带图标的按钮\"},{level:2,title:\"3.4.2 自定义按钮外观\",slug:\"_3-4-2-自定义按钮外观\"}]},{title:\"基础Widget\",frontmatter:{},regularPath:\"/v2/chapter3/\",relativePath:\"v2/chapter3/index.md\",key:\"v-71ec0a12\",path:\"/v2/chapter3/\",headers:[{level:2,title:\"本章目录\",slug:\"本章目录\"}]},{title:\"3.5 图片及ICON\",frontmatter:{},regularPath:\"/v2/chapter3/img_and_icon.html\",relativePath:\"v2/chapter3/img_and_icon.md\",key:\"v-b54d4c78\",path:\"/v2/chapter3/img_and_icon.html\",headers:[{level:2,title:\"3.5 图片及ICON\",slug:\"_3-5-图片及icon\"},{level:2,title:\"3.5.1 图片\",slug:\"_3-5-1-图片\"},{level:3,title:\"ImageProvider\",slug:\"imageprovider\"},{level:3,title:\"Image\",slug:\"image\"},{level:3,title:\"Image缓存\",slug:\"image缓存\"},{level:2,title:\"3.5.2 ICON\",slug:\"_3-5-2-icon\"}]},{title:\"3.8 进度指示器\",frontmatter:{},regularPath:\"/v2/chapter3/progress.html\",relativePath:\"v2/chapter3/progress.md\",key:\"v-61c665a4\",path:\"/v2/chapter3/progress.html\",headers:[{level:3,title:\"LinearProgressIndicator\",slug:\"linearprogressindicator\"},{level:3,title:\"示例\",slug:\"示例\"},{level:3,title:\"CircularProgressIndicator\",slug:\"circularprogressindicator\"},{level:3,title:\"自定义尺寸\",slug:\"自定义尺寸\"},{level:3,title:\"进度色动画\",slug:\"进度色动画\"},{level:3,title:\"自定义进度指示器样式\",slug:\"自定义进度指示器样式\"}]},{title:\"3.1 Widget简介\",frontmatter:{},regularPath:\"/v2/chapter3/flutter_widget_intro.html\",relativePath:\"v2/chapter3/flutter_widget_intro.md\",key:\"v-75ff76d6\",path:\"/v2/chapter3/flutter_widget_intro.html\",headers:[{level:2,title:\"3.1.1 概念\",slug:\"_3-1-1-概念\"},{level:2,title:\"3.1.2 Widget与Element\",slug:\"_3-1-2-widget与element\"},{level:2,title:\"3.1.3 Widget主要接口\",slug:\"_3-1-3-widget主要接口\"},{level:2,title:\"3.1.4 StatelessWidget\",slug:\"_3-1-4-statelesswidget\"},{level:3,title:\"Context\",slug:\"context\"},{level:2,title:\"3.1.5 StatefulWidget\",slug:\"_3-1-5-statefulwidget\"},{level:2,title:\"3.1.6 State\",slug:\"_3-1-6-state\"},{level:3,title:\"为什么要将build方法放在State中，而不是放在StatefulWidget中？\",slug:\"为什么要将build方法放在state中-而不是放在statefulwidget中\"},{level:2,title:\"3.1.7 在Widget树中获取State对象\",slug:\"_3-1-7-在widget树中获取state对象\"},{level:3,title:\"通过Context获取\",slug:\"通过context获取\"},{level:3,title:\"通过GlobalKey\",slug:\"通过globalkey\"},{level:2,title:\"3.1.8 Flutter SDK内置组件库介绍\",slug:\"_3-1-8-flutter-sdk内置组件库介绍\"},{level:3,title:\"关于示例\",slug:\"关于示例\"},{level:3,title:\"总结\",slug:\"总结\"}]},{title:\"3.7 输入框及表单\",frontmatter:{},regularPath:\"/v2/chapter3/input_and_form.html\",relativePath:\"v2/chapter3/input_and_form.md\",key:\"v-1bfa063c\",path:\"/v2/chapter3/input_and_form.html\",headers:[{level:2,title:\"3.7.1 TextField\",slug:\"_3-7-1-textfield\"},{level:2,title:\"3.7.2 表单Form\",slug:\"_3-7-2-表单form\"}]},{title:\"3.6 单选开关和复选框\",frontmatter:{},regularPath:\"/v2/chapter3/radio_and_checkbox.html\",relativePath:\"v2/chapter3/radio_and_checkbox.md\",key:\"v-2d7d46e0\",path:\"/v2/chapter3/radio_and_checkbox.html\",headers:[{level:3,title:\"属性及外观\",slug:\"属性及外观\"},{level:3,title:\"总结\",slug:\"总结\"}]},{title:\"3.2 状态管理\",frontmatter:{},regularPath:\"/v2/chapter3/state_manage.html\",relativePath:\"v2/chapter3/state_manage.md\",key:\"v-5fbad350\",path:\"/v2/chapter3/state_manage.html\",headers:[{level:3,title:\"3.2.1 Widget管理自身状态\",slug:\"_3-2-1-widget管理自身状态\"},{level:3,title:\"3.2.2 父Widget管理子Widget的状态\",slug:\"_3-2-2-父widget管理子widget的状态\"},{level:3,title:\"3.2.3 混合状态管理\",slug:\"_3-2-3-混合状态管理\"},{level:3,title:\"3.2.4 全局状态管理\",slug:\"_3-2-4-全局状态管理\"}]},{title:\"3.3 文本及样式\",frontmatter:{},regularPath:\"/v2/chapter3/text.html\",relativePath:\"v2/chapter3/text.md\",key:\"v-0bc353b8\",path:\"/v2/chapter3/text.html\",headers:[{level:2,title:\"3.3 文本及样式\",slug:\"_3-3-文本及样式\"},{level:3,title:\"3.3.1 Text\",slug:\"_3-3-1-text\"},{level:3,title:\"3.3.2 TextStyle\",slug:\"_3-3-2-textstyle\"},{level:3,title:\"3.3.3 TextSpan\",slug:\"_3-3-3-textspan\"},{level:3,title:\"3.3.4 DefaultTextStyle\",slug:\"_3-3-4-defaulttextstyle\"},{level:3,title:\"3.3.5 字体\",slug:\"_3-3-5-字体\"}]},{title:\"本章目录\",frontmatter:{},regularPath:\"/v2/chapter4/\",relativePath:\"v2/chapter4/index.md\",key:\"v-113b9457\",path:\"/v2/chapter4/\"},{title:\"4.6 对齐与相对定位（Align）\",frontmatter:{},regularPath:\"/v2/chapter4/alignment.html\",relativePath:\"v2/chapter4/alignment.md\",key:\"v-c42c9184\",path:\"/v2/chapter4/alignment.html\",headers:[{level:2,title:\"4.6.1 Align\",slug:\"_4-6-1-align\"},{level:3,title:\"示例\",slug:\"示例\"},{level:3,title:\"Alignment\",slug:\"alignment\"},{level:3,title:\"FractionalOffset\",slug:\"fractionaloffset\"},{level:2,title:\"4.6.2 Align和Stack对比\",slug:\"_4-6-2-align和stack对比\"},{level:2,title:\"4.6.3 Center组件\",slug:\"_4-6-3-center组件\"},{level:2,title:\"总结\",slug:\"总结\"}]},{title:\"4.3 弹性布局（Flex）\",frontmatter:{},regularPath:\"/v2/chapter4/flex.html\",relativePath:\"v2/chapter4/flex.md\",key:\"v-da668ee4\",path:\"/v2/chapter4/flex.html\",headers:[{level:3,title:\"Flex\",slug:\"flex\"},{level:3,title:\"Expanded\",slug:\"expanded\"},{level:3,title:\"小结\",slug:\"小结\"}]},{title:\"4.1 布局类组件简介\",frontmatter:{},regularPath:\"/v2/chapter4/intro.html\",relativePath:\"v2/chapter4/intro.md\",key:\"v-2ef33ac4\",path:\"/v2/chapter4/intro.html\"},{title:\"4.2 线性布局（Row和Column）\",frontmatter:{},regularPath:\"/v2/chapter4/row_and_column.html\",relativePath:\"v2/chapter4/row_and_column.md\",key:\"v-5c09bcfa\",path:\"/v2/chapter4/row_and_column.html\",headers:[{level:3,title:\"主轴和纵轴\",slug:\"主轴和纵轴\"},{level:3,title:\"Row\",slug:\"row\"},{level:3,title:\"示例\",slug:\"示例\"},{level:3,title:\"Column\",slug:\"column\"},{level:3,title:\"特殊情况\",slug:\"特殊情况\"}]},{title:\"4.5 层叠布局 Stack、Positioned\",frontmatter:{},regularPath:\"/v2/chapter4/stack.html\",relativePath:\"v2/chapter4/stack.md\",key:\"v-4cab2dc4\",path:\"/v2/chapter4/stack.html\",headers:[{level:3,title:\"Stack\",slug:\"stack\"},{level:3,title:\"Positioned\",slug:\"positioned\"},{level:3,title:\"示例\",slug:\"示例\"}]},{title:\"4.4 流式布局\",frontmatter:{},regularPath:\"/v2/chapter4/wrap_and_flow.html\",relativePath:\"v2/chapter4/wrap_and_flow.md\",key:\"v-c47fd984\",path:\"/v2/chapter4/wrap_and_flow.html\",headers:[{level:2,title:\"4.4.1 Wrap\",slug:\"_4-4-1-wrap\"},{level:2,title:\"4.4.2 Flow\",slug:\"_4-4-2-flow\"}]},{title:\"5.7 剪裁（Clip）\",frontmatter:{},regularPath:\"/v2/chapter5/clip.html\",relativePath:\"v2/chapter5/clip.md\",key:\"v-bf6de9bc\",path:\"/v2/chapter5/clip.html\",headers:[{level:3,title:\"CustomClipper\",slug:\"customclipper\"}]},{title:\"5.2 尺寸限制类容器\",frontmatter:{},regularPath:\"/v2/chapter5/constrainedbox_and_sizebox.html\",relativePath:\"v2/chapter5/constrainedbox_and_sizebox.md\",key:\"v-ccad755c\",path:\"/v2/chapter5/constrainedbox_and_sizebox.html\",headers:[{level:2,title:\"5.2.1 ConstrainedBox\",slug:\"_5-2-1-constrainedbox\"},{level:2,title:\"5.2.2 SizedBox\",slug:\"_5-2-2-sizedbox\"},{level:2,title:\"5.2.3 多重限制\",slug:\"_5-2-3-多重限制\"},{level:2,title:\"5.2.4 UnconstrainedBox\",slug:\"_5-2-4-unconstrainedbox\"},{level:2,title:\"5.2.4 其它尺寸限制类容器\",slug:\"_5-2-4-其它尺寸限制类容器\"}]},{title:\"5.5 Container\",frontmatter:{},regularPath:\"/v2/chapter5/container.html\",relativePath:\"v2/chapter5/container.md\",key:\"v-a9cc6b44\",path:\"/v2/chapter5/container.html\",headers:[{level:3,title:\"实例\",slug:\"实例\"},{level:3,title:\"Padding和Margin\",slug:\"padding和margin\"}]},{title:\"5.3 装饰容器DecoratedBox\",frontmatter:{},regularPath:\"/v2/chapter5/decoratedbox.html\",relativePath:\"v2/chapter5/decoratedbox.md\",key:\"v-d752c44c\",path:\"/v2/chapter5/decoratedbox.html\"},{title:\"容器类Widget\",frontmatter:{},regularPath:\"/v2/chapter5/\",relativePath:\"v2/chapter5/index.md\",key:\"v-5b6d2db7\",path:\"/v2/chapter5/\",headers:[{level:2,title:\"本章目录\",slug:\"本章目录\"}]},{title:\"5.1 填充（Padding）\",frontmatter:{},regularPath:\"/v2/chapter5/padding.html\",relativePath:\"v2/chapter5/padding.md\",key:\"v-b98538c4\",path:\"/v2/chapter5/padding.html\",headers:[{level:2,title:\"5.1 填充（Padding）\",slug:\"_5-1-填充-padding\"},{level:3,title:\"EdgeInsets\",slug:\"edgeinsets\"},{level:3,title:\"示例\",slug:\"示例\"}]},{title:\"5.6 Scaffold、TabBar、底部导航\",frontmatter:{},regularPath:\"/v2/chapter5/material_scaffold.html\",relativePath:\"v2/chapter5/material_scaffold.md\",key:\"v-5b676bbe\",path:\"/v2/chapter5/material_scaffold.html\",headers:[{level:2,title:\"5.6.1 Scaffold\",slug:\"_5-6-1-scaffold\"},{level:3,title:\"示例\",slug:\"示例\"},{level:2,title:\"5.6.2 AppBar\",slug:\"_5-6-2-appbar\"},{level:3,title:\"TabBar\",slug:\"tabbar\"},{level:3,title:\"TabBarView\",slug:\"tabbarview\"},{level:2,title:\"5.6.3 抽屉菜单Drawer\",slug:\"_5-6-3-抽屉菜单drawer\"},{level:2,title:\"5.6.4 FloatingActionButton\",slug:\"_5-6-4-floatingactionbutton\"},{level:2,title:\"5.6.5  底部Tab导航栏\",slug:\"_5-6-5-底部tab导航栏\"}]},{title:\"5.4 变换（Transform）\",frontmatter:{},regularPath:\"/v2/chapter5/transform.html\",relativePath:\"v2/chapter5/transform.md\",key:\"v-5c06b7fe\",path:\"/v2/chapter5/transform.html\",headers:[{level:3,title:\"平移\",slug:\"平移\"},{level:3,title:\"旋转\",slug:\"旋转\"},{level:3,title:\"缩放\",slug:\"缩放\"},{level:3,title:\"注意\",slug:\"注意\"},{level:3,title:\"RotatedBox\",slug:\"rotatedbox\"}]},{title:\"6.5 CustomScrollView\",frontmatter:{},regularPath:\"/v2/chapter6/custom_scrollview.html\",relativePath:\"v2/chapter6/custom_scrollview.md\",key:\"v-e7c1d544\",path:\"/v2/chapter6/custom_scrollview.html\",headers:[{level:3,title:\"可滚动组件的Sliver版\",slug:\"可滚动组件的sliver版\"},{level:3,title:\"示例\",slug:\"示例\"}]},{title:\"本章目录\",frontmatter:{},regularPath:\"/v2/chapter6/\",relativePath:\"v2/chapter6/index.md\",key:\"v-b4c271d2\",path:\"/v2/chapter6/\"},{title:\"6.4 GridView\",frontmatter:{},regularPath:\"/v2/chapter6/gridview.html\",relativePath:\"v2/chapter6/gridview.md\",key:\"v-4517d02e\",path:\"/v2/chapter6/gridview.html\",headers:[{level:3,title:\"SliverGridDelegateWithFixedCrossAxisCount\",slug:\"slivergriddelegatewithfixedcrossaxiscount\"},{level:3,title:\"SliverGridDelegateWithMaxCrossAxisExtent\",slug:\"slivergriddelegatewithmaxcrossaxisextent\"},{level:3,title:\"GridView.builder\",slug:\"gridview-builder\"},{level:3,title:\"更多\",slug:\"更多\"}]},{title:\"6.1 可滚动组件简介\",frontmatter:{},regularPath:\"/v2/chapter6/intro.html\",relativePath:\"v2/chapter6/intro.md\",key:\"v-18cdb544\",path:\"/v2/chapter6/intro.html\",headers:[{level:3,title:\"Scrollbar\",slug:\"scrollbar\"},{level:3,title:\"ViewPort视口\",slug:\"viewport视口\"},{level:3,title:\"基于Sliver的延迟构建\",slug:\"基于sliver的延迟构建\"},{level:3,title:\"主轴和纵轴\",slug:\"主轴和纵轴\"}]},{title:\"6.3 ListView\",frontmatter:{},regularPath:\"/v2/chapter6/listview.html\",relativePath:\"v2/chapter6/listview.md\",key:\"v-3f1fd904\",path:\"/v2/chapter6/listview.html\",headers:[{level:3,title:\"默认构造函数\",slug:\"默认构造函数\"},{level:3,title:\"ListView.builder\",slug:\"listview-builder\"},{level:3,title:\"ListView.separated\",slug:\"listview-separated\"},{level:3,title:\"实例：无限加载列表\",slug:\"实例-无限加载列表\"},{level:3,title:\"添加固定列表头\",slug:\"添加固定列表头\"},{level:3,title:\"总结\",slug:\"总结\"}]},{title:\"6.6 滚动监听及控制\",frontmatter:{},regularPath:\"/v2/chapter6/scroll_controller.html\",relativePath:\"v2/chapter6/scroll_controller.md\",key:\"v-1f7008c4\",path:\"/v2/chapter6/scroll_controller.html\",headers:[{level:2,title:\"6.6.1 ScrollController\",slug:\"_6-6-1-scrollcontroller\"},{level:3,title:\"示例\",slug:\"示例\"},{level:3,title:\"滚动位置恢复\",slug:\"滚动位置恢复\"},{level:3,title:\"ScrollPosition\",slug:\"scrollposition\"},{level:3,title:\"ScrollController控制原理\",slug:\"scrollcontroller控制原理\"},{level:2,title:\"6.6.2 滚动监听\",slug:\"_6-6-2-滚动监听\"},{level:3,title:\"示例\",slug:\"示例-2\"}]},{title:\"6.2 SingleChildScrollView\",frontmatter:{},regularPath:\"/v2/chapter6/single_child_scrollview.html\",relativePath:\"v2/chapter6/single_child_scrollview.md\",key:\"v-67fac5c4\",path:\"/v2/chapter6/single_child_scrollview.html\",headers:[{level:3,title:\"示例\",slug:\"示例\"}]},{title:\"7.5 异步UI更新（FutureBuilder、StreamBuilder）\",frontmatter:{},regularPath:\"/v2/chapter7/futurebuilder_and_streambuilder.html\",relativePath:\"v2/chapter7/futurebuilder_and_streambuilder.md\",key:\"v-1cc8923e\",path:\"/v2/chapter7/futurebuilder_and_streambuilder.html\",headers:[{level:2,title:\"7.5.1 FutureBuilder\",slug:\"_7-5-1-futurebuilder\"},{level:3,title:\"示例\",slug:\"示例\"},{level:2,title:\"7.5.2 StreamBuilder\",slug:\"_7-5-2-streambuilder\"},{level:3,title:\"示例\",slug:\"示例-2\"}]},{title:\"功能型Widget简介\",frontmatter:{},regularPath:\"/v2/chapter7/\",relativePath:\"v2/chapter7/index.md\",key:\"v-205f3f12\",path:\"/v2/chapter7/\",headers:[{level:2,title:\"功能型Widget简介\",slug:\"功能型widget简介\"},{level:2,title:\"本章目录\",slug:\"本章目录\"}]},{title:\"7.2 数据共享（InheritedWidget）\",frontmatter:{},regularPath:\"/v2/chapter7/inherited_widget.html\",relativePath:\"v2/chapter7/inherited_widget.md\",key:\"v-63db8374\",path:\"/v2/chapter7/inherited_widget.html\",headers:[{level:3,title:\"didChangeDependencies\",slug:\"didchangedependencies\"},{level:3,title:\"深入了解InheritedWidget\",slug:\"深入了解inheritedwidget\"}]},{title:\"7.3 跨组件状态共享（Provider）\",frontmatter:{},regularPath:\"/v2/chapter7/provider.html\",relativePath:\"v2/chapter7/provider.md\",key:\"v-339f6524\",path:\"/v2/chapter7/provider.html\",headers:[{level:2,title:\"Provider\",slug:\"provider\"},{level:3,title:\"购物车示例\",slug:\"购物车示例\"},{level:3,title:\"优化\",slug:\"优化\"},{level:3,title:\"其它状态管理包\",slug:\"其它状态管理包\"},{level:3,title:\"总结\",slug:\"总结\"}]},{title:\"7.4 颜色和主题\",frontmatter:{},regularPath:\"/v2/chapter7/theme.html\",relativePath:\"v2/chapter7/theme.md\",key:\"v-581622c4\",path:\"/v2/chapter7/theme.html\",headers:[{level:2,title:\"7.4.1 颜色\",slug:\"_7-4-1-颜色\"},{level:3,title:\"如何将颜色字符串转成Color对象\",slug:\"如何将颜色字符串转成color对象\"},{level:3,title:\"颜色亮度\",slug:\"颜色亮度\"},{level:3,title:\"MaterialColor\",slug:\"materialcolor\"},{level:2,title:\"7.4.2 Theme\",slug:\"_7-4-2-theme\"},{level:3,title:\"ThemeData\",slug:\"themedata\"},{level:3,title:\"示例\",slug:\"示例\"}]},{title:\"7.1 导航返回拦截（WillPopScope）\",frontmatter:{},regularPath:\"/v2/chapter7/willpopscope.html\",relativePath:\"v2/chapter7/willpopscope.md\",key:\"v-f1d8fec8\",path:\"/v2/chapter7/willpopscope.html\",headers:[{level:3,title:\"示例\",slug:\"示例\"}]},{title:\"8.3 事件总线\",frontmatter:{},regularPath:\"/v2/chapter8/eventbus.html\",relativePath:\"v2/chapter8/eventbus.md\",key:\"v-541c787c\",path:\"/v2/chapter8/eventbus.html\"},{title:\"8.2 手势识别\",frontmatter:{},regularPath:\"/v2/chapter8/gesture.html\",relativePath:\"v2/chapter8/gesture.md\",key:\"v-8c0e8c04\",path:\"/v2/chapter8/gesture.html\",headers:[{level:2,title:\"8.2.1 GestureDetector\",slug:\"_8-2-1-gesturedetector\"},{level:3,title:\"点击、双击、长按\",slug:\"点击、双击、长按\"},{level:3,title:\"拖动、滑动\",slug:\"拖动、滑动\"},{level:3,title:\"单一方向拖动\",slug:\"单一方向拖动\"},{level:3,title:\"缩放\",slug:\"缩放\"},{level:2,title:\"8.2.2 GestureRecognizer\",slug:\"_8-2-2-gesturerecognizer\"},{level:2,title:\"8.2.3 手势竞争与冲突\",slug:\"_8-2-3-手势竞争与冲突\"},{level:3,title:\"竞争\",slug:\"竞争\"},{level:3,title:\"示例\",slug:\"示例-2\"},{level:3,title:\"手势冲突\",slug:\"手势冲突\"}]},{title:\"事件处理与通知\",frontmatter:{},regularPath:\"/v2/chapter8/\",relativePath:\"v2/chapter8/index.md\",key:\"v-3a01f9d7\",path:\"/v2/chapter8/\",headers:[{level:2,title:\"事件处理与通知\",slug:\"事件处理与通知\"},{level:2,title:\"本章目录\",slug:\"本章目录\"}]},{title:\"8.1 原始指针事件处理\",frontmatter:{},regularPath:\"/v2/chapter8/listener.html\",relativePath:\"v2/chapter8/listener.md\",key:\"v-d28cf440\",path:\"/v2/chapter8/listener.html\",headers:[{level:3,title:\"忽略PointerEvent\",slug:\"忽略pointerevent\"}]},{title:\"8.4 Notification\",frontmatter:{},regularPath:\"/v2/chapter8/notification.html\",relativePath:\"v2/chapter8/notification.md\",key:\"v-62746032\",path:\"/v2/chapter8/notification.html\",headers:[{level:3,title:\"阻止冒泡\",slug:\"阻止冒泡\"},{level:3,title:\"通知冒泡原理\",slug:\"通知冒泡原理\"},{level:3,title:\"总结\",slug:\"总结\"}]},{title:\"9.6 通用“动画切换”组件（AnimatedSwitcher）\",frontmatter:{},regularPath:\"/v2/chapter9/animated_switcher.html\",relativePath:\"v2/chapter9/animated_switcher.md\",key:\"v-8bd98544\",path:\"/v2/chapter9/animated_switcher.html\",headers:[{level:2,title:\"9.6.1 AnimatedSwitcher\",slug:\"_9-6-1-animatedswitcher\"},{level:3,title:\"例子\",slug:\"例子\"},{level:3,title:\"AnimatedSwitcher实现原理\",slug:\"animatedswitcher实现原理\"},{level:2,title:\"9.6.2 AnimatedSwitcher高级用法\",slug:\"_9-6-2-animatedswitcher高级用法\"},{level:3,title:\"SlideTransitionX\",slug:\"slidetransitionx\"},{level:2,title:\"总结\",slug:\"总结\"}]},{title:\"9.7 动画过渡组件\",frontmatter:{},regularPath:\"/v2/chapter9/animated_widgets.html\",relativePath:\"v2/chapter9/animated_widgets.md\",key:\"v-72651364\",path:\"/v2/chapter9/animated_widgets.html\",headers:[{level:2,title:\"9.7.1 自定义动画过渡组件\",slug:\"_9-7-1-自定义动画过渡组件\"},{level:3,title:\"动画过渡组件的反向动画\",slug:\"动画过渡组件的反向动画\"},{level:2,title:\"9.7.2 Flutter预置的动画过渡组件\",slug:\"_9-7-2-flutter预置的动画过渡组件\"}]},{title:\"9.2 动画基本结构及状态监听\",frontmatter:{},regularPath:\"/v2/chapter9/animation_structure.html\",relativePath:\"v2/chapter9/animation_structure.md\",key:\"v-8de95804\",path:\"/v2/chapter9/animation_structure.html\",headers:[{level:2,title:\"9.2.1 动画基本结构\",slug:\"_9-2-1-动画基本结构\"},{level:3,title:\"基础版本\",slug:\"基础版本\"},{level:3,title:\"使用AnimatedWidget简化\",slug:\"使用animatedwidget简化\"},{level:3,title:\"用AnimatedBuilder重构\",slug:\"用animatedbuilder重构\"},{level:2,title:\"9.2.2 动画状态监听\",slug:\"_9-2-2-动画状态监听\"}]},{title:\"9.4 Hero动画\",frontmatter:{},regularPath:\"/v2/chapter9/hero.html\",relativePath:\"v2/chapter9/hero.md\",key:\"v-6ae8ab56\",path:\"/v2/chapter9/hero.html\"},{title:\"简介\",frontmatter:{},regularPath:\"/v2/chapter9/\",relativePath:\"v2/chapter9/index.md\",key:\"v-f798d992\",path:\"/v2/chapter9/\",headers:[{level:2,title:\"简介\",slug:\"简介\"},{level:2,title:\"本章目录\",slug:\"本章目录\"}]},{title:\"9.1 Flutter动画简介\",frontmatter:{},regularPath:\"/v2/chapter9/intro.html\",relativePath:\"v2/chapter9/intro.md\",key:\"v-f7956d04\",path:\"/v2/chapter9/intro.html\",headers:[{level:3,title:\"Flutter中动画抽象\",slug:\"flutter中动画抽象\"},{level:3,title:\"Animation\",slug:\"animation\"},{level:3,title:\"Curve\",slug:\"curve\"},{level:3,title:\"AnimationController\",slug:\"animationcontroller\"},{level:3,title:\"Tween\",slug:\"tween\"}]},{title:\"9.3 自定义路由切换动画\",frontmatter:{},regularPath:\"/v2/chapter9/route_transition.html\",relativePath:\"v2/chapter9/route_transition.md\",key:\"v-d4f6e118\",path:\"/v2/chapter9/route_transition.html\"},{title:\"9.5 交织动画\",frontmatter:{},regularPath:\"/v2/chapter9/stagger_animation.html\",relativePath:\"v2/chapter9/stagger_animation.md\",key:\"v-b4184284\",path:\"/v2/chapter9/stagger_animation.html\",headers:[{level:3,title:\"示例\",slug:\"示例\"}]},{title:\"前言\",frontmatter:{},regularPath:\"/v2/imgs/\",relativePath:\"v2/imgs/index.md\",key:\"v-fde52892\",path:\"/v2/imgs/\",headers:[{level:2,title:\"缘起\",slug:\"缘起\"},{level:3,title:\"本书组织结构\",slug:\"本书组织结构\"},{level:2,title:\"本书特色\",slug:\"本书特色\"},{level:2,title:\"本书读者对象\",slug:\"本书读者对象\"},{level:2,title:\"关于随书源码\",slug:\"关于随书源码\"},{level:2,title:\"致谢\",slug:\"致谢\"},{level:2,title:\"权益\",slug:\"权益\"},{level:3,title:\"勘误\",slug:\"勘误\"}]},{title:\"前言\",frontmatter:{},regularPath:\"/v2/intro.html\",relativePath:\"v2/intro.md\",key:\"v-5ade8610\",path:\"/v2/intro.html\",headers:[{level:2,title:\"缘起\",slug:\"缘起\"},{level:3,title:\"本书组织结构\",slug:\"本书组织结构\"},{level:2,title:\"本书特色\",slug:\"本书特色\"},{level:2,title:\"本书读者对象\",slug:\"本书读者对象\"},{level:2,title:\"关于随书源码\",slug:\"关于随书源码\"},{level:2,title:\"致谢\",slug:\"致谢\"},{level:2,title:\"权益\",slug:\"权益\"},{level:3,title:\"勘误\",slug:\"勘误\"}]},{title:\"字节跳动-内推\",frontmatter:{sidebar:\"auto\"},regularPath:\"/v2/join_us.html\",relativePath:\"v2/join_us.md\",key:\"v-0a03b62c\",path:\"/v2/join_us.html\",headers:[{level:2,title:\"前端团队介绍\",slug:\"前端团队介绍\"},{level:3,title:\"业务线介绍\",slug:\"业务线介绍\"},{level:3,title:\"团队福利\",slug:\"团队福利\"},{level:2,title:\"职位介绍\",slug:\"职位介绍\"},{level:3,title:\"职位—：前端开发工程师（校招/社招）急\",slug:\"职位-前端开发工程师-校招-社招-急\"},{level:3,title:\"职位二：校招前端开发实习生\",slug:\"职位二-校招前端开发实习生\"},{level:3,title:\"职位三：web3D开发工程师（急）\",slug:\"职位三-web3d开发工程师-急\"},{level:2,title:\"其它职位（实习/校招/社招均可）\",slug:\"其它职位-实习-校招-社招均可\"},{level:3,title:\"Android开发工程师\",slug:\"android开发工程师\"},{level:3,title:\"iOS开发工程师\",slug:\"ios开发工程师\"},{level:3,title:\"后端开发工程师\",slug:\"后端开发工程师\"},{level:2,title:\"返回书籍菜单列表\",slug:\"返回书籍菜单列表\"}]},{title:\"下一步\",frontmatter:{},regularPath:\"/v2/next.html\",relativePath:\"v2/next.md\",key:\"v-7c80aebe\",path:\"/v2/next.html\",headers:[{level:3,title:\"其它平台\",slug:\"其它平台\"}]},{title:\"前言\",frontmatter:{},regularPath:\"/v2/preface.html\",relativePath:\"v2/preface.md\",key:\"v-49c18490\",path:\"/v2/preface.html\",headers:[{level:3,title:\"缘起\",slug:\"缘起\"},{level:3,title:\"本书组织结构\",slug:\"本书组织结构\"},{level:3,title:\"本书特色\",slug:\"本书特色\"},{level:3,title:\"本书读者对象\",slug:\"本书读者对象\"},{level:3,title:\"关于随书源码\",slug:\"关于随书源码\"},{level:3,title:\"勘误\",slug:\"勘误\"},{level:3,title:\"致谢\",slug:\"致谢\"}]},{title:\"参考文献\",frontmatter:{},regularPath:\"/v2/reference.html\",relativePath:\"v2/reference.md\",key:\"v-77333b7a\",path:\"/v2/reference.html\"},{title:\"Summary\",frontmatter:{},regularPath:\"/v2/summary.html\",relativePath:\"v2/summary.md\",key:\"v-5a6d2e44\",path:\"/v2/summary.html\",headers:[{level:2,title:\"入门篇\",slug:\"入门篇\"},{level:2,title:\"进阶篇\",slug:\"进阶篇\"},{level:2,title:\"实例篇\",slug:\"实例篇\"}]},{title:\"1.4 Dart语言简介\",frontmatter:{},regularPath:\"/v2/chapter1/dart.html\",relativePath:\"v2/chapter1/dart.md\",key:\"v-55ace208\",path:\"/v2/chapter1/dart.html\",headers:[{level:2,title:\"1.4.1 变量声明\",slug:\"_1-4-1-变量声明\"},{level:2,title:\"1.4.2 函数\",slug:\"_1-4-2-函数\"},{level:2,title:\"1.4.3 异步支持\",slug:\"_1-4-3-异步支持\"},{level:3,title:\"Future\",slug:\"future\"},{level:3,title:\"Async/await\",slug:\"async-await\"},{level:2,title:\"1.4.4 Stream\",slug:\"_1-4-4-stream\"},{level:2,title:\"1.4.5 Dart和Java及JavaScript对比\",slug:\"_1-4-5-dart和java及javascript对比\"},{level:3,title:\"Dart vs Java\",slug:\"dart-vs-java\"},{level:3,title:\"Dart vs JavaScript\",slug:\"dart-vs-javascript\"}]},{title:\"7.6 对话框详解\",frontmatter:{},regularPath:\"/v2/chapter7/dailog.html\",relativePath:\"v2/chapter7/dailog.md\",key:\"v-03050454\",path:\"/v2/chapter7/dailog.html\",headers:[{level:2,title:\"7.6.1 使用对话框\",slug:\"_7-6-1-使用对话框\"},{level:3,title:\"AlertDialog\",slug:\"alertdialog\"},{level:3,title:\"SimpleDialog\",slug:\"simpledialog\"},{level:3,title:\"Dialog\",slug:\"dialog\"},{level:2,title:\"7.6.2 对话框打开动画及遮罩\",slug:\"_7-6-2-对话框打开动画及遮罩\"},{level:2,title:\"7.6.3 对话框实现原理\",slug:\"_7-6-3-对话框实现原理\"},{level:2,title:\"7.6.4 对话框状态管理\",slug:\"_7-6-4-对话框状态管理\"},{level:3,title:\"单独抽离出StatefulWidget\",slug:\"单独抽离出statefulwidget\"},{level:3,title:\"使用StatefulBuilder方法\",slug:\"使用statefulbuilder方法\"},{level:3,title:\"精妙的解法\",slug:\"精妙的解法\"},{level:2,title:\"7.6.5 其它类型的对话框\",slug:\"_7-6-5-其它类型的对话框\"},{level:3,title:\"底部菜单列表\",slug:\"底部菜单列表\"},{level:3,title:\"Loading框\",slug:\"loading框\"},{level:3,title:\"日历选择\",slug:\"日历选择\"}]}],themeConfig:{logo:\"/logo.png\",nav:[{text:\"和作者做同事\",link:\"/join_us\"},{text:\"Flutter中国\",link:\"https://github.com/flutterchina\"},{text:\"Github\",link:\"https://github.com/flutterchina/flutter-in-action\"},{text:\"实体书\",link:\"https://item.jd.com/12816296.html\"}],sidebar:[{title:\"首页\",path:\"/\"},{title:\"第一章：起步\",path:\"/chapter1/index\",collapsable:!1,children:[\"/chapter1/mobile_development_intro\",\"/chapter1/flutter_intro\",\"/chapter1/install_flutter\",\"/chapter1/dart\"]},{title:\"第二章：第一个Flutter应用\",path:\"/chapter2/index\",collapsable:!1,children:[\"/chapter2/first_flutter_app\",\"/chapter2/flutter_router\",\"/chapter2/flutter_package_mgr\",\"/chapter2/flutter_assets_mgr\",\"/chapter2/flutter_app_debug\",\"/chapter2/thread_model_and_error_report\"]},{title:\"第三章：基础组件\",path:\"/chapter3/index\",collapsable:!1,children:[\"/chapter3/flutter_widget_intro\",\"/chapter3/state_manage\",\"/chapter3/text\",\"/chapter3/buttons\",\"/chapter3/img_and_icon\",\"/chapter3/radio_and_checkbox\",\"/chapter3/input_and_form\",\"/chapter3/progress\"]},{title:\"第四章：布局类组件\",path:\"chapter4/index.md\",collapsable:!1,children:[\"/chapter4/intro\",\"/chapter4/row_and_column\",\"/chapter4/flex\",\"/chapter4/wrap_and_flow\",\"/chapter4/stack\",\"/chapter4/alignment\"]},{title:\"第五章：容器类组件\",path:\"/chapter5/index\",collapsable:!1,children:[\"/chapter5/padding\",\"/chapter5/constrainedbox_and_sizebox\",\"/chapter5/decoratedbox\",\"/chapter5/transform\",\"/chapter5/container\",\"/chapter5/material_scaffold\",\"/chapter5/clip\"]},{title:\"第六章：可滚动组件\",path:\"/chapter6/index\",collapsable:!1,children:[\"/chapter6/intro\",\"/chapter6/single_child_scrollview\",\"/chapter6/listview\",\"/chapter6/gridview\",\"/chapter6/custom_scrollview\",\"/chapter6/scroll_controller\"]},{title:\"第七章：功能型组件\",path:\"/chapter7/index\",collapsable:!1,children:[\"/chapter7/willpopscope\",\"/chapter7/inherited_widget\",\"/chapter7/provider\",\"/chapter7/theme\",\"/chapter7/futurebuilder_and_streambuilder\",\"/chapter7/dailog\"]},{title:\"第八章：事件处理与通知\",path:\"/chapter8/index\",collapsable:!1,children:[\"/chapter8/listener\",\"/chapter8/gesture\",\"/chapter8/eventbus\",\"/chapter8/notification\"]},{title:\"第九章：动画\",path:\"/chapter9/index\",collapsable:!1,children:[\"/chapter9/intro\",\"/chapter9/animation_structure\",\"/chapter9/route_transition\",\"/chapter9/hero\",\"/chapter9/stagger_animation\",\"/chapter9/animated_switcher\",\"/chapter9/animated_widgets\"]},{title:\"第十章：自定义组件\",path:\"/chapter10/index\",collapsable:!1,children:[\"/chapter10/intro\",\"/chapter10/combine\",\"/chapter10/turn_box\",\"/chapter10/custom_paint\",\"/chapter10/gradient_circular_progress_demo\"]},{title:\"第十一章：文件操作与网络请求\",path:\"/chapter11/index\",collapsable:!1,children:[\"/chapter11/file_operation\",\"/chapter11/http\",\"/chapter11/dio\",\"/chapter11/download_with_chunks\",\"/chapter11/websocket\",\"/chapter11/socket\",\"/chapter11/json_model\"]},{title:\"第十二章：包与插件\",path:\"/chapter12/index\",collapsable:!1,children:[\"/chapter12/develop_package\",\"/chapter12/platform-channel\",\"/chapter12/develop_plugin\",\"/chapter12/android_implement\",\"/chapter12/ios_implement\",\"/chapter12/texture_platformview\"]},{title:\"第十三章：国际化\",path:\"/chapter13/index\",collapsable:!1,children:[\"/chapter13/multi_languages_support\",\"/chapter13/locallization_implement\",\"/chapter13/intl\",\"/chapter13/faq\"]},{title:\"第十四章：Flutter核心原理\",path:\"/chapter14/index\",collapsable:!1,children:[\"/chapter14/flutter_ui_system\",\"/chapter14/element_buildcontext\",\"/chapter14/render_object\",\"/chapter14/flutter_app_startup\",\"/chapter14/image_and_cache\"]},{title:\"第十五章：一个完整的Flutter应用\",collapsable:!1,children:[\"/chapter15/intro\",\"/chapter15/code_structure\",\"/chapter15/models\",\"/chapter15/globals\",\"/chapter15/network\",\"/chapter15/entry\",\"/chapter15/login_page\",\"/chapter15/language_and_theme_setting\"]}]}};n(298);Ta.component(\"CodeBlock\",(function(){return Promise.all([n.e(0),n.e(59)]).then(n.bind(null,719))})),Ta.component(\"Badge\",(function(){return Promise.all([n.e(0),n.e(58)]).then(n.bind(null,943))})),Ta.component(\"CodeGroup\",(function(){return Promise.all([n.e(0),n.e(60)]).then(n.bind(null,720))}));n(299);var fl=[{},function(t){t.Vue.mixin({computed:{$dataBlock:function(){return this.$options.__data__block__}}})},{},{}],pl=[];function vl(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}n(300);function dl(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,\"value\"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}function ml(t,e,n){return e&&dl(t.prototype,e),n&&dl(t,n),t}n(164);n(157);function gl(t,e){return(gl=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t})(t,e)}n(158);function bl(t){return(bl=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)})(t)}var yl=n(163),_l=n.n(yl);function xl(t,e){return!e||\"object\"!==_l()(e)&&\"function\"!=typeof e?function(t){if(void 0===t)throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\");return t}(t):e}function wl(t){var e=function(){if(\"undefined\"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if(\"function\"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(t){return!1}}();return function(){var n,r=bl(t);if(e){var a=bl(this).constructor;n=Reflect.construct(r,arguments,a)}else n=r.apply(this,arguments);return xl(this,n)}}var Pl=function(t){!function(t,e){if(\"function\"!=typeof e&&null!==e)throw new TypeError(\"Super expression must either be null or a function\");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),e&&gl(t,e)}(n,t);var e=wl(n);function n(){return vl(this,n),e.apply(this,arguments)}return n}(function(){function t(){vl(this,t),this.store=new Ta({data:{state:{}}})}return ml(t,[{key:\"$get\",value:function(t){return this.store.state[t]}},{key:\"$set\",value:function(t,e){Ta.set(this.store.state,t,e)}},{key:\"$emit\",value:function(){var t;(t=this.store).$emit.apply(t,arguments)}},{key:\"$on\",value:function(){var t;(t=this.store).$on.apply(t,arguments)}}]),t}());Object.assign(Pl.prototype,{getPageAsyncComponent:zi,getLayoutAsyncComponent:Ui,getAsyncComponent:Wi,getVueComponent:Vi});var kl={install:function(t){var e=new Pl;t.$vuepress=e,t.prototype.$vuepress=e}};function El(t){t.beforeEach((function(e,n,r){if(Sl(t,e.path))r();else if(/(\\/|\\.html)$/.test(e.path))if(/\\/$/.test(e.path)){var a=e.path.replace(/\\/$/,\"\")+\".html\";Sl(t,a)?r(a):r()}else r();else{var o=e.path+\"/\",i=e.path+\".html\";Sl(t,i)?r(i):Sl(t,o)?r(o):r()}}))}function Sl(t,e){var n=e.toLowerCase();return t.options.routes.some((function(t){return t.path.toLowerCase()===n}))}var Ol={props:{pageKey:String,slotKey:{type:String,default:\"default\"}},render:function(t){var e=this.pageKey||this.$parent.$page.key;return Hi(\"pageKey\",e),Ta.component(e)||Ta.component(e,zi(e)),Ta.component(e)?t(e):t(\"\")}},jl={functional:!0,props:{slotKey:String,required:!0},render:function(t,e){var n=e.props,r=e.slots;return t(\"div\",{class:[\"content__\".concat(n.slotKey)]},r()[n.slotKey])}},Ll={computed:{openInNewWindowTitle:function(){return this.$themeLocaleConfig.openNewWindowText||\"(opens new window)\"}}},Al=(n(302),n(303),Object(cl.a)(Ll,(function(){var t=this.$createElement,e=this._self._c||t;return e(\"span\",[e(\"svg\",{staticClass:\"icon outbound\",attrs:{xmlns:\"http://www.w3.org/2000/svg\",\"aria-hidden\":\"true\",focusable:\"false\",x:\"0px\",y:\"0px\",viewBox:\"0 0 100 100\",width:\"15\",height:\"15\"}},[e(\"path\",{attrs:{fill:\"currentColor\",d:\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"}}),this._v(\" \"),e(\"polygon\",{attrs:{fill:\"currentColor\",points:\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"}})]),this._v(\" \"),e(\"span\",{staticClass:\"sr-only\"},[this._v(this._s(this.openInNewWindowTitle))])])}),[],!1,null,null,null).exports);function Cl(){return(Cl=a(regeneratorRuntime.mark((function t(e){var n,r,a,o;return regeneratorRuntime.wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return n=\"undefined\"!=typeof window&&window.__VUEPRESS_ROUTER_BASE__?window.__VUEPRESS_ROUTER_BASE__:hl.routerBase||hl.base,El(r=new ki({base:n,mode:\"history\",fallback:!1,routes:sl,scrollBehavior:function(t,e,n){return n||(t.hash?!Ta.$vuepress.$get(\"disableScrollBehavior\")&&{selector:decodeURIComponent(t.hash)}:{x:0,y:0})}})),a={},t.prev=4,t.next=7,Promise.all(fl.filter((function(t){return\"function\"==typeof t})).map((function(t){return t({Vue:Ta,options:a,router:r,siteData:hl,isServer:e})})));case 7:t.next=12;break;case 9:t.prev=9,t.t0=t.catch(4),console.error(t.t0);case 12:return o=new Ta(Object.assign(a,{router:r,render:function(t){return t(\"div\",{attrs:{id:\"app\"}},[t(\"RouterView\",{ref:\"layout\"}),t(\"div\",{class:\"global-ui\"},pl.map((function(e){return t(e)})))])}})),t.abrupt(\"return\",{app:o,router:r});case 14:case\"end\":return t.stop()}}),t,null,[[4,9]])})))).apply(this,arguments)}Ta.config.productionTip=!1,Ta.use(ki),Ta.use(kl),Ta.mixin(function(t,e){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:Ta;Ei(e),n.$vuepress.$set(\"siteData\",e);var r=t(n.$vuepress.$get(\"siteData\")),a=new r,o=Object.getOwnPropertyDescriptors(Object.getPrototypeOf(a)),i={};return Object.keys(o).reduce((function(t,e){return e.startsWith(\"$\")&&(t[e]=o[e].get),t}),i),{computed:i}}((function(t){return function(){function e(){vl(this,e)}return ml(e,[{key:\"setPage\",value:function(t){this.__page=t}},{key:\"$site\",get:function(){return t}},{key:\"$themeConfig\",get:function(){return this.$site.themeConfig}},{key:\"$frontmatter\",get:function(){return this.$page.frontmatter}},{key:\"$localeConfig\",get:function(){var t,e,n=this.$site.locales,r=void 0===n?{}:n;for(var a in r)\"/\"===a?e=r[a]:0===this.$page.path.indexOf(a)&&(t=r[a]);return t||e||{}}},{key:\"$siteTitle\",get:function(){return this.$localeConfig.title||this.$site.title||\"\"}},{key:\"$canonicalUrl\",get:function(){var t=this.$page.frontmatter.canonicalUrl;return\"string\"==typeof t&&t}},{key:\"$title\",get:function(){var t=this.$page,e=this.$page.frontmatter.metaTitle;if(\"string\"==typeof e)return e;var n=this.$siteTitle,r=t.frontmatter.home?null:t.frontmatter.title||t.title;return n?r?r+\" | \"+n:n:r||\"VuePress\"}},{key:\"$description\",get:function(){var t=function(t){if(t){var e=t.filter((function(t){return\"description\"===t.name}))[0];if(e)return e.content}}(this.$page.frontmatter.meta);return t||(this.$page.frontmatter.description||this.$localeConfig.description||this.$site.description||\"\")}},{key:\"$lang\",get:function(){return this.$page.frontmatter.lang||this.$localeConfig.lang||\"en-US\"}},{key:\"$localePath\",get:function(){return this.$localeConfig.path||\"/\"}},{key:\"$themeLocaleConfig\",get:function(){return(this.$site.themeConfig.locales||{})[this.$localePath]||{}}},{key:\"$page\",get:function(){return this.__page?this.__page:function(t,e){for(var n=0;n<t.length;n++){var r=t[n];if(r.path.toLowerCase()===e.toLowerCase())return r}return{path:\"\",frontmatter:{}}}(this.$site.pages,this.$route.path)}}]),e}()}),hl)),Ta.component(\"Content\",Ol),Ta.component(\"ContentSlotsDistributor\",jl),Ta.component(\"OutboundLink\",Al),Ta.component(\"ClientOnly\",{functional:!0,render:function(t,e){var n=e.parent,r=e.children;if(n._isMounted)return r;n.$once(\"hook:mounted\",(function(){n.$forceUpdate()}))}}),Ta.component(\"Layout\",Ui(\"Layout\")),Ta.component(\"NotFound\",Ui(\"NotFound\")),Ta.prototype.$withBase=function(t){var e=this.$site.base;return\"/\"===t.charAt(0)?e+t.slice(1):t},window.__VUEPRESS__={version:\"1.8.2\",hash:\"5d174c6b\"},function(t){return Cl.apply(this,arguments)}(!1).then((function(t){var e=t.app;t.router.onReady((function(){e.$mount(\"#app\")}))}))}]);"
  },
  {
    "path": "docs/chapter1/dart.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>1.4 Dart语言简介 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/130.f1922c51.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable open\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" aria-current=\"page\" class=\"active sidebar-link\">1.4 Dart语言简介</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/chapter1/dart.html#_1-4-1-变量声明\" class=\"sidebar-link\">1.4.1 变量声明</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter1/dart.html#_1-4-2-函数\" class=\"sidebar-link\">1.4.2 函数</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter1/dart.html#_1-4-3-异步支持\" class=\"sidebar-link\">1.4.3 异步支持</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter1/dart.html#_1-4-4-stream\" class=\"sidebar-link\">1.4.4 Stream</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter1/dart.html#_1-4-5-dart和java及javascript对比\" class=\"sidebar-link\">1.4.5 Dart和Java及JavaScript对比</a></li></ul></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_1-4-dart语言简介\"><a href=\"#_1-4-dart语言简介\" class=\"header-anchor\">#</a> 1.4 Dart语言简介</h1> <p>在之前我们已经介绍过Dart语言的相关特性，读者可以翻看一下，如果读者已经熟悉Dart语法，可以跳过本节，如果你还不了解Dart，也不用担心，按照笔者经验，如果你有过其他编程语言经验（尤其是Java和JavaScript）的话会非常容易上手Dart。当然，如果你是iOS开发者，也不用担心，Dart中也有一些与Swift比较相似的特性，如命名参数等，笔者当时学习Dart时，只是花了一个小时，看完Dart官网的Language Tour，就开始动手写Flutter了。</p> <p>在笔者看来，Dart的设计目标应该是同时借鉴了Java和JavaScript。Dart在静态语法方面和Java非常相似，如类型定义、函数声明、泛型等，而在动态特性方面又和JavaScript很像，如函数式特性、异步支持等。除了融合Java和JavaScript语言之所长之外，Dart也具有一些其它具有表现力的语法，如可选命名参数、<code>..</code>（级联运算符）和<code>?.</code>（条件成员访问运算符）以及<code>??</code>（判空赋值运算符）。其实，对编程语言了解比较多的读者会发现，在Dart中其实看到的不仅有Java和JavaScript的影子，它还具有其它编程语言中的身影，如命名参数在Objective-C和Swift中早就很普遍，而<code>??</code>操作符在PHP 7.0语法中就已经存在了，因此我们可以看到Google对Dart语言给予厚望，是想把Dart打造成一门集百家之所长的编程语言。</p> <p>接下来，我们先对Dart语法做一个简单的介绍，然后再将Dart与JavaScript和Java做一个简要的对比，方便读者更好的理解。</p> <blockquote><p>注意：由于本书并非专门介绍Dart语言的书籍，所以本章主要会介绍一下在Flutter开发中常用的语法特性，如果想更多了解Dart，读者可以去Dart官网学习，现在互联网上Dart相关资料已经很多了。另外Dart 2.0已经正式发布，所以本书所有示例均采用Dart 2.0语法。</p></blockquote> <h2 id=\"_1-4-1-变量声明\"><a href=\"#_1-4-1-变量声明\" class=\"header-anchor\">#</a> 1.4.1 变量声明</h2> <ol><li><p><strong>var</strong></p> <p>类似于JavaScript中的<code>var</code>，它可以接收任何类型的变量，但最大的不同是Dart中var变量一旦赋值，类型便会确定，则不能再改变其类型，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">var</span> t<span class=\"token punctuation\">;</span>\nt <span class=\"token operator\">=</span> <span class=\"token string\">&quot;hi world&quot;</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">// 下面代码在dart中会报错，因为变量t的类型已经确定为String，</span>\n<span class=\"token comment\">// 类型一旦确定后则不能再更改其类型。</span>\nt <span class=\"token operator\">=</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>上面的代码在JavaScript是没有问题的，前端开发者需要注意一下，之所以有此差异是因为Dart本身是一个强类型语言，任何变量都是有确定类型的，在Dart中，当用<code>var</code>声明一个变量后，Dart在编译时会根据第一次赋值数据的类型来推断其类型，编译结束后其类型就已经被确定，而JavaScript是纯粹的弱类型脚本语言，var只是变量的声明方式而已。</p></li> <li><p><strong>dynamic</strong>和<strong>Object</strong></p> <p><code>Object</code> 是Dart所有对象的根基类，也就是说所有类型都是<code>Object</code>的子类(包括Function和Null)，所以任何类型的数据都可以赋值给<code>Object</code>声明的对象.\n<code>dynamic</code>与<code>var</code>一样都是关键词,声明的变量可以赋值任意对象。\n而<code>dynamic</code>与<code>Object</code>相同之处在于,他们声明的变量可以在后期改变赋值类型。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">dynamic</span> t<span class=\"token punctuation\">;</span>\nObject x<span class=\"token punctuation\">;</span>\nt <span class=\"token operator\">=</span> <span class=\"token string\">&quot;hi world&quot;</span><span class=\"token punctuation\">;</span>\nx <span class=\"token operator\">=</span> <span class=\"token string\">'Hello Object'</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">//下面代码没有问题</span>\nt <span class=\"token operator\">=</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">;</span>\nx <span class=\"token operator\">=</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p><code>dynamic</code>与<code>Object</code>不同的是,<code>dynamic</code>声明的对象编译器会提供所有可能的组合,\n而<code>Object</code>声明的对象只能使用Object的属性与方法, 否则编译器会报错。如:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code> <span class=\"token keyword\">dynamic</span> a<span class=\"token punctuation\">;</span>\n Object b<span class=\"token punctuation\">;</span>\n <span class=\"token function\">main</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n     a <span class=\"token operator\">=</span> <span class=\"token string\">&quot;&quot;</span><span class=\"token punctuation\">;</span>\n     b <span class=\"token operator\">=</span> <span class=\"token string\">&quot;&quot;</span><span class=\"token punctuation\">;</span>\n     <span class=\"token function\">printLengths</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n <span class=\"token punctuation\">}</span>   \n\n <span class=\"token function\">printLengths</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n     <span class=\"token comment\">// no warning</span>\n     <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>a<span class=\"token punctuation\">.</span>length<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n     <span class=\"token comment\">// warning:</span>\n     <span class=\"token comment\">// The getter 'length' is not defined for the class 'Object'</span>\n     <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>b<span class=\"token punctuation\">.</span>length<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n <span class=\"token punctuation\">}</span>\n</code></pre></div><p>变量a不会报错, 变量b编译器会报错</p> <p><code>dynamic</code>的这个特性与<code>Objective-C</code>中的<code>id</code>作用很像.\n<code>dynamic</code>的这个特点使得我们在使用它时需要格外注意,这很容易引入一个运行时错误.</p></li> <li><p><strong>final</strong>和<strong>const</strong></p> <p>如果您从未打算更改一个变量，那么使用 <code>final</code> 或 <code>const</code>，不是<code>var</code>，也不是一个类型。 一个 <code>final</code> 变量只能被设置一次，两者区别在于：<code>const</code> 变量是一个编译时常量，<code>final</code>变量在第一次使用时被初始化。被<code>final</code>或者<code>const</code>修饰的变量，变量类型可以省略，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//可以省略String这个类型声明</span>\n<span class=\"token keyword\">final</span> str <span class=\"token operator\">=</span> <span class=\"token string\">&quot;hi world&quot;</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">//final String str = &quot;hi world&quot;; </span>\n<span class=\"token keyword\">const</span> str1 <span class=\"token operator\">=</span> <span class=\"token string\">&quot;hi world&quot;</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">//const String str1 = &quot;hi world&quot;;</span>\n</code></pre></div></li></ol> <h2 id=\"_1-4-2-函数\"><a href=\"#_1-4-2-函数\" class=\"header-anchor\">#</a> 1.4.2 函数</h2> <p>Dart是一种真正的面向对象的语言，所以即使是函数也是对象，并且有一个类型<strong>Function</strong>。这意味着函数可以赋值给变量或作为参数传递给其他函数，这是函数式编程的典型特征。</p> <ol><li><p>函数声明</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>bool <span class=\"token function\">isNoble</span><span class=\"token punctuation\">(</span>int atomicNumber<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> _nobleGases<span class=\"token punctuation\">[</span>atomicNumber<span class=\"token punctuation\">]</span> <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>Dart函数声明如果没有显式声明返回值类型时会默认当做<code>dynamic</code>处理，注意，函数返回值没有类型推断：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">typedef</span> bool <span class=\"token function\">CALLBACK</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">//不指定返回类型，此时默认为dynamic，不是bool</span>\n<span class=\"token function\">isNoble</span><span class=\"token punctuation\">(</span>int atomicNumber<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> _nobleGases<span class=\"token punctuation\">[</span>atomicNumber<span class=\"token punctuation\">]</span> <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">void</span> <span class=\"token function\">test</span><span class=\"token punctuation\">(</span>CALLBACK cb<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n   <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token function\">cb</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> \n<span class=\"token punctuation\">}</span>\n<span class=\"token comment\">//报错，isNoble不是bool类型</span>\n<span class=\"token function\">test</span><span class=\"token punctuation\">(</span>isNoble<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div></li> <li><p>对于只包含一个表达式的函数，可以使用简写语法</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>bool isNoble <span class=\"token punctuation\">(</span>int atomicNumber<span class=\"token punctuation\">)</span><span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _nobleGases <span class=\"token punctuation\">[</span> atomicNumber <span class=\"token punctuation\">]</span> ！<span class=\"token operator\">=</span> <span class=\"token keyword\">null</span> <span class=\"token punctuation\">;</span>   \n</code></pre></div></li> <li><p>函数作为变量</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">var</span> say <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>str<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n  <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>str<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n<span class=\"token function\">say</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;hi world&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div></li> <li><p>函数作为参数传递</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">execute</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">var</span> callback<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">callback</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n<span class=\"token function\">execute</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;xxx&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n</code></pre></div></li> <li><p>可选的位置参数</p> <p>包装一组函数参数，用[]标记为可选的位置参数，并放在参数列表的最后面：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>String <span class=\"token function\">say</span><span class=\"token punctuation\">(</span>String from<span class=\"token punctuation\">,</span> String msg<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>String device<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">var</span> result <span class=\"token operator\">=</span> <span class=\"token string\">'$from says $msg'</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>device <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    result <span class=\"token operator\">=</span> <span class=\"token string\">'$result with a $device'</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token keyword\">return</span> result<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>下面是一个不带可选参数调用这个函数的例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">say</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Bob'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Howdy'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//结果是： Bob says Howdy</span>\n</code></pre></div><p>下面是用第三个参数调用这个函数的例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">say</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Bob'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Howdy'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'smoke signal'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//结果是：Bob says Howdy with a smoke signal</span>\n</code></pre></div></li> <li><p>可选的命名参数</p> <p>定义函数时，使用{param1, param2, …}，放在参数列表的最后面，用于指定命名参数。例如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//设置[bold]和[hidden]标志</span>\n<span class=\"token keyword\">void</span> <span class=\"token function\">enableFlags</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>bool bold<span class=\"token punctuation\">,</span> bool hidden<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// ... </span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>调用函数时，可以使用指定命名参数。例如：<code>paramName: value</code></p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">enableFlags</span><span class=\"token punctuation\">(</span>bold<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span> hidden<span class=\"token punctuation\">:</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>可选命名参数在Flutter中使用非常多。</p> <p><strong>注意，不能同时使用可选的位置参数和可选的命名参数</strong></p></li></ol> <h2 id=\"_1-4-3-异步支持\"><a href=\"#_1-4-3-异步支持\" class=\"header-anchor\">#</a> 1.4.3 异步支持</h2> <p>Dart类库有非常多的返回<code>Future</code>或者<code>Stream</code>对象的函数。 这些函数被称为<strong>异步函数</strong>：它们只会在设置好一些耗时操作之后返回，比如像 IO操作。而不是等到这个操作完成。</p> <p><code>async</code>和<code>await</code>关键词支持了异步编程，允许您写出和同步代码很像的异步代码。</p> <h3 id=\"future\"><a href=\"#future\" class=\"header-anchor\">#</a> Future</h3> <p><code>Future</code>与JavaScript中的<code>Promise</code>非常相似，表示一个异步操作的最终完成（或失败）及其结果值的表示。简单来说，它就是用于处理异步操作的，异步处理成功了就执行成功的操作，异步处理失败了就捕获错误或者停止后续操作。一个Future只会对应一个结果，要么成功，要么失败。</p> <p>由于本身功能较多，这里我们只介绍其常用的API及特性。还有，请记住，<code>Future</code> 的所有API的返回值仍然是一个<code>Future</code>对象，所以可以很方便的进行链式调用。</p> <h4 id=\"future-then\"><a href=\"#future-then\" class=\"header-anchor\">#</a> Future.then</h4> <p>为了方便示例，在本例中我们使用<code>Future.delayed</code> 创建了一个延时任务（实际场景会是一个真正的耗时任务，比如一次网络请求），即2秒后返回结果字符串&quot;hi world!&quot;，然后我们在<code>then</code>中接收异步结果并打印结果，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Future<span class=\"token punctuation\">.</span><span class=\"token function\">delayed</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n   <span class=\"token keyword\">return</span> <span class=\"token string\">&quot;hi world!&quot;</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n   <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><h4 id=\"future-catcherror\"><a href=\"#future-catcherror\" class=\"header-anchor\">#</a> Future.catchError</h4> <p>如果异步任务发生错误，我们可以在<code>catchError</code>中捕获错误，我们将上面示例改为：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Future<span class=\"token punctuation\">.</span><span class=\"token function\">delayed</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n   <span class=\"token comment\">//return &quot;hi world!&quot;;</span>\n   <span class=\"token keyword\">throw</span> <span class=\"token function\">AssertionError</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Error&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>  \n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n   <span class=\"token comment\">//执行成功会走到这里  </span>\n   <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;success&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">catchError</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n   <span class=\"token comment\">//执行失败会走到这里  </span>\n   <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>在本示例中，我们在异步任务中抛出了一个异常，<code>then</code>的回调函数将不会被执行，取而代之的是 <code>catchError</code>回调函数将被调用；但是，并不是只有 <code>catchError</code>回调才能捕获错误，<code>then</code>方法还有一个可选参数<code>onError</code>，我们也可以它来捕获异常：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Future<span class=\"token punctuation\">.</span><span class=\"token function\">delayed</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n\t<span class=\"token comment\">//return &quot;hi world!&quot;;</span>\n\t<span class=\"token keyword\">throw</span> <span class=\"token function\">AssertionError</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Error&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n\t<span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;success&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> onError<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n\t<span class=\"token function\">print</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><h4 id=\"future-whencomplete\"><a href=\"#future-whencomplete\" class=\"header-anchor\">#</a> Future.whenComplete</h4> <p>有些时候，我们会遇到无论异步任务执行成功或失败都需要做一些事的场景，比如在网络请求前弹出加载对话框，在请求结束后关闭对话框。这种场景，有两种方法，第一种是分别在<code>then</code>或<code>catch</code>中关闭一下对话框，第二种就是使用<code>Future</code>的<code>whenComplete</code>回调，我们将上面示例改一下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Future<span class=\"token punctuation\">.</span><span class=\"token function\">delayed</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n   <span class=\"token comment\">//return &quot;hi world!&quot;;</span>\n   <span class=\"token keyword\">throw</span> <span class=\"token function\">AssertionError</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Error&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n   <span class=\"token comment\">//执行成功会走到这里 </span>\n   <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">catchError</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n   <span class=\"token comment\">//执行失败会走到这里   </span>\n   <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">whenComplete</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n   <span class=\"token comment\">//无论成功或失败都会走到这里</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><h4 id=\"future-wait\"><a href=\"#future-wait\" class=\"header-anchor\">#</a> Future.wait</h4> <p>有些时候，我们需要等待多个异步任务都执行结束后才进行一些操作，比如我们有一个界面，需要先分别从两个网络接口获取数据，获取成功后，我们需要将两个接口数据进行特定的处理后再显示到UI界面上，应该怎么做？答案是<code>Future.wait</code>，它接受一个<code>Future</code>数组参数，只有数组中所有<code>Future</code>都执行成功后，才会触发<code>then</code>的成功回调，只要有一个<code>Future</code>执行失败，就会触发错误回调。下面，我们通过模拟<code>Future.delayed</code> 来模拟两个数据获取的异步任务，等两个异步任务都执行成功时，将两个异步任务的结果拼接打印出来，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Future<span class=\"token punctuation\">.</span><span class=\"token function\">wait</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span>\n  <span class=\"token comment\">// 2秒后返回结果  </span>\n  Future<span class=\"token punctuation\">.</span><span class=\"token function\">delayed</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token string\">&quot;hello&quot;</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token comment\">// 4秒后返回结果  </span>\n  Future<span class=\"token punctuation\">.</span><span class=\"token function\">delayed</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">4</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token string\">&quot; world&quot;</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>results<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n  <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>results<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token operator\">+</span>results<span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">catchError</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n  <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>执行上面代码，4秒后你会在控制台中看到“hello world”。</p> <h3 id=\"async-await\"><a href=\"#async-await\" class=\"header-anchor\">#</a> Async/await</h3> <p>Dart中的<code>async/await</code> 和JavaScript中的<code>async/await</code>功能和用法是一模一样的，如果你已经了解JavaScript中的<code>async/await</code>的用法，可以直接跳过本节。</p> <h4 id=\"回调地狱-callback-hell\"><a href=\"#回调地狱-callback-hell\" class=\"header-anchor\">#</a> 回调地狱(Callback Hell)</h4> <p>如果代码中有大量异步逻辑，并且出现大量异步任务依赖其它异步任务的结果时，必然会出现<code>Future.then</code>回调中套回调情况。举个例子，比如现在有个需求场景是用户先登录，登录成功后会获得用户ID，然后通过用户ID，再去请求用户个人信息，获取到用户个人信息后，为了使用方便，我们需要将其缓存在本地文件系统，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//先分别定义各个异步任务</span>\nFuture<span class=\"token operator\">&lt;</span>String<span class=\"token operator\">&gt;</span> <span class=\"token function\">login</span><span class=\"token punctuation\">(</span>String userName<span class=\"token punctuation\">,</span> String pwd<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n\t<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n    <span class=\"token comment\">//用户登录</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\nFuture<span class=\"token operator\">&lt;</span>String<span class=\"token operator\">&gt;</span> <span class=\"token function\">getUserInfo</span><span class=\"token punctuation\">(</span>String id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n\t<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n    <span class=\"token comment\">//获取用户信息 </span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\nFuture <span class=\"token function\">saveUserInfo</span><span class=\"token punctuation\">(</span>String userInfo<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n\t<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n\t<span class=\"token comment\">// 保存用户信息 </span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span> \n</code></pre></div><p>接下来，执行整个任务流：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">login</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;alice&quot;</span><span class=\"token punctuation\">,</span><span class=\"token string\">&quot;******&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n <span class=\"token comment\">//登录成功后通过，id获取用户信息    </span>\n <span class=\"token function\">getUserInfo</span><span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>userInfo<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//获取用户信息后保存 </span>\n    <span class=\"token function\">saveUserInfo</span><span class=\"token punctuation\">(</span>userInfo<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n       <span class=\"token comment\">//保存用户信息，接下来执行其它操作</span>\n        <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>可以感受一下，如果业务逻辑中有大量异步依赖的情况，将会出现上面这种在回调里面套回调的情况，过多的嵌套会导致的代码可读性下降以及出错率提高，并且非常难维护，这个问题被形象的称为<strong>回调地狱（Callback Hell）</strong>。回调地狱问题在之前JavaScript中非常突出，也是JavaScript被吐槽最多的点，但随着ECMAScript6和ECMAScript7标准发布后，这个问题得到了非常好的解决，而解决回调地狱的两大神器正是ECMAScript6引入了<code>Promise</code>，以及ECMAScript7中引入的<code>async/await</code>。 而在Dart中几乎是完全平移了JavaScript中的这两者：<code>Future</code>相当于<code>Promise</code>，而<code>async/await</code>连名字都没改。接下来我们看看通过<code>Future</code>和<code>async/await</code>如何消除上面示例中的嵌套问题。</p> <h5 id=\"使用future消除callback-hell\"><a href=\"#使用future消除callback-hell\" class=\"header-anchor\">#</a> 使用Future消除Callback Hell</h5> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">login</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;alice&quot;</span><span class=\"token punctuation\">,</span><span class=\"token string\">&quot;******&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n  \t<span class=\"token keyword\">return</span> <span class=\"token function\">getUserInfo</span><span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>userInfo<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">saveUserInfo</span><span class=\"token punctuation\">(</span>userInfo<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n   <span class=\"token comment\">//执行接下来的操作 </span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">catchError</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">//错误处理  </span>\n  <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>正如上文所述， <em>“<code>Future</code> 的所有API的返回值仍然是一个<code>Future</code>对象，所以可以很方便的进行链式调用”</em> ，如果在then中返回的是一个<code>Future</code>的话，该<code>future</code>会执行，执行结束后会触发后面的<code>then</code>回调，这样依次向下，就避免了层层嵌套。</p> <h5 id=\"使用async-await消除callback-hell\"><a href=\"#使用async-await消除callback-hell\" class=\"header-anchor\">#</a> 使用async/await消除callback hell</h5> <p>通过<code>Future</code>回调中再返回<code>Future</code>的方式虽然能避免层层嵌套，但是还是有一层回调，有没有一种方式能够让我们可以像写同步代码那样来执行异步任务而不使用回调的方式？答案是肯定的，这就要使用<code>async/await</code>了，下面我们先直接看代码，然后再解释，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">task</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n   <span class=\"token keyword\">try</span><span class=\"token punctuation\">{</span>\n    String id <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">login</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;alice&quot;</span><span class=\"token punctuation\">,</span><span class=\"token string\">&quot;******&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    String userInfo <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">getUserInfo</span><span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">await</span> <span class=\"token function\">saveUserInfo</span><span class=\"token punctuation\">(</span>userInfo<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//执行接下来的操作   </span>\n   <span class=\"token punctuation\">}</span> <span class=\"token keyword\">catch</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//错误处理   </span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>   \n   <span class=\"token punctuation\">}</span>  \n<span class=\"token punctuation\">}</span>\n</code></pre></div><ul><li><code>async</code>用来表示函数是异步的，定义的函数会返回一个<code>Future</code>对象，可以使用then方法添加回调函数。</li> <li><code>await</code> 后面是一个<code>Future</code>，表示等待该异步任务完成，异步完成后才会往下走；<code>await</code>必须出现在 <code>async</code> 函数内部。</li></ul> <p>可以看到，我们通过<code>async/await</code>将一个异步流用同步的代码表示出来了。</p> <blockquote><p>其实，无论是在JavaScript还是Dart中，<code>async/await</code>都只是一个语法糖，编译器或解释器最终都会将其转化为一个Promise（Future）的调用链。</p></blockquote> <h2 id=\"_1-4-4-stream\"><a href=\"#_1-4-4-stream\" class=\"header-anchor\">#</a> 1.4.4 Stream</h2> <p><code>Stream</code> 也是用于接收异步事件数据，和<code>Future</code> 不同的是，它可以接收多个异步操作的结果（成功或失败）。 也就是说，在执行异步任务时，可以通过多次触发成功或失败事件来传递结果数据或错误异常。 <code>Stream</code> 常用于会多次读取数据的异步任务场景，如网络内容下载、文件读写等。举个例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Stream<span class=\"token punctuation\">.</span><span class=\"token function\">fromFutures</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span>\n  <span class=\"token comment\">// 1秒后返回结果</span>\n  Future<span class=\"token punctuation\">.</span><span class=\"token function\">delayed</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token string\">&quot;hello 1&quot;</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token comment\">// 抛出一个异常</span>\n  Future<span class=\"token punctuation\">.</span><span class=\"token function\">delayed</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">throw</span> <span class=\"token function\">AssertionError</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Error&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token comment\">// 3秒后返回结果</span>\n  Future<span class=\"token punctuation\">.</span><span class=\"token function\">delayed</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">3</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token string\">&quot;hello 3&quot;</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">listen</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n   <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> onError<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n   <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">.</span>message<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>onDone<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>上面的代码依次会输出：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>I/flutter (17666): hello 1\nI/flutter (17666): Error\nI/flutter (17666): hello 3\n</code></pre></div><p>代码很简单，就不赘述了。</p> <blockquote><p>思考题：既然Stream可以接收多次事件，那能不能用Stream来实现一个订阅者模式的事件总线？</p></blockquote> <h2 id=\"_1-4-5-dart和java及javascript对比\"><a href=\"#_1-4-5-dart和java及javascript对比\" class=\"header-anchor\">#</a> 1.4.5 Dart和Java及JavaScript对比</h2> <p>通过上面介绍，相信你对Dart应该有了一个初步的印象，由于笔者平时也使用Java和JavaScript，下面笔者根据自己的经验，结合Java和JavaScript，谈一下自己的看法。</p> <blockquote><p>之所以将Dart与Java和JavaScript对比，是因为，这两者分别是强类型语言和弱类型语言的典型代表，并且Dart 语法中很多地方也都借鉴了Java和JavaScript。</p></blockquote> <h3 id=\"dart-vs-java\"><a href=\"#dart-vs-java\" class=\"header-anchor\">#</a> Dart vs Java</h3> <p>客观的来讲，Dart在语法层面确实比Java更有表现力；在VM层面，Dart VM在内存回收和吞吐量都进行了反复的优化，但具体的性能对比，笔者没有找到相关测试数据，但在笔者看来，只要Dart语言能流行，VM的性能就不用担心，毕竟Google在Go（没用VM但有GC）、JavaScript（v8）、Dalvik（Android上的Java VM）上已经有了很多技术积淀。值得注意的是Dart在Flutter中已经可以将GC做到10ms以内，所以Dart和Java相比，决胜因素并不会是在性能方面。而在语法层面，Dart要比Java更有表现力，最重要的是Dart对函数式编程支持要远强于Java(目前只停留在Lambda表达式)，而Dart目前真正的不足是<strong>生态</strong>，但笔者相信，随着Flutter的逐渐火热，会回过头来反推Dart生态加速发展，对于Dart来说，现在需要的是时间。</p> <h3 id=\"dart-vs-javascript\"><a href=\"#dart-vs-javascript\" class=\"header-anchor\">#</a> Dart vs JavaScript</h3> <p>JavaScript的弱类型一直被抓短，所以TypeScript、CoffeeScript甚至是Facebook的flow（虽然并不能算JavaScript的一个超集，但也通过标注和打包工具提供了静态类型检查）才有市场。就笔者使用过的脚本语言中（笔者曾使用过Python、PHP），JavaScript无疑是<strong>动态化</strong>支持最好的脚本语言，比如在JavaScript中，可以给任何对象在任何时候动态扩展属性，对于精通JavaScript的高手来说，这无疑是一把利剑。但是，任何事物都有两面性，JavaScript的强大的动态化特性也是把双刃剑，你可经常听到另一个声音，认为JavaScript的这种动态性糟糕透了，太过灵活反而导致代码很难预期，无法限制不被期望的修改。毕竟有些人总是对自己或别人写的代码不放心，他们希望能够让代码变得可控，并期望有一套静态类型检查系统来帮助自己减少错误。正因如此，在Flutter中，Dart几乎放弃了脚本语言动态化的特性，如不支持反射、也不支持动态创建函数等。并且Dart在2.0强制开启了类型检查（Strong Mode），原先的检查模式（checked mode）和可选类型（optional type）将淡出，所以在类型安全这个层面来说，Dart和TypeScript、CoffeeScript是差不多的，所以单从这一点来看，Dart并不具备什么明显优势，但综合起来看，Dart既能进行服务端脚本、APP开发、web开发，这就有优势了！</p> <p>综上所述，笔者还是很看好Dart语言的将来，之所以表这个态，是因为在新技术发展初期，很多人可能还有所摇摆，有所犹豫，所以有必要给大家打一剂强心针，当然，这是一个见仁见智的问题，大家可以各抒己见。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter1/install_flutter.html\" class=\"prev\">\n        1.3 搭建Flutter开发环境\n      </a></span> <span class=\"next\"><a href=\"/chapter2/first_flutter_app.html\">\n        2.1 计数器应用示例\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/130.f1922c51.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter1/flutter_intro.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>1.2 初识Flutter | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/81.f8e7c85e.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable open\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" aria-current=\"page\" class=\"active sidebar-link\">1.2 初识Flutter</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/chapter1/flutter_intro.html#_1-2-1-flutter简介\" class=\"sidebar-link\">1.2.1 Flutter简介</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter1/flutter_intro.html#_1-2-2-flutter框架结构\" class=\"sidebar-link\">1.2.2 Flutter框架结构</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter1/flutter_intro.html#_1-2-3-如何学习flutter\" class=\"sidebar-link\">1.2.3 如何学习Flutter</a></li></ul></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_1-2-初识flutter\"><a href=\"#_1-2-初识flutter\" class=\"header-anchor\">#</a> 1.2 初识Flutter</h1> <h2 id=\"_1-2-1-flutter简介\"><a href=\"#_1-2-1-flutter简介\" class=\"header-anchor\">#</a> 1.2.1 Flutter简介</h2> <p>Flutter 是 Google推出并开源的移动应用开发框架，主打跨平台、高保真、高性能。开发者可以通过 Dart语言开发 App，一套代码同时运行在 iOS 和 Android平台。 Flutter提供了丰富的组件、接口，开发者可以很快地为 Flutter添加 native扩展。同时 Flutter还使用 Native引擎渲染视图，这无疑能为用户提供良好的体验。</p> <h4 id=\"跨平台自绘引擎\"><a href=\"#跨平台自绘引擎\" class=\"header-anchor\">#</a> 跨平台自绘引擎</h4> <p>Flutter与用于构建移动应用程序的其它大多数框架不同，因为Flutter既不使用WebView，也不使用操作系统的原生控件。 相反，Flutter使用自己的高性能渲染引擎来绘制widget。这样不仅可以保证在Android和iOS上UI的一致性，而且也可以避免对原生控件依赖而带来的限制及高昂的维护成本。</p> <p>Flutter使用Skia作为其2D渲染引擎，Skia是Google的一个2D图形处理函数库，包含字型、坐标转换，以及点阵图都有高效能且简洁的表现，Skia是跨平台的，并提供了非常友好的API，目前Google Chrome浏览器和Android均采用Skia作为其绘图引擎。</p> <p>目前Flutter默认支持iOS、Android、Fuchsia（Google新的自研操作系统）三个移动平台。但Flutter亦可支持Web开发（Flutter for web）和PC开发，本书的示例和介绍主要是基于iOS和Android平台的，其它平台读者可以自行了解。</p> <h4 id=\"高性能\"><a href=\"#高性能\" class=\"header-anchor\">#</a> 高性能</h4> <p>Flutter高性能主要靠两点来保证，首先，Flutter APP采用Dart语言开发。Dart在 JIT（即时编译）模式下，速度与 JavaScript基本持平。但是 Dart支持 AOT，当以 AOT模式运行时，JavaScript便远远追不上了。速度的提升对高帧率下的视图数据计算很有帮助。其次，Flutter使用自己的渲染引擎来绘制UI，布局数据等由Dart语言直接控制，所以在布局过程中不需要像RN那样要在JavaScript和Native之间通信，这在一些滑动和拖动的场景下具有明显优势，因为在滑动和拖动过程往往都会引起布局发生变化，所以JavaScript需要和Native之间不停的同步布局信息，这和在浏览器中要JavaScript频繁操作DOM所带来的问题是相同的，都会带来比较可观的性能开销。</p> <h4 id=\"采用dart语言开发\"><a href=\"#采用dart语言开发\" class=\"header-anchor\">#</a> 采用Dart语言开发</h4> <p>这是一个很有意思，但也很有争议的问题，在了解Flutter为什么选择了 Dart而不是 JavaScript之前我们先来介绍两个概念：JIT和AOT。</p> <p>目前，程序主要有两种运行方式：静态编译与动态解释。静态编译的程序在执行前全部被翻译为机器码，通常将这种类型称为<strong>AOT</strong> （Ahead of time）即 “提前编译”；而解释执行的则是一句一句边翻译边运行，通常将这种类型称为<strong>JIT</strong>（Just-in-time）即“即时编译”。AOT程序的典型代表是用C/C++开发的应用，它们必须在执行前编译成机器码，而JIT的代表则非常多，如JavaScript、python等，事实上，所有脚本语言都支持JIT模式。但需要注意的是JIT和AOT指的是程序运行方式，和编程语言并非强关联的，有些语言既可以以JIT方式运行也可以以AOT方式运行，如Java、Python，它们可以在第一次执行时编译成中间字节码、然后在之后执行时可以直接执行字节码，也许有人会说，中间字节码并非机器码，在程序执行时仍然需要动态将字节码转为机器码，是的，这没有错，不过通常我们区分是否为AOT的标准就是看代码在执行之前是否需要编译，只要需要编译，无论其编译产物是字节码还是机器码，都属于AOT。在此，读者不必纠结于概念，概念就是为了传达精神而发明的，只要读者能够理解其原理即可，得其神忘其形。</p> <p>现在我们看看Flutter为什么选择Dart语言？笔者根据官方解释以及自己对Flutter的理解总结了以下几条（由于其它跨平台框架都将JavaScript作为其开发语言，所以主要将Dart和JavaScript做一个对比）：</p> <ol><li><p><strong>开发效率高</strong></p> <p>Dart运行时和编译器支持Flutter的两个关键特性的组合：</p> <p><strong>基于JIT的快速开发周期</strong>：Flutter在开发阶段采用，采用JIT模式，这样就避免了每次改动都要进行编译，极大的节省了开发时间；</p> <p><strong>基于AOT的发布包</strong>:  Flutter在发布时可以通过AOT生成高效的ARM代码以保证应用性能。而JavaScript则不具有这个能力。</p></li> <li><p><strong>高性能</strong></p> <p>Flutter旨在提供流畅、高保真的的UI体验。为了实现这一点，Flutter中需要能够在每个动画帧中运行大量的代码。这意味着需要一种既能提供高性能的语言，而不会出现会丢帧的周期性暂停，而Dart支持AOT，在这一点上可以做的比JavaScript更好。</p></li> <li><p><strong>快速内存分配</strong></p> <p>Flutter框架使用函数式流，这使得它在很大程度上依赖于底层的内存分配器。因此，拥有一个能够有效地处理琐碎任务的内存分配器将显得十分重要，在缺乏此功能的语言中，Flutter将无法有效地工作。当然Chrome V8的JavaScript引擎在内存分配上也已经做的很好，事实上Dart开发团队的很多成员都是来自Chrome团队的，所以在内存分配上Dart并不能作为超越JavaScript的优势，而对于Flutter来说，它需要这样的特性，而Dart也正好满足而已。</p></li> <li><p><strong>类型安全</strong></p> <p>由于Dart是类型安全的语言，支持静态类型检测，所以可以在编译前发现一些类型的错误，并排除潜在问题，这一点对于前端开发者来说可能会更具有吸引力。与之不同的，JavaScript是一个弱类型语言，也因此前端社区出现了很多给JavaScript代码添加静态类型检测的扩展语言和工具，如：微软的TypeScript以及Facebook的Flow。相比之下，Dart本身就支持静态类型，这是它的一个重要优势。</p></li> <li><p><strong>Dart团队就在你身边</strong></p> <p>看似不起眼，实则举足轻重。由于有Dart团队的积极投入，Flutter团队可以获得更多、更方便的支持，正如Flutter官网所述“我们正与Dart社区进行密切合作，以改进Dart在Flutter中的使用。例如，当我们最初采用Dart时，该语言并没有提供生成原生二进制文件的工具链（这对于实现可预测的高性能具有很大的帮助），但是现在它实现了，因为Dart团队专门为Flutter构建了它。同样，Dart VM之前已经针对吞吐量进行了优化，但团队现在正在优化VM的延迟时间，这对于Flutter的工作负载更为重要。”</p></li></ol> <h4 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h4> <p>本节主要介绍了一下Flutter的特点，如果你感到有些点还不是很好理解，不用着急，随着日后对Flutter细节的了解，再回过头来看，相信你会有更深的体会。</p> <h2 id=\"_1-2-2-flutter框架结构\"><a href=\"#_1-2-2-flutter框架结构\" class=\"header-anchor\">#</a> 1.2.2 Flutter框架结构</h2> <p>本节我们先对Flutter的框架做一个整体介绍，旨在让读者心中有一个整体的印象，这对初学者来说非常重要。如果一下子便深入到Flutter中，就会像是一个在沙漠中没有地图的人，即使可以找到一个绿洲，但是他也不会知道下一个绿洲在哪。因此，无论学什么技术，都要先有一张清晰的“地图”，而我们的学习过程就是“按图索骥”，这样我们才不会陷于细节而“目无全牛”。言归正传，我们看一下Flutter官方提供的Flutter框架图，如图1-1所示：</p> <p><img src=\"/assets/img/1-1.41c572c4.png\" alt=\"图1-1\"></p> <h3 id=\"flutter-framework\"><a href=\"#flutter-framework\" class=\"header-anchor\">#</a> Flutter Framework</h3> <p>这是一个纯 Dart实现的 SDK，它实现了一套基础库，自底向上，我们来简单介绍一下：</p> <ul><li><p>底下两层（Foundation和Animation、Painting、Gestures）在Google的一些视频中被合并为一个dart UI层，对应的是Flutter中的<code>dart:ui</code>包，它是Flutter引擎暴露的底层UI库，提供动画、手势及绘制能力。</p></li> <li><p>Rendering层，这一层是一个抽象的布局层，它依赖于dart UI层，Rendering层会构建一个UI树，当UI树有变化时，会计算出有变化的部分，然后更新UI树，最终将UI树绘制到屏幕上，这个过程类似于React中的虚拟DOM。Rendering层可以说是Flutter UI框架最核心的部分，它除了确定每个UI元素的位置、大小之外还要进行坐标变换、绘制(调用底层dart:ui)。</p></li> <li><p>Widgets层是Flutter提供的的一套基础组件库，在基础组件库之上，Flutter还提供了 Material 和Cupertino两种视觉风格的组件库。而<strong>我们Flutter开发的大多数场景，只是和这两层打交道</strong>。</p></li></ul> <h3 id=\"flutter-engine\"><a href=\"#flutter-engine\" class=\"header-anchor\">#</a> Flutter Engine</h3> <p>这是一个纯 C++实现的 SDK，其中包括了 Skia引擎、Dart运行时、文字排版引擎等。在代码调用 <code>dart:ui</code>库时，调用最终会走到Engine层，然后实现真正的绘制逻辑。</p> <h3 id=\"总结-2\"><a href=\"#总结-2\" class=\"header-anchor\">#</a> 总结</h3> <p>Flutter框架本身有着良好的分层设计，本节旨在让读者对Flutter整体框架有个大概的印象，相信到现在为止，读者已经对Flutter有一个初始印象，在我们正式动手之前，我们还需要了解一下Flutter的开发语言Dart。</p> <h2 id=\"_1-2-3-如何学习flutter\"><a href=\"#_1-2-3-如何学习flutter\" class=\"header-anchor\">#</a> 1.2.3 如何学习Flutter</h2> <p>本节给大家一些学习建议，分享一下笔者在学习Flutter中的一些心得，希望可以帮助你提高学习效率，避免不必要的坑。</p> <h3 id=\"资源\"><a href=\"#资源\" class=\"header-anchor\">#</a> 资源</h3> <ul><li><p><strong>官网</strong>：阅读Flutter官网的资源是快速入门的最佳方式，同时官网也是了解最新Flutter发展动态的地方，由于目前Flutter仍然处于快速发展阶段，所以建议读者还是时不时的去官网看看有没有新的动态。</p></li> <li><p><strong>源码及注释</strong>：源码注释应作为学习Flutter的第一文档，Flutter SDK的源码是开源的，并且注释非常详细，也有很多示例，实际上，Flutter官方的SDK文档就是通过注释生成的。源码结合注释可以帮你解决大多数问题。</p></li> <li><p><strong>Github</strong>：如果遇到的问题在StackOverflow上也没有找到答案，可以去github flutter 项目下提issue。</p></li> <li><p><strong>Gallery源码</strong>：Gallery是Flutter官方示例APP，里面有丰富的示例，读者可以在网上下载安装。Gallery的源码在Flutter源码“examples”目录下。</p></li></ul> <h3 id=\"社区\"><a href=\"#社区\" class=\"header-anchor\">#</a> 社区</h3> <ul><li><strong>StackOverflow</strong>：如果你还没听过StackOverflow，这是目前全球最大的程序员问答社区，现在也是活跃度最高的Flutter问答社区。StackOverflow上面除了世界各地的Flutter使用者会在上面交流之外，Flutter开发团队的成员也经常会在上面回答问题。</li> <li><strong>Flutter中文网社区</strong>：Flutter中文网(https://flutterchina.club)是笔者维护中文网站，目前也是最大的中文资源社区，上面提供了Flutter官网的文档翻译、开源项目、及案例，还有申请加入组织的入口哦。</li> <li><strong>博客</strong>：随着Flutter技术的推广，相信很快网上将会有很多Flutter相关的文章、博客，读者可以多去浏览、阅读。</li></ul> <h3 id=\"总结-3\"><a href=\"#总结-3\" class=\"header-anchor\">#</a> 总结</h3> <p>有了资料和社区后，对于我们学习者自身来说，最重要的还是要多动手、多实践，在本书后面的章节中，希望读者能够亲自动手写一下示例。准备好了吗，下一章中，我们将正式进入Flutter的世界！</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter1/mobile_development_intro.html\" class=\"prev\">\n        1.1 移动开发技术简介\n      </a></span> <span class=\"next\"><a href=\"/chapter1/install_flutter.html\">\n        1.3 搭建Flutter开发环境\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/81.f8e7c85e.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter1/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>本章目录 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/131.7f51717c.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h2 id=\"本章目录\"><a href=\"#本章目录\" class=\"header-anchor\">#</a> 本章目录</h2> <ul><li><a href=\"/chapter1/mobile_development_intro.html\">移动开发技术简介</a></li> <li><a href=\"/chapter1/flutter_intro.html\">Flutter简介</a></li> <li><a href=\"/chapter1/install_flutter.html\">搭建Flutter开发环境</a></li> <li><a href=\"/chapter1/dart.html\">Dart语言简介</a></li></ul></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/131.7f51717c.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter1/install_flutter.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>1.3 搭建Flutter开发环境 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/5.0941abdb.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable open\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" aria-current=\"page\" class=\"active sidebar-link\">1.3 搭建Flutter开发环境</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/chapter1/install_flutter.html#_1-3-1-安装flutter\" class=\"sidebar-link\">1.3.1 安装Flutter</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter1/install_flutter.html#_1-3-2-ide配置与使用\" class=\"sidebar-link\">1.3.2 IDE配置与使用</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter1/install_flutter.html#_1-3-3-连接设备运行flutter应用\" class=\"sidebar-link\">1.3.3 连接设备运行Flutter应用</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter1/install_flutter.html#_1-3-4-常见配置问题\" class=\"sidebar-link\">1.3.4 常见配置问题</a></li></ul></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_1-3-搭建flutter开发环境\"><a href=\"#_1-3-搭建flutter开发环境\" class=\"header-anchor\">#</a> 1.3 搭建Flutter开发环境</h1> <p>工欲善其事必先利其器，本节首先会分别介绍一下在Windows和macOS下Flutter SDK的安装，然后再介绍一下配IDE和模拟器的使用。</p> <h2 id=\"_1-3-1-安装flutter\"><a href=\"#_1-3-1-安装flutter\" class=\"header-anchor\">#</a> 1.3.1 安装Flutter</h2> <p>由于Flutter会同时构建Android和IOS两个平台的发布包，所以Flutter同时依赖Android SDK和iOS SDK，在安装Flutter时也需要安装相应平台的构建工具和SDK。下面我们分别介绍一下Windows和macOS下的环境搭建。</p> <blockquote><p>注意：本节介绍的安装方式随着Flutter的升级可能会发生变化，如果下面介绍的内容在您安装Flutter时已经失效，请访问Flutter官网，按照官网最新的安装教程安装。</p></blockquote> <h3 id=\"使用镜像\"><a href=\"#使用镜像\" class=\"header-anchor\">#</a> 使用镜像</h3> <p>由于在国内访问Flutter有时可能会受到限制，Flutter官方为中国开发者搭建了临时镜像，大家可以将如下环境变量加入到用户环境变量中：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>export PUB_HOSTED_URL=https://pub.flutter-io.cn\nexport FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn\n</code></pre></div><p><strong>注意：</strong> 此镜像为临时镜像，并不能保证一直可用，读者可以参考https://flutter.io/community/china 以获得有关镜像服务器的最新动态。</p> <h3 id=\"在windows上搭建flutter开发环境\"><a href=\"#在windows上搭建flutter开发环境\" class=\"header-anchor\">#</a> 在Windows上搭建Flutter开发环境</h3> <h4 id=\"系统要求\"><a href=\"#系统要求\" class=\"header-anchor\">#</a> 系统要求</h4> <p>要安装并运行Flutter，您的开发环境必须满足以下最低要求:</p> <ul><li><p>操作系统: Windows 7 或更高版本 (64-bit)</p></li> <li><p>磁盘空间: 400 MB (不包括Android Studio的磁盘空间).</p></li> <li><p>工具: Flutter 依赖下面这些命令行工具.</p> <ul><li><p><a href=\"https://docs.microsoft.com/en-us/powershell/scripting/setup/installing-windows-powershell#upgrading-existing-windows-powershell\" target=\"_blank\" rel=\"noopener noreferrer\">PowerShell 5.0<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 或更新的版本</p></li> <li><p><a href=\"https://git-scm.com/download/win\" target=\"_blank\" rel=\"noopener noreferrer\">Git for Windows<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>  (Git命令行工具)；</p></li></ul> <p>如果已安装Git for Windows，请确保可以在命令提示符或PowerShell中运行 git 命令</p></li></ul> <h4 id=\"获取flutter-sdk\"><a href=\"#获取flutter-sdk\" class=\"header-anchor\">#</a> 获取Flutter SDK</h4> <ol><li><p>去flutter官网下载其最新可用的安装包，下载地址：https://flutter.dev/docs/development/tools/sdk/releases ，打开后如图1-2所示：</p> <p><img src=\"/assets/img/1-2.c3960e42.png\" alt=\"图1-2\"></p> <p>注意，Flutter的渠道版本会不停变动，请以Flutter官网为准。另外，在中国大陆地区，要想正常获取安装包列表或下载安装包，可能需要翻墙，读者也可以去Flutter github项目下去下载安装包，地址：https://github.com/flutter/flutter/releases 。</p></li> <li><p>将安装包zip解压到你想安装Flutter SDK的路径（如：<code>C:\\src\\flutter</code>；注意，<strong>不要</strong>将flutter安装到需要一些高权限的路径如<code>C:\\Program Files\\</code>）。</p></li> <li><p>在Flutter安装目录的<code>flutter</code>文件下找到<code>flutter_console.bat</code>，双击运行并启动<strong>flutter命令行</strong>，接下来，你就可以在Flutter命令行运行flutter命令了。</p></li></ol> <h5 id=\"更新环境变量\"><a href=\"#更新环境变量\" class=\"header-anchor\">#</a> 更新环境变量</h5> <p>如果你想在Windows系统自带命令行运行flutter命令，需要添加以下环境变量到用户PATH：</p> <ul><li>转到 “控制面板&gt;用户帐户&gt;用户帐户&gt;更改我的环境变量”</li> <li>在“用户变量”下检查是否有名为“Path”的条目:\n<ul><li>如果该条目存在， 追加 flutter\\bin的全路径，使用 ; 作为分隔符.</li> <li>如果该条目不存在，创建一个新用户变量 Path ，然后将 <code>flutter\\bin</code> 的全路径作为它的值.</li></ul></li></ul> <p>重启Windows以应用此更改.</p> <h5 id=\"运行-flutter-doctor命令\"><a href=\"#运行-flutter-doctor命令\" class=\"header-anchor\">#</a> 运行 flutter doctor命令</h5> <p>在Flutter命令行运行如下命令来查看是否还需要安装其它依赖，如果需要，安装它们：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>flutter doctor\n</code></pre></div><p>该命令检查你的环境并在命令行窗口中显示报告。Dart SDK已经在打包在Flutter SDK里了，没有必要单独安装Dart。 仔细检查命令行输出以获取可能需要安装的其他软件或进一步需要执行的任务。</p> <p>例如：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>[-] Android toolchain - develop for Android devices\n    • Android SDK at D:\\Android\\sdk\n    ✗ Android SDK is missing command line tools; download from https://goo.gl/XxQghQ\n    • Try re-installing or updating your Android SDK,\n      visit https://flutter.io/setup/#android-setup for detailed instructions.\n</code></pre></div><p>第一次运行flutter命令（如<code>flutter doctor</code>）时，它会下载它自己的依赖项并自行编译。以后再运行就会快得多。缺失的依赖需要安装一下，安装完成后再运行<code>flutter doctor</code>命令来验证是否安装成功。</p> <h4 id=\"android设置\"><a href=\"#android设置\" class=\"header-anchor\">#</a> Android设置</h4> <p>Flutter依赖于Android Studio的全量安装。Android Studio不仅可以管理Android 平台依赖、SDK版本等，而且它也是Flutter开发推荐的IDE之一（当然，你也可以使用其它编辑器或IDE，我们将会在后面讨论）。</p> <h5 id=\"安装android-studio\"><a href=\"#安装android-studio\" class=\"header-anchor\">#</a> 安装Android Studio</h5> <ol><li>下载并安装 Android Studio，下载地址：https://developer.android.com/studio/index.html 。</li> <li>启动Android Studio，然后执行“Android Studio安装向导”。这将安装最新的Android SDK、Android SDK平台工具和Android SDK构建工具，这些是用Flutter进行Android开发所需要的。</li></ol> <h4 id=\"安装遇到问题\"><a href=\"#安装遇到问题\" class=\"header-anchor\">#</a> 安装遇到问题？</h4> <p>如果在安装过程中遇到问题，可以先去flutter官网查看一下安装方式是否发生变化，或者在网上搜索一下解决方案。</p> <h3 id=\"在macos上搭建flutter开发环境\"><a href=\"#在macos上搭建flutter开发环境\" class=\"header-anchor\">#</a> 在macOS上搭建Flutter开发环境</h3> <p>在masOS下可以同时进行Android和iOS设备的测试。</p> <h4 id=\"系统要求-2\"><a href=\"#系统要求-2\" class=\"header-anchor\">#</a> 系统要求</h4> <p>要安装并运行Flutter，您的开发环境必须满足以下最低要求:</p> <ul><li>操作系统: macOS (64-bit)</li> <li>磁盘空间: 700 MB (不包括Xcode或Android Studio的磁盘空间）.</li> <li>工具: Flutter 依赖下面这些命令行工具.\n<ul><li><code>bash、mkdir、rm、git、curl、unzip、which</code></li></ul></li></ul> <h4 id=\"获取flutter-sdk-2\"><a href=\"#获取flutter-sdk-2\" class=\"header-anchor\">#</a> 获取Flutter SDK</h4> <ol><li><p>去flutter官网下载其最新可用的安装包，官网地址：https://flutter.io/sdk-archive/#macos</p> <p>注意，Flutter的渠道版本会不停变动，请以Flutter官网为准。另外，在中国大陆地区，要想正常获取安装包列表或下载安装包，可能需要翻墙，读者也可以去Flutter github项目下去下载安装包，地址：https://github.com/flutter/flutter/releases 。</p></li> <li><p>解压安装包到你想安装的目录，如：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code><span class=\"token builtin class-name\">cd</span> ~/development\n<span class=\"token function\">unzip</span> ~/Downloads/flutter_macos_v0.5.1-beta.zip\n</code></pre></div></li> <li><p>添加<code>flutter</code>相关工具到path中：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code><span class=\"token builtin class-name\">export</span> <span class=\"token assign-left variable\"><span class=\"token environment constant\">PATH</span></span><span class=\"token operator\">=</span><span class=\"token variable\"><span class=\"token variable\">`</span><span class=\"token builtin class-name\">pwd</span><span class=\"token variable\">`</span></span>/flutter/bin:<span class=\"token environment constant\">$PATH</span>\n</code></pre></div><p>此代码只能暂时针对当前命令行窗口设置PATH环境变量，要想永久将Flutter添加到PATH中请参考下面<strong>更新环境变量</strong> 部分。</p></li></ol> <h5 id=\"运行-flutter-doctor命令-2\"><a href=\"#运行-flutter-doctor命令-2\" class=\"header-anchor\">#</a> 运行 flutter doctor命令</h5> <p>这一步和Windows下步骤一致，不再赘述。</p> <h5 id=\"更新环境变量-2\"><a href=\"#更新环境变量-2\" class=\"header-anchor\">#</a> 更新环境变量</h5> <p>将Flutter添加到PATH中，可以在任何终端会话中运行<code>flutter</code>命令。</p> <p>对于所有终端会话永久修改此变量的步骤是和特定计算机系统相关的。通常，您会在打开新窗口时将设置环境变量的命令添加到执行的文件中。例如</p> <ol><li><p>确定您Flutter SDK的目录记为“FLUTTER_INSTALL_PATH”，您将在步骤3中用到。</p></li> <li><p>打开(或创建) <code>$HOME/.bash_profile</code>。文件路径和文件名可能在你的电脑上不同.</p></li> <li><p>添加以下路径:</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code><span class=\"token builtin class-name\">export</span> <span class=\"token assign-left variable\"><span class=\"token environment constant\">PATH</span></span><span class=\"token operator\">=</span><span class=\"token punctuation\">[</span>FLUTTER_INSTALL_PATH<span class=\"token punctuation\">]</span>/flutter/bin:<span class=\"token environment constant\">$PATH</span>\n</code></pre></div><p>例如笔者Flutter 安装目录是“~/code/flutter_dir”，那么代码为：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>export PATH=~/code/flutter_dir/flutter/bin:$PATH\n</code></pre></div></li> <li><p>运行 <code>source $HOME/.bash_profile</code> 刷新当前终端窗口。</p> <blockquote><p><strong>注意:</strong> 如果你使用终端是zsh，终端启动时 <code>~/.bash_profile</code> 将不会被加载，解决办法就是修改 <code>～/.zshrc</code> ，在其中添加：source ～/.bash_profile</p></blockquote></li> <li><p>验证“flutter/bin”是否已在PATH中：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>echo $PATH\n</code></pre></div></li></ol> <h4 id=\"安装-xcode\"><a href=\"#安装-xcode\" class=\"header-anchor\">#</a> 安装 Xcode</h4> <p>要为iOS开发Flutter应用程序，您需要Xcode 9.0或更高版本:</p> <ol><li>安装Xcode 9.0或更新版本(通过<a href=\"https://developer.apple.com/xcode/\" target=\"_blank\" rel=\"noopener noreferrer\">链接下载<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>或<a href=\"https://itunes.apple.com/us/app/xcode/id497799835\" target=\"_blank\" rel=\"noopener noreferrer\">苹果应用商店<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>).</li> <li>配置Xcode命令行工具以使用新安装的Xcode版本 <code>sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer</code> 对于大多数情况，当您想要使用最新版本的Xcode时，这是正确的路径。如果您需要使用不同的版本，请指定相应路径。</li> <li>确保Xcode许可协议是通过打开一次Xcode或通过命令<code>sudo xcodebuild -license</code>同意过了.</li></ol> <p>使用Xcode，您可以在iOS设备或模拟器上运行Flutter应用程序。</p> <h4 id=\"安装android-studio-2\"><a href=\"#安装android-studio-2\" class=\"header-anchor\">#</a> 安装Android Studio</h4> <p>和Window一样，要在Android设备上构建并运行Flutter程序都需要先安装Android Studio，读者可以先自行下载并安装Android Studio，在此不再赘述。</p> <h3 id=\"升级-flutter\"><a href=\"#升级-flutter\" class=\"header-anchor\">#</a> 升级 Flutter</h3> <h4 id=\"flutter-sdk分支\"><a href=\"#flutter-sdk分支\" class=\"header-anchor\">#</a> Flutter SDK分支</h4> <p>Flutter SDK有多个分支，如beta、dev、master、stable，其中stable分支为稳定分支（日后有新的稳定版本发布后可能也会有新的稳定分支，如1.0.0），dev和master为开发分支，安装flutter后，你可以运行<code>flutter channel</code>查看所有分支，如笔者本地运行后，结果如下：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>Flutter channels:\n  beta\n  dev\n* master\n</code></pre></div><p>带&quot;*&quot;号的分支即你本地的Flutter SDK 跟踪的分支，要切换分支，可以使用<code>flutter channel beta</code> 或 <code>flutter channel master</code>，Flutter官方建议跟踪稳定分支，但你也可以跟踪<code>master</code>分支，这样可以查看最新的变化，但这样稳定性要低的多。</p> <h4 id=\"升级flutter-sdk和依赖包\"><a href=\"#升级flutter-sdk和依赖包\" class=\"header-anchor\">#</a> 升级Flutter SDK和依赖包</h4> <p>要升级flutter sdk，只需一句命令：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>flutter upgrade\n</code></pre></div><p>该命令会同时更新Flutter SDK和你的flutter项目依赖包。如果你只想更新项目依赖包（不包括Flutter SDK），可以使用如下命令：</p> <ul><li><code>flutter packages get</code>获取项目所有的依赖包。</li> <li><code>flutter packages upgrade</code> 获取项目所有依赖包的最新版本。</li></ul> <h4 id=\"\"><a href=\"#\" class=\"header-anchor\">#</a></h4> <h2 id=\"_1-3-2-ide配置与使用\"><a href=\"#_1-3-2-ide配置与使用\" class=\"header-anchor\">#</a> 1.3.2 IDE配置与使用</h2> <p>理论上可以使用任何文本编辑器与命令行工具来构建Flutter应用程序。 不过，Flutter官方建议使用Android Studio和VS Code之一以获得更好的开发体验。Flutter官方提供了这两款编辑器插件，通过IDE和插件可获得代码补全、语法高亮、widget编辑辅助、运行和调试支持等功能，可以帮助我们极大的提高开发效率。下面我们分别介绍一下Android Studio和VS Code的配置及使用（Android Studio和VS Code读者可以在其官网获得最新的安装，由于安装比较简单，故不再赘述）。</p> <h3 id=\"android-studio-配置与使用\"><a href=\"#android-studio-配置与使用\" class=\"header-anchor\">#</a> Android Studio 配置与使用</h3> <p>由于Android Studio是基于IntelliJ IDEA开发的，所以读者也可以使用IntelliJ IDEA。</p> <h4 id=\"安装flutter和dart插件\"><a href=\"#安装flutter和dart插件\" class=\"header-anchor\">#</a> 安装Flutter和Dart插件</h4> <p>需要安装两个插件:</p> <ul><li><code>Flutter</code>插件： 支持Flutter开发工作流 (运行、调试、热重载等)。</li> <li><code>Dart</code>插件： 提供代码分析 (输入代码时进行验证、代码补全等)。</li></ul> <p>安装步骤：</p> <ol><li>启动Android Studio。</li> <li>打开插件首选项 (macOS：<strong>Preferences&gt;Plugins</strong>, Windows：<strong>File&gt;Settings&gt;Plugins</strong>)。</li> <li>选择 <strong>Browse repositories…</strong>，选择 flutter 插件并点击 <code>install</code>。</li> <li>重启Android Studio后插件生效。</li></ol> <p>接下来，让我们用Android Studio创建一个Flutter项目，然后运行它，并体验“热重载”。</p> <h4 id=\"创建flutter应用\"><a href=\"#创建flutter应用\" class=\"header-anchor\">#</a> 创建Flutter应用</h4> <ol><li>选择 <strong>File&gt;New Flutter Project</strong> 。</li> <li>选择 <strong>Flutter application</strong> 作为 project 类型, 然后点击 Next。</li> <li>输入项目名称 (如 <code>myapp</code>)，然后点击 Next。</li> <li>点击 <strong>Finish</strong>。</li> <li>等待Android Studio安装SDK并创建项目。</li></ol> <p>上述命令创建一个Flutter项目，项目名为myapp，其中包含一个使用<a href=\"https://material.io/guidelines/\" target=\"_blank\" rel=\"noopener noreferrer\">Material 组件<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>的简单演示应用程序。</p> <p>在项目目录中，您应用程序的代码位于 <code>lib/main.dart</code>。</p> <h4 id=\"运行应用程序\"><a href=\"#运行应用程序\" class=\"header-anchor\">#</a> 运行应用程序</h4> <ol><li><p>定位到Android Studio工具栏，如图1-3所示：</p> <p><img src=\"/assets/img/1-3.656e852b.png\" alt=\"图1-3\"></p></li> <li><p>在 <strong>target selector</strong> 中, 选择一个运行该应用的Android设备。如果没有列出可用，请选择 <strong>Tools&gt;Android&gt;AVD Manager</strong> 并在那里创建一个。</p></li> <li><p>在工具栏中点击 <strong>Run图标</strong>。</p></li> <li><p>如果一切正常, 您应该在您的设备或模拟器上会看到启动的应用程序：</p> <p><img src=\"/assets/img/1-4.801e91b2.png\" alt=\"图1-4\"></p></li></ol> <h4 id=\"体验热重载\"><a href=\"#体验热重载\" class=\"header-anchor\">#</a> 体验热重载</h4> <p>Flutter 可以通过 <em>热重载（hot reload）</em> 实现快速的开发周期，热重载就是无需重启应用程序就能实时加载修改后的代码，并且不会丢失状态。简单的对代码进行更改，然后告诉IDE或命令行工具你需要重新加载（点击reload按钮），你就会在你的设备或模拟器上看到更改。</p> <ol><li><p>打开<code>lib/main.dart</code>文件</p></li> <li><p>将字符串\n<code>'You have pushed the button this many times:'</code> 更改为\n<code>'You have clicked the button this many times:'</code></p></li> <li><p>不要按“停止”按钮; 让您的应用继续运行.</p></li> <li><p>要查更改，请调用 <strong>Save</strong> (<code>cmd-s</code> / <code>ctrl-s</code>)，或者点击 <strong>热重载按钮</strong> (带有闪电⚡️图标的按钮)。</p> <p>你会立即在运行的应用程序中看到更新的字符串。</p></li></ol> <h3 id=\"vs-code的配置与使用\"><a href=\"#vs-code的配置与使用\" class=\"header-anchor\">#</a> VS Code的配置与使用</h3> <p>VS Code是一个轻量级编辑器，支持Flutter运行和调试。</p> <h4 id=\"安装flutter插件\"><a href=\"#安装flutter插件\" class=\"header-anchor\">#</a> 安装flutter插件</h4> <ol><li>启动 VS Code。</li> <li>调用 <strong>View&gt;Command Palette…</strong>。</li> <li>输入 ‘install’, 然后选择 <strong>Extensions: Install Extension</strong> action。</li> <li>在搜索框输入 <code>flutter</code> ，在搜索结果列表中选择 ‘Flutter’, 然后点击 <strong>Install</strong>。</li> <li>选择 ‘OK’ 重新启动 VS Code。</li> <li>验证配置\n<ul><li>调用 <strong>View&gt;Command Palette…</strong></li> <li>输入 ‘doctor’, 然后选择 <strong>‘Flutter: Run Flutter Doctor’</strong> action。</li> <li>查看“OUTPUT”窗口中的输出是否有问题</li></ul></li></ol> <h4 id=\"创建flutter应用-2\"><a href=\"#创建flutter应用-2\" class=\"header-anchor\">#</a> 创建Flutter应用</h4> <ol><li>启动 VS Code</li> <li>调用 <strong>View&gt;Command Palette…</strong></li> <li>输入 ‘flutter’, 然后选择 <strong>‘Flutter: New Project’</strong> action</li> <li>输入 Project 名称 (如<code>myapp</code>), 然后按回车键</li> <li>指定放置项目的位置，然后按蓝色的确定按钮</li> <li>等待项目创建继续，并显示main.dart文件</li></ol> <h4 id=\"体验热重载-2\"><a href=\"#体验热重载-2\" class=\"header-anchor\">#</a> 体验热重载</h4> <ol><li>打开<code>lib/main.dart</code>文件。</li> <li>将字符串\n<code>'You have pushed the button this many times:'</code> 更改为\n<code>'You have clicked the button this many times:'</code>。</li> <li>不要按“停止”按钮; 让您的应用继续运行。</li> <li>要查看您的更改，请调用 <strong>Save</strong> (<code>cmd-s</code> / <code>ctrl-s</code>), 或者点击 <strong>热重载按钮</strong> (绿色圆形箭头按钮)。</li></ol> <p>你会立即在运行的应用程序中看到更新的字符串。</p> <h2 id=\"_1-3-3-连接设备运行flutter应用\"><a href=\"#_1-3-3-连接设备运行flutter应用\" class=\"header-anchor\">#</a> 1.3.3 连接设备运行Flutter应用</h2> <p>Window下只支持为Android设备构建并运行Flutter应用，而macOS同时支持iOS和Android设备。下面分别介绍如何连接Android和iOS设备来运行flutter应用。</p> <h3 id=\"连接android模拟器\"><a href=\"#连接android模拟器\" class=\"header-anchor\">#</a> 连接Android模拟器</h3> <p>要准备在Android模拟器上运行并测试Flutter应用，请按照以下步骤操作：</p> <ol><li><p>启动 <strong>Android Studio&gt;Tools&gt;Android&gt;AVD Manager</strong> 并选择 <strong>Create Virtual Device</strong>.</p></li> <li><p>选择一个设备并选择 <strong>Next</strong>。</p></li> <li><p>为要模拟的Android版本选择一个或多个系统印象，然后选择 <strong>Next</strong>. 建议使用 <em>x86</em> 或 <em>x86_64</em> image .</p></li> <li><p>在 “Emulated Performance”下, 选择 <strong>Hardware - GLES 2.0</strong> 以启用 <a href=\"https://developer.android.com/studio/run/emulator-acceleration.html\" target=\"_blank\" rel=\"noopener noreferrer\">硬件加速<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>.</p></li> <li><p>验证AVD配置是否正确，然后选择 <strong>Finish</strong>。</p> <p>有关上述步骤的详细信息，请参阅 <a href=\"https://developer.android.com/studio/run/managing-avds.html\" target=\"_blank\" rel=\"noopener noreferrer\">Managing AVDs<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>.</p></li> <li><p>在“Android Virtual Device Manager”中，点击工具栏的 <strong>Run</strong>。模拟器启动并显示所选操作系统版本或设备的启动画面。</p></li> <li><p>运行 <code>flutter run</code> 启动您的设备。 连接的设备名是 <code>Android SDK built for &lt;platform&gt;</code>，其中 <em>platform</em> 是芯片系列，如 x86。</p></li></ol> <h3 id=\"连接android真机设备\"><a href=\"#连接android真机设备\" class=\"header-anchor\">#</a> 连接Android真机设备</h3> <p>要准备在Android设备上运行并测试Flutter应用，需要Android 4.1（API level 16）或更高版本的Android设备.</p> <ol><li>在Android设备上启用 <strong>开发人员选项</strong> 和 <strong>USB调试</strong> 。详细说明可在<a href=\"https://developer.android.com/studio/debug/dev-options.html\" target=\"_blank\" rel=\"noopener noreferrer\">Android文档<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>中找到。</li> <li>使用USB将手机插入电脑。如果设备出现调试授权提示，请授权你的电脑可以访问该设备。</li> <li>在命令行运行 <code>flutter devices</code> 命令以验证Flutter识别您连接的Android设备。</li> <li>运行启动你应用程序 <code>flutter run</code>。</li></ol> <p>默认情况下，Flutter使用的Android SDK版本是基于你的 <code>adb</code> 工具版本。 如果想让Flutter使用不同版本的Android SDK，则必须将该 <code>ANDROID_HOME</code> 环境变量设置为相应的SDK安装目录。</p> <h3 id=\"连接ios模拟器\"><a href=\"#连接ios模拟器\" class=\"header-anchor\">#</a> 连接iOS模拟器</h3> <p>要准备在iOS模拟器上运行并测试Flutter应用，请按以下步骤操作：</p> <ol><li><p>在你的MAC上，通过 Spotlight 或以下命令找到模拟器：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code><span class=\"token function\">open</span> -a Simulator\n</code></pre></div></li> <li><p>通过检查模拟器 <strong>Hardware &gt; Device</strong> 菜单中的设置，确保模拟器正在使用64位设备（iPhone 5s或更高版本）。</p></li> <li><p>根据你电脑屏幕大小，模拟高清屏iOS设备可能会溢出屏幕。可以在模拟器的 <strong>Window&gt; Scale</strong> 菜单下设置设备比例。</p></li> <li><p>运行 <code>flutter run</code>启动flutter应用程序。</p></li></ol> <h3 id=\"连接ios真机设备\"><a href=\"#连接ios真机设备\" class=\"header-anchor\">#</a> 连接iOS真机设备</h3> <p>要将Flutter应用安装到iOS真机设备，需要一些额外的工具和一个Apple帐户，还需要在Xcode中进行一些设置。</p> <ol><li><p>安装 <a href=\"http://brew.sh/\" target=\"_blank\" rel=\"noopener noreferrer\">homebrew<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> （如果已经安装了brew,跳过此步骤）。</p></li> <li><p>打开终端并运行如下这些命令:</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>brew update\nbrew <span class=\"token function\">install</span> --HEAD libimobiledevice\nbrew <span class=\"token function\">install</span> ideviceinstaller ios-deploy cocoapods\npod setup\n</code></pre></div><p>如果这些命令中的任何一个失败并出现错误，请运行brew doctor并按照说明解决问题.</p></li> <li><p>遵循Xcode签名流程来配置您的项目:</p> <ul><li><p>在你Flutter项目目录中通过 <code>open ios/Runner.xcworkspace</code> 打开默认的Xcode workspace.</p></li> <li><p>在Xcode中，选择导航面板左侧中的<code>Runner</code>项目。</p></li> <li><p>在<code>Runner</code> target设置页面中，确保在 <strong>General &gt; Signing &gt; Team</strong> 下选择了你的开发团队。当你选择一个团队时，Xcode会创建并下载开发证书，向你的设备注册你的帐户，并创建和下载配置文件（如果需要）。</p></li> <li><p>要开始您的第一个iOS开发项目，您可能需要使用您的Apple ID登录Xcode，如图1-5：</p> <p><img src=\"/assets/img/1-5.cc75b912.png\" alt=\"图1-5\"></p> <p>任何Apple ID都支持开发和测试，但若想将应用分发到App Store，就必须注册Apple开发者计划，有关详情读者可以自行了解。</p></li></ul> <ol start=\"4\"><li><p>当您第一次attach真机设备进行iOS开发时，需要同时信任你的Mac和该设备上的开发证书。首次将iOS设备连接到Mac时，请在对话框中选择 <code>Trust</code>。</p> <p><img src=\"/assets/img/1-6.739453e7.png\" alt=\"添加信任\"></p> <p>然后，转到iOS设备上的<strong>设置</strong>菜单，选择 <strong>常规&gt;设备管理</strong> 并信任您的证书。</p></li> <li><p>如果Xcode中的自动签名失败，请验证项目的 <strong>General &gt; Identity &gt; Bundle Identifier</strong> 值是否唯一，如图1-7所示：</p> <p><img src=\"/assets/img/1-7.4b555c37.png\" alt=\"验证bundle id是否唯一\"></p></li> <li><p>运行 <code>flutter run</code>启动flutter应用程序。</p></li></ol></li></ol> <h2 id=\"_1-3-4-常见配置问题\"><a href=\"#_1-3-4-常见配置问题\" class=\"header-anchor\">#</a> 1.3.4 常见配置问题</h2> <h3 id=\"android-studio问题\"><a href=\"#android-studio问题\" class=\"header-anchor\">#</a> Android Studio问题</h3> <h4 id=\"缺少依赖库问题\"><a href=\"#缺少依赖库问题\" class=\"header-anchor\">#</a> 缺少依赖库问题</h4> <p>上手安卓最常遇见的问题之一，错误如图1-8所示，此时点击超链接即可自动跳转到安装页面</p> <p><img src=\"data:image/png;base64,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\" alt=\"图1-8\"></p> <p>安装之后重新运行即可，如图1-9：</p> <p><img src=\"/assets/img/1-9.69ea9a5c.png\" alt=\"install_request_components.png\"></p> <h4 id=\"连接不上android-repository\"><a href=\"#连接不上android-repository\" class=\"header-anchor\">#</a> 连接不上Android Repository</h4> <p>这也是最常见的问题之一，当你发现自己无法下载部分依赖的时候，请优先考虑这种情况。进入 <code>File</code> -&gt; <code>Settings</code> -&gt; <code>Appearance &amp; Behavior</code> -&gt; <code>System Settings</code> -&gt; <code>Android SDK</code> -&gt; <code>SDK Update Sites</code> 列表，可以看到此时的 <code>Android Repository</code> 无法连接，如图1-10所示：</p> <p><img src=\"/assets/img/1-10.a2561ece.png\" alt=\"下载依赖失败\"></p> <p>这是由于要去Google下载Android SDK，但在国内目前无法访问Google所致，因此，我们可以配置代理或使用vpn。</p> <h4 id=\"安卓包配置问题\"><a href=\"#安卓包配置问题\" class=\"header-anchor\">#</a> 安卓包配置问题</h4> <p>一般格式为</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>Could not HEAD **\nCould not Get **\n</code></pre></div><p>如：<code>Android Studio Could not GET gradle-3.2.0.pom</code></p> <p>这一类问题是由于无法连接到 Maven 库造成的，解决方法如下：</p> <ol><li><p>进入<code>当前所在项目名/android</code></p></li> <li><p>打开 <code>build.gradle</code></p></li> <li><p>找到下面这一部分，并加上 <code>maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }</code></p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>allprojects {\n    repositories {\n      google()\n      jcenter()\n      maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' } //添加这一句\n\t}\n}\n</code></pre></div></li> <li><p>进入 File/ Settings/ Build, Execution, Deployment/ BuildTools/ Gradle/ Android Studio 中，勾选上 Enable embedded Maven repository ，重启 Android Studio 即可解决。</p> <blockquote><p>**注意：**存在这样的一种情况，当你根据上述步骤设置了之后，依旧无法解决这个问题，并有类似于 <code>Could not HEAD maven.aliyun.com</code> 的报错信息，请检查 <code>C:\\Users\\{user_name}\\.gradle\\gradle.properties</code> 是否有设置代理。删除后问题即可解决。</p></blockquote></li></ol> <h4 id=\"hot-reload-热重载失效问题\"><a href=\"#hot-reload-热重载失效问题\" class=\"header-anchor\">#</a> Hot Reload 热重载失效问题</h4> <p>在给 <code>Terminal</code> 之类的终端模拟器设置代理之后，会导致“Hot Reload”重载失效，此时调用 <strong>Save</strong> (<code>cmd-s</code> / <code>ctrl-s</code>)将不会进行热重载，<strong>热重载按钮</strong> (带有闪电⚡️图标的按钮)也不会显示，将代理移除即可解决。</p> <p>另外，有些情况下热重载是不生效的，比如修改了<code>main</code>函数、修改了全局静态方法等，读者可以认为“Hot Reload”只会重新构建整个widget树，如果变动不在构建widget树的过程中，“Hot Reload”就不会起作用。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter1/flutter_intro.html\" class=\"prev\">\n        1.2 初识Flutter\n      </a></span> <span class=\"next\"><a href=\"/chapter1/dart.html\">\n        1.4 Dart语言简介\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/5.0941abdb.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter1/mobile_development_intro.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>1.1 移动开发技术简介 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/132.4b82358d.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable open\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" aria-current=\"page\" class=\"active sidebar-link\">1.1 移动开发技术简介</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/chapter1/mobile_development_intro.html#_1-1-1-原生开发与跨平台技术\" class=\"sidebar-link\">1.1.1 原生开发与跨平台技术</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter1/mobile_development_intro.html#_1-1-2-hybrid技术简介\" class=\"sidebar-link\">1.1.2 Hybrid技术简介</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter1/mobile_development_intro.html#_1-1-3-react-native、weex及快应用\" class=\"sidebar-link\">1.1.3 React Native、Weex及快应用</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter1/mobile_development_intro.html#_1-1-4-qt-mobile\" class=\"sidebar-link\">1.1.4 QT Mobile</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter1/mobile_development_intro.html#_1-1-5-flutter出世\" class=\"sidebar-link\">1.1.5 Flutter出世</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter1/mobile_development_intro.html#_1-1-6-小结\" class=\"sidebar-link\">1.1.6 小结</a></li></ul></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_1-1-移动开发技术简介\"><a href=\"#_1-1-移动开发技术简介\" class=\"header-anchor\">#</a> 1.1 移动开发技术简介</h1> <p>本节将主要介绍一下移动开发技术的进化历程，主要是想让读者知道Flutter技术出现的背景。笔者认为，了解一门新技术出现的背景是非常重要的，因为只有了解之前是什么样的，才能理解为什么会是现在这样。</p> <h2 id=\"_1-1-1-原生开发与跨平台技术\"><a href=\"#_1-1-1-原生开发与跨平台技术\" class=\"header-anchor\">#</a> 1.1.1 原生开发与跨平台技术</h2> <h3 id=\"原生开发\"><a href=\"#原生开发\" class=\"header-anchor\">#</a> 原生开发</h3> <p>原生应用程序是指某一个移动平台（比如iOS或安卓）所特有的应用，使用相应平台支持的开发工具和语言，并直接调用系统提供的SDK API。比如Android原生应用就是指使用Java或Kotlin语言直接调用Android SDK开发的应用程序；而iOS原生应用就是指通过Objective-C或Swift语言直接调用iOS SDK开发的应用程序。原生开发有以下主要优势：</p> <ul><li>可访问平台全部功能（GPS、摄像头）；</li> <li>速度快、性能高、可以实现复杂动画及绘制，整体用户体验好；</li></ul> <p>主要缺点：</p> <ul><li>平台特定，开发成本高；不同平台必须维护不同代码，人力成本随之变大；</li> <li>内容固定，动态化弱，大多数情况下，有新功能更新时只能发版；</li></ul> <p>在移动互联网发展初期，业务场景并不复杂，原生开发还可以应对产品需求迭代。 但近几年，随着物联网时代到来、移动互联网高歌猛进，日新月异，在很多业务场景中，传统的纯原生开发已经不能满足日益增长的业务需求。主要表现在：</p> <ul><li>动态化内容需求增大；当需求发生变化时，纯原生应用需要通过版本升级来更新内容，但应用上架、审核是需要周期的，这对高速变化的互联网时代来说是很难接受的，所以，对应用动态化(不发版也可以更新应用内容)的需求就变的迫在眉睫。</li> <li>业务需求变化快，开发成本变大；由于原生开发一般都要维护Android、iOS两个开发团队，版本迭代时，无论人力成本，还是测试成本都会变大。</li></ul> <p>总结一下，纯原生开发主要面临动态化和开发成本两个问题，而针对这两个问题，诞生了一些跨平台的动态化框架。</p> <h3 id=\"跨平台技术简介\"><a href=\"#跨平台技术简介\" class=\"header-anchor\">#</a> 跨平台技术简介</h3> <p>针对原生开发面临问题，人们一直都在努力寻找好的解决方案，而时至今日，已经有很多跨平台框架(注意，本书中所指的“跨平台”若无特殊说明，即特指Android和iOS两个平台)，根据其原理，主要分为三类：</p> <ul><li>H5+原生（Cordova、Ionic、微信小程序）</li> <li>JavaScript开发+原生渲染 （React Native、Weex、快应用）</li> <li>自绘UI+原生(QT for mobile、Flutter)</li></ul> <p>在接下来的章节中我们逐个来看看这三类框架的原理及优缺点。</p> <h2 id=\"_1-1-2-hybrid技术简介\"><a href=\"#_1-1-2-hybrid技术简介\" class=\"header-anchor\">#</a> 1.1.2 Hybrid技术简介</h2> <h3 id=\"h5-原生混合开发\"><a href=\"#h5-原生混合开发\" class=\"header-anchor\">#</a> H5+原生混合开发</h3> <p>这类框架主要原理就是将APP的一部分需要动态变动的内容通过H5来实现，通过原生的网页加载控件WebView (Android)或WKWebView（iOS）来加载（以后若无特殊说明，我们用WebView来统一指代android和iOS中的网页加载控件）。这样以来，H5部分是可以随时改变而不用发版，动态化需求能满足；同时，由于h5代码只需要一次开发，就能同时在Android和iOS两个平台运行，这也可以减小开发成本，也就是说，H5部分功能越多，开发成本就越小。我们称这种h5+原生的开发模式为<strong>混合开发 ** ，采用混合模式开发的APP我们称之为</strong>混合应用<strong>或</strong>Hybrid APP**  ，如果一个应用的大多数功能都是H5实现的话，我们称其为<strong>Web APP</strong> 。</p> <p>目前混合开发框架的典型代表有：Cordova、Ionic 和微信小程序，值得一提的是微信小程序目前是在webview中渲染的，并非原生渲染，但将来有可能会采用原生渲染。</p> <h3 id=\"混合开发技术点\"><a href=\"#混合开发技术点\" class=\"header-anchor\">#</a> 混合开发技术点</h3> <p>如之前所述，原生开发可以访问平台所有功能，而混合开发中，H5代码是运行在WebView中，而WebView实质上就是一个浏览器内核，其JavaScript依然运行在一个权限受限的沙箱中，所以对于大多数系统能力都没有访问权限，如无法访问文件系统、不能使用蓝牙等。所以，对于H5不能实现的功能，都需要原生去做。而混合框架一般都会在原生代码中预先实现一些访问系统能力的API， 然后暴露给WebView以供JavaScript调用，这样一来，WebView就成为了JavaScript与原生API之间通信的桥梁，主要负责JavaScript与原生之间传递调用消息，而消息的传递必须遵守一个标准的协议，它规定了消息的格式与含义，我们把依赖于WebView的用于在JavaScript与原生之间通信并实现了某种消息传输协议的工具称之为<strong>WebView JavaScript Bridge</strong>, 简称 <strong>JsBridge</strong>，它也是混合开发框架的核心。</p> <h4 id=\"示例-javascript调用原生api获取手机型号\"><a href=\"#示例-javascript调用原生api获取手机型号\" class=\"header-anchor\">#</a> 示例：JavaScript调用原生API获取手机型号</h4> <p>下面我们以Android为例，实现一个获取手机型号的原生API供JavaScript调用。在这个示例中将展示JavaScript调用原生API的流程，读者可以直观的感受一下调用流程。我们选用笔者在Github上开源的dsBridge作为JsBridge来进行通信。dsBridge是一个支持同步调用的跨平台的JsBridge，此示例中只使用其同步调用功能。</p> <ol><li><p>首先在原生中实现获取手机型号的API <code>getPhoneModel</code></p> <div class=\"language-java extra-class\"><pre class=\"language-java\"><code><span class=\"token keyword\">class</span> JSAPI <span class=\"token punctuation\">{</span>\n <span class=\"token annotation punctuation\">@JavascriptInterface</span>\n <span class=\"token keyword\">public</span> <span class=\"token class-name\">Object</span> <span class=\"token function\">getPhoneModel</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">Object</span> msg<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n   <span class=\"token keyword\">return</span> <span class=\"token class-name\">Build</span><span class=\"token punctuation\">.</span>MODEL<span class=\"token punctuation\">;</span>\n <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>    \n</code></pre></div></li> <li><p>将原生API通过WebView注册到JsBridge中</p> <div class=\"language-java extra-class\"><pre class=\"language-java\"><code><span class=\"token keyword\">import</span> <span class=\"token namespace\">wendu<span class=\"token punctuation\">.</span>dsbridge<span class=\"token punctuation\">.</span></span><span class=\"token class-name\">DWebView</span>\n<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token comment\">//DWebView继承自WebView，由dsBridge提供  </span>\n<span class=\"token class-name\">DWebView</span> dwebView <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token class-name\">DWebView</span><span class=\"token punctuation\">)</span> <span class=\"token function\">findViewById</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">R</span><span class=\"token punctuation\">.</span>id<span class=\"token punctuation\">.</span>dwebview<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">//注册原生API到JsBridge</span>\ndwebView<span class=\"token punctuation\">.</span><span class=\"token function\">addJavascriptObject</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">JsAPI</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div></li> <li><p>在JavaScript中调用原生API</p> <div class=\"language-javascript extra-class\"><pre class=\"language-javascript\"><code><span class=\"token keyword\">var</span> dsBridge <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;dsbridge&quot;</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//直接调用原生API `getPhoneModel`</span>\n<span class=\"token keyword\">var</span> model <span class=\"token operator\">=</span> <span class=\"token function\">dsBridge</span><span class=\"token punctuation\">.</span><span class=\"token function\">call</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;getPhoneModel&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">//打印机型</span>\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>model<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div></li></ol> <p>上面示例演示了JavaScript调用原生API的过程，同样的，一般来说优秀的JsBridge也支持原生调用JavaScript，dsBridge也是支持的，如果您感兴趣，可以去github dsBridge项目主页查看。</p> <p>现在，我们回头来看一下，混合应用无非就是在第一步中预先实现一系列API供JavaScript调用，让JavaScript有访问系统的能力，看到这里，我相信你也可以自己实现一个混合开发框架了。</p> <h3 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h3> <p>混合应用的优点是动态内容是H5，web技术栈，社区及资源丰富，缺点是性能不好，对于复杂用户界面或动画，WebView不堪重任。</p> <h2 id=\"_1-1-3-react-native、weex及快应用\"><a href=\"#_1-1-3-react-native、weex及快应用\" class=\"header-anchor\">#</a> 1.1.3 React Native、Weex及快应用</h2> <p>本篇主要介绍一下 <strong>JavaScript开发+原生渲染</strong>的跨平台框架原理。</p> <p>React Native (简称RN)是Facebook于2015年4月开源的跨平台移动应用开发框架，是Facebook早先开源的JS框架 React 在原生移动应用平台的衍生产物，目前支持iOS和Android两个平台。RN使用Javascript语言，类似于HTML的JSX，以及CSS来开发移动应用，因此熟悉Web前端开发的技术人员只需很少的学习就可以进入移动应用开发领域。</p> <p>由于RN和React原理相通，并且Flutter也是受React启发，很多思想也都是相通的，万丈高楼平地起，我们有必要深入了解一下React原理。React是一个响应式的Web框架，我们先了解一下两个重要的概念：DOM树与响应式编程。</p> <h3 id=\"dom树与控件树\"><a href=\"#dom树与控件树\" class=\"header-anchor\">#</a> DOM树与控件树</h3> <p>文档对象模型（Document Object Model，简称DOM），是W3C组织推荐的处理可扩展标志语言的标准编程接口，一种独立于平台和语言的方式访问和修改一个文档的内容和结构。换句话说，这是表示和处理一个HTML或XML文档的标准接口。简单来说，DOM就是文档树，与用户界面控件树对应，在前端开发中通常指HTML对应的渲染树，但广义的DOM也可以指Android中的XML布局文件对应的控件树，而术语<strong>DOM操作</strong>就是指直接来操作渲染树（或控件树）， 因此，可以看到其实DOM树和控件树是等价的概念，只不过前者常用于Web开发中，而后者常用于原生开发中。</p> <h3 id=\"响应式编程\"><a href=\"#响应式编程\" class=\"header-anchor\">#</a> 响应式编程</h3> <p>React中提出一个重要思想：状态改变则UI随之自动改变，而React框架本身就是响应用户状态改变的事件而执行重新构建用户界面的工作，这就是典型的<strong>响应式</strong>编程范式，下面我们总结一下React中响应式原理：</p> <ul><li>开发者只需关注状态转移（数据），当状态发生变化，React框架会自动根据新的状态重新构建UI。</li> <li>React框架在接收到用户状态改变通知后，会根据当前渲染树，结合最新的状态改变，通过Diff算法，计算出树中变化的部分，然后只更新变化的部分（DOM操作），从而避免整棵树重构，提高性能。</li></ul> <p>值得注意的是，在第二步中，状态变化后React框架并不会立即去计算并渲染DOM树的变化部分，相反，React会在DOM的基础上建立一个抽象层，即<strong>虚拟DOM</strong>树，对数据和状态所做的任何改动，都会被自动且高效的同步到虚拟DOM，最后再批量同步到真实DOM中，而不是每次改变都去操作一下DOM。为什么不能每次改变都直接去操作DOM树？这是因为在浏览器中每一次DOM操作都有可能引起浏览器的重绘或回流：</p> <ol><li>如果DOM只是外观风格发生变化，如颜色变化，会导致浏览器重绘界面。</li> <li>如果DOM树的结构发生变化，如尺寸、布局、节点隐藏等导致，浏览器就需要回流（及重新排版布局）。</li></ol> <p>而浏览器的重绘和回流都是比较昂贵的操作，如果每一次改变都直接对DOM进行操作，这会带来性能问题，而批量操作只会触发一次DOM更新。</p> <blockquote><p>思考题：Diff操作和DOM批量更新难道不应该是浏览器的职责吗？第三方框架中去做合不合适？</p></blockquote> <blockquote><p>此处需要有一张插图</p></blockquote> <h3 id=\"react-native\"><a href=\"#react-native\" class=\"header-anchor\">#</a> React Native</h3> <p>上文已经提到React Native 是React 在原生移动应用平台的衍生产物，那两者主要的区别是什么呢？其实，主要的区别在于虚拟DOM映射的对象是什么？React中虚拟DOM最终会映射为浏览器DOM树，而RN中虚拟DOM会通过 JavaScriptCore 映射为原生控件树。</p> <p>JavaScriptCore 是一个JavaScript解释器，它在React Native中主要有两个作用：</p> <ol><li>为JavaScript提供运行环境。</li> <li>是JavaScript与原生应用之间通信的桥梁，作用和JsBridge一样，事实上，在iOS中，很多JsBridge的实现都是基于 JavaScriptCore 。</li></ol> <p>而RN中将虚拟DOM映射为原生控件的过程中分两步：</p> <ol><li>布局消息传递； 将虚拟DOM布局信息传递给原生；</li> <li>原生根据布局信息通过对应的原生控件渲染控件树；</li></ol> <p>至此，React Native 便实现了跨平台。 相对于混合应用，由于React Native是原生控件渲染，所以性能会比混合应用中H5好很多，同时React Native使用了Web开发技术栈，也只需维护一份代码，同样是跨平台框架。</p> <h3 id=\"weex\"><a href=\"#weex\" class=\"header-anchor\">#</a> Weex</h3> <p>Weex是阿里巴巴于2016年发布的跨平台移动端开发框架，思想及原理和React Native类似，最大的不同是语法层面，Weex支持Vue语法和Rax语法，Rax 的 DSL(Domain Specific Language) 语法是基于 React JSX 语法而创造。与 React 不同，在 Rax 中 JSX 是必选的，它不支持通过其它方式创建组件，所以学习 JSX 是使用 Rax 的必要基础。而React Native只支持JSX语法。</p> <h3 id=\"快应用\"><a href=\"#快应用\" class=\"header-anchor\">#</a> 快应用</h3> <p>快应用是华为、小米、OPPO、魅族等国内9大主流手机厂商共同制定的轻量级应用标准，目标直指微信小程序。它也是采用JavaScript语言开发，原生控件渲染，与React Native和Weex相比主要有两点不同：</p> <ol><li>快应用自身不支持Vue或React语法，其采用原生JavaScript开发，其开发框架和微信小程序很像，值得一提的是小程序目前已经可以使用Vue语法开发（mpvue），从原理上来讲，Vue的语法也可以移植到快应用上。</li> <li>React Native和Weex的渲染/排版引擎是集成到框架中的，每一个APP都需要打包一份，安装包体积较大；而快应用渲染/排版引擎是集成到ROM中的，应用中无需打包，安装包体积小，正因如此，快应用才能在保证性能的同时做到快速分发。</li></ol> <h3 id=\"总结-2\"><a href=\"#总结-2\" class=\"header-anchor\">#</a> 总结</h3> <p>JavaScript开发+原生渲染的方式主要优点如下：</p> <ol><li>采用Web开发技术栈，社区庞大、上手快、开发成本相对较低。</li> <li>原生渲染，性能相比H5提高很多。</li> <li>动态化较好，支持热更新。</li></ol> <p>不足：</p> <ol><li>渲染时需要JavaScript和原生之间通信，在有些场景如拖动可能会因为通信频繁导致卡顿。</li> <li>JavaScript为脚本语言，执行时需要JIT(Just In Time)，执行效率和AOT(Ahead Of Time)代码仍有差距。</li> <li>由于渲染依赖原生控件，不同平台的控件需要单独维护，并且当系统更新时，社区控件可能会滞后；除此之外，其控件系统也会受到原生UI系统限制，例如，在Android中，手势冲突消歧规则是固定的，这在使用不同人写的控件嵌套时，手势冲突问题将会变得非常棘手。</li></ol> <h2 id=\"_1-1-4-qt-mobile\"><a href=\"#_1-1-4-qt-mobile\" class=\"header-anchor\">#</a> 1.1.4 QT Mobile</h2> <p>在本篇中，我们看看最后一种跨平台技术：自绘UI+原生。这种技术的思路是，通过在不同平台实现一个统一接口的渲染引擎来绘制UI，而不依赖系统原生控件，所以可以做到不同平台UI的一致性。注意，自绘引擎解决的是UI的跨平台问题，如果涉及其它系统能力调用，依然要涉及原生开发。这种平台技术的优点如下：</p> <ol><li><p>性能高；由于自绘引擎是直接调用系统API来绘制UI，所以性能和原生控件接近。</p></li> <li><p>灵活、组件库易维护、UI外观保真度和一致性高；由于UI渲染不依赖原生控件，也就不需要根据不同平台的控件单独维护一套组件库，所以代码容易维护。由于组件库是同一套代码、同一个渲染引擎，所以在不同平台，组件显示外观可以做到高保真和高一致性；另外，由于不依赖原生控件，也就不会受原生布局系统的限制，这样布局系统会非常灵活。</p></li></ol> <p>不足：</p> <ol><li>动态性不足；为了保证UI绘制性能，自绘UI系统一般都会采用AOT模式编译其发布包，所以应用发布后，不能像Hybrid和RN那些使用JavaScript（JIT）作为开发语言的框架那样动态下发代码。</li> <li>开发效率低：QT使用C++作为其开发语言，而编程效率是直接会影响APP开发效率的，C++作为一门静态语言，在UI开发方面灵活性不及JavaScript这样的动态语言，另外，C++需要开发者手动去管理内存分配，没有JavaScript及Java中垃圾回收（GC）的机制。</li></ol> <p>也许你已经猜到Flutter就属于这一类跨平台技术，没错，Flutter正是实现一套自绘引擎，并拥有一套自己的UI布局系统。不过，自绘制引擎的思路并不是什么新概念，Flutter并不是第一个尝试这么做的，在它之前有一个典型的代表，即大名鼎鼎的QT。</p> <h3 id=\"qt简介\"><a href=\"#qt简介\" class=\"header-anchor\">#</a> QT简介</h3> <p>Qt是一个1991年由Qt Company开发的跨平台C++图形用户界面应用程序开发框架。2008年，Qt Company科技被诺基亚公司收购，Qt也因此成为诺基亚旗下的编程语言工具。2012年，Qt被Digia收购。2014年4月，跨平台集成开发环境Qt Creator 3.1.0正式发布，实现了对于iOS的完全支持，新增WinRT、Beautifier等插件，废弃了无Python接口的GDB调试支持，集成了基于Clang的C/C++代码模块，并对Android支持做出了调整，至此实现了全面支持iOS、Android、WP，它提供给应用程序开发者构建图形用户界面所需的所有功能。但是，QT虽然在PC端获得了巨大成功，备受社区追捧，然而其在移动端却表现不佳，在近几年，虽然偶尔能听到QT的声音，但一直很弱，无论QT本身技术如何、设计思想如何，但事实上终究是败了，究其原因，笔者认为主要有四：</p> <p>第一：QT移动开发社区太小，学习资料不足，生态不好。</p> <p>第二：官方推广不利，支持不够。</p> <p>第三：移动端发力较晚，市场已被其它动态化框架占领（Hybrid和RN)。</p> <p>第四：在移动开发中，C++开发和Web开发栈相比有着先天的劣势，直接结果就是QT开发效率太低。</p> <p>基于此四点，尽管QT是移动端开发跨平台自绘引擎的先驱，但却成为了烈士。</p> <h2 id=\"_1-1-5-flutter出世\"><a href=\"#_1-1-5-flutter出世\" class=\"header-anchor\">#</a> 1.1.5 Flutter出世</h2> <p>“千呼万唤始出来”，铺垫这么久，现在终于等到本书的主角出场了！</p> <p>Flutter是Google发布的一个用于创建跨平台、高性能移动应用的框架。Flutter和QT mobile一样，都没有使用原生控件，相反都实现了一个自绘引擎，使用自身的布局、绘制系统。那么，我们会担心，QT mobile面对的问题Flutter是否也一样，Flutter会不会步入QT mobile后尘，成为另一个烈士？要回到这个问题，我们先来看看Flutter诞生过程：</p> <ul><li>2017 年 Google I/O 大会上，Google 首次推出了一款新的用于创建跨平台、高性能的移动应用框架——Flutter。</li> <li>2018年2月，Flutter发布了第一个Beta版本，同年五月， 在2018年Google I/O 大会上，Flutter 更新到了 beta 3 版本。</li> <li>2018年6月，Flutter发布了首个预览版本，这意味着 Flutter 进入了正式版（1.0）发布前的最后阶段。</li></ul> <p>观其发展，在2018年5月份，Flutter 进入了 GitHub stars 排行榜前 100 名，已有 27k  star。而今天(2019年5月29日)，已经有65K的Star。经历了短短2年多的时间，Flutter 生态系统得以快速增长，由此可见，Flutter在开发者中受到了热烈的欢迎，其未来发展值得期待！</p> <p>现在，我们来和QT mobile做一个对比：</p> <ol><li>生态：从Github上来看，目前Flutter活跃用户正在高速增长。从Stackoverflow上提问来看，Flutter社区现在已经很庞大。Flutter的文档、资源也越来越丰富，开发过程中遇到的很多问题都可以在Stackoverflow或其github issue中找到答案。</li> <li>技术支持：现在Google正在大力推广Flutter，Flutter的作者中很多人都是来自Chromium团队，并且github上活跃度很高。另一个角度，从今年上半年Flutter频繁的版本发布也可以看出Google对Flutter的投入的资源不小，所以在官方技术支持这方面，大可不必担心。</li> <li>开发效率：Flutter的热重载可帮助开发者快速地进行测试、构建UI、添加功能并更快地修复错误。在iOS和Android模拟器或真机上可以实现毫秒级热重载，并且不会丢失状态。这真的很棒，相信我，如果你是一名原生开发者，体验了Flutter开发流后，很可能就不想重新回去做原生了，毕竟很少有人不吐槽原生开发的编译速度。</li></ol> <p>基于以上三点，相信读者和笔者一样，Flutter未来如何，心中自有定论。到现在为止，我们已经对移动端开发技术有了一个全面的了解，接下来我们便要进入本书的主题，你准备好了吗！</p> <h2 id=\"_1-1-6-小结\"><a href=\"#_1-1-6-小结\" class=\"header-anchor\">#</a> 1.1.6 小结</h2> <p>本章主要介绍了目前移动开发中三种跨平台技术，现在我们从框架角度对比一下它们，如表1-1所示：</p> <table><thead><tr><th>技术类型</th> <th>UI渲染方式</th> <th>性能</th> <th>开发效率</th> <th>动态化</th> <th>框架代表</th></tr></thead> <tbody><tr><td>H5+原生</td> <td>WebView渲染</td> <td>一般</td> <td>高</td> <td>支持</td> <td>Cordova、Ionic</td></tr> <tr><td>JavaScript+原生渲染</td> <td>原生控件渲染</td> <td>好</td> <td>中</td> <td>支持</td> <td>RN、Weex</td></tr> <tr><td>自绘UI+原生</td> <td>调用系统API渲染</td> <td>好</td> <td>Flutter高, QT低</td> <td>默认不支持</td> <td>QT、Flutter</td></tr></tbody></table> <center>表1-1: 跨平台技术对比</center>\n上表中开发语言主要指UI的开发语言。而开发效率，是指整个开发周期的效率，包括编码时间、调试时间、以及排错、兼容时间。动态化主要指是否支持动态下发代码和是否支持热更新。值得注意的是Flutter的Release包默认是使用Dart AOT模式编译的，所以不支持动态化，但Dart还有JIT或snapshot运行方式，这些模式都是支持动态化的。\n</div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/\" class=\"prev router-link-active\">\n        首页\n      </a></span> <span class=\"next\"><a href=\"/chapter1/flutter_intro.html\">\n        1.2 初识Flutter\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/132.4b82358d.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter10/combine.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>10.2 组合现有组件 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/82.a6b89300.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable open\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" aria-current=\"page\" class=\"active sidebar-link\">10.2 组合现有组件</a><ul class=\"sidebar-sub-headers\"></ul></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_10-2-组合现有组件\"><a href=\"#_10-2-组合现有组件\" class=\"header-anchor\">#</a> 10.2 组合现有组件</h1> <p>在Flutter中页面UI通常都是由一些低阶别的组件组合而成，当我们需要封装一些通用组件时，应该首先考虑是否可以通过组合其它组件来实现，如果可以，则应优先使用组合，因为直接通过现有组件拼装会非常简单、灵活、高效。</p> <h3 id=\"示例-自定义渐变按钮\"><a href=\"#示例-自定义渐变按钮\" class=\"header-anchor\">#</a> 示例：自定义渐变按钮</h3> <p>Flutter Material组件库中的按钮默认不支持渐变背景，为了实现渐变背景按钮，我们自定义一个<code>GradientButton</code>组件，它需要支持一下功能：</p> <ol><li>背景支持渐变色</li> <li>手指按下时有涟漪效果</li> <li>可以支持圆角</li></ol> <p>我们先来看看最终要实现的效果（图10-1）：</p> <p><img src=\"/assets/img/10-1.f2135ea6.png\" alt=\"gradient-button\"></p> <p>我们<code>DecoratedBox</code>可以支持背景色渐变和圆角，<code>InkWell</code>在手指按下有涟漪效果，所以我们可以通过组合<code>DecoratedBox</code>和<code>InkWell</code>来实现<code>GradientButton</code>，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">GradientButton</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">GradientButton</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>colors<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>width<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>height<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>onPressed<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>borderRadius<span class=\"token punctuation\">,</span>\n    <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// 渐变色数组</span>\n  <span class=\"token keyword\">final</span> List<span class=\"token operator\">&lt;</span>Color<span class=\"token operator\">&gt;</span> colors<span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// 按钮宽高</span>\n  <span class=\"token keyword\">final</span> double width<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> double height<span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> Widget child<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> BorderRadius borderRadius<span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">//点击回调</span>\n  <span class=\"token keyword\">final</span> GestureTapCallback onPressed<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    ThemeData theme <span class=\"token operator\">=</span> Theme<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token comment\">//确保colors数组不空</span>\n    List<span class=\"token operator\">&lt;</span>Color<span class=\"token operator\">&gt;</span> _colors <span class=\"token operator\">=</span> colors <span class=\"token operator\">?</span><span class=\"token operator\">?</span>\n        <span class=\"token punctuation\">[</span>theme<span class=\"token punctuation\">.</span>primaryColor<span class=\"token punctuation\">,</span> theme<span class=\"token punctuation\">.</span>primaryColorDark <span class=\"token operator\">?</span><span class=\"token operator\">?</span> theme<span class=\"token punctuation\">.</span>primaryColor<span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">return</span> <span class=\"token function\">DecoratedBox</span><span class=\"token punctuation\">(</span>\n      decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>\n        gradient<span class=\"token punctuation\">:</span> <span class=\"token function\">LinearGradient</span><span class=\"token punctuation\">(</span>colors<span class=\"token punctuation\">:</span> _colors<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        borderRadius<span class=\"token punctuation\">:</span> borderRadius<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Material</span><span class=\"token punctuation\">(</span>\n        type<span class=\"token punctuation\">:</span> MaterialType<span class=\"token punctuation\">.</span>transparency<span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">InkWell</span><span class=\"token punctuation\">(</span>\n          splashColor<span class=\"token punctuation\">:</span> _colors<span class=\"token punctuation\">.</span>last<span class=\"token punctuation\">,</span>\n          highlightColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>transparent<span class=\"token punctuation\">,</span>\n          borderRadius<span class=\"token punctuation\">:</span> borderRadius<span class=\"token punctuation\">,</span>\n          onTap<span class=\"token punctuation\">:</span> onPressed<span class=\"token punctuation\">,</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">ConstrainedBox</span><span class=\"token punctuation\">(</span>\n            constraints<span class=\"token punctuation\">:</span> BoxConstraints<span class=\"token punctuation\">.</span><span class=\"token function\">tightFor</span><span class=\"token punctuation\">(</span>height<span class=\"token punctuation\">:</span> height<span class=\"token punctuation\">,</span> width<span class=\"token punctuation\">:</span> width<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n                padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">8.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                child<span class=\"token punctuation\">:</span> <span class=\"token function\">DefaultTextStyle</span><span class=\"token punctuation\">(</span>\n                  style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>fontWeight<span class=\"token punctuation\">:</span> FontWeight<span class=\"token punctuation\">.</span>bold<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  child<span class=\"token punctuation\">:</span> child<span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以看到<code>GradientButton</code>是由<code>DecoratedBox</code>、<code>Padding</code>、<code>Center</code>、<code>InkWell</code>等组件组合而成。当然上面的代码只是一个示例，作为一个按钮它还并不完整，比如没有禁用状态，读者可以根据实际需要来完善。</p> <h4 id=\"使用gradientbutton\"><a href=\"#使用gradientbutton\" class=\"header-anchor\">#</a> 使用GradientButton</h4> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'../widgets/index.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">GradientButtonRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _GradientButtonRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">_GradientButtonRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_GradientButtonRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>GradientButtonRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n        children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n          <span class=\"token function\">GradientButton</span><span class=\"token punctuation\">(</span>\n            colors<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>Colors<span class=\"token punctuation\">.</span>orange<span class=\"token punctuation\">,</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n            height<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Submit&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            onPressed<span class=\"token punctuation\">:</span> onTap<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">GradientButton</span><span class=\"token punctuation\">(</span>\n            height<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n            colors<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>Colors<span class=\"token punctuation\">.</span>lightGreen<span class=\"token punctuation\">,</span> Colors<span class=\"token punctuation\">.</span>green<span class=\"token punctuation\">[</span><span class=\"token number\">700</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Submit&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            onPressed<span class=\"token punctuation\">:</span> onTap<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">GradientButton</span><span class=\"token punctuation\">(</span>\n            height<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n            colors<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>Colors<span class=\"token punctuation\">.</span>lightBlue<span class=\"token punctuation\">[</span><span class=\"token number\">300</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> Colors<span class=\"token punctuation\">.</span>blueAccent<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Submit&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            onPressed<span class=\"token punctuation\">:</span> onTap<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token function\">onTap</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;button click&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h3 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h3> <p>通过组合的方式定义组件和我们之前写界面并无差异，不过在抽离出单独的组件时我们要考虑代码规范性，如必要参数要用<code>@required</code> 标注，对于可选参数在特定场景需要判空或设置默认值等。这是由于使用者大多时候可能不了解组件的内部细节，所以为了保证代码健壮性，我们需要在用户错误地使用组件时能够兼容或报错提示（使用<code>assert</code>断言函数）。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter10/intro.html\" class=\"prev\">\n        10.1 自定义组件方法简介\n      </a></span> <span class=\"next\"><a href=\"/chapter10/turn_box.html\">\n        10.3 组合实例：TurnBox\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/82.a6b89300.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter10/custom_paint.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>10.4 自绘组件 （CustomPaint与Canvas） | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/83.daf1b549.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable open\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" aria-current=\"page\" class=\"active sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a><ul class=\"sidebar-sub-headers\"></ul></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_10-4-自绘组件-custompaint与canvas\"><a href=\"#_10-4-自绘组件-custompaint与canvas\" class=\"header-anchor\">#</a> 10.4 自绘组件 （CustomPaint与Canvas）</h1> <p>对于一些复杂或不规则的UI，我们可能无法通过组合其它组件的方式来实现，比如我们需要一个正六边形、一个渐变的圆形进度条、一个棋盘等。当然，有时候我们可以使用图片来实现，但在一些需要动态交互的场景静态图片也是实现不了的，比如要实现一个手写输入面板，这时，我们就需要来自己绘制UI外观。</p> <p>几乎所有的UI系统都会提供一个自绘UI的接口，这个接口通常会提供一块2D画布<code>Canvas</code>，<code>Canvas</code>内部封装了一些基本绘制的API，开发者可以通过<code>Canvas</code>绘制各种自定义图形。在Flutter中，提供了一个<code>CustomPaint</code> 组件，它可以结合画笔<code>CustomPainter</code>来实现自定义图形绘制。</p> <h3 id=\"custompaint\"><a href=\"#custompaint\" class=\"header-anchor\">#</a> CustomPaint</h3> <p>我们看看<code>CustomPaint</code>构造函数：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">CustomPaint</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  Key key<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>painter<span class=\"token punctuation\">,</span> \n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>foregroundPainter<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>size <span class=\"token operator\">=</span> Size<span class=\"token punctuation\">.</span>zero<span class=\"token punctuation\">,</span> \n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>isComplex <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span> \n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>willChange <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span> \n  Widget child<span class=\"token punctuation\">,</span> <span class=\"token comment\">//子节点，可以为空</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><ul><li><code>painter</code>: 背景画笔，会显示在子节点后面;</li> <li><code>foregroundPainter</code>: 前景画笔，会显示在子节点前面</li> <li><code>size</code>：当child为null时，代表默认绘制区域大小，如果有child则忽略此参数，画布尺寸则为child尺寸。如果有child但是想指定画布为特定大小，可以使用SizeBox包裹CustomPaint实现。</li> <li><code>isComplex</code>：是否复杂的绘制，如果是，Flutter会应用一些缓存策略来减少重复渲染的开销。</li> <li><code>willChange</code>：和<code>isComplex</code>配合使用，当启用缓存时，该属性代表在下一帧中绘制是否会改变。</li></ul> <p>可以看到，绘制时我们需要提供前景或背景画笔，两者也可以同时提供。我们的画笔需要继承<code>CustomPainter</code>类，我们在画笔类中实现真正的绘制逻辑。</p> <h4 id=\"注意\"><a href=\"#注意\" class=\"header-anchor\">#</a> 注意</h4> <p>如果<code>CustomPaint</code>有子节点，为了避免子节点不必要的重绘并提高性能，通常情况下都会将子节点包裹在<code>RepaintBoundary</code>组件中，这样会在绘制时就会创建一个新的绘制层（Layer），其子组件将在新的Layer上绘制，而父组件将在原来Layer上绘制，也就是说<code>RepaintBoundary</code> 子组件的绘制将独立于父组件的绘制，<code>RepaintBoundary</code>会隔离其子节点和<code>CustomPaint</code>本身的绘制边界。示例如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">CustomPaint</span><span class=\"token punctuation\">(</span>\n  size<span class=\"token punctuation\">:</span> <span class=\"token function\">Size</span><span class=\"token punctuation\">(</span><span class=\"token number\">300</span><span class=\"token punctuation\">,</span> <span class=\"token number\">300</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//指定画布大小</span>\n  painter<span class=\"token punctuation\">:</span> <span class=\"token function\">MyPainter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">RepaintBoundary</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">:</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> \n<span class=\"token punctuation\">)</span>\n</code></pre></div><h3 id=\"custompainter\"><a href=\"#custompainter\" class=\"header-anchor\">#</a> CustomPainter</h3> <p><code>CustomPainter</code>中提定义了一个虚函数<code>paint</code>：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>void paint(Canvas canvas, Size size);\n</code></pre></div><p><code>paint</code>有两个参数:</p> <ul><li><p><code>Canvas</code>：一个画布，包括各种绘制方法，我们列出一下常用的方法：</p> <table><thead><tr><th>API名称</th> <th>功能</th></tr></thead> <tbody><tr><td>drawLine</td> <td>画线</td></tr> <tr><td>drawPoint</td> <td>画点</td></tr> <tr><td>drawPath</td> <td>画路径</td></tr> <tr><td>drawImage</td> <td>画图像</td></tr> <tr><td>drawRect</td> <td>画矩形</td></tr> <tr><td>drawCircle</td> <td>画圆</td></tr> <tr><td>drawOval</td> <td>画椭圆</td></tr> <tr><td>drawArc</td> <td>画圆弧</td></tr></tbody></table></li> <li><p><code>Size</code>：当前绘制区域大小。</p></li></ul> <h3 id=\"画笔paint\"><a href=\"#画笔paint\" class=\"header-anchor\">#</a> 画笔Paint</h3> <p>现在画布有了，我们最后还缺一个画笔，Flutter提供了<code>Paint</code>类来实现画笔。在<code>Paint</code>中，我们可以配置画笔的各种属性如粗细、颜色、样式等。如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">var</span> paint <span class=\"token operator\">=</span> <span class=\"token function\">Paint</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//创建一个画笔并配置其属性</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>isAntiAlias <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span> <span class=\"token comment\">//是否抗锯齿</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>style <span class=\"token operator\">=</span> PaintingStyle<span class=\"token punctuation\">.</span>fill <span class=\"token comment\">//画笔样式：填充</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>color<span class=\"token operator\">=</span><span class=\"token function\">Color</span><span class=\"token punctuation\">(</span><span class=\"token number\">0x77cdb175</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span><span class=\"token comment\">//画笔颜色</span>\n</code></pre></div><p>更多的配置属性读者可以参考Paint类定义。</p> <h3 id=\"示例-五子棋-盘\"><a href=\"#示例-五子棋-盘\" class=\"header-anchor\">#</a> 示例：五子棋/盘</h3> <p>下面我们通过一个五子棋游戏中棋盘和棋子的绘制来演示自绘UI的过程，首先我们看一下我们的目标效果，如图10-3所示：</p> <p><img src=\"/assets/img/10-3.3989fea9.png\" alt=\"图10-3\"></p> <p>代码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'dart:math'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">CustomPaintRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">CustomPaint</span><span class=\"token punctuation\">(</span>\n        size<span class=\"token punctuation\">:</span> <span class=\"token function\">Size</span><span class=\"token punctuation\">(</span><span class=\"token number\">300</span><span class=\"token punctuation\">,</span> <span class=\"token number\">300</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//指定画布大小</span>\n        painter<span class=\"token punctuation\">:</span> <span class=\"token function\">MyPainter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">MyPainter</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">CustomPainter</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">paint</span><span class=\"token punctuation\">(</span>Canvas canvas<span class=\"token punctuation\">,</span> Size size<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    double eWidth <span class=\"token operator\">=</span> size<span class=\"token punctuation\">.</span>width <span class=\"token operator\">/</span> <span class=\"token number\">15</span><span class=\"token punctuation\">;</span>\n    double eHeight <span class=\"token operator\">=</span> size<span class=\"token punctuation\">.</span>height <span class=\"token operator\">/</span> <span class=\"token number\">15</span><span class=\"token punctuation\">;</span>\n      \n    <span class=\"token comment\">//画棋盘背景</span>\n    <span class=\"token keyword\">var</span> paint <span class=\"token operator\">=</span> <span class=\"token function\">Paint</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>isAntiAlias <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span>\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>style <span class=\"token operator\">=</span> PaintingStyle<span class=\"token punctuation\">.</span>fill <span class=\"token comment\">//填充</span>\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>color <span class=\"token operator\">=</span> <span class=\"token function\">Color</span><span class=\"token punctuation\">(</span><span class=\"token number\">0x77cdb175</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//背景为纸黄色</span>\n    canvas<span class=\"token punctuation\">.</span><span class=\"token function\">drawRect</span><span class=\"token punctuation\">(</span>Offset<span class=\"token punctuation\">.</span>zero <span class=\"token operator\">&amp;</span> size<span class=\"token punctuation\">,</span> paint<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token comment\">//画棋盘网格</span>\n    paint\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>style <span class=\"token operator\">=</span> PaintingStyle<span class=\"token punctuation\">.</span>stroke <span class=\"token comment\">//线</span>\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>color <span class=\"token operator\">=</span> Colors<span class=\"token punctuation\">.</span>black87\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>strokeWidth <span class=\"token operator\">=</span> <span class=\"token number\">1.0</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span>int i <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span> i <span class=\"token operator\">&lt;=</span> <span class=\"token number\">15</span><span class=\"token punctuation\">;</span> <span class=\"token operator\">++</span>i<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      double dy <span class=\"token operator\">=</span> eHeight <span class=\"token operator\">*</span> i<span class=\"token punctuation\">;</span>\n      canvas<span class=\"token punctuation\">.</span><span class=\"token function\">drawLine</span><span class=\"token punctuation\">(</span><span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">,</span> dy<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span>size<span class=\"token punctuation\">.</span>width<span class=\"token punctuation\">,</span> dy<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> paint<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span>int i <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span> i <span class=\"token operator\">&lt;=</span> <span class=\"token number\">15</span><span class=\"token punctuation\">;</span> <span class=\"token operator\">++</span>i<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      double dx <span class=\"token operator\">=</span> eWidth <span class=\"token operator\">*</span> i<span class=\"token punctuation\">;</span>\n      canvas<span class=\"token punctuation\">.</span><span class=\"token function\">drawLine</span><span class=\"token punctuation\">(</span><span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span>dx<span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span>dx<span class=\"token punctuation\">,</span> size<span class=\"token punctuation\">.</span>height<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> paint<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token comment\">//画一个黑子</span>\n    paint\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>style <span class=\"token operator\">=</span> PaintingStyle<span class=\"token punctuation\">.</span>fill\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>color <span class=\"token operator\">=</span> Colors<span class=\"token punctuation\">.</span>black<span class=\"token punctuation\">;</span>\n    canvas<span class=\"token punctuation\">.</span><span class=\"token function\">drawCircle</span><span class=\"token punctuation\">(</span>\n      <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span>size<span class=\"token punctuation\">.</span>width <span class=\"token operator\">/</span> <span class=\"token number\">2</span> <span class=\"token operator\">-</span> eWidth <span class=\"token operator\">/</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> size<span class=\"token punctuation\">.</span>height <span class=\"token operator\">/</span> <span class=\"token number\">2</span> <span class=\"token operator\">-</span> eHeight <span class=\"token operator\">/</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token function\">min</span><span class=\"token punctuation\">(</span>eWidth <span class=\"token operator\">/</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> eHeight <span class=\"token operator\">/</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span>\n      paint<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      \n    <span class=\"token comment\">//画一个白子</span>\n    paint<span class=\"token punctuation\">.</span>color <span class=\"token operator\">=</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">;</span>\n    canvas<span class=\"token punctuation\">.</span><span class=\"token function\">drawCircle</span><span class=\"token punctuation\">(</span>\n      <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span>size<span class=\"token punctuation\">.</span>width <span class=\"token operator\">/</span> <span class=\"token number\">2</span> <span class=\"token operator\">+</span> eWidth <span class=\"token operator\">/</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> size<span class=\"token punctuation\">.</span>height <span class=\"token operator\">/</span> <span class=\"token number\">2</span> <span class=\"token operator\">-</span> eHeight <span class=\"token operator\">/</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token function\">min</span><span class=\"token punctuation\">(</span>eWidth <span class=\"token operator\">/</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> eHeight <span class=\"token operator\">/</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span>\n      paint<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">//在实际场景中正确利用此回调可以避免重绘开销，本示例我们简单的返回true</span>\n  <span class=\"token metadata symbol\">@override</span>\n  bool <span class=\"token function\">shouldRepaint</span><span class=\"token punctuation\">(</span>CustomPainter oldDelegate<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h3 id=\"性能\"><a href=\"#性能\" class=\"header-anchor\">#</a> 性能</h3> <p>绘制是比较昂贵的操作，所以我们在实现自绘控件时应该考虑到性能开销，下面是两条关于性能优化的建议：</p> <ul><li><p>尽可能的利用好<code>shouldRepaint</code>返回值；在UI树重新build时，控件在绘制前都会先调用该方法以确定是否有必要重绘；假如我们绘制的UI不依赖外部状态，那么就应该始终返回<code>false</code>，因为外部状态改变导致重新build时不会影响我们的UI外观；如果绘制依赖外部状态，那么我们就应该在<code>shouldRepaint</code>中判断依赖的状态是否改变，如果已改变则应返回<code>true</code>来重绘，反之则应返回<code>false</code>不需要重绘。</p></li> <li><p>绘制尽可能多的分层；在上面五子棋的示例中，我们将棋盘和棋子的绘制放在了一起，这样会有一个问题：由于棋盘始终是不变的，用户每次落子时变的只是棋子，但是如果按照上面的代码来实现，每次绘制棋子时都要重新绘制一次棋盘，这是没必要的。优化的方法就是将棋盘单独抽为一个组件，并设置其<code>shouldRepaint</code>回调值为<code>false</code>，然后将棋盘组件作为背景。然后将棋子的绘制放到另一个组件中，这样每次落子时只需要绘制棋子。</p></li></ul> <h3 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h3> <p>自绘控件非常强大，理论上可以实现任何2D图形外观，实际上Flutter提供的所有组件最终都是通过调用Canvas绘制出来的，只不过绘制的逻辑被封装起来了，读者有兴趣可以查看具有外观样式的组件源码，找到其对应的<code>RenderObject</code>对象，如<code>Text</code>对应的<code>RenderParagraph</code>对象最终会通过<code>Canvas</code>实现文本绘制逻辑。下一节我们会再通过一个自绘的圆形背景渐变进度条的实例来帮助读者加深印象。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter10/turn_box.html\" class=\"prev\">\n        10.3 组合实例：TurnBox\n      </a></span> <span class=\"next\"><a href=\"/chapter10/gradient_circular_progress_demo.html\">\n        10.5 自绘实例：圆形背景渐变进度条\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/83.daf1b549.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter10/gradient_circular_progress_demo.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>10.5 自绘实例：圆形背景渐变进度条 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/84.f330e398.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable open\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" aria-current=\"page\" class=\"active sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_10-5-自绘实例-圆形背景渐变进度条\"><a href=\"#_10-5-自绘实例-圆形背景渐变进度条\" class=\"header-anchor\">#</a> 10.5 自绘实例：圆形背景渐变进度条</h1> <p>本节我们实现一个圆形背景渐变进度条，它支持：</p> <ol><li>支持多种背景渐变色。</li> <li>任意弧度；进度条可以不是整圆。</li> <li>可以自定义粗细、两端是否圆角等样式。</li></ol> <p>可以发现要实现这样的一个进度条是无法通过现有组件组合而成的，所以我们通过自绘方式实现，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'dart:math'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">GradientCircularProgressIndicator</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">GradientCircularProgressIndicator</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>strokeWidth <span class=\"token operator\">=</span> <span class=\"token number\">2.0</span><span class=\"token punctuation\">,</span>\n    <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>radius<span class=\"token punctuation\">,</span>\n    <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>colors<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>stops<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>strokeCapRound <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>backgroundColor <span class=\"token operator\">=</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Color</span><span class=\"token punctuation\">(</span><span class=\"token number\">0xFFEEEEEE</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>totalAngle <span class=\"token operator\">=</span> <span class=\"token number\">2</span> <span class=\"token operator\">*</span> pi<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>value\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">///粗细</span>\n  <span class=\"token keyword\">final</span> double strokeWidth<span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">/// 圆的半径</span>\n  <span class=\"token keyword\">final</span> double radius<span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">///两端是否为圆角</span>\n  <span class=\"token keyword\">final</span> bool strokeCapRound<span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">/// 当前进度，取值范围 [0.0-1.0]</span>\n  <span class=\"token keyword\">final</span> double value<span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">/// 进度条背景色</span>\n  <span class=\"token keyword\">final</span> Color backgroundColor<span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">/// 进度条的总弧度，2*PI为整圆，小于2*PI则不是整圆</span>\n  <span class=\"token keyword\">final</span> double totalAngle<span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">/// 渐变色数组</span>\n  <span class=\"token keyword\">final</span> List<span class=\"token operator\">&lt;</span>Color<span class=\"token operator\">&gt;</span> colors<span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">/// 渐变色的终止点，对应colors属性</span>\n  <span class=\"token keyword\">final</span> List<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> stops<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    double _offset <span class=\"token operator\">=</span> <span class=\"token number\">.0</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">// 如果两端为圆角，则需要对起始位置进行调整，否则圆角部分会偏离起始位置</span>\n    <span class=\"token comment\">// 下面调整的角度的计算公式是通过数学几何知识得出，读者有兴趣可以研究一下为什么是这样  </span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>strokeCapRound<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _offset <span class=\"token operator\">=</span> <span class=\"token function\">asin</span><span class=\"token punctuation\">(</span>strokeWidth <span class=\"token operator\">/</span> <span class=\"token punctuation\">(</span>radius <span class=\"token operator\">*</span> <span class=\"token number\">2</span> <span class=\"token operator\">-</span> strokeWidth<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">var</span> _colors <span class=\"token operator\">=</span> colors<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>_colors <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      Color color <span class=\"token operator\">=</span> Theme\n          <span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span>\n          <span class=\"token punctuation\">.</span>accentColor<span class=\"token punctuation\">;</span>\n      _colors <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span>color<span class=\"token punctuation\">,</span> color<span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">return</span> Transform<span class=\"token punctuation\">.</span><span class=\"token function\">rotate</span><span class=\"token punctuation\">(</span>\n      angle<span class=\"token punctuation\">:</span> <span class=\"token operator\">-</span>pi <span class=\"token operator\">/</span> <span class=\"token number\">2.0</span> <span class=\"token operator\">-</span> _offset<span class=\"token punctuation\">,</span> \n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">CustomPaint</span><span class=\"token punctuation\">(</span>\n          size<span class=\"token punctuation\">:</span> Size<span class=\"token punctuation\">.</span><span class=\"token function\">fromRadius</span><span class=\"token punctuation\">(</span>radius<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          painter<span class=\"token punctuation\">:</span> <span class=\"token function\">_GradientCircularProgressPainter</span><span class=\"token punctuation\">(</span>\n            strokeWidth<span class=\"token punctuation\">:</span> strokeWidth<span class=\"token punctuation\">,</span>\n            strokeCapRound<span class=\"token punctuation\">:</span> strokeCapRound<span class=\"token punctuation\">,</span>\n            backgroundColor<span class=\"token punctuation\">:</span> backgroundColor<span class=\"token punctuation\">,</span>\n            value<span class=\"token punctuation\">:</span> value<span class=\"token punctuation\">,</span>\n            total<span class=\"token punctuation\">:</span> totalAngle<span class=\"token punctuation\">,</span>\n            radius<span class=\"token punctuation\">:</span> radius<span class=\"token punctuation\">,</span>\n            colors<span class=\"token punctuation\">:</span> _colors<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">//实现画笔</span>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_GradientCircularProgressPainter</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">CustomPainter</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">_GradientCircularProgressPainter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>strokeWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">10.0</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>strokeCapRound<span class=\"token punctuation\">:</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>backgroundColor <span class=\"token operator\">=</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Color</span><span class=\"token punctuation\">(</span><span class=\"token number\">0xFFEEEEEE</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>radius<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>total <span class=\"token operator\">=</span> <span class=\"token number\">2</span> <span class=\"token operator\">*</span> pi<span class=\"token punctuation\">,</span>\n    <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>colors<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>stops<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>value\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> double strokeWidth<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> bool strokeCapRound<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> double value<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> Color backgroundColor<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> List<span class=\"token operator\">&lt;</span>Color<span class=\"token operator\">&gt;</span> colors<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> double total<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> double radius<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> List<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> stops<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">paint</span><span class=\"token punctuation\">(</span>Canvas canvas<span class=\"token punctuation\">,</span> Size size<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>radius <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      size <span class=\"token operator\">=</span> Size<span class=\"token punctuation\">.</span><span class=\"token function\">fromRadius</span><span class=\"token punctuation\">(</span>radius<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    double _offset <span class=\"token operator\">=</span> strokeWidth <span class=\"token operator\">/</span> <span class=\"token number\">2.0</span><span class=\"token punctuation\">;</span>\n    double _value <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>value <span class=\"token operator\">?</span><span class=\"token operator\">?</span> <span class=\"token number\">.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    _value <span class=\"token operator\">=</span> _value<span class=\"token punctuation\">.</span><span class=\"token function\">clamp</span><span class=\"token punctuation\">(</span><span class=\"token number\">.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">1.0</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">*</span> total<span class=\"token punctuation\">;</span>\n    double _start <span class=\"token operator\">=</span> <span class=\"token number\">.0</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>strokeCapRound<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _start <span class=\"token operator\">=</span> <span class=\"token function\">asin</span><span class=\"token punctuation\">(</span>strokeWidth<span class=\"token operator\">/</span> <span class=\"token punctuation\">(</span>size<span class=\"token punctuation\">.</span>width <span class=\"token operator\">-</span> strokeWidth<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    Rect rect <span class=\"token operator\">=</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span>_offset<span class=\"token punctuation\">,</span> _offset<span class=\"token punctuation\">)</span> <span class=\"token operator\">&amp;</span> <span class=\"token function\">Size</span><span class=\"token punctuation\">(</span>\n        size<span class=\"token punctuation\">.</span>width <span class=\"token operator\">-</span> strokeWidth<span class=\"token punctuation\">,</span>\n        size<span class=\"token punctuation\">.</span>height <span class=\"token operator\">-</span> strokeWidth\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">var</span> paint <span class=\"token operator\">=</span> <span class=\"token function\">Paint</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>strokeCap <span class=\"token operator\">=</span> strokeCapRound <span class=\"token operator\">?</span> StrokeCap<span class=\"token punctuation\">.</span>round <span class=\"token punctuation\">:</span> StrokeCap<span class=\"token punctuation\">.</span>butt\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>style <span class=\"token operator\">=</span> PaintingStyle<span class=\"token punctuation\">.</span>stroke\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>isAntiAlias <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span>\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>strokeWidth <span class=\"token operator\">=</span> strokeWidth<span class=\"token punctuation\">;</span>\n\n    <span class=\"token comment\">// 先画背景</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>backgroundColor <span class=\"token operator\">!=</span> Colors<span class=\"token punctuation\">.</span>transparent<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      paint<span class=\"token punctuation\">.</span>color <span class=\"token operator\">=</span> backgroundColor<span class=\"token punctuation\">;</span>\n      canvas<span class=\"token punctuation\">.</span><span class=\"token function\">drawArc</span><span class=\"token punctuation\">(</span>\n          rect<span class=\"token punctuation\">,</span>\n          _start<span class=\"token punctuation\">,</span>\n          total<span class=\"token punctuation\">,</span>\n          <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n          paint\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token comment\">// 再画前景，应用渐变</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>_value <span class=\"token operator\">&gt;</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      paint<span class=\"token punctuation\">.</span>shader <span class=\"token operator\">=</span> <span class=\"token function\">SweepGradient</span><span class=\"token punctuation\">(</span>\n        startAngle<span class=\"token punctuation\">:</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span>\n        endAngle<span class=\"token punctuation\">:</span> _value<span class=\"token punctuation\">,</span>\n        colors<span class=\"token punctuation\">:</span> colors<span class=\"token punctuation\">,</span>\n        stops<span class=\"token punctuation\">:</span> stops<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">createShader</span><span class=\"token punctuation\">(</span>rect<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n      canvas<span class=\"token punctuation\">.</span><span class=\"token function\">drawArc</span><span class=\"token punctuation\">(</span>\n          rect<span class=\"token punctuation\">,</span>\n          _start<span class=\"token punctuation\">,</span>\n          _value<span class=\"token punctuation\">,</span>\n          <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n          paint\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  bool <span class=\"token function\">shouldRepaint</span><span class=\"token punctuation\">(</span>CustomPainter oldDelegate<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>下面我们来测试一下，为了尽可能多的展示<code>GradientCircularProgressIndicator</code>的不同外观和用途，这个示例代码会比较长，并且添加了动画，建议读者将此示例运行起来观看实际效果，我们先看看其中的一帧动画的截图：</p> <p><img src=\"/assets/img/gradient_circular_progress.4cea87cb.png\" alt=\"gradient_circular_progress\"></p> <p>示例代码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'dart:math'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'../widgets/index.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">GradientCircularProgressRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  GradientCircularProgressRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">GradientCircularProgressRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">GradientCircularProgressRouteState</span>\n    <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>GradientCircularProgressRoute<span class=\"token operator\">&gt;</span> <span class=\"token keyword\">with</span> TickerProviderStateMixin <span class=\"token punctuation\">{</span>\n  AnimationController _animationController<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    _animationController <span class=\"token operator\">=</span>\n        <span class=\"token keyword\">new</span> <span class=\"token class-name\">AnimationController</span><span class=\"token punctuation\">(</span>vsync<span class=\"token punctuation\">:</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">,</span> duration<span class=\"token punctuation\">:</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">3</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    bool isForward <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n    _animationController<span class=\"token punctuation\">.</span><span class=\"token function\">addStatusListener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>status<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>status <span class=\"token operator\">==</span> AnimationStatus<span class=\"token punctuation\">.</span>forward<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        isForward <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>status <span class=\"token operator\">==</span> AnimationStatus<span class=\"token punctuation\">.</span>completed <span class=\"token operator\">||</span>\n          status <span class=\"token operator\">==</span> AnimationStatus<span class=\"token punctuation\">.</span>dismissed<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>isForward<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          _animationController<span class=\"token punctuation\">.</span><span class=\"token function\">reverse</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n          _animationController<span class=\"token punctuation\">.</span><span class=\"token function\">forward</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n      <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>status <span class=\"token operator\">==</span> AnimationStatus<span class=\"token punctuation\">.</span>reverse<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        isForward <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    _animationController<span class=\"token punctuation\">.</span><span class=\"token function\">forward</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    _animationController<span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">SingleChildScrollView</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n          crossAxisAlignment<span class=\"token punctuation\">:</span> CrossAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            <span class=\"token function\">AnimatedBuilder</span><span class=\"token punctuation\">(</span>\n              animation<span class=\"token punctuation\">:</span> _animationController<span class=\"token punctuation\">,</span>\n              builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> Widget child<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token keyword\">return</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n                  padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">symmetric</span><span class=\"token punctuation\">(</span>vertical<span class=\"token punctuation\">:</span> <span class=\"token number\">16.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n                    children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                      <span class=\"token function\">Wrap</span><span class=\"token punctuation\">(</span>\n                        spacing<span class=\"token punctuation\">:</span> <span class=\"token number\">10.0</span><span class=\"token punctuation\">,</span>\n                        runSpacing<span class=\"token punctuation\">:</span> <span class=\"token number\">16.0</span><span class=\"token punctuation\">,</span>\n                        children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                          <span class=\"token function\">GradientCircularProgressIndicator</span><span class=\"token punctuation\">(</span>\n                            <span class=\"token comment\">// No gradient</span>\n                            colors<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                            radius<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n                            strokeWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">3.0</span><span class=\"token punctuation\">,</span>\n                            value<span class=\"token punctuation\">:</span> _animationController<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span>\n                          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                          <span class=\"token function\">GradientCircularProgressIndicator</span><span class=\"token punctuation\">(</span>\n                            colors<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span> Colors<span class=\"token punctuation\">.</span>orange<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                            radius<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n                            strokeWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">3.0</span><span class=\"token punctuation\">,</span>\n                            value<span class=\"token punctuation\">:</span> _animationController<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span>\n                          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                          <span class=\"token function\">GradientCircularProgressIndicator</span><span class=\"token punctuation\">(</span>\n                            colors<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span> Colors<span class=\"token punctuation\">.</span>orange<span class=\"token punctuation\">,</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                            radius<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n                            strokeWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">5.0</span><span class=\"token punctuation\">,</span>\n                            value<span class=\"token punctuation\">:</span> _animationController<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span>\n                          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                          <span class=\"token function\">GradientCircularProgressIndicator</span><span class=\"token punctuation\">(</span>\n                            colors<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>Colors<span class=\"token punctuation\">.</span>teal<span class=\"token punctuation\">,</span> Colors<span class=\"token punctuation\">.</span>cyan<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                            radius<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n                            strokeWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">5.0</span><span class=\"token punctuation\">,</span>\n                            strokeCapRound<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n                            value<span class=\"token punctuation\">:</span> <span class=\"token function\">CurvedAnimation</span><span class=\"token punctuation\">(</span>\n                                    parent<span class=\"token punctuation\">:</span> _animationController<span class=\"token punctuation\">,</span>\n                                    curve<span class=\"token punctuation\">:</span> Curves<span class=\"token punctuation\">.</span>decelerate<span class=\"token punctuation\">)</span>\n                                <span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span>\n                          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                          <span class=\"token function\">TurnBox</span><span class=\"token punctuation\">(</span>\n                            turns<span class=\"token punctuation\">:</span> <span class=\"token number\">1</span> <span class=\"token operator\">/</span> <span class=\"token number\">8</span><span class=\"token punctuation\">,</span>\n                            child<span class=\"token punctuation\">:</span> <span class=\"token function\">GradientCircularProgressIndicator</span><span class=\"token punctuation\">(</span>\n                                colors<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span> Colors<span class=\"token punctuation\">.</span>orange<span class=\"token punctuation\">,</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                                radius<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n                                strokeWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">5.0</span><span class=\"token punctuation\">,</span>\n                                strokeCapRound<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n                                backgroundColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">[</span><span class=\"token number\">50</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                                totalAngle<span class=\"token punctuation\">:</span> <span class=\"token number\">1.5</span> <span class=\"token operator\">*</span> pi<span class=\"token punctuation\">,</span>\n                                value<span class=\"token punctuation\">:</span> <span class=\"token function\">CurvedAnimation</span><span class=\"token punctuation\">(</span>\n                                        parent<span class=\"token punctuation\">:</span> _animationController<span class=\"token punctuation\">,</span>\n                                        curve<span class=\"token punctuation\">:</span> Curves<span class=\"token punctuation\">.</span>ease<span class=\"token punctuation\">)</span>\n                                    <span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                          <span class=\"token function\">RotatedBox</span><span class=\"token punctuation\">(</span>\n                            quarterTurns<span class=\"token punctuation\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n                            child<span class=\"token punctuation\">:</span> <span class=\"token function\">GradientCircularProgressIndicator</span><span class=\"token punctuation\">(</span>\n                                colors<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">[</span><span class=\"token number\">700</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">[</span><span class=\"token number\">200</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                                radius<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n                                strokeWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">3.0</span><span class=\"token punctuation\">,</span>\n                                strokeCapRound<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n                                backgroundColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>transparent<span class=\"token punctuation\">,</span>\n                                value<span class=\"token punctuation\">:</span> _animationController<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                          <span class=\"token function\">GradientCircularProgressIndicator</span><span class=\"token punctuation\">(</span>\n                            colors<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>\n                              Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span>\n                              Colors<span class=\"token punctuation\">.</span>amber<span class=\"token punctuation\">,</span>\n                              Colors<span class=\"token punctuation\">.</span>cyan<span class=\"token punctuation\">,</span>\n                              Colors<span class=\"token punctuation\">.</span>green<span class=\"token punctuation\">[</span><span class=\"token number\">200</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                              Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span>\n                              Colors<span class=\"token punctuation\">.</span>red\n                            <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                            radius<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n                            strokeWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">5.0</span><span class=\"token punctuation\">,</span>\n                            strokeCapRound<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n                            value<span class=\"token punctuation\">:</span> _animationController<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span>\n                          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                      <span class=\"token function\">GradientCircularProgressIndicator</span><span class=\"token punctuation\">(</span>\n                        colors<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">[</span><span class=\"token number\">700</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">[</span><span class=\"token number\">200</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                        radius<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n                        strokeWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">20.0</span><span class=\"token punctuation\">,</span>\n                        value<span class=\"token punctuation\">:</span> _animationController<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span>\n                      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n\n                      <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n                        padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">symmetric</span><span class=\"token punctuation\">(</span>vertical<span class=\"token punctuation\">:</span> <span class=\"token number\">16.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                        child<span class=\"token punctuation\">:</span> <span class=\"token function\">GradientCircularProgressIndicator</span><span class=\"token punctuation\">(</span>\n                          colors<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">[</span><span class=\"token number\">700</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">[</span><span class=\"token number\">300</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                          radius<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n                          strokeWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">20.0</span><span class=\"token punctuation\">,</span>\n                          value<span class=\"token punctuation\">:</span> _animationController<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span>\n                          strokeCapRound<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n                        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                      <span class=\"token comment\">//剪裁半圆</span>\n                      <span class=\"token function\">ClipRect</span><span class=\"token punctuation\">(</span>\n                        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Align</span><span class=\"token punctuation\">(</span>\n                          alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>topCenter<span class=\"token punctuation\">,</span>\n                          heightFactor<span class=\"token punctuation\">:</span> <span class=\"token number\">.5</span><span class=\"token punctuation\">,</span>\n                          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n                            padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">only</span><span class=\"token punctuation\">(</span>bottom<span class=\"token punctuation\">:</span> <span class=\"token number\">8.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                            child<span class=\"token punctuation\">:</span> <span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span>\n                              <span class=\"token comment\">//width: 100.0,</span>\n                              child<span class=\"token punctuation\">:</span> <span class=\"token function\">TurnBox</span><span class=\"token punctuation\">(</span>\n                                turns<span class=\"token punctuation\">:</span> <span class=\"token number\">.75</span><span class=\"token punctuation\">,</span>\n                                child<span class=\"token punctuation\">:</span> <span class=\"token function\">GradientCircularProgressIndicator</span><span class=\"token punctuation\">(</span>\n                                  colors<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>Colors<span class=\"token punctuation\">.</span>teal<span class=\"token punctuation\">,</span> Colors<span class=\"token punctuation\">.</span>cyan<span class=\"token punctuation\">[</span><span class=\"token number\">500</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                                  radius<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n                                  strokeWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">8.0</span><span class=\"token punctuation\">,</span>\n                                  value<span class=\"token punctuation\">:</span> _animationController<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span>\n                                  totalAngle<span class=\"token punctuation\">:</span> pi<span class=\"token punctuation\">,</span>\n                                  strokeCapRound<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n                                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                      <span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span>\n                        height<span class=\"token punctuation\">:</span> <span class=\"token number\">104.0</span><span class=\"token punctuation\">,</span>\n                        width<span class=\"token punctuation\">:</span> <span class=\"token number\">200.0</span><span class=\"token punctuation\">,</span>\n                        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Stack</span><span class=\"token punctuation\">(</span>\n                          alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n                          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                            <span class=\"token function\">Positioned</span><span class=\"token punctuation\">(</span>\n                              height<span class=\"token punctuation\">:</span> <span class=\"token number\">200.0</span><span class=\"token punctuation\">,</span>\n                              top<span class=\"token punctuation\">:</span> <span class=\"token number\">.0</span><span class=\"token punctuation\">,</span>\n                              child<span class=\"token punctuation\">:</span> <span class=\"token function\">TurnBox</span><span class=\"token punctuation\">(</span>\n                                turns<span class=\"token punctuation\">:</span> <span class=\"token number\">.75</span><span class=\"token punctuation\">,</span>\n                                child<span class=\"token punctuation\">:</span> <span class=\"token function\">GradientCircularProgressIndicator</span><span class=\"token punctuation\">(</span>\n                                  colors<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>Colors<span class=\"token punctuation\">.</span>teal<span class=\"token punctuation\">,</span> Colors<span class=\"token punctuation\">.</span>cyan<span class=\"token punctuation\">[</span><span class=\"token number\">500</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                                  radius<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n                                  strokeWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">8.0</span><span class=\"token punctuation\">,</span>\n                                  value<span class=\"token punctuation\">:</span> _animationController<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span>\n                                  totalAngle<span class=\"token punctuation\">:</span> pi<span class=\"token punctuation\">,</span>\n                                  strokeCapRound<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n                                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                            <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n                              padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">only</span><span class=\"token punctuation\">(</span>top<span class=\"token punctuation\">:</span> <span class=\"token number\">10.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>\n                                <span class=\"token string\">&quot;${(_animationController.value * 100).toInt()}%&quot;</span><span class=\"token punctuation\">,</span>\n                                style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>\n                                  fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">25.0</span><span class=\"token punctuation\">,</span>\n                                  color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blueGrey<span class=\"token punctuation\">,</span>\n                                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                            <span class=\"token punctuation\">)</span>\n                          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>怎么样，很炫酷吧！<code>GradientCircularProgressIndicator</code>已经被添加进了笔者维护的flukit组件库中了，读者如果有需要，可以直接依赖flukit包。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter10/custom_paint.html\" class=\"prev\">\n        10.4 自绘组件 （CustomPaint与Canvas）\n      </a></span> <span class=\"next\"><a href=\"/chapter11/file_operation.html\">\n        11.1 文件操作\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/84.f330e398.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter10/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>本章目录 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/133.85003caa.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"本章目录\"><a href=\"#本章目录\" class=\"header-anchor\">#</a> 本章目录</h1> <ul><li><a href=\"/chapter10/intro.html\">10.1：自定义组件方法简介</a></li> <li><a href=\"/chapter10/combine.html\">10.2：组合现有组件</a></li> <li><a href=\"/chapter10/turn_box.html\">10.3：组合实例：TurnBox</a></li> <li><a href=\"/chapter10/custom_paint.html\">10.4：自绘组件（CustomPaint与Canvas）</a></li> <li><a href=\"/chapter10/gradient_circular_progress_demo.html\">10.5：自绘实例：圆形渐变进度条(自绘)</a></li></ul></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/133.85003caa.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter10/intro.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>10.1 自定义组件方法简介 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/134.68a25fe9.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable open\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" aria-current=\"page\" class=\"active sidebar-link\">10.1 自定义组件方法简介</a><ul class=\"sidebar-sub-headers\"></ul></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_10-1-自定义组件方法简介\"><a href=\"#_10-1-自定义组件方法简介\" class=\"header-anchor\">#</a> 10.1 自定义组件方法简介</h1> <p>当Flutter提供的现有组件无法满足我们的需求，或者我们为了共享代码需要封装一些通用组件，这时我们就需要自定义组件。在Flutter中自定义组件有三种方式：通过组合其它组件、自绘和实现RenderObject。本节我们先分别介绍一下这三种方式的特点，后面章节中则详细介绍它们的细节。</p> <h3 id=\"组合其它widget\"><a href=\"#组合其它widget\" class=\"header-anchor\">#</a> 组合其它Widget</h3> <p>这种方式是通过拼装其它组件来组合成一个新的组件。例如我们之前介绍的<code>Container</code>就是一个组合组件，它是由<code>DecoratedBox</code>、<code>ConstrainedBox</code>、<code>Transform</code>、<code>Padding</code>、<code>Align</code>等组件组成。</p> <p>在Flutter中，组合的思想非常重要，Flutter提供了非常多的基础组件，而我们的界面开发其实就是按照需要组合这些组件来实现各种不同的布局而已。</p> <h3 id=\"自绘\"><a href=\"#自绘\" class=\"header-anchor\">#</a> 自绘</h3> <p>如果遇到无法通过现有的组件来实现需要的UI时，我们可以通过自绘组件的方式来实现，例如我们需要一个颜色渐变的圆形进度条，而Flutter提供的<code>CircularProgressIndicator</code>并不支持在显示精确进度时对进度条应用渐变色（其<code>valueColor</code> 属性只支持执行旋转动画时变化Indicator的颜色），这时最好的方法就是通过自定义组件来绘制出我们期望的外观。我们可以通过Flutter中提供的<code>CustomPaint</code>和<code>Canvas</code>来实现UI自绘。</p> <h3 id=\"实现renderobject\"><a href=\"#实现renderobject\" class=\"header-anchor\">#</a> 实现RenderObject</h3> <p>Flutter提供的自身具有UI外观的组件，如文本<code>Text</code>、<code>Image</code>都是通过相应的<code>RenderObject</code>（我们将在“Flutter核心原理”一章中详细介绍<code>RenderObject</code>）渲染出来的，如Text是由<code>RenderParagraph</code>渲染；而<code>Image</code>是由<code>RenderImage</code>渲染。<code>RenderObject</code>是一个抽象类，它定义了一个抽象方法<code>paint(...)</code>：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">paint</span><span class=\"token punctuation\">(</span>PaintingContext context<span class=\"token punctuation\">,</span> Offset offset<span class=\"token punctuation\">)</span>\n</code></pre></div><p><code>PaintingContext</code>代表组件的绘制上下文，通过<code>PaintingContext.canvas</code>可以获得<code>Canvas</code>，而绘制逻辑主要是通过<code>Canvas</code> API来实现。子类需要重写此方法以实现自身的绘制逻辑，如<code>RenderParagraph</code>需要实现文本绘制逻辑，而<code>RenderImage</code>需要实现图片绘制逻辑。</p> <p>可以发现，<code>RenderObject</code>中最终也是通过<code>Canvas</code> API来绘制的，那么通过实现<code>RenderObject</code>的方式和上面介绍的通过<code>CustomPaint</code>和<code>Canvas</code>自绘的方式有什么区别？其实答案很简单，<code>CustomPaint</code>只是为了方便开发者封装的一个代理类，它直接继承自<code>SingleChildRenderObjectWidget</code>，通过<code>RenderCustomPaint</code>的<code>paint</code>方法将<code>Canvas</code>和画笔<code>Painter</code>(需要开发者实现，后面章节介绍)连接起来实现了最终的绘制（绘制逻辑在<code>Painter</code>中）。</p> <h3 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h3> <p>“组合”是自定义组件最简单的方法，在任何需要自定义组件的场景下，我们都应该优先考虑是否能够通过组合来实现。而自绘和通过实现<code>RenderObject</code>的方法本质上是一样的，都需要开发者调用<code>Canvas</code> API手动去绘制UI，优点是强大灵活，理论上可以实现任何外观的UI，而缺点是必须了解<code>Canvas</code> API细节，并且得自己去实现绘制逻辑。</p> <p>在本章接下来的小节中，我们将通过一些实例来详细介绍自定义UI的过程，由于后两种方法本质是相同的，并且Flutter中很多基础组件都是通过<code>RenderObject</code>的形式来实现的，所以后续我们只介绍<code>CustomPaint</code>和<code>Canvas</code>的方式，读者如果对自定义<code>RenderObject</code>的方法好奇，可以查看Flutter中相关基础组件对应的<code>RenderObject</code>的实现源码，如<code>RenderParagraph</code>或<code>RenderImage</code>。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter9/animated_widgets.html\" class=\"prev\">\n        9.7 动画过渡组件\n      </a></span> <span class=\"next\"><a href=\"/chapter10/combine.html\">\n        10.2 组合现有组件\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/134.68a25fe9.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter10/turn_box.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>10.3 组合实例：TurnBox | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/85.f647b247.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable open\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" aria-current=\"page\" class=\"active sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_10-3-组合实例-turnbox\"><a href=\"#_10-3-组合实例-turnbox\" class=\"header-anchor\">#</a> 10.3 组合实例：TurnBox</h1> <p>我们之前已经介绍过<code>RotatedBox</code>，它可以旋转子组件，但是它有两个缺点：一是只能将其子节点以90度的倍数旋转；二是当旋转的角度发生变化时，旋转角度更新过程没有动画。</p> <p>本节我们将实现一个<code>TurnBox</code>组件，它不仅可以以任意角度来旋转其子节点，而且可以在角度发生变化时执行一个动画以过渡到新状态，同时，我们可以手动指定动画速度。</p> <p><code>TurnBox</code>的完整代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/widgets.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">TurnBox</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function\">TurnBox</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>turns <span class=\"token operator\">=</span> <span class=\"token number\">.0</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//旋转的“圈”数,一圈为360度，如0.25圈即90度</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>speed <span class=\"token operator\">=</span> <span class=\"token number\">200</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//过渡动画执行的总时长</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>child\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span><span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> double turns<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> int speed<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> Widget child<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  _TurnBoxState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_TurnBoxState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_TurnBoxState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>TurnBox<span class=\"token operator\">&gt;</span>\n    <span class=\"token keyword\">with</span> SingleTickerProviderStateMixin <span class=\"token punctuation\">{</span>\n  AnimationController _controller<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    _controller <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AnimationController</span><span class=\"token punctuation\">(</span>\n        vsync<span class=\"token punctuation\">:</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">,</span>\n        lowerBound<span class=\"token punctuation\">:</span> <span class=\"token operator\">-</span>double<span class=\"token punctuation\">.</span>infinity<span class=\"token punctuation\">,</span>\n        upperBound<span class=\"token punctuation\">:</span> double<span class=\"token punctuation\">.</span>infinity\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    _controller<span class=\"token punctuation\">.</span>value <span class=\"token operator\">=</span> widget<span class=\"token punctuation\">.</span>turns<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    _controller<span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">RotationTransition</span><span class=\"token punctuation\">(</span>\n      turns<span class=\"token punctuation\">:</span> _controller<span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> widget<span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">didUpdateWidget</span><span class=\"token punctuation\">(</span>TurnBox oldWidget<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">didUpdateWidget</span><span class=\"token punctuation\">(</span>oldWidget<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//旋转角度发生变化时执行过渡动画  </span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>oldWidget<span class=\"token punctuation\">.</span>turns <span class=\"token operator\">!=</span> widget<span class=\"token punctuation\">.</span>turns<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _controller<span class=\"token punctuation\">.</span><span class=\"token function\">animateTo</span><span class=\"token punctuation\">(</span>\n        widget<span class=\"token punctuation\">.</span>turns<span class=\"token punctuation\">,</span>\n        duration<span class=\"token punctuation\">:</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>milliseconds<span class=\"token punctuation\">:</span> widget<span class=\"token punctuation\">.</span>speed<span class=\"token operator\">?</span><span class=\"token operator\">?</span><span class=\"token number\">200</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        curve<span class=\"token punctuation\">:</span> Curves<span class=\"token punctuation\">.</span>easeOut<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面代码中：</p> <ol><li>我们是通过组合<code>RotationTransition</code>和child来实现的旋转效果。</li> <li>在<code>didUpdateWidget</code>中，我们判断要旋转的角度是否发生了变化，如果变了，则执行一个过渡动画。</li></ol> <p>下面我们测试一下<code>TurnBox</code>的功能，测试代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'../widgets/index.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">TurnBoxRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _TurnBoxRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_TurnBoxRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_TurnBoxRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>TurnBoxRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  double _turns <span class=\"token operator\">=</span> <span class=\"token number\">.0</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n        children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n          <span class=\"token function\">TurnBox</span><span class=\"token punctuation\">(</span>\n            turns<span class=\"token punctuation\">:</span> _turns<span class=\"token punctuation\">,</span>\n            speed<span class=\"token punctuation\">:</span> <span class=\"token number\">500</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>refresh<span class=\"token punctuation\">,</span> size<span class=\"token punctuation\">:</span> <span class=\"token number\">50</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">TurnBox</span><span class=\"token punctuation\">(</span>\n            turns<span class=\"token punctuation\">:</span> _turns<span class=\"token punctuation\">,</span>\n            speed<span class=\"token punctuation\">:</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>refresh<span class=\"token punctuation\">,</span> size<span class=\"token punctuation\">:</span> <span class=\"token number\">150.0</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;顺时针旋转1/5圈&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                _turns <span class=\"token operator\">+=</span> <span class=\"token number\">.2</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;逆时针旋转1/5圈&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                _turns <span class=\"token operator\">-=</span> <span class=\"token number\">.2</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>测试代码运行后效果如图10-2所示：</p> <p><img src=\"/assets/img/10-2.2a9ed156.png\" alt=\"图10-2\"></p> <p>当我们点击旋转按钮时，两个图标的旋转都会旋转1/5圈，但旋转的速度是不同的，读者可以自己运行一下示例看看效果。</p> <p>实际上本示例只组合了<code>RotationTransition</code>一个组件，它是一个最简的组合类组件示例。另外，如果我们封装的是<code>StatefulWidget</code>，那么一定要注意在组件更新时是否需要同步状态。比如我们要封装一个富文本展示组件<code>MyRichText</code> ，它可以自动处理url链接，定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">MyRichText</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">MyRichText</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>text<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 文本字符串</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>linkStyle<span class=\"token punctuation\">,</span> <span class=\"token comment\">// url链接样式</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> String text<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> TextStyle linkStyle<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  _MyRichTextState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">_MyRichTextState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>接下来我们在<code>_MyRichTextState</code>中要实现的功能有两个：</p> <ol><li>解析文本字符串“text”，生成<code>TextSpan</code>缓存起来；</li> <li>在<code>build</code>中返回最终的富文本样式；</li></ol> <p><code>_MyRichTextState</code> 实现的代码大致如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">_MyRichTextState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>MyRichText<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n\n  TextSpan _textSpan<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">RichText</span><span class=\"token punctuation\">(</span>\n      text<span class=\"token punctuation\">:</span> _textSpan<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  TextSpan <span class=\"token function\">parseText</span><span class=\"token punctuation\">(</span>String text<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 耗时操作：解析文本字符串，构建出TextSpan。</span>\n    <span class=\"token comment\">// 省略具体实现。</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    _textSpan <span class=\"token operator\">=</span> <span class=\"token function\">parseText</span><span class=\"token punctuation\">(</span>widget<span class=\"token punctuation\">.</span>text<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>由于解析文本字符串，构建出<code>TextSpan</code>是一个耗时操作，为了不在每次build的时候都解析一次，所以我们在<code>initState</code>中对解析的结果进行了缓存，然后再<code>build</code>中直接使用解析的结果<code>_textSpan</code>。这看起来很不错，但是上面的代码有一个严重的问题，就是父组件传入的<code>text</code>发生变化时（组件树结构不变），那么<code>MyRichText</code>显示的内容不会更新，原因就是<code>initState</code>只会在State创建时被调用，所以在<code>text</code>发生变化时，<code>parseText</code>没有重新执行，导致<code>_textSpan</code>任然是旧的解析值。要解决这个问题也很简单，我们只需添加一个<code>didUpdateWidget</code>回调，然后再里面重新调用<code>parseText</code>即可：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token metadata symbol\">@override</span>\n<span class=\"token keyword\">void</span> <span class=\"token function\">didUpdateWidget</span><span class=\"token punctuation\">(</span>MyRichText oldWidget<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>widget<span class=\"token punctuation\">.</span>text <span class=\"token operator\">!=</span> oldWidget<span class=\"token punctuation\">.</span>text<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    _textSpan <span class=\"token operator\">=</span> <span class=\"token function\">parseText</span><span class=\"token punctuation\">(</span>widget<span class=\"token punctuation\">.</span>text<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">didUpdateWidget</span><span class=\"token punctuation\">(</span>oldWidget<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>有些读者可能会觉得这个点也很简单，是的，的确很简单，之所以要在这里反复强调是因为这个点在实际开发中很容易被忽略，它虽然简单，但却很重要。总之，当我们在State中会缓存某些依赖Widget参数的数据时，一定要注意在组件更新时是否需要同步状态。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter10/combine.html\" class=\"prev\">\n        10.2 组合现有组件\n      </a></span> <span class=\"next\"><a href=\"/chapter10/custom_paint.html\">\n        10.4 自绘组件 （CustomPaint与Canvas）\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/85.f647b247.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter11/dio.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>11.3 Http请求-Dio http库 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/135.765bad57.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable open\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" aria-current=\"page\" class=\"active sidebar-link\">11.3 Http请求-Dio http库</a><ul class=\"sidebar-sub-headers\"></ul></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_11-3-http请求-dio-http库\"><a href=\"#_11-3-http请求-dio-http库\" class=\"header-anchor\">#</a> 11.3 Http请求-Dio http库</h1> <p>通过上一节介绍，我们可以发现直接使用HttpClient发起网络请求是比较麻烦的，很多事情得我们手动处理，如果再涉及到文件上传/下载、Cookie管理等就会非常繁琐。幸运的是，Dart社区有一些第三方http请求库，用它们来发起http请求将会简单的多，本节我们介绍一下目前人气较高的<a href=\"https://github.com/flutterchina/dio\" target=\"_blank\" rel=\"noopener noreferrer\">dio<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>库。</p> <blockquote><p>dio是一个强大的Dart Http请求库，支持Restful API、FormData、拦截器、请求取消、Cookie管理、文件上传/下载、超时等。dio的使用方式随着其版本升级可能会发生变化，如果本节所述内容和dio官方有差异，请以dio官方文档为准。</p></blockquote> <h3 id=\"引入\"><a href=\"#引入\" class=\"header-anchor\">#</a> 引入</h3> <p>引入dio:</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code><span class=\"token key atrule\">dependencies</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">dio</span><span class=\"token punctuation\">:</span> ^x.x.x <span class=\"token comment\">#请使用pub上的最新版本</span>\n</code></pre></div><p>导入并创建dio实例：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:dio/dio.dart'</span><span class=\"token punctuation\">;</span>\nDio dio <span class=\"token operator\">=</span>  <span class=\"token function\">Dio</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>接下来就可以通过 dio实例来发起网络请求了，注意，一个dio实例可以发起多个http请求，一般来说，APP只有一个http数据源时，dio应该使用单例模式。</p> <h3 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h3> <p>发起 <code>GET</code> 请求 :</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Response response<span class=\"token punctuation\">;</span>\nresponse<span class=\"token operator\">=</span><span class=\"token keyword\">await</span> dio<span class=\"token punctuation\">.</span><span class=\"token keyword\">get</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;/test?id=12&amp;name=wendu&quot;</span><span class=\"token punctuation\">)</span>\n<span class=\"token function\">print</span><span class=\"token punctuation\">(</span>response<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>对于<code>GET</code>请求我们可以将query参数通过对象来传递，上面的代码等同于：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>response<span class=\"token operator\">=</span><span class=\"token keyword\">await</span> dio<span class=\"token punctuation\">.</span><span class=\"token keyword\">get</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;/test&quot;</span><span class=\"token punctuation\">,</span>queryParameters<span class=\"token punctuation\">:</span><span class=\"token punctuation\">{</span><span class=\"token string\">&quot;id&quot;</span><span class=\"token punctuation\">:</span><span class=\"token number\">12</span><span class=\"token punctuation\">,</span><span class=\"token string\">&quot;name&quot;</span><span class=\"token punctuation\">:</span><span class=\"token string\">&quot;wendu&quot;</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token function\">print</span><span class=\"token punctuation\">(</span>response<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>发起一个 <code>POST</code> 请求:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>response<span class=\"token operator\">=</span><span class=\"token keyword\">await</span> dio<span class=\"token punctuation\">.</span><span class=\"token function\">post</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;/test&quot;</span><span class=\"token punctuation\">,</span>data<span class=\"token punctuation\">:</span><span class=\"token punctuation\">{</span><span class=\"token string\">&quot;id&quot;</span><span class=\"token punctuation\">:</span><span class=\"token number\">12</span><span class=\"token punctuation\">,</span><span class=\"token string\">&quot;name&quot;</span><span class=\"token punctuation\">:</span><span class=\"token string\">&quot;wendu&quot;</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>发起多个并发请求:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>response<span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> Future<span class=\"token punctuation\">.</span><span class=\"token function\">wait</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span>dio<span class=\"token punctuation\">.</span><span class=\"token function\">post</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;/info&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>dio<span class=\"token punctuation\">.</span><span class=\"token keyword\">get</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;/token&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>下载文件:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>response<span class=\"token operator\">=</span><span class=\"token keyword\">await</span> dio<span class=\"token punctuation\">.</span><span class=\"token function\">download</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;https://www.google.com/&quot;</span><span class=\"token punctuation\">,</span>_savePath<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>发送 FormData:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>FormData formData <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">FormData<span class=\"token punctuation\">.</span>from</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n   <span class=\"token string\">&quot;name&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;wendux&quot;</span><span class=\"token punctuation\">,</span>\n   <span class=\"token string\">&quot;age&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token number\">25</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\nresponse <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> dio<span class=\"token punctuation\">.</span><span class=\"token function\">post</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;/info&quot;</span><span class=\"token punctuation\">,</span> data<span class=\"token punctuation\">:</span> formData<span class=\"token punctuation\">)</span>\n</code></pre></div><p>如果发送的数据是FormData，则dio会将请求header的<code>contentType</code>设为“multipart/form-data”。</p> <p>通过FormData上传多个文件:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>FormData formData <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">FormData<span class=\"token punctuation\">.</span>from</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n   <span class=\"token string\">&quot;name&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;wendux&quot;</span><span class=\"token punctuation\">,</span>\n   <span class=\"token string\">&quot;age&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token number\">25</span><span class=\"token punctuation\">,</span>\n   <span class=\"token string\">&quot;file1&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">UploadFileInfo</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">File</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;./upload.txt&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;upload1.txt&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n   <span class=\"token string\">&quot;file2&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">UploadFileInfo</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">File</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;./upload.txt&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;upload2.txt&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n     <span class=\"token comment\">// 支持文件数组上传</span>\n   <span class=\"token string\">&quot;files&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>\n      <span class=\"token keyword\">new</span> <span class=\"token class-name\">UploadFileInfo</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">File</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;./example/upload.txt&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;upload.txt&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token keyword\">new</span> <span class=\"token class-name\">UploadFileInfo</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">File</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;./example/upload.txt&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;upload.txt&quot;</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\nresponse <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> dio<span class=\"token punctuation\">.</span><span class=\"token function\">post</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;/info&quot;</span><span class=\"token punctuation\">,</span> data<span class=\"token punctuation\">:</span> formData<span class=\"token punctuation\">)</span>\n</code></pre></div><p>值得一提的是，dio内部仍然使用HttpClient发起的请求，所以代理、请求认证、证书校验等和HttpClient是相同的，我们可以在<code>onHttpClientCreate</code>回调中设置，例如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token punctuation\">(</span>dio<span class=\"token punctuation\">.</span>httpClientAdapter <span class=\"token operator\">as</span> DefaultHttpClientAdapter<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>onHttpClientCreate <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>client<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//设置代理 </span>\n    client<span class=\"token punctuation\">.</span>findProxy <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>uri<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token string\">&quot;PROXY 192.168.1.2:8888&quot;</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//校验证书</span>\n    httpClient<span class=\"token punctuation\">.</span>badCertificateCallback<span class=\"token operator\">=</span><span class=\"token punctuation\">(</span>X509Certificate cert<span class=\"token punctuation\">,</span> String host<span class=\"token punctuation\">,</span> int port<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>cert<span class=\"token punctuation\">.</span>pem<span class=\"token operator\">==</span>PEM<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//证书一致，则允许发送数据</span>\n     <span class=\"token punctuation\">}</span>\n     <span class=\"token keyword\">return</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>   \n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>注意，<code>onHttpClientCreate</code>会在当前dio实例内部需要创建HttpClient时调用，所以通过此回调配置HttpClient会对整个dio实例生效，如果你想针对某个应用请求单独的代理或证书校验策略，可以创建一个新的dio实例即可。</p> <p>怎么样，是不是很简单，除了这些基本的用法，dio还支持请求配置、拦截器等，官方资料比较详细，故本书不再赘述，详情可以参考dio主页：https://github.com/flutterchina/dio 。 下一节我们将使用dio实现一个分块下载器。</p> <h3 id=\"实例\"><a href=\"#实例\" class=\"header-anchor\">#</a> 实例</h3> <p>我们通过Github开放的API来请求flutterchina组织下的所有公开的开源项目，实现：</p> <ol><li>在请求阶段弹出loading</li> <li>请求结束后，如果请求失败，则展示错误信息；如果成功，则将项目名称列表展示出来。</li></ol> <p>代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">_FutureBuilderRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>FutureBuilderRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  Dio _dio <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Dio</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Container</span><span class=\"token punctuation\">(</span>\n      alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">FutureBuilder</span><span class=\"token punctuation\">(</span>\n          future<span class=\"token punctuation\">:</span> _dio<span class=\"token punctuation\">.</span><span class=\"token keyword\">get</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;https://api.github.com/orgs/flutterchina/repos&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> AsyncSnapshot snapshot<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">//请求完成</span>\n            <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>snapshot<span class=\"token punctuation\">.</span>connectionState <span class=\"token operator\">==</span> ConnectionState<span class=\"token punctuation\">.</span>done<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              Response response <span class=\"token operator\">=</span> snapshot<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">;</span>\n              <span class=\"token comment\">//发生错误</span>\n              <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>snapshot<span class=\"token punctuation\">.</span>hasError<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token keyword\">return</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>snapshot<span class=\"token punctuation\">.</span>error<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span>\n              <span class=\"token comment\">//请求成功，通过项目信息构建用于显示项目名称的ListView</span>\n              <span class=\"token keyword\">return</span> <span class=\"token function\">ListView</span><span class=\"token punctuation\">(</span>\n                children<span class=\"token punctuation\">:</span> response<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">.</span>map<span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span>\n                    <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">[</span><span class=\"token string\">&quot;full_name&quot;</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toList</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span>\n            <span class=\"token comment\">//请求未完成时弹出loading</span>\n            <span class=\"token keyword\">return</span> <span class=\"token function\">CircularProgressIndicator</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter11/http.html\" class=\"prev\">\n        11.2 通过HttpClient发起HTTP请求\n      </a></span> <span class=\"next\"><a href=\"/chapter11/download_with_chunks.html\">\n        11.4 实例：Http分块下载\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/135.765bad57.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter11/download_with_chunks.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>11.4 实例：Http分块下载 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/136.46f11ea2.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable open\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" aria-current=\"page\" class=\"active sidebar-link\">11.4 实例：Http分块下载</a><ul class=\"sidebar-sub-headers\"></ul></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_11-4-实例-http分块下载\"><a href=\"#_11-4-实例-http分块下载\" class=\"header-anchor\">#</a> 11.4 实例：Http分块下载</h1> <p>本节将通过一个“Http分块下载”的示例演示一下dio的具体用法。</p> <h3 id=\"原理\"><a href=\"#原理\" class=\"header-anchor\">#</a> 原理</h3> <p>Http协议定义了分块传输的响应header字段，但具体是否支持取决于Server的实现，我们可以指定请求头的&quot;range&quot;字段来验证服务器是否支持分块传输。例如，我们可以利用curl命令来验证：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>bogon:~ duwen$ <span class=\"token function\">curl</span> -H <span class=\"token string\">&quot;Range: bytes=0-10&quot;</span> http://download.dcloud.net.cn/HBuilder.9.0.2.macosx_64.dmg -v\n<span class=\"token comment\"># 请求头</span>\n<span class=\"token operator\">&gt;</span> GET /HBuilder.9.0.2.macosx_64.dmg HTTP/1.1\n<span class=\"token operator\">&gt;</span> Host: download.dcloud.net.cn\n<span class=\"token operator\">&gt;</span> User-Agent: curl/7.54.0\n<span class=\"token operator\">&gt;</span> Accept: */*\n<span class=\"token operator\">&gt;</span> Range: <span class=\"token assign-left variable\">bytes</span><span class=\"token operator\">=</span><span class=\"token number\">0</span>-10\n<span class=\"token comment\"># 响应头</span>\n<span class=\"token operator\">&lt;</span> HTTP/1.1 <span class=\"token number\">206</span> Partial Content\n<span class=\"token operator\">&lt;</span> Content-Type: application/octet-stream\n<span class=\"token operator\">&lt;</span> Content-Length: <span class=\"token number\">11</span>\n<span class=\"token operator\">&lt;</span> Connection: keep-alive\n<span class=\"token operator\">&lt;</span> Date: Thu, <span class=\"token number\">21</span> Feb <span class=\"token number\">2019</span> 06:25:15 GMT\n<span class=\"token operator\">&lt;</span> Content-Range: bytes <span class=\"token number\">0</span>-10/233295878\n\n</code></pre></div><p>我们在请求头中添加&quot;Range: bytes=0-10&quot;的作用是，告诉服务器本次请求我们只想获取文件0-10(包括10，共11字节)这块内容。如果服务器支持分块传输，则响应状态码为206，表示“部分内容”，并且同时响应头中包含“Content-Range”字段，如果不支持则不会包含。我们看看上面“Content-Range”的内容：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>Content-Range: bytes 0-10/233295878\n</code></pre></div><p>0-10表示本次返回的区块，233295878代表文件的总长度，单位都是byte,  也就是该文件大概233M多一点。</p> <p>基于此，我们可以设计一个简单的多线程的文件分块下载器，实现的思路是：</p> <ol><li>先检测是否支持分块传输，如果不支持，则直接下载；若支持，则将剩余内容分块下载。</li> <li>各个分块下载时保存到各自临时文件，等到所有分块下载完后合并临时文件。</li> <li>删除临时文件。</li></ol> <h3 id=\"实现\"><a href=\"#实现\" class=\"header-anchor\">#</a> 实现</h3> <p>下面是整体的流程：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">// 通过第一个分块请求检测服务器是否支持分块传输  </span>\nResponse response <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">downloadChunk</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">,</span> firstChunkSize<span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>response<span class=\"token punctuation\">.</span>statusCode <span class=\"token operator\">==</span> <span class=\"token number\">206</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>    <span class=\"token comment\">//如果支持</span>\n    <span class=\"token comment\">//解析文件总长度，进而算出剩余长度</span>\n    total <span class=\"token operator\">=</span> int<span class=\"token punctuation\">.</span><span class=\"token function\">parse</span><span class=\"token punctuation\">(</span>\n        response<span class=\"token punctuation\">.</span>headers<span class=\"token punctuation\">.</span><span class=\"token function\">value</span><span class=\"token punctuation\">(</span>HttpHeaders<span class=\"token punctuation\">.</span>contentRangeHeader<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">split</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;/&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>last<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    int reserved <span class=\"token operator\">=</span> total <span class=\"token operator\">-</span>\n        int<span class=\"token punctuation\">.</span><span class=\"token function\">parse</span><span class=\"token punctuation\">(</span>response<span class=\"token punctuation\">.</span>headers<span class=\"token punctuation\">.</span><span class=\"token function\">value</span><span class=\"token punctuation\">(</span>HttpHeaders<span class=\"token punctuation\">.</span>contentLengthHeader<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//文件的总块数(包括第一块)</span>\n    int chunk <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>reserved <span class=\"token operator\">/</span> firstChunkSize<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">ceil</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>chunk <span class=\"token operator\">&gt;</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        int chunkSize <span class=\"token operator\">=</span> firstChunkSize<span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>chunk <span class=\"token operator\">&gt;</span> maxChunk <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            chunk <span class=\"token operator\">=</span> maxChunk <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">;</span>\n            chunkSize <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>reserved <span class=\"token operator\">/</span> maxChunk<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">ceil</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n        <span class=\"token keyword\">var</span> futures <span class=\"token operator\">=</span> <span class=\"token operator\">&lt;</span>Future<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span>int i <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span> i <span class=\"token operator\">&lt;</span> maxChunk<span class=\"token punctuation\">;</span> <span class=\"token operator\">++</span>i<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            int start <span class=\"token operator\">=</span> firstChunkSize <span class=\"token operator\">+</span> i <span class=\"token operator\">*</span> chunkSize<span class=\"token punctuation\">;</span>\n            <span class=\"token comment\">//分块下载剩余文件  </span>\n            futures<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token function\">downloadChunk</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">,</span> start<span class=\"token punctuation\">,</span> start <span class=\"token operator\">+</span> chunkSize<span class=\"token punctuation\">,</span> i <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n        <span class=\"token comment\">//等待所有分块全部下载完成</span>\n        <span class=\"token keyword\">await</span> Future<span class=\"token punctuation\">.</span><span class=\"token function\">wait</span><span class=\"token punctuation\">(</span>futures<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token comment\">//合并文件文件  </span>\n    <span class=\"token keyword\">await</span> <span class=\"token function\">mergeTempFiles</span><span class=\"token punctuation\">(</span>chunk<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>下面我们使用dio的<code>download</code> API 实现<code>downloadChunk</code>：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//start 代表当前块的起始位置，end代表结束位置</span>\n<span class=\"token comment\">//no 代表当前是第几块</span>\nFuture<span class=\"token operator\">&lt;</span>Response<span class=\"token operator\">&gt;</span> <span class=\"token function\">downloadChunk</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">,</span> start<span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">,</span> no<span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n  progress<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//progress记录每一块已接收数据的长度</span>\n  <span class=\"token operator\">--</span>end<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">return</span> dio<span class=\"token punctuation\">.</span><span class=\"token function\">download</span><span class=\"token punctuation\">(</span>\n    url<span class=\"token punctuation\">,</span>\n    savePath <span class=\"token operator\">+</span> <span class=\"token string\">&quot;temp$no&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//临时文件按照块的序号命名，方便最后合并</span>\n    onReceiveProgress<span class=\"token punctuation\">:</span> <span class=\"token function\">createCallback</span><span class=\"token punctuation\">(</span>no<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 创建进度回调，后面实现</span>\n    options<span class=\"token punctuation\">:</span> <span class=\"token function\">Options</span><span class=\"token punctuation\">(</span>\n      headers<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span><span class=\"token string\">&quot;range&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;bytes=$start-$end&quot;</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//指定请求的内容区间</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>接下来实现<code>mergeTempFiles</code>:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Future <span class=\"token function\">mergeTempFiles</span><span class=\"token punctuation\">(</span>chunk<span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n  File f <span class=\"token operator\">=</span> <span class=\"token function\">File</span><span class=\"token punctuation\">(</span>savePath <span class=\"token operator\">+</span> <span class=\"token string\">&quot;temp0&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  IOSink ioSink<span class=\"token operator\">=</span> f<span class=\"token punctuation\">.</span><span class=\"token function\">openWrite</span><span class=\"token punctuation\">(</span>mode<span class=\"token punctuation\">:</span> FileMode<span class=\"token punctuation\">.</span>writeOnlyAppend<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">//合并临时文件  </span>\n  <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span>int i <span class=\"token operator\">=</span> <span class=\"token number\">1</span><span class=\"token punctuation\">;</span> i <span class=\"token operator\">&lt;</span> chunk<span class=\"token punctuation\">;</span> <span class=\"token operator\">++</span>i<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    File _f <span class=\"token operator\">=</span> <span class=\"token function\">File</span><span class=\"token punctuation\">(</span>savePath <span class=\"token operator\">+</span> <span class=\"token string\">&quot;temp$i&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">await</span> ioSink<span class=\"token punctuation\">.</span><span class=\"token function\">addStream</span><span class=\"token punctuation\">(</span>_f<span class=\"token punctuation\">.</span><span class=\"token function\">openRead</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">await</span> _f<span class=\"token punctuation\">.</span><span class=\"token function\">delete</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//删除临时文件</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token keyword\">await</span> ioSink<span class=\"token punctuation\">.</span><span class=\"token function\">close</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">await</span> f<span class=\"token punctuation\">.</span><span class=\"token function\">rename</span><span class=\"token punctuation\">(</span>savePath<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//合并后的文件重命名为真正的名称</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>下面我们看一下完整实现：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">/// Downloading by spiting as file in chunks</span>\nFuture <span class=\"token function\">downloadWithChunks</span><span class=\"token punctuation\">(</span>\n  url<span class=\"token punctuation\">,</span>\n  savePath<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n  ProgressCallback onReceiveProgress<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> firstChunkSize <span class=\"token operator\">=</span> <span class=\"token number\">102</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> maxChunk <span class=\"token operator\">=</span> <span class=\"token number\">3</span><span class=\"token punctuation\">;</span>\n\n  int total <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">var</span> dio <span class=\"token operator\">=</span> <span class=\"token function\">Dio</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">var</span> progress <span class=\"token operator\">=</span> <span class=\"token operator\">&lt;</span>int<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token function\">createCallback</span><span class=\"token punctuation\">(</span>no<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>int received<span class=\"token punctuation\">,</span> _<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      progress<span class=\"token punctuation\">[</span>no<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> received<span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>onReceiveProgress <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">&amp;&amp;</span> total <span class=\"token operator\">!=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">onReceiveProgress</span><span class=\"token punctuation\">(</span>progress<span class=\"token punctuation\">.</span><span class=\"token function\">reduce</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>a<span class=\"token punctuation\">,</span> b<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> a <span class=\"token operator\">+</span> b<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> total<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  Future<span class=\"token operator\">&lt;</span>Response<span class=\"token operator\">&gt;</span> <span class=\"token function\">downloadChunk</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">,</span> start<span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">,</span> no<span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    progress<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token operator\">--</span>end<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> dio<span class=\"token punctuation\">.</span><span class=\"token function\">download</span><span class=\"token punctuation\">(</span>\n      url<span class=\"token punctuation\">,</span>\n      savePath <span class=\"token operator\">+</span> <span class=\"token string\">&quot;temp$no&quot;</span><span class=\"token punctuation\">,</span>\n      onReceiveProgress<span class=\"token punctuation\">:</span> <span class=\"token function\">createCallback</span><span class=\"token punctuation\">(</span>no<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      options<span class=\"token punctuation\">:</span> <span class=\"token function\">Options</span><span class=\"token punctuation\">(</span>\n        headers<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span><span class=\"token string\">&quot;range&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;bytes=$start-$end&quot;</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  Future <span class=\"token function\">mergeTempFiles</span><span class=\"token punctuation\">(</span>chunk<span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    File f <span class=\"token operator\">=</span> <span class=\"token function\">File</span><span class=\"token punctuation\">(</span>savePath <span class=\"token operator\">+</span> <span class=\"token string\">&quot;temp0&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    IOSink ioSink<span class=\"token operator\">=</span> f<span class=\"token punctuation\">.</span><span class=\"token function\">openWrite</span><span class=\"token punctuation\">(</span>mode<span class=\"token punctuation\">:</span> FileMode<span class=\"token punctuation\">.</span>writeOnlyAppend<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span>int i <span class=\"token operator\">=</span> <span class=\"token number\">1</span><span class=\"token punctuation\">;</span> i <span class=\"token operator\">&lt;</span> chunk<span class=\"token punctuation\">;</span> <span class=\"token operator\">++</span>i<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      File _f <span class=\"token operator\">=</span> <span class=\"token function\">File</span><span class=\"token punctuation\">(</span>savePath <span class=\"token operator\">+</span> <span class=\"token string\">&quot;temp$i&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">await</span> ioSink<span class=\"token punctuation\">.</span><span class=\"token function\">addStream</span><span class=\"token punctuation\">(</span>_f<span class=\"token punctuation\">.</span><span class=\"token function\">openRead</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">await</span> _f<span class=\"token punctuation\">.</span><span class=\"token function\">delete</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">await</span> ioSink<span class=\"token punctuation\">.</span><span class=\"token function\">close</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">await</span> f<span class=\"token punctuation\">.</span><span class=\"token function\">rename</span><span class=\"token punctuation\">(</span>savePath<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  Response response <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">downloadChunk</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">,</span> firstChunkSize<span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>response<span class=\"token punctuation\">.</span>statusCode <span class=\"token operator\">==</span> <span class=\"token number\">206</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    total <span class=\"token operator\">=</span> int<span class=\"token punctuation\">.</span><span class=\"token function\">parse</span><span class=\"token punctuation\">(</span>\n        response<span class=\"token punctuation\">.</span>headers<span class=\"token punctuation\">.</span><span class=\"token function\">value</span><span class=\"token punctuation\">(</span>HttpHeaders<span class=\"token punctuation\">.</span>contentRangeHeader<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">split</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;/&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>last<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    int reserved <span class=\"token operator\">=</span> total <span class=\"token operator\">-</span>\n        int<span class=\"token punctuation\">.</span><span class=\"token function\">parse</span><span class=\"token punctuation\">(</span>response<span class=\"token punctuation\">.</span>headers<span class=\"token punctuation\">.</span><span class=\"token function\">value</span><span class=\"token punctuation\">(</span>HttpHeaders<span class=\"token punctuation\">.</span>contentLengthHeader<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    int chunk <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>reserved <span class=\"token operator\">/</span> firstChunkSize<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">ceil</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>chunk <span class=\"token operator\">&gt;</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      int chunkSize <span class=\"token operator\">=</span> firstChunkSize<span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>chunk <span class=\"token operator\">&gt;</span> maxChunk <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        chunk <span class=\"token operator\">=</span> maxChunk <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">;</span>\n        chunkSize <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>reserved <span class=\"token operator\">/</span> maxChunk<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">ceil</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n      <span class=\"token keyword\">var</span> futures <span class=\"token operator\">=</span> <span class=\"token operator\">&lt;</span>Future<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span>int i <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span> i <span class=\"token operator\">&lt;</span> maxChunk<span class=\"token punctuation\">;</span> <span class=\"token operator\">++</span>i<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        int start <span class=\"token operator\">=</span> firstChunkSize <span class=\"token operator\">+</span> i <span class=\"token operator\">*</span> chunkSize<span class=\"token punctuation\">;</span>\n        futures<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token function\">downloadChunk</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">,</span> start<span class=\"token punctuation\">,</span> start <span class=\"token operator\">+</span> chunkSize<span class=\"token punctuation\">,</span> i <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n      <span class=\"token keyword\">await</span> Future<span class=\"token punctuation\">.</span><span class=\"token function\">wait</span><span class=\"token punctuation\">(</span>futures<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">await</span> <span class=\"token function\">mergeTempFiles</span><span class=\"token punctuation\">(</span>chunk<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>现在可以进行分块下载了：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">main</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">var</span> url <span class=\"token operator\">=</span> <span class=\"token string\">&quot;http://download.dcloud.net.cn/HBuilder.9.0.2.macosx_64.dmg&quot;</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">var</span> savePath <span class=\"token operator\">=</span> <span class=\"token string\">&quot;./example/HBuilder.9.0.2.macosx_64.dmg&quot;</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">await</span> <span class=\"token function\">downloadWithChunks</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">,</span> savePath<span class=\"token punctuation\">,</span> onReceiveProgress<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>received<span class=\"token punctuation\">,</span> total<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>total <span class=\"token operator\">!=</span> <span class=\"token operator\">-</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;${(received / total * 100).floor()}%&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h3 id=\"思考\"><a href=\"#思考\" class=\"header-anchor\">#</a> 思考</h3> <ol><li><p>分块下载真的能提高下载速度吗？</p> <p>其实下载速度的主要瓶颈是取决于网络速度和服务器的出口速度，如果是同一个数据源，分块下载的意义并不大，因为服务器是同一个，出口速度确定的，主要取决于网速，而上面的例子正式同源分块下载，读者可以自己对比一下分块和不分块的的下载速度。如果有多个下载源，并且每个下载源的出口带宽都是有限制的，这时分块下载可能会更快一下，之所以说“可能”，是由于这并不是一定的，比如有三个源，三个源的出口带宽都为1G/s，而我们设备所连网络的峰值假设只有800M/s，那么瓶颈就在我们的网络。即使我们设备的带宽大于任意一个源，下载速度依然不一定就比单源单线下载快，试想一下，假设有两个源A和B，速度A源是B源的3倍，如果采用分块下载，两个源各下载一半的话，读者可以算一下所需的下载时间，然后再算一下只从A源下载所需的时间，看看哪个更快。</p> <p>分块下载的最终速度受设备所在网络带宽、源出口速度、每个块大小、以及分块的数量等诸多因素影响，实际过程中很难保证速度最优。在实际开发中，读者可可以先测试对比后再决定是否使用。</p></li> <li><p>分块下载有什么实际的用处吗？</p> <p>分块下载还有一个比较使用的场景是断点续传，可以将文件分为若干个块，然后维护一个下载状态文件用以记录每一个块的状态，这样即使在网络中断后，也可以恢复中断前的状态，具体实现读者可以自己尝试一下，还是有一些细节需要特别注意的，比如分块大小多少合适？下载到一半的块如何处理？要不要维护一个任务队列？</p></li></ol></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter11/dio.html\" class=\"prev\">\n        11.3 Http请求-Dio http库\n      </a></span> <span class=\"next\"><a href=\"/chapter11/websocket.html\">\n        使用WebSockets\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/136.46f11ea2.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter11/file_operation.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>11.1 文件操作 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/137.0345664d.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable open\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" aria-current=\"page\" class=\"active sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_11-1-文件操作\"><a href=\"#_11-1-文件操作\" class=\"header-anchor\">#</a> 11.1 文件操作</h1> <p>Dart的IO库包含了文件读写的相关类，它属于Dart语法标准的一部分，所以通过Dart IO库，无论是Dart VM下的脚本还是Flutter，都是通过Dart IO库来操作文件的，不过和Dart VM相比，Flutter有一个重要差异是文件系统路径不同，这是因为Dart VM是运行在PC或服务器操作系统下，而Flutter是运行在移动操作系统中，他们的文件系统会有一些差异。</p> <h4 id=\"app目录\"><a href=\"#app目录\" class=\"header-anchor\">#</a> APP目录</h4> <p>Android和iOS的应用存储目录不同，<a href=\"https://pub.dartlang.org/packages/path_provider\" target=\"_blank\" rel=\"noopener noreferrer\"><code>PathProvider</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 插件提供了一种平台透明的方式来访问设备文件系统上的常用位置。该类当前支持访问两个文件系统位置：</p> <ul><li><strong>临时目录:</strong>  可以使用 <code>getTemporaryDirectory()</code> 来获取临时目录； 系统可随时清除的临时目录（缓存）。在iOS上，这对应于<a href=\"https://developer.apple.com/reference/foundation/1409211-nstemporarydirectory\" target=\"_blank\" rel=\"noopener noreferrer\"><code>NSTemporaryDirectory()</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 返回的值。在Android上，这是<a href=\"https://developer.android.com/reference/android/content/Context.html#getCacheDir()\" target=\"_blank\" rel=\"noopener noreferrer\"><code>getCacheDir()</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>返回的值。</li> <li><strong>文档目录:</strong> 可以使用<code>getApplicationDocumentsDirectory()</code>来获取应用程序的文档目录，该目录用于存储只有自己可以访问的文件。只有当应用程序被卸载时，系统才会清除该目录。在iOS上，这对应于<code>NSDocumentDirectory</code>。在Android上，这是<code>AppData</code>目录。</li> <li><strong>外部存储目录</strong>：可以使用<code>getExternalStorageDirectory()</code>来获取外部存储目录，如SD卡；由于iOS不支持外部目录，所以在iOS下调用该方法会抛出<code>UnsupportedError</code>异常，而在Android下结果是android SDK中<code>getExternalStorageDirectory</code>的返回值。</li></ul> <p>一旦你的Flutter应用程序有一个文件位置的引用，你可以使用<a href=\"https://api.dartlang.org/stable/dart-io/dart-io-library.html\" target=\"_blank\" rel=\"noopener noreferrer\">dart:io<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>API来执行对文件系统的读/写操作。有关使用Dart处理文件和目录的详细内容可以参考Dart语言文档，下面我们看一个简单的例子。</p> <h4 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h4> <p>我们还是以计数器为例，实现在应用退出重启后可以恢复点击次数。 这里，我们使用文件来保存数据：</p> <ol><li><p>引入PathProvider插件；在<code>pubspec.yaml</code>文件中添加如下声明：</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code><span class=\"token key atrule\">path_provider</span><span class=\"token punctuation\">:</span> ^0.4.1\n</code></pre></div><p>添加后，执行<code>flutter packages get</code> 获取一下, 版本号可能随着时间推移会发生变化，读者可以使用最新版。</p></li> <li><p>实现：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'dart:io'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'dart:async'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:path_provider/path_provider.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">FileOperationRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">FileOperationRoute</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>Key key<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  _FileOperationRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_FileOperationRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_FileOperationRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>FileOperationRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  int _counter<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//从文件读取点击次数</span>\n    <span class=\"token function\">_readCounter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>int value<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        _counter <span class=\"token operator\">=</span> value<span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  Future<span class=\"token operator\">&lt;</span>File<span class=\"token operator\">&gt;</span> <span class=\"token function\">_getLocalFile</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 获取应用目录</span>\n    String dir <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">await</span> <span class=\"token function\">getApplicationDocumentsDirectory</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>path<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">File</span><span class=\"token punctuation\">(</span><span class=\"token string\">'$dir/counter.txt'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  Future<span class=\"token operator\">&lt;</span>int<span class=\"token operator\">&gt;</span> <span class=\"token function\">_readCounter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n      File file <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">_getLocalFile</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token comment\">// 读取点击次数（以字符串）</span>\n      String contents <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> file<span class=\"token punctuation\">.</span><span class=\"token function\">readAsString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">return</span> int<span class=\"token punctuation\">.</span><span class=\"token function\">parse</span><span class=\"token punctuation\">(</span>contents<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">on</span> FileSystemException <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  Future<span class=\"token operator\">&lt;</span>Null<span class=\"token operator\">&gt;</span> <span class=\"token function\">_incrementCounter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _counter<span class=\"token operator\">++</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">// 将点击次数以字符串类型写到文件中</span>\n    <span class=\"token keyword\">await</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">await</span> <span class=\"token function\">_getLocalFile</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">writeAsString</span><span class=\"token punctuation\">(</span><span class=\"token string\">'$_counter'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Scaffold</span><span class=\"token punctuation\">(</span>\n      appBar<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AppBar</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'文件操作'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      body<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Center</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'点击了 $_counter 次'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      floatingActionButton<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">FloatingActionButton</span><span class=\"token punctuation\">(</span>\n        onPressed<span class=\"token punctuation\">:</span> _incrementCounter<span class=\"token punctuation\">,</span>\n        tooltip<span class=\"token punctuation\">:</span> <span class=\"token string\">'Increment'</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>add<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面代码比较简单，不再赘述，需要说明的是，本示例只是为了演示文件读写，而在实际开发中，如果要存储一些简单的数据，使用shared_preferences插件会比较简单。</p> <blockquote><p>注意，Dart IO库操作文件的API非常丰富，但本书不是介绍Dart语言的，故不详细说明，读者需要的话可以自行学习。</p></blockquote></li></ol></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"prev\">\n        10.5 自绘实例：圆形背景渐变进度条\n      </a></span> <span class=\"next\"><a href=\"/chapter11/http.html\">\n        11.2 通过HttpClient发起HTTP请求\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/137.0345664d.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter11/http.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>11.2 通过HttpClient发起HTTP请求 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/86.e82005d4.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable open\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" aria-current=\"page\" class=\"active sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_11-2-通过httpclient发起http请求\"><a href=\"#_11-2-通过httpclient发起http请求\" class=\"header-anchor\">#</a> 11.2 通过HttpClient发起HTTP请求</h1> <p>Dart IO库中提供了用于发起Http请求的一些类，我们可以直接使用<code>HttpClient</code>来发起请求。使用<code>HttpClient</code>发起请求分为五步：</p> <ol><li><p>创建一个<code>HttpClient</code>：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code> HttpClient httpClient <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">HttpClient</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div></li> <li><p>打开Http连接，设置请求头：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>HttpClientRequest request <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> httpClient<span class=\"token punctuation\">.</span><span class=\"token function\">getUrl</span><span class=\"token punctuation\">(</span>uri<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>这一步可以使用任意Http Method，如<code>httpClient.post(...)</code>、<code>httpClient.delete(...)</code>等。如果包含Query参数，可以在构建uri时添加，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Uri uri<span class=\"token operator\">=</span><span class=\"token function\">Uri</span><span class=\"token punctuation\">(</span>scheme<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;https&quot;</span><span class=\"token punctuation\">,</span> host<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;flutterchina.club&quot;</span><span class=\"token punctuation\">,</span> queryParameters<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token string\">&quot;xx&quot;</span><span class=\"token punctuation\">:</span><span class=\"token string\">&quot;xx&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token string\">&quot;yy&quot;</span><span class=\"token punctuation\">:</span><span class=\"token string\">&quot;dd&quot;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>通过<code>HttpClientRequest</code>可以设置请求header，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>request<span class=\"token punctuation\">.</span>headers<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;user-agent&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;test&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>如果是post或put等可以携带请求体方法，可以通过HttpClientRequest对象发送request body，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>String payload<span class=\"token operator\">=</span><span class=\"token string\">&quot;...&quot;</span><span class=\"token punctuation\">;</span>\nrequest<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span>utf8<span class=\"token punctuation\">.</span><span class=\"token function\">encode</span><span class=\"token punctuation\">(</span>payload<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> \n<span class=\"token comment\">//request.addStream(_inputStream); //可以直接添加输入流</span>\n</code></pre></div></li> <li><p>等待连接服务器：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>HttpClientResponse response <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> request<span class=\"token punctuation\">.</span><span class=\"token function\">close</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>这一步完成后，请求信息就已经发送给服务器了，返回一个<code>HttpClientResponse</code>对象，它包含响应头（header）和响应流(响应体的Stream)，接下来就可以通过读取响应流来获取响应内容。</p></li> <li><p>读取响应内容：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>String responseBody <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> response<span class=\"token punctuation\">.</span><span class=\"token function\">transform</span><span class=\"token punctuation\">(</span>utf8<span class=\"token punctuation\">.</span>decoder<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">join</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>我们通过读取响应流来获取服务器返回的数据，在读取时我们可以设置编码格式，这里是utf8。</p></li> <li><p>请求结束，关闭<code>HttpClient</code>：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>httpClient<span class=\"token punctuation\">.</span><span class=\"token function\">close</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>关闭client后，通过该client发起的所有请求都会中止。</p></li></ol> <h4 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h4> <p>我们实现一个获取百度首页html的例子，示例效果如图11-1所示：</p> <p>​    <img src=\"/assets/img/11-1.dd10418c.png\" alt=\"图11-1\"></p> <p>点击“获取百度首页”按钮后，会请求百度首页，请求成功后，我们将返回内容显示出来并在控制台打印响应header，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'dart:convert'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'dart:io'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">HttpTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _HttpTestRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_HttpTestRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_HttpTestRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>HttpTestRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  bool _loading <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n  String _text <span class=\"token operator\">=</span> <span class=\"token string\">&quot;&quot;</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">ConstrainedBox</span><span class=\"token punctuation\">(</span>\n      constraints<span class=\"token punctuation\">:</span> BoxConstraints<span class=\"token punctuation\">.</span><span class=\"token function\">expand</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">SingleChildScrollView</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n                child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;获取百度首页&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                onPressed<span class=\"token punctuation\">:</span> _loading <span class=\"token operator\">?</span> <span class=\"token keyword\">null</span> <span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n                  <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                    _loading <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n                    _text <span class=\"token operator\">=</span> <span class=\"token string\">&quot;正在请求...&quot;</span><span class=\"token punctuation\">;</span>\n                  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                  <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n                    <span class=\"token comment\">//创建一个HttpClient</span>\n                    HttpClient httpClient <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">HttpClient</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                    <span class=\"token comment\">//打开Http连接</span>\n                    HttpClientRequest request <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> httpClient<span class=\"token punctuation\">.</span><span class=\"token function\">getUrl</span><span class=\"token punctuation\">(</span>\n                        Uri<span class=\"token punctuation\">.</span><span class=\"token function\">parse</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;https://www.baidu.com&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                    <span class=\"token comment\">//使用iPhone的UA</span>\n                    request<span class=\"token punctuation\">.</span>headers<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;user-agent&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                    <span class=\"token comment\">//等待连接服务器（会将请求信息发送给服务器）</span>\n                    HttpClientResponse response <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> request<span class=\"token punctuation\">.</span><span class=\"token function\">close</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                    <span class=\"token comment\">//读取响应内容</span>\n                    _text <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> response<span class=\"token punctuation\">.</span><span class=\"token function\">transform</span><span class=\"token punctuation\">(</span>utf8<span class=\"token punctuation\">.</span>decoder<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">join</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                    <span class=\"token comment\">//输出响应头</span>\n                    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>response<span class=\"token punctuation\">.</span>headers<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n                    <span class=\"token comment\">//关闭client后，通过该client发起的所有请求都会中止。</span>\n                    httpClient<span class=\"token punctuation\">.</span><span class=\"token function\">close</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n                  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                    _text <span class=\"token operator\">=</span> <span class=\"token string\">&quot;请求失败：$e&quot;</span><span class=\"token punctuation\">;</span>\n                  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">finally</span> <span class=\"token punctuation\">{</span>\n                    <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                      _loading <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n                    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                  <span class=\"token punctuation\">}</span>\n                <span class=\"token punctuation\">}</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n                width<span class=\"token punctuation\">:</span> MediaQuery<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>size<span class=\"token punctuation\">.</span>width<span class=\"token operator\">-</span><span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n                child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>_text<span class=\"token punctuation\">.</span><span class=\"token function\">replaceAll</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">RegExp</span><span class=\"token punctuation\">(</span><span class=\"token string\">r&quot;\\s&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">)</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>控制台输出：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>I/flutter (18545): connection: Keep-Alive\nI/flutter (18545): cache-control: no-cache\nI/flutter (18545): set-cookie: ....  //有多个，省略...\nI/flutter (18545): transfer-encoding: chunked\nI/flutter (18545): date: Tue, 30 Oct 2018 10:00:52 GMT\nI/flutter (18545): content-encoding: gzip\nI/flutter (18545): vary: Accept-Encoding\nI/flutter (18545): strict-transport-security: max-age=172800\nI/flutter (18545): content-type: text/html;charset=utf-8\nI/flutter (18545): tracecode: 00525262401065761290103018, 00522983\n</code></pre></div><h4 id=\"httpclient配置\"><a href=\"#httpclient配置\" class=\"header-anchor\">#</a> HttpClient配置</h4> <p><code>HttpClient</code>有很多属性可以配置，常用的属性列表如下：</p> <table><thead><tr><th>属性</th> <th>含义</th></tr></thead> <tbody><tr><td>idleTimeout</td> <td>对应请求头中的keep-alive字段值，为了避免频繁建立连接，httpClient在请求结束后会保持连接一段时间，超过这个阈值后才会关闭连接。</td></tr> <tr><td>connectionTimeout</td> <td>和服务器建立连接的超时，如果超过这个值则会抛出SocketException异常。</td></tr> <tr><td>maxConnectionsPerHost</td> <td>同一个host，同时允许建立连接的最大数量。</td></tr> <tr><td>autoUncompress</td> <td>对应请求头中的Content-Encoding，如果设置为true，则请求头中Content-Encoding的值为当前HttpClient支持的压缩算法列表，目前只有&quot;gzip&quot;</td></tr> <tr><td>userAgent</td> <td>对应请求头中的User-Agent字段。</td></tr></tbody></table> <p>可以发现，有些属性只是为了更方便的设置请求头，对于这些属性，你完全可以通过<code>HttpClientRequest</code>直接设置header，不同的是通过<code>HttpClient</code>设置的对整个<code>httpClient</code>都生效，而通过<code>HttpClientRequest</code>设置的只对当前请求生效。</p> <h4 id=\"http请求认证\"><a href=\"#http请求认证\" class=\"header-anchor\">#</a> HTTP请求认证</h4> <p>Http协议的认证（Authentication）机制可以用于保护非公开资源。如果Http服务器开启了认证，那么用户在发起请求时就需要携带用户凭据，如果你在浏览器中访问了启用Basic认证的资源时，浏览就会弹出一个登录框，如：</p> <p><img src=\"https://cdn.jsdelivr.net/gh/flutterchina/flutter-in-action@1.0/docs/imgs/image-20181031114207514.png\" alt=\"image-20181031114207514\"></p> <p>我们先看看Basic认证的基本过程：</p> <ol><li><p>客户端发送http请求给服务器，服务器验证该用户是否已经登录验证过了，如果没有的话，  服务器会返回一个401 Unauthozied给客户端，并且在响应header中添加一个 “WWW-Authenticate” 字段，例如：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>WWW-Authenticate: Basic realm=&quot;admin&quot;\n</code></pre></div><p>其中&quot;Basic&quot;为认证方式，realm为用户角色的分组，可以在后台添加分组。</p></li> <li><p>客户端得到响应码后，将用户名和密码进行base64编码（格式为用户名:密码），设置请求头Authorization，继续访问 :</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>Authorization: Basic YXXFISDJFISJFGIJIJG\n</code></pre></div><p>服务器验证用户凭据，如果通过就返回资源内容。</p></li></ol> <p>注意，Http的方式除了Basic认证之外还有：Digest认证、Client认证、Form Based认证等，目前Flutter的HttpClient只支持Basic和Digest两种认证方式，这两种认证方式最大的区别是发送用户凭据时，对于用户凭据的内容，前者只是简单的通过Base64编码（可逆），而后者会进行哈希运算，相对来说安全一点点，但是为了安全起见，<strong>无论是采用Basic认证还是Digest认证，都应该在Https协议下</strong>，这样可以防止抓包和中间人攻击。</p> <p><code>HttpClient</code>关于Http认证的方法和属性：</p> <ol><li><p><code>addCredentials(Uri url, String realm, HttpClientCredentials credentials)</code></p> <p>该方法用于添加用户凭据,如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>httpClient<span class=\"token punctuation\">.</span><span class=\"token function\">addCredentials</span><span class=\"token punctuation\">(</span>_uri<span class=\"token punctuation\">,</span>\n <span class=\"token string\">&quot;admin&quot;</span><span class=\"token punctuation\">,</span> \n  <span class=\"token keyword\">new</span> <span class=\"token class-name\">HttpClientBasicCredentials</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;username&quot;</span><span class=\"token punctuation\">,</span><span class=\"token string\">&quot;password&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//Basic认证凭据</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>如果是Digest认证，可以创建Digest认证凭据：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">HttpClientDigestCredentials</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;username&quot;</span><span class=\"token punctuation\">,</span><span class=\"token string\">&quot;password&quot;</span><span class=\"token punctuation\">)</span>\n</code></pre></div></li> <li><p><code>authenticate(Future&lt;bool&gt; f(Uri url, String scheme, String realm))</code></p> <p>这是一个setter，类型是一个回调，当服务器需要用户凭据且该用户凭据未被添加时，httpClient会调用此回调，在这个回调当中，一般会调用<code>addCredential()</code>来动态添加用户凭证，例如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>httpClient<span class=\"token punctuation\">.</span>authenticate<span class=\"token operator\">=</span><span class=\"token punctuation\">(</span>Uri url<span class=\"token punctuation\">,</span> String scheme<span class=\"token punctuation\">,</span> String realm<span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span><span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">.</span>host<span class=\"token operator\">==</span><span class=\"token string\">&quot;xx.com&quot;</span> <span class=\"token operator\">&amp;&amp;</span> realm<span class=\"token operator\">==</span><span class=\"token string\">&quot;admin&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    httpClient<span class=\"token punctuation\">.</span><span class=\"token function\">addCredentials</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">,</span>\n      <span class=\"token string\">&quot;admin&quot;</span><span class=\"token punctuation\">,</span>\n      <span class=\"token keyword\">new</span> <span class=\"token class-name\">HttpClientBasicCredentials</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;username&quot;</span><span class=\"token punctuation\">,</span><span class=\"token string\">&quot;pwd&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> \n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token keyword\">return</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>一个建议是，如果所有请求都需要认证，那么应该在HttpClient初始化时就调用<code>addCredentials()</code>来添加全局凭证，而不是去动态添加。</p></li></ol> <h4 id=\"代理\"><a href=\"#代理\" class=\"header-anchor\">#</a> 代理</h4> <p>可以通过<code>findProxy</code>来设置代理策略，例如，我们要将所有请求通过代理服务器（192.168.1.2:8888）发送出去：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>  client<span class=\"token punctuation\">.</span>findProxy <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>uri<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 如果需要过滤uri，可以手动判断</span>\n    <span class=\"token keyword\">return</span> <span class=\"token string\">&quot;PROXY 192.168.1.2:8888&quot;</span><span class=\"token punctuation\">;</span>\n <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p><code>findProxy</code> 回调返回值是一个遵循浏览器PAC脚本格式的字符串，详情可以查看API文档，如果不需要代理，返回&quot;DIRECT&quot;即可。</p> <p>在APP开发中，很多时候我们需要抓包来调试，而抓包软件(如charles)就是一个代理，这时我们就可以将请求发送到我们的抓包软件，我们就可以在抓包软件中看到请求的数据了。</p> <p>有时代理服务器也启用了身份验证，这和http协议的认证是相似的，HttpClient提供了对应的Proxy认证方法和属性：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">set</span> <span class=\"token function\">authenticateProxy</span><span class=\"token punctuation\">(</span>\n    Future<span class=\"token operator\">&lt;</span>bool<span class=\"token operator\">&gt;</span> <span class=\"token function\">f</span><span class=\"token punctuation\">(</span>String host<span class=\"token punctuation\">,</span> int port<span class=\"token punctuation\">,</span> String scheme<span class=\"token punctuation\">,</span> String realm<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">void</span> <span class=\"token function\">addProxyCredentials</span><span class=\"token punctuation\">(</span>\n    String host<span class=\"token punctuation\">,</span> int port<span class=\"token punctuation\">,</span> String realm<span class=\"token punctuation\">,</span> HttpClientCredentials credentials<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>他们的使用方法和上面“HTTP请求认证”一节中介绍的<code>addCredentials</code>和<code>authenticate</code> 相同，故不再赘述。</p> <h4 id=\"证书校验\"><a href=\"#证书校验\" class=\"header-anchor\">#</a> 证书校验</h4> <p>Https中为了防止通过伪造证书而发起的中间人攻击，客户端应该对自签名或非CA颁发的证书进行校验。<code>HttpClient</code>对证书校验的逻辑如下：</p> <ol><li>如果请求的Https证书是可信CA颁发的，并且访问host包含在证书的domain列表中(或者符合通配规则)并且证书未过期，则验证通过。</li> <li>如果第一步验证失败，但在创建HttpClient时，已经通过SecurityContext将证书添加到证书信任链中，那么当服务器返回的证书在信任链中的话，则验证通过。</li> <li>如果1、2验证都失败了，如果用户提供了<code>badCertificateCallback</code>回调，则会调用它，如果回调返回<code>true</code>，则允许继续链接，如果返回<code>false</code>，则终止链接。</li></ol> <p>综上所述，我们的证书校验其实就是提供一个<code>badCertificateCallback</code>回调，下面通过一个示例来说明。</p> <h5 id=\"示例-2\"><a href=\"#示例-2\" class=\"header-anchor\">#</a> 示例</h5> <p>假设我们的后台服务使用的是自签名证书，证书格式是PEM格式，我们将证书的内容保存在本地字符串中，那么我们的校验逻辑如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>String PEM<span class=\"token operator\">=</span><span class=\"token string\">&quot;XXXXX&quot;</span><span class=\"token punctuation\">;</span><span class=\"token comment\">//可以从文件读取</span>\n<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\nhttpClient<span class=\"token punctuation\">.</span>badCertificateCallback<span class=\"token operator\">=</span><span class=\"token punctuation\">(</span>X509Certificate cert<span class=\"token punctuation\">,</span> String host<span class=\"token punctuation\">,</span> int port<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>cert<span class=\"token punctuation\">.</span>pem<span class=\"token operator\">==</span>PEM<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//证书一致，则允许发送数据</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token keyword\">return</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p><code>X509Certificate</code>是证书的标准格式，包含了证书除私钥外所有信息，读者可以自行查阅文档。另外，上面的示例没有校验host，是因为只要服务器返回的证书内容和本地的保存一致就已经能证明是我们的服务器了（而不是中间人），host验证通常是为了防止证书和域名不匹配。</p> <p>对于自签名的证书，我们也可以将其添加到本地证书信任链中，这样证书验证时就会自动通过，而不会再走到<code>badCertificateCallback</code>回调中：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>SecurityContext sc<span class=\"token operator\">=</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">SecurityContext</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">//file为证书路径</span>\nsc<span class=\"token punctuation\">.</span><span class=\"token function\">setTrustedCertificates</span><span class=\"token punctuation\">(</span>file<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">//创建一个HttpClient</span>\nHttpClient httpClient <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">HttpClient</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">:</span> sc<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>注意，通过<code>setTrustedCertificates()</code>设置的证书格式必须为PEM或PKCS12，如果证书格式为PKCS12，则需将证书密码传入，这样则会在代码中暴露证书密码，所以客户端证书校验不建议使用PKCS12格式的证书。</p> <h4 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h4> <p>值得注意的是，<code>HttpClient</code>提供的这些属性和方法最终都会作用在请求header里，我们完全可以通过手动去设置header来实现，之所以提供这些方法，只是为了方便开发者而已。另外，Http协议是一个非常重要的、使用最多的网络协议，每一个开发者都应该对http协议非常熟悉。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter11/file_operation.html\" class=\"prev\">\n        11.1 文件操作\n      </a></span> <span class=\"next\"><a href=\"/chapter11/dio.html\">\n        11.3 Http请求-Dio http库\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/86.e82005d4.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter11/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>本章目录 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/138.1a184d5a.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"本章目录\"><a href=\"#本章目录\" class=\"header-anchor\">#</a> 本章目录</h1> <ul><li><a href=\"/chapter11/file_operation.html\">11.1：文件操作</a></li> <li><a href=\"/chapter11/http.html\">11.2：Http请求-HttpClient</a></li> <li><a href=\"/chapter11/dio.html\">11.3：Http请求-Dio package</a></li> <li><a href=\"/chapter11/download_with_chunks.html\">11.4：实例：Http分块下载</a></li> <li><a href=\"/chapter11/websocket.html\">11.5：WebSocket</a></li> <li><a href=\"/chapter11/socket.html\">11.6：使用Socket API</a></li> <li><a href=\"/chapter11/json_model.html\">11.7：Json转Dart Model类</a></li></ul></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/138.1a184d5a.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter11/json_model.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>11.7 Json转Dart Model类 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/87.e1f95a69.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable open\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" aria-current=\"page\" class=\"active sidebar-link\">11.7 Json转Dart Model类</a><ul class=\"sidebar-sub-headers\"></ul></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_11-7-json转dart-model类\"><a href=\"#_11-7-json转dart-model类\" class=\"header-anchor\">#</a> 11.7 Json转Dart Model类</h1> <p>在实战中，后台接口往往会返回一些结构化数据，如JSON、XML等，如之前我们请求Github API的示例，它返回的数据就是JSON格式的字符串，为了方便我们在代码中操作JSON，我们先将JSON格式的字符串转为Dart对象，这个可以通过<code>dart:convert</code>中内置的JSON解码器json.decode() 来实现，该方法可以根据JSON字符串具体内容将其转为List或Map，这样我们就可以通过他们来查找所需的值，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//一个JSON格式的用户列表字符串</span>\nString jsonStr<span class=\"token operator\">=</span><span class=\"token string\">'[{&quot;name&quot;:&quot;Jack&quot;},{&quot;name&quot;:&quot;Rose&quot;}]'</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">//将JSON字符串转为Dart对象(此处是List)</span>\nList items<span class=\"token operator\">=</span>json<span class=\"token punctuation\">.</span><span class=\"token function\">decode</span><span class=\"token punctuation\">(</span>jsonStr<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">//输出第一个用户的姓名</span>\n<span class=\"token function\">print</span><span class=\"token punctuation\">(</span>items<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">[</span><span class=\"token string\">&quot;name&quot;</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>通过json.decode() 将JSON字符串转为List/Map的方法比较简单，它没有外部依赖或其它的设置，对于小项目很方便。但当项目变大时，这种手动编写序列化逻辑可能变得难以管理且容易出错，例如有如下JSON:</p> <div class=\"language-json extra-class\"><pre class=\"language-json\"><code><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;name&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;John Smith&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;email&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;john@example.com&quot;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们可以通过调用<code>json.decode</code>方法来解码JSON ，使用JSON字符串作为参数:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Map<span class=\"token operator\">&lt;</span>String<span class=\"token punctuation\">,</span> <span class=\"token keyword\">dynamic</span><span class=\"token operator\">&gt;</span> user <span class=\"token operator\">=</span> json<span class=\"token punctuation\">.</span><span class=\"token function\">decode</span><span class=\"token punctuation\">(</span>json<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Howdy, ${user['</span>name<span class=\"token string\">']}!'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">'We sent the verification link to ${user['</span>email<span class=\"token string\">']}.'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>由于<code>json.decode()</code>仅返回一个<code>Map&lt;String, dynamic&gt;</code>，这意味着直到运行时我们才知道值的类型。 通过这种方法，我们失去了大部分静态类型语言特性：类型安全、自动补全和最重要的编译时异常。这样一来，我们的代码可能会变得非常容易出错。例如，当我们访问<code>name</code>或<code>email</code>字段时，我们输入的很快，导致字段名打错了。但由于这个JSON在map结构中，所以编译器不知道这个错误的字段名，所以编译时不会报错。</p> <p>其实，这个问题在很多平台上都会遇到，而也早就有了好的解决方法即“Json Model化”，具体做法就是，通过预定义一些与Json结构对应的Model类，然后在请求到数据后再动态根据数据创建出Model类的实例。这样一来，在开发阶段我们使用的是Model类的实例，而不再是Map/List，这样访问内部属性时就不会发生拼写错误。例如，我们可以通过引入一个简单的模型类(Model class)来解决前面提到的问题，我们称之为<code>User</code>。在User类内部，我们有：</p> <ul><li>一个<code>User.fromJson</code> 构造函数, 用于从一个map构造出一个 <code>User</code>实例 map structure</li> <li>一个<code>toJson</code> 方法, 将 <code>User</code> 实例转化为一个map.</li></ul> <p>这样，调用代码现在可以具有类型安全、自动补全字段（name和email）以及编译时异常。如果我们将拼写错误字段视为<code>int</code>类型而不是<code>String</code>， 那么我们的代码就不会通过编译，而不是在运行时崩溃。</p> <p><strong>user.dart</strong></p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">User</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">final</span> String name<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> String email<span class=\"token punctuation\">;</span>\n\n  <span class=\"token function\">User</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>name<span class=\"token punctuation\">,</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>email<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  User<span class=\"token punctuation\">.</span><span class=\"token function\">fromJson</span><span class=\"token punctuation\">(</span>Map<span class=\"token operator\">&lt;</span>String<span class=\"token punctuation\">,</span> <span class=\"token keyword\">dynamic</span><span class=\"token operator\">&gt;</span> json<span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">:</span> name <span class=\"token operator\">=</span> json<span class=\"token punctuation\">[</span><span class=\"token string\">'name'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        email <span class=\"token operator\">=</span> json<span class=\"token punctuation\">[</span><span class=\"token string\">'email'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n  Map<span class=\"token operator\">&lt;</span>String<span class=\"token punctuation\">,</span> <span class=\"token keyword\">dynamic</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">toJson</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span>\n    <span class=\"token operator\">&lt;</span>String<span class=\"token punctuation\">,</span> <span class=\"token keyword\">dynamic</span><span class=\"token operator\">&gt;</span><span class=\"token punctuation\">{</span>\n      <span class=\"token string\">'name'</span><span class=\"token punctuation\">:</span> name<span class=\"token punctuation\">,</span>\n      <span class=\"token string\">'email'</span><span class=\"token punctuation\">:</span> email<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>现在，序列化逻辑移到了模型本身内部。采用这种新方法，我们可以非常容易地反序列化user.</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Map userMap <span class=\"token operator\">=</span> json<span class=\"token punctuation\">.</span><span class=\"token function\">decode</span><span class=\"token punctuation\">(</span>json<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">var</span> user <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">User<span class=\"token punctuation\">.</span>fromJson</span><span class=\"token punctuation\">(</span>userMap<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Howdy, ${user.name}!'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">'We sent the verification link to ${user.email}.'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>要序列化一个user，我们只是将该<code>User</code>对象传递给该<code>json.encode</code>方法。我们不需要手动调用<code>toJson</code>这个方法，因为`JSON.encode内部会自动调用。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>String json <span class=\"token operator\">=</span> json<span class=\"token punctuation\">.</span><span class=\"token function\">encode</span><span class=\"token punctuation\">(</span>user<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>这样，调用代码就不用担心JSON序列化了，但是，Model类还是必须的。在实践中，<code>User.fromJson</code>和<code>User.toJson</code>方法都需要单元测试到位，以验证正确的行为。</p> <p>另外，实际场景中，JSON对象很少会这么简单，嵌套的JSON对象并不罕见，如果有什么能为我们自动处理JSON序列化，那将会非常好。幸运的是，有！</p> <h3 id=\"自动生成model\"><a href=\"#自动生成model\" class=\"header-anchor\">#</a> 自动生成Model</h3> <p>尽管还有其他库可用，但在本书中，我们介绍一下官方推荐的<a href=\"https://pub.dartlang.org/packages/json_serializable\" target=\"_blank\" rel=\"noopener noreferrer\">json_serializable package<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>包。 它是一个自动化的源代码生成器，可以在开发阶段为我们生成JSON序列化模板，这样一来，由于序列化代码不再由我们手写和维护，我们将运行时产生JSON序列化异常的风险降至最低。</p> <h3 id=\"在项目中设置json-serializable\"><a href=\"#在项目中设置json-serializable\" class=\"header-anchor\">#</a> 在项目中设置json_serializable</h3> <p>要包含<code>json_serializable</code>到我们的项目中，我们需要一个常规和两个<strong>开发依赖</strong>项。简而言之，<strong>开发依赖项</strong>是不包含在我们的应用程序源代码中的依赖项，它是开发过程中的一些辅助工具、脚本，和node中的开发依赖项相似。</p> <p><strong>pubspec.yaml</strong></p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code><span class=\"token key atrule\">dependencies</span><span class=\"token punctuation\">:</span>\n  <span class=\"token comment\"># Your other regular dependencies here</span>\n  <span class=\"token key atrule\">json_annotation</span><span class=\"token punctuation\">:</span> ^2.0.0\n\n<span class=\"token key atrule\">dev_dependencies</span><span class=\"token punctuation\">:</span>\n  <span class=\"token comment\"># Your other dev_dependencies here</span>\n  <span class=\"token key atrule\">build_runner</span><span class=\"token punctuation\">:</span> ^1.0.0\n  <span class=\"token key atrule\">json_serializable</span><span class=\"token punctuation\">:</span> ^2.0.0\n</code></pre></div><p>在您的项目根文件夹中运行 <code>flutter packages get</code> (或者在编辑器中点击 “Packages Get”) 以在项目中使用这些新的依赖项.</p> <h3 id=\"以json-serializable的方式创建model类\"><a href=\"#以json-serializable的方式创建model类\" class=\"header-anchor\">#</a> 以json_serializable的方式创建model类</h3> <p>让我们看看如何将我们的<code>User</code>类转换为一个<code>json_serializable</code>。为了简单起见，我们使用前面示例中的简化JSON model。</p> <p><strong>user.dart</strong></p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:json_annotation/json_annotation.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// user.g.dart 将在我们运行生成命令后自动生成</span>\n<span class=\"token keyword\">part</span> <span class=\"token string\">'user.g.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">///这个标注是告诉生成器，这个类是需要生成Model类的</span>\n<span class=\"token metadata symbol\">@JsonSerializable</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">User</span><span class=\"token punctuation\">{</span>\n  <span class=\"token function\">User</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>name<span class=\"token punctuation\">,</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>email<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  String name<span class=\"token punctuation\">;</span>\n  String email<span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">//不同的类使用不同的mixin即可</span>\n  <span class=\"token keyword\">factory</span> User<span class=\"token punctuation\">.</span><span class=\"token function\">fromJson</span><span class=\"token punctuation\">(</span>Map<span class=\"token operator\">&lt;</span>String<span class=\"token punctuation\">,</span> <span class=\"token keyword\">dynamic</span><span class=\"token operator\">&gt;</span> json<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _$<span class=\"token function\">UserFromJson</span><span class=\"token punctuation\">(</span>json<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  Map<span class=\"token operator\">&lt;</span>String<span class=\"token punctuation\">,</span> <span class=\"token keyword\">dynamic</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">toJson</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _$<span class=\"token function\">UserToJson</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>  \n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>有了上面的设置，源码生成器将生成用于序列化<code>name</code>和<code>email</code>字段的JSON代码。</p> <p>如果需要，自定义命名策略也很容易。例如，如果我们正在使用的API返回带有_snake_case_的对象，但我们想在我们的模型中使用_lowerCamelCase_， 那么我们可以使用@JsonKey标注：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//显式关联JSON字段名与Model属性的对应关系 </span>\n<span class=\"token metadata symbol\">@JsonKey</span><span class=\"token punctuation\">(</span>name<span class=\"token punctuation\">:</span> <span class=\"token string\">'registration_date_millis'</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">final</span> int registrationDateMillis<span class=\"token punctuation\">;</span>\n</code></pre></div><h3 id=\"运行代码生成程序\"><a href=\"#运行代码生成程序\" class=\"header-anchor\">#</a> 运行代码生成程序</h3> <p><code>json_serializable</code>第一次创建类时，您会看到与图11-4类似的错误。</p> <p><img src=\"/assets/img/11-4.b1cfbd03.png\" alt=\"ide_warning\"></p> <p>这些错误是完全正常的，这是因为Model类的生成代码还不存在。为了解决这个问题，我们必须运行代码生成器来为我们生成序列化模板。有两种运行代码生成器的方法：</p> <h4 id=\"一次性生成\"><a href=\"#一次性生成\" class=\"header-anchor\">#</a> 一次性生成</h4> <p>通过在我们的项目根目录下运行:</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>flutter packages pub run build_runner build\n</code></pre></div><p>这触发了一次性构建，我们可以在需要时为我们的Model生成json序列化代码，它通过我们的源文件，找出需要生成Model类的源文件（包含@JsonSerializable标注的）来生成对应的.g.dart文件。一个好的建议是将所有Model类放在一个单独的目录下，然后在该目录下执行命令。</p> <p>虽然这非常方便，但如果我们不需要每次在Model类中进行更改时都要手动运行构建命令的话会更好。</p> <h4 id=\"持续生成\"><a href=\"#持续生成\" class=\"header-anchor\">#</a> 持续生成</h4> <p>使用_watcher_可以使我们的源代码生成的过程更加方便。它会监视我们项目中文件的变化，并在需要时自动构建必要的文件，我们可以通过<code>flutter packages pub run build_runner watch</code>在项目根目录下运行来启动_watcher_。只需启动一次观察器，然后它就会在后台运行，这是安全的。</p> <h3 id=\"自动化生成模板\"><a href=\"#自动化生成模板\" class=\"header-anchor\">#</a> 自动化生成模板</h3> <p>上面的方法有一个最大的问题就是要为每一个json写模板，这是比较枯燥的。如果有一个工具可以直接根据JSON文本生成模板，那我们就能彻底解放双手了。笔者自己用dart实现了一个脚本，它可以自动生成模板，并直接将JSON转为Model类，下面我们看看怎么做：</p> <ol><li><p>定义一个&quot;模板的模板&quot;，名为&quot;template.dart&quot;：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:json_annotation/json_annotation.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token operator\">%</span>t\n<span class=\"token keyword\">part</span> <span class=\"token string\">'%s.g.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token metadata symbol\">@JsonSerializable</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">class</span> <span class=\"token operator\">%</span>s <span class=\"token punctuation\">{</span>\n    <span class=\"token operator\">%</span><span class=\"token function\">s</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token operator\">%</span>s\n    <span class=\"token keyword\">factory</span> <span class=\"token operator\">%</span>s<span class=\"token punctuation\">.</span><span class=\"token function\">fromJson</span><span class=\"token punctuation\">(</span>Map<span class=\"token operator\">&lt;</span>String<span class=\"token punctuation\">,</span><span class=\"token keyword\">dynamic</span><span class=\"token operator\">&gt;</span> json<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _$<span class=\"token operator\">%</span><span class=\"token function\">sFromJson</span><span class=\"token punctuation\">(</span>json<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    Map<span class=\"token operator\">&lt;</span>String<span class=\"token punctuation\">,</span> <span class=\"token keyword\">dynamic</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">toJson</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _$<span class=\"token operator\">%</span><span class=\"token function\">sToJson</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>模板中的“%t”、“%s”为占位符，将在脚本运行时动态被替换为合适的导入头和类名。</p></li> <li><p>写一个自动生成模板的脚本(mo.dart)，它可以根据指定的JSON目录，遍历生成模板，在生成时我们定义一些规则：</p> <ul><li>如果JSON文件名以下划线“_”开始，则忽略此JSON文件。</li> <li>复杂的JSON对象往往会出现嵌套，我们可以通过一个特殊标志来手动指定嵌套的对象（后面举例）。</li></ul> <p>脚本我们通过Dart来写，源码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'dart:convert'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'dart:io'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:path/path.dart'</span> <span class=\"token operator\">as</span> path<span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">const</span> TAG<span class=\"token operator\">=</span><span class=\"token string\">&quot;\\$&quot;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">const</span> SRC<span class=\"token operator\">=</span><span class=\"token string\">&quot;./json&quot;</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//JSON 目录</span>\n<span class=\"token keyword\">const</span> DIST<span class=\"token operator\">=</span><span class=\"token string\">&quot;lib/models/&quot;</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//输出model目录</span>\n\n<span class=\"token keyword\">void</span> <span class=\"token function\">walk</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span> <span class=\"token comment\">//遍历JSON目录生成模板</span>\n  <span class=\"token keyword\">var</span> src <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Directory</span><span class=\"token punctuation\">(</span>SRC<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">var</span> list <span class=\"token operator\">=</span> src<span class=\"token punctuation\">.</span><span class=\"token function\">listSync</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">var</span> template<span class=\"token operator\">=</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">File</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;./template.dart&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">readAsStringSync</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  File file<span class=\"token punctuation\">;</span>\n  list<span class=\"token punctuation\">.</span><span class=\"token function\">forEach</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>f<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>FileSystemEntity<span class=\"token punctuation\">.</span><span class=\"token function\">isFileSync</span><span class=\"token punctuation\">(</span>f<span class=\"token punctuation\">.</span>path<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      file <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">File</span><span class=\"token punctuation\">(</span>f<span class=\"token punctuation\">.</span>path<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">var</span> paths<span class=\"token operator\">=</span>path<span class=\"token punctuation\">.</span><span class=\"token function\">basename</span><span class=\"token punctuation\">(</span>f<span class=\"token punctuation\">.</span>path<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">split</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;.&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      String name<span class=\"token operator\">=</span>paths<span class=\"token punctuation\">.</span>first<span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>paths<span class=\"token punctuation\">.</span>last<span class=\"token punctuation\">.</span><span class=\"token function\">toLowerCase</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">!=</span><span class=\"token string\">&quot;json&quot;</span><span class=\"token operator\">||</span>name<span class=\"token punctuation\">.</span><span class=\"token function\">startsWith</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;_&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> <span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>name<span class=\"token punctuation\">.</span><span class=\"token function\">startsWith</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;_&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span><span class=\"token punctuation\">;</span>\n      <span class=\"token comment\">//下面生成模板</span>\n      <span class=\"token keyword\">var</span> map <span class=\"token operator\">=</span> json<span class=\"token punctuation\">.</span><span class=\"token function\">decode</span><span class=\"token punctuation\">(</span>file<span class=\"token punctuation\">.</span><span class=\"token function\">readAsStringSync</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token comment\">//为了避免重复导入相同的包，我们用Set来保存生成的import语句。</span>\n      <span class=\"token keyword\">var</span> <span class=\"token keyword\">set</span><span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Set</span><span class=\"token operator\">&lt;</span>String<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      StringBuffer attrs<span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">StringBuffer</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">(</span>map <span class=\"token operator\">as</span> Map<span class=\"token operator\">&lt;</span>String<span class=\"token punctuation\">,</span> <span class=\"token keyword\">dynamic</span><span class=\"token operator\">&gt;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">forEach</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">,</span> v<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">.</span><span class=\"token function\">startsWith</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;_&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> <span class=\"token punctuation\">;</span>\n          attrs<span class=\"token punctuation\">.</span><span class=\"token function\">write</span><span class=\"token punctuation\">(</span><span class=\"token function\">getType</span><span class=\"token punctuation\">(</span>v<span class=\"token punctuation\">,</span><span class=\"token keyword\">set</span><span class=\"token punctuation\">,</span>name<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          attrs<span class=\"token punctuation\">.</span><span class=\"token function\">write</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot; &quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          attrs<span class=\"token punctuation\">.</span><span class=\"token function\">write</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          attrs<span class=\"token punctuation\">.</span><span class=\"token function\">writeln</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;;&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          attrs<span class=\"token punctuation\">.</span><span class=\"token function\">write</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;    &quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      String  className<span class=\"token operator\">=</span>name<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span><span class=\"token function\">toUpperCase</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">+</span>name<span class=\"token punctuation\">.</span><span class=\"token function\">substring</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">var</span> dist<span class=\"token operator\">=</span><span class=\"token function\">format</span><span class=\"token punctuation\">(</span>template<span class=\"token punctuation\">,</span><span class=\"token punctuation\">[</span>name<span class=\"token punctuation\">,</span>className<span class=\"token punctuation\">,</span>className<span class=\"token punctuation\">,</span>attrs<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                                className<span class=\"token punctuation\">,</span>className<span class=\"token punctuation\">,</span>className<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">var</span> _import<span class=\"token operator\">=</span><span class=\"token keyword\">set</span><span class=\"token punctuation\">.</span><span class=\"token function\">join</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;;\\r\\n&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      _import<span class=\"token operator\">+=</span>_import<span class=\"token punctuation\">.</span>isEmpty<span class=\"token operator\">?</span><span class=\"token string\">&quot;&quot;</span><span class=\"token punctuation\">:</span><span class=\"token string\">&quot;;&quot;</span><span class=\"token punctuation\">;</span>\n      dist<span class=\"token operator\">=</span>dist<span class=\"token punctuation\">.</span><span class=\"token function\">replaceFirst</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;%t&quot;</span><span class=\"token punctuation\">,</span>_import <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token comment\">//将生成的模板输出</span>\n      <span class=\"token keyword\">new</span> <span class=\"token class-name\">File</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;$DIST$name.dart&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">writeAsStringSync</span><span class=\"token punctuation\">(</span>dist<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\nString <span class=\"token function\">changeFirstChar</span><span class=\"token punctuation\">(</span>String str<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>bool upper<span class=\"token operator\">=</span><span class=\"token boolean\">true</span><span class=\"token punctuation\">]</span> <span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>upper<span class=\"token operator\">?</span>str<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span><span class=\"token function\">toUpperCase</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>str<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span><span class=\"token function\">toLowerCase</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token operator\">+</span>str<span class=\"token punctuation\">.</span><span class=\"token function\">substring</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">//将JSON类型转为对应的dart类型</span>\n String <span class=\"token function\">getType</span><span class=\"token punctuation\">(</span>v<span class=\"token punctuation\">,</span>Set<span class=\"token operator\">&lt;</span>String<span class=\"token operator\">&gt;</span> <span class=\"token keyword\">set</span><span class=\"token punctuation\">,</span>String current<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n  current<span class=\"token operator\">=</span>current<span class=\"token punctuation\">.</span><span class=\"token function\">toLowerCase</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>v <span class=\"token operator\">is</span> bool<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token string\">&quot;bool&quot;</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token keyword\">else</span> <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>v <span class=\"token operator\">is</span> num<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token string\">&quot;num&quot;</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token keyword\">else</span> <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>v <span class=\"token operator\">is</span> Map<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token string\">&quot;Map&lt;String,dynamic&gt;&quot;</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token keyword\">else</span> <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>v <span class=\"token operator\">is</span> List<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token string\">&quot;List&quot;</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token keyword\">else</span> <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>v <span class=\"token operator\">is</span> String<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span> <span class=\"token comment\">//处理特殊标志</span>\n    <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>v<span class=\"token punctuation\">.</span><span class=\"token function\">startsWith</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;$TAG[]&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">var</span> className<span class=\"token operator\">=</span><span class=\"token function\">changeFirstChar</span><span class=\"token punctuation\">(</span>v<span class=\"token punctuation\">.</span><span class=\"token function\">substring</span><span class=\"token punctuation\">(</span><span class=\"token number\">3</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>className<span class=\"token punctuation\">.</span><span class=\"token function\">toLowerCase</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">!=</span>current<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">set</span><span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token string\">'import &quot;$className.dart&quot;'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n      <span class=\"token keyword\">return</span> <span class=\"token string\">&quot;List&lt;${changeFirstChar(className)}&gt;&quot;</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token punctuation\">}</span><span class=\"token keyword\">else</span> <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>v<span class=\"token punctuation\">.</span><span class=\"token function\">startsWith</span><span class=\"token punctuation\">(</span>TAG<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">var</span> fileName<span class=\"token operator\">=</span><span class=\"token function\">changeFirstChar</span><span class=\"token punctuation\">(</span>v<span class=\"token punctuation\">.</span><span class=\"token function\">substring</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>fileName<span class=\"token punctuation\">.</span><span class=\"token function\">toLowerCase</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">!=</span>current<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">set</span><span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token string\">'import &quot;$fileName.dart&quot;'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n      <span class=\"token keyword\">return</span> <span class=\"token function\">changeFirstChar</span><span class=\"token punctuation\">(</span>fileName<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">return</span> <span class=\"token string\">&quot;String&quot;</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token keyword\">else</span><span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token string\">&quot;String&quot;</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n <span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">//替换模板占位符</span>\nString <span class=\"token function\">format</span><span class=\"token punctuation\">(</span>String fmt<span class=\"token punctuation\">,</span> List<span class=\"token operator\">&lt;</span>Object<span class=\"token operator\">&gt;</span> params<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  int matchIndex <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span>\n  String <span class=\"token function\">replace</span><span class=\"token punctuation\">(</span>Match m<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>matchIndex <span class=\"token operator\">&lt;</span> params<span class=\"token punctuation\">.</span>length<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">switch</span> <span class=\"token punctuation\">(</span>m<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">case</span> <span class=\"token string\">&quot;%s&quot;</span><span class=\"token punctuation\">:</span>\n          <span class=\"token keyword\">return</span> params<span class=\"token punctuation\">[</span>matchIndex<span class=\"token operator\">++</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Exception</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Missing parameter for string format&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Exception</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Invalid format string: &quot;</span> <span class=\"token operator\">+</span> m<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token keyword\">return</span> fmt<span class=\"token punctuation\">.</span><span class=\"token function\">replaceAllMapped</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;%s&quot;</span><span class=\"token punctuation\">,</span> replace<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">void</span> <span class=\"token function\">main</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n  <span class=\"token function\">walk</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div></li> <li><p>写一个shell(mo.sh)，将生成模板和生成model串起来：</p> <div class=\"language-sh extra-class\"><pre class=\"language-sh\"><code>dart mo.dart\nflutter packages pub run build_runner build --delete-conflicting-outputs\n</code></pre></div></li></ol> <p>至此，我们的脚本写好了，我们在根目录下新建一个json目录，然后把user.json移进去，然后在lib目录下创建一个models目录，用于保存最终生成的Model类。现在我们只需要一句命令即可生成Model类了:</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>./mo.sh  \n</code></pre></div><p>运行后，一切都将自动执行，现在好多了，不是吗？</p> <h4 id=\"嵌套json\"><a href=\"#嵌套json\" class=\"header-anchor\">#</a> 嵌套JSON</h4> <p>我们定义一个person.json内容修改为：</p> <div class=\"language-json extra-class\"><pre class=\"language-json\"><code><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;name&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;John Smith&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;email&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;john@example.com&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;mother&quot;</span><span class=\"token operator\">:</span><span class=\"token punctuation\">{</span>\n    <span class=\"token property\">&quot;name&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;Alice&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;email&quot;</span><span class=\"token operator\">:</span><span class=\"token string\">&quot;alice@example.com&quot;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;friends&quot;</span><span class=\"token operator\">:</span><span class=\"token punctuation\">[</span>\n    <span class=\"token punctuation\">{</span>\n      <span class=\"token property\">&quot;name&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;Jack&quot;</span><span class=\"token punctuation\">,</span>\n      <span class=\"token property\">&quot;email&quot;</span><span class=\"token operator\">:</span><span class=\"token string\">&quot;Jack@example.com&quot;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">{</span>\n      <span class=\"token property\">&quot;name&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;Nancy&quot;</span><span class=\"token punctuation\">,</span>\n      <span class=\"token property\">&quot;email&quot;</span><span class=\"token operator\">:</span><span class=\"token string\">&quot;Nancy@example.com&quot;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>每个Person都有<code>name</code> 、<code>email</code> 、 <code>mother</code>和<code>friends</code>四个字段，由于<code>mother</code>也是一个Person，朋友是多个Person(数组)，所以我们期望生成的Model是下面这样：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:json_annotation/json_annotation.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">part</span> <span class=\"token string\">'person.g.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token metadata symbol\">@JsonSerializable</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">Person</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">Person</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    \n    String name<span class=\"token punctuation\">;</span>\n    String email<span class=\"token punctuation\">;</span>\n    Person mother<span class=\"token punctuation\">;</span>\n    List<span class=\"token operator\">&lt;</span>Person<span class=\"token operator\">&gt;</span> friends<span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">factory</span> Person<span class=\"token punctuation\">.</span><span class=\"token function\">fromJson</span><span class=\"token punctuation\">(</span>Map<span class=\"token operator\">&lt;</span>String<span class=\"token punctuation\">,</span><span class=\"token keyword\">dynamic</span><span class=\"token operator\">&gt;</span> json<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _$<span class=\"token function\">PersonFromJson</span><span class=\"token punctuation\">(</span>json<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    Map<span class=\"token operator\">&lt;</span>String<span class=\"token punctuation\">,</span> <span class=\"token keyword\">dynamic</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">toJson</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _$<span class=\"token function\">PersonToJson</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n</code></pre></div><p>这时，我们只需要简单修改一下JSON，添加一些特殊标志，重新运行mo.sh即可：</p> <div class=\"language-json extra-class\"><pre class=\"language-json\"><code><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;name&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;John Smith&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;email&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;john@example.com&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;mother&quot;</span><span class=\"token operator\">:</span><span class=\"token string\">&quot;$person&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;friends&quot;</span><span class=\"token operator\">:</span><span class=\"token string\">&quot;$[]person&quot;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们使用美元符“$”作为特殊标志符(如果与内容冲突，可以修改mo.dart中的<code>TAG</code>常量，自定义标志符)，脚本在遇到特殊标志符后会先把相应字段转为相应的对象或对象数组，对象数组需要在标志符后面添加数组符“[]”，符号后面接具体的类型名，此例中是person。其它类型同理，加入我们给User添加一个Person类型的 <code>boss</code>字段：</p> <div class=\"language-json extra-class\"><pre class=\"language-json\"><code><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;name&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;John Smith&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;email&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;john@example.com&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;boss&quot;</span><span class=\"token operator\">:</span><span class=\"token string\">&quot;$person&quot;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>重新运行mo.sh，生成的user.dart如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:json_annotation/json_annotation.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">&quot;person.dart&quot;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">part</span> <span class=\"token string\">'user.g.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token metadata symbol\">@JsonSerializable</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">User</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">User</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    String name<span class=\"token punctuation\">;</span>\n    String email<span class=\"token punctuation\">;</span>\n    Person boss<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token keyword\">factory</span> User<span class=\"token punctuation\">.</span><span class=\"token function\">fromJson</span><span class=\"token punctuation\">(</span>Map<span class=\"token operator\">&lt;</span>String<span class=\"token punctuation\">,</span><span class=\"token keyword\">dynamic</span><span class=\"token operator\">&gt;</span> json<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _$<span class=\"token function\">UserFromJson</span><span class=\"token punctuation\">(</span>json<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    Map<span class=\"token operator\">&lt;</span>String<span class=\"token punctuation\">,</span> <span class=\"token keyword\">dynamic</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">toJson</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _$<span class=\"token function\">UserToJson</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以看到，<code>boss</code>字段已自动添加，并自动导入了“person.dart”。</p> <h3 id=\"json-model-包\"><a href=\"#json-model-包\" class=\"header-anchor\">#</a> Json_model 包</h3> <p>如果每个项目都要构建一个上面这样的脚本显然很麻烦，为此，我们将上面脚本和生成模板封装了一个包,已经发布到了Pub上，包名为<a href=\"https://github.com/flutterchina/json_model\" target=\"_blank\" rel=\"noopener noreferrer\">Json_model<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>，开发者把该包加入开发依赖后，便可以用一条命令，根据Json文件生成Dart类。另外<a href=\"https://github.com/flutterchina/json_model\" target=\"_blank\" rel=\"noopener noreferrer\">Json_model<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 处于迭代中，功能会逐渐完善，所以建议读者直接使用该包（而不是手动复制上面的代码）。</p> <h3 id=\"使用ide插件生成model\"><a href=\"#使用ide插件生成model\" class=\"header-anchor\">#</a> 使用IDE插件生成model</h3> <p>目前Android Studio(或IntelliJ)有几个插件，可以将json文件转成Model类，但插件质量参差不齐，甚至还有一些沾染上了抄袭风波，故笔者在此不做优先推荐，读者有兴趣可以自行了解。但是，我们还是要了解一下IDE插件和<a href=\"https://link.juejin.im?target=https%3A%2F%2Fgithub.com%2Fflutterchina%2Fjson_model\" target=\"_blank\" rel=\"noopener noreferrer\">Json_model<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>的优劣：</p> <ol><li><a href=\"https://link.juejin.im?target=https%3A%2F%2Fgithub.com%2Fflutterchina%2Fjson_model\" target=\"_blank\" rel=\"noopener noreferrer\">Json_model<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>需要单独维护一个存放Json文件的文件夹，如果有改动，只需修改Json文件便可重新生成Model类；而IDE插件一般需要用户手动将Json内容拷贝复制到一个输入框中，这样生成之后Json文件没有存档的化，之后要改动就需要手动。</li> <li><a href=\"https://link.juejin.im?target=https%3A%2F%2Fgithub.com%2Fflutterchina%2Fjson_model\" target=\"_blank\" rel=\"noopener noreferrer\">Json_model<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>可以手动指定某个字段引用的其它Model类，可以避免生成重复的类；而IDE插件一般会为每一个Json文件中所有嵌套对象都单独生成一个Model类，即使这些嵌套对象可能在其它Model类中已经生成过。</li> <li><a href=\"https://link.juejin.im?target=https%3A%2F%2Fgithub.com%2Fflutterchina%2Fjson_model\" target=\"_blank\" rel=\"noopener noreferrer\">Json_model<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 提供了命令行转化方式，可以方便集成到CI等非UI环境的场景。</li></ol> <h3 id=\"faq\"><a href=\"#faq\" class=\"header-anchor\">#</a> FAQ</h3> <p>很多人可能会问Flutter中有没有像Java开发中的Gson/Jackson一样的Json序列化类库？答案是没有！因为这样的库需要使用运行时反射，这在Flutter中是禁用的。运行时反射会干扰Dart的_tree shaking_，使用_tree shaking_，可以在release版中“去除”未使用的代码，这可以显著优化应用程序的大小。由于反射会默认应用到所有代码，因此_tree shaking_会很难工作，因为在启用反射时很难知道哪些代码未被使用，因此冗余代码很难剥离，所以Flutter中禁用了Dart的反射功能，而正因如此也就无法实现动态转化Model的功能。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter11/socket.html\" class=\"prev\">\n        11.6 使用Socket API\n      </a></span> <span class=\"next\"><a href=\"/chapter12/develop_package.html\">\n        12.1 开发Package\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/87.e1f95a69.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter11/socket.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>11.6 使用Socket API | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/88.9709422a.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable open\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" aria-current=\"page\" class=\"active sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_11-6-使用socket-api\"><a href=\"#_11-6-使用socket-api\" class=\"header-anchor\">#</a> 11.6 使用Socket API</h1> <p>我们之前介绍的Http协议和WebSocket协议都属于应用层协议，除了它们，应用层协议还有很多如：SMTP、FTP等，这些应用层协议的实现都是通过Socket API来实现的。其实，操作系统中提供的原生网络请求API是标准的，在C语言的Socket库中，它主要提供了端到端建立链接和发送数据的基础API，而高级编程语言中的Socket库其实都是对操作系统的socket API的一个封装。所以，如果我们需要自定义协议或者想直接来控制管理网络链接、又或者我们觉得自带的HttpClient不好用想重新实现一个，这时我们就需要使用Socket。Flutter的Socket API在dart：io包中，下面我们看一个使用Socket实现简单http请求的示例，以请求百度首页为例：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">_request</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span><span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">//建立连接</span>\n  <span class=\"token keyword\">var</span> socket<span class=\"token operator\">=</span><span class=\"token keyword\">await</span> Socket<span class=\"token punctuation\">.</span><span class=\"token function\">connect</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;baidu.com&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token number\">80</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">//根据http协议，发送请求头</span>\n  socket<span class=\"token punctuation\">.</span><span class=\"token function\">writeln</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;GET / HTTP/1.1&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  socket<span class=\"token punctuation\">.</span><span class=\"token function\">writeln</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Host:baidu.com&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  socket<span class=\"token punctuation\">.</span><span class=\"token function\">writeln</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Connection:close&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  socket<span class=\"token punctuation\">.</span><span class=\"token function\">writeln</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">await</span> socket<span class=\"token punctuation\">.</span><span class=\"token function\">flush</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//发送</span>\n  <span class=\"token comment\">//读取返回内容</span>\n  _response <span class=\"token operator\">=</span><span class=\"token keyword\">await</span> socket<span class=\"token punctuation\">.</span><span class=\"token function\">transform</span><span class=\"token punctuation\">(</span>utf8<span class=\"token punctuation\">.</span>decoder<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">join</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">await</span> socket<span class=\"token punctuation\">.</span><span class=\"token function\">close</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以看到，使用Socket需要我们自己实现Http协议（需要自己实现和服务器的通信过程），本例只是一个简单示例，没有处理重定向、cookie等。本示例完整代码参考示例demo，运行后效果如图11-2所示：</p> <p><img src=\"/assets/img/11-2.f480cac6.png\" alt=\"图11-2\"></p> <p>可以看到响应内容分两个部分，第一部分是响应头，第二部分是响应体，服务端可以根据请求信息动态来输出响应体。由于本示例请求头比较简单，所以响应体和浏览器中访问的会有差别，读者可以补充一些请求头(如user-agent)来看看输出的变化。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter11/websocket.html\" class=\"prev\">\n        使用WebSockets\n      </a></span> <span class=\"next\"><a href=\"/chapter11/json_model.html\">\n        11.7 Json转Dart Model类\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/88.9709422a.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter11/websocket.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>使用WebSockets | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/139.94803f35.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable open\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" aria-current=\"page\" class=\"active sidebar-link\">使用WebSockets</a><ul class=\"sidebar-sub-headers\"></ul></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"使用websockets\"><a href=\"#使用websockets\" class=\"header-anchor\">#</a> 使用WebSockets</h1> <p>Http协议是无状态的，只能由客户端主动发起，服务端再被动响应，服务端无法向客户端主动推送内容，并且一旦服务器响应结束，链接就会断开(见注解部分)，所以无法进行实时通信。WebSocket协议正是为解决客户端与服务端实时通信而产生的技术，现在已经被主流浏览器支持，所以对于Web开发者来说应该比较熟悉了，Flutter也提供了专门的包来支持WebSocket协议。</p> <blockquote><p>注意：Http协议中虽然可以通过keep-alive机制使服务器在响应结束后链接会保持一段时间，但最终还是会断开，keep-alive机制主要是用于避免在同一台服务器请求多个资源时频繁创建链接，它本质上是支持链接复用的技术，而并非用于实时通信，读者需要知道这两者的区别。</p></blockquote> <p>WebSocket协议本质上是一个基于tcp的协议，它是先通过HTTP协议发起一条特殊的http请求进行握手后，如果服务端支持WebSocket协议，则会进行协议升级。WebSocket会使用http协议握手后创建的tcp链接，和http协议不同的是，WebSocket的tcp链接是个长链接（不会断开），所以服务端与客户端就可以通过此TCP连接进行实时通信。有关WebSocket协议细节，读者可以看RFC文档，下面我们重点看看Flutter中如何使用WebSocket。</p> <p>在接下来例子中，我们将连接到由<a href=\"http://www.websocket.org/echo.html\" target=\"_blank\" rel=\"noopener noreferrer\">websocket.org提供的测试服务器<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>。服务器将简单地返回我们发送给它的相同消息！</p> <h3 id=\"步骤\"><a href=\"#步骤\" class=\"header-anchor\">#</a> 步骤</h3> <ol><li>连接到WebSocket服务器。</li> <li>监听来自服务器的消息。</li> <li>将数据发送到服务器。</li> <li>关闭WebSocket连接。</li></ol> <h3 id=\"_1-连接到websocket服务器\"><a href=\"#_1-连接到websocket服务器\" class=\"header-anchor\">#</a> 1. 连接到WebSocket服务器</h3> <p><a href=\"https://pub.dartlang.org/packages/web_socket_channel\" target=\"_blank\" rel=\"noopener noreferrer\">web_socket_channel<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> package 提供了我们需要连接到WebSocket服务器的工具。该package提供了一个<code>WebSocketChannel</code>允许我们既可以监听来自服务器的消息，又可以将消息发送到服务器的方法。</p> <p>在Flutter中，我们可以创建一个<code>WebSocketChannel</code>连接到一台服务器：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">final</span> channel <span class=\"token operator\">=</span> IOWebSocketChannel<span class=\"token punctuation\">.</span><span class=\"token function\">connect</span><span class=\"token punctuation\">(</span><span class=\"token string\">'ws://echo.websocket.org'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><h3 id=\"_2-监听来自服务器的消息\"><a href=\"#_2-监听来自服务器的消息\" class=\"header-anchor\">#</a> 2. 监听来自服务器的消息</h3> <p>现在我们建立了连接，我们可以监听来自服务器的消息，在我们发送消息给测试服务器之后，它会返回相同的消息。</p> <p>我们如何收取消息并显示它们？在这个例子中，我们将使用一个<a href=\"https://docs.flutter.io/flutter/widgets/StreamBuilder-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>StreamBuilder</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 来监听新消息， 并用一个Text来显示它们。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">new</span> <span class=\"token class-name\">StreamBuilder</span><span class=\"token punctuation\">(</span>\n  stream<span class=\"token punctuation\">:</span> widget<span class=\"token punctuation\">.</span>channel<span class=\"token punctuation\">.</span>stream<span class=\"token punctuation\">,</span>\n  builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> snapshot<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span>snapshot<span class=\"token punctuation\">.</span>hasData <span class=\"token operator\">?</span> <span class=\"token string\">'${snapshot.data}'</span> <span class=\"token punctuation\">:</span> <span class=\"token string\">''</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><h4 id=\"工作原理\"><a href=\"#工作原理\" class=\"header-anchor\">#</a> 工作原理</h4> <p><code>WebSocketChannel</code>提供了一个来自服务器的消息<code>Stream</code> 。该<code>Stream</code>类是<code>dart:async</code>包中的一个基础类。它提供了一种方法来监听来自数据源的异步事件。与<code>Future</code>返回单个异步响应不同，<code>Stream</code>类可以随着时间推移传递很多事件。该<a href=\"https://docs.flutter.io/flutter/widgets/StreamBuilder-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>StreamBuilder</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 组件将连接到一个<code>Stream</code>， 并在每次收到消息时通知Flutter重新构建界面。</p> <h3 id=\"_3-将数据发送到服务器\"><a href=\"#_3-将数据发送到服务器\" class=\"header-anchor\">#</a> 3. 将数据发送到服务器</h3> <p>为了将数据发送到服务器，我们会<code>add</code>消息给<code>WebSocketChannel</code>提供的sink。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>channel<span class=\"token punctuation\">.</span>sink<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Hello!'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><h4 id=\"工作原理-2\"><a href=\"#工作原理-2\" class=\"header-anchor\">#</a> 工作原理</h4> <p><code>WebSocketChannel</code>提供了一个<a href=\"https://docs.flutter.io/flutter/dart-async/StreamSink-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>StreamSink</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>，它将消息发给服务器。</p> <p><code>StreamSink</code>类提供了给数据源同步或异步添加事件的一般方法。</p> <h3 id=\"_4-关闭websocket连接\"><a href=\"#_4-关闭websocket连接\" class=\"header-anchor\">#</a> 4. 关闭WebSocket连接</h3> <p>在我们使用<code>WebSocket</code>后，要关闭连接：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>channel<span class=\"token punctuation\">.</span>sink<span class=\"token punctuation\">.</span><span class=\"token function\">close</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><h3 id=\"完整的例子\"><a href=\"#完整的例子\" class=\"header-anchor\">#</a> 完整的例子</h3> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:web_socket_channel/io.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">WebSocketRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _WebSocketRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_WebSocketRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_WebSocketRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>WebSocketRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  TextEditingController _controller <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">TextEditingController</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  IOWebSocketChannel channel<span class=\"token punctuation\">;</span>\n  String _text <span class=\"token operator\">=</span> <span class=\"token string\">&quot;&quot;</span><span class=\"token punctuation\">;</span>\n\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//创建websocket连接</span>\n    channel <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">IOWebSocketChannel<span class=\"token punctuation\">.</span>connect</span><span class=\"token punctuation\">(</span><span class=\"token string\">'ws://echo.websocket.org'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Scaffold</span><span class=\"token punctuation\">(</span>\n      appBar<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AppBar</span><span class=\"token punctuation\">(</span>\n        title<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;WebSocket(内容回显)&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      body<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Padding</span><span class=\"token punctuation\">(</span>\n        padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">20.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Column</span><span class=\"token punctuation\">(</span>\n          crossAxisAlignment<span class=\"token punctuation\">:</span> CrossAxisAlignment<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            <span class=\"token keyword\">new</span> <span class=\"token class-name\">Form</span><span class=\"token punctuation\">(</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">TextFormField</span><span class=\"token punctuation\">(</span>\n                controller<span class=\"token punctuation\">:</span> _controller<span class=\"token punctuation\">,</span>\n                decoration<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">InputDecoration</span><span class=\"token punctuation\">(</span>labelText<span class=\"token punctuation\">:</span> <span class=\"token string\">'Send a message'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token keyword\">new</span> <span class=\"token class-name\">StreamBuilder</span><span class=\"token punctuation\">(</span>\n              stream<span class=\"token punctuation\">:</span> channel<span class=\"token punctuation\">.</span>stream<span class=\"token punctuation\">,</span>\n              builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> snapshot<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token comment\">//网络不通会走到这</span>\n                <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>snapshot<span class=\"token punctuation\">.</span>hasError<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                  _text <span class=\"token operator\">=</span> <span class=\"token string\">&quot;网络不通...&quot;</span><span class=\"token punctuation\">;</span>\n                <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>snapshot<span class=\"token punctuation\">.</span>hasData<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                  _text <span class=\"token operator\">=</span> <span class=\"token string\">&quot;echo: &quot;</span><span class=\"token operator\">+</span>snapshot<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">;</span>\n                <span class=\"token punctuation\">}</span>\n                <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Padding</span><span class=\"token punctuation\">(</span>\n                  padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">symmetric</span><span class=\"token punctuation\">(</span>vertical<span class=\"token punctuation\">:</span> <span class=\"token number\">24.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span>_text<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      floatingActionButton<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">FloatingActionButton</span><span class=\"token punctuation\">(</span>\n        onPressed<span class=\"token punctuation\">:</span> _sendMessage<span class=\"token punctuation\">,</span>\n        tooltip<span class=\"token punctuation\">:</span> <span class=\"token string\">'Send message'</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>send<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_sendMessage</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>_controller<span class=\"token punctuation\">.</span>text<span class=\"token punctuation\">.</span>isNotEmpty<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      channel<span class=\"token punctuation\">.</span>sink<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span>_controller<span class=\"token punctuation\">.</span>text<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    channel<span class=\"token punctuation\">.</span>sink<span class=\"token punctuation\">.</span><span class=\"token function\">close</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面的例子比较简单，不再赘述。我们现在思考一个问题，假如我们想通过WebSocket传输二进制数据应该怎么做（比如要从服务器接收一张图片）？我们发现<code>StreamBuilder</code>和<code>Stream</code>都没有指定接收类型的参数，并且在创建WebSocket链接时也没有相应的配置，貌似没有什么办法……其实很简单，要接收二进制数据仍然使用<code>StreamBuilder</code>，因为WebSocket中所有发送的数据使用帧的形式发送，而帧是有固定格式，每一个帧的数据类型都可以通过Opcode字段指定，它可以指定当前帧是文本类型还是二进制类型（还有其它类型），所以客户端在收到帧时就已经知道了其数据类型，所以flutter完全可以在收到数据后解析出正确的类型，所以就无需开发者去关心，当服务器传输的数据是指定为二进制时，<code>StreamBuilder</code>的<code>snapshot.data</code>的类型就是<code>List&lt;int&gt;</code>，是文本时，则为<code>String</code>。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter11/download_with_chunks.html\" class=\"prev\">\n        11.4 实例：Http分块下载\n      </a></span> <span class=\"next\"><a href=\"/chapter11/socket.html\">\n        11.6 使用Socket API\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/139.94803f35.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter12/android_implement.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>12.4 插件开发：Android端API实现 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/140.c76b4d30.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable open\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" aria-current=\"page\" class=\"active sidebar-link\">12.4 插件开发：Android端API实现</a><ul class=\"sidebar-sub-headers\"></ul></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_12-4-插件开发-android端api实现\"><a href=\"#_12-4-插件开发-android端api实现\" class=\"header-anchor\">#</a> 12.4 插件开发：Android端API实现</h1> <p>本节我们接着上一节&quot;获取电池电量&quot;插件的示例，来完成Android端API的实现。以下步骤是使用Java的示例，如果您更喜欢Kotlin，可以直接跳到后面Kotlin部分。</p> <p>首先在Android Studio中打开您的Flutter应用的Android部分：</p> <ol><li>启动 Android Studio</li> <li>选择 File &gt; Open…</li> <li>定位到您 Flutter app目录, 然后选择里面的 <code>android</code>文件夹，点击 OK</li> <li>在<code>java</code>目录下打开 <code>MainActivity.java</code></li></ol> <p>接下来，在<code>onCreate</code>里创建MethodChannel并设置一个<code>MethodCallHandler</code>。确保使用和Flutter客户端中使用的通道名称相同的名称。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> io<span class=\"token punctuation\">.</span>flutter<span class=\"token punctuation\">.</span>app<span class=\"token punctuation\">.</span>FlutterActivity<span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> io<span class=\"token punctuation\">.</span>flutter<span class=\"token punctuation\">.</span>plugin<span class=\"token punctuation\">.</span>common<span class=\"token punctuation\">.</span>MethodCall<span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> io<span class=\"token punctuation\">.</span>flutter<span class=\"token punctuation\">.</span>plugin<span class=\"token punctuation\">.</span>common<span class=\"token punctuation\">.</span>MethodChannel<span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> io<span class=\"token punctuation\">.</span>flutter<span class=\"token punctuation\">.</span>plugin<span class=\"token punctuation\">.</span>common<span class=\"token punctuation\">.</span>MethodChannel<span class=\"token punctuation\">.</span>MethodCallHandler<span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> io<span class=\"token punctuation\">.</span>flutter<span class=\"token punctuation\">.</span>plugin<span class=\"token punctuation\">.</span>common<span class=\"token punctuation\">.</span>MethodChannel<span class=\"token punctuation\">.</span>Result<span class=\"token punctuation\">;</span>\n\npublic <span class=\"token keyword\">class</span> <span class=\"token class-name\">MainActivity</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">FlutterActivity</span> <span class=\"token punctuation\">{</span>\n    private <span class=\"token keyword\">static</span> <span class=\"token keyword\">final</span> String CHANNEL <span class=\"token operator\">=</span> <span class=\"token string\">&quot;samples.flutter.io/battery&quot;</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token metadata symbol\">@Override</span>\n    public <span class=\"token keyword\">void</span> <span class=\"token function\">onCreate</span><span class=\"token punctuation\">(</span>Bundle savedInstanceState<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">onCreate</span><span class=\"token punctuation\">(</span>savedInstanceState<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">new</span> <span class=\"token class-name\">MethodChannel</span><span class=\"token punctuation\">(</span><span class=\"token function\">getFlutterView</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> CHANNEL<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">setMethodCallHandler</span><span class=\"token punctuation\">(</span>\n          <span class=\"token keyword\">new</span> <span class=\"token class-name\">MethodCallHandler</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n             <span class=\"token metadata symbol\">@Override</span>\n             public <span class=\"token keyword\">void</span> <span class=\"token function\">onMethodCall</span><span class=\"token punctuation\">(</span>MethodCall call<span class=\"token punctuation\">,</span> Result result<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                 <span class=\"token comment\">// TODO</span>\n             <span class=\"token punctuation\">}</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>接下来，我们添加Java代码，使用Android电池API来获取电池电量。此代码和在原生Android应用中编写的代码完全相同。</p> <p>首先，添加需要导入的依赖。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> android<span class=\"token punctuation\">.</span>content<span class=\"token punctuation\">.</span>ContextWrapper<span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> android<span class=\"token punctuation\">.</span>content<span class=\"token punctuation\">.</span>Intent<span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> android<span class=\"token punctuation\">.</span>content<span class=\"token punctuation\">.</span>IntentFilter<span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> android<span class=\"token punctuation\">.</span>os<span class=\"token punctuation\">.</span>BatteryManager<span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> android<span class=\"token punctuation\">.</span>os<span class=\"token punctuation\">.</span>Build<span class=\"token punctuation\">.</span>VERSION<span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> android<span class=\"token punctuation\">.</span>os<span class=\"token punctuation\">.</span>Build<span class=\"token punctuation\">.</span>VERSION_CODES<span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> android<span class=\"token punctuation\">.</span>os<span class=\"token punctuation\">.</span>Bundle<span class=\"token punctuation\">;</span>\n</code></pre></div><p>然后，将下面的新方法添加到activity类中的，位于onCreate 方法下方：</p> <div class=\"language-java extra-class\"><pre class=\"language-java\"><code><span class=\"token keyword\">private</span> <span class=\"token keyword\">int</span> <span class=\"token function\">getBatteryLevel</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">int</span> batteryLevel <span class=\"token operator\">=</span> <span class=\"token operator\">-</span><span class=\"token number\">1</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>VERSION<span class=\"token punctuation\">.</span>SDK_INT <span class=\"token operator\">&gt;=</span> VERSION_CODES<span class=\"token punctuation\">.</span>LOLLIPOP<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token class-name\">BatteryManager</span> batteryManager <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token class-name\">BatteryManager</span><span class=\"token punctuation\">)</span> <span class=\"token function\">getSystemService</span><span class=\"token punctuation\">(</span>BATTERY_SERVICE<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    batteryLevel <span class=\"token operator\">=</span> batteryManager<span class=\"token punctuation\">.</span><span class=\"token function\">getIntProperty</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">BatteryManager</span><span class=\"token punctuation\">.</span>BATTERY_PROPERTY_CAPACITY<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token class-name\">Intent</span> intent <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">ContextWrapper</span><span class=\"token punctuation\">(</span><span class=\"token function\">getApplicationContext</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>\n        <span class=\"token function\">registerReceiver</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">IntentFilter</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">Intent</span><span class=\"token punctuation\">.</span>ACTION_BATTERY_CHANGED<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    batteryLevel <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>intent<span class=\"token punctuation\">.</span><span class=\"token function\">getIntExtra</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">BatteryManager</span><span class=\"token punctuation\">.</span>EXTRA_LEVEL<span class=\"token punctuation\">,</span> <span class=\"token operator\">-</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">*</span> <span class=\"token number\">100</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">/</span>\n        intent<span class=\"token punctuation\">.</span><span class=\"token function\">getIntExtra</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">BatteryManager</span><span class=\"token punctuation\">.</span>EXTRA_SCALE<span class=\"token punctuation\">,</span> <span class=\"token operator\">-</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">return</span> batteryLevel<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>最后，我们完成之前添加的<code>onMethodCall</code>方法。我们需要处理平台方法名为<code>getBatteryLevel</code>的调用消息，所以我们需要先在call参数判断调用的方法是否为<code>getBatteryLevel</code>。 这个平台方法的实现只需调用我们在前一步中编写的Android代码，并通过result参数返回成功或错误情况的响应信息。如果调用了未定义的API，我们也会通知返回：</p> <div class=\"language-java extra-class\"><pre class=\"language-java\"><code><span class=\"token annotation punctuation\">@Override</span>\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">void</span> <span class=\"token function\">onMethodCall</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">MethodCall</span> call<span class=\"token punctuation\">,</span> <span class=\"token class-name\">Result</span> result<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>call<span class=\"token punctuation\">.</span>method<span class=\"token punctuation\">.</span><span class=\"token function\">equals</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;getBatteryLevel&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">int</span> batteryLevel <span class=\"token operator\">=</span> <span class=\"token function\">getBatteryLevel</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>batteryLevel <span class=\"token operator\">!=</span> <span class=\"token operator\">-</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            result<span class=\"token punctuation\">.</span><span class=\"token function\">success</span><span class=\"token punctuation\">(</span>batteryLevel<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n            result<span class=\"token punctuation\">.</span><span class=\"token function\">error</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;UNAVAILABLE&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;Battery level not available.&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n        result<span class=\"token punctuation\">.</span><span class=\"token function\">notImplemented</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>  \n</code></pre></div><p>现在就可以在Android上运行该应用程序了，如果使用的是Android模拟器，则可以通过工具栏中的&quot;...&quot;按钮访问Extended Controls面板中的电池电量。</p> <h3 id=\"使用kotlin添加android平台特定的实现\"><a href=\"#使用kotlin添加android平台特定的实现\" class=\"header-anchor\">#</a> 使用Kotlin添加Android平台特定的实现</h3> <p>使用Kotlin和使用Java的步骤类似，首先在Android Studio中打开您的Flutter应用的Android部分：</p> <ol><li>启动 Android Studio。</li> <li>选择 the menu item &quot;File &gt; Open…&quot;。</li> <li>定位到 Flutter app目录, 然后选择里面的 <code>android</code>文件夹，点击 OK。</li> <li>在<code>kotlin</code>目录中打开<code>MainActivity.kt</code>。</li></ol> <p>接下来，在<code>onCreate</code>里创建MethodChannel并设置一个<code>MethodCallHandler</code>。确保使用与在Flutter客户端使用的通道名称相同。</p> <div class=\"language-kotlin extra-class\"><pre class=\"language-kotlin\"><code><span class=\"token keyword\">import</span> android<span class=\"token punctuation\">.</span>os<span class=\"token punctuation\">.</span>Bundle\n<span class=\"token keyword\">import</span> io<span class=\"token punctuation\">.</span>flutter<span class=\"token punctuation\">.</span>app<span class=\"token punctuation\">.</span>FlutterActivity\n<span class=\"token keyword\">import</span> io<span class=\"token punctuation\">.</span>flutter<span class=\"token punctuation\">.</span>plugin<span class=\"token punctuation\">.</span>common<span class=\"token punctuation\">.</span>MethodChannel\n<span class=\"token keyword\">import</span> io<span class=\"token punctuation\">.</span>flutter<span class=\"token punctuation\">.</span>plugins<span class=\"token punctuation\">.</span>GeneratedPluginRegistrant\n\n<span class=\"token keyword\">class</span> <span class=\"token function\">MainActivity</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">:</span> <span class=\"token function\">FlutterActivity</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">private</span> <span class=\"token keyword\">val</span> CHANNEL <span class=\"token operator\">=</span> <span class=\"token string\">&quot;samples.flutter.io/battery&quot;</span>\n\n  <span class=\"token keyword\">override</span> <span class=\"token keyword\">fun</span> <span class=\"token function\">onCreate</span><span class=\"token punctuation\">(</span>savedInstanceState<span class=\"token operator\">:</span> Bundle<span class=\"token operator\">?</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">onCreate</span><span class=\"token punctuation\">(</span>savedInstanceState<span class=\"token punctuation\">)</span>\n    GeneratedPluginRegistrant<span class=\"token punctuation\">.</span><span class=\"token function\">registerWith</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span>\n\n    <span class=\"token function\">MethodChannel</span><span class=\"token punctuation\">(</span>flutterView<span class=\"token punctuation\">,</span> CHANNEL<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">setMethodCallHandler</span> <span class=\"token punctuation\">{</span> call<span class=\"token punctuation\">,</span> result <span class=\"token operator\">-&gt;</span>\n      <span class=\"token comment\">// TODO</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>接下来，我们添加Kotlin代码，使用Android电池API来获取电池电量，这和原生开发是一样的。</p> <p>首先，添加需要导入的依赖。</p> <div class=\"language-kotlin extra-class\"><pre class=\"language-kotlin\"><code><span class=\"token keyword\">import</span> android<span class=\"token punctuation\">.</span>content<span class=\"token punctuation\">.</span>Context\n<span class=\"token keyword\">import</span> android<span class=\"token punctuation\">.</span>content<span class=\"token punctuation\">.</span>ContextWrapper\n<span class=\"token keyword\">import</span> android<span class=\"token punctuation\">.</span>content<span class=\"token punctuation\">.</span>Intent\n<span class=\"token keyword\">import</span> android<span class=\"token punctuation\">.</span>content<span class=\"token punctuation\">.</span>IntentFilter\n<span class=\"token keyword\">import</span> android<span class=\"token punctuation\">.</span>os<span class=\"token punctuation\">.</span>BatteryManager\n<span class=\"token keyword\">import</span> android<span class=\"token punctuation\">.</span>os<span class=\"token punctuation\">.</span>Build<span class=\"token punctuation\">.</span>VERSION\n<span class=\"token keyword\">import</span> android<span class=\"token punctuation\">.</span>os<span class=\"token punctuation\">.</span>Build<span class=\"token punctuation\">.</span>VERSION_CODES\n</code></pre></div><p>然后，将下面的新方法添加到activity类中的，位于onCreate 方法下方：</p> <div class=\"language-kotlin extra-class\"><pre class=\"language-kotlin\"><code>  <span class=\"token keyword\">private</span> <span class=\"token keyword\">fun</span> <span class=\"token function\">getBatteryLevel</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> Int <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">val</span> batteryLevel<span class=\"token operator\">:</span> Int\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>VERSION<span class=\"token punctuation\">.</span>SDK_INT <span class=\"token operator\">&gt;=</span> VERSION_CODES<span class=\"token punctuation\">.</span>LOLLIPOP<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">val</span> batteryManager <span class=\"token operator\">=</span> <span class=\"token function\">getSystemService</span><span class=\"token punctuation\">(</span>Context<span class=\"token punctuation\">.</span>BATTERY_SERVICE<span class=\"token punctuation\">)</span> <span class=\"token keyword\">as</span> BatteryManager\n      batteryLevel <span class=\"token operator\">=</span> batteryManager<span class=\"token punctuation\">.</span><span class=\"token function\">getIntProperty</span><span class=\"token punctuation\">(</span>BatteryManager<span class=\"token punctuation\">.</span>BATTERY_PROPERTY_CAPACITY<span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">val</span> intent <span class=\"token operator\">=</span> <span class=\"token function\">ContextWrapper</span><span class=\"token punctuation\">(</span>applicationContext<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">registerReceiver</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token function\">IntentFilter</span><span class=\"token punctuation\">(</span>Intent<span class=\"token punctuation\">.</span>ACTION_BATTERY_CHANGED<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n      batteryLevel <span class=\"token operator\">=</span> intent<span class=\"token operator\">!!</span><span class=\"token punctuation\">.</span><span class=\"token function\">getIntExtra</span><span class=\"token punctuation\">(</span>BatteryManager<span class=\"token punctuation\">.</span>EXTRA_LEVEL<span class=\"token punctuation\">,</span> <span class=\"token operator\">-</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">*</span> <span class=\"token number\">100</span> <span class=\"token operator\">/</span> intent<span class=\"token punctuation\">.</span><span class=\"token function\">getIntExtra</span><span class=\"token punctuation\">(</span>BatteryManager<span class=\"token punctuation\">.</span>EXTRA_SCALE<span class=\"token punctuation\">,</span> <span class=\"token operator\">-</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">return</span> batteryLevel\n  <span class=\"token punctuation\">}</span>\n</code></pre></div><p>最后，我们完成之前添加的<code>onMethodCall</code>方法。我们需要处理平台方法名为<code>getBatteryLevel</code>的调用消息，所以我们需要先在call参数判断调用的方法是否为<code>getBatteryLevel</code>。 这个平台方法的实现只需调用我们在前一步中编写的Android代码，并通过result参数返回成功或错误情况的响应信息。如果调用了未定义的API，我们也会通知返回：\n​</p> <div class=\"language-kotlin extra-class\"><pre class=\"language-kotlin\"><code><span class=\"token function\">MethodChannel</span><span class=\"token punctuation\">(</span>flutterView<span class=\"token punctuation\">,</span> CHANNEL<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">setMethodCallHandler</span> <span class=\"token punctuation\">{</span> call<span class=\"token punctuation\">,</span> result <span class=\"token operator\">-&gt;</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>call<span class=\"token punctuation\">.</span>method <span class=\"token operator\">==</span> <span class=\"token string\">&quot;getBatteryLevel&quot;</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n     <span class=\"token keyword\">val</span> batteryLevel <span class=\"token operator\">=</span> <span class=\"token function\">getBatteryLevel</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n     <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>batteryLevel <span class=\"token operator\">!=</span> <span class=\"token operator\">-</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n       result<span class=\"token punctuation\">.</span><span class=\"token function\">success</span><span class=\"token punctuation\">(</span>batteryLevel<span class=\"token punctuation\">)</span>\n     <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n       result<span class=\"token punctuation\">.</span><span class=\"token function\">error</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;UNAVAILABLE&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;Battery level not available.&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span>\n     <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n      result<span class=\"token punctuation\">.</span><span class=\"token function\">notImplemented</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>您现在就可以在Android上运行该应用程序。如果您使用的是Android模拟器，则可以通过工具栏中的&quot;...&quot;按钮访问Extended Controls面板中的电池电量。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter12/develop_plugin.html\" class=\"prev\">\n        12.3 开发Flutter插件\n      </a></span> <span class=\"next\"><a href=\"/chapter12/ios_implement.html\">\n        12.5 插件开发：iOS端API实现\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/140.c76b4d30.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter12/develop_package.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>12.1 开发Package | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/61.470fa853.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable open\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" aria-current=\"page\" class=\"active sidebar-link\">12.1 开发Package</a><ul class=\"sidebar-sub-headers\"></ul></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_12-1-开发package\"><a href=\"#_12-1-开发package\" class=\"header-anchor\">#</a> 12.1 开发Package</h1> <p>第二章中已经讲过如何使用Package（包），我们知道通过package可以创建共享的模块化代码，本节将重点讲一下如何开发并发布我们自己的Package。一个最小的Package包括：</p> <ul><li>一个<code>pubspec.yaml</code>文件：声明了Package的名称、版本、作者等的元数据文件。</li> <li>一个 <code>lib</code> 文件夹：包括包中公开的(public)代码，最少应有一个<code>&lt;package-name&gt;.dart</code>文件</li></ul> <p>Flutter Packages分为两类：</p> <ul><li>Dart包：其中一些可能包含Flutter的特定功能，因此对Flutter框架具有依赖性，这种包仅用于Flutter，例如<a href=\"https://pub.dartlang.org/packages/fluro\" target=\"_blank\" rel=\"noopener noreferrer\"><code>fluro</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>包。</li> <li>插件包：一种专用的Dart包，其中包含用Dart代码编写的API，以及针对Android（使用Java或Kotlin）和针对iOS（使用OC或Swift）平台的特定实现，也就是说插件包括原生代码，一个具体的例子是<a href=\"https://pub.dartlang.org/packages/battery\" target=\"_blank\" rel=\"noopener noreferrer\"><code>battery</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>插件包。</li></ul> <p>注意，虽然Flutter的Dart运行时和Dart VM运行时不是完全相同，但是如果Package中没有涉及这些存在差异的部分，那么这样的包可以同时支持Flutter和Dart VM，如Dart http网络库<a href=\"https://github.com/flutterchina/dio\" target=\"_blank\" rel=\"noopener noreferrer\">dio<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>。</p> <p>下面我将带领读者一步步来开发一个Dart Package。</p> <h3 id=\"第一步-创建dart包\"><a href=\"#第一步-创建dart包\" class=\"header-anchor\">#</a> 第一步：创建Dart包</h3> <p>您可以通过Android Studio：File&gt;New&gt;New Flutter Project 来创建一个Package工程，如图12-1所示：</p> <p><img src=\"/assets/img/12-1.456f4488.png\" alt=\"图12-1\"></p> <p>您也可以通过使用<code>--template=package</code> 来执行 <code>flutter create</code> 命令来创建：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>flutter create --template<span class=\"token operator\">=</span>package hello\n</code></pre></div><p>这将在<code>hello/</code>文件夹下创建一个具有以下专用内容的package工程：</p> <ul><li><p><code>lib/hello.dart</code>：Package的Dart代码</p></li> <li><p><code>test/hello_test.dart</code>：Package的单元测试代码。</p></li></ul> <h3 id=\"实现package\"><a href=\"#实现package\" class=\"header-anchor\">#</a> 实现package</h3> <p>对于纯Dart包，只需在主<code>lib/&lt;package name&gt;.dart</code>文件内或<code>lib</code>目录中的文件中添加功能即可 。要测试软件包，请在<code>test</code>目录中添加<a href=\"https://flutter.io/testing/#unit-testing\" target=\"_blank\" rel=\"noopener noreferrer\">unit tests<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>。下面我们看看如何组织Package包的代码，我们以shelf Package为例，它的目录结构如图12-2所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图12-2\"></p> <p>在lib根目录下的“shelf.dart”中，导出了多个“lib/src”目录下的dart文件：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">export</span> <span class=\"token string\">'src/cascade.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">export</span> <span class=\"token string\">'src/handler.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">export</span> <span class=\"token string\">'src/handlers/logger.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">export</span> <span class=\"token string\">'src/hijack_exception.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">export</span> <span class=\"token string\">'src/middleware.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">export</span> <span class=\"token string\">'src/pipeline.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">export</span> <span class=\"token string\">'src/request.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">export</span> <span class=\"token string\">'src/response.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">export</span> <span class=\"token string\">'src/server.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">export</span> <span class=\"token string\">'src/server_handler.dart'</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>而Package中主要的功能的源码都在src目录下。shelf Package也导出了一个迷你库: shelf_io，它主要是处理HttpRequest的。</p> <h3 id=\"导入包\"><a href=\"#导入包\" class=\"header-anchor\">#</a> <strong>导入包</strong></h3> <p>当需要使用这个Package时，我们可以通过&quot;package:&quot;指令来指定包的入口文件：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:utilities/utilities.dart'</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>同一个包中的源码文件之间也可以使用相对路径来导入。</p> <h3 id=\"生成文档\"><a href=\"#生成文档\" class=\"header-anchor\">#</a> 生成文档</h3> <p>可以使用 <a href=\"https://github.com/dart-lang/dartdoc#dartdoc\" target=\"_blank\" rel=\"noopener noreferrer\">dartdoc<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 工具来为Package生成文档，开发者需要做的就是遵守文档注释语法在代码中添加文档注释，最后使用dartdoc可以直接生成API文档（一个静态网站）。文档注释是使用三斜线&quot;///&quot;开始，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">/// The event handler responsible for updating the badge in the UI.</span>\n<span class=\"token keyword\">void</span> <span class=\"token function\">updateBadge</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>详细的文档语法请参考<a href=\"https://github.com/dart-lang/dartdoc#dartdoc\" target=\"_blank\" rel=\"noopener noreferrer\">dartdoc<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 。</p> <h3 id=\"处理包的相互依赖\"><a href=\"#处理包的相互依赖\" class=\"header-anchor\">#</a> 处理包的相互依赖</h3> <p>如果我们正在开发一个<code>hello</code>包，它依赖于另一个包，则需要将该依赖包添加到<code>pubspec.yaml</code>文件的<code>dependencies</code>部分。 下面的代码使<code>url_launcher</code>插件的API在<code>hello</code>包中是可用的：</p> <p>在 <code>hello/pubspec.yaml</code>中:</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code><span class=\"token key atrule\">dependencies</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">url_launcher</span><span class=\"token punctuation\">:</span> ^0.4.2\n</code></pre></div><p>现在可以在<code>hello</code>中<code>import 'package:url_launcher/url_launcher.dart'</code> 然后调用 <code>launch()</code>方法了。</p> <p>这与在Flutter应用程序或任何其他Dart项目中引用软件包没有什么不同。</p> <p>但是，如果<code>hello</code>碰巧是一个插件包，其平台特定的代码需要访问<code>url_launcher</code>公开的特定于平台的API，那么我们还需要为特定于平台的构建文件添加合适的依赖声明，如下所示。</p> <p><strong>Android</strong></p> <p>在 <code>hello/android/build.gradle</code>:</p> <div class=\"language-groovy extra-class\"><pre class=\"language-groovy\"><code>android <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// lines skipped</span>\n    dependencies <span class=\"token punctuation\">{</span>\n        provided rootProject<span class=\"token punctuation\">.</span><span class=\"token function\">findProject</span><span class=\"token punctuation\">(</span><span class=\"token string gstring\">&quot;:url_launcher&quot;</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>您现在可以在<code>hello/android/src</code>源码中<code>import io.flutter.plugins.urllauncher.UrlLauncherPlugin</code>访问<code>UrlLauncherPlugin</code>类。</p> <p><strong>iOS</strong></p> <p>在<code>hello/ios/hello.podspec</code>:</p> <div class=\"language-ruby extra-class\"><pre class=\"language-ruby\"><code><span class=\"token constant\">Pod</span><span class=\"token punctuation\">:</span><span class=\"token punctuation\">:</span><span class=\"token constant\">Spec</span><span class=\"token punctuation\">.</span><span class=\"token keyword\">new</span> <span class=\"token keyword\">do</span> <span class=\"token operator\">|</span>s<span class=\"token operator\">|</span>\n  <span class=\"token comment\"># lines skipped</span>\n  s<span class=\"token punctuation\">.</span>dependency <span class=\"token string\">'url_launcher'</span>\n</code></pre></div><p>您现在可以在<code>hello/ios/Classes</code>源码中 <code>#import &quot;UrlLauncherPlugin.h&quot;</code> 然后访问 <code>UrlLauncherPlugin</code>类。</p> <h3 id=\"解决依赖冲突\"><a href=\"#解决依赖冲突\" class=\"header-anchor\">#</a> 解决依赖冲突</h3> <p>假设我们想在我们的<code>hello</code>包中使用<code>some_package</code>和<code>other_package</code>，并且这两个包都依赖<code>url_launcher</code>，但是依赖的是<code>url_launcher</code>的不同的版本。 那我们就有潜在的冲突。避免这种情况的最好方法是在指定依赖关系时，程序包作者使用<a href=\"https://www.dartlang.org/tools/pub/dependencies#version-constraints\" target=\"_blank\" rel=\"noopener noreferrer\">版本范围<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>而不是特定版本。</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code><span class=\"token key atrule\">dependencies</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">url_launcher</span><span class=\"token punctuation\">:</span> ^0.4.2    <span class=\"token comment\"># 这样会较好, 任何0.4.x(x &gt;= 2)都可.</span>\n  <span class=\"token key atrule\">image_picker</span><span class=\"token punctuation\">:</span> <span class=\"token string\">'0.1.1'</span>   <span class=\"token comment\"># 不是很好，只有0.1.1版本.</span>\n</code></pre></div><p>如果<code>some_package</code>声明了上面的依赖关系,<code>other_package</code>声明了<code>url_launcher</code>版本像’0.4.5’或’^0.4.0’，pub将能够自动解决问题。</p> <p>即使<code>some_package</code>和<code>other_package</code>声明了不兼容的<code>url_launcher</code>版本，它仍然可能会和<code>url_launcher</code>以兼容的方式正常工作。 你可以通过向<code>hello</code>包的<code>pubspec.yaml</code>文件中添加依赖性覆盖声明来处理冲突，从而强制使用特定版本：</p> <p>强制使用 <code>0.4.3</code>版本的<code>url_launcher</code>，在 <code>hello/pubspec.yaml</code>中:</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code><span class=\"token key atrule\">dependencies</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">some_package</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">other_package</span><span class=\"token punctuation\">:</span>\n<span class=\"token key atrule\">dependency_overrides</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">url_launcher</span><span class=\"token punctuation\">:</span> <span class=\"token string\">'0.4.3'</span>\n</code></pre></div><p>如果冲突的依赖不是一个包，而是一个特定于Android的库，比如<code>guava</code>，那么必须将依赖重写声明添加到Gradle构建逻辑中。</p> <p>强制使用<code>23.0</code>版本的<code>guava</code>库，在<code>hello/android/build.gradle</code>中：</p> <div class=\"language-groovy extra-class\"><pre class=\"language-groovy\"><code>configurations<span class=\"token punctuation\">.</span>all <span class=\"token punctuation\">{</span>\n    resolutionStrategy <span class=\"token punctuation\">{</span>\n        force <span class=\"token string\">'com.google.guava:guava:23.0-android'</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>Cocoapods目前不提供依赖覆盖功能。</p> <h3 id=\"发布package\"><a href=\"#发布package\" class=\"header-anchor\">#</a> 发布Package</h3> <p>一旦实现了一个包，我们可以在<a href=\"https://pub.dartlang.org/\" target=\"_blank\" rel=\"noopener noreferrer\">Pub<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>上发布它 ，这样其他开发者就可以轻松使用它。</p> <p>在发布之前，检查<code>pubspec.yaml</code>、<code>README.md</code>以及<code>CHANGELOG.md</code>文件，以确保其内容的完整性和正确性。然后，运行 dry-run 命令以查看是否都准备OK了:</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>flutter packages pub publish --dry-run\n</code></pre></div><p>验证无误后，我们就可以运行发布命令了：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>flutter packages pub publish\n</code></pre></div><blockquote><p>如果你遇到包发布失败的情况，先检查是否因为众所周知的网络原因，如果是网络问题，可以使用VPN，这里需要注意的是一些代理只会代理部分APP的网络请求，如浏览器的，它们可能并不能代理dart的网络请求，所以在这种情况下，即使开了代理也依然无法连接到Pub，因此，在发布Pub包时使用全局代理或全局VPN会保险些。如果网络没有问题，以管理员权限(sudo)运行发布命令重试。<br>\n很多时候开启全局代理也不会让terminal中的流量打代理服务器走，以socks5为例，应该在终端下输入以下指令：</p></blockquote> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code><span class=\"token builtin class-name\">export</span> <span class=\"token assign-left variable\">all_proxy</span><span class=\"token operator\">=</span>socks5://127.0.0.1:1080\n</code></pre></div><blockquote><p>此时终端中的http和https流量会打代理服务器走，可以通过<code>curl -i https://ip.cn</code>指令查看代理设置是否成功。</p></blockquote></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter11/json_model.html\" class=\"prev\">\n        11.7 Json转Dart Model类\n      </a></span> <span class=\"next\"><a href=\"/chapter12/platform-channel.html\">\n        12.2 插件开发：平台通道简介\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/61.470fa853.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter12/develop_plugin.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>12.3 开发Flutter插件 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/141.592ee9d6.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable open\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" aria-current=\"page\" class=\"active sidebar-link\">12.3 开发Flutter插件</a><ul class=\"sidebar-sub-headers\"></ul></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_12-3-开发flutter插件\"><a href=\"#_12-3-开发flutter插件\" class=\"header-anchor\">#</a> 12.3 开发Flutter插件</h1> <p>下面我们通过一个获取电池电量的插件来介绍一下Flutter插件的开发流程。该插件中我们在Dart中通过<code>getBatteryLevel</code> 调用Android <code>BatteryManager</code> API和iOS <code>device.batteryLevel</code> API。</p> <h3 id=\"创建一个新的应用程序项目\"><a href=\"#创建一个新的应用程序项目\" class=\"header-anchor\">#</a> 创建一个新的应用程序项目</h3> <p>首先创建一个新的应用程序:</p> <ul><li>在终端中运行：<code>flutter create batterylevel</code></li></ul> <p>默认情况下，模板支持使用Java编写Android代码，或使用Objective-C编写iOS代码。要使用Kotlin或Swift，请使用-i和/或-a标志:</p> <ul><li>在终端中运行: <code>flutter create -i swift -a kotlin batterylevel</code></li></ul> <h3 id=\"创建flutter平台客户端\"><a href=\"#创建flutter平台客户端\" class=\"header-anchor\">#</a> 创建Flutter平台客户端</h3> <p>该应用的<code>State</code>类拥有当前的应用状态。我们需要延长这一点以保持当前的电量</p> <p>首先，我们构建通道。我们使用<code>MethodChannel</code>调用一个方法来返回电池电量。</p> <p>通道的客户端和宿主通过通道构造函数中传递的通道名称进行连接。单个应用中使用的所有通道名称必须是唯一的; 我们建议在通道名称前加一个唯一的“域名前缀”，例如<code>samples.flutter.io/battery</code>。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'dart:async'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/services.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_MyHomePageState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>MyHomePage<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">static</span> <span class=\"token keyword\">const</span> platform <span class=\"token operator\">=</span> <span class=\"token keyword\">const</span> <span class=\"token function\">MethodChannel</span><span class=\"token punctuation\">(</span><span class=\"token string\">'samples.flutter.io/battery'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// Get battery level.</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>接下来，我们调用通道上的方法，指定通过字符串标识符调用方法<code>getBatteryLevel</code>。 该调用可能失败(平台不支持平台API，例如在模拟器中运行时)，所以我们将invokeMethod调用包装在try-catch语句中。</p> <p>我们使用返回的结果，在<code>setState</code>中来更新用户界面状态<code>batteryLevel</code>。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>  <span class=\"token comment\">// Get battery level.</span>\n  String _batteryLevel <span class=\"token operator\">=</span> <span class=\"token string\">'Unknown battery level.'</span><span class=\"token punctuation\">;</span>\n\n  Future<span class=\"token operator\">&lt;</span>Null<span class=\"token operator\">&gt;</span> <span class=\"token function\">_getBatteryLevel</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    String batteryLevel<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">final</span> int result <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> platform<span class=\"token punctuation\">.</span><span class=\"token function\">invokeMethod</span><span class=\"token punctuation\">(</span><span class=\"token string\">'getBatteryLevel'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      batteryLevel <span class=\"token operator\">=</span> <span class=\"token string\">'Battery level at $result % .'</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">on</span> PlatformException <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      batteryLevel <span class=\"token operator\">=</span> <span class=\"token string\">&quot;Failed to get battery level: '${e.message}'.&quot;</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _batteryLevel <span class=\"token operator\">=</span> batteryLevel<span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n</code></pre></div><p>最后，我们在build创建包含一个小字体显示电池状态和一个用于刷新值的按钮的用户界面。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token metadata symbol\">@override</span>\nWidget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Material</span><span class=\"token punctuation\">(</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Center</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Column</span><span class=\"token punctuation\">(</span>\n        mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>spaceEvenly<span class=\"token punctuation\">,</span>\n        children<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>\n          <span class=\"token keyword\">new</span> <span class=\"token class-name\">RaisedButton</span><span class=\"token punctuation\">(</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Get Battery Level'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            onPressed<span class=\"token punctuation\">:</span> _getBatteryLevel<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span>_batteryLevel<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>至此Flutter部分的测试代码写好了，接下来我们需要实现Android和iOS平台下的API，由于平台API实现部分篇幅较大，我们将在接下来的两节中，分别介绍Android和iOS端API的实现。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter12/platform-channel.html\" class=\"prev\">\n        12.2 插件开发：平台通道简介\n      </a></span> <span class=\"next\"><a href=\"/chapter12/android_implement.html\">\n        12.4 插件开发：Android端API实现\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/141.592ee9d6.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter12/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>包与插件 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/142.d7d8dbaf.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"包与插件\"><a href=\"#包与插件\" class=\"header-anchor\">#</a> 包与插件</h1> <ul><li><a href=\"/chapter12/develop_package.html\">12.1：开发package</a></li> <li><a href=\"/chapter12/platform-channel.html\">12.2：平台通道简介</a></li> <li><a href=\"/chapter12/develop_plugin.html\">12.3：开发Flutter插件</a></li> <li><a href=\"/chapter12/android_implement.html\">12.4：插件开发：实现Android端API</a></li> <li><a href=\"/chapter12/ios_implement.html\">12.5：插件开发：实现IOS端API</a></li> <li><a href=\"/chapter12/texture_platformview.html\">12.6：Texture和PlatformView</a></li></ul></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/142.d7d8dbaf.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter12/ios_implement.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>12.5 插件开发：iOS端API实现 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/143.77583b74.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable open\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" aria-current=\"page\" class=\"active sidebar-link\">12.5 插件开发：iOS端API实现</a><ul class=\"sidebar-sub-headers\"></ul></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_12-5-插件开发-ios端api实现\"><a href=\"#_12-5-插件开发-ios端api实现\" class=\"header-anchor\">#</a> 12.5 插件开发：iOS端API实现</h1> <p>本节我们接着之前&quot;获取电池电量&quot;插件的示例，来完成iOS端API的实现。以下步骤使用Objective-C，如果您更喜欢Swift，可以直接跳到后面Swift部分。</p> <p>首先打开Xcode中Flutter应用程序的iOS部分:</p> <ol><li>启动 Xcode</li> <li>选择 File &gt; Open…</li> <li>定位到您 Flutter app目录, 然后选择里面的 <code>iOS</code>文件夹，点击 OK</li> <li>确保Xcode项目的构建没有错误。</li> <li>选择 Runner &gt; Runner ，打开<code>AppDelegate.m</code></li></ol> <p>接下来，在<code>application didFinishLaunchingWithOptions:</code>方法内部创建一个<code>FlutterMethodChannel</code>，并添加一个处理方法。 确保与在Flutter客户端使用的通道名称相同。</p> <div class=\"language-objectivec extra-class\"><pre class=\"language-objectivec\"><code><span class=\"token macro property\"><span class=\"token directive-hash\">#</span><span class=\"token directive keyword\">import</span> <span class=\"token expression\"><span class=\"token operator\">&lt;</span>Flutter<span class=\"token operator\">/</span>Flutter<span class=\"token punctuation\">.</span>h<span class=\"token operator\">&gt;</span></span></span>\n\n<span class=\"token keyword\">@implementation</span> AppDelegate\n<span class=\"token operator\">-</span> <span class=\"token punctuation\">(</span>BOOL<span class=\"token punctuation\">)</span>application<span class=\"token punctuation\">:</span><span class=\"token punctuation\">(</span>UIApplication<span class=\"token operator\">*</span><span class=\"token punctuation\">)</span>application didFinishLaunchingWithOptions<span class=\"token punctuation\">:</span><span class=\"token punctuation\">(</span>NSDictionary<span class=\"token operator\">*</span><span class=\"token punctuation\">)</span>launchOptions <span class=\"token punctuation\">{</span>\n  FlutterViewController<span class=\"token operator\">*</span> controller <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>FlutterViewController<span class=\"token operator\">*</span><span class=\"token punctuation\">)</span><span class=\"token keyword\">self</span><span class=\"token punctuation\">.</span>window<span class=\"token punctuation\">.</span>rootViewController<span class=\"token punctuation\">;</span>\n\n  FlutterMethodChannel<span class=\"token operator\">*</span> batteryChannel <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span>FlutterMethodChannel\n                                          methodChannelWithName<span class=\"token punctuation\">:</span><span class=\"token string\">@&quot;samples.flutter.io/battery&quot;</span>\n                                          binaryMessenger<span class=\"token punctuation\">:</span>controller<span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token punctuation\">[</span>batteryChannel setMethodCallHandler<span class=\"token punctuation\">:</span><span class=\"token operator\">^</span><span class=\"token punctuation\">(</span>FlutterMethodCall<span class=\"token operator\">*</span> call<span class=\"token punctuation\">,</span> FlutterResult result<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// TODO</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">[</span><span class=\"token keyword\">super</span> application<span class=\"token punctuation\">:</span>application didFinishLaunchingWithOptions<span class=\"token punctuation\">:</span>launchOptions<span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>接下来，我们添加Objective-C代码，使用iOS电池API来获取电池电量，这和原生是相同的。</p> <p>在<code>AppDelegate</code>类中添加以下新的方法：</p> <div class=\"language-objectivec extra-class\"><pre class=\"language-objectivec\"><code><span class=\"token operator\">-</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">int</span><span class=\"token punctuation\">)</span>getBatteryLevel <span class=\"token punctuation\">{</span>\n  UIDevice<span class=\"token operator\">*</span> device <span class=\"token operator\">=</span> UIDevice<span class=\"token punctuation\">.</span>currentDevice<span class=\"token punctuation\">;</span>\n  device<span class=\"token punctuation\">.</span>batteryMonitoringEnabled <span class=\"token operator\">=</span> YES<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>device<span class=\"token punctuation\">.</span>batteryState <span class=\"token operator\">==</span> UIDeviceBatteryStateUnknown<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token operator\">-</span><span class=\"token number\">1</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">int</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span>device<span class=\"token punctuation\">.</span>batteryLevel <span class=\"token operator\">*</span> <span class=\"token number\">100</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>最后，我们完成之前添加的<code>setMethodCallHandler</code>方法。我们需要处理的平台方法名为<code>getBatteryLevel</code>，所以我们在call参数中需要先判断是否为<code>getBatteryLevel</code>。 这个平台方法的实现只需调用我们在前一步中编写的iOS代码，并使用result参数返回成功或错误的响应。如果调用了未定义的API，我们也会通知返回：</p> <div class=\"language-objectivec extra-class\"><pre class=\"language-objectivec\"><code><span class=\"token punctuation\">[</span>batteryChannel setMethodCallHandler<span class=\"token punctuation\">:</span><span class=\"token operator\">^</span><span class=\"token punctuation\">(</span>FlutterMethodCall<span class=\"token operator\">*</span> call<span class=\"token punctuation\">,</span> FlutterResult result<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token string\">@&quot;getBatteryLevel&quot;</span> isEqualToString<span class=\"token punctuation\">:</span>call<span class=\"token punctuation\">.</span>method<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">int</span> batteryLevel <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token keyword\">self</span> getBatteryLevel<span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>batteryLevel <span class=\"token operator\">==</span> <span class=\"token operator\">-</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">result</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span>FlutterError errorWithCode<span class=\"token punctuation\">:</span><span class=\"token string\">@&quot;UNAVAILABLE&quot;</span>\n                                 message<span class=\"token punctuation\">:</span><span class=\"token string\">@&quot;电池信息不可用&quot;</span>\n                                 details<span class=\"token punctuation\">:</span>nil<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">result</span><span class=\"token punctuation\">(</span><span class=\"token operator\">@</span><span class=\"token punctuation\">(</span>batteryLevel<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">result</span><span class=\"token punctuation\">(</span>FlutterMethodNotImplemented<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>现在可以在iOS上运行该应用程序了，如果使用的是iOS模拟器，请注意，它不支持电池API，因此应用程序将显示“电池信息不可用”。</p> <h3 id=\"使用swift实现ios-api\"><a href=\"#使用swift实现ios-api\" class=\"header-anchor\">#</a> 使用Swift实现iOS API</h3> <p>以下步骤与上面使用Objective-C相似，首先打开Xcode中Flutter应用程序的iOS部分:</p> <ol><li>启动 Xcode</li> <li>选择 File &gt; Open…</li> <li>定位到您 Flutter app目录, 然后选择里面的 <code>ios</code>文件夹，点击 OK</li> <li>确保Xcode项目的构建没有错误。</li> <li>选择 Runner &gt; Runner ，然后打开<code>AppDelegate.swift</code></li></ol> <p>接下来，覆盖application方法并创建一个<code>FlutterMethodChannel</code>绑定通道名称<code>samples.flutter.io/battery</code>：</p> <div class=\"language-swift extra-class\"><pre class=\"language-swift\"><code><span class=\"token atrule\">@UIApplicationMain</span>\n<span class=\"token atrule\">@objc</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">AppDelegate</span><span class=\"token punctuation\">:</span> <span class=\"token builtin\">FlutterAppDelegate</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">override</span> <span class=\"token keyword\">func</span> <span class=\"token function\">application</span><span class=\"token punctuation\">(</span>\n    <span class=\"token number\">_</span> application<span class=\"token punctuation\">:</span> <span class=\"token builtin\">UIApplication</span><span class=\"token punctuation\">,</span>\n    didFinishLaunchingWithOptions launchOptions<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span><span class=\"token builtin\">UIApplicationLaunchOptionsKey</span><span class=\"token punctuation\">:</span> <span class=\"token builtin\">Any</span><span class=\"token punctuation\">]</span><span class=\"token operator\">?</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span><span class=\"token operator\">&gt;</span> <span class=\"token builtin\">Bool</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token builtin\">GeneratedPluginRegistrant</span><span class=\"token punctuation\">.</span><span class=\"token function\">register</span><span class=\"token punctuation\">(</span>with<span class=\"token punctuation\">:</span> <span class=\"token keyword\">self</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">let</span> controller <span class=\"token punctuation\">:</span> <span class=\"token builtin\">FlutterViewController</span> <span class=\"token operator\">=</span> window<span class=\"token operator\">?</span><span class=\"token punctuation\">.</span>rootViewController <span class=\"token keyword\">as</span><span class=\"token operator\">!</span> <span class=\"token builtin\">FlutterViewController</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">let</span> batteryChannel <span class=\"token operator\">=</span> <span class=\"token builtin\">FlutterMethodChannel</span><span class=\"token punctuation\">.</span><span class=\"token keyword\">init</span><span class=\"token punctuation\">(</span>name<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;samples.flutter.io/battery&quot;</span><span class=\"token punctuation\">,</span>\n                                                   binaryMessenger<span class=\"token punctuation\">:</span> controller<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    batteryChannel<span class=\"token punctuation\">.</span><span class=\"token function\">setMethodCallHandler</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n      <span class=\"token punctuation\">(</span>call<span class=\"token punctuation\">:</span> <span class=\"token builtin\">FlutterMethodCall</span><span class=\"token punctuation\">,</span> result<span class=\"token punctuation\">:</span> <span class=\"token builtin\">FlutterResult</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span><span class=\"token operator\">&gt;</span> <span class=\"token builtin\">Void</span> <span class=\"token keyword\">in</span>\n      <span class=\"token comment\">// Handle battery messages.</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">application</span><span class=\"token punctuation\">(</span>application<span class=\"token punctuation\">,</span> didFinishLaunchingWithOptions<span class=\"token punctuation\">:</span> launchOptions<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>接下来，我们添加Swift代码，使用iOS电池API来获取电池电量，这和原生开发是相同的。</p> <p>将以下新方法添加到<code>AppDelegate.swift</code>底部:</p> <div class=\"language-swift extra-class\"><pre class=\"language-swift\"><code><span class=\"token keyword\">private</span> <span class=\"token keyword\">func</span> <span class=\"token function\">receiveBatteryLevel</span><span class=\"token punctuation\">(</span>result<span class=\"token punctuation\">:</span> <span class=\"token builtin\">FlutterResult</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">let</span> device <span class=\"token operator\">=</span> <span class=\"token builtin\">UIDevice</span><span class=\"token punctuation\">.</span>current<span class=\"token punctuation\">;</span>\n  device<span class=\"token punctuation\">.</span>isBatteryMonitoringEnabled <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>device<span class=\"token punctuation\">.</span>batteryState <span class=\"token operator\">==</span> <span class=\"token builtin\">UIDeviceBatteryState</span><span class=\"token punctuation\">.</span>unknown<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">result</span><span class=\"token punctuation\">(</span><span class=\"token builtin\">FlutterError</span><span class=\"token punctuation\">.</span><span class=\"token keyword\">init</span><span class=\"token punctuation\">(</span>code<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;UNAVAILABLE&quot;</span><span class=\"token punctuation\">,</span>\n                             message<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;电池信息不可用&quot;</span><span class=\"token punctuation\">,</span>\n                             details<span class=\"token punctuation\">:</span> <span class=\"token constant\">nil</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">result</span><span class=\"token punctuation\">(</span><span class=\"token function\">Int</span><span class=\"token punctuation\">(</span>device<span class=\"token punctuation\">.</span>batteryLevel <span class=\"token operator\">*</span> <span class=\"token number\">100</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>最后，我们完成之前添加的<code>setMethodCallHandler</code>方法。我们需要处理的平台方法名为<code>getBatteryLevel</code>，所以我们在call参数中需要先判断是否为<code>getBatteryLevel</code>。 这个平台方法的实现只需调用我们在前一步中编写的iOS代码，并使用result参数返回成功或错误的响应。如果调用了未定义的API，我们也会通知返回：</p> <div class=\"language-swift extra-class\"><pre class=\"language-swift\"><code>batteryChannel<span class=\"token punctuation\">.</span><span class=\"token function\">setMethodCallHandler</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">(</span>call<span class=\"token punctuation\">:</span> <span class=\"token builtin\">FlutterMethodCall</span><span class=\"token punctuation\">,</span> result<span class=\"token punctuation\">:</span> <span class=\"token builtin\">FlutterResult</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span><span class=\"token operator\">&gt;</span> <span class=\"token builtin\">Void</span> <span class=\"token keyword\">in</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token string\">&quot;getBatteryLevel&quot;</span> <span class=\"token operator\">==</span> call<span class=\"token punctuation\">.</span>method<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">receiveBatteryLevel</span><span class=\"token punctuation\">(</span>result<span class=\"token punctuation\">:</span> result<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">result</span><span class=\"token punctuation\">(</span><span class=\"token builtin\">FlutterMethodNotImplemented</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>现在可以在iOS上运行应用程序，如果使用的是iOS模拟器，请注意，它不支持电池API，因此应用程序将显示“电池信息不可用”。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter12/android_implement.html\" class=\"prev\">\n        12.4 插件开发：Android端API实现\n      </a></span> <span class=\"next\"><a href=\"/chapter12/texture_platformview.html\">\n        12.6 Texture和PlatformView\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/143.77583b74.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter12/platform-channel.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>12.2 插件开发：平台通道简介 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/89.4ff5c882.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable open\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" aria-current=\"page\" class=\"active sidebar-link\">12.2 插件开发：平台通道简介</a><ul class=\"sidebar-sub-headers\"></ul></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_12-2-插件开发-平台通道简介\"><a href=\"#_12-2-插件开发-平台通道简介\" class=\"header-anchor\">#</a> 12.2 插件开发：平台通道简介</h1> <p>“平台特定”或“特定平台”中的平台指的就是Flutter应用程序运行的平台，如Android或IOS。我们知道一个完整的Flutter应用程序实际上包括原生代码和Flutter代码两部分。由于Flutter本身只是一个UI系统，它本身是无法提供一些系统能力，比如使用蓝牙、相机、GPS等，因此要在Flutter APP中调用这些能力就必须和原生平台进行通信。为此，Flutter中提供了一个平台通道（platform channel），用于Flutter和原生平台的通信。平台通道正是Flutter和原生之间通信的桥梁，它也是Flutter插件的底层基础设施。</p> <p>Flutter使用了一个灵活的系统，允许您调用特定平台的API，无论在Android上的Java或Kotlin代码中，还是iOS上的ObjectiveC或Swift代码中均可用。</p> <p>Flutter与原生之间的通信依赖灵活的消息传递方式：</p> <ul><li>应用的Flutter部分通过平台通道（platform channel）将消息发送到其应用程序的所在的宿主（iOS或Android）应用（原生应用）。</li> <li>宿主监听平台通道，并接收该消息。然后它会调用该平台的API，并将响应发送回客户端，即应用程序的Flutter部分。</li></ul> <h3 id=\"平台通道\"><a href=\"#平台通道\" class=\"header-anchor\">#</a> 平台通道</h3> <p>使用平台通道在Flutter(client)和原生(host)之间传递消息，如下图所示：</p> <p><img src=\"/assets/img/12-3.2326714a.png\" alt=\"平台通道\"></p> <p>当在Flutter中调用原生方法时，调用信息通过平台通道传递到原生，原生收到调用信息后方可执行指定的操作，如需返回数据，则原生会将数据再通过平台通道传递给Flutter。值得注意的是消息传递是异步的，这确保了用户界面在消息传递时不会被挂起。</p> <p>在客户端，<a href=\"https://docs.flutter.io/flutter/services/MethodChannel-class.html\" target=\"_blank\" rel=\"noopener noreferrer\">MethodChannel  API<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 可以发送与方法调用相对应的消息。 在宿主平台上，<code>MethodChannel</code> 在<a href=\"https://docs.flutter.io/javadoc/io/flutter/plugin/common/MethodChannel.html\" target=\"_blank\" rel=\"noopener noreferrer\">Android API<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 和 <a href=\"https://docs.flutter.io/objcdoc/Classes/FlutterMethodChannel.html\" target=\"_blank\" rel=\"noopener noreferrer\">FlutterMethodChannel iOS API<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>可以接收方法调用并返回结果。这些类可以帮助我们用很少的代码就能开发平台插件。</p> <blockquote><p><strong>注意</strong>: 如果需要，方法调用(消息传递)可以是反向的，即宿主作为客户端调用Dart中实现的API。 <a href=\"https://pub.dartlang.org/packages/quick_actions\" target=\"_blank\" rel=\"noopener noreferrer\"><code>quick_actions</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>插件就是一个具体的例子。</p></blockquote> <h3 id=\"平台通道数据类型支持\"><a href=\"#平台通道数据类型支持\" class=\"header-anchor\">#</a> 平台通道数据类型支持</h3> <p>平台通道使用标准消息编/解码器对消息进行编解码，它可以高效的对消息进行二进制序列化与反序列化。由于Dart与原生平台之间数据类型有所差异，下面我们列出数据类型之间的映射关系。</p> <table><thead><tr><th>Dart</th> <th>Android</th> <th>iOS</th></tr></thead> <tbody><tr><td>null</td> <td>null</td> <td>nil (NSNull when nested)</td></tr> <tr><td>bool</td> <td>java.lang.Boolean</td> <td>NSNumber numberWithBool:</td></tr> <tr><td>int</td> <td>java.lang.Integer</td> <td>NSNumber numberWithInt:</td></tr> <tr><td>int, 如果不足32位</td> <td>java.lang.Long</td> <td>NSNumber numberWithLong:</td></tr> <tr><td>int, 如果不足64位</td> <td>java.math.BigInteger</td> <td>FlutterStandardBigInteger</td></tr> <tr><td>double</td> <td>java.lang.Double</td> <td>NSNumber numberWithDouble:</td></tr> <tr><td>String</td> <td>java.lang.String</td> <td>NSString</td></tr> <tr><td>Uint8List</td> <td>byte[]</td> <td>FlutterStandardTypedData typedDataWithBytes:</td></tr> <tr><td>Int32List</td> <td>int[]</td> <td>FlutterStandardTypedData typedDataWithInt32:</td></tr> <tr><td>Int64List</td> <td>long[]</td> <td>FlutterStandardTypedData typedDataWithInt64:</td></tr> <tr><td>Float64List</td> <td>double[]</td> <td>FlutterStandardTypedData typedDataWithFloat64:</td></tr> <tr><td>List</td> <td>java.util.ArrayList</td> <td>NSArray</td></tr> <tr><td>Map</td> <td>java.util.HashMap</td> <td>NSDictionary</td></tr></tbody></table> <p>当在发送和接收值时，这些值在消息中的序列化和反序列化会自动进行。</p> <h3 id=\"自定义编解码器\"><a href=\"#自定义编解码器\" class=\"header-anchor\">#</a> 自定义编解码器</h3> <p>除了上面提到的<code>MethodChannel</code>，还可以使用<a href=\"https://docs.flutter.io/flutter/services/BasicMessageChannel-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>BasicMessageChannel</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>，它支持使用自定义消息编解码器进行基本的异步消息传递。 此外，可以使用专门的<a href=\"https://docs.flutter.io/flutter/services/BinaryCodec-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>BinaryCodec</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>、<a href=\"https://docs.flutter.io/flutter/services/StringCodec-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>StringCodec</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>和 <a href=\"https://docs.flutter.io/flutter/services/JSONMessageCodec-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>JSONMessageCodec</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>类，或创建自己的编解码器。</p> <h3 id=\"如何获取平台信息\"><a href=\"#如何获取平台信息\" class=\"header-anchor\">#</a> 如何获取平台信息</h3> <p>Flutter 中提供了一个全局变量<code>defaultTargetPlatform</code>来获取当前应用的平台信息，<code>defaultTargetPlatform</code>定义在&quot;platform.dart&quot;中，它的类型是<code>TargetPlatform</code>，这是一个枚举类，定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">enum</span> TargetPlatform <span class=\"token punctuation\">{</span>\n  android<span class=\"token punctuation\">,</span>\n  fuchsia<span class=\"token punctuation\">,</span>\n  iOS<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以看到目前Flutter只支持这三个平台。我们可以通过如下代码判断平台：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>defaultTargetPlatform<span class=\"token operator\">==</span>TargetPlatform<span class=\"token punctuation\">.</span>android<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// 是安卓系统，do something</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n</code></pre></div><p>由于不同平台有它们各自的交互规范，Flutter Material库中的一些组件都针对相应的平台做了一些适配，比如路由组件<code>MaterialPageRoute</code>，它在android和ios中会应用各自平台规范的切换动画。那如果我们想让我们的APP在所有平台都表现一致，比如希望在所有平台路由切换动画都按照ios平台一致的左右滑动切换风格该怎么做？Flutter中提供了一种覆盖默认平台的机制，我们可以通过显式指定<code>debugDefaultTargetPlatformOverride</code>全局变量的值来指定应用平台。比如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>debugDefaultTargetPlatformOverride<span class=\"token operator\">=</span>TargetPlatform<span class=\"token punctuation\">.</span>iOS<span class=\"token punctuation\">;</span>\n<span class=\"token function\">print</span><span class=\"token punctuation\">(</span>defaultTargetPlatform<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// 会输出TargetPlatform.iOS</span>\n</code></pre></div><p>上面代码即在Android中运行后，Flutter APP就会认为是当前系统是iOS，Material组件库中所有组件交互方式都会和iOS平台对齐，<code>defaultTargetPlatform</code>的值也会变为<code>TargetPlatform.iOS</code>。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter12/develop_package.html\" class=\"prev\">\n        12.1 开发Package\n      </a></span> <span class=\"next\"><a href=\"/chapter12/develop_plugin.html\">\n        12.3 开发Flutter插件\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/89.4ff5c882.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter12/texture_platformview.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>12.6 Texture和PlatformView | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/62.b51637d5.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable open\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" aria-current=\"page\" class=\"active sidebar-link\">12.6 Texture和PlatformView</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/chapter12/texture_platformview.html#_12-6-1-texture-示例-使用摄像头\" class=\"sidebar-link\">12.6.1 Texture（示例：使用摄像头）</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter12/texture_platformview.html#_12-6-2-platformview-示例-webview\" class=\"sidebar-link\">12.6.2 PlatformView （示例：WebView）</a></li></ul></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_12-6-texture和platformview\"><a href=\"#_12-6-texture和platformview\" class=\"header-anchor\">#</a> 12.6 Texture和PlatformView</h1> <p>本节主要介绍原生和Flutter之间如何共享图像，以及如何在Flutter中嵌套原生组件。</p> <h2 id=\"_12-6-1-texture-示例-使用摄像头\"><a href=\"#_12-6-1-texture-示例-使用摄像头\" class=\"header-anchor\">#</a> 12.6.1 Texture（示例：使用摄像头）</h2> <p>前面说过Flutter本身只是一个UI系统，对于一些系统能力的调用我们可以通过消息传送机制与原生交互。但是这种消息传送机制并不能覆盖所有的应用场景，比如我们想调用摄像头来拍照或录视频，但在拍照和录视频的过程中我们需要将预览画面显示到我们的Flutter UI中，如果我们要用Flutter定义的消息通道机制来实现这个功能，就需要将摄像头采集的每一帧图片都要从原生传递到Flutter中，这样做代价将会非常大，因为将图像或视频数据通过消息通道实时传输必然会引起内存和CPU的巨大消耗！为此，Flutter提供了一种基于Texture的图片数据共享机制。</p> <p>Texture可以理解为GPU内保存将要绘制的图像数据的一个对象，Flutter engine会将Texture的数据在内存中直接进行映射（而无需在原生和Flutter之间再进行数据传递），Flutter会给每一个Texture分配一个id，同时Flutter中提供了一个<code>Texture</code>组件，<code>Texture</code>构造函数定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">const</span> <span class=\"token function\">Texture</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  Key key<span class=\"token punctuation\">,</span>\n  <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>textureId<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p><code>Texture</code> 组件正是通过<code>textureId</code>与Texture数据关联起来；在<code>Texture</code>组件绘制时，Flutter会自动从内存中找到相应id的Texture数据，然后进行绘制。可以总结一下整个流程：图像数据先在原生部分缓存，然后在Flutter部分再通过<code>textureId</code>和缓存关联起来，最后绘制由Flutter完成。</p> <p>如果我们作为一个插件开发者，我们在原生代码中分配了<code>textureId</code>，那么在Flutter侧使用<code>Texture</code>组件时要如何获取<code>textureId</code>呢？这又回到了之前的内容了，<code>textureId</code>完全可以通过MethodChannel来传递。</p> <p>另外，值得注意的是，当原生摄像头捕获的图像发生变化时，<code>Texture</code> 组件会自动重绘，这不需要我们写任何Dart 代码去控制。</p> <h3 id=\"texture用法\"><a href=\"#texture用法\" class=\"header-anchor\">#</a> Texture用法</h3> <p>如果我们要手动实现一个相机插件，和前面几节介绍的“获取剩余电量”插件的步骤一样，需要分别实现原生部分和Flutter部分。考虑到大多数读者可能并非同时既了解Android开发，又了解iOS开发，如果我们再花大量篇幅来介绍不同端的实现可能会没什么意义，另外，由于Flutter官方提供的相机（camera）插件和视频播放（video_player）插件都是使用Texture来实现的，它们本身就是Texture非常好的示例，所以在本书中将不会再介绍使用Texture的具体流程了，读者有兴趣查看camera和video_player的实现代码。下面我们重点介绍一下如何使用camera和video_player。</p> <h3 id=\"相机示例\"><a href=\"#相机示例\" class=\"header-anchor\">#</a> 相机示例</h3> <p>下面我们看一下camera包自带的一个示例，它包含如下功能：</p> <ol><li>可以拍照，也可以拍视频，拍摄完成后可以保存；排号的视频可以播放预览。</li> <li>可以切换摄像头（前置摄像头、后置摄像头、其它）</li> <li>可以显示已经拍摄内容的预览图。</li></ol> <p>下面我们看一下具体代码：</p> <ol><li><p>首先，依赖camera插件的最新版，并下载依赖。</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code><span class=\"token key atrule\">dependencies</span><span class=\"token punctuation\">:</span>\n  <span class=\"token punctuation\">...</span>  //省略无关代码\n  <span class=\"token key atrule\">camera</span><span class=\"token punctuation\">:</span> ^0.5.2+2\n</code></pre></div></li> <li><p>在<code>main</code>方法中获取可用摄像头列表。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">main</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// 获取可用摄像头列表，cameras为全局变量</span>\n  cameras <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">availableCameras</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token function\">runApp</span><span class=\"token punctuation\">(</span><span class=\"token function\">MyApp</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div></li> <li><p>构建UI。现在我们构建如图12-4的测试界面：</p> <p><img src=\"/assets/img/12-4.49c3c9bf.jpg\" alt=\"12-4\">\n线面是完整的代码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:camera/camera.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'../common.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'dart:async'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'dart:io'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:path_provider/path_provider.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:video_player/video_player.dart'</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//用于播放录制的视频</span>\n\n<span class=\"token comment\">/// 获取不同摄像头的图标（前置、后置、其它）</span>\nIconData <span class=\"token function\">getCameraLensIcon</span><span class=\"token punctuation\">(</span>CameraLensDirection direction<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">switch</span> <span class=\"token punctuation\">(</span>direction<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">case</span> CameraLensDirection<span class=\"token punctuation\">.</span>back<span class=\"token punctuation\">:</span>\n      <span class=\"token keyword\">return</span> Icons<span class=\"token punctuation\">.</span>camera_rear<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">case</span> CameraLensDirection<span class=\"token punctuation\">.</span>front<span class=\"token punctuation\">:</span>\n      <span class=\"token keyword\">return</span> Icons<span class=\"token punctuation\">.</span>camera_front<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">case</span> CameraLensDirection<span class=\"token punctuation\">.</span><span class=\"token keyword\">external</span><span class=\"token punctuation\">:</span>\n      <span class=\"token keyword\">return</span> Icons<span class=\"token punctuation\">.</span>camera<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token keyword\">throw</span> <span class=\"token function\">ArgumentError</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Unknown lens direction'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">void</span> <span class=\"token function\">logError</span><span class=\"token punctuation\">(</span>String code<span class=\"token punctuation\">,</span> String message<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Error: $code\\nError Message: $message'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// 示例页面路由</span>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">CameraExampleHome</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _CameraExampleHomeState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">_CameraExampleHomeState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_CameraExampleHomeState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>CameraExampleHome<span class=\"token operator\">&gt;</span>\n    <span class=\"token keyword\">with</span> WidgetsBindingObserver <span class=\"token punctuation\">{</span>\n  CameraController controller<span class=\"token punctuation\">;</span>\n  String imagePath<span class=\"token punctuation\">;</span> <span class=\"token comment\">// 图片保存路径</span>\n  String videoPath<span class=\"token punctuation\">;</span> <span class=\"token comment\">//视频保存路径</span>\n  VideoPlayerController videoController<span class=\"token punctuation\">;</span>\n  VoidCallback videoPlayerListener<span class=\"token punctuation\">;</span>\n  bool enableAudio <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">// 监听APP状态改变，是否在前台</span>\n    WidgetsBinding<span class=\"token punctuation\">.</span>instance<span class=\"token punctuation\">.</span><span class=\"token function\">addObserver</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    WidgetsBinding<span class=\"token punctuation\">.</span>instance<span class=\"token punctuation\">.</span><span class=\"token function\">removeObserver</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">didChangeAppLifecycleState</span><span class=\"token punctuation\">(</span>AppLifecycleState state<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 如果APP不在在前台</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>state <span class=\"token operator\">==</span> AppLifecycleState<span class=\"token punctuation\">.</span>inactive<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      controller<span class=\"token operator\">?</span><span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>state <span class=\"token operator\">==</span> AppLifecycleState<span class=\"token punctuation\">.</span>resumed<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">// 在前台</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>controller <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">onNewCameraSelected</span><span class=\"token punctuation\">(</span>controller<span class=\"token punctuation\">.</span>description<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">final</span> GlobalKey<span class=\"token operator\">&lt;</span>ScaffoldState<span class=\"token operator\">&gt;</span> _scaffoldKey <span class=\"token operator\">=</span> GlobalKey<span class=\"token operator\">&lt;</span>ScaffoldState<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n      key<span class=\"token punctuation\">:</span> _scaffoldKey<span class=\"token punctuation\">,</span>\n      appBar<span class=\"token punctuation\">:</span> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>\n        title<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'相机示例'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      body<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n        children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n          <span class=\"token function\">Expanded</span><span class=\"token punctuation\">(</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n                padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">1.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                child<span class=\"token punctuation\">:</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n                  child<span class=\"token punctuation\">:</span> <span class=\"token function\">_cameraPreviewWidget</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>\n                color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>black<span class=\"token punctuation\">,</span>\n                border<span class=\"token punctuation\">:</span> Border<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span>\n                  color<span class=\"token punctuation\">:</span> controller <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">&amp;&amp;</span> controller<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>isRecordingVideo\n                      <span class=\"token operator\">?</span> Colors<span class=\"token punctuation\">.</span>redAccent\n                      <span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">,</span>\n                  width<span class=\"token punctuation\">:</span> <span class=\"token number\">3.0</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">_captureControlRowWidget</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">_toggleAudioWidget</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n            padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">5.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n              mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n              children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                <span class=\"token function\">_cameraTogglesRowWidget</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token function\">_thumbnailWidget</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">/// 展示预览窗口</span>\n  Widget <span class=\"token function\">_cameraPreviewWidget</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>controller <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">||</span> <span class=\"token operator\">!</span>controller<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>isInitialized<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>\n        <span class=\"token string\">'选择一个摄像头'</span><span class=\"token punctuation\">,</span>\n        style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>\n          color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">,</span>\n          fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">24.0</span><span class=\"token punctuation\">,</span>\n          fontWeight<span class=\"token punctuation\">:</span> FontWeight<span class=\"token punctuation\">.</span>w900<span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token function\">AspectRatio</span><span class=\"token punctuation\">(</span>\n        aspectRatio<span class=\"token punctuation\">:</span> controller<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>aspectRatio<span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">CameraPreview</span><span class=\"token punctuation\">(</span>controller<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">/// 开启或关闭录音</span>\n  Widget <span class=\"token function\">_toggleAudioWidget</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n      padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">only</span><span class=\"token punctuation\">(</span>left<span class=\"token punctuation\">:</span> <span class=\"token number\">25</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n        children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n          <span class=\"token keyword\">const</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'开启录音:'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">Switch</span><span class=\"token punctuation\">(</span>\n            value<span class=\"token punctuation\">:</span> enableAudio<span class=\"token punctuation\">,</span>\n            onChanged<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>bool value<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              enableAudio <span class=\"token operator\">=</span> value<span class=\"token punctuation\">;</span>\n              <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>controller <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token function\">onNewCameraSelected</span><span class=\"token punctuation\">(</span>controller<span class=\"token punctuation\">.</span>description<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">/// 显示已拍摄的图片/视频缩略图。</span>\n  Widget <span class=\"token function\">_thumbnailWidget</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Expanded</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Align</span><span class=\"token punctuation\">(</span>\n        alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>centerRight<span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n          mainAxisSize<span class=\"token punctuation\">:</span> MainAxisSize<span class=\"token punctuation\">.</span>min<span class=\"token punctuation\">,</span>\n          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            videoController <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">&amp;&amp;</span> imagePath <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span>\n                <span class=\"token operator\">?</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n                <span class=\"token punctuation\">:</span> <span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>videoController <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span>\n                  <span class=\"token operator\">?</span> Image<span class=\"token punctuation\">.</span><span class=\"token function\">file</span><span class=\"token punctuation\">(</span><span class=\"token function\">File</span><span class=\"token punctuation\">(</span>imagePath<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n                  <span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n                child<span class=\"token punctuation\">:</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n                  child<span class=\"token punctuation\">:</span> <span class=\"token function\">AspectRatio</span><span class=\"token punctuation\">(</span>\n                      aspectRatio<span class=\"token punctuation\">:</span>\n                      videoController<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>size <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span>\n                          <span class=\"token operator\">?</span> videoController<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>aspectRatio\n                          <span class=\"token punctuation\">:</span> <span class=\"token number\">1.0</span><span class=\"token punctuation\">,</span>\n                      child<span class=\"token punctuation\">:</span> <span class=\"token function\">VideoPlayer</span><span class=\"token punctuation\">(</span>videoController<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>\n                    border<span class=\"token punctuation\">:</span> Border<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>pink<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              width<span class=\"token punctuation\">:</span> <span class=\"token number\">64.0</span><span class=\"token punctuation\">,</span>\n              height<span class=\"token punctuation\">:</span> <span class=\"token number\">64.0</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">/// 相机工具栏</span>\n  Widget <span class=\"token function\">_captureControlRowWidget</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n      mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>spaceEvenly<span class=\"token punctuation\">,</span>\n      mainAxisSize<span class=\"token punctuation\">:</span> MainAxisSize<span class=\"token punctuation\">.</span>max<span class=\"token punctuation\">,</span>\n      children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n        <span class=\"token function\">IconButton</span><span class=\"token punctuation\">(</span>\n          icon<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>camera_alt<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span>\n          onPressed<span class=\"token punctuation\">:</span> controller <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">&amp;&amp;</span>\n              controller<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>isInitialized <span class=\"token operator\">&amp;&amp;</span>\n              <span class=\"token operator\">!</span>controller<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>isRecordingVideo\n              <span class=\"token operator\">?</span> onTakePictureButtonPressed\n              <span class=\"token punctuation\">:</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token function\">IconButton</span><span class=\"token punctuation\">(</span>\n          icon<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>videocam<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span>\n          onPressed<span class=\"token punctuation\">:</span> controller <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">&amp;&amp;</span>\n              controller<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>isInitialized <span class=\"token operator\">&amp;&amp;</span>\n              <span class=\"token operator\">!</span>controller<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>isRecordingVideo\n              <span class=\"token operator\">?</span> onVideoRecordButtonPressed\n              <span class=\"token punctuation\">:</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token function\">IconButton</span><span class=\"token punctuation\">(</span>\n          icon<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>stop<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span>\n          onPressed<span class=\"token punctuation\">:</span> controller <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">&amp;&amp;</span>\n              controller<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>isInitialized <span class=\"token operator\">&amp;&amp;</span>\n              controller<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>isRecordingVideo\n              <span class=\"token operator\">?</span> onStopButtonPressed\n              <span class=\"token punctuation\">:</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">/// 展示所有摄像头</span>\n  Widget <span class=\"token function\">_cameraTogglesRowWidget</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">final</span> List<span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span> toggles <span class=\"token operator\">=</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>cameras<span class=\"token punctuation\">.</span>isEmpty<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'没有检测到摄像头'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span>CameraDescription cameraDescription <span class=\"token keyword\">in</span> cameras<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        toggles<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span>\n          <span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span>\n            width<span class=\"token punctuation\">:</span> <span class=\"token number\">90.0</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> RadioListTile<span class=\"token operator\">&lt;</span>CameraDescription<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n              title<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span><span class=\"token function\">getCameraLensIcon</span><span class=\"token punctuation\">(</span>cameraDescription<span class=\"token punctuation\">.</span>lensDirection<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              groupValue<span class=\"token punctuation\">:</span> controller<span class=\"token operator\">?</span><span class=\"token punctuation\">.</span>description<span class=\"token punctuation\">,</span>\n              value<span class=\"token punctuation\">:</span> cameraDescription<span class=\"token punctuation\">,</span>\n              onChanged<span class=\"token punctuation\">:</span> controller <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">&amp;&amp;</span> controller<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>isRecordingVideo\n                  <span class=\"token operator\">?</span> <span class=\"token keyword\">null</span>\n                  <span class=\"token punctuation\">:</span> onNewCameraSelected<span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>children<span class=\"token punctuation\">:</span> toggles<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  String <span class=\"token function\">timestamp</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> DateTime<span class=\"token punctuation\">.</span><span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>millisecondsSinceEpoch<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">showInSnackBar</span><span class=\"token punctuation\">(</span>String message<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    _scaffoldKey<span class=\"token punctuation\">.</span>currentState<span class=\"token punctuation\">.</span><span class=\"token function\">showSnackBar</span><span class=\"token punctuation\">(</span><span class=\"token function\">SnackBar</span><span class=\"token punctuation\">(</span>content<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>message<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">// 摄像头选中回调</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">onNewCameraSelected</span><span class=\"token punctuation\">(</span>CameraDescription cameraDescription<span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>controller <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">await</span> controller<span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    controller <span class=\"token operator\">=</span> <span class=\"token function\">CameraController</span><span class=\"token punctuation\">(</span>\n      cameraDescription<span class=\"token punctuation\">,</span>\n      ResolutionPreset<span class=\"token punctuation\">.</span>high<span class=\"token punctuation\">,</span>\n      enableAudio<span class=\"token punctuation\">:</span> enableAudio<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    controller<span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>mounted<span class=\"token punctuation\">)</span> <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>controller<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>hasError<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">showInSnackBar</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Camera error ${controller.value.errorDescription}'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">await</span> controller<span class=\"token punctuation\">.</span><span class=\"token function\">initialize</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">on</span> CameraException <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">_showCameraException</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>mounted<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">// 拍照按钮点击回调</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">onTakePictureButtonPressed</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">takePicture</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>String filePath<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>mounted<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          imagePath <span class=\"token operator\">=</span> filePath<span class=\"token punctuation\">;</span>\n          videoController<span class=\"token operator\">?</span><span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          videoController <span class=\"token operator\">=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>filePath <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token function\">showInSnackBar</span><span class=\"token punctuation\">(</span><span class=\"token string\">'图片保存在 $filePath'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">// 开始录制视频</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">onVideoRecordButtonPressed</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">startVideoRecording</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>String filePath<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>mounted<span class=\"token punctuation\">)</span> <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>filePath <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token function\">showInSnackBar</span><span class=\"token punctuation\">(</span><span class=\"token string\">'正在保存视频于 $filePath'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">// 终止视频录制</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">onStopButtonPressed</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">stopVideoRecording</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>_<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>mounted<span class=\"token punctuation\">)</span> <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token function\">showInSnackBar</span><span class=\"token punctuation\">(</span><span class=\"token string\">'视频保存在: $videoPath'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  Future<span class=\"token operator\">&lt;</span>String<span class=\"token operator\">&gt;</span> <span class=\"token function\">startVideoRecording</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>controller<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>isInitialized<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">showInSnackBar</span><span class=\"token punctuation\">(</span><span class=\"token string\">'请先选择一个摄像头'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">return</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token comment\">// 确定视频保存的路径</span>\n    <span class=\"token keyword\">final</span> Directory extDir <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">getApplicationDocumentsDirectory</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">final</span> String dirPath <span class=\"token operator\">=</span> <span class=\"token string\">'${extDir.path}/Movies/flutter_test'</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">await</span> <span class=\"token function\">Directory</span><span class=\"token punctuation\">(</span>dirPath<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">create</span><span class=\"token punctuation\">(</span>recursive<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">final</span> String filePath <span class=\"token operator\">=</span> <span class=\"token string\">'$dirPath/${timestamp()}.mp4'</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>controller<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>isRecordingVideo<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">// 如果正在录制，则直接返回</span>\n      <span class=\"token keyword\">return</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n      videoPath <span class=\"token operator\">=</span> filePath<span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">await</span> controller<span class=\"token punctuation\">.</span><span class=\"token function\">startVideoRecording</span><span class=\"token punctuation\">(</span>filePath<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">on</span> CameraException <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">_showCameraException</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">return</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">return</span> filePath<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  Future<span class=\"token operator\">&lt;</span><span class=\"token keyword\">void</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">stopVideoRecording</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>controller<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>isRecordingVideo<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">await</span> controller<span class=\"token punctuation\">.</span><span class=\"token function\">stopVideoRecording</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">on</span> CameraException <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">_showCameraException</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">return</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">await</span> <span class=\"token function\">_startVideoPlayer</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  Future<span class=\"token operator\">&lt;</span><span class=\"token keyword\">void</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">_startVideoPlayer</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">final</span> VideoPlayerController vcontroller <span class=\"token operator\">=</span>\n    VideoPlayerController<span class=\"token punctuation\">.</span><span class=\"token function\">file</span><span class=\"token punctuation\">(</span><span class=\"token function\">File</span><span class=\"token punctuation\">(</span>videoPath<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    videoPlayerListener <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>videoController <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">&amp;&amp;</span> videoController<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>size <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">// Refreshing the state to update video player with the correct ratio.</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>mounted<span class=\"token punctuation\">)</span> <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        videoController<span class=\"token punctuation\">.</span><span class=\"token function\">removeListener</span><span class=\"token punctuation\">(</span>videoPlayerListener<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n    vcontroller<span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span>videoPlayerListener<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">await</span> vcontroller<span class=\"token punctuation\">.</span><span class=\"token function\">setLooping</span><span class=\"token punctuation\">(</span><span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">await</span> vcontroller<span class=\"token punctuation\">.</span><span class=\"token function\">initialize</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">await</span> videoController<span class=\"token operator\">?</span><span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>mounted<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        imagePath <span class=\"token operator\">=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n        videoController <span class=\"token operator\">=</span> vcontroller<span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">await</span> vcontroller<span class=\"token punctuation\">.</span><span class=\"token function\">play</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  Future<span class=\"token operator\">&lt;</span>String<span class=\"token operator\">&gt;</span> <span class=\"token function\">takePicture</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>controller<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>isInitialized<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">showInSnackBar</span><span class=\"token punctuation\">(</span><span class=\"token string\">'错误: 请先选择一个相机'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">return</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">final</span> Directory extDir <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">getApplicationDocumentsDirectory</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">final</span> String dirPath <span class=\"token operator\">=</span> <span class=\"token string\">'${extDir.path}/Pictures/flutter_test'</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">await</span> <span class=\"token function\">Directory</span><span class=\"token punctuation\">(</span>dirPath<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">create</span><span class=\"token punctuation\">(</span>recursive<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">final</span> String filePath <span class=\"token operator\">=</span> <span class=\"token string\">'$dirPath/${timestamp()}.jpg'</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>controller<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>isTakingPicture<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">// A capture is already pending, do nothing.</span>\n      <span class=\"token keyword\">return</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">await</span> controller<span class=\"token punctuation\">.</span><span class=\"token function\">takePicture</span><span class=\"token punctuation\">(</span>filePath<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">on</span> CameraException <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">_showCameraException</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">return</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">return</span> filePath<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_showCameraException</span><span class=\"token punctuation\">(</span>CameraException e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">logError</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">.</span>code<span class=\"token punctuation\">,</span> e<span class=\"token punctuation\">.</span>description<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token function\">showInSnackBar</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Error: ${e.code}\\n${e.description}'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div></li></ol> <blockquote><p>如果代码运行遇到困难，请直接查看<a href=\"https://pub.dev/packages/camera\" target=\"_blank\" rel=\"noopener noreferrer\">camera官方文档<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>。</p></blockquote> <h2 id=\"_12-6-2-platformview-示例-webview\"><a href=\"#_12-6-2-platformview-示例-webview\" class=\"header-anchor\">#</a> 12.6.2 PlatformView （示例：WebView）</h2> <p>如果我们在开发过程中需要使用一个原生组件，但这个原生组件在Flutter中很难实现时怎么办（如webview）？这时一个简单的方法就是将需要使用原生组件的页面全部用原生实现，在flutter中需要打开该页面时通过消息通道打开这个原生的页面。但是这种方法有一个最大的缺点，就是原生组件很难和Flutter组件进行组合。</p> <p>在 Flutter 1.0版本中，Flutter SDK中新增了<code>AndroidView</code>和<code>UIKitView</code> 两个组件，这两个组件的主要功能就是将原生的Android组件和iOS组件嵌入到Flutter的组件树中，这个功能是非常重要的，尤其是对一些实现非常复杂的组件，比如webview，这些组件原生已经有了，如果Flutter中要用，重新实现的话成本将非常高，所以如果有一种机制能让Flutter共享原生组件，这将会非常有用，也正因如此，Flutter才提供了这两个组件。</p> <p>由于<code>AndroidView</code>和<code>UIKitView</code> 是和具体平台相关的，所以称它们为PlatformView。需要说明的是将来Flutter支持的平台可能会增多，则相应的PlatformView也将会变多。那么如何使用Platform View呢？我们以Flutter官方提供的<a href=\"https://github.com/flutter/plugins/tree/master/packages/webview_flutter\" target=\"_blank\" rel=\"noopener noreferrer\">webview_flutter插件<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>为例：</p> <blockquote><p>注意，在本书写作之时，webview_flutter仍处于预览阶段，如您想在项目中使用它，请查看一下webview_flutter插件最新版本及动态。</p></blockquote> <ol><li><p>原生代码中注册要被Flutter嵌入的组件工厂，如webview_flutter插件中Android端注册webview插件代码：</p> <div class=\"language-java extra-class\"><pre class=\"language-java\"><code><span class=\"token keyword\">public</span> <span class=\"token keyword\">static</span> <span class=\"token keyword\">void</span> <span class=\"token function\">registerWith</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">Registrar</span> registrar<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n   registrar<span class=\"token punctuation\">.</span><span class=\"token function\">platformViewRegistry</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">registerViewFactory</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;webview&quot;</span><span class=\"token punctuation\">,</span> \n   <span class=\"token class-name\">WebViewFactory</span><span class=\"token punctuation\">(</span>registrar<span class=\"token punctuation\">.</span><span class=\"token function\">messenger</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p><code>WebViewFactory</code>的具体实现请参考webview_flutter插件的实现源码，在此不再赘述。</p></li> <li><p>在Flutter中使用；打开Flutter中文社区首页。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">PlatformViewRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">WebView</span><span class=\"token punctuation\">(</span>\n      initialUrl<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;https://flutterchina.club&quot;</span><span class=\"token punctuation\">,</span>\n      javascriptMode<span class=\"token punctuation\">:</span> JavascriptMode<span class=\"token punctuation\">.</span>unrestricted<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行效果如图12-5所示：</p> <p><img src=\"data:image/jpeg;base64,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\" alt=\"12-5\"></p></li></ol> <p>注意，使用PlatformView的开销是非常大的，因此，如果一个原生组件用Flutter实现的难度不大时，我们应该首选Flutter实现。</p> <p>另外，PlatformView的相关功能在作者写作时还处于预览阶段，可能还会发生变化，因此，读者如果需要在项目中使用的话，应查看一下最新的文档。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter12/ios_implement.html\" class=\"prev\">\n        12.5 插件开发：iOS端API实现\n      </a></span> <span class=\"next\"><a href=\"/chapter13/multi_languages_support.html\">\n        13.1 让App支持多语言\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/62.b51637d5.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter13/faq.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>13.4 国际化常见问题 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/144.2ce6c614.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable open\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" aria-current=\"page\" class=\"active sidebar-link\">13.4 国际化常见问题</a><ul class=\"sidebar-sub-headers\"></ul></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_13-4-国际化常见问题\"><a href=\"#_13-4-国际化常见问题\" class=\"header-anchor\">#</a> 13.4 国际化常见问题</h1> <p>本节主要解答一下在国际化中常见的问题。</p> <h3 id=\"默认语言区域不对\"><a href=\"#默认语言区域不对\" class=\"header-anchor\">#</a> 默认语言区域不对</h3> <p>在一些非大陆行货渠道买的一些Android和iOS设备，会出现默认的Locale不是中文简体的情况。这属于正常现象，但是为了防止设备获取的Locale与实际的地区不一致，所有的支持多语言的APP都必须提供一个手动选择语言的入口。</p> <h3 id=\"如何对应用标题进行国际化\"><a href=\"#如何对应用标题进行国际化\" class=\"header-anchor\">#</a> 如何对应用标题进行国际化</h3> <p><code>MaterialApp</code>有一个<code>title</code>属性，用于指定APP的标题。在Android系统中，APP的标题会出现在任务管理器中。所以也需要对<code>title</code>进行国际化。但是问题是很多国际化的配置都是在<code>MaterialApp</code>上设置的，我们无法在构建<code>MaterialApp</code>时通过<code>Localizations.of</code>来获取本地化资源，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">MaterialApp</span><span class=\"token punctuation\">(</span>\n  title<span class=\"token punctuation\">:</span> DemoLocalizations<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>title<span class=\"token punctuation\">,</span> <span class=\"token comment\">//不能正常工作！</span>\n  localizationsDelegates<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>\n    <span class=\"token comment\">// 本地化的代理类</span>\n    GlobalMaterialLocalizations<span class=\"token punctuation\">.</span>delegate<span class=\"token punctuation\">,</span>\n    GlobalWidgetsLocalizations<span class=\"token punctuation\">.</span>delegate<span class=\"token punctuation\">,</span>\n    <span class=\"token function\">DemoLocalizationsDelegate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// 设置Delegate</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>上面代码运行后，<code>DemoLocalizations.of(context).title</code> 是会报错的，原因是<code>Localizations.of</code>会从当前的context沿着widget树向顶部查找<code>DemoLocalizations</code>，但是我们在<code>MaterialApp</code>中设置完<code>DemoLocalizationsDelegate</code>后，实际上<code>DemoLocalizations</code>是在当前context的子树中的，所以<code>DemoLocalizations.of(context)</code>会返回null，报错。那么我们该如何处理这种情况呢？其实很简单，我们只需要设置一个<code>onGenerateTitle</code>回调即可：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">MaterialApp</span><span class=\"token punctuation\">(</span>\n  onGenerateTitle<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 此时context在Localizations的子树中</span>\n    <span class=\"token keyword\">return</span> DemoLocalizations<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>title<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  localizationsDelegates<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>\n    <span class=\"token function\">DemoLocalizationsDelegate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><h3 id=\"如何为英语系的国家指定同一个locale\"><a href=\"#如何为英语系的国家指定同一个locale\" class=\"header-anchor\">#</a> 如何为英语系的国家指定同一个locale</h3> <p>英语系的国家非常多，如美国、英国、澳大利亚等，这些英语系国家虽然说的都是英语，但也会有一些区别。如果我们的APP只想提供一种英语（如美国英语）供所有英语系国家使用，我们可以在前面介绍的<code>localeListResolutionCallback</code>中来做兼容：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>localeListResolutionCallback<span class=\"token punctuation\">:</span>\n    <span class=\"token punctuation\">(</span>List<span class=\"token operator\">&lt;</span>Locale<span class=\"token operator\">&gt;</span> locales<span class=\"token punctuation\">,</span> Iterable<span class=\"token operator\">&lt;</span>Locale<span class=\"token operator\">&gt;</span> supportedLocales<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// 判断当前locale是否为英语系国家，如果是直接返回Locale('en', 'US')     </span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter13/intl.html\" class=\"prev\">\n        使用Intl包\n      </a></span> <span class=\"next\"><a href=\"/chapter14/flutter_ui_system.html\">\n        14.1 Flutter UI系统\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/144.2ce6c614.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter13/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>本章目录 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/145.8fe852a4.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h2 id=\"本章目录\"><a href=\"#本章目录\" class=\"header-anchor\">#</a> 本章目录</h2> <ul><li><a href=\"/chapter13/multi_languages_support.html\">13.1：让App支持多语言</a></li> <li><a href=\"/chapter13/locallization_implement.html\">13.2：实现Localizations</a></li> <li><a href=\"/chapter13/intl.html\">13.3：使用Intl包</a></li> <li><a href=\"/chapter13/faq.html\">13.4：国际化常见问题</a></li></ul></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/145.8fe852a4.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter13/intl.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>使用Intl包 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/146.86bb1e0f.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable open\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" aria-current=\"page\" class=\"active sidebar-link\">使用Intl包</a><ul class=\"sidebar-sub-headers\"></ul></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"使用intl包\"><a href=\"#使用intl包\" class=\"header-anchor\">#</a> 使用Intl包</h1> <p>使用<a href=\"https://pub.dartlang.org/packages/intl\" target=\"_blank\" rel=\"noopener noreferrer\">Intl<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>包我们不仅可以非常轻松的实现国际化，而且也可以将字符串文本分离成单独的文件，方便开发人员和翻译人员分工协作。为了使用<a href=\"https://pub.dartlang.org/packages/intl\" target=\"_blank\" rel=\"noopener noreferrer\">Intl<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>包我们需要添加两个依赖：</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code><span class=\"token key atrule\">dependencies</span><span class=\"token punctuation\">:</span>\n  <span class=\"token comment\">#...省略无关项</span>\n  <span class=\"token key atrule\">intl</span><span class=\"token punctuation\">:</span> ^0.15.7 \n<span class=\"token key atrule\">dev_dependencies</span><span class=\"token punctuation\">:</span>\n   <span class=\"token comment\">#...省略无关项</span>\n  <span class=\"token key atrule\">intl_translation</span><span class=\"token punctuation\">:</span> ^0.17.2  \n</code></pre></div><p><a href=\"https://pub.dartlang.org/packages/intl_translation\" target=\"_blank\" rel=\"noopener noreferrer\">intl_translation<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 包主要包含了一些工具，它在开发阶段主要主要的作用是从代码中提取要国际化的字符串到单独的arb文件和根据arb文件生成对应语言的dart代码，而intl包主要是引用和加载intl_translation生成后的dart代码。下面我们将一步步来说明如何使用：</p> <h3 id=\"第一步-创建必要目录\"><a href=\"#第一步-创建必要目录\" class=\"header-anchor\">#</a> 第一步：创建必要目录</h3> <p>首先，在项目根目录下创建一个l10n-arb目录，该目录保存我们接下来通过intl_translation命令生成的arb文件。一个简单的arb文件内容如下：</p> <div class=\"language-json extra-class\"><pre class=\"language-json\"><code><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;@@last_modified&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;2018-12-10T15:46:20.897228&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;@@locale&quot;</span><span class=\"token operator\">:</span><span class=\"token string\">&quot;zh_CH&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;title&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;Flutter应用&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;@title&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">&quot;description&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;Title for the Demo application&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;type&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;text&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;placeholders&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们根据&quot;@@locale&quot;字段可以看出这个arb对应的是中文简体的翻译，里面的<code>title</code>字段对应的正是我们应用标题的中文简体翻译。<code>@title</code>字段是对<code>title</code>的一些描述信息。</p> <p>接下来，我们在lib目录下创建一个l10n的目录，该目录用于保存从arb文件生成的dart代码文件。</p> <h3 id=\"第二步-实现localizations和delegate类\"><a href=\"#第二步-实现localizations和delegate类\" class=\"header-anchor\">#</a> 第二步：实现Localizations和Delegate类</h3> <p>和上一节中的步骤类似，我们仍然要实现<code>Localizations</code>和Delegate类，不同的是，现在我们在实现时要使用intl包的一些方法（有些是动态生成的）。</p> <p>下面我们在<code>lib/l10n</code>目录下新建一个“localization_intl.dart”的文件，文件内容如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:intl/intl.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'messages_all.dart'</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//1</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">DemoLocalizations</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">static</span> Future<span class=\"token operator\">&lt;</span>DemoLocalizations<span class=\"token operator\">&gt;</span> <span class=\"token function\">load</span><span class=\"token punctuation\">(</span>Locale locale<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">final</span> String name <span class=\"token operator\">=</span> locale<span class=\"token punctuation\">.</span>countryCode<span class=\"token punctuation\">.</span>isEmpty <span class=\"token operator\">?</span> locale<span class=\"token punctuation\">.</span>languageCode <span class=\"token punctuation\">:</span> locale<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">final</span> String localeName <span class=\"token operator\">=</span> Intl<span class=\"token punctuation\">.</span><span class=\"token function\">canonicalizedLocale</span><span class=\"token punctuation\">(</span>name<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//2</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">initializeMessages</span><span class=\"token punctuation\">(</span>localeName<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>b<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      Intl<span class=\"token punctuation\">.</span>defaultLocale <span class=\"token operator\">=</span> localeName<span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">DemoLocalizations</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">static</span> DemoLocalizations <span class=\"token function\">of</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> Localizations<span class=\"token punctuation\">.</span>of<span class=\"token operator\">&lt;</span>DemoLocalizations<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> DemoLocalizations<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  String <span class=\"token keyword\">get</span> title <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> Intl<span class=\"token punctuation\">.</span><span class=\"token function\">message</span><span class=\"token punctuation\">(</span>\n      <span class=\"token string\">'Flutter APP'</span><span class=\"token punctuation\">,</span>\n      name<span class=\"token punctuation\">:</span> <span class=\"token string\">'title'</span><span class=\"token punctuation\">,</span>\n      desc<span class=\"token punctuation\">:</span> <span class=\"token string\">'Title for the Demo application'</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">//Locale代理类</span>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">DemoLocalizationsDelegate</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">LocalizationsDelegate</span><span class=\"token operator\">&lt;</span>DemoLocalizations<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function\">DemoLocalizationsDelegate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">//是否支持某个Local</span>\n  <span class=\"token metadata symbol\">@override</span>\n  bool <span class=\"token function\">isSupported</span><span class=\"token punctuation\">(</span>Locale locale<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">[</span><span class=\"token string\">'en'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'zh'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span><span class=\"token function\">contains</span><span class=\"token punctuation\">(</span>locale<span class=\"token punctuation\">.</span>languageCode<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// Flutter会调用此类加载相应的Locale资源类</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Future<span class=\"token operator\">&lt;</span>DemoLocalizations<span class=\"token operator\">&gt;</span> <span class=\"token function\">load</span><span class=\"token punctuation\">(</span>Locale locale<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//3</span>\n    <span class=\"token keyword\">return</span>  DemoLocalizations<span class=\"token punctuation\">.</span><span class=\"token function\">load</span><span class=\"token punctuation\">(</span>locale<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">// 当Localizations Widget重新build时，是否调用load重新加载Locale资源.</span>\n  <span class=\"token metadata symbol\">@override</span>\n  bool <span class=\"token function\">shouldReload</span><span class=\"token punctuation\">(</span>DemoLocalizationsDelegate old<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>注意：</p> <ul><li>注释1的&quot;messages_all.dart&quot;文件是通过<a href=\"https://pub.dartlang.org/packages/intl_translation\" target=\"_blank\" rel=\"noopener noreferrer\">intl_translation<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>工具从arb文件生成的代码，所以在第一次运行生成命令之前，此文件不存在。注释2处的<code>initializeMessages()</code>方法和&quot;messages_all.dart&quot;文件一样，是同时生成的。</li> <li>注释3处和上一节示例代码不同，这里我们直接调用<code>DemoLocalizations.load()</code>即可。</li></ul> <h3 id=\"第三步-添加需要国际化的属性\"><a href=\"#第三步-添加需要国际化的属性\" class=\"header-anchor\">#</a> 第三步：添加需要国际化的属性</h3> <p>现在我们可以在DemoLocalizations类中添加需要国际化的属性或方法，如上面示例代码中的<code>title</code>属性，这时我们就要用到Intl库提供的一些方法，这些方法可以帮我们轻松实现不同语言的一些语法特性，如复数语境，举个例子，比如我们有一个电子邮件列表页，我们需要在顶部显示未读邮件的数量，在未读数量不同事，我们展示的文本可能会不同：</p> <table><thead><tr><th>未读邮件数</th> <th>提示语</th></tr></thead> <tbody><tr><td>0</td> <td>There are no emails left</td></tr> <tr><td>1</td> <td>There is 1 email left</td></tr> <tr><td>n(n&gt;1)</td> <td>There are n emails left</td></tr></tbody></table> <p>我们可以通过<code>Intl.plural(...)</code>来实现：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">remainingEmailsMessage</span><span class=\"token punctuation\">(</span>int howMany<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Intl<span class=\"token punctuation\">.</span><span class=\"token function\">plural</span><span class=\"token punctuation\">(</span>howMany<span class=\"token punctuation\">,</span>\n    zero<span class=\"token punctuation\">:</span> <span class=\"token string\">'There are no emails left'</span><span class=\"token punctuation\">,</span>\n    one<span class=\"token punctuation\">:</span> <span class=\"token string\">'There is $howMany email left'</span><span class=\"token punctuation\">,</span>\n    other<span class=\"token punctuation\">:</span> <span class=\"token string\">'There are $howMany emails left'</span><span class=\"token punctuation\">,</span>\n    name<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;remainingEmailsMessage&quot;</span><span class=\"token punctuation\">,</span>\n    args<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>howMany<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    desc<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;How many emails remain after archiving.&quot;</span><span class=\"token punctuation\">,</span>\n    examples<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span><span class=\"token string\">'howMany'</span><span class=\"token punctuation\">:</span> <span class=\"token number\">42</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'userName'</span><span class=\"token punctuation\">:</span> <span class=\"token string\">'Fred'</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>可以看到通过<code>Intl.plural</code>方法可以在<code>howMany</code>值不同时输出不同的提示信息。</p> <p><a href=\"https://pub.dartlang.org/packages/intl\" target=\"_blank\" rel=\"noopener noreferrer\">Intl<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>包还有一些其他的方法，读者可以自行查看其文档，本书不在赘述。</p> <h3 id=\"第四步-生成arb文件\"><a href=\"#第四步-生成arb文件\" class=\"header-anchor\">#</a> 第四步：生成arb文件</h3> <p>现在我们可以通<a href=\"https://pub.dartlang.org/packages/intl_translation\" target=\"_blank\" rel=\"noopener noreferrer\">intl_translation<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>包的工具来提取代码中的字符串到一个arb文件，运行如下命名：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>flutter pub pub run intl_translation:extract_to_arb --output-dir<span class=\"token operator\">=</span>l10n-arb <span class=\"token punctuation\">\\</span> lib/l10n/localization_intl.dart\n</code></pre></div><p>运行此命令后，会将我们之前通过Intl API标识的属性和字符串提取到“l10n-arb/intl_messages.arb”文件中，我们看看其内容：</p> <div class=\"language-json extra-class\"><pre class=\"language-json\"><code><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;@@last_modified&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;2018-12-10T17:37:28.505088&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;title&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;Flutter APP&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;@title&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">&quot;description&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;Title for the Demo application&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;type&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;text&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;placeholders&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;remainingEmailsMessage&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;{howMany,plural, =0{There are no emails left}=1{There is {howMany} email left}other{There are {howMany} emails left}}&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;@remainingEmailsMessage&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">&quot;description&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;How many emails remain after archiving.&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;type&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;text&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;placeholders&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token property\">&quot;howMany&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token property\">&quot;example&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">42</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>这个是默认的Locale资源文件，如果我们现在要支持中文简体，只需要在该文件同级目录创建一个&quot;intl_zh_CN.arb&quot;文件，然后将&quot;intl_messages.arb&quot;的内容拷贝到&quot;intl_zh_CN.arb&quot;文件，接下来将英文翻译为中文即可，翻译后的&quot;intl_zh_CN.arb&quot;文件内容如下：</p> <div class=\"language-json extra-class\"><pre class=\"language-json\"><code><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;@@last_modified&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;2018-12-10T15:46:20.897228&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;@@locale&quot;</span><span class=\"token operator\">:</span><span class=\"token string\">&quot;zh_CN&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;title&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;Flutter应用&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;@title&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">&quot;description&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;Title for the Demo application&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;type&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;text&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;placeholders&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;remainingEmailsMessage&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;{howMany,plural, =0{没有未读邮件}=1{有{howMany}封未读邮件}other{有{howMany}封未读邮件}}&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;@remainingEmailsMessage&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">&quot;description&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;How many emails remain after archiving.&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;type&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;text&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;placeholders&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token property\">&quot;howMany&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token property\">&quot;example&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">42</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们必须要翻译<code>title</code>和<code>remainingEmailsMessage</code>字段，<code>description</code>是该字段的说明，通常给翻译人员看，代码中不会用到。</p> <p>有两点需要说明：</p> <ol><li>如果某个特定的arb中缺失某个属性，那么应用将会加载默认的arb文件(intl_messages.arb)中的相应属性，这是Intl的托底策略。</li> <li>每次运行提取命令时，intl_messages.arb都会根据代码重新生成，但其他arb文件不会，所以当要添加新的字段或方法时，其他arb文件是增量的，不用担心会覆盖。</li> <li>arb文件是标准的，其格式规范可以自行了解。通常会将arb文件交给翻译人员，当他们完成翻译后，我们再通过下面的步骤根据arb文件生成最终的dart代码。</li></ol> <h3 id=\"第五步-生成dart代码\"><a href=\"#第五步-生成dart代码\" class=\"header-anchor\">#</a> 第五步：生成dart代码</h3> <p>最后一步就是根据arb生成dart文件：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>flutter pub pub run intl_translation:generate_from_arb --output-dir<span class=\"token operator\">=</span>lib/l10n --no-use-deferred-loading lib/l10n/localization_intl.dart l10n-arb/intl_*.arb\n</code></pre></div><p>这句命令在首次运行时会在&quot;lib/l10n&quot;目录下生成多个文件，对应多种Locale，这些代码便是最终要使用的dart代码。</p> <h3 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h3> <p>至此，我们将使用<a href=\"https://pub.dartlang.org/packages/intl\" target=\"_blank\" rel=\"noopener noreferrer\">Intl<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>包对APP进行国际化的流程介绍完了，我们可以发现，其中第一步和第二步只在第一次需要，而我们开发时的主要的工作都是在第三步。由于最后两步在第三步完成后每次也都需要，所以我们可以将最后两步放在一个shell脚本里，当我们完成第三步或完成arb文件翻译后只需要分别执行该脚本即可。我们在根目录下创建一个intl.sh的脚本，内容为：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>flutter pub pub run intl_translation:extract_to_arb --output-dir<span class=\"token operator\">=</span>l10n-arb lib/l10n/localization_intl.dart\nflutter pub pub run intl_translation:generate_from_arb --output-dir<span class=\"token operator\">=</span>lib/l10n --no-use-deferred-loading lib/l10n/localization_intl.dart l10n-arb/intl_*.arb\n</code></pre></div><p>然后授予执行权限：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code><span class=\"token function\">chmod</span> +x intl.sh\n</code></pre></div><p>执行intl.sh</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>./intl.sh\n</code></pre></div></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter13/locallization_implement.html\" class=\"prev\">\n        13.2 实现Localizations\n      </a></span> <span class=\"next\"><a href=\"/chapter13/faq.html\">\n        13.4 国际化常见问题\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/146.86bb1e0f.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter13/locallization_implement.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>13.2 实现Localizations | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/147.e9009fb7.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable open\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" aria-current=\"page\" class=\"active sidebar-link\">13.2 实现Localizations</a><ul class=\"sidebar-sub-headers\"></ul></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_13-2-实现localizations\"><a href=\"#_13-2-实现localizations\" class=\"header-anchor\">#</a> 13.2 实现Localizations</h1> <p>前面讲了Material组件库如何支持国际化，本节我们将介绍一下我们自己的UI中如何支持多语言。根据上节所述，我们需要实现两个类：一个<code>Delegate</code>类一个<code>Localizations</code>类，下面我们通过一个实例说明。</p> <h3 id=\"实现localizations类\"><a href=\"#实现localizations类\" class=\"header-anchor\">#</a> 实现Localizations类</h3> <p>我们已经知道<code>Localizations</code>类中主要实现提供了本地化值，如文本：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//Locale资源类</span>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">DemoLocalizations</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">DemoLocalizations</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>isZh<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">//是否为中文</span>\n  bool isZh <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">//为了使用方便，我们定义一个静态方法</span>\n  <span class=\"token keyword\">static</span> DemoLocalizations <span class=\"token function\">of</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> Localizations<span class=\"token punctuation\">.</span>of<span class=\"token operator\">&lt;</span>DemoLocalizations<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> DemoLocalizations<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token comment\">//Locale相关值，title为应用标题</span>\n  String <span class=\"token keyword\">get</span> title <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> isZh <span class=\"token operator\">?</span> <span class=\"token string\">&quot;Flutter应用&quot;</span> <span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;Flutter APP&quot;</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token comment\">//... 其它的值  </span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p><code>DemoLocalizations</code>中会根据当前的语言来返回不同的文本，如<code>title</code>，我们可以将所有需要支持多语言的文本都在此类中定义。<code>DemoLocalizations</code>的实例将会在Delegate类的<code>load</code>方法中创建。</p> <h3 id=\"实现delegate类\"><a href=\"#实现delegate类\" class=\"header-anchor\">#</a> 实现Delegate类</h3> <p>Delegate类的职责是在Locale改变时加载新的Locale资源，所以它有一个<code>load</code>方法，Delegate类需要继承自<code>LocalizationsDelegate</code>类，实现相应的接口，示例如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//Locale代理类</span>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">DemoLocalizationsDelegate</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">LocalizationsDelegate</span><span class=\"token operator\">&lt;</span>DemoLocalizations<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function\">DemoLocalizationsDelegate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">//是否支持某个Local</span>\n  <span class=\"token metadata symbol\">@override</span>\n  bool <span class=\"token function\">isSupported</span><span class=\"token punctuation\">(</span>Locale locale<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">[</span><span class=\"token string\">'en'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'zh'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span><span class=\"token function\">contains</span><span class=\"token punctuation\">(</span>locale<span class=\"token punctuation\">.</span>languageCode<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// Flutter会调用此类加载相应的Locale资源类</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Future<span class=\"token operator\">&lt;</span>DemoLocalizations<span class=\"token operator\">&gt;</span> <span class=\"token function\">load</span><span class=\"token punctuation\">(</span>Locale locale<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;$locale&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> SynchronousFuture<span class=\"token operator\">&lt;</span>DemoLocalizations<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n        <span class=\"token function\">DemoLocalizations</span><span class=\"token punctuation\">(</span>locale<span class=\"token punctuation\">.</span>languageCode <span class=\"token operator\">==</span> <span class=\"token string\">&quot;zh&quot;</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  bool <span class=\"token function\">shouldReload</span><span class=\"token punctuation\">(</span>DemoLocalizationsDelegate old<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p><code>shouldReload</code>的返回值决定当Localizations组件重新build时，是否调用<code>load</code>方法重新加载Locale资源。一般情况下，Locale资源只应该在Locale切换时加载一次，不需要每次在<code>Localizations</code>重新build时都加载，所以返回<code>false</code>即可。可能有些人会担心返回<code>false</code>的话在APP启动后用户再改变系统语言时<code>load</code>方法将不会被调用，所以Locale资源将不会被加载。事实上，每当Locale改变时Flutter都会再调用<code>load</code>方法加载新的Locale，无论<code>shouldReload</code>返回<code>true</code>还是<code>false</code>。</p> <h3 id=\"最后一步-添加多语言支持\"><a href=\"#最后一步-添加多语言支持\" class=\"header-anchor\">#</a> 最后一步：添加多语言支持</h3> <p>和上一节中介绍的相同，我们现在需要先注册<code>DemoLocalizationsDelegate</code>类，然后再通过<code>DemoLocalizations.of(context)</code>来动态获取当前Locale文本。</p> <p>只需要在MaterialApp或WidgetsApp的<code>localizationsDelegates</code>列表中添加我们的Delegate实例即可完成注册：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>localizationsDelegates<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>\n <span class=\"token comment\">// 本地化的代理类</span>\n GlobalMaterialLocalizations<span class=\"token punctuation\">.</span>delegate<span class=\"token punctuation\">,</span>\n GlobalWidgetsLocalizations<span class=\"token punctuation\">.</span>delegate<span class=\"token punctuation\">,</span>\n <span class=\"token comment\">// 注册我们的Delegate</span>\n <span class=\"token function\">DemoLocalizationsDelegate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n</code></pre></div><p>接下来我们可以在Widget中使用Locale值：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">return</span> <span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n  appBar<span class=\"token punctuation\">:</span> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>\n    <span class=\"token comment\">//使用Locale title  </span>\n    title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>DemoLocalizations<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>title<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n ） \n</code></pre></div><p>这样，当在美国英语和中文简体之间切换系统语言时，APP的标题将会分别为“Flutter APP”和“Flutter应用”。</p> <h3 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h3> <p>本节我们通过一个简单的示例说明了Flutter应用国际化的基本过程及原理。但是上面的实例还有一个严重的不足就是我们需要在DemoLocalizations类中获取<code>title</code>时手动的判断当前语言Locale，然后返回合适的文本。试想一下，当我们要支持的语言不是两种而是8种甚至20几种时，如果为每个文本属性都要分别去判断到底是哪种Locale从而获取相应语言的文本将会是一件非常复杂的事。还有，通常情况下翻译人员并不是开发人员，能不能像i18n或l10n标准那样可以将翻译单独保存为一个arb文件交由翻译人员去翻译，翻译好之后开发人员再通过工具将arb文件转为代码。答案是肯定的！我们将在下一节介绍如何通过Dart intl包来实现这些。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter13/multi_languages_support.html\" class=\"prev\">\n        13.1 让App支持多语言\n      </a></span> <span class=\"next\"><a href=\"/chapter13/intl.html\">\n        使用Intl包\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/147.e9009fb7.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter13/multi_languages_support.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>13.1 让App支持多语言 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/90.fff47bcf.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable open\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" aria-current=\"page\" class=\"active sidebar-link\">13.1 让App支持多语言</a><ul class=\"sidebar-sub-headers\"></ul></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_13-1-让app支持多语言\"><a href=\"#_13-1-让app支持多语言\" class=\"header-anchor\">#</a> 13.1 让App支持多语言</h1> <p>如果我们的应用要支持多种语言，那么我们需要“国际化”它。这意味着我们在开发时需要为应用程序支持的每种语言环境设置“本地化”的一些值，如文本和布局。Flutter SDK已经提供了一些组件和类来帮助我们实现国际化，下面我们来介绍一下Flutter中实现国际化的步骤。</p> <p>接下来我们以<code>MaterialApp</code>类为入口的应用来说明如何支持国际化。</p> <blockquote><p>大多数应用程序都是通过<code>MaterialApp</code>为入口，但根据低级别的<code>WidgetsApp</code>类为入口编写的应用程序也可以使用相同的类和逻辑进行国际化。<code>MaterialApp</code>实际上也是<code>WidgetsApp</code>的一个包装。</p></blockquote> <p>注意，”本地化的值和资源“是指我们针对不同语言准备的不同资源，这些资源一般是指文案（字符串），当然也会有一些其他的资源会根据不同语言地区而不同，比如我们需要显示一个APP上架地的国旗图片，那么不同Locale区域我们就需要提供不同的的国旗图片。</p> <h3 id=\"支持国际化\"><a href=\"#支持国际化\" class=\"header-anchor\">#</a> 支持国际化</h3> <p>默认情况下，Flutter SDK中的组件仅提供美国英语本地化资源（主要是文本）。要添加对其他语言的支持，应用程序须添加一个名为“flutter_localizations”的包依赖，然后还需要在<code>MaterialApp</code>中进行一些配置。 要使用<code>flutter_localizations</code>包，首先需要添加依赖到<code>pubspec.yaml</code>文件中：</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code><span class=\"token key atrule\">dependencies</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">flutter</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">sdk</span><span class=\"token punctuation\">:</span> flutter\n  <span class=\"token key atrule\">flutter_localizations</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">sdk</span><span class=\"token punctuation\">:</span> flutter\n</code></pre></div><p>接下来，下载<code>flutter_localizations</code>库，然后指定<code>MaterialApp</code>的<code>localizationsDelegates</code>和<code>supportedLocales</code>：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter_localizations/flutter_localizations.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">new</span> <span class=\"token class-name\">MaterialApp</span><span class=\"token punctuation\">(</span>\n localizationsDelegates<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>\n   <span class=\"token comment\">// 本地化的代理类</span>\n   GlobalMaterialLocalizations<span class=\"token punctuation\">.</span>delegate<span class=\"token punctuation\">,</span>\n   GlobalWidgetsLocalizations<span class=\"token punctuation\">.</span>delegate<span class=\"token punctuation\">,</span>\n <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n supportedLocales<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>\n    <span class=\"token keyword\">const</span> <span class=\"token function\">Locale</span><span class=\"token punctuation\">(</span><span class=\"token string\">'en'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'US'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 美国英语</span>\n    <span class=\"token keyword\">const</span> <span class=\"token function\">Locale</span><span class=\"token punctuation\">(</span><span class=\"token string\">'zh'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'CN'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 中文简体</span>\n    <span class=\"token comment\">//其它Locales</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token comment\">// ...</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><blockquote><p>与<code>MaterialApp</code>类为入口的应用不同, 对基于<code>WidgetsApp</code>类为入口的应用程序进行国际化时,不需要<code>GlobalMaterialLocalizations.delegate</code>。</p></blockquote> <p><code>localizationsDelegates</code>列表中的元素是生成本地化值集合的工厂。<code>GlobalMaterialLocalizations.delegate</code> 为Material 组件库提供的本地化的字符串和其他值，它可以使Material 组件支持多语言。 <code>GlobalWidgetsLocalizations.delegate</code>定义组件默认的文本方向，从左到右或从右到左，这是因为有些语言的阅读习惯并不是从左到右，比如如阿拉伯语就是从右向左的。</p> <p><code>supportedLocales</code>也接收一个Locale数组，表示我们的应用支持的语言列表，在本例中我们的应用只支持美国英语和中文简体两种语言。</p> <h3 id=\"获取当前区域locale\"><a href=\"#获取当前区域locale\" class=\"header-anchor\">#</a> 获取当前区域Locale</h3> <p><a href=\"https://docs.flutter.io/flutter/dart-ui/Locale-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>Locale</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>类是用来标识用户的语言环境的，它包括语言和国家两个标志如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">const</span> <span class=\"token function\">Locale</span><span class=\"token punctuation\">(</span><span class=\"token string\">'zh'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'CN'</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// 中文简体</span>\n</code></pre></div><p>我们始终可以通过以下方式来获取应用的当前区域Locale：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Locale myLocale <span class=\"token operator\">=</span> Localizations<span class=\"token punctuation\">.</span><span class=\"token function\">localeOf</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p><a href=\"https://docs.flutter.io/flutter/widgets/Localizations-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>Localizations</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 组件一般位于widget树中其它业务组件的顶部，它的作用是定义区域Locale以及设置子树依赖的本地化资源。 如果系统的语言环境发生变化，<a href=\"https://docs.flutter.io/flutter/widgets/WidgetsApp-class.html\" target=\"_blank\" rel=\"noopener noreferrer\">WidgetsApp<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>将创建一个新的Localizations 组件并重建它，这样子树中通过<code>Localizations.localeOf(context)</code> 获取的Locale就会更新。</p> <h3 id=\"监听系统语言切换\"><a href=\"#监听系统语言切换\" class=\"header-anchor\">#</a> 监听系统语言切换</h3> <p>当我们更改系统语言设置时，APP中的Localizations组件会重新构建，<code>Localizations.localeOf(context)</code> 获取的Locale就会更新，最终界面会重新build达到切换语言的效果。但是这个过程是隐式完成的，我们并没有主动去监听系统语言切换，但是有时我们需要在系统语言发生改变时做一些事，比如系统语言切换为一种我们APP不支持的语言时，我们需要设置一个默认的语言，这时我们就需要监听locale改变事件。</p> <p>我们可以通过<code>localeResolutionCallback</code>或<code>localeListResolutionCallback</code>回调来监听locale改变的事件，我们先看看<code>localeResolutionCallback</code>的回调函数签名：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Locale <span class=\"token keyword\">Function</span><span class=\"token punctuation\">(</span>Locale locale<span class=\"token punctuation\">,</span> Iterable<span class=\"token operator\">&lt;</span>Locale<span class=\"token operator\">&gt;</span> supportedLocales<span class=\"token punctuation\">)</span>\n</code></pre></div><ul><li><p>参数<code>locale</code>的值为当前的当前的系统语言设置，当应用启动时或用户动态改变系统语言设置时此locale即为系统的当前locale。当开发者手动指定APP的locale时，那么此locale参数代表开发者指定的locale，此时将忽略系统locale如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">MaterialApp</span><span class=\"token punctuation\">(</span>\n <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n locale<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Locale</span><span class=\"token punctuation\">(</span><span class=\"token string\">'en'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'US'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//手动指定locale</span>\n <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>上面的例子中手动指定了应用locale为美国英语，指定后即使设备当前语言是中文简体，应用中的locale也依然是美国英语。如果<code>locale</code>为<code>null</code>，则表示Flutter未能获取到设备的Locale信息，所以我们在使用<code>locale</code>之前一定要先判空。</p></li> <li><p><code>supportedLocales</code> 为当前应用支持的locale列表，是开发者在MaterialApp中通过<code>supportedLocales</code>属性注册的。</p></li> <li><p>返回值是一个<code>Locale</code>，此<code>Locale</code>为Flutter APP最终使用的<code>Locale</code>。通常在不支持的语言区域时返回一个默认的<code>Locale</code>。</p></li></ul> <p><code>localeListResolutionCallback</code>和<code>localeResolutionCallback</code>唯一的不同就在第一个参数类型，前者接收的是一个<code>Locale</code>列表，而后者接收的是单个<code>Locale</code>。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Locale <span class=\"token keyword\">Function</span><span class=\"token punctuation\">(</span>List<span class=\"token operator\">&lt;</span>Locale<span class=\"token operator\">&gt;</span> locales<span class=\"token punctuation\">,</span> Iterable<span class=\"token operator\">&lt;</span>Locale<span class=\"token operator\">&gt;</span> supportedLocales<span class=\"token punctuation\">)</span>\n</code></pre></div><p>在较新的Android系统中，用户可以设置一个语言列表，这样一来，支持多语言的应用就会得到这个列表，应用通常的处理方式就是按照列表的顺序依次尝试加载相应的Locale，如果某一种语言加载成功则会停止。图13-1是Android系统中设置语言列表的截图：</p> <p><img src=\"/assets/img/13-1.fcda4f48.jpeg\" alt=\"设置语言列表\"></p> <p>在Flutter中，应该优先使用<code>localeListResolutionCallback</code>，当然你不必担心Android系统的差异性，如果在低版本的Android系统中，Flutter会自动处理这种情况，这时Locale列表只会包含一项。</p> <h3 id=\"localization-组件\"><a href=\"#localization-组件\" class=\"header-anchor\">#</a> Localization 组件</h3> <p>Localizations组件用于加载和查找应用当前语言下的本地化值或资源。应用程序通过<a href=\"https://docs.flutter.io/flutter/widgets/Localizations/of.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>Localizations.of(context,type)</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>来引用这些对象。 如果设备的Locale区域设置发生更改，则Localizations 组件会自动加载新区域的Locale值，然后重新build使用（依赖）了它们的组件，之所以会这样，是因为<code>Localizations</code>内部使用了<a href=\"https://book.flutterchina.club/chapter7/inherited_widget.html\" target=\"_blank\" rel=\"noopener noreferrer\">InheritedWidget<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> ，我们在介绍该组件时讲过：当子组件的<code>build</code>函数引用了<code>InheritedWidget</code>时，会创建对<code>InheritedWidget</code>的隐式依赖关系。因此，当<code>InheritedWidget</code>发生更改时，即<code>Localizations</code>的Locale设置发生更改时，将重建所有依赖它的子组件。</p> <p>本地化值由<code>Localizations</code>的 <a href=\"https://docs.flutter.io/flutter/widgets/LocalizationsDelegate-class.html\" target=\"_blank\" rel=\"noopener noreferrer\">LocalizationsDelegates<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 列表加载 。 <strong>每个委托必须定义一个异步load() 方法</strong>，以生成封装了一系列本地化值的对象。通常这些对象为每个本地化值定义一个方法。</p> <p>在大型应用程序中，不同模块或Package可能会与自己的本地化值捆绑在一起。 这就是为什么要用<code>Localizations</code> 管理对象表的原因。 要使用由<code>LocalizationsDelegate</code>的<code>load</code>方法之一产生的对象，可以指定一个<code>BuildContext</code>和对象的类型来找到它。例如，Material 组件库的本地化字符串由<a href=\"https://docs.flutter.io/flutter/material/MaterialLocalizations-class.html\" target=\"_blank\" rel=\"noopener noreferrer\">MaterialLocalizations<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>类定义，此类的实例由<a href=\"https://docs.flutter.io/flutter/material/MaterialApp-class.html\" target=\"_blank\" rel=\"noopener noreferrer\">MaterialApp<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>类提供的<code>LocalizationDelegate</code>创建， 它们可以如下方式获取到：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Localizations<span class=\"token punctuation\">.</span>of<span class=\"token operator\">&lt;</span>MaterialLocalizations<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> MaterialLocalizations<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>这个特殊的<code>Localizations.of()</code>表达式会经常使用，所以MaterialLocalizations类提供了一个便捷方法：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">static</span> MaterialLocalizations <span class=\"token function\">of</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> Localizations<span class=\"token punctuation\">.</span>of<span class=\"token operator\">&lt;</span>MaterialLocalizations<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> MaterialLocalizations<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">// 可以直接调用便捷方法</span>\ntooltip<span class=\"token punctuation\">:</span> MaterialLocalizations<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>backButtonTooltip<span class=\"token punctuation\">,</span>\n</code></pre></div><h3 id=\"使用打包好的localizationsdelegates\"><a href=\"#使用打包好的localizationsdelegates\" class=\"header-anchor\">#</a> 使用打包好的LocalizationsDelegates</h3> <p>为了尽可能小而且简单，flutter软件包中仅提供美国英语值的<code>MaterialLocalizations</code>和<code>WidgetsLocalizations</code>接口的实现。 这些实现类分别称为<code>DefaultMaterialLocalizations</code>和<code>DefaultWidgetsLocalizations</code>。flutter_localizations 包包含<code>GlobalMaterialLocalizations</code>和<code>GlobalWidgetsLocalizations</code>的本地化接口的多语言实现， 国际化的应用程序必须按照本节开头说明的那样为这些类指定本地化Delegate。</p> <p>上述的<code>GlobalMaterialLocalizations</code>和<code>GlobalWidgetsLocalizations</code>只是Material组件库的本地化实现，如果我们要让自己的布局支持多语言，那么就需要实现在即的<code>Localizations</code>，我们将在下一节介绍其具体的实现方式。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter12/texture_platformview.html\" class=\"prev\">\n        12.6 Texture和PlatformView\n      </a></span> <span class=\"next\"><a href=\"/chapter13/locallization_implement.html\">\n        13.2 实现Localizations\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/90.fff47bcf.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter14/element_buildcontext.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>14.2 Element与BuildContext | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/40.31216541.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable open\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" aria-current=\"page\" class=\"active sidebar-link\">14.2 Element与BuildContext</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/chapter14/element_buildcontext.html#_14-2-1-element\" class=\"sidebar-link\">14.2.1 Element</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter14/element_buildcontext.html#_14-2-2-buildcontext\" class=\"sidebar-link\">14.2.2 BuildContext</a></li></ul></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_14-2-element与buildcontext\"><a href=\"#_14-2-element与buildcontext\" class=\"header-anchor\">#</a> 14.2 Element与BuildContext</h1> <h2 id=\"_14-2-1-element\"><a href=\"#_14-2-1-element\" class=\"header-anchor\">#</a> 14.2.1 Element</h2> <p>在“Widget简介”一节，我们介绍了Widget和Element的关系，我们知道最终的UI树其实是由一个个独立的Element节点构成。我们也说过组件最终的Layout、渲染都是通过<code>RenderObject</code>来完成的，从创建到渲染的大体流程是：根据Widget生成Element，然后创建相应的<code>RenderObject</code>并关联到<code>Element.renderObject</code>属性上，最后再通过<code>RenderObject</code>来完成布局排列和绘制。</p> <p>Element就是Widget在UI树具体位置的一个实例化对象，大多数Element只有唯一的<code>renderObject</code>，但还有一些Element会有多个子节点，如继承自<code>RenderObjectElement</code>的一些类，比如<code>MultiChildRenderObjectElement</code>。最终所有Element的RenderObject构成一棵树，我们称之为”Render Tree“即”渲染树“。总结一下，我们可以认为Flutter的UI系统包含三棵树：Widget树、Element树、渲染树。他们的依赖关系是：Element树根据Widget树生成，而渲染树又依赖于Element树，如图14-0所示。</p> <p><img src=\"/assets/img/14-0.bd5c2d9d.png\" alt=\"图14-0\"></p> <p>现在我们重点看一下Element，Element的生命周期如下：</p> <ol><li>Framework 调用<code>Widget.createElement</code> 创建一个Element实例，记为<code>element</code></li> <li>Framework 调用 <code>element.mount(parentElement,newSlot)</code> ，mount方法中首先调用<code>element</code>所对应Widget的<code>createRenderObject</code>方法创建与<code>element</code>相关联的RenderObject对象，然后调用<code>element.attachRenderObject</code>方法将<code>element.renderObject</code>添加到渲染树中插槽指定的位置（这一步不是必须的，一般发生在Element树结构发生变化时才需要重新attach）。插入到渲染树后的<code>element</code>就处于“active”状态，处于“active”状态后就可以显示在屏幕上了（可以隐藏）。</li> <li>当有父Widget的配置数据改变时，同时其<code>State.build</code>返回的Widget结构与之前不同，此时就需要重新构建对应的Element树。为了进行Element复用，在Element重新构建前会先尝试是否可以复用旧树上相同位置的element，element节点在更新前都会调用其对应Widget的<code>canUpdate</code>方法，如果返回<code>true</code>，则复用旧Element，旧的Element会使用新Widget配置数据更新，反之则会创建一个新的Element。<code>Widget.canUpdate</code>主要是判断<code>newWidget</code>与<code>oldWidget</code>的<code>runtimeType</code>和<code>key</code>是否同时相等，如果同时相等就返回<code>true</code>，否则就会返回<code>false</code>。根据这个原理，当我们需要强制更新一个Widget时，可以通过指定不同的Key来避免复用。</li> <li>当有祖先Element决定要移除<code>element</code> 时（如Widget树结构发生了变化，导致<code>element</code>对应的Widget被移除），这时该祖先Element就会调用<code>deactivateChild</code> 方法来移除它，移除后<code>element.renderObject</code>也会被从渲染树中移除，然后Framework会调用<code>element.deactivate</code> 方法，这时<code>element</code>状态变为“inactive”状态。</li> <li>“inactive”态的element将不会再显示到屏幕。为了避免在一次动画执行过程中反复创建、移除某个特定element，“inactive”态的element在当前动画最后一帧结束前都会保留，如果在动画执行结束后它还未能重新变成“active”状态，Framework就会调用其<code>unmount</code>方法将其彻底移除，这时element的状态为<code>defunct</code>,它将永远不会再被插入到树中。</li> <li>如果<code>element</code>要重新插入到Element树的其它位置，如<code>element</code>或<code>element</code>的祖先拥有一个GlobalKey（用于全局复用元素），那么Framework会先将element从现有位置移除，然后再调用其<code>activate</code>方法，并将其<code>renderObject</code>重新attach到渲染树。</li></ol> <p>看完Element的生命周期，可能有些读者会有疑问，开发者会直接操作Element树吗？其实对于开发者来说，大多数情况下只需要关注Widget树就行，Flutter框架已经将对Widget树的操作映射到了Element树上，这可以极大的降低复杂度，提高开发效率。但是了解Element对理解整个Flutter UI框架是至关重要的，Flutter正是通过Element这个纽带将Widget和RenderObject关联起来，了解Element层不仅会帮助读者对Flutter UI框架有个清晰的认识，而且也会提高自己的抽象能力和设计能力。另外在有些时候，我们必须得直接使用Element对象来完成一些操作，比如获取主题Theme数据，具体细节将在下文介绍。</p> <h2 id=\"_14-2-2-buildcontext\"><a href=\"#_14-2-2-buildcontext\" class=\"header-anchor\">#</a> 14.2.2 BuildContext</h2> <p>我们已经知道，<code>StatelessWidget</code>和<code>StatefulWidget</code>的<code>build</code>方法都会传一个<code>BuildContext</code>对象：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们也知道，在很多时候我们都需要使用这个<code>context</code> 做一些事，比如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Theme<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token comment\">//获取主题</span>\nNavigator<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> route<span class=\"token punctuation\">)</span> <span class=\"token comment\">//入栈新路由</span>\nLocalizations<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> type<span class=\"token punctuation\">)</span> <span class=\"token comment\">//获取Local</span>\ncontext<span class=\"token punctuation\">.</span>size <span class=\"token comment\">//获取上下文大小</span>\ncontext<span class=\"token punctuation\">.</span><span class=\"token function\">findRenderObject</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//查找当前或最近的一个祖先RenderObject</span>\n</code></pre></div><p>那么<code>BuildContext</code>到底是什么呢，查看其定义，发现其是一个抽象接口类：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">abstract</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">BuildContext</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>那这个<code>context</code>对象对应的实现类到底是谁呢？我们顺藤摸瓜，发现<code>build</code>调用是发生在<code>StatelessWidget</code>和<code>StatefulWidget</code>对应的<code>StatelessElement</code>和<code>StatefulElement</code>的<code>build</code>方法中，以<code>StatelessElement</code>为例：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">StatelessElement</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">ComponentElement</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> widget<span class=\"token punctuation\">.</span><span class=\"token function\">build</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>发现<code>build</code>传递的参数是<code>this</code>，很明显！这个<code>BuildContext</code>就是<code>StatelessElement</code>。同样，我们同样发现<code>StatefulWidget</code>的<code>context</code>是<code>StatefulElement</code>。但<code>StatelessElement</code>和<code>StatefulElement</code>本身并没有实现<code>BuildContext</code>接口，继续跟踪代码，发现它们间接继承自<code>Element</code>类，然后查看<code>Element</code>类定义，发现<code>Element</code>类果然实现了<code>BuildContext</code>接口:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">Element</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">DiagnosticableTree</span> <span class=\"token keyword\">implements</span> <span class=\"token class-name\">BuildContext</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>至此真相大白，<code>BuildContext</code>就是widget对应的<code>Element</code>，所以我们可以通过<code>context</code>在<code>StatelessWidget</code>和<code>StatefulWidget</code>的<code>build</code>方法中直接访问<code>Element</code>对象。我们获取主题数据的代码<code>Theme.of(context)</code>内部正是调用了Element的<code>dependOnInheritedWidgetOfExactType()</code>方法。</p> <blockquote><p>思考题：为什么build方法的参数不定义成Element对象，而要定义成BuildContext ?</p></blockquote> <h3 id=\"进阶\"><a href=\"#进阶\" class=\"header-anchor\">#</a> 进阶</h3> <p>我们可以看到Element是Flutter UI框架内部连接widget和<code>RenderObject</code>的纽带，大多数时候开发者只需要关注widget层即可，但是widget层有时候并不能完全屏蔽<code>Element</code>细节，所以Framework在<code>StatelessWidget</code>和<code>StatefulWidget</code>中通过<code>build</code>方法参数又将<code>Element</code>对象也传递给了开发者，这样一来，开发者便可以在需要时直接操作<code>Element</code>对象。那么现在笔者提两个问题，请读者先自己思考一下：</p> <ol><li>如果没有widget层，单靠<code>Element</code>层是否可以搭建起一个可用的UI框架？如果可以应该是什么样子？</li> <li>Flutter UI框架能不做成响应式吗？</li></ol> <p>对于问题1，答案当然是肯定的，因为我们之前说过widget树只是<code>Element</code>树的映射，我们完全可以直接通过Element来搭建一个UI框架。下面举一个例子：</p> <p>我们通过纯粹的Element来模拟一个<code>StatefulWidget</code>的功能，假设有一个页面，该页面有一个按钮，按钮的文本是一个9位数，点击一次按钮，则对9个数随机排一次序，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">HomeView</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">ComponentElement</span><span class=\"token punctuation\">{</span>\n  <span class=\"token function\">HomeView</span><span class=\"token punctuation\">(</span>Widget widget<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>widget<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  String text <span class=\"token operator\">=</span> <span class=\"token string\">&quot;123456789&quot;</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    Color primary<span class=\"token operator\">=</span>Theme<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>primaryColor<span class=\"token punctuation\">;</span> <span class=\"token comment\">//1</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">GestureDetector</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>text<span class=\"token punctuation\">,</span> style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> primary<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword\">var</span> t <span class=\"token operator\">=</span> text<span class=\"token punctuation\">.</span><span class=\"token function\">split</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token function\">shuffle</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            text <span class=\"token operator\">=</span> t<span class=\"token punctuation\">.</span><span class=\"token function\">join</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token function\">markNeedsBuild</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//点击后将该Element标记为dirty，Element将会rebuild</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><ul><li><p>上面<code>build</code>方法不接收参数，这一点和在<code>StatelessWidget</code>和<code>StatefulWidget</code>中<code>build(BuildContext)</code>方法不同。代码中需要用到<code>BuildContext</code>的地方直接用<code>this</code>代替即可，如代码注释1处<code>Theme.of(this)</code>参数直接传<code>this</code>即可，因为当前对象本身就是<code>Element</code>实例。</p></li> <li><p>当<code>text</code>发生改变时，我们调用<code>markNeedsBuild()</code>方法将当前Element标记为dirty即可，标记为dirty的Element会在下一帧中重建。实际上，<code>State.setState()</code>在内部也是调用的<code>markNeedsBuild()</code>方法。</p></li> <li><p>上面代码中build方法返回的仍然是一个widget，这是由于Flutter框架中已经有了widget这一层，并且组件库都已经是以widget的形式提供了，如果在Flutter框架中所有组件都像示例的<code>HomeView</code>一样以<code>Element</code>形式提供，那么就可以用纯<code>Element</code>来构建UI了<code>HomeView</code>的build方法返回值类型就可以是<code>Element</code>了。</p></li></ul> <p>如果我们需要将上面代码在现有Flutter框架中跑起来，那么还是得提供一个“适配器”widget将<code>HomeView</code>结合到现有框架中，下面<code>CustomHome</code>就相当于“适配器”：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">CustomHome</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">Widget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Element <span class=\"token function\">createElement</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">HomeView</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>现在就可以将<code>CustomHome</code>添加到widget树了，我们在一个新路由页创建它，最终效果如下如图14-1和14-2（点击后）所示：</p> <p><img src=\"/assets/img/14-1.075fa468.png\" alt=\"图14-1\"> <img src=\"/assets/img/14-2.7141498f.png\" alt=\"图14-2\"></p> <p>点击按钮则按钮文本会随机排序。</p> <p>对于问题2，答案当然也是肯定的，Flutter engine提供的dart API是原始且独立的，这个与操作系统提供的API类似，上层UI框架设计成什么样完全取决于设计者，完全可以将UI框架设计成Android风格或iOS风格，但这些事Google不会再去做，我们也没必要再去搞这一套，这是因为响应式的思想本身是很棒的，之所以提出这个问题，是因为笔者认为做与不做是一回事，但知道能不能做是另一回事，这能反映出我们对知识的理解程度。</p> <h3 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h3> <p>本节详细的介绍了<code>Element</code>的生命周期，以及它Widget、BuildContext的关系，也介绍了Element在Flutter UI系统中的角色和作用，我们将在下一节介绍Flutter UI系统中另一个重要的角色RenderObject。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter14/flutter_ui_system.html\" class=\"prev\">\n        14.1 Flutter UI系统\n      </a></span> <span class=\"next\"><a href=\"/chapter14/render_object.html\">\n        14.3 RenderObject和RenderBox\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/40.31216541.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter14/flutter_app_startup.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>14.4 Flutter运行机制-从启动到显示 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/148.0c341180.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable open\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" aria-current=\"page\" class=\"active sidebar-link\">14.4 Flutter运行机制-从启动到显示</a><ul class=\"sidebar-sub-headers\"></ul></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_14-4-flutter运行机制-从启动到显示\"><a href=\"#_14-4-flutter运行机制-从启动到显示\" class=\"header-anchor\">#</a> 14.4 Flutter运行机制-从启动到显示</h1> <p>本节我们主要介绍一下Flutter从启动到显示的过程。</p> <h3 id=\"启动\"><a href=\"#启动\" class=\"header-anchor\">#</a> 启动</h3> <p>Flutter的入口在&quot;lib/main.dart&quot;的<code>main()</code>函数中，它是Dart应用程序的起点。在Flutter应用中，<code>main()</code>函数最简单的实现如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">main</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">runApp</span><span class=\"token punctuation\">(</span><span class=\"token function\">MyApp</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以看<code>main()</code>函数只调用了一个<code>runApp()</code>方法，我们看看<code>runApp()</code>方法中都做了什么：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">runApp</span><span class=\"token punctuation\">(</span>Widget app<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  WidgetsFlutterBinding<span class=\"token punctuation\">.</span><span class=\"token function\">ensureInitialized</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token function\">attachRootWidget</span><span class=\"token punctuation\">(</span>app<span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token function\">scheduleWarmUpFrame</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>参数<code>app</code>是一个widget，它是Flutter应用启动后要展示的第一个Widget。而<code>WidgetsFlutterBinding</code>正是绑定widget 框架和Flutter engine的桥梁，定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">WidgetsFlutterBinding</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">BindingBase</span> <span class=\"token keyword\">with</span> GestureBinding<span class=\"token punctuation\">,</span> ServicesBinding<span class=\"token punctuation\">,</span> SchedulerBinding<span class=\"token punctuation\">,</span> PaintingBinding<span class=\"token punctuation\">,</span> SemanticsBinding<span class=\"token punctuation\">,</span> RendererBinding<span class=\"token punctuation\">,</span> WidgetsBinding <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">static</span> WidgetsBinding <span class=\"token function\">ensureInitialized</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>WidgetsBinding<span class=\"token punctuation\">.</span>instance <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span>\n      <span class=\"token function\">WidgetsFlutterBinding</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> WidgetsBinding<span class=\"token punctuation\">.</span>instance<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以看到<code>WidgetsFlutterBinding</code>继承自<code>BindingBase</code> 并混入了很多<code>Binding</code>，在介绍这些<code>Binding</code>之前我们先介绍一下<code>Window</code>，下面是<code>Window</code>的官方解释：</p> <blockquote><p>The most basic interface to the host operating system's user interface.</p></blockquote> <p>很明显，<code>Window</code>正是Flutter Framework连接宿主操作系统的接口。我们看一下<code>Window</code>类的部分定义：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">Window</span> <span class=\"token punctuation\">{</span>\n    \n  <span class=\"token comment\">// 当前设备的DPI，即一个逻辑像素显示多少物理像素，数字越大，显示效果就越精细保真。</span>\n  <span class=\"token comment\">// DPI是设备屏幕的固件属性，如Nexus 6的屏幕DPI为3.5 </span>\n  double <span class=\"token keyword\">get</span> devicePixelRatio <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _devicePixelRatio<span class=\"token punctuation\">;</span>\n  \n  <span class=\"token comment\">// Flutter UI绘制区域的大小</span>\n  Size <span class=\"token keyword\">get</span> physicalSize <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _physicalSize<span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// 当前系统默认的语言Locale</span>\n  Locale <span class=\"token keyword\">get</span> locale<span class=\"token punctuation\">;</span>\n    \n  <span class=\"token comment\">// 当前系统字体缩放比例。  </span>\n  double <span class=\"token keyword\">get</span> textScaleFactor <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _textScaleFactor<span class=\"token punctuation\">;</span>  \n    \n  <span class=\"token comment\">// 当绘制区域大小改变回调</span>\n  VoidCallback <span class=\"token keyword\">get</span> onMetricsChanged <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _onMetricsChanged<span class=\"token punctuation\">;</span>  \n  <span class=\"token comment\">// Locale发生变化回调</span>\n  VoidCallback <span class=\"token keyword\">get</span> onLocaleChanged <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _onLocaleChanged<span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">// 系统字体缩放变化回调</span>\n  VoidCallback <span class=\"token keyword\">get</span> onTextScaleFactorChanged <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _onTextScaleFactorChanged<span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">// 绘制前回调，一般会受显示器的垂直同步信号VSync驱动，当屏幕刷新时就会被调用</span>\n  FrameCallback <span class=\"token keyword\">get</span> onBeginFrame <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _onBeginFrame<span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">// 绘制回调  </span>\n  VoidCallback <span class=\"token keyword\">get</span> onDrawFrame <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _onDrawFrame<span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">// 点击或指针事件回调</span>\n  PointerDataPacketCallback <span class=\"token keyword\">get</span> onPointerDataPacket <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _onPointerDataPacket<span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">// 调度Frame，该方法执行后，onBeginFrame和onDrawFrame将紧接着会在合适时机被调用，</span>\n  <span class=\"token comment\">// 此方法会直接调用Flutter engine的Window_scheduleFrame方法</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">scheduleFrame</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> native <span class=\"token string\">'Window_scheduleFrame'</span><span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">// 更新应用在GPU上的渲染,此方法会直接调用Flutter engine的Window_render方法</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">render</span><span class=\"token punctuation\">(</span>Scene scene<span class=\"token punctuation\">)</span> native <span class=\"token string\">'Window_render'</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// 发送平台消息</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">sendPlatformMessage</span><span class=\"token punctuation\">(</span>String name<span class=\"token punctuation\">,</span>\n                           ByteData data<span class=\"token punctuation\">,</span>\n                           PlatformMessageResponseCallback callback<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">// 平台通道消息处理回调  </span>\n  PlatformMessageCallback <span class=\"token keyword\">get</span> onPlatformMessage <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _onPlatformMessage<span class=\"token punctuation\">;</span>\n  \n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//其它属性及回调</span>\n   \n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以看到<code>Window</code>类包含了当前设备和系统的一些信息以及Flutter Engine的一些回调。现在我们再回来看看<code>WidgetsFlutterBinding</code>混入的各种Binding。通过查看这些 Binding的源码，我们可以发现这些Binding中基本都是监听并处理<code>Window</code>对象的一些事件，然后将这些事件按照Framework的模型包装、抽象然后分发。可以看到<code>WidgetsFlutterBinding</code>正是粘连Flutter engine与上层Framework的“胶水”。</p> <ul><li><code>GestureBinding</code>：提供了<code>window.onPointerDataPacket</code> 回调，绑定Framework手势子系统，是Framework事件模型与底层事件的绑定入口。</li> <li><code>ServicesBinding</code>：提供了<code>window.onPlatformMessage</code> 回调， 用于绑定平台消息通道（message channel），主要处理原生和Flutter通信。</li> <li><code>SchedulerBinding</code>：提供了<code>window.onBeginFrame</code>和<code>window.onDrawFrame</code>回调，监听刷新事件，绑定Framework绘制调度子系统。</li> <li><code>PaintingBinding</code>：绑定绘制库，主要用于处理图片缓存。</li> <li><code>SemanticsBinding</code>：语义化层与Flutter engine的桥梁，主要是辅助功能的底层支持。</li> <li><code>RendererBinding</code>: 提供了<code>window.onMetricsChanged</code> 、<code>window.onTextScaleFactorChanged</code> 等回调。它是渲染树与Flutter engine的桥梁。</li> <li><code>WidgetsBinding</code>：提供了<code>window.onLocaleChanged</code>、<code>onBuildScheduled</code> 等回调。它是Flutter widget层与engine的桥梁。</li></ul> <p><code>WidgetsFlutterBinding.ensureInitialized()</code>负责初始化一个<code>WidgetsBinding</code>的全局单例，紧接着会调用<code>WidgetsBinding</code>的<code>attachRootWidget</code>方法，该方法负责将根Widget添加到<code>RenderView</code>上，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">attachRootWidget</span><span class=\"token punctuation\">(</span>Widget rootWidget<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  _renderViewElement <span class=\"token operator\">=</span> RenderObjectToWidgetAdapter<span class=\"token operator\">&lt;</span>RenderBox<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n    container<span class=\"token punctuation\">:</span> renderView<span class=\"token punctuation\">,</span> \n    debugShortDescription<span class=\"token punctuation\">:</span> <span class=\"token string\">'[root]'</span><span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> rootWidget\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">attachToRenderTree</span><span class=\"token punctuation\">(</span>buildOwner<span class=\"token punctuation\">,</span> renderViewElement<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>注意，代码中的有<code>renderView</code>和<code>renderViewElement</code>两个变量，<code>renderView</code>是一个<code>RenderObject</code>，它是渲染树的根，而<code>renderViewElement</code>是<code>renderView</code>对应的<code>Element</code>对象，可见该方法主要完成了根widget到根 <code>RenderObject</code>再到根<code>Element</code>的整个关联过程。我们看看<code>attachToRenderTree</code>的源码实现：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>RenderObjectToWidgetElement<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span> <span class=\"token function\">attachToRenderTree</span><span class=\"token punctuation\">(</span>BuildOwner owner<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>RenderObjectToWidgetElement<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span> element<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>element <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    owner<span class=\"token punctuation\">.</span><span class=\"token function\">lockState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      element <span class=\"token operator\">=</span> <span class=\"token function\">createElement</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span>element <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      element<span class=\"token punctuation\">.</span><span class=\"token function\">assignOwner</span><span class=\"token punctuation\">(</span>owner<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    owner<span class=\"token punctuation\">.</span><span class=\"token function\">buildScope</span><span class=\"token punctuation\">(</span>element<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      element<span class=\"token punctuation\">.</span><span class=\"token function\">mount</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n    element<span class=\"token punctuation\">.</span>_newWidget <span class=\"token operator\">=</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">;</span>\n    element<span class=\"token punctuation\">.</span><span class=\"token function\">markNeedsBuild</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token keyword\">return</span> element<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>该方法负责创建根element，即<code>RenderObjectToWidgetElement</code>，并且将element与widget 进行关联，即创建出 widget树对应的element树。如果element 已经创建过了，则将根element 中关联的widget 设为新的，由此可以看出element 只会创建一次，后面会进行复用。那么<code>BuildOwner</code>是什么呢？其实他就是widget framework的管理类，它跟踪哪些widget需要重新构建。</p> <h3 id=\"渲染\"><a href=\"#渲染\" class=\"header-anchor\">#</a> 渲染</h3> <p>回到<code>runApp</code>的实现中，当调用完<code>attachRootWidget</code>后，最后一行会调用 <code>WidgetsFlutterBinding</code> 实例的 <code>scheduleWarmUpFrame()</code> 方法，该方法的实现在<code>SchedulerBinding</code> 中，它被调用后会立即进行一次绘制（而不是等待&quot;vsync&quot; 信号），在此次绘制结束前，该方法会锁定事件分发，也就是说在本次绘制结束完成之前Flutter将不会响应各种事件，这可以保证在绘制过程中不会再触发新的重绘。下面是<code>scheduleWarmUpFrame()</code> 方法的部分实现(省略了无关代码)：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">scheduleWarmUpFrame</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  Timer<span class=\"token punctuation\">.</span><span class=\"token function\">run</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">handleBeginFrame</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> \n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  Timer<span class=\"token punctuation\">.</span><span class=\"token function\">run</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">handleDrawFrame</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>  \n    <span class=\"token function\">resetEpoch</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">// 锁定事件</span>\n  <span class=\"token function\">lockEvents</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">await</span> endOfFrame<span class=\"token punctuation\">;</span>\n    Timeline<span class=\"token punctuation\">.</span><span class=\"token function\">finishSync</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以看到该方法中主要调用了<code>handleBeginFrame()</code> 和 <code>handleDrawFrame()</code> 两个方法，在看这两个方法之前我们首先了解一下Frame 和 FrameCallback 的概念：</p> <ul><li><p>Frame: 一次绘制过程，我们称其为一帧。Flutter engine受显示器垂直同步信号&quot;VSync&quot;的驱使不断的触发绘制。我们之前说的Flutter可以实现60fps（Frame Per-Second），就是指一秒钟可以触发60次重绘，FPS值越大，界面就越流畅。</p></li> <li><p>FrameCallback：<code>SchedulerBinding</code> 类中有三个FrameCallback回调队列， 在一次绘制过程中，这三个回调队列会放在不同时机被执行：</p> <ol><li><code>transientCallbacks</code>：用于存放一些临时回调，一般存放动画回调。可以通过<code>SchedulerBinding.instance.scheduleFrameCallback</code> 添加回调。</li> <li><code>persistentCallbacks</code>：用于存放一些持久的回调，不能在此类回调中再请求新的绘制帧，持久回调一经注册则不能移除。<code>SchedulerBinding.instance.addPersitentFrameCallback()</code>，这个回调中处理了布局与绘制工作。</li> <li><code>postFrameCallbacks</code>：在Frame结束时只会被调用一次，调用后会被系统移除，可由 <code>SchedulerBinding.instance.addPostFrameCallback()</code> 注册，注意，不要在此类回调中再触发新的Frame，这可以会导致循环刷新。</li></ol></li></ul> <p>现在请读者自行查看<code>handleBeginFrame()</code> 和 <code>handleDrawFrame()</code> 两个方法的源码，可以发现前者主要是执行了<code>transientCallbacks</code>队列，而后者执行了 <code>persistentCallbacks</code> 和 <code>postFrameCallbacks</code> 队列。</p> <h3 id=\"绘制\"><a href=\"#绘制\" class=\"header-anchor\">#</a> 绘制</h3> <p>渲染和绘制逻辑在<code>RendererBinding</code>中实现，查看其源码，发现在其<code>initInstances()</code>方法中有如下代码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">initInstances</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n      \n  <span class=\"token comment\">//监听Window对象的事件  </span>\n  ui<span class=\"token punctuation\">.</span>window\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>onMetricsChanged <span class=\"token operator\">=</span> handleMetricsChanged\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>onTextScaleFactorChanged <span class=\"token operator\">=</span> handleTextScaleFactorChanged\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>onSemanticsEnabledChanged <span class=\"token operator\">=</span> _handleSemanticsEnabledChanged\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>onSemanticsAction <span class=\"token operator\">=</span> _handleSemanticsAction<span class=\"token punctuation\">;</span>\n   \n  <span class=\"token comment\">//添加PersistentFrameCallback    </span>\n  <span class=\"token function\">addPersistentFrameCallback</span><span class=\"token punctuation\">(</span>_handlePersistentFrameCallback<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们看最后一行，通过<code>addPersistentFrameCallback</code> 向<code>persistentCallbacks</code>队列添加了一个回调 <code>_handlePersistentFrameCallback</code>:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">_handlePersistentFrameCallback</span><span class=\"token punctuation\">(</span>Duration timeStamp<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">drawFrame</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>该方法直接调用了<code>RendererBinding</code>的<code>drawFrame()</code>方法：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">drawFrame</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span>renderView <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  pipelineOwner<span class=\"token punctuation\">.</span><span class=\"token function\">flushLayout</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//布局</span>\n  pipelineOwner<span class=\"token punctuation\">.</span><span class=\"token function\">flushCompositingBits</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//重绘之前的预处理操作，检查RenderObject是否需要重绘</span>\n  pipelineOwner<span class=\"token punctuation\">.</span><span class=\"token function\">flushPaint</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// 重绘</span>\n  renderView<span class=\"token punctuation\">.</span><span class=\"token function\">compositeFrame</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// 将需要绘制的比特数据发给GPU</span>\n  pipelineOwner<span class=\"token punctuation\">.</span><span class=\"token function\">flushSemantics</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// this also sends the semantics to the OS.</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们看看这些方法分别做了什么：</p> <h4 id=\"flushlayout\"><a href=\"#flushlayout\" class=\"header-anchor\">#</a> flushLayout()</h4> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">flushLayout</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n   <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n    <span class=\"token keyword\">while</span> <span class=\"token punctuation\">(</span>_nodesNeedingLayout<span class=\"token punctuation\">.</span>isNotEmpty<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">final</span> List<span class=\"token operator\">&lt;</span>RenderObject<span class=\"token operator\">&gt;</span> dirtyNodes <span class=\"token operator\">=</span> _nodesNeedingLayout<span class=\"token punctuation\">;</span>\n      _nodesNeedingLayout <span class=\"token operator\">=</span> <span class=\"token operator\">&lt;</span>RenderObject<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span>RenderObject node <span class=\"token keyword\">in</span> \n           dirtyNodes<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token function\">sort</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>RenderObject a<span class=\"token punctuation\">,</span> RenderObject b<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> a<span class=\"token punctuation\">.</span>depth <span class=\"token operator\">-</span> b<span class=\"token punctuation\">.</span>depth<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>node<span class=\"token punctuation\">.</span>_needsLayout <span class=\"token operator\">&amp;&amp;</span> node<span class=\"token punctuation\">.</span>owner <span class=\"token operator\">==</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span>\n          node<span class=\"token punctuation\">.</span><span class=\"token function\">_layoutWithoutResize</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span> \n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>源码很简单，该方法主要任务是更新了所有被标记为“dirty”的<code>RenderObject</code>的布局信息。主要的动作发生在<code>node._layoutWithoutResize()</code>方法中，该方法中会调用<code>performLayout()</code>进行重新布局。</p> <h4 id=\"flushcompositingbits\"><a href=\"#flushcompositingbits\" class=\"header-anchor\">#</a> flushCompositingBits()</h4> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">flushCompositingBits</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  _nodesNeedingCompositingBitsUpdate<span class=\"token punctuation\">.</span><span class=\"token function\">sort</span><span class=\"token punctuation\">(</span>\n      <span class=\"token punctuation\">(</span>RenderObject a<span class=\"token punctuation\">,</span> RenderObject b<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> a<span class=\"token punctuation\">.</span>depth <span class=\"token operator\">-</span> b<span class=\"token punctuation\">.</span>depth\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span>RenderObject node <span class=\"token keyword\">in</span> _nodesNeedingCompositingBitsUpdate<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>node<span class=\"token punctuation\">.</span>_needsCompositingBitsUpdate <span class=\"token operator\">&amp;&amp;</span> node<span class=\"token punctuation\">.</span>owner <span class=\"token operator\">==</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span>\n      node<span class=\"token punctuation\">.</span><span class=\"token function\">_updateCompositingBits</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//更新RenderObject.needsCompositing属性值</span>\n  <span class=\"token punctuation\">}</span>\n  _nodesNeedingCompositingBitsUpdate<span class=\"token punctuation\">.</span><span class=\"token function\">clear</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>检查<code>RenderObject</code>是否需要重绘，然后更新<code>RenderObject.needsCompositing</code>属性，如果该属性值被标记为<code>true</code>则需要重绘。</p> <h4 id=\"flushpaint\"><a href=\"#flushpaint\" class=\"header-anchor\">#</a> flushPaint()</h4> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">flushPaint</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">final</span> List<span class=\"token operator\">&lt;</span>RenderObject<span class=\"token operator\">&gt;</span> dirtyNodes <span class=\"token operator\">=</span> _nodesNeedingPaint<span class=\"token punctuation\">;</span> \n    _nodesNeedingPaint <span class=\"token operator\">=</span> <span class=\"token operator\">&lt;</span>RenderObject<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">// 反向遍历需要重绘的RenderObject</span>\n    <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span>RenderObject node <span class=\"token keyword\">in</span> \n         dirtyNodes<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token function\">sort</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>RenderObject a<span class=\"token punctuation\">,</span> RenderObject b<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> b<span class=\"token punctuation\">.</span>depth <span class=\"token operator\">-</span> a<span class=\"token punctuation\">.</span>depth<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>node<span class=\"token punctuation\">.</span>_needsPaint <span class=\"token operator\">&amp;&amp;</span> node<span class=\"token punctuation\">.</span>owner <span class=\"token operator\">==</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>node<span class=\"token punctuation\">.</span>_layer<span class=\"token punctuation\">.</span>attached<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token comment\">// 真正的绘制逻辑  </span>\n          PaintingContext<span class=\"token punctuation\">.</span><span class=\"token function\">repaintCompositedChild</span><span class=\"token punctuation\">(</span>node<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n          node<span class=\"token punctuation\">.</span><span class=\"token function\">_skippedPaintingOnLayer</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span> \n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>该方法进行了最终的绘制，可以看出它不是重绘了所有 <code>RenderObject</code>，而是只重绘了需要重绘的 <code>RenderObject</code>。真正的绘制是通过<code>PaintingContext.repaintCompositedChild()</code>来绘制的，该方法最终会调用Flutter engine提供的Canvas API来完成绘制。</p> <h4 id=\"compositeframe\"><a href=\"#compositeframe\" class=\"header-anchor\">#</a> compositeFrame()</h4> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">compositeFrame</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">final</span> ui<span class=\"token punctuation\">.</span>SceneBuilder builder <span class=\"token operator\">=</span> ui<span class=\"token punctuation\">.</span><span class=\"token function\">SceneBuilder</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">final</span> ui<span class=\"token punctuation\">.</span>Scene scene <span class=\"token operator\">=</span> layer<span class=\"token punctuation\">.</span><span class=\"token function\">buildScene</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>automaticSystemUiAdjustment<span class=\"token punctuation\">)</span>\n      <span class=\"token function\">_updateSystemChrome</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    ui<span class=\"token punctuation\">.</span>window<span class=\"token punctuation\">.</span><span class=\"token function\">render</span><span class=\"token punctuation\">(</span>scene<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//调用Flutter engine的渲染API</span>\n    scene<span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> \n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">finally</span> <span class=\"token punctuation\">{</span>\n    Timeline<span class=\"token punctuation\">.</span><span class=\"token function\">finishSync</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>这个方法中有一个<code>Scene</code>对象，Scene对象是一个数据结构，保存最终渲染后的像素信息。这个方法将Canvas画好的<code>Scene</code>传给<code>window.render()</code>方法，该方法会直接将scene信息发送给Flutter engine，最终由engine将图像画在设备屏幕上。</p> <h4 id=\"最后\"><a href=\"#最后\" class=\"header-anchor\">#</a> 最后</h4> <p>需要注意的是：由于<code>RendererBinding</code>只是一个mixin，而with它的是<code>WidgetsBinding</code>，所以我们需要看看<code>WidgetsBinding</code>中是否重写该方法，查看<code>WidgetsBinding</code>的<code>drawFrame()</code>方法源码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token metadata symbol\">@override</span>\n<span class=\"token keyword\">void</span> <span class=\"token function\">drawFrame</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token comment\">//省略无关代码</span>\n  <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>renderViewElement <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span>\n      buildOwner<span class=\"token punctuation\">.</span><span class=\"token function\">buildScope</span><span class=\"token punctuation\">(</span>renderViewElement<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> \n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">drawFrame</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//调用RendererBinding的drawFrame()方法</span>\n    buildOwner<span class=\"token punctuation\">.</span><span class=\"token function\">finalizeTree</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span> \n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们发现在调用<code>RendererBinding.drawFrame()</code>方法前会调用 <code>buildOwner.buildScope()</code> （非首次绘制），该方法会将被标记为“dirty” 的 element 进行 <code>rebuild()</code> 。</p> <h3 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h3> <p>本节介绍了Flutter APP从启动到显示到屏幕上的主流程，读者可以结合前面章节对Widget、Element以及RenderObject的介绍来加强细节理解。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter14/render_object.html\" class=\"prev\">\n        14.3 RenderObject和RenderBox\n      </a></span> <span class=\"next\"><a href=\"/chapter14/image_and_cache.html\">\n        14.5 图片加载原理与缓存\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/148.0c341180.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter14/flutter_ui_system.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>14.1 Flutter UI系统 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/149.46210223.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable open\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" aria-current=\"page\" class=\"active sidebar-link\">14.1 Flutter UI系统</a><ul class=\"sidebar-sub-headers\"></ul></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_14-1-flutter-ui系统\"><a href=\"#_14-1-flutter-ui系统\" class=\"header-anchor\">#</a> 14.1 Flutter UI系统</h1> <p>在本书的前面章节中，我们多次提到&quot;UI系统&quot;这个概念，本书中所指的UI系统特指：基于一个平台，在此平台上实现GUI的一个系统，这里的平台特指操作系统，如Android、iOS或者Windows、macOS。我们说过各个平台UI系统的原理是相通的，也就是说无论是Android还是iOS，他们将一个用户界面展示到屏幕的流程是相似的，所以，在介绍Flutter UI系统之前，我们先看看UI系统的基本原理，这样可以帮助读者对操作系统和系统底层UI逻辑有一个清晰的认识。</p> <h3 id=\"硬件绘图基本原理\"><a href=\"#硬件绘图基本原理\" class=\"header-anchor\">#</a> 硬件绘图基本原理</h3> <p>提到原理，我们要从屏幕显示图像的基本原理谈起。我们知道显示器（屏幕）是由一个个物理显示单元组成，每一个单元我们可以称之为一个物理像素点，而每一个像素点可以发出多种颜色，显示器成相的原理就是在不同的物理像素点上显示不同的颜色，最终构成完整的图像。</p> <p>一个像素点能发出的所有颜色总数是显示器的一个重要指标，比如我们所说的1600万色的屏幕就是指一个像素点可以显示出1600万种颜色，而显示器颜色是有RGB三基色组成，所以1600万即2的24次方，即每个基本色（R、G、B）深度扩展至8 bit(位)，颜色深度越深，所能显示的色彩更加丰富靓丽。</p> <p>为了更新显示画面，显示器是以固定的频率刷新（从GPU取数据），比如有一部手机屏幕的刷新频率是 60Hz。当一帧图像绘制完毕后准备绘制下一帧时，显示器会发出一个垂直同步信号（如VSync）， 60Hz的屏幕就会一秒内发出 60次这样的信号。而这个信号主要是用于同步CPU、GPU和显示器的。一般地来说，计算机系统中，CPU、GPU和显示器以一种特定的方式协作：CPU将计算好的显示内容提交给 GPU，GPU渲染后放入帧缓冲区，然后视频控制器按照同步信号从帧缓冲区取帧数据传递给显示器显示。</p> <p>CPU和GPU的任务是各有偏重的，CPU主要用于基本数学和逻辑计算，而GPU主要执行和图形处理相关的复杂的数学，如矩阵变化和几何计算，GPU的主要作用就是确定最终输送给显示器的各个像素点的色值。</p> <h3 id=\"操作系统绘制api的封装\"><a href=\"#操作系统绘制api的封装\" class=\"header-anchor\">#</a> 操作系统绘制API的封装</h3> <p>由于最终的图形计算和绘制都是由相应的硬件来完成，而直接操作硬件的指令通常都会有操作系统屏蔽，应用开发者通常不会直接面对硬件，操作系统屏蔽了这些底层硬件操作后会提供一些封装后的API供操作系统之上的应用调用，但是对于应用开发者来说，直接调用这些操作系统提供的API是比较复杂和低效的，因为操作系统提供的API往往比较基础，直接调用需要了解API的很多细节。正是因为这个原因，几乎所有用于开发GUI程序的编程语言都会在操作系统之上再封装一层，将操作系统原生API封装在一个编程框架和模型中，然后定义一种简单的开发规则来开发GUI应用程序，而这一层抽象，正是我们所说的“UI”系统，如Android SDK正是封装了Android操作系统API，提供了一个“UI描述文件XML+Java操作DOM”的UI系统，而iOS的UIKit 对View的抽象也是一样的，他们都将操作系统API抽象成一个基础对象（如用于2D图形绘制的Canvas），然后再定义一套规则来描述UI，如UI树结构，UI操作的单线程原则等。</p> <h3 id=\"flutter-ui系统\"><a href=\"#flutter-ui系统\" class=\"header-anchor\">#</a> Flutter UI系统</h3> <p>我们可以看到，无论是Android SDK还是iOS的UIKit 的职责都是相同的，它们只是语言载体和底层的系统不同而已。那么可不可以实现这么一个UI系统：可以使用同一种编程语言开发，然后针对不同操作系统API抽象一个对上接口一致，对下适配不同操作系统的的中间层，然后在打包编译时再使用相应的中间层代码？如果可以做到，那么我们就可以使用同一套代码编写跨平台的应用了。而Flutter的原理正是如此，它提供了一套Dart API，然后在底层通过OpenGL这种跨平台的绘制库（内部会调用操作系统API）实现了一套代码跨多端。由于Dart API也是调用操作系统API，所以它的性能接近原生。</p> <blockquote><p>注意，虽然Dart是先调用了OpenGL，OpenGL才会调用操作系统API，但是这仍然是原生渲染，因为OpenGL只是操作系统API的一个封装库，它并不像WebView渲染那样需要JavaScript运行环境和CSS渲染器，所以不会有性能损失。</p></blockquote> <p>至此，我们已经介绍了Flutter UI系统和操作系统交互的这一部分原理，现在需要说一些它对应用开发者定义的开发标准。其实在前面的章节中，我们已经对这个标准非常熟悉了, 简单概括就是：组合和响应式。我们要开发一个UI界面，需要通过组合其它Widget来实现，Flutter中，一切都是Widget，当UI要发生变化时，我们不去直接修改DOM，而是通过更新状态，让Flutter UI系统来根据新的状态来重新构建UI。</p> <p>讲到这里，读者可能发现Flutter UI系统和Flutter Framework的概念是差不多的，的确如此，之所以用“UI系统”，是因为其他平台中可能不这么叫，我们只是为了概念统一，便于描述，读者不必纠结于概念本身。</p> <p>在接下来的小节中，我们先详细介绍一下<code>Element</code>、<code>RenderObject</code>，它们是组成Flutter UI系统的基石。最后我们再分析一下Flutter应用启动和运行的整体过程。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter13/faq.html\" class=\"prev\">\n        13.4 国际化常见问题\n      </a></span> <span class=\"next\"><a href=\"/chapter14/element_buildcontext.html\">\n        14.2 Element与BuildContext\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/149.46210223.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter14/image_and_cache.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>14.5 图片加载原理与缓存 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/91.9eb501f7.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable open\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" aria-current=\"page\" class=\"active sidebar-link\">14.5 图片加载原理与缓存</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/chapter14/image_and_cache.html#_14-5-1-imageprovider\" class=\"sidebar-link\">14.5.1 ImageProvider</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter14/image_and_cache.html#_14-5-2-image组件原理\" class=\"sidebar-link\">14.5.2 Image组件原理</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter14/image_and_cache.html#总结-2\" class=\"sidebar-link\">总结</a></li></ul></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_14-5-图片加载原理与缓存\"><a href=\"#_14-5-图片加载原理与缓存\" class=\"header-anchor\">#</a> 14.5 图片加载原理与缓存</h1> <p>在本书前面章节已经介绍过<code>Image</code> 组件，并提到Flutter框架对加载过的图片是有缓存的（内存），默认最大缓存数量是1000，最大缓存空间为100M。本节便详细介绍Image的原理及图片缓存机制，下面我们先看看<code>ImageProvider</code> 类。</p> <h2 id=\"_14-5-1-imageprovider\"><a href=\"#_14-5-1-imageprovider\" class=\"header-anchor\">#</a> 14.5.1 ImageProvider</h2> <p>我们已经知道<code>Image</code> 组件的<code>image</code> 参数是一个必选参数，它是<code>ImageProvider</code>类型。下面我们便详细介绍一下<code>ImageProvider</code>，<code>ImageProvider</code>是一个抽象类，定义了图片数据获取和加载的相关接口。它的主要职责有两个：</p> <ol><li>提供图片数据源</li> <li>缓存图片</li></ol> <p>我们看看<code>ImageProvider</code>抽象类的详细定义：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">abstract</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">ImageProvider</span><span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n\n  ImageStream <span class=\"token function\">resolve</span><span class=\"token punctuation\">(</span>ImageConfiguration configuration<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 实现代码省略</span>\n  <span class=\"token punctuation\">}</span>\n  Future<span class=\"token operator\">&lt;</span>bool<span class=\"token operator\">&gt;</span> <span class=\"token function\">evict</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> ImageCache cache<span class=\"token punctuation\">,</span>\n                      ImageConfiguration configuration <span class=\"token operator\">=</span> ImageConfiguration<span class=\"token punctuation\">.</span>empty <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 实现代码省略</span>\n  <span class=\"token punctuation\">}</span>\n\n  Future<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span> <span class=\"token function\">obtainKey</span><span class=\"token punctuation\">(</span>ImageConfiguration configuration<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> \n  <span class=\"token metadata symbol\">@protected</span>\n  ImageStreamCompleter <span class=\"token function\">load</span><span class=\"token punctuation\">(</span>T key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// 需子类实现</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h4 id=\"load-t-key-方法\"><a href=\"#load-t-key-方法\" class=\"header-anchor\">#</a> <code>load(T key)</code>方法</h4> <p>加载图片数据源的接口，不同的数据源的加载方法不同，每个<code>ImageProvider</code>的子类必须实现它。比如<code>NetworkImage</code>类和<code>AssetImage</code>类，它们都是<code>ImageProvider</code>的子类，但它们需要从不同的数据源来加载图片数据：<code>NetworkImage</code>是从网络来加载图片数据，而<code>AssetImage</code>则是从最终的应用包里来加载（加载打到应用安装包里的资源图片）。 我们以<code>NetworkImage</code>为例，看看其load方法的实现：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>\n<span class=\"token metadata symbol\">@override</span>\nImageStreamCompleter <span class=\"token function\">load</span><span class=\"token punctuation\">(</span>image_provider<span class=\"token punctuation\">.</span>NetworkImage key<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n\n  <span class=\"token keyword\">final</span> StreamController<span class=\"token operator\">&lt;</span>ImageChunkEvent<span class=\"token operator\">&gt;</span> chunkEvents <span class=\"token operator\">=</span> StreamController<span class=\"token operator\">&lt;</span>ImageChunkEvent<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  \n  <span class=\"token keyword\">return</span> <span class=\"token function\">MultiFrameImageStreamCompleter</span><span class=\"token punctuation\">(</span>\n    codec<span class=\"token punctuation\">:</span> <span class=\"token function\">_loadAsync</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">,</span> chunkEvents<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//调用</span>\n    chunkEvents<span class=\"token punctuation\">:</span> chunkEvents<span class=\"token punctuation\">.</span>stream<span class=\"token punctuation\">,</span>\n    scale<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">.</span>scale<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们看到，<code>load</code>方法的返回值类型是<code>ImageStreamCompleter</code> ，它是一个抽象类，定义了管理图片加载过程的一些接口，<code>Image</code> Widget中正是通过它来监听图片加载状态的（我们将在下面介绍<code>Image</code> 原理时详细介绍）。</p> <p><code>MultiFrameImageStreamCompleter</code> 是 <code>ImageStreamCompleter</code>的一个子类，是flutter sdk预置的类，通过该类，我们以方便、轻松地创建出一个<code>ImageStreamCompleter</code>实例来做为<code>load</code>方法的返回值。</p> <p>我们可以看到，<code>MultiFrameImageStreamCompleter</code> 需要一个<code>codec</code>参数，该参数类型为<code>Future&lt;ui.Codec&gt;</code>。<code>Codec</code> 是处理图片编解码的类的一个handler，实际上，它只是一个flutter engine API 的包装类，也就是说图片的编解码逻辑不是在Dart 代码部分实现，而是在flutter engine中实现的。<code>Codec</code>类部分定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token metadata symbol\">@pragma</span><span class=\"token punctuation\">(</span><span class=\"token string\">'vm:entry-point'</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">Codec</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">NativeFieldWrapperClass2</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// 此类由flutter engine创建，不应该手动实例化此类或直接继承此类。</span>\n  <span class=\"token metadata symbol\">@pragma</span><span class=\"token punctuation\">(</span><span class=\"token string\">'vm:entry-point'</span><span class=\"token punctuation\">)</span>\n  Codec<span class=\"token punctuation\">.</span><span class=\"token function\">_</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">/// 图片中的帧数(动态图会有多帧)</span>\n  int <span class=\"token keyword\">get</span> frameCount native <span class=\"token string\">'Codec_frameCount'</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">/// 动画重复的次数</span>\n  <span class=\"token comment\">/// * 0 表示只执行一次</span>\n  <span class=\"token comment\">/// * -1 表示循环执行</span>\n  int <span class=\"token keyword\">get</span> repetitionCount native <span class=\"token string\">'Codec_repetitionCount'</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">/// 获取下一个动画帧</span>\n  Future<span class=\"token operator\">&lt;</span>FrameInfo<span class=\"token operator\">&gt;</span> <span class=\"token function\">getNextFrame</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">_futurize</span><span class=\"token punctuation\">(</span>_getNextFrame<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  String <span class=\"token function\">_getNextFrame</span><span class=\"token punctuation\">(</span>_Callback<span class=\"token operator\">&lt;</span>FrameInfo<span class=\"token operator\">&gt;</span> callback<span class=\"token punctuation\">)</span> native <span class=\"token string\">'Codec_getNextFrame'</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>我们可以看到<code>Codec</code>最终的结果是一个或多个（动图）帧，而这些帧最终会绘制到屏幕上。</p> <p><code>MultiFrameImageStreamCompleter 的</code> <code>codec</code>参数值为<code>_loadAsync</code>方法的返回值，我们继续看<code>_loadAsync</code>方法的实现：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>\n Future<span class=\"token operator\">&lt;</span>ui<span class=\"token punctuation\">.</span>Codec<span class=\"token operator\">&gt;</span> <span class=\"token function\">_loadAsync</span><span class=\"token punctuation\">(</span>\n    NetworkImage key<span class=\"token punctuation\">,</span>\n    StreamController<span class=\"token operator\">&lt;</span>ImageChunkEvent<span class=\"token operator\">&gt;</span> chunkEvents<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">//下载图片</span>\n      <span class=\"token keyword\">final</span> Uri resolved <span class=\"token operator\">=</span> Uri<span class=\"token punctuation\">.</span>base<span class=\"token punctuation\">.</span><span class=\"token function\">resolve</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">.</span>url<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">final</span> HttpClientRequest request <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> _httpClient<span class=\"token punctuation\">.</span><span class=\"token function\">getUrl</span><span class=\"token punctuation\">(</span>resolved<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      headers<span class=\"token operator\">?</span><span class=\"token punctuation\">.</span><span class=\"token function\">forEach</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>String name<span class=\"token punctuation\">,</span> String value<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        request<span class=\"token punctuation\">.</span>headers<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span>name<span class=\"token punctuation\">,</span> value<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">final</span> HttpClientResponse response <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> request<span class=\"token punctuation\">.</span><span class=\"token function\">close</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>response<span class=\"token punctuation\">.</span>statusCode <span class=\"token operator\">!=</span> HttpStatus<span class=\"token punctuation\">.</span>ok<span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">throw</span> <span class=\"token function\">Exception</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token comment\">// 接收图片数据 </span>\n      <span class=\"token keyword\">final</span> Uint8List bytes <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">consolidateHttpClientResponseBytes</span><span class=\"token punctuation\">(</span>\n        response<span class=\"token punctuation\">,</span>\n        onBytesReceived<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>int cumulative<span class=\"token punctuation\">,</span> int total<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          chunkEvents<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token function\">ImageChunkEvent</span><span class=\"token punctuation\">(</span>\n            cumulativeBytesLoaded<span class=\"token punctuation\">:</span> cumulative<span class=\"token punctuation\">,</span>\n            expectedTotalBytes<span class=\"token punctuation\">:</span> total<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>bytes<span class=\"token punctuation\">.</span>lengthInBytes <span class=\"token operator\">==</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">throw</span> <span class=\"token function\">Exception</span><span class=\"token punctuation\">(</span><span class=\"token string\">'NetworkImage is an empty file: $resolved'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token comment\">// 对图片数据进行解码</span>\n      <span class=\"token keyword\">return</span> PaintingBinding<span class=\"token punctuation\">.</span>instance<span class=\"token punctuation\">.</span><span class=\"token function\">instantiateImageCodec</span><span class=\"token punctuation\">(</span>bytes<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">finally</span> <span class=\"token punctuation\">{</span>\n      chunkEvents<span class=\"token punctuation\">.</span><span class=\"token function\">close</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以看到<code>_loadAsync</code>方法主要做了两件事：</p> <ol><li>下载图片。</li> <li>对下载的图片数据进行解码。</li></ol> <p>下载逻辑比较简单：通过<code>HttpClient</code>从网上下载图片，另外下载请求会设置一些自定义的header，开发者可以通过<code>NetworkImage</code>的<code>headers</code>命名参数来传递。</p> <p>在图片下载完成后调用了<code>PaintingBinding.instance.instantiateImageCodec(bytes)</code>对图片进行解码，值得注意的是<code>instantiateImageCodec(...)</code>也是一个Native API的包装，实际上会调用Flutter engine的<code>instantiateImageCodec</code>方法，源码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>String <span class=\"token function\">_instantiateImageCodec</span><span class=\"token punctuation\">(</span>Uint8List list<span class=\"token punctuation\">,</span> _Callback<span class=\"token operator\">&lt;</span>Codec<span class=\"token operator\">&gt;</span> callback<span class=\"token punctuation\">,</span> _ImageInfo imageInfo<span class=\"token punctuation\">,</span> int targetWidth<span class=\"token punctuation\">,</span> int targetHeight<span class=\"token punctuation\">)</span>\n  native <span class=\"token string\">'instantiateImageCodec'</span><span class=\"token punctuation\">;</span>\n</code></pre></div><h4 id=\"obtainkey-imageconfiguration-方法\"><a href=\"#obtainkey-imageconfiguration-方法\" class=\"header-anchor\">#</a> <code>obtainKey(ImageConfiguration)</code>方法</h4> <p>该接口主要是为了配合实现图片缓存，<code>ImageProvider</code>从数据源加载完数据后，会在全局的<code>ImageCache</code>中缓存图片数据，而图片数据缓存是一个Map，而Map的key便是调用此方法的返回值，不同的key代表不同的图片数据缓存。</p> <h4 id=\"resolve-imageconfiguration-方法\"><a href=\"#resolve-imageconfiguration-方法\" class=\"header-anchor\">#</a> <code>resolve(ImageConfiguration)</code> 方法</h4> <p><code>resolve</code>方法是<code>ImageProvider</code>的暴露的给<code>Image</code>的主入口方法，它接受一个<code>ImageConfiguration</code>参数，返回<code>ImageStream</code>，即图片数据流。我们重点看一下<code>resolve</code>执行流程：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>ImageStream <span class=\"token function\">resolve</span><span class=\"token punctuation\">(</span>ImageConfiguration configuration<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n  <span class=\"token keyword\">final</span> ImageStream stream <span class=\"token operator\">=</span> <span class=\"token function\">ImageStream</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  T obtainedKey<span class=\"token punctuation\">;</span> <span class=\"token comment\">//</span>\n  <span class=\"token comment\">//定义错误处理函数</span>\n  Future<span class=\"token operator\">&lt;</span><span class=\"token keyword\">void</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">handleError</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">dynamic</span> exception<span class=\"token punctuation\">,</span> StackTrace stack<span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n    stream<span class=\"token punctuation\">.</span><span class=\"token function\">setCompleter</span><span class=\"token punctuation\">(</span>imageCompleter<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    imageCompleter<span class=\"token punctuation\">.</span><span class=\"token function\">setError</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">// 创建一个新Zone，主要是为了当发生错误时不会干扰MainZone</span>\n  <span class=\"token keyword\">final</span> Zone dangerZone <span class=\"token operator\">=</span> Zone<span class=\"token punctuation\">.</span>current<span class=\"token punctuation\">.</span><span class=\"token function\">fork</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  \n  dangerZone<span class=\"token punctuation\">.</span><span class=\"token function\">runGuarded</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    Future<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span> key<span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">// 先验证是否已经有缓存</span>\n    <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">// 生成缓存key，后面会根据此key来检测是否有缓存</span>\n      key <span class=\"token operator\">=</span> <span class=\"token function\">obtainKey</span><span class=\"token punctuation\">(</span>configuration<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span>error<span class=\"token punctuation\">,</span> stackTrace<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">handleError</span><span class=\"token punctuation\">(</span>error<span class=\"token punctuation\">,</span> stackTrace<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">return</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    key<span class=\"token punctuation\">.</span>then<span class=\"token operator\">&lt;</span><span class=\"token keyword\">void</span><span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>T key<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      obtainedKey <span class=\"token operator\">=</span> key<span class=\"token punctuation\">;</span>\n      <span class=\"token comment\">// 缓存的处理逻辑在这里，记为A，下面详细介绍</span>\n      <span class=\"token keyword\">final</span> ImageStreamCompleter completer <span class=\"token operator\">=</span> PaintingBinding<span class=\"token punctuation\">.</span>instance\n          <span class=\"token punctuation\">.</span>imageCache<span class=\"token punctuation\">.</span><span class=\"token function\">putIfAbsent</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">load</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> onError<span class=\"token punctuation\">:</span> handleError<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>completer <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        stream<span class=\"token punctuation\">.</span><span class=\"token function\">setCompleter</span><span class=\"token punctuation\">(</span>completer<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">catchError</span><span class=\"token punctuation\">(</span>handleError<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">return</span> stream<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p><code>ImageConfiguration</code>  包含图片和设备的相关信息，如图片的大小、所在的<code>AssetBundle</code>(只有打到安装包的图片存在)以及当前的设备平台、devicePixelRatio（设备像素比等）。Flutter SDK提供了一个便捷函数<code>createLocalImageConfiguration</code>来创建<code>ImageConfiguration</code>  对象：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>ImageConfiguration <span class=\"token function\">createLocalImageConfiguration</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> Size size <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">ImageConfiguration</span><span class=\"token punctuation\">(</span>\n    bundle<span class=\"token punctuation\">:</span> DefaultAssetBundle<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    devicePixelRatio<span class=\"token punctuation\">:</span> MediaQuery<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> nullOk<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token operator\">?</span><span class=\"token punctuation\">.</span>devicePixelRatio <span class=\"token operator\">?</span><span class=\"token operator\">?</span> <span class=\"token number\">1.0</span><span class=\"token punctuation\">,</span>\n    locale<span class=\"token punctuation\">:</span> Localizations<span class=\"token punctuation\">.</span><span class=\"token function\">localeOf</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> nullOk<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    textDirection<span class=\"token punctuation\">:</span> Directionality<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    size<span class=\"token punctuation\">:</span> size<span class=\"token punctuation\">,</span>\n    platform<span class=\"token punctuation\">:</span> defaultTargetPlatform<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们可以发现这些信息基本都是通过<code>Context</code>来获取。</p> <p>上面代码A处就是处理缓存的主要代码，这里的<code>PaintingBinding.instance.imageCache</code> 是 <code>ImageCache</code>的一个实例，它是<code>PaintingBinding</code>的一个属性，而Flutter框架中的<code>PaintingBinding.instance</code>是一个单例，<code>imageCache</code>事实上也是一个单例，也就是说图片缓存是全局的，统一由<code>PaintingBinding.instance.imageCache</code> 来管理。</p> <p>下面我们看看<code>ImageCache</code>类定义：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">const</span> int _kDefaultSize <span class=\"token operator\">=</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">const</span> int _kDefaultSizeBytes <span class=\"token operator\">=</span> <span class=\"token number\">100</span> <span class=\"token operator\">&lt;&lt;</span> <span class=\"token number\">20</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// 100 MiB</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">ImageCache</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// 正在加载中的图片队列</span>\n  <span class=\"token keyword\">final</span> Map<span class=\"token operator\">&lt;</span>Object<span class=\"token punctuation\">,</span> _PendingImage<span class=\"token operator\">&gt;</span> _pendingImages <span class=\"token operator\">=</span> <span class=\"token operator\">&lt;</span>Object<span class=\"token punctuation\">,</span> _PendingImage<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">// 缓存队列</span>\n  <span class=\"token keyword\">final</span> Map<span class=\"token operator\">&lt;</span>Object<span class=\"token punctuation\">,</span> _CachedImage<span class=\"token operator\">&gt;</span> _cache <span class=\"token operator\">=</span> <span class=\"token operator\">&lt;</span>Object<span class=\"token punctuation\">,</span> _CachedImage<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// 缓存数量上限(1000)</span>\n  int _maximumSize <span class=\"token operator\">=</span> _kDefaultSize<span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">// 缓存容量上限 (100 MB)</span>\n  int _maximumSizeBytes <span class=\"token operator\">=</span> _kDefaultSizeBytes<span class=\"token punctuation\">;</span>\n  \n  <span class=\"token comment\">// 缓存上限设置的setter</span>\n  <span class=\"token keyword\">set</span> <span class=\"token function\">maximumSize</span><span class=\"token punctuation\">(</span>int value<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">}</span>\n  <span class=\"token keyword\">set</span> <span class=\"token function\">maximumSizeBytes</span><span class=\"token punctuation\">(</span>int value<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">}</span>\n \n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">// 省略部分定义</span>\n\n  <span class=\"token comment\">// 清除所有缓存</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">clear</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// ...省略具体实现代码</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">// 清除指定key对应的图片缓存</span>\n  bool <span class=\"token function\">evict</span><span class=\"token punctuation\">(</span>Object key<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n   <span class=\"token comment\">// ...省略具体实现代码</span>\n  <span class=\"token punctuation\">}</span>\n\n \n  ImageStreamCompleter <span class=\"token function\">putIfAbsent</span><span class=\"token punctuation\">(</span>Object key<span class=\"token punctuation\">,</span> ImageStreamCompleter <span class=\"token function\">loader</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> ImageErrorListener onError <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span>key <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span>loader <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    ImageStreamCompleter result <span class=\"token operator\">=</span> _pendingImages<span class=\"token punctuation\">[</span>key<span class=\"token punctuation\">]</span><span class=\"token operator\">?</span><span class=\"token punctuation\">.</span>completer<span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">// 图片还未加载成功，直接返回</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>result <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span>\n      <span class=\"token keyword\">return</span> result<span class=\"token punctuation\">;</span>\n \n    <span class=\"token comment\">// 有缓存，继续往下走</span>\n    <span class=\"token comment\">// 先移除缓存，后再添加，可以让最新使用过的缓存在_map中的位置更近一些，清理时会LRU来清除</span>\n    <span class=\"token keyword\">final</span> _CachedImage image <span class=\"token operator\">=</span> _cache<span class=\"token punctuation\">.</span><span class=\"token function\">remove</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>image <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _cache<span class=\"token punctuation\">[</span>key<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> image<span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">return</span> image<span class=\"token punctuation\">.</span>completer<span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n      result <span class=\"token operator\">=</span> <span class=\"token function\">loader</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span>error<span class=\"token punctuation\">,</span> stackTrace<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>onError <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">onError</span><span class=\"token punctuation\">(</span>error<span class=\"token punctuation\">,</span> stackTrace<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">return</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">rethrow</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">void</span> <span class=\"token function\">listener</span><span class=\"token punctuation\">(</span>ImageInfo info<span class=\"token punctuation\">,</span> bool syncCall<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">final</span> int imageSize <span class=\"token operator\">=</span> info<span class=\"token operator\">?</span><span class=\"token punctuation\">.</span>image <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">?</span> <span class=\"token number\">0</span> <span class=\"token punctuation\">:</span> info<span class=\"token punctuation\">.</span>image<span class=\"token punctuation\">.</span>height <span class=\"token operator\">*</span> info<span class=\"token punctuation\">.</span>image<span class=\"token punctuation\">.</span>width <span class=\"token operator\">*</span> <span class=\"token number\">4</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">final</span> _CachedImage image <span class=\"token operator\">=</span> <span class=\"token function\">_CachedImage</span><span class=\"token punctuation\">(</span>result<span class=\"token punctuation\">,</span> imageSize<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token comment\">// 下面是缓存处理的逻辑</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>maximumSizeBytes <span class=\"token operator\">&gt;</span> <span class=\"token number\">0</span> <span class=\"token operator\">&amp;&amp;</span> imageSize <span class=\"token operator\">&gt;</span> maximumSizeBytes<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        _maximumSizeBytes <span class=\"token operator\">=</span> imageSize <span class=\"token operator\">+</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n      _currentSizeBytes <span class=\"token operator\">+=</span> imageSize<span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">final</span> _PendingImage pendingImage <span class=\"token operator\">=</span> _pendingImages<span class=\"token punctuation\">.</span><span class=\"token function\">remove</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>pendingImage <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        pendingImage<span class=\"token punctuation\">.</span><span class=\"token function\">removeListener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n\n      _cache<span class=\"token punctuation\">[</span>key<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> image<span class=\"token punctuation\">;</span>\n      <span class=\"token function\">_checkCacheSize</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>maximumSize <span class=\"token operator\">&gt;</span> <span class=\"token number\">0</span> <span class=\"token operator\">&amp;&amp;</span> maximumSizeBytes <span class=\"token operator\">&gt;</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">final</span> ImageStreamListener streamListener <span class=\"token operator\">=</span> <span class=\"token function\">ImageStreamListener</span><span class=\"token punctuation\">(</span>listener<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      _pendingImages<span class=\"token punctuation\">[</span>key<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">_PendingImage</span><span class=\"token punctuation\">(</span>result<span class=\"token punctuation\">,</span> streamListener<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token comment\">// Listener is removed in [_PendingImage.removeListener].</span>\n      result<span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span>streamListener<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">return</span> result<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">// 当缓存数量超过最大值或缓存的大小超过最大缓存容量，会调用此方法清理到缓存上限以内</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_checkCacheSize</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n   <span class=\"token keyword\">while</span> <span class=\"token punctuation\">(</span>_currentSizeBytes <span class=\"token operator\">&gt;</span> _maximumSizeBytes <span class=\"token operator\">||</span> _cache<span class=\"token punctuation\">.</span>length <span class=\"token operator\">&gt;</span> _maximumSize<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">final</span> Object key <span class=\"token operator\">=</span> _cache<span class=\"token punctuation\">.</span>keys<span class=\"token punctuation\">.</span>first<span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">final</span> _CachedImage image <span class=\"token operator\">=</span> _cache<span class=\"token punctuation\">[</span>key<span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n      _currentSizeBytes <span class=\"token operator\">-=</span> image<span class=\"token punctuation\">.</span>sizeBytes<span class=\"token punctuation\">;</span>\n      _cache<span class=\"token punctuation\">.</span><span class=\"token function\">remove</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>有缓存则使用缓存，没有缓存则调用load方法加载图片，加载成功后:</p> <ol><li>先判断图片数据有没有缓存，如果有，则直接返回<code>ImageStream</code>。</li> <li>如果没有缓存，则调用<code>load(T key)</code>方法从数据源加载图片数据，加载成功后先缓存，然后返回ImageStream。</li></ol> <p>另外，我们可以看到<code>ImageCache</code>类中有设置缓存上限的setter，所以，如果我们可以自定义缓存上限：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code> PaintingBinding<span class=\"token punctuation\">.</span>instance<span class=\"token punctuation\">.</span>imageCache<span class=\"token punctuation\">.</span>maximumSize<span class=\"token operator\">=</span><span class=\"token number\">2000</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//最多2000张</span>\n PaintingBinding<span class=\"token punctuation\">.</span>instance<span class=\"token punctuation\">.</span>imageCache<span class=\"token punctuation\">.</span>maximumSizeBytes <span class=\"token operator\">=</span> <span class=\"token number\">200</span> <span class=\"token operator\">&lt;&lt;</span> <span class=\"token number\">20</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//最大200M</span>\n</code></pre></div><p>现在我们看一下缓存的key，因为Map中相同key的值会被覆盖，也就是说key是图片缓存的一个唯一标识，只要是不同key，那么图片数据就会分别缓存（即使事实上是同一张图片）。那么图片的唯一标识是什么呢？跟踪源码，很容易发现key正是<code>ImageProvider.obtainKey()</code>方法的返回值，而此方法需要<code>ImageProvider</code>子类去重写，这也就意味着不同的<code>ImageProvider</code>对key的定义逻辑会不同。其实也很好理解，比如对于<code>NetworkImage</code>，将图片的url作为key会很合适，而对于<code>AssetImage</code>，则应该将“包名+路径”作为唯一的key。下面我们以<code>NetworkImage</code>为例，看一下它的<code>obtainKey()</code>实现：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token metadata symbol\">@override</span>\nFuture<span class=\"token operator\">&lt;</span>NetworkImage<span class=\"token operator\">&gt;</span> <span class=\"token function\">obtainKey</span><span class=\"token punctuation\">(</span>image_provider<span class=\"token punctuation\">.</span>ImageConfiguration configuration<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> SynchronousFuture<span class=\"token operator\">&lt;</span>NetworkImage<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>代码很简单，创建了一个同步的future，然后直接将自身做为key返回。因为Map中在判断key（此时是<code>NetworkImage</code>对象）是否相等时会使用“==”运算符，那么定义key的逻辑就是<code>NetworkImage</code>的“==”运算符：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token metadata symbol\">@override</span>\nbool <span class=\"token keyword\">operator</span> <span class=\"token operator\">==</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">dynamic</span> other<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n  <span class=\"token keyword\">final</span> NetworkImage typedOther <span class=\"token operator\">=</span> other<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">return</span> url <span class=\"token operator\">==</span> typedOther<span class=\"token punctuation\">.</span>url\n      <span class=\"token operator\">&amp;&amp;</span> scale <span class=\"token operator\">==</span> typedOther<span class=\"token punctuation\">.</span>scale<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>很清晰，对于网络图片来说，会将其“url+缩放比例”作为缓存的key。也就是说<strong>如果两张图片的url或scale只要有一个不同，便会重新下载并分别缓存</strong>。</p> <p>另外，我们需要注意的是，图片缓存是在内存中，并没有进行本地文件持久化存储，这也是为什么网络图片在应用重启后需要重新联网下载的原因。</p> <p>同时也意味着在应用生命周期内，如果缓存没有超过上限，相同的图片只会被下载一次。</p> <h3 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h3> <p>上面主要结合源码，探索了<code>ImageProvider</code>的主要功能和原理，如果要用一句话来总结<code>ImageProvider</code>功能，那么应该是：加载图片数据并进行缓存、解码。在此再次提醒读者，Flutter的源码是非常好的第一手资料，建议读者多多探索，另外，在阅读源码学习的同时一定要有总结，这样才不至于在源码中迷失。</p> <h2 id=\"_14-5-2-image组件原理\"><a href=\"#_14-5-2-image组件原理\" class=\"header-anchor\">#</a> 14.5.2 Image组件原理</h2> <p>前面章节中我们介绍过<code>Image</code>的基础用法，现在我们更深入一些，研究一下<code>Image</code>是如何和<code>ImageProvider</code>配合来获取最终解码后的数据，然后又如何将图片绘制到屏幕上的。</p> <p>本节换一个思路，我们先不去直接看<code>Image</code>的源码，而根据已经掌握的知识来实现一个简版的“<code>Image</code>组件” <code>MyImage</code>，代码大致如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">MyImage</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function\">MyImage</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n    <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>imageProvider<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">:</span> <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span>imageProvider <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> ImageProvider imageProvider<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  _MyImageState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">_MyImageState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_MyImageState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>MyImage<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  ImageStream _imageStream<span class=\"token punctuation\">;</span>\n  ImageInfo _imageInfo<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">didChangeDependencies</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">didChangeDependencies</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">// 依赖改变时，图片的配置信息可能会发生改变</span>\n    <span class=\"token function\">_getImage</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">didUpdateWidget</span><span class=\"token punctuation\">(</span>MyImage oldWidget<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">didUpdateWidget</span><span class=\"token punctuation\">(</span>oldWidget<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>widget<span class=\"token punctuation\">.</span>imageProvider <span class=\"token operator\">!=</span> oldWidget<span class=\"token punctuation\">.</span>imageProvider<span class=\"token punctuation\">)</span>\n      <span class=\"token function\">_getImage</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_getImage</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">final</span> ImageStream oldImageStream <span class=\"token operator\">=</span> _imageStream<span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">// 调用imageProvider.resolve方法，获得ImageStream。</span>\n    _imageStream <span class=\"token operator\">=</span>\n        widget<span class=\"token punctuation\">.</span>imageProvider<span class=\"token punctuation\">.</span><span class=\"token function\">resolve</span><span class=\"token punctuation\">(</span><span class=\"token function\">createLocalImageConfiguration</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//判断新旧ImageStream是否相同，如果不同，则需要调整流的监听器</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>_imageStream<span class=\"token punctuation\">.</span>key <span class=\"token operator\">!=</span> oldImageStream<span class=\"token operator\">?</span><span class=\"token punctuation\">.</span>key<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">final</span> ImageStreamListener listener <span class=\"token operator\">=</span> <span class=\"token function\">ImageStreamListener</span><span class=\"token punctuation\">(</span>_updateImage<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      oldImageStream<span class=\"token operator\">?</span><span class=\"token punctuation\">.</span><span class=\"token function\">removeListener</span><span class=\"token punctuation\">(</span>listener<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      _imageStream<span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span>listener<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_updateImage</span><span class=\"token punctuation\">(</span>ImageInfo imageInfo<span class=\"token punctuation\">,</span> bool synchronousCall<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">// Trigger a build whenever the image changes.</span>\n      _imageInfo <span class=\"token operator\">=</span> imageInfo<span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    _imageStream<span class=\"token punctuation\">.</span><span class=\"token function\">removeListener</span><span class=\"token punctuation\">(</span><span class=\"token function\">ImageStreamListener</span><span class=\"token punctuation\">(</span>_updateImage<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">RawImage</span><span class=\"token punctuation\">(</span>\n      image<span class=\"token punctuation\">:</span> _imageInfo<span class=\"token operator\">?</span><span class=\"token punctuation\">.</span>image<span class=\"token punctuation\">,</span> <span class=\"token comment\">// this is a dart:ui Image object</span>\n      scale<span class=\"token punctuation\">:</span> _imageInfo<span class=\"token operator\">?</span><span class=\"token punctuation\">.</span>scale <span class=\"token operator\">?</span><span class=\"token operator\">?</span> <span class=\"token number\">1.0</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面代码流程如下：</p> <ol><li>通过<code>imageProvider.resolve</code>方法可以得到一个<code>ImageStream</code>（图片数据流），然后监听<code>ImageStream</code>的变化。当图片数据源发生变化时，<code>ImageStream</code>会触发相应的事件，而本例中我们只设置了图片成功的监听器<code>_updateImage</code>，而<code>_updateImage</code>中只更新了<code>_imageInfo</code>。值得注意的是，如果是静态图，<code>ImageStream</code>只会触发一次时间，如果是动态图，则会触发多次事件，每一次都会有一个解码后的图片帧。</li> <li><code>_imageInfo</code> 更新后会rebuild，此时会创建一个<code>RawImage</code> Widget。<code>RawImage</code>最终会通过<code>RenderImage</code>来将图片绘制在屏幕上。如果继续跟进<code>RenderImage</code>类，我们会发现<code>RenderImage</code>的<code>paint</code> 方法中调用了<code>paintImage</code>方法，而<code>paintImage</code>方法中通过<code>Canvas</code>的<code>drawImageRect(…)</code>、<code>drawImageNine(...)</code>等方法来完成最终的绘制。</li> <li>最终的绘制由<code>RawImage</code>来完成。</li></ol> <p>下面测试一下<code>MyImage</code>：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">ImageInternalTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n      children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n        <span class=\"token function\">MyImage</span><span class=\"token punctuation\">(</span>\n          imageProvider<span class=\"token punctuation\">:</span> <span class=\"token function\">NetworkImage</span><span class=\"token punctuation\">(</span>\n            <span class=\"token string\">&quot;https://avatars2.githubusercontent.com/u/20411648?s=460&amp;v=4&quot;</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行效果如图14-4所示：</p> <p><img src=\"/assets/img/14-4.4a6d698c.png\" alt=\"图14-4\"></p> <p>成功了！ 现在，想必<code>Image</code> Widget的源码已经没必要在花费篇章去介绍了，读者有兴趣可以自行去阅读。</p> <h2 id=\"总结-2\"><a href=\"#总结-2\" class=\"header-anchor\">#</a> 总结</h2> <p>本节主要介绍了Flutter 图片的加载、缓存和绘制流程。其中<code>ImageProvider</code>主要负责图片数据的加载和缓存，而绘制部分逻辑主要是由<code>RawImage</code>来完成。 而<code>Image</code>正是连接起<code>ImageProvider</code>和<code>RawImage</code> 的桥梁。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter14/flutter_app_startup.html\" class=\"prev\">\n        14.4 Flutter运行机制-从启动到显示\n      </a></span> <span class=\"next\"><a href=\"/chapter15/intro.html\">\n        15.1 Github客户端示例\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/91.9eb501f7.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter14/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>本章目录 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/150.220fd8cf.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"本章目录\"><a href=\"#本章目录\" class=\"header-anchor\">#</a> 本章目录</h1> <ul><li><a href=\"/chapter14/flutter_ui_system.html\">Flutter UI系统</a></li> <li><a href=\"/chapter14/element_buildcontext.html\">Element和BuildContext</a></li> <li><a href=\"/chapter14/render_object.html\">RenderObject和RenderBox</a></li> <li><a href=\"/chapter14/flutter_app_startup.html\">Flutter从启动到显示</a></li></ul></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/150.220fd8cf.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter14/render_object.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>14.3 RenderObject和RenderBox | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/92.7b12896a.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable open\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" aria-current=\"page\" class=\"active sidebar-link\">14.3 RenderObject和RenderBox</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/chapter14/render_object.html#_14-3-1-布局过程\" class=\"sidebar-link\">14.3.1 布局过程</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter14/render_object.html#_14-3-2-绘制过程\" class=\"sidebar-link\">14.3.2 绘制过程</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter14/render_object.html#_14-3-3-命中测试\" class=\"sidebar-link\">14.3.3 命中测试</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter14/render_object.html#_14-3-4-语义化\" class=\"sidebar-link\">14.3.4 语义化</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter14/render_object.html#_14-3-5-总结\" class=\"sidebar-link\">14.3.5 总结</a></li></ul></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_14-3-renderobject和renderbox\"><a href=\"#_14-3-renderobject和renderbox\" class=\"header-anchor\">#</a> 14.3 RenderObject和RenderBox</h1> <p>在上一节我们说过每个<code>Element</code>都对应一个<code>RenderObject</code>，我们可以通过<code>Element.renderObject</code> 来获取。并且我们也说过<code>RenderObject</code>的主要职责是Layout和绘制，所有的<code>RenderObject</code>会组成一棵渲染树Render Tree。本节我们将重点介绍一下<code>RenderObject</code>的作用。</p> <p><code>RenderObject</code>就是渲染树中的一个对象，它拥有一个<code>parent</code>和一个<code>parentData</code> 插槽（slot），所谓插槽，就是指预留的一个接口或位置，这个接口和位置是由其它对象来接入或占据的，这个接口或位置在软件中通常用预留变量来表示，而<code>parentData</code>正是一个预留变量，它正是由<code>parent</code> 来赋值的，<code>parent</code>通常会通过子<code>RenderObject</code>的<code>parentData</code>存储一些和子元素相关的数据，如在Stack布局中，<code>RenderStack</code>就会将子元素的偏移数据存储在子元素的<code>parentData</code>中（具体可以查看<code>Positioned</code>实现）。</p> <p><code>RenderObject</code>类本身实现了一套基础的layout和绘制协议，但是并没有定义子节点模型（如一个节点可以有几个子节点，没有子节点？一个？两个？或者更多？）。 它也没有定义坐标系统（如子节点定位是在笛卡尔坐标中还是极坐标？）和具体的布局协议（是通过宽高还是通过constraint和size?，或者是否由父节点在子节点布局之前或之后设置子节点的大小和位置等）。为此，Flutter提供了一个<code>RenderBox</code>类，它继承自``RenderObject<code>，布局坐标系统采用笛卡尔坐标系，这和Android和iOS原生坐标系是一致的，都是屏幕的top、left是原点，然后分宽高两个轴，大多数情况下，我们直接使用</code>RenderBox<code>就可以了，除非遇到要自定义布局模型或坐标系统的情况，下面我们重点介绍一下</code>RenderBox`。</p> <h2 id=\"_14-3-1-布局过程\"><a href=\"#_14-3-1-布局过程\" class=\"header-anchor\">#</a> 14.3.1 布局过程</h2> <h3 id=\"constraints\"><a href=\"#constraints\" class=\"header-anchor\">#</a> Constraints</h3> <p>在<code>RenderBox</code> 中，有个<code>size</code>属性用来保存控件的宽和高。<code>RenderBox</code>的layout是通过在组件树中从上往下传递<code>BoxConstraints</code>对象的实现的。<code>BoxConstraints</code>对象可以限制子节点的最大和最小宽高，子节点必须遵守父节点给定的限制条件。</p> <p>在布局阶段，父节点会调用子节点的<code>layout()</code>方法，下面我们看看<code>RenderObject</code>中<code>layout()</code>方法的大致实现（删掉了一些无关代码和异常捕获）:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">layout</span><span class=\"token punctuation\">(</span>Constraints constraints<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> bool parentUsesSize <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n   <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n   RenderObject relayoutBoundary<span class=\"token punctuation\">;</span> \n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>parentUsesSize <span class=\"token operator\">||</span> sizedByParent <span class=\"token operator\">||</span> constraints<span class=\"token punctuation\">.</span>isTight \n    \t<span class=\"token operator\">||</span> parent <span class=\"token operator\">is!</span> RenderObject<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      relayoutBoundary <span class=\"token operator\">=</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">final</span> RenderObject parent <span class=\"token operator\">=</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>parent<span class=\"token punctuation\">;</span>\n      relayoutBoundary <span class=\"token operator\">=</span> parent<span class=\"token punctuation\">.</span>_relayoutBoundary<span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>sizedByParent<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">performResize</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token function\">performLayout</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以看到<code>layout</code>方法需要传入两个参数，第一个为<code>constraints</code>，即 父节点对子节点大小的限制，该值根据父节点的布局逻辑确定。另外一个参数是 <code>parentUsesSize</code>，该值用于确定 <code>relayoutBoundary</code>，该参数表示子节点布局变化是否影响父节点，如果为<code>true</code>，当子节点布局发生变化时父节点都会标记为需要重新布局，如果为<code>false</code>，则子节点布局发生变化后不会影响父节点。</p> <h4 id=\"relayoutboundary\"><a href=\"#relayoutboundary\" class=\"header-anchor\">#</a> relayoutBoundary</h4> <p>上面<code>layout()</code>源码中定义了一个<code>relayoutBoundary</code>变量，什么是 <code>relayoutBoundary</code>？在前面介绍<code>Element</code>时，我们讲过当一个<code>Element</code>标记为 dirty 时便会重新build，这时<code>RenderObject</code>便会重新布局，我们是通过调用 <code>markNeedsBuild()</code> 来标记<code>Element</code>为dirty的。在<code>RenderObject</code>中有一个类似的<code>markNeedsLayout()</code>方法，它会将<code>RenderObject</code>的布局状态标记为 dirty，这样在下一个frame中便会重新layout，我们看看<code>RenderObject</code>的<code>markNeedsLayout()</code>的部分源码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">markNeedsLayout</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span>_relayoutBoundary <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>_relayoutBoundary <span class=\"token operator\">!=</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">markParentNeedsLayout</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n    _needsLayout <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>owner <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n      owner<span class=\"token punctuation\">.</span>_nodesNeedingLayout<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      owner<span class=\"token punctuation\">.</span><span class=\"token function\">requestVisualUpdate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>代码大致逻辑是先判断自身是不是<code>relayoutBoundary</code>，如果不是就继续向parent 查找，一直向上查找到是 <code>relayoutBoundary</code> 的 <code>RenderObject</code>为止，然后再将其标记为 dirty 的。这样来看它的作用就比较明显了，意思就是当一个控件的大小被改变时可能会影响到它的 parent，因此 parent 也需要被重新布局，那么到什么时候是个头呢？答案就是 <code>relayoutBoundary</code>，如果一个 <code>RenderObject</code> 是 <code>relayoutBoundary</code>，就表示它的大小变化不会再影响到 parent 的大小了，于是 parent 也就不用重新布局了。</p> <h4 id=\"performresize-和-performlayout\"><a href=\"#performresize-和-performlayout\" class=\"header-anchor\">#</a> performResize 和 performLayout</h4> <p><code>RenderBox</code>实际的测量和布局逻辑是在<code>performResize()</code> 和 <code>performLayout()</code>两个方法中，RenderBox子类需要实现这两个方法来定制自身的布局逻辑。根据<code>layout()</code> 源码可以看出只有 <code>sizedByParent</code> 为 <code>true</code> 时，<code>performResize()</code> 才会被调用，而 <code>performLayout()</code> 是每次布局都会被调用的。<code>sizedByParent</code> 意为该节点的大小是否仅通过 parent 传给它的 constraints 就可以确定了，即该节点的大小与它自身的属性和其子节点无关，比如如果一个控件永远充满 parent 的大小，那么 <code>sizedByParent</code>就应该返回<code>true</code>，此时其大小在 <code>performResize()</code> 中就确定了，在后面的 <code>performLayout()</code> 方法中将不会再被修改了，这种情况下 <code>performLayout()</code> 只负责布局子节点。</p> <p>在 <code>performLayout()</code> 方法中除了完成自身布局，也必须完成子节点的布局，这是因为只有父子节点全部完成后布局流程才算真正完成。所以最终的调用栈将会变成：<em>layout() &gt; performResize()/performLayout() &gt; child.layout() &gt; ...</em>  ，如此递归完成整个UI的布局。</p> <p><code>RenderBox</code>子类要定制布局算法不应该重写<code>layout()</code>方法，因为对于任何RenderBox的子类来说，它的layout流程基本是相同的，不同之处只在具体的布局算法，而具体的布局算法子类应该通过重写<code>performResize()</code> 和 <code>performLayout()</code>两个方法来实现，他们会在<code>layout()</code>中被调用。</p> <h4 id=\"parentdata\"><a href=\"#parentdata\" class=\"header-anchor\">#</a> ParentData</h4> <p>当layout结束后，每个节点的位置（相对于父节点的偏移）就已经确定了，<code>RenderObject</code>就可以根据位置信息来进行最终的绘制。但是在layout过程中，节点的位置信息怎么保存？对于大多数<code>RenderBox</code>子类来说如果子类只有一个子节点，那么子节点偏移一般都是<code>Offset.zero</code> ，如果有多个子节点，则每个子节点的偏移就可能不同。而子节点在父节点的偏移数据正是通过<code>RenderObject</code>的<code>parentData</code>属性来保存的。在<code>RenderBox</code>中，其<code>parentData</code>属性默认是一个<code>BoxParentData</code>对象，该属性只能通过父节点的<code>setupParentData()</code>方法来设置：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">abstract</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">RenderBox</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">RenderObject</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">setupParentData</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">covariant</span> RenderObject child<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">.</span>parentData <span class=\"token operator\">is!</span> BoxParentData<span class=\"token punctuation\">)</span>\n      child<span class=\"token punctuation\">.</span>parentData <span class=\"token operator\">=</span> <span class=\"token function\">BoxParentData</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p><code>BoxParentData</code>定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">/// Parentdata 会被RenderBox和它的子类使用.</span>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">BoxParentData</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">ParentData</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">/// offset表示在子节点在父节点坐标系中的绘制偏移  </span>\n  Offset offset <span class=\"token operator\">=</span> Offset<span class=\"token punctuation\">.</span>zero<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  String <span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token string\">'offset=$offset'</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><blockquote><p>一定要注意，<code>RenderObject</code>的<code>parentData</code> 只能通过父元素设置.</p></blockquote> <p>当然，<code>ParentData</code>并不仅仅可以用来存储偏移信息，通常所有和子节点特定的数据都可以存储到子节点的<code>ParentData</code>中，如<code>ContainerBox</code>的<code>ParentData</code>就保存了指向兄弟节点的<code>previousSibling</code>和<code>nextSibling</code>，<code>Element.visitChildren()</code>方法也正是通过它们来实现对子节点的遍历。再比如<code>KeepAlive</code> 组件，它使用<code>KeepAliveParentDataMixin</code>（继承自<code>ParentData</code>） 来保存子节的<code>keepAlive</code>状态。</p> <h2 id=\"_14-3-2-绘制过程\"><a href=\"#_14-3-2-绘制过程\" class=\"header-anchor\">#</a> 14.3.2 绘制过程</h2> <p><code>RenderObject</code>可以通过<code>paint()</code>方法来完成具体绘制逻辑，流程和布局流程相似，子类可以实现<code>paint()</code>方法来完成自身的绘制逻辑，<code>paint()</code>签名如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">paint</span><span class=\"token punctuation\">(</span>PaintingContext context<span class=\"token punctuation\">,</span> Offset offset<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span> <span class=\"token punctuation\">}</span>\n</code></pre></div><p>通过<code>context.canvas</code>可以取到<code>Canvas</code>对象，接下来就可以调用<code>Canvas</code> API来实现具体的绘制逻辑。</p> <p>如果节点有子节点，它除了完成自身绘制逻辑之外，还要调用子节点的绘制方法。我们以<code>RenderFlex</code>对象为例说明：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token metadata symbol\">@override</span>\n<span class=\"token keyword\">void</span> <span class=\"token function\">paint</span><span class=\"token punctuation\">(</span>PaintingContext context<span class=\"token punctuation\">,</span> Offset offset<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n\n  <span class=\"token comment\">// 如果子元素未超出当前边界，则绘制子元素  </span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>_overflow <span class=\"token operator\">&lt;=</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">defaultPaint</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> offset<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">// 如果size为空，则无需绘制</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>size<span class=\"token punctuation\">.</span>isEmpty<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// 剪裁掉溢出边界的部分</span>\n  context<span class=\"token punctuation\">.</span><span class=\"token function\">pushClipRect</span><span class=\"token punctuation\">(</span>needsCompositing<span class=\"token punctuation\">,</span> offset<span class=\"token punctuation\">,</span> Offset<span class=\"token punctuation\">.</span>zero <span class=\"token operator\">&amp;</span> size<span class=\"token punctuation\">,</span> defaultPaint<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">final</span> String debugOverflowHints <span class=\"token operator\">=</span> <span class=\"token string\">'...'</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//溢出提示内容，省略</span>\n    <span class=\"token comment\">// 绘制溢出部分的错误提示样式</span>\n    Rect overflowChildRect<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">switch</span> <span class=\"token punctuation\">(</span>_direction<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">case</span> Axis<span class=\"token punctuation\">.</span>horizontal<span class=\"token punctuation\">:</span>\n        overflowChildRect <span class=\"token operator\">=</span> Rect<span class=\"token punctuation\">.</span><span class=\"token function\">fromLTWH</span><span class=\"token punctuation\">(</span><span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span> size<span class=\"token punctuation\">.</span>width <span class=\"token operator\">+</span> _overflow<span class=\"token punctuation\">,</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">case</span> Axis<span class=\"token punctuation\">.</span>vertical<span class=\"token punctuation\">:</span>\n        overflowChildRect <span class=\"token operator\">=</span> Rect<span class=\"token punctuation\">.</span><span class=\"token function\">fromLTWH</span><span class=\"token punctuation\">(</span><span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span> size<span class=\"token punctuation\">.</span>height <span class=\"token operator\">+</span> _overflow<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>  \n    <span class=\"token function\">paintOverflowIndicator</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> offset<span class=\"token punctuation\">,</span> Offset<span class=\"token punctuation\">.</span>zero <span class=\"token operator\">&amp;</span> size<span class=\"token punctuation\">,</span>\n                           overflowChildRect<span class=\"token punctuation\">,</span> overflowHints<span class=\"token punctuation\">:</span> debugOverflowHints<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>代码很简单，首先判断有无溢出，如果没有则调用<code>defaultPaint(context, offset)</code>来完成绘制，该方法源码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">defaultPaint</span><span class=\"token punctuation\">(</span>PaintingContext context<span class=\"token punctuation\">,</span> Offset offset<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  ChildType child <span class=\"token operator\">=</span> firstChild<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">while</span> <span class=\"token punctuation\">(</span>child <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">final</span> ParentDataType childParentData <span class=\"token operator\">=</span> child<span class=\"token punctuation\">.</span>parentData<span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//绘制子节点， </span>\n    context<span class=\"token punctuation\">.</span><span class=\"token function\">paintChild</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">,</span> childParentData<span class=\"token punctuation\">.</span>offset <span class=\"token operator\">+</span> offset<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    child <span class=\"token operator\">=</span> childParentData<span class=\"token punctuation\">.</span>nextSibling<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>很明显，由于Flex本身没有需要绘制的东西，所以直接遍历其子节点，然后调用<code>paintChild()</code>来绘制子节点，同时将子节点<code>ParentData</code>中在layout阶段保存的offset加上自身偏移作为第二个参数传递给<code>paintChild()</code>。而如果子节点还有子节点时，<code>paintChild()</code>方法还会调用子节点的<code>paint()</code>方法，如此递归完成整个节点树的绘制，最终调用栈为： <em>paint() &gt; paintChild() &gt; paint() ...</em> 。</p> <p>当需要绘制的内容大小溢出当前空间时，将会执行<code>paintOverflowIndicator()</code> 来绘制溢出部分提示，这个就是我们经常看到的溢出提示，如图14-3所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"overflow\"></p> <h3 id=\"repaintboundary\"><a href=\"#repaintboundary\" class=\"header-anchor\">#</a> RepaintBoundary</h3> <p>我们已经在<code>CustomPaint</code>一节中介绍过<code>RepaintBoundary</code>，现在我们深入的了解一些。与 <code>RelayoutBoundary</code> 相似，<code>RepaintBoundary</code>是用于在确定重绘边界的，与<code>RelayoutBoundary</code>不同的是，这个绘制边界需要由开发者通过<code>RepaintBoundary</code> 组件自己指定，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">CustomPaint</span><span class=\"token punctuation\">(</span>\n  size<span class=\"token punctuation\">:</span> <span class=\"token function\">Size</span><span class=\"token punctuation\">(</span><span class=\"token number\">300</span><span class=\"token punctuation\">,</span> <span class=\"token number\">300</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//指定画布大小</span>\n  painter<span class=\"token punctuation\">:</span> <span class=\"token function\">MyPainter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">RepaintBoundary</span><span class=\"token punctuation\">(</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</code></pre></div><p>下面我们看看<code>RepaintBoundary</code>的原理，<code>RenderObject</code>有一个<code>isRepaintBoundary</code>属性，该属性决定这个<code>RenderObject</code>重绘时是否独立于其父元素，如果该属性值为<code>true</code> ，则独立绘制，反之则一起绘制。那独立绘制是怎么实现的呢？ 答案就在<code>paintChild()</code>源码中：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">paintChild</span><span class=\"token punctuation\">(</span>RenderObject child<span class=\"token punctuation\">,</span> Offset offset<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">.</span>isRepaintBoundary<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">stopRecordingIfNeeded</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token function\">_compositeChild</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">,</span> offset<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n    child<span class=\"token punctuation\">.</span><span class=\"token function\">_paintWithContext</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">,</span> offset<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们可以看到，在绘制子节点时，如果<code>child.isRepaintBoundary</code> 为 <code>true</code>则会调用<code>_compositeChild()</code>方法，<code>_compositeChild()</code>源码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">_compositeChild</span><span class=\"token punctuation\">(</span>RenderObject child<span class=\"token punctuation\">,</span> Offset offset<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// 给子节点创建一个layer ，然后再上面绘制子节点 </span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">.</span>_needsPaint<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">repaintCompositedChild</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">,</span> debugAlsoPaintedParent<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">.</span>_layer <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  child<span class=\"token punctuation\">.</span>_layer<span class=\"token punctuation\">.</span>offset <span class=\"token operator\">=</span> offset<span class=\"token punctuation\">;</span>\n  <span class=\"token function\">appendLayer</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">.</span>_layer<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>很明显了，独立绘制是通过在不同的layer（层）上绘制的。所以，很明显，正确使用<code>isRepaintBoundary</code>属性可以提高绘制效率，避免不必要的重绘。具体原理是：和触发重新build和layout类似，<code>RenderObject</code>也提供了一个<code>markNeedsPaint()</code>方法，其源码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">markNeedsPaint</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token comment\">//如果RenderObject.isRepaintBoundary 为true,则该RenderObject拥有layer，直接绘制  </span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>isRepaintBoundary<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>owner <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">//找到最近的layer，绘制  </span>\n      owner<span class=\"token punctuation\">.</span>_nodesNeedingPaint<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      owner<span class=\"token punctuation\">.</span><span class=\"token function\">requestVisualUpdate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>parent <span class=\"token operator\">is</span> RenderObject<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 没有自己的layer, 会和一个祖先节点共用一个layer  </span>\n    <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span>_layer <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">final</span> RenderObject parent <span class=\"token operator\">=</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>parent<span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">// 向父级递归查找  </span>\n    parent<span class=\"token punctuation\">.</span><span class=\"token function\">markNeedsPaint</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span>parent <span class=\"token operator\">==</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>parent<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 如果直到根节点也没找到一个Layer，那么便需要绘制自身，因为没有其它节点可以绘制根节点。  </span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>owner <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span>\n      owner<span class=\"token punctuation\">.</span><span class=\"token function\">requestVisualUpdate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以看出，当调用 <code>markNeedsPaint()</code> 方法时，会从当前 <code>RenderObject</code> 开始一直向父节点查找，直到找到 一个<code>isRepaintBoundary</code> 为 <code>true</code>的<code>RenderObject</code> 时，才会触发重绘，这样便可以实现局部重绘。当 有<code>RenderObject</code> 绘制的很频繁或很复杂时，可以通过RepaintBoundary Widget来指定<code>isRepaintBoundary</code> 为 <code>true</code>，这样在绘制时仅会重绘自身而无需重绘它的 parent，如此便可提高性能。</p> <p>还有一个问题，通过<code>RepaintBoundary</code> 如何设置<code>isRepaintBoundary</code>属性呢？其实，如果使用了<code>RepaintBoundary</code>，其对应的<code>RenderRepaintBoundary</code>会自动将<code>isRepaintBoundary</code>设为<code>true</code>的：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">RenderRepaintBoundary</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">RenderProxyBox</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">/// Creates a repaint boundary around [child].</span>\n  <span class=\"token function\">RenderRepaintBoundary</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> RenderBox child <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  bool <span class=\"token keyword\">get</span> isRepaintBoundary <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h2 id=\"_14-3-3-命中测试\"><a href=\"#_14-3-3-命中测试\" class=\"header-anchor\">#</a> 14.3.3 命中测试</h2> <p>我们在“事件处理与通知”一章中已经讲过Flutter事件机制和命中测试流程，本节我们看一下其内部实现原理。</p> <p>一个对象是否可以响应事件，取决于其对命中测试的返回，当发生用户事件时，会从根节点（<code>RenderView</code>）开始进行命中测试，下面是<code>RenderView</code>的<code>hitTest()</code>源码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>bool <span class=\"token function\">hitTest</span><span class=\"token punctuation\">(</span>HitTestResult result<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> Offset position <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>child <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span>\n    child<span class=\"token punctuation\">.</span><span class=\"token function\">hitTest</span><span class=\"token punctuation\">(</span>result<span class=\"token punctuation\">,</span> position<span class=\"token punctuation\">:</span> position<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//递归子RenderBox进行命中测试</span>\n  result<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token function\">HitTestEntry</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//将测试结果添加到result中</span>\n  <span class=\"token keyword\">return</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们再看看<code>RenderBox</code>默认的<code>hitTest()</code>实现：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>bool <span class=\"token function\">hitTest</span><span class=\"token punctuation\">(</span>HitTestResult result<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> <span class=\"token metadata symbol\">@required</span> Offset position <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>  \n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>_size<span class=\"token punctuation\">.</span><span class=\"token function\">contains</span><span class=\"token punctuation\">(</span>position<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token function\">hitTestChildren</span><span class=\"token punctuation\">(</span>result<span class=\"token punctuation\">,</span> position<span class=\"token punctuation\">:</span> position<span class=\"token punctuation\">)</span> <span class=\"token operator\">||</span> <span class=\"token function\">hitTestSelf</span><span class=\"token punctuation\">(</span>position<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      result<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token function\">BoxHitTestEntry</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">,</span> position<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">return</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token keyword\">return</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们看到默认的实现里调用了<code>hitTestSelf()</code>和<code>hitTestChildren()</code>两个方法，这两个方法默认实现如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code> \n<span class=\"token metadata symbol\">@protected</span>\nbool <span class=\"token function\">hitTestSelf</span><span class=\"token punctuation\">(</span>Offset position<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n \n<span class=\"token metadata symbol\">@protected</span>\nbool <span class=\"token function\">hitTestChildren</span><span class=\"token punctuation\">(</span>HitTestResult result<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> Offset position <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p><code>hitTest</code> 方法用来判断该<code>RenderObject</code> 是否在被点击的范围内，同时负责将被点击的 <code>RenderBox</code> 添加到 <code>HitTestResult</code> 列表中，参数 <code>position</code> 为事件触发的坐标（如果有的话），返回 true 则表示有<code>RenderBox</code> 通过了命中测试，需要响应事件，反之则认为当前<code>RenderBox</code>没有命中。在继承<code>RenderBox</code>时，可以直接重写<code>hitTest()</code>方法，也可以重写 <code>hitTestSelf()</code> 或 <code>hitTestChildren()</code>, 唯一不同的是 <code>hitTest()</code>中需要将通过命中测试的节点信息添加到命中测试结果列表中，而 <code>hitTestSelf()</code> 和 <code>hitTestChildren()</code>则只需要简单的返回<code>true</code>或<code>false</code>。</p> <h2 id=\"_14-3-4-语义化\"><a href=\"#_14-3-4-语义化\" class=\"header-anchor\">#</a> 14.3.4 语义化</h2> <p>语义化即Semantics，主要是提供给读屏软件的接口，也是实现辅助功能的基础，通过语义化接口可以让机器理解页面上的内容，对于有视力障碍用户可以使用读屏软件来理解UI内容。如果一个<code>RenderObject</code>要支持语义化接口，可以实现 <code>describeApproximatePaintClip</code>和 <code>visitChildrenForSemantics</code>方法和<code>semanticsAnnotator</code> getter。更多关于语义化的信息可以查看API文档。</p> <h2 id=\"_14-3-5-总结\"><a href=\"#_14-3-5-总结\" class=\"header-anchor\">#</a> 14.3.5 总结</h2> <p>本节我们介绍了<code>RenderObject</code>主要的功能和方法，理解这些内容可以帮助我们更好的理解Flutter UI底层原理。我们也可以看到，如果要从头到尾实现一个<code>RenderObject</code>是比较麻烦的，我们必须去实现layout、绘制和命中测试逻辑，但是值得庆幸的是，大多数时候我们可以直接在Widget层通过组合或者<code>CustomPaint</code>完成自定义UI。如果遇到只能定义一个新<code>RenderObject</code>的场景时（如要实现一个新的layout算法的布局容器），可以直接继承自<code>RenderBox</code>，这样可以帮我们减少一部分工作。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter14/element_buildcontext.html\" class=\"prev\">\n        14.2 Element与BuildContext\n      </a></span> <span class=\"next\"><a href=\"/chapter14/flutter_app_startup.html\">\n        14.4 Flutter运行机制-从启动到显示\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/92.7b12896a.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter15/code_structure.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>15.2 Flutter APP代码结构 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/151.a1cacc6d.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter15/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading open\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" aria-current=\"page\" class=\"active sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_15-2-flutter-app代码结构\"><a href=\"#_15-2-flutter-app代码结构\" class=\"header-anchor\">#</a> 15.2 Flutter APP代码结构</h1> <p>我们先来创建一个全新的Flutter工程，命名为&quot;github_client_app&quot;；创建新工程的步骤视读者使用的编辑器而定，都比较简单，在此不再赘述。创建完成后，工程结构如下：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>github_client_app\n├── android\n├── ios\n├── lib\n└── <span class=\"token builtin class-name\">test</span>\n</code></pre></div><p>由于我们需要使用外部图片和Icon资源，所以我们在项目根目录下分别创建“imgs”和“fonts”文件夹，前者用于保存图片，后者用于保存Icon文件。关于图片和Icon，读者可以参考第三章中相应的内容。</p> <p>由于在网络数据传输和持久化时，我们需要通过Json来传输、保存数据；但是在应用开发时我们又需要将Json转成Dart Model类，现在我们使用在第十一章中“Json转Model”小节中介绍的方案，所以，我们需要在根目录下再创建一个用于保存Json文件的“jsons”文件夹。</p> <p>多语言支持我们使用第十三章“国际化”中介绍的方案，所以还需要在根目录下创建一个“l10n”文件夹，用于保存各国语言对应的arb文件。</p> <p>现在工程目录变为：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>github_client_app\n├── android\n├── fonts\n├── l10n-arb\n├── imgs\n├── ios\n├── jsons\n├── lib\n└── <span class=\"token builtin class-name\">test</span>\n</code></pre></div><p>由于我们的Dart代码都在“lib”文件夹下，笔者根据技术选型和经验在lib文件下创建了如下目录：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>lib\n├── common\n├── l10n\n├── models\n├── states\n├── routes\n└── widgets \n</code></pre></div><table><thead><tr><th>文件夹</th> <th>作用</th></tr></thead> <tbody><tr><td>common</td> <td>一些工具类，如通用方法类、网络接口类、保存全局变量的静态类等</td></tr> <tr><td>l10n</td> <td>国际化相关的类都在此目录下</td></tr> <tr><td>models</td> <td>Json文件对应的Dart Model类会在此目录下</td></tr> <tr><td>states</td> <td>保存APP中需要跨组件共享的状态类</td></tr> <tr><td>routes</td> <td>存放所有路由页面类</td></tr> <tr><td>widgets</td> <td>APP内封装的一些Widget组件都在该目录下</td></tr></tbody></table> <p>注意，使用不同的框架或技术选型会对代码有不同的组织方式，因此，本节介绍的代码组织结构并不是固定或者“最佳”的，在实战中，读者可以自己根据情况调整源码结构。但是无论采取何种源码组织结构，清晰和解耦都是一个通用原则，我们应该让自己的代码结构清晰，以便交流和维护。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter15/intro.html\" class=\"prev\">\n        15.1 Github客户端示例\n      </a></span> <span class=\"next\"><a href=\"/chapter15/models.html\">\n        15.3 Model类定义\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/151.a1cacc6d.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter15/entry.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>15.6 APP入口及主页 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/25.869317d7.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter15/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading open\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" aria-current=\"page\" class=\"active sidebar-link\">15.6 APP入口及主页</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/chapter15/entry.html#_15-6-1-app入口\" class=\"sidebar-link\">15.6.1 APP入口</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter15/entry.html#_15-6-2-主页\" class=\"sidebar-link\">15.6.2 主页</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter15/entry.html#_15-6-3-抽屉菜单\" class=\"sidebar-link\">15.6.3 抽屉菜单</a></li></ul></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_15-6-app入口及主页\"><a href=\"#_15-6-app入口及主页\" class=\"header-anchor\">#</a> 15.6 APP入口及主页</h1> <p>本节来介绍一下APP入口及首页。</p> <h2 id=\"_15-6-1-app入口\"><a href=\"#_15-6-1-app入口\" class=\"header-anchor\">#</a> 15.6.1 APP入口</h2> <p><code>main</code>函数为APP入口函数，实现如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">main</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Global<span class=\"token punctuation\">.</span><span class=\"token function\">init</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">runApp</span><span class=\"token punctuation\">(</span><span class=\"token function\">MyApp</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>初始化完成后才会加载UI(<code>MyApp</code>)，<code>MyApp</code> 是应用的入口Widget，实现如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">MyApp</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// This widget is the root of your application.</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">MultiProvider</span><span class=\"token punctuation\">(</span>\n      providers<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>SingleChildCloneableWidget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n        ChangeNotifierProvider<span class=\"token punctuation\">.</span><span class=\"token function\">value</span><span class=\"token punctuation\">(</span>value<span class=\"token punctuation\">:</span> <span class=\"token function\">ThemeModel</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        ChangeNotifierProvider<span class=\"token punctuation\">.</span><span class=\"token function\">value</span><span class=\"token punctuation\">(</span>value<span class=\"token punctuation\">:</span> <span class=\"token function\">UserModel</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        ChangeNotifierProvider<span class=\"token punctuation\">.</span><span class=\"token function\">value</span><span class=\"token punctuation\">(</span>value<span class=\"token punctuation\">:</span> <span class=\"token function\">LocaleModel</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> Consumer2<span class=\"token operator\">&lt;</span>ThemeModel<span class=\"token punctuation\">,</span> LocaleModel<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n        builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> themeModel<span class=\"token punctuation\">,</span> localeModel<span class=\"token punctuation\">,</span> Widget child<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">return</span> <span class=\"token function\">MaterialApp</span><span class=\"token punctuation\">(</span>\n            theme<span class=\"token punctuation\">:</span> <span class=\"token function\">ThemeData</span><span class=\"token punctuation\">(</span>\n              primarySwatch<span class=\"token punctuation\">:</span> themeModel<span class=\"token punctuation\">.</span>theme<span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            onGenerateTitle<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n              <span class=\"token keyword\">return</span> GmLocalizations<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>title<span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n            home<span class=\"token punctuation\">:</span> <span class=\"token function\">HomeRoute</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//应用主页</span>\n            locale<span class=\"token punctuation\">:</span> localeModel<span class=\"token punctuation\">.</span><span class=\"token function\">getLocale</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token comment\">//我们只支持美国英语和中文简体</span>\n            supportedLocales<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>\n              <span class=\"token keyword\">const</span> <span class=\"token function\">Locale</span><span class=\"token punctuation\">(</span><span class=\"token string\">'en'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'US'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 美国英语</span>\n              <span class=\"token keyword\">const</span> <span class=\"token function\">Locale</span><span class=\"token punctuation\">(</span><span class=\"token string\">'zh'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'CN'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 中文简体</span>\n              <span class=\"token comment\">//其它Locales</span>\n            <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n            localizationsDelegates<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>\n              <span class=\"token comment\">// 本地化的代理类</span>\n              GlobalMaterialLocalizations<span class=\"token punctuation\">.</span>delegate<span class=\"token punctuation\">,</span>\n              GlobalWidgetsLocalizations<span class=\"token punctuation\">.</span>delegate<span class=\"token punctuation\">,</span>\n              <span class=\"token function\">GmLocalizationsDelegate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n            localeResolutionCallback<span class=\"token punctuation\">:</span>\n                <span class=\"token punctuation\">(</span>Locale _locale<span class=\"token punctuation\">,</span> Iterable<span class=\"token operator\">&lt;</span>Locale<span class=\"token operator\">&gt;</span> supportedLocales<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>localeModel<span class=\"token punctuation\">.</span><span class=\"token function\">getLocale</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token comment\">//如果已经选定语言，则不跟随系统</span>\n                <span class=\"token keyword\">return</span> localeModel<span class=\"token punctuation\">.</span><span class=\"token function\">getLocale</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n         \n                Locale locale<span class=\"token punctuation\">;</span>\n                <span class=\"token comment\">//APP语言跟随系统语言，如果系统语言不是中文简体或美国英语，</span>\n                <span class=\"token comment\">//则默认使用美国英语</span>\n                <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>supportedLocales<span class=\"token punctuation\">.</span><span class=\"token function\">contains</span><span class=\"token punctuation\">(</span>_locale<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                  locale<span class=\"token operator\">=</span> _locale<span class=\"token punctuation\">;</span>\n                <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n                  locale<span class=\"token operator\">=</span> <span class=\"token function\">Locale</span><span class=\"token punctuation\">(</span><span class=\"token string\">'en'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'US'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                <span class=\"token punctuation\">}</span>\n                <span class=\"token keyword\">return</span> locale<span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n            <span class=\"token comment\">// 注册命名路由表</span>\n            routes<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>String<span class=\"token punctuation\">,</span> WidgetBuilder<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">{</span>\n              <span class=\"token string\">&quot;login&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">LoginRoute</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token string\">&quot;themes&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">ThemeChangeRoute</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token string\">&quot;language&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">LanguageRoute</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>在上面的代码中：</p> <ol><li>我们的根widget是<code>MultiProvider</code>，它将主题、用户、语言三种状态绑定到了应用的根上，如此一来，任何路由中都可以通过<code>Provider.of()</code>来获取这些状态，也就是说这三种状态是全局共享的！</li> <li><code>HomeRoute</code>是应用的主页。</li> <li>在构建<code>MaterialApp</code>时，我们配置了APP支持的语言列表，以及监听了系统语言改变事件；另外<code>MaterialApp</code>消费（依赖）了<code>ThemeModel</code>和<code>LocaleModel</code>，所以当APP主题或语言改变时<code>MaterialApp</code>会重新构建</li> <li>我们注册了命名路由表，以便在APP中可以直接通过路由名跳转。</li> <li>为了支持多语言（本APP中我们支持美国英语和中文简体两种语言）我们实现了一个<code>GmLocalizationsDelegate</code>，子Widget中都可以通过<code>GmLocalizations</code>来动态获取APP当前语言对应的文案。关于<code>GmLocalizationsDelegate</code>和<code>GmLocalizations</code>的实现方式读者可以参考“国际化”一章中的介绍，此处不再赘述。</li></ol> <h2 id=\"_15-6-2-主页\"><a href=\"#_15-6-2-主页\" class=\"header-anchor\">#</a> 15.6.2 主页</h2> <p>为了简单起见，当APP启动后，如果之前已登录了APP，则显示该用户项目列表；如果之前未登录，则显示一个登录按钮，点击后跳转到登录页。另外，我们实现一个抽屉菜单，里面包含当前用户头像及APP的菜单。下面我们先看看要实现的效果，如图15-1、15-2所示：</p> <p><img src=\"/assets/img/15-1.c59fd39e.png\" alt=\"15-1\"><img src=\"/assets/img/15-2.cb77ca5a.png\" alt=\"15-2\"></p> <p>我们在“lib/routes”下创建一个“home_page.dart”文件，实现如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">HomeRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _HomeRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">_HomeRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_HomeRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>HomeRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n      appBar<span class=\"token punctuation\">:</span> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>\n        title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>GmLocalizations<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>home<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      body<span class=\"token punctuation\">:</span> <span class=\"token function\">_buildBody</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 构建主页面</span>\n      drawer<span class=\"token punctuation\">:</span> <span class=\"token function\">MyDrawer</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//抽屉菜单</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token comment\">// 省略</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面代码中，主页的标题（title）我们是通过<code>GmLocalizations.of(context).home</code>来获得，<code>GmLocalizations</code>是我们提供的一个<code>Localizations</code>类，用于支持多语言，因此当APP语言改变时，凡是使用<code>GmLocalizations</code>动态获取的文案都会是相应语言的文案，这在前面“国际化”一章中已经介绍过，读者可以前翻查阅。</p> <p>我们通过 <code>_buildBody()</code>方法来构建主页内容，<code>_buildBody()</code>方法实现代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>  Widget <span class=\"token function\">_buildBody</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    UserModel userModel <span class=\"token operator\">=</span> Provider<span class=\"token punctuation\">.</span>of<span class=\"token operator\">&lt;</span>UserModel<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>userModel<span class=\"token punctuation\">.</span>isLogin<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">//用户未登录，显示登录按钮</span>\n      <span class=\"token keyword\">return</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>GmLocalizations<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>login<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pushNamed</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;login&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">//已登录，则展示项目列表</span>\n      <span class=\"token keyword\">return</span> InfiniteListView<span class=\"token operator\">&lt;</span>Repo<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n        onRetrieveData<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>int page<span class=\"token punctuation\">,</span> List<span class=\"token operator\">&lt;</span>Repo<span class=\"token operator\">&gt;</span> items<span class=\"token punctuation\">,</span> bool refresh<span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">var</span> data <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">Git</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">getRepos</span><span class=\"token punctuation\">(</span>\n            refresh<span class=\"token punctuation\">:</span> refresh<span class=\"token punctuation\">,</span>\n            queryParameters<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token string\">'page'</span><span class=\"token punctuation\">:</span> page<span class=\"token punctuation\">,</span>\n              <span class=\"token string\">'page_size'</span><span class=\"token punctuation\">:</span> <span class=\"token number\">20</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token comment\">//把请求到的新数据添加到items中</span>\n          items<span class=\"token punctuation\">.</span><span class=\"token function\">addAll</span><span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> \n          <span class=\"token comment\">// 如果接口返回的数量等于'page_size'，则认为还有数据，反之则认为最后一页</span>\n          <span class=\"token keyword\">return</span> data<span class=\"token punctuation\">.</span>length<span class=\"token operator\">==</span><span class=\"token number\">20</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        itemBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>List list<span class=\"token punctuation\">,</span> int index<span class=\"token punctuation\">,</span> BuildContext ctx<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token comment\">// 项目信息列表项</span>\n          <span class=\"token keyword\">return</span> <span class=\"token function\">RepoItem</span><span class=\"token punctuation\">(</span>list<span class=\"token punctuation\">[</span>index<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面代码注释很清楚：如果用户未登录，显示登录按钮；如果用户已登录，则展示项目列表。这里项目列表使用了<code>InfiniteListView</code> Widget，它是flukit package中提供的。<code>InfiniteListView</code>同时支持了下拉刷新和上拉加载更多两种功能。<code>onRetrieveData</code> 为数据获取回调，该回调函数接收三个参数：</p> <table><thead><tr><th>参数名</th> <th>类型</th> <th>解释</th></tr></thead> <tbody><tr><td>page</td> <td>int</td> <td>当前页号</td></tr> <tr><td>items</td> <td>List<T></T></td> <td>保存当前列表数据的List</td></tr> <tr><td>refresh</td> <td>bool</td> <td>是否是下拉刷新触发</td></tr></tbody></table> <p>返回值类型为<code>bool</code>，为<code>true</code>时表示还有数据，为<code>false</code>时则表示后续没有数据了。<code>onRetrieveData</code> 回调中我们调用<code>Git(context).getRepos(...)</code>来获取用户项目列表，同时指定每次请求获取20条。当获取成功时，首先要将新获取的项目数据添加到<code>items</code>中，然后根据本次请求的项目条数是否等于期望的20条来判断还有没有更多的数据。在此需要注意，<code>Git(context).getRepos(…)</code>方法中需要<code>refresh</code>参数来判断是否使用缓存。</p> <p><code>itemBuilder</code>为列表项的builder，我们需要在该回调中构建每一个列表项Widget。由于列表项构建逻辑较复杂，我们单独封装一个<code>RepoItem</code> Widget 专门用于构建列表项UI。<code>RepoItem</code> 实现如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'../index.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">RepoItem</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// 将`repo.id`作为RepoItem的默认key</span>\n  <span class=\"token function\">RepoItem</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>repo<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> <span class=\"token function\">ValueKey</span><span class=\"token punctuation\">(</span>repo<span class=\"token punctuation\">.</span>id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> Repo repo<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  _RepoItemState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">_RepoItemState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_RepoItemState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>RepoItem<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">var</span> subtitle<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n      padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">only</span><span class=\"token punctuation\">(</span>top<span class=\"token punctuation\">:</span> <span class=\"token number\">8.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Material</span><span class=\"token punctuation\">(</span>\n        color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">,</span>\n        shape<span class=\"token punctuation\">:</span> <span class=\"token function\">BorderDirectional</span><span class=\"token punctuation\">(</span>\n          bottom<span class=\"token punctuation\">:</span> <span class=\"token function\">BorderSide</span><span class=\"token punctuation\">(</span>\n            color<span class=\"token punctuation\">:</span> Theme<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>dividerColor<span class=\"token punctuation\">,</span>\n            width<span class=\"token punctuation\">:</span> <span class=\"token number\">.5</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n          padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">only</span><span class=\"token punctuation\">(</span>top<span class=\"token punctuation\">:</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span> bottom<span class=\"token punctuation\">:</span> <span class=\"token number\">16</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n            crossAxisAlignment<span class=\"token punctuation\">:</span> CrossAxisAlignment<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n            children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n              <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>\n                dense<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n                leading<span class=\"token punctuation\">:</span> <span class=\"token function\">gmAvatar</span><span class=\"token punctuation\">(</span>\n                  <span class=\"token comment\">//项目owner头像</span>\n                  widget<span class=\"token punctuation\">.</span>repo<span class=\"token punctuation\">.</span>owner<span class=\"token punctuation\">.</span>avatar_url<span class=\"token punctuation\">,</span>\n                  width<span class=\"token punctuation\">:</span> <span class=\"token number\">24.0</span><span class=\"token punctuation\">,</span>\n                  borderRadius<span class=\"token punctuation\">:</span> BorderRadius<span class=\"token punctuation\">.</span><span class=\"token function\">circular</span><span class=\"token punctuation\">(</span><span class=\"token number\">12</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>\n                  widget<span class=\"token punctuation\">.</span>repo<span class=\"token punctuation\">.</span>owner<span class=\"token punctuation\">.</span>login<span class=\"token punctuation\">,</span>\n                  textScaleFactor<span class=\"token punctuation\">:</span> <span class=\"token number\">.9</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                subtitle<span class=\"token punctuation\">:</span> subtitle<span class=\"token punctuation\">,</span>\n                trailing<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>widget<span class=\"token punctuation\">.</span>repo<span class=\"token punctuation\">.</span>language <span class=\"token operator\">?</span><span class=\"token operator\">?</span> <span class=\"token string\">&quot;&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token comment\">// 构建项目标题和简介</span>\n              <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n                padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">symmetric</span><span class=\"token punctuation\">(</span>horizontal<span class=\"token punctuation\">:</span> <span class=\"token number\">16.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n                  crossAxisAlignment<span class=\"token punctuation\">:</span> CrossAxisAlignment<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n                  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                    <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>\n                      widget<span class=\"token punctuation\">.</span>repo<span class=\"token punctuation\">.</span>fork\n                          <span class=\"token operator\">?</span> widget<span class=\"token punctuation\">.</span>repo<span class=\"token punctuation\">.</span>full_name\n                          <span class=\"token punctuation\">:</span> widget<span class=\"token punctuation\">.</span>repo<span class=\"token punctuation\">.</span>name<span class=\"token punctuation\">,</span>\n                      style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>\n                        fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">15</span><span class=\"token punctuation\">,</span>\n                        fontWeight<span class=\"token punctuation\">:</span> FontWeight<span class=\"token punctuation\">.</span>bold<span class=\"token punctuation\">,</span>\n                        fontStyle<span class=\"token punctuation\">:</span> widget<span class=\"token punctuation\">.</span>repo<span class=\"token punctuation\">.</span>fork\n                            <span class=\"token operator\">?</span> FontStyle<span class=\"token punctuation\">.</span>italic\n                            <span class=\"token punctuation\">:</span> FontStyle<span class=\"token punctuation\">.</span>normal<span class=\"token punctuation\">,</span>\n                      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                    <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n                      padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">only</span><span class=\"token punctuation\">(</span>top<span class=\"token punctuation\">:</span> <span class=\"token number\">8</span><span class=\"token punctuation\">,</span> bottom<span class=\"token punctuation\">:</span> <span class=\"token number\">12</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                      child<span class=\"token punctuation\">:</span> widget<span class=\"token punctuation\">.</span>repo<span class=\"token punctuation\">.</span>description <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span>\n                          <span class=\"token operator\">?</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>\n                              GmLocalizations<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>noDescription<span class=\"token punctuation\">,</span>\n                              style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>\n                                  fontStyle<span class=\"token punctuation\">:</span> FontStyle<span class=\"token punctuation\">.</span>italic<span class=\"token punctuation\">,</span>\n                                  color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">[</span><span class=\"token number\">700</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                            <span class=\"token punctuation\">)</span>\n                          <span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>\n                              widget<span class=\"token punctuation\">.</span>repo<span class=\"token punctuation\">.</span>description<span class=\"token punctuation\">,</span>\n                              maxLines<span class=\"token punctuation\">:</span> <span class=\"token number\">3</span><span class=\"token punctuation\">,</span>\n                              style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>\n                                height<span class=\"token punctuation\">:</span> <span class=\"token number\">1.15</span><span class=\"token punctuation\">,</span>\n                                color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blueGrey<span class=\"token punctuation\">[</span><span class=\"token number\">700</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                                fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">13</span><span class=\"token punctuation\">,</span>\n                              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token comment\">// 构建卡片底部信息</span>\n              <span class=\"token function\">_buildBottom</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">// 构建卡片底部信息</span>\n  Widget <span class=\"token function\">_buildBottom</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> paddingWidth <span class=\"token operator\">=</span> <span class=\"token number\">10</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">IconTheme</span><span class=\"token punctuation\">(</span>\n      data<span class=\"token punctuation\">:</span> <span class=\"token function\">IconThemeData</span><span class=\"token punctuation\">(</span>\n        color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">,</span>\n        size<span class=\"token punctuation\">:</span> <span class=\"token number\">15</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">DefaultTextStyle</span><span class=\"token punctuation\">(</span>\n        style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">,</span> fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">12</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n          padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">symmetric</span><span class=\"token punctuation\">(</span>horizontal<span class=\"token punctuation\">:</span> <span class=\"token number\">16</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword\">var</span> children <span class=\"token operator\">=</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n              <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>star<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot; &quot;</span> <span class=\"token operator\">+</span>\n                  widget<span class=\"token punctuation\">.</span>repo<span class=\"token punctuation\">.</span>stargazers_count\n                      <span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n                      <span class=\"token punctuation\">.</span><span class=\"token function\">padRight</span><span class=\"token punctuation\">(</span>paddingWidth<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>info_outline<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot; &quot;</span> <span class=\"token operator\">+</span>\n                  widget<span class=\"token punctuation\">.</span>repo<span class=\"token punctuation\">.</span>open_issues_count\n                      <span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n                      <span class=\"token punctuation\">.</span><span class=\"token function\">padRight</span><span class=\"token punctuation\">(</span>paddingWidth<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n\n              <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>MyIcons<span class=\"token punctuation\">.</span>fork<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//我们的自定义图标</span>\n              <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>widget<span class=\"token punctuation\">.</span>repo<span class=\"token punctuation\">.</span>forks_count<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">padRight</span><span class=\"token punctuation\">(</span>paddingWidth<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n            <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>widget<span class=\"token punctuation\">.</span>repo<span class=\"token punctuation\">.</span>fork<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              children<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Forked&quot;</span><span class=\"token punctuation\">.</span><span class=\"token function\">padRight</span><span class=\"token punctuation\">(</span>paddingWidth<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span>\n\n            <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>widget<span class=\"token punctuation\">.</span>repo<span class=\"token punctuation\">.</span>private <span class=\"token operator\">==</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              children<span class=\"token punctuation\">.</span><span class=\"token function\">addAll</span><span class=\"token punctuation\">(</span><span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>lock<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot; private&quot;</span><span class=\"token punctuation\">.</span><span class=\"token function\">padRight</span><span class=\"token punctuation\">(</span>paddingWidth<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n              <span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span>\n            <span class=\"token keyword\">return</span> <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>children<span class=\"token punctuation\">:</span> children<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面代码有两点需要注意：</p> <ol><li><p>在构建项目拥有者头像时调用了<code>gmAvatar(…)</code>方法，该方法是是一个全局工具函数，专门用于获取头像图片，实现如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Widget <span class=\"token function\">gmAvatar</span><span class=\"token punctuation\">(</span>String url<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n  double width <span class=\"token operator\">=</span> <span class=\"token number\">30</span><span class=\"token punctuation\">,</span>\n  double height<span class=\"token punctuation\">,</span>\n  BoxFit fit<span class=\"token punctuation\">,</span>\n  BorderRadius borderRadius<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">var</span> placeholder <span class=\"token operator\">=</span> Image<span class=\"token punctuation\">.</span><span class=\"token function\">asset</span><span class=\"token punctuation\">(</span>\n      <span class=\"token string\">&quot;imgs/avatar-default.png&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//头像占位图，加载过程中显示</span>\n      width<span class=\"token punctuation\">:</span> width<span class=\"token punctuation\">,</span>\n      height<span class=\"token punctuation\">:</span> height\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">ClipRRect</span><span class=\"token punctuation\">(</span>\n    borderRadius<span class=\"token punctuation\">:</span> borderRadius <span class=\"token operator\">?</span><span class=\"token operator\">?</span> BorderRadius<span class=\"token punctuation\">.</span><span class=\"token function\">circular</span><span class=\"token punctuation\">(</span><span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">CachedNetworkImage</span><span class=\"token punctuation\">(</span> \n      imageUrl<span class=\"token punctuation\">:</span> url<span class=\"token punctuation\">,</span>\n      width<span class=\"token punctuation\">:</span> width<span class=\"token punctuation\">,</span>\n      height<span class=\"token punctuation\">:</span> height<span class=\"token punctuation\">,</span>\n      fit<span class=\"token punctuation\">:</span> fit<span class=\"token punctuation\">,</span>\n      placeholder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> url<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span>placeholder<span class=\"token punctuation\">,</span>\n      errorWidget<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> url<span class=\"token punctuation\">,</span> error<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span>placeholder<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>代码中调用了<code>CachedNetworkImage</code> 是cached_network_image包中提供的一个Widget，它不仅可以在图片加载过程中指定一个占位图，而且还可以对网络请求的图片进行缓存，更多详情读者可以自行查阅其文档。</p></li> <li><p>由于Flutter 的Material 图标库中没有fork图标，所以我们在iconfont.cn上找了一个fork图标，然后根据“图片和Icon”一节中介绍的使用自定义字体图标的方法集成到了我们的项目中。</p></li></ol> <h2 id=\"_15-6-3-抽屉菜单\"><a href=\"#_15-6-3-抽屉菜单\" class=\"header-anchor\">#</a> 15.6.3 抽屉菜单</h2> <p>抽屉菜单分为两部分：顶部头像和底部功能菜单项。当用户未登录，则抽屉菜单顶部会显示一个默认的灰色占位图，若用户已登录，则会显示用户的头像。抽屉菜单底部有“换肤”和“语言”两个固定菜单，若用户已登录，则会多一个“注销”菜单。用户点击“换肤”和“语言”两个菜单项，会进入相应的设置页面。我们的抽屉菜单效果如图15-3、15-4所示：</p> <p><img src=\"/assets/img/15-3.28296c90.png\" alt=\"15-3\"><img src=\"/assets/img/15-4.19779c8f.png\" alt=\"15-4\"></p> <p>实现代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">MyDrawer</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function\">MyDrawer</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Drawer</span><span class=\"token punctuation\">(</span>\n      <span class=\"token comment\">//移除顶部padding</span>\n      child<span class=\"token punctuation\">:</span> MediaQuery<span class=\"token punctuation\">.</span><span class=\"token function\">removePadding</span><span class=\"token punctuation\">(</span>\n        context<span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">,</span>\n        removeTop<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n          crossAxisAlignment<span class=\"token punctuation\">:</span> CrossAxisAlignment<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            <span class=\"token function\">_buildHeader</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//构建抽屉菜单头部</span>\n            <span class=\"token function\">Expanded</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">:</span> <span class=\"token function\">_buildMenus</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//构建功能菜单</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  Widget <span class=\"token function\">_buildHeader</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> Consumer<span class=\"token operator\">&lt;</span>UserModel<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n      builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> UserModel value<span class=\"token punctuation\">,</span> Widget child<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">return</span> <span class=\"token function\">GestureDetector</span><span class=\"token punctuation\">(</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n            color<span class=\"token punctuation\">:</span> Theme<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>primaryColor<span class=\"token punctuation\">,</span>\n            padding<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">only</span><span class=\"token punctuation\">(</span>top<span class=\"token punctuation\">:</span> <span class=\"token number\">40</span><span class=\"token punctuation\">,</span> bottom<span class=\"token punctuation\">:</span> <span class=\"token number\">20</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n              children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n                  padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">symmetric</span><span class=\"token punctuation\">(</span>horizontal<span class=\"token punctuation\">:</span> <span class=\"token number\">16.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  child<span class=\"token punctuation\">:</span> <span class=\"token function\">ClipOval</span><span class=\"token punctuation\">(</span>\n                    <span class=\"token comment\">// 如果已登录，则显示用户头像；若未登录，则显示默认头像</span>\n                    child<span class=\"token punctuation\">:</span> value<span class=\"token punctuation\">.</span>isLogin\n                        <span class=\"token operator\">?</span> <span class=\"token function\">gmAvatar</span><span class=\"token punctuation\">(</span>value<span class=\"token punctuation\">.</span>user<span class=\"token punctuation\">.</span>avatar_url<span class=\"token punctuation\">,</span> width<span class=\"token punctuation\">:</span> <span class=\"token number\">80</span><span class=\"token punctuation\">)</span>\n                        <span class=\"token punctuation\">:</span> Image<span class=\"token punctuation\">.</span><span class=\"token function\">asset</span><span class=\"token punctuation\">(</span>\n                            <span class=\"token string\">&quot;imgs/avatar-default.png&quot;</span><span class=\"token punctuation\">,</span>\n                            width<span class=\"token punctuation\">:</span> <span class=\"token number\">80</span><span class=\"token punctuation\">,</span>\n                          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>\n                  value<span class=\"token punctuation\">.</span>isLogin\n                      <span class=\"token operator\">?</span> value<span class=\"token punctuation\">.</span>user<span class=\"token punctuation\">.</span>login\n                      <span class=\"token punctuation\">:</span> GmLocalizations<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>login<span class=\"token punctuation\">,</span>\n                  style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>\n                    fontWeight<span class=\"token punctuation\">:</span> FontWeight<span class=\"token punctuation\">.</span>bold<span class=\"token punctuation\">,</span>\n                    color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span>\n              <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          onTap<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>value<span class=\"token punctuation\">.</span>isLogin<span class=\"token punctuation\">)</span> Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pushNamed</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;login&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">// 构建菜单项</span>\n  Widget <span class=\"token function\">_buildMenus</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> Consumer<span class=\"token operator\">&lt;</span>UserModel<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n      builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> UserModel userModel<span class=\"token punctuation\">,</span> Widget child<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">var</span> gm <span class=\"token operator\">=</span> GmLocalizations<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">return</span> <span class=\"token function\">ListView</span><span class=\"token punctuation\">(</span>\n          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>\n              leading<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>color_lens<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>gm<span class=\"token punctuation\">.</span>theme<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              onTap<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">pushNamed</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;themes&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>\n              leading<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>language<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>gm<span class=\"token punctuation\">.</span>language<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              onTap<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">pushNamed</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;language&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>userModel<span class=\"token punctuation\">.</span>isLogin<span class=\"token punctuation\">)</span> <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>\n              leading<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>power_settings_new<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>gm<span class=\"token punctuation\">.</span>logout<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              onTap<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token function\">showDialog</span><span class=\"token punctuation\">(</span>\n                  context<span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">,</span>\n                  builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>ctx<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                    <span class=\"token comment\">//退出账号前先弹二次确认窗</span>\n                    <span class=\"token keyword\">return</span> <span class=\"token function\">AlertDialog</span><span class=\"token punctuation\">(</span>\n                      content<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>gm<span class=\"token punctuation\">.</span>logoutTip<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                      actions<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                        <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n                          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>gm<span class=\"token punctuation\">.</span>cancel<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                          onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                        <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n                          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>gm<span class=\"token punctuation\">.</span>yes<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                          onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                            <span class=\"token comment\">//该赋值语句会触发MaterialApp rebuild</span>\n                            userModel<span class=\"token punctuation\">.</span>user <span class=\"token operator\">=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n                            Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n                        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>用户点击“注销”，<code>userModel.user</code> 会被置空，此时所有依赖<code>userModel</code>的组件都会被<code>rebuild</code>，如主页会恢复成未登录的状态。</p> <p>本小节我们介绍了APP入口<code>MaterialApp</code>的一些配置，然后实现了APP的首页。后面我们将展示登录页、换肤页、语言切换页。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter15/network.html\" class=\"prev\">\n        15.5 网络请求封装\n      </a></span> <span class=\"next\"><a href=\"/chapter15/login_page.html\">\n        15.7 登录页\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/25.869317d7.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter15/globals.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>15.4 全局变量及共享状态 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/152.5323fd98.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter15/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading open\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" aria-current=\"page\" class=\"active sidebar-link\">15.4 全局变量及共享状态</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/chapter15/globals.html#_15-4-1-全局变量-global类\" class=\"sidebar-link\">15.4.1 全局变量-Global类</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter15/globals.html#_15-4-2-共享状态\" class=\"sidebar-link\">15.4.2 共享状态</a></li></ul></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_15-4-全局变量及共享状态\"><a href=\"#_15-4-全局变量及共享状态\" class=\"header-anchor\">#</a> 15.4 全局变量及共享状态</h1> <p>应用程序中通常会包含一些贯穿APP生命周期的变量信息，这些信息在APP大多数地方可能都会被用到，比如当前用户信息、Local信息等。在Flutter中我们把需要全局共享的信息分为两类：全局变量和共享状态。全局变量就是单纯指会贯穿整个APP生命周期的变量，用于单纯的保存一些信息，或者封装一些全局工具和方法的对象。而共享状态则是指哪些需要跨组件或跨路由共享的信息，这些信息通常也是全局变量，而共享状态和全局变量的不同在于前者发生改变时需要通知所有使用该状态的组件，而后者不需要。为此，我们将全局变量和共享状态分开单独管理。</p> <h2 id=\"_15-4-1-全局变量-global类\"><a href=\"#_15-4-1-全局变量-global类\" class=\"header-anchor\">#</a> 15.4.1 全局变量-Global类</h2> <p>我们在“lib/common”目录下创建一个<code>Global</code>类，它主要管理APP的全局变量，定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">// 提供五套可选主题色</span>\n<span class=\"token keyword\">const</span> _themes <span class=\"token operator\">=</span> <span class=\"token operator\">&lt;</span>MaterialColor<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n  Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span>\n  Colors<span class=\"token punctuation\">.</span>cyan<span class=\"token punctuation\">,</span>\n  Colors<span class=\"token punctuation\">.</span>teal<span class=\"token punctuation\">,</span>\n  Colors<span class=\"token punctuation\">.</span>green<span class=\"token punctuation\">,</span>\n  Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">Global</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">static</span> SharedPreferences _prefs<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">static</span> Profile profile <span class=\"token operator\">=</span> <span class=\"token function\">Profile</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">// 网络缓存对象</span>\n  <span class=\"token keyword\">static</span> NetCache netCache <span class=\"token operator\">=</span> <span class=\"token function\">NetCache</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// 可选的主题列表</span>\n  <span class=\"token keyword\">static</span> List<span class=\"token operator\">&lt;</span>MaterialColor<span class=\"token operator\">&gt;</span> <span class=\"token keyword\">get</span> themes <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _themes<span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// 是否为release版</span>\n  <span class=\"token keyword\">static</span> bool <span class=\"token keyword\">get</span> isRelease <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> bool<span class=\"token punctuation\">.</span><span class=\"token function\">fromEnvironment</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;dart.vm.product&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">//初始化全局信息，会在APP启动时执行</span>\n  <span class=\"token keyword\">static</span> Future <span class=\"token function\">init</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    _prefs <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> SharedPreferences<span class=\"token punctuation\">.</span><span class=\"token function\">getInstance</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">var</span> _profile <span class=\"token operator\">=</span> _prefs<span class=\"token punctuation\">.</span><span class=\"token function\">getString</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;profile&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>_profile <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n        profile <span class=\"token operator\">=</span> Profile<span class=\"token punctuation\">.</span><span class=\"token function\">fromJson</span><span class=\"token punctuation\">(</span><span class=\"token function\">jsonDecode</span><span class=\"token punctuation\">(</span>_profile<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span> <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token comment\">// 如果没有缓存策略，设置默认缓存策略</span>\n    profile<span class=\"token punctuation\">.</span>cache <span class=\"token operator\">=</span> profile<span class=\"token punctuation\">.</span>cache <span class=\"token operator\">?</span><span class=\"token operator\">?</span> <span class=\"token function\">CacheConfig</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>enable <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span>\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>maxAge <span class=\"token operator\">=</span> <span class=\"token number\">3600</span>\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>maxCount <span class=\"token operator\">=</span> <span class=\"token number\">100</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token comment\">//初始化网络请求相关配置</span>\n    Git<span class=\"token punctuation\">.</span><span class=\"token function\">init</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">// 持久化Profile信息</span>\n  <span class=\"token keyword\">static</span> <span class=\"token function\">saveProfile</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span>\n      _prefs<span class=\"token punctuation\">.</span><span class=\"token function\">setString</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;profile&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token function\">jsonEncode</span><span class=\"token punctuation\">(</span>profile<span class=\"token punctuation\">.</span><span class=\"token function\">toJson</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>Global类的各个字段的意义都有注释，在此不再赘述，需要注意的是<code>init()</code>需要在App启动时就要执行，所以应用的<code>main</code>方法如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">main</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Global<span class=\"token punctuation\">.</span><span class=\"token function\">init</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">runApp</span><span class=\"token punctuation\">(</span><span class=\"token function\">MyApp</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>在此，一定要确保<code>Global.init()</code>方法不能抛出异常，否则 <code>runApp(MyApp())</code>根本执行不到。</p> <h2 id=\"_15-4-2-共享状态\"><a href=\"#_15-4-2-共享状态\" class=\"header-anchor\">#</a> 15.4.2 共享状态</h2> <p>有了全局变量，我们还需要考虑如何跨组件共享状态。当然，如果我们将要共享的状态全部用全局变量替代也是可以的，但是这在Flutter开发中并不是一个好主意，因为组件的状态是和UI相关，而在状态改变时我们会期望依赖该状态的UI组件会自动更新，如果使用全局变量，那么我们必须得去手动处理状态变动通知、接收机制以及变量和组件依赖关系。因此，本实例中，我们使用前面介绍过的Provider包来实现跨组件状态共享，因此我们需要定义相关的Provider。在本实例中，需要共享的状态有登录用户信息、APP主题信息、APP语言信息。由于这些信息改变后都要立即通知其它依赖的该信息的Widget更新，所以我们应该使用<code>ChangeNotifierProvider</code>，另外，这些信息改变后都是需要更新Profile信息并进行持久化的。综上所述，我们可以定义一个<code>ProfileChangeNotifier</code>基类，然后让需要共享的Model继承自该类即可，<code>ProfileChangeNotifier</code>定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">ProfileChangeNotifier</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">ChangeNotifier</span> <span class=\"token punctuation\">{</span>\n  Profile <span class=\"token keyword\">get</span> _profile <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Global<span class=\"token punctuation\">.</span>profile<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">notifyListeners</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    Global<span class=\"token punctuation\">.</span><span class=\"token function\">saveProfile</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//保存Profile变更</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">notifyListeners</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//通知依赖的Widget更新</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h3 id=\"用户状态\"><a href=\"#用户状态\" class=\"header-anchor\">#</a> 用户状态</h3> <p>用户状态在登录状态发生变化时更新、通知其依赖项，我们定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">UserModel</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">ProfileChangeNotifier</span> <span class=\"token punctuation\">{</span>\n  User <span class=\"token keyword\">get</span> user <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _profile<span class=\"token punctuation\">.</span>user<span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// APP是否登录(如果有用户信息，则证明登录过)</span>\n  bool <span class=\"token keyword\">get</span> isLogin <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> user <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">//用户信息发生变化，更新用户信息并通知依赖它的子孙Widgets更新</span>\n  <span class=\"token keyword\">set</span> <span class=\"token function\">user</span><span class=\"token punctuation\">(</span>User user<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>user<span class=\"token operator\">?</span><span class=\"token punctuation\">.</span>login <span class=\"token operator\">!=</span> _profile<span class=\"token punctuation\">.</span>user<span class=\"token operator\">?</span><span class=\"token punctuation\">.</span>login<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _profile<span class=\"token punctuation\">.</span>lastLogin <span class=\"token operator\">=</span> _profile<span class=\"token punctuation\">.</span>user<span class=\"token operator\">?</span><span class=\"token punctuation\">.</span>login<span class=\"token punctuation\">;</span>\n      _profile<span class=\"token punctuation\">.</span>user <span class=\"token operator\">=</span> user<span class=\"token punctuation\">;</span>\n      <span class=\"token function\">notifyListeners</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h3 id=\"app主题状态\"><a href=\"#app主题状态\" class=\"header-anchor\">#</a> APP主题状态</h3> <p>主题状态在用户更换APP主题时更新、通知其依赖项，定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">ThemeModel</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">ProfileChangeNotifier</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// 获取当前主题，如果为设置主题，则默认使用蓝色主题</span>\n  ColorSwatch <span class=\"token keyword\">get</span> theme <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Global<span class=\"token punctuation\">.</span>themes\n      <span class=\"token punctuation\">.</span><span class=\"token function\">firstWhere</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> e<span class=\"token punctuation\">.</span>value <span class=\"token operator\">==</span> _profile<span class=\"token punctuation\">.</span>theme<span class=\"token punctuation\">,</span> orElse<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// 主题改变后，通知其依赖项，新主题会立即生效</span>\n  <span class=\"token keyword\">set</span> <span class=\"token function\">theme</span><span class=\"token punctuation\">(</span>ColorSwatch color<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>color <span class=\"token operator\">!=</span> theme<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _profile<span class=\"token punctuation\">.</span>theme <span class=\"token operator\">=</span> color<span class=\"token punctuation\">[</span><span class=\"token number\">500</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">;</span>\n      <span class=\"token function\">notifyListeners</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h3 id=\"app语言状态\"><a href=\"#app语言状态\" class=\"header-anchor\">#</a> APP语言状态</h3> <p>当APP语言选为跟随系统（Auto）时，在系通语言改变时，APP语言会更新；当用户在APP中选定了具体语言时（美国英语或中文简体），则APP便会一直使用用户选定的语言，不会再随系统语言而变。语言状态类定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">LocaleModel</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">ProfileChangeNotifier</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// 获取当前用户的APP语言配置Locale类，如果为null，则语言跟随系统语言</span>\n  Locale <span class=\"token function\">getLocale</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>_profile<span class=\"token punctuation\">.</span>locale <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">var</span> t <span class=\"token operator\">=</span> _profile<span class=\"token punctuation\">.</span>locale<span class=\"token punctuation\">.</span><span class=\"token function\">split</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;_&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Locale</span><span class=\"token punctuation\">(</span>t<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> t<span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">// 获取当前Locale的字符串表示</span>\n  String <span class=\"token keyword\">get</span> locale <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _profile<span class=\"token punctuation\">.</span>locale<span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// 用户改变APP语言后，通知依赖项更新，新语言会立即生效</span>\n  <span class=\"token keyword\">set</span> <span class=\"token function\">locale</span><span class=\"token punctuation\">(</span>String locale<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>locale <span class=\"token operator\">!=</span> _profile<span class=\"token punctuation\">.</span>locale<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _profile<span class=\"token punctuation\">.</span>locale <span class=\"token operator\">=</span> locale<span class=\"token punctuation\">;</span>\n      <span class=\"token function\">notifyListeners</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter15/models.html\" class=\"prev\">\n        15.3 Model类定义\n      </a></span> <span class=\"next\"><a href=\"/chapter15/network.html\">\n        15.5 网络请求封装\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/152.5323fd98.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter15/intro.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>15.1 Github客户端示例 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/153.26e3ed31.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter15/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading open\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" aria-current=\"page\" class=\"active sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_15-1-github客户端示例\"><a href=\"#_15-1-github客户端示例\" class=\"header-anchor\">#</a> 15.1 Github客户端示例</h1> <p>本章新建一个Flutter工程，实现一个简单的Github客户端。这个实例的主要目标有两个：</p> <ol><li>带领读者了解如何使用Flutter来开发一个完整APP，了解Flutter应用开发流程及工程结构等。</li> <li>对前面章节所学内容的一个应用及总结。</li></ol> <p>需要注意的是，由于Github本身功能非常多，我们的焦点并不是去实现Github的所有业务功能。因此，我们只需要实现一个APP的骨架，能达到上面这两点即可。下面对我们要实现的功能如下：</p> <ol><li>实现Github账号登录、退出登录功能</li> <li>登录后可以查看自己的项目主页</li> <li>支持换肤</li> <li>支持多语言</li> <li>登录状态可以持久化；</li></ol> <p>要实现上面这些功能会涉及到如下技术点：</p> <ol><li>网络请求；需要请求Github API。</li> <li>Json转Dart Model类；</li> <li>全局状态管理；语言、主题、登录态等都需要全局共享。</li> <li>持久化存储；保存登录信息，用户信息等。</li> <li>支持国际化、Intl包的使用</li></ol> <p>现在，目标已经确定，在接下来章节中，我们将分模块一步一步实现上述功能。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter14/image_and_cache.html\" class=\"prev\">\n        14.5 图片加载原理与缓存\n      </a></span> <span class=\"next\"><a href=\"/chapter15/code_structure.html\">\n        15.2 Flutter APP代码结构\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/153.26e3ed31.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter15/language_and_theme_setting.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>15.8 多语言和多主题 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/41.7b70eaf0.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter15/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading open\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" aria-current=\"page\" class=\"active sidebar-link\">15.8 多语言和多主题</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/chapter15/language_and_theme_setting.html#_15-8-1-语言选择页\" class=\"sidebar-link\">15.8.1 语言选择页</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter15/language_and_theme_setting.html#_15-8-2-主题选择页\" class=\"sidebar-link\">15.8.2 主题选择页</a></li></ul></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_15-8-多语言和多主题\"><a href=\"#_15-8-多语言和多主题\" class=\"header-anchor\">#</a> 15.8 多语言和多主题</h1> <p>本实例APP中语言和主题都是可以设置的，而两者都是通过<code>ChangeNotifierProvider</code>来实现的：我们在<code>main</code>函数中使用了<code>Consumer2</code>，依赖了<code>ThemeModel</code>和<code>LocaleModel</code>，因此，当我们在语言和主题设置页更该当前的配置后，<code>Consumer2</code>的<code>builder</code>都会重新执行，构建一个新的<code>MaterialApp</code>，所以修改会立即生效。下面看一下语言和主题设置页的实现。</p> <h2 id=\"_15-8-1-语言选择页\"><a href=\"#_15-8-1-语言选择页\" class=\"header-anchor\">#</a> 15.8.1 语言选择页</h2> <p>APP语言选择页提供三个选项：中文简体、美国英语、跟随系统。我们将当前APP使用的语言高亮显示，并且在后面添加一个“对号”图标，实现如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">LanguageRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">var</span> color <span class=\"token operator\">=</span> Theme<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>primaryColor<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">var</span> localeModel <span class=\"token operator\">=</span> Provider<span class=\"token punctuation\">.</span>of<span class=\"token operator\">&lt;</span>LocaleModel<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">var</span> gm <span class=\"token operator\">=</span> GmLocalizations<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//构建语言选择项</span>\n    Widget <span class=\"token function\">_buildLanguageItem</span><span class=\"token punctuation\">(</span>String lan<span class=\"token punctuation\">,</span> value<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>\n        title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>\n          lan<span class=\"token punctuation\">,</span>\n          <span class=\"token comment\">// 对APP当前语言进行高亮显示</span>\n          style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> localeModel<span class=\"token punctuation\">.</span>locale <span class=\"token operator\">==</span> value <span class=\"token operator\">?</span> color <span class=\"token punctuation\">:</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        trailing<span class=\"token punctuation\">:</span>\n            localeModel<span class=\"token punctuation\">.</span>locale <span class=\"token operator\">==</span> value <span class=\"token operator\">?</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>done<span class=\"token punctuation\">,</span> color<span class=\"token punctuation\">:</span> color<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span>\n        onTap<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token comment\">// 更新locale后MaterialApp会重新build</span>\n          localeModel<span class=\"token punctuation\">.</span>locale <span class=\"token operator\">=</span> value<span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n      appBar<span class=\"token punctuation\">:</span> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>\n        title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>gm<span class=\"token punctuation\">.</span>language<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      body<span class=\"token punctuation\">:</span> <span class=\"token function\">ListView</span><span class=\"token punctuation\">(</span>\n        children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n          <span class=\"token function\">_buildLanguageItem</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;中文简体&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;zh_CN&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">_buildLanguageItem</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;English&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;en_US&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">_buildLanguageItem</span><span class=\"token punctuation\">(</span>gm<span class=\"token punctuation\">.</span>auto<span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面代码逻辑很简单，唯一需要注意的是我们在<code>build(…)</code>方法里面定义了<code>_buildLanguageItem(…)</code>方法，它和在<code>LanguageRoute</code>类中定义该方法的区别就在于：在<code>build(…)</code>内定义的方法可以共享<code>build(...)</code>方法上下文中的变量，本例中是共享了<code>localeModel</code>。当然，如果<code>_buildLanguageItem(…)</code>的实现复杂一些的话不建议这样做，此时最好是将其作为<code>LanguageRoute</code>类的方法。该页面运行效果如图15-6、15-7所示：</p> <p><img src=\"/assets/img/15-6.92dd6b15.png\" alt=\"15-6\"><img src=\"/assets/img/15-7.aa6d4238.png\" alt=\"15-7\"></p> <p>切换语言后立即生效。</p> <h2 id=\"_15-8-2-主题选择页\"><a href=\"#_15-8-2-主题选择页\" class=\"header-anchor\">#</a> 15.8.2 主题选择页</h2> <p>一个完整的主题<code>Theme</code>包括很多选项，这些选项在<code>ThemeData</code>中定义。本实例为了简单起见，我们只配置主题颜色。我们提供几种默认预定义的主题色供用户选择，用户点击一种色块后则更新主题。主题选择页的实现代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">ThemeChangeRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span><span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n      appBar<span class=\"token punctuation\">:</span> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>\n        title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>GmLocalizations<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>theme<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      body<span class=\"token punctuation\">:</span> <span class=\"token function\">ListView</span><span class=\"token punctuation\">(</span> <span class=\"token comment\">//显示主题色块</span>\n        children<span class=\"token punctuation\">:</span> Global<span class=\"token punctuation\">.</span>themes<span class=\"token punctuation\">.</span>map<span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">return</span> <span class=\"token function\">GestureDetector</span><span class=\"token punctuation\">(</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n              padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">symmetric</span><span class=\"token punctuation\">(</span>vertical<span class=\"token punctuation\">:</span> <span class=\"token number\">5</span><span class=\"token punctuation\">,</span> horizontal<span class=\"token punctuation\">:</span> <span class=\"token number\">16</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n                color<span class=\"token punctuation\">:</span> e<span class=\"token punctuation\">,</span>\n                height<span class=\"token punctuation\">:</span> <span class=\"token number\">40</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            onTap<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token comment\">//主题更新后，MaterialApp会重新build</span>\n              Provider<span class=\"token punctuation\">.</span>of<span class=\"token operator\">&lt;</span>ThemeModel<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>theme <span class=\"token operator\">=</span> e<span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toList</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行效果如图15-8所示：</p> <p><img src=\"/assets/img/15-8.1362ff85.png\" alt=\"15-8\"></p> <p>点击其它主题色块后，APP主题色立马切换生效。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter15/login_page.html\" class=\"prev\">\n        15.7 登录页\n      </a></span> <!----></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/41.7b70eaf0.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter15/login_page.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>15.7 登录页 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/93.54d0e6b6.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter15/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading open\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" aria-current=\"page\" class=\"active sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_15-7-登录页\"><a href=\"#_15-7-登录页\" class=\"header-anchor\">#</a> 15.7 登录页</h1> <p>我们说过Github有多种登录方式，为了简单起见，我们只实现通过用户名和密码登录。在实现登录页时有四点需要注意：</p> <ol><li>可以自动填充上次登录的用户名（如果有）。</li> <li>为了防止密码输入错误，密码框应该有开关可以看明文。</li> <li>用户名或密码字段在调用登录接口前有本地合法性校验（比如不能为空）。</li> <li>登录成功后需更新用户信息。</li></ol> <p>实现代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'../index.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">LoginRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _LoginRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">_LoginRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_LoginRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>LoginRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  TextEditingController _unameController <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">TextEditingController</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  TextEditingController _pwdController <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">TextEditingController</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  bool pwdShow <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//密码是否显示明文</span>\n  GlobalKey _formKey <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">GlobalKey</span><span class=\"token operator\">&lt;</span>FormState<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  bool _nameAutoFocus <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 自动填充上次登录的用户名，填充后将焦点定位到密码输入框</span>\n    _unameController<span class=\"token punctuation\">.</span>text <span class=\"token operator\">=</span> Global<span class=\"token punctuation\">.</span>profile<span class=\"token punctuation\">.</span>lastLogin<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>_unameController<span class=\"token punctuation\">.</span>text <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _nameAutoFocus <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">var</span> gm <span class=\"token operator\">=</span> GmLocalizations<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n      appBar<span class=\"token punctuation\">:</span> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>gm<span class=\"token punctuation\">.</span>login<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      body<span class=\"token punctuation\">:</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n        padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">16.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Form</span><span class=\"token punctuation\">(</span>\n          key<span class=\"token punctuation\">:</span> _formKey<span class=\"token punctuation\">,</span>\n          autovalidate<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n            children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n              <span class=\"token function\">TextFormField</span><span class=\"token punctuation\">(</span>\n                  autofocus<span class=\"token punctuation\">:</span> _nameAutoFocus<span class=\"token punctuation\">,</span>\n                  controller<span class=\"token punctuation\">:</span> _unameController<span class=\"token punctuation\">,</span>\n                  decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">InputDecoration</span><span class=\"token punctuation\">(</span>\n                    labelText<span class=\"token punctuation\">:</span> gm<span class=\"token punctuation\">.</span>userName<span class=\"token punctuation\">,</span>\n                    hintText<span class=\"token punctuation\">:</span> gm<span class=\"token punctuation\">.</span>userNameOrEmail<span class=\"token punctuation\">,</span>\n                    prefixIcon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>person<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token comment\">// 校验用户名（不能为空）</span>\n                  validator<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>v<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                    <span class=\"token keyword\">return</span> v<span class=\"token punctuation\">.</span><span class=\"token function\">trim</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>isNotEmpty <span class=\"token operator\">?</span> <span class=\"token keyword\">null</span> <span class=\"token punctuation\">:</span> gm<span class=\"token punctuation\">.</span>userNameRequired<span class=\"token punctuation\">;</span>\n                  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token function\">TextFormField</span><span class=\"token punctuation\">(</span>\n                controller<span class=\"token punctuation\">:</span> _pwdController<span class=\"token punctuation\">,</span>\n                autofocus<span class=\"token punctuation\">:</span> <span class=\"token operator\">!</span>_nameAutoFocus<span class=\"token punctuation\">,</span>\n                decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">InputDecoration</span><span class=\"token punctuation\">(</span>\n                    labelText<span class=\"token punctuation\">:</span> gm<span class=\"token punctuation\">.</span>password<span class=\"token punctuation\">,</span>\n                    hintText<span class=\"token punctuation\">:</span> gm<span class=\"token punctuation\">.</span>password<span class=\"token punctuation\">,</span>\n                    prefixIcon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>lock<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                    suffixIcon<span class=\"token punctuation\">:</span> <span class=\"token function\">IconButton</span><span class=\"token punctuation\">(</span>\n                      icon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>\n                          pwdShow <span class=\"token operator\">?</span> Icons<span class=\"token punctuation\">.</span>visibility_off <span class=\"token punctuation\">:</span> Icons<span class=\"token punctuation\">.</span>visibility<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                      onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                        <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                          pwdShow <span class=\"token operator\">=</span> <span class=\"token operator\">!</span>pwdShow<span class=\"token punctuation\">;</span>\n                        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n                    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                obscureText<span class=\"token punctuation\">:</span> <span class=\"token operator\">!</span>pwdShow<span class=\"token punctuation\">,</span>\n                <span class=\"token comment\">//校验密码（不能为空）</span>\n                validator<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>v<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                  <span class=\"token keyword\">return</span> v<span class=\"token punctuation\">.</span><span class=\"token function\">trim</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>isNotEmpty <span class=\"token operator\">?</span> <span class=\"token keyword\">null</span> <span class=\"token punctuation\">:</span> gm<span class=\"token punctuation\">.</span>passwordRequired<span class=\"token punctuation\">;</span>\n                <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n                padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">only</span><span class=\"token punctuation\">(</span>top<span class=\"token punctuation\">:</span> <span class=\"token number\">25</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                child<span class=\"token punctuation\">:</span> <span class=\"token function\">ConstrainedBox</span><span class=\"token punctuation\">(</span>\n                  constraints<span class=\"token punctuation\">:</span> BoxConstraints<span class=\"token punctuation\">.</span><span class=\"token function\">expand</span><span class=\"token punctuation\">(</span>height<span class=\"token punctuation\">:</span> <span class=\"token number\">55.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  child<span class=\"token punctuation\">:</span> <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n                    color<span class=\"token punctuation\">:</span> Theme<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>primaryColor<span class=\"token punctuation\">,</span>\n                    onPressed<span class=\"token punctuation\">:</span> _onLogin<span class=\"token punctuation\">,</span>\n                    textColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">,</span>\n                    child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>gm<span class=\"token punctuation\">.</span>login<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_onLogin</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 提交前，先验证各个表单字段是否合法</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>_formKey<span class=\"token punctuation\">.</span>currentState <span class=\"token operator\">as</span> FormState<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">validate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">showLoading</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      User user<span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n        user <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">Git</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">login</span><span class=\"token punctuation\">(</span>_unameController<span class=\"token punctuation\">.</span>text<span class=\"token punctuation\">,</span> _pwdController<span class=\"token punctuation\">.</span>text<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token comment\">// 因为登录页返回后，首页会build，所以我们传false，更新user后不触发更新</span>\n        Provider<span class=\"token punctuation\">.</span>of<span class=\"token operator\">&lt;</span>UserModel<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> listen<span class=\"token punctuation\">:</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>user <span class=\"token operator\">=</span> user<span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span> <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">//登录失败则提示</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">.</span>response<span class=\"token operator\">?</span><span class=\"token punctuation\">.</span>statusCode <span class=\"token operator\">==</span> <span class=\"token number\">401</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token function\">showToast</span><span class=\"token punctuation\">(</span>GmLocalizations<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>userNameOrPasswordWrong<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token function\">showToast</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n      <span class=\"token punctuation\">}</span> <span class=\"token keyword\">finally</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">// 隐藏loading框</span>\n        Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>user <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">// 返回</span>\n        Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>代码很简单，关键地方都有注释，不再赘述，下面我们看一下运行效果，如图15-5所示。</p> <p><img src=\"/assets/img/15-5.bcaa565a.png\" alt=\"图15-5\"></p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter15/entry.html\" class=\"prev\">\n        15.6 APP入口及主页\n      </a></span> <span class=\"next\"><a href=\"/chapter15/language_and_theme_setting.html\">\n        15.8 多语言和多主题\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/93.54d0e6b6.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter15/models.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>15.3 Model类定义 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/154.40b97c4a.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter15/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading open\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" aria-current=\"page\" class=\"active sidebar-link\">15.3 Model类定义</a><ul class=\"sidebar-sub-headers\"></ul></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_15-3-model类定义\"><a href=\"#_15-3-model类定义\" class=\"header-anchor\">#</a> 15.3 Model类定义</h1> <p>本节我们先梳理一下APP中将用到的数据，然后生成相应的Dart Model类。Json文件转Dart Model的方案采用前面介绍过的 json_model 包方案</p> <h3 id=\"github账号信息\"><a href=\"#github账号信息\" class=\"header-anchor\">#</a> Github账号信息</h3> <p>登录Github后，我们需要获取当前登录者的Github账号信息，Github API接口返回Json结构如下：</p> <div class=\"language-json extra-class\"><pre class=\"language-json\"><code><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;login&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;octocat&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//用户登录名</span>\n  <span class=\"token property\">&quot;avatar_url&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;https://github.com/images/error/octocat_happy.gif&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//用户头像地址</span>\n  <span class=\"token property\">&quot;type&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;User&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//用户类型，可能是组织</span>\n  <span class=\"token property\">&quot;name&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;monalisa octocat&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//用户名字</span>\n  <span class=\"token property\">&quot;company&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;GitHub&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//公司</span>\n  <span class=\"token property\">&quot;blog&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;https://github.com/blog&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//博客地址</span>\n  <span class=\"token property\">&quot;location&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;San Francisco&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 用户所处地理位置</span>\n  <span class=\"token property\">&quot;email&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;octocat@github.com&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 邮箱</span>\n  <span class=\"token property\">&quot;hireable&quot;</span><span class=\"token operator\">:</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;bio&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;There once was...&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 用户简介</span>\n  <span class=\"token property\">&quot;public_repos&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 公开项目数</span>\n  <span class=\"token property\">&quot;followers&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">20</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//关注该用户的人数</span>\n  <span class=\"token property\">&quot;following&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 该用户关注的人数</span>\n  <span class=\"token property\">&quot;created_at&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;2008-01-14T04:33:35Z&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 账号创建时间</span>\n  <span class=\"token property\">&quot;updated_at&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;2008-01-14T04:33:35Z&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 账号信息更新时间</span>\n  <span class=\"token property\">&quot;total_private_repos&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">100</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//该用户总的私有项目数(包括参与的其它组织的私有项目)</span>\n  <span class=\"token property\">&quot;owned_private_repos&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">100</span> <span class=\"token comment\">//该用户自己的私有项目数</span>\n  ... <span class=\"token comment\">//省略其它字段</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们在“jsons”目录下创建一个“user.json”文件保存上述信息。</p> <h3 id=\"api缓存策略信息\"><a href=\"#api缓存策略信息\" class=\"header-anchor\">#</a> API缓存策略信息</h3> <p>由于Github服务器在国内访问速度较慢，我们对Github API应用一些简单的缓存策略。我们在“jsons”目录下创建一个“cacheConfig.json”文件缓存策略信息，定义如下：</p> <div class=\"language-json extra-class\"><pre class=\"language-json\"><code><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;enable&quot;</span><span class=\"token operator\">:</span><span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 是否启用缓存</span>\n  <span class=\"token property\">&quot;maxAge&quot;</span><span class=\"token operator\">:</span><span class=\"token number\">1000</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 缓存的最长时间，单位（秒）</span>\n  <span class=\"token property\">&quot;maxCount&quot;</span><span class=\"token operator\">:</span><span class=\"token number\">100</span> <span class=\"token comment\">// 最大缓存数</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h3 id=\"用户信息\"><a href=\"#用户信息\" class=\"header-anchor\">#</a> 用户信息</h3> <p>用户信息(Profile)应包括如下信息：</p> <ol><li>Github账号信息；由于我们的APP可以切换账号登录，且登录后再次打开则不需要登录，所以我们需要对用户账号信息和登录状态进行持久化。</li> <li>应用使用配置信息；每一个用户都应有自己的APP配置信息，如主题、语言、以及数据缓存策略等。</li> <li>用户注销登录后，为了便于用户在退出APP前再次登录，我们需要记住上次登录的用户名。</li></ol> <p>需要注意的是，目前Github有三种登录方式，分别是账号密码登录、oauth授权登录、二次认证登录；这三种登录方式的安全性依次加强，但是在本示例中，为了简单起见，我们使用账号密码登录，因此我们需要保存用户的密码。</p> <blockquote><p>注意：在这里需要提醒读者，在登录场景中，保护用户账号安全是一个非常重要且永恒的话题，在实际开发中应严格杜绝直接明文存储用户账密的行为。</p></blockquote> <p>我们在“jsons”目录下创建一个“profile.json”文件，结构如下：</p> <div class=\"language-json extra-class\"><pre class=\"language-json\"><code><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;user&quot;</span><span class=\"token operator\">:</span><span class=\"token string\">&quot;$user&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//Github账号信息，结构见&quot;user.json&quot;</span>\n  <span class=\"token property\">&quot;token&quot;</span><span class=\"token operator\">:</span><span class=\"token string\">&quot;&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 登录用户的token(oauth)或密码</span>\n  <span class=\"token property\">&quot;theme&quot;</span><span class=\"token operator\">:</span><span class=\"token number\">5678</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//主题色值</span>\n  <span class=\"token property\">&quot;cache&quot;</span><span class=\"token operator\">:</span><span class=\"token string\">&quot;$cacheConfig&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 缓存策略信息，结构见&quot;cacheConfig.json&quot;</span>\n  <span class=\"token property\">&quot;lastLogin&quot;</span><span class=\"token operator\">:</span><span class=\"token string\">&quot;&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//最近一次的注销登录的用户名</span>\n  <span class=\"token property\">&quot;locale&quot;</span><span class=\"token operator\">:</span><span class=\"token string\">&quot;&quot;</span> <span class=\"token comment\">// APP语言信息</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h3 id=\"项目信息\"><a href=\"#项目信息\" class=\"header-anchor\">#</a> 项目信息</h3> <p>由于APP主页要显示其所有项目信息，我们在“jsons”目录下创建一个“repo.json”文件保存项目信息。通过参考Github 获取项目信息的API文档，定义出最终的“repo.json”文件结构，如下：</p> <div class=\"language-json extra-class\"><pre class=\"language-json\"><code><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;id&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">1296269</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;name&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;Hello-World&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//项目名称</span>\n  <span class=\"token property\">&quot;full_name&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;octocat/Hello-World&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//项目完整名称</span>\n  <span class=\"token property\">&quot;owner&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;$user&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 项目拥有者，结构见&quot;user.json&quot;</span>\n  <span class=\"token property\">&quot;parent&quot;</span><span class=\"token operator\">:</span><span class=\"token string\">&quot;$repo&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 如果是fork的项目，则此字段表示fork的父项目信息</span>\n  <span class=\"token property\">&quot;private&quot;</span><span class=\"token operator\">:</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 是否私有项目</span>\n  <span class=\"token property\">&quot;description&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;This your first repo!&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//项目描述</span>\n  <span class=\"token property\">&quot;fork&quot;</span><span class=\"token operator\">:</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 该项目是否为fork的项目</span>\n  <span class=\"token property\">&quot;language&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;JavaScript&quot;</span><span class=\"token punctuation\">,</span><span class=\"token comment\">//该项目的主要编程语言</span>\n  <span class=\"token property\">&quot;forks_count&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">9</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// fork了该项目的数量</span>\n  <span class=\"token property\">&quot;stargazers_count&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">80</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//该项目的star数量</span>\n  <span class=\"token property\">&quot;size&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">108</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 项目占用的存储大小</span>\n  <span class=\"token property\">&quot;default_branch&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;master&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//项目的默认分支</span>\n  <span class=\"token property\">&quot;open_issues_count&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//该项目当前打开的issue数量</span>\n  <span class=\"token property\">&quot;pushed_at&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;2011-01-26T19:06:43Z&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;created_at&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;2011-01-26T19:01:12Z&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;updated_at&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;2011-01-26T19:14:43Z&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;subscribers_count&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">42</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//订阅（关注）该项目的人数</span>\n  <span class=\"token property\">&quot;license&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> <span class=\"token comment\">// 该项目的开源许可证</span>\n    <span class=\"token property\">&quot;key&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;mit&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;name&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;MIT License&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;spdx_id&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;MIT&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;url&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;https://api.github.com/licenses/mit&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;node_id&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;MDc6TGljZW5zZW1pdA==&quot;</span>\n  <span class=\"token punctuation\">}</span>\n  ...<span class=\"token comment\">//省略其它字段</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h3 id=\"生成dart-model类\"><a href=\"#生成dart-model类\" class=\"header-anchor\">#</a> 生成Dart Model类</h3> <p>现在，我们需要的Json数据已经定义完毕，现在只需要运行json_model package提供的命令来通过json文件生成相应的Dart类：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>flutter packages pub run json_model\n</code></pre></div><p>命令执行成功后，可以看到lib/models文件夹下会生成相应的Dart Model类：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>├── models\n│   ├── cacheConfig.dart\n│   ├── cacheConfig.g.dart\n│   ├── index.dart\n│   ├── profile.dart\n│   ├── profile.g.dart\n│   ├── repo.dart\n│   ├── repo.g.dart\n│   ├── user.dart\n│   └── user.g.dart\n\n</code></pre></div><h3 id=\"数据持久化\"><a href=\"#数据持久化\" class=\"header-anchor\">#</a> 数据持久化</h3> <p>我们使用shared_preferences包来对登录用户的Profile信息进行持久化。shared_preferences是一个Flutter插件，它通过Android和iOS平台提供的机制来实现数据持久化。由于shared_preferences的使用非常简单，读者可以自行查看其文档，在此不再赘述。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter15/code_structure.html\" class=\"prev\">\n        15.2 Flutter APP代码结构\n      </a></span> <span class=\"next\"><a href=\"/chapter15/globals.html\">\n        15.4 全局变量及共享状态\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/154.40b97c4a.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter15/network.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>15.5 网络请求封装 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/155.b55f4193.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter15/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading open\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" aria-current=\"page\" class=\"active sidebar-link\">15.5 网络请求封装</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/chapter15/network.html#_15-5-1-网络接口缓存\" class=\"sidebar-link\">15.5.1 网络接口缓存</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter15/network.html#_15-5-2-封装网络请求\" class=\"sidebar-link\">15.5.2 封装网络请求</a></li></ul></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_15-5-网络请求封装\"><a href=\"#_15-5-网络请求封装\" class=\"header-anchor\">#</a> 15.5 网络请求封装</h1> <p>本节我们会基于前面介绍过的dio网络库封装APP中用到的网络请求接口，并同时应用一个简单的缓存策略。下面我们先介绍一下网络接口缓存原理，然后再封装APP的业务请求接口。</p> <h2 id=\"_15-5-1-网络接口缓存\"><a href=\"#_15-5-1-网络接口缓存\" class=\"header-anchor\">#</a> 15.5.1 网络接口缓存</h2> <p>由于在国内访问Github服务器速度较慢，所以我们应用一些简单的缓存策略：将请求的url作为key，对请求的返回值在一个指定时间段类进行缓存，另外设置一个最大缓存数，当超过最大缓存数后移除最早的一条缓存。但是也得提供一种针对特定接口或请求决定是否启用缓存的机制，这种机制可以指定哪些接口或那次请求不应用缓存，这种机制是很有必要的，比如登录接口就不应该缓存，又比如用户在下拉刷新时就不应该再应用缓存。在实现缓存之前我们先定义保存缓存信息的<code>CacheObject</code>类：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">CacheObject</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">CacheObject</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>response<span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">:</span> timeStamp <span class=\"token operator\">=</span> DateTime<span class=\"token punctuation\">.</span><span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>millisecondsSinceEpoch<span class=\"token punctuation\">;</span>\n  Response response<span class=\"token punctuation\">;</span>\n  int timeStamp<span class=\"token punctuation\">;</span> <span class=\"token comment\">// 缓存创建时间</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  bool <span class=\"token keyword\">operator</span> <span class=\"token operator\">==</span><span class=\"token punctuation\">(</span>other<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> response<span class=\"token punctuation\">.</span>hashCode <span class=\"token operator\">==</span> other<span class=\"token punctuation\">.</span>hashCode<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">//将请求uri作为缓存的key</span>\n  <span class=\"token metadata symbol\">@override</span>\n  int <span class=\"token keyword\">get</span> hashCode <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> response<span class=\"token punctuation\">.</span>realUri<span class=\"token punctuation\">.</span>hashCode<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>接下来我们需要实现具体的缓存策略，由于我们使用的是dio package，所以我们可以直接通过拦截器来实现缓存策略：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'dart:collection'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:dio/dio.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'../index.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">CacheObject</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">CacheObject</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>response<span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">:</span> timeStamp <span class=\"token operator\">=</span> DateTime<span class=\"token punctuation\">.</span><span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>millisecondsSinceEpoch<span class=\"token punctuation\">;</span>\n  Response response<span class=\"token punctuation\">;</span>\n  int timeStamp<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  bool <span class=\"token keyword\">operator</span> <span class=\"token operator\">==</span><span class=\"token punctuation\">(</span>other<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> response<span class=\"token punctuation\">.</span>hashCode <span class=\"token operator\">==</span> other<span class=\"token punctuation\">.</span>hashCode<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  int <span class=\"token keyword\">get</span> hashCode <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> response<span class=\"token punctuation\">.</span>realUri<span class=\"token punctuation\">.</span>hashCode<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">NetCache</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">Interceptor</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// 为确保迭代器顺序和对象插入时间一致顺序一致，我们使用LinkedHashMap</span>\n  <span class=\"token keyword\">var</span> cache <span class=\"token operator\">=</span> LinkedHashMap<span class=\"token operator\">&lt;</span>String<span class=\"token punctuation\">,</span> CacheObject<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token function\">onRequest</span><span class=\"token punctuation\">(</span>RequestOptions options<span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>Global<span class=\"token punctuation\">.</span>profile<span class=\"token punctuation\">.</span>cache<span class=\"token punctuation\">.</span>enable<span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> options<span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">// refresh标记是否是&quot;下拉刷新&quot;</span>\n    bool refresh <span class=\"token operator\">=</span> options<span class=\"token punctuation\">.</span>extra<span class=\"token punctuation\">[</span><span class=\"token string\">&quot;refresh&quot;</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">==</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//如果是下拉刷新，先删除相关缓存</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>refresh<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>options<span class=\"token punctuation\">.</span>extra<span class=\"token punctuation\">[</span><span class=\"token string\">&quot;list&quot;</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">==</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">//若是列表，则只要url中包含当前path的缓存全部删除（简单实现，并不精准）</span>\n        cache<span class=\"token punctuation\">.</span><span class=\"token function\">removeWhere</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">,</span> v<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> key<span class=\"token punctuation\">.</span><span class=\"token function\">contains</span><span class=\"token punctuation\">(</span>options<span class=\"token punctuation\">.</span>path<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">// 如果不是列表，则只删除uri相同的缓存</span>\n        <span class=\"token function\">delete</span><span class=\"token punctuation\">(</span>options<span class=\"token punctuation\">.</span>uri<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n      <span class=\"token keyword\">return</span> options<span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>options<span class=\"token punctuation\">.</span>extra<span class=\"token punctuation\">[</span><span class=\"token string\">&quot;noCache&quot;</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">!=</span> <span class=\"token boolean\">true</span> <span class=\"token operator\">&amp;&amp;</span>\n        options<span class=\"token punctuation\">.</span>method<span class=\"token punctuation\">.</span><span class=\"token function\">toLowerCase</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">==</span> <span class=\"token string\">'get'</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      String key <span class=\"token operator\">=</span> options<span class=\"token punctuation\">.</span>extra<span class=\"token punctuation\">[</span><span class=\"token string\">&quot;cacheKey&quot;</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">?</span><span class=\"token operator\">?</span> options<span class=\"token punctuation\">.</span>uri<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">var</span> ob <span class=\"token operator\">=</span> cache<span class=\"token punctuation\">[</span>key<span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>ob <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">//若缓存未过期，则返回缓存内容</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>DateTime<span class=\"token punctuation\">.</span><span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>millisecondsSinceEpoch <span class=\"token operator\">-</span> ob<span class=\"token punctuation\">.</span>timeStamp<span class=\"token punctuation\">)</span> <span class=\"token operator\">/</span> <span class=\"token number\">1000</span> <span class=\"token operator\">&lt;</span>\n            Global<span class=\"token punctuation\">.</span>profile<span class=\"token punctuation\">.</span>cache<span class=\"token punctuation\">.</span>maxAge<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">return</span> cache<span class=\"token punctuation\">[</span>key<span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span>response<span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token comment\">//若已过期则删除缓存，继续向服务器请求</span>\n          cache<span class=\"token punctuation\">.</span><span class=\"token function\">remove</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token function\">onError</span><span class=\"token punctuation\">(</span>DioError err<span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 错误状态不缓存</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token function\">onResponse</span><span class=\"token punctuation\">(</span>Response response<span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 如果启用缓存，将返回结果保存到缓存</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>Global<span class=\"token punctuation\">.</span>profile<span class=\"token punctuation\">.</span>cache<span class=\"token punctuation\">.</span>enable<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">_saveCache</span><span class=\"token punctuation\">(</span>response<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token function\">_saveCache</span><span class=\"token punctuation\">(</span>Response object<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    RequestOptions options <span class=\"token operator\">=</span> object<span class=\"token punctuation\">.</span>request<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>options<span class=\"token punctuation\">.</span>extra<span class=\"token punctuation\">[</span><span class=\"token string\">&quot;noCache&quot;</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">!=</span> <span class=\"token boolean\">true</span> <span class=\"token operator\">&amp;&amp;</span>\n        options<span class=\"token punctuation\">.</span>method<span class=\"token punctuation\">.</span><span class=\"token function\">toLowerCase</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">==</span> <span class=\"token string\">&quot;get&quot;</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">// 如果缓存数量超过最大数量限制，则先移除最早的一条记录</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>cache<span class=\"token punctuation\">.</span>length <span class=\"token operator\">==</span> Global<span class=\"token punctuation\">.</span>profile<span class=\"token punctuation\">.</span>cache<span class=\"token punctuation\">.</span>maxCount<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        cache<span class=\"token punctuation\">.</span><span class=\"token function\">remove</span><span class=\"token punctuation\">(</span>cache<span class=\"token punctuation\">[</span>cache<span class=\"token punctuation\">.</span>keys<span class=\"token punctuation\">.</span>first<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n      String key <span class=\"token operator\">=</span> options<span class=\"token punctuation\">.</span>extra<span class=\"token punctuation\">[</span><span class=\"token string\">&quot;cacheKey&quot;</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">?</span><span class=\"token operator\">?</span> options<span class=\"token punctuation\">.</span>uri<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      cache<span class=\"token punctuation\">[</span>key<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">CacheObject</span><span class=\"token punctuation\">(</span>object<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">delete</span><span class=\"token punctuation\">(</span>String key<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    cache<span class=\"token punctuation\">.</span><span class=\"token function\">remove</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>关于代码的解释都在注释中了，在此需要说明的是dio包的<code>option.extra</code>是专门用于扩展请求参数的，我们通过定义了“refresh”和“noCache”两个参数实现了“针对特定接口或请求决定是否启用缓存的机制”，这两个参数含义如下：</p> <table><thead><tr><th>参数名</th> <th>类型</th> <th>解释</th></tr></thead> <tbody><tr><td>refresh</td> <td>bool</td> <td>如果为true，则本次请求不使用缓存，但新的请求结果依然会被缓存</td></tr> <tr><td>noCache</td> <td>bool</td> <td>本次请求禁用缓存，请求结果也不会被缓存。</td></tr></tbody></table> <h2 id=\"_15-5-2-封装网络请求\"><a href=\"#_15-5-2-封装网络请求\" class=\"header-anchor\">#</a> 15.5.2 封装网络请求</h2> <p>一个完整的APP，可能会涉及很多网络请求，为了便于管理、收敛请求入口，工程上最好的作法就是将所有网络请求放到同一个源码文件中。由于我们的接口都是请求的Github 开发平台提供的API，所以我们定义一个Git类，专门用于Github API接口调用。另外，在调试过程中，我们通常需要一些工具来查看网络请求、响应报文，使用网络代理工具来调试网络数据问题是主流方式。配置代理需要在应用中指定代理服务器的地址和端口，另外Github API是HTTPS协议，所以在配置完代理后还应该禁用证书校验，这些配置我们在Git类初始化时执行（<code>init()方法</code>）。下面是Git类的源码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'dart:async'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'dart:convert'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'dart:io'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:dio/dio.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:dio/adapter.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'../index.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">Git</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// 在网络请求过程中可能会需要使用当前的context信息，比如在请求失败时</span>\n  <span class=\"token comment\">// 打开一个新路由，而打开新路由需要context信息。</span>\n  <span class=\"token function\">Git</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>context<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    _options <span class=\"token operator\">=</span> <span class=\"token function\">Options</span><span class=\"token punctuation\">(</span>extra<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span><span class=\"token string\">&quot;context&quot;</span><span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  BuildContext context<span class=\"token punctuation\">;</span>\n  Options _options<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">static</span> Dio dio <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Dio</span><span class=\"token punctuation\">(</span><span class=\"token function\">BaseOptions</span><span class=\"token punctuation\">(</span>\n    baseUrl<span class=\"token punctuation\">:</span> <span class=\"token string\">'https://api.github.com/'</span><span class=\"token punctuation\">,</span>\n    headers<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span>\n      HttpHeaders<span class=\"token punctuation\">.</span>acceptHeader<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;application/vnd.github.squirrel-girl-preview,&quot;</span>\n          <span class=\"token string\">&quot;application/vnd.github.symmetra-preview+json&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">static</span> <span class=\"token keyword\">void</span> <span class=\"token function\">init</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 添加缓存插件</span>\n    dio<span class=\"token punctuation\">.</span>interceptors<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span>Global<span class=\"token punctuation\">.</span>netCache<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">// 设置用户token（可能为null，代表未登录）</span>\n    dio<span class=\"token punctuation\">.</span>options<span class=\"token punctuation\">.</span>headers<span class=\"token punctuation\">[</span>HttpHeaders<span class=\"token punctuation\">.</span>authorizationHeader<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> Global<span class=\"token punctuation\">.</span>profile<span class=\"token punctuation\">.</span>token<span class=\"token punctuation\">;</span>\n\n    <span class=\"token comment\">// 在调试模式下需要抓包调试，所以我们使用代理，并禁用HTTPS证书校验</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>Global<span class=\"token punctuation\">.</span>isRelease<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token punctuation\">(</span>dio<span class=\"token punctuation\">.</span>httpClientAdapter <span class=\"token operator\">as</span> DefaultHttpClientAdapter<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>onHttpClientCreate <span class=\"token operator\">=</span>\n          <span class=\"token punctuation\">(</span>client<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        client<span class=\"token punctuation\">.</span>findProxy <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>uri<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">return</span> <span class=\"token string\">&quot;PROXY 10.1.10.250:8888&quot;</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n        <span class=\"token comment\">//代理工具会提供一个抓包的自签名证书，会通不过证书校验，所以我们禁用证书校验</span>\n        client<span class=\"token punctuation\">.</span>badCertificateCallback <span class=\"token operator\">=</span>\n            <span class=\"token punctuation\">(</span>X509Certificate cert<span class=\"token punctuation\">,</span> String host<span class=\"token punctuation\">,</span> int port<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">// 登录接口，登录成功后返回用户信息</span>\n  Future<span class=\"token operator\">&lt;</span>User<span class=\"token operator\">&gt;</span> <span class=\"token function\">login</span><span class=\"token punctuation\">(</span>String login<span class=\"token punctuation\">,</span> String pwd<span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    String basic <span class=\"token operator\">=</span> <span class=\"token string\">'Basic '</span> <span class=\"token operator\">+</span> base64<span class=\"token punctuation\">.</span><span class=\"token function\">encode</span><span class=\"token punctuation\">(</span>utf8<span class=\"token punctuation\">.</span><span class=\"token function\">encode</span><span class=\"token punctuation\">(</span><span class=\"token string\">'$login:$pwd'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">var</span> r <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> dio<span class=\"token punctuation\">.</span><span class=\"token keyword\">get</span><span class=\"token punctuation\">(</span>\n      <span class=\"token string\">&quot;/users/$login&quot;</span><span class=\"token punctuation\">,</span>\n      options<span class=\"token punctuation\">:</span> _options<span class=\"token punctuation\">.</span><span class=\"token function\">merge</span><span class=\"token punctuation\">(</span>headers<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span>\n        HttpHeaders<span class=\"token punctuation\">.</span>authorizationHeader<span class=\"token punctuation\">:</span> basic\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> extra<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token string\">&quot;noCache&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//本接口禁用缓存</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//登录成功后更新公共头（authorization），此后的所有请求都会带上用户身份信息</span>\n    dio<span class=\"token punctuation\">.</span>options<span class=\"token punctuation\">.</span>headers<span class=\"token punctuation\">[</span>HttpHeaders<span class=\"token punctuation\">.</span>authorizationHeader<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> basic<span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//清空所有缓存</span>\n    Global<span class=\"token punctuation\">.</span>netCache<span class=\"token punctuation\">.</span>cache<span class=\"token punctuation\">.</span><span class=\"token function\">clear</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//更新profile中的token信息</span>\n    Global<span class=\"token punctuation\">.</span>profile<span class=\"token punctuation\">.</span>token <span class=\"token operator\">=</span> basic<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> User<span class=\"token punctuation\">.</span><span class=\"token function\">fromJson</span><span class=\"token punctuation\">(</span>r<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">//获取用户项目列表</span>\n  Future<span class=\"token operator\">&lt;</span>List<span class=\"token operator\">&lt;</span>Repo<span class=\"token operator\">&gt;&gt;</span> <span class=\"token function\">getRepos</span><span class=\"token punctuation\">(</span>\n      <span class=\"token punctuation\">{</span>Map<span class=\"token operator\">&lt;</span>String<span class=\"token punctuation\">,</span> <span class=\"token keyword\">dynamic</span><span class=\"token operator\">&gt;</span> queryParameters<span class=\"token punctuation\">,</span> <span class=\"token comment\">//query参数，用于接收分页信息</span>\n      refresh <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>refresh<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">// 列表下拉刷新，需要删除缓存（拦截器中会读取这些信息）</span>\n      _options<span class=\"token punctuation\">.</span>extra<span class=\"token punctuation\">.</span><span class=\"token function\">addAll</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span><span class=\"token string\">&quot;refresh&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;list&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">var</span> r <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> dio<span class=\"token punctuation\">.</span><span class=\"token keyword\">get</span><span class=\"token operator\">&lt;</span>List<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n      <span class=\"token string\">&quot;user/repos&quot;</span><span class=\"token punctuation\">,</span>\n      queryParameters<span class=\"token punctuation\">:</span> queryParameters<span class=\"token punctuation\">,</span>\n      options<span class=\"token punctuation\">:</span> _options<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> r<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Repo<span class=\"token punctuation\">.</span><span class=\"token function\">fromJson</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toList</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以看到我们在<code>init()</code>方法中，我们判断了是否是调试环境，然后做了一些针对调试环境的网络配置（设置代理和禁用证书校验）。而<code>Git.init()</code>方法是应用启动时被调用的（<code>Global.init()</code>方法中会调用<code>Git.init()</code>）。</p> <p>另外需要注意，我们所有的网络请求是通过同一个<code>dio</code>实例（静态变量）发出的，在创建该<code>dio</code>实例时我们将Github API的基地址和API支持的Header进行了全局配置，这样所有通过该<code>dio</code>实例发出的请求都会默认使用者些配置。</p> <p>在本实例中，我们只用到了登录接口和获取用户项目的接口，所以在<code>Git</code>类中只定义了<code>login(…)</code>和<code>getRepos(…)</code>方法，如果读者要在本实例的基础上扩充功能，读者可以将其它的接口请求方法添加到<code>Git</code>类中，这样便实现了网络请求接口在代码层面的集中管理和维护。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter15/globals.html\" class=\"prev\">\n        15.4 全局变量及共享状态\n      </a></span> <span class=\"next\"><a href=\"/chapter15/entry.html\">\n        15.6 APP入口及主页\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/155.b55f4193.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter2/first_flutter_app.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>2.1 计数器应用示例 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/94.c6e7a2f7.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable open\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" aria-current=\"page\" class=\"active sidebar-link\">2.1 计数器应用示例</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/chapter2/first_flutter_app.html#_2-1-1-创建flutter应用模板\" class=\"sidebar-link\">2.1.1 创建Flutter应用模板</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter2/first_flutter_app.html#_2-1-2-首页\" class=\"sidebar-link\">2.1.2 首页</a></li></ul></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_2-1-计数器应用示例\"><a href=\"#_2-1-计数器应用示例\" class=\"header-anchor\">#</a> 2.1 计数器应用示例</h1> <p>用Android Studio和VS Code创建的Flutter应用模板默认是一个简单的计数器示例。本节先仔细讲解一下这个计数器Demo的源码，让读者对Flutter应用程序结构有个基本了解，然后在随后的小节中将会基于此示例，一步一步添加一些新的功能来介绍Flutter应用的其它概念与技术。</p> <p>对于接下来的示例，希望读者可以跟着笔者一起亲自动手来写一下，这样不仅可以加深印象，而且也会对介绍的概念与技术有一个真切的体会。如果你还不是很熟悉Dart语言或者没有移动开发经验，不用担心，只要你熟悉面向对象和基本编程概念（如变量、循环和条件控制），则可以完成本示例。</p> <h2 id=\"_2-1-1-创建flutter应用模板\"><a href=\"#_2-1-1-创建flutter应用模板\" class=\"header-anchor\">#</a> 2.1.1 创建Flutter应用模板</h2> <p>通过Android Studio或VS Code创建一个新的Flutter工程，命名为&quot;first_flutter_app&quot;。创建好后，就会得到一个计数器应用的Demo。</p> <blockquote><p>注意，默认Demo示例可能随着编辑器Flutter插件的版本变化而变化，本例中会介绍计数器示例的全部代码，所以不会对本示例产生影响。</p></blockquote> <p>我们先运行创建的工程，效果如图2-1所示：</p> <p><img src=\"/assets/img/2-1.801e91b2.png\" alt=\"图2-1\"></p> <p>该计数器示例中，每点击一次右下角带“+”号的悬浮按钮，屏幕中央的数字就会加1。</p> <p>在这个示例中，主要Dart代码是在 <strong>lib/main.dart</strong> 文件中，下面是它的源码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">void</span> <span class=\"token function\">main</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">runApp</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">MyApp</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">MyApp</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">MaterialApp</span><span class=\"token punctuation\">(</span>\n      title<span class=\"token punctuation\">:</span> <span class=\"token string\">'Flutter Demo'</span><span class=\"token punctuation\">,</span>\n      theme<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">ThemeData</span><span class=\"token punctuation\">(</span>\n        primarySwatch<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      home<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">MyHomePage</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token string\">'Flutter Demo Home Page'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">MyHomePage</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">MyHomePage</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>Key key<span class=\"token punctuation\">,</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>title<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> String title<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  _MyHomePageState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_MyHomePageState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_MyHomePageState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>MyHomePage<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  int _counter <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_incrementCounter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _counter<span class=\"token operator\">++</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Scaffold</span><span class=\"token punctuation\">(</span>\n      appBar<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AppBar</span><span class=\"token punctuation\">(</span>\n        title<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span>widget<span class=\"token punctuation\">.</span>title<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      body<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Center</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Column</span><span class=\"token punctuation\">(</span>\n          mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span>\n              <span class=\"token string\">'You have pushed the button this many times:'</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span>\n              <span class=\"token string\">'$_counter'</span><span class=\"token punctuation\">,</span>\n              style<span class=\"token punctuation\">:</span> Theme<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>textTheme<span class=\"token punctuation\">.</span>headline4<span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      floatingActionButton<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">FloatingActionButton</span><span class=\"token punctuation\">(</span>\n        onPressed<span class=\"token punctuation\">:</span> _incrementCounter<span class=\"token punctuation\">,</span>\n        tooltip<span class=\"token punctuation\">:</span> <span class=\"token string\">'Increment'</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>add<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// This trailing comma makes auto-formatting nicer for build methods.</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n</code></pre></div><h3 id=\"分析\"><a href=\"#分析\" class=\"header-anchor\">#</a> 分析</h3> <ol><li><p>导入包。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>此行代码作用是导入了Material UI组件库。<a href=\"https://material.io/guidelines/\" target=\"_blank\" rel=\"noopener noreferrer\">Material<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>是一种标准的移动端和web端的视觉设计语言， Flutter默认提供了一套丰富的Material风格的UI组件。</p></li> <li><p>应用入口。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">main</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">runApp</span><span class=\"token punctuation\">(</span><span class=\"token function\">MyApp</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><ul><li>与C/C++、Java类似，Flutter 应用中<code>main</code>函数为应用程序的入口。<code>main</code>函数中调用了<code>runApp</code> 方法，它的功能是启动Flutter应用。<code>runApp</code>它接受一个<code>Widget</code>参数，在本示例中它是一个<code>MyApp</code>对象，<code>MyApp()</code>是Flutter应用的根组件。</li> <li><code>main</code>函数使用了(<code>=&gt;</code>)符号，这是Dart中单行函数或方法的简写。</li></ul></li> <li><p>应用结构。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">MyApp</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">MaterialApp</span><span class=\"token punctuation\">(</span>\n      <span class=\"token comment\">//应用名称  </span>\n      title<span class=\"token punctuation\">:</span> <span class=\"token string\">'Flutter Demo'</span><span class=\"token punctuation\">,</span> \n      theme<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">ThemeData</span><span class=\"token punctuation\">(</span>\n        <span class=\"token comment\">//蓝色主题  </span>\n        primarySwatch<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token comment\">//应用首页路由  </span>\n      home<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">MyHomePage</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token string\">'Flutter Demo Home Page'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><ul><li><p><code>MyApp</code>类代表Flutter应用，它继承了 <code>StatelessWidget</code>类，这也就意味着应用本身也是一个widget。</p></li> <li><p>在Flutter中，大多数东西都是widget（后同“组件”或“部件”），包括对齐(alignment)、填充(padding)和布局(layout)等，它们都是以widget的形式提供。</p></li> <li><p>Flutter在构建页面时，会调用组件的<code>build</code>方法，widget的主要工作是提供一个build()方法来描述如何构建UI界面（通常是通过组合、拼装其它基础widget）。</p></li> <li><p><code>MaterialApp</code> 是Material库中提供的Flutter APP框架，通过它可以设置应用的名称、主题、语言、首页及路由列表等。<code>MaterialApp</code>也是一个widget。</p></li> <li><p><code>home</code> 为Flutter应用的首页，它也是一个widget。</p></li></ul></li></ol> <h2 id=\"_2-1-2-首页\"><a href=\"#_2-1-2-首页\" class=\"header-anchor\">#</a> 2.1.2 首页</h2> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">MyHomePage</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">MyHomePage</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>Key key<span class=\"token punctuation\">,</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>title<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> String title<span class=\"token punctuation\">;</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _MyHomePageState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_MyHomePageState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_MyHomePageState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>MyHomePage<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p><code>MyHomePage</code> 是Flutter应用的首页，它继承自<code>StatefulWidget</code>类，表示它是一个有状态的组件（Stateful widget）。关于Stateful widget我们将在第三章“Widget简介”一节仔细介绍，现在我们只需简单认为有状态的组件（Stateful widget） 和无状态的组件（Stateless widget）有两点不同：</p> <ol><li><p>Stateful widget可以拥有状态，这些状态在widget生命周期中是可以变的，而Stateless widget是不可变的。</p></li> <li><p>Stateful widget至少由两个类组成：</p> <ul><li>一个<code>StatefulWidget</code>类。</li> <li>一个 <code>State</code>类； <code>StatefulWidget</code>类本身是不变的，但是<code>State</code>类中持有的状态在widget生命周期中可能会发生变化。</li></ul> <p><code>_MyHomePageState</code>类是<code>MyHomePage</code>类对应的状态类。看到这里，读者可能已经发现：和<code>MyApp</code> 类不同， <code>MyHomePage</code>类中并没有<code>build</code>方法，取而代之的是，<code>build</code>方法被挪到了<code>_MyHomePageState</code>方法中，至于为什么这么做，先留个疑问，在分析完完整代码后再来解答。</p></li></ol> <h3 id=\"state类\"><a href=\"#state类\" class=\"header-anchor\">#</a> State类</h3> <p>接下来，我们看看<code>_MyHomePageState</code>中都包含哪些东西：</p> <ol><li><p>该组件的状态。由于我们只需要维护一个点击次数计数器，所以定义一个<code>_counter</code>状态：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>int _counter <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//用于记录按钮点击的总次数</span>\n</code></pre></div><p><code>_counter</code> 为保存屏幕右下角带“+”号按钮点击次数的状态。</p></li> <li><p>设置状态的自增函数。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">_incrementCounter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n     _counter<span class=\"token operator\">++</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>当按钮点击时，会调用此函数，该函数的作用是先自增<code>_counter</code>，然后调用<code>setState</code> 方法。<code>setState</code>方法的作用是通知Flutter框架，有状态发生了改变，Flutter框架收到通知后，会执行<code>build</code>方法来根据新的状态重新构建界面， Flutter 对此方法做了优化，使重新执行变的很快，所以你可以重新构建任何需要更新的东西，而无需分别去修改各个widget。</p></li> <li><p>构建UI界面</p> <p>构建UI界面的逻辑在<code>build</code>方法中，当<code>MyHomePage</code>第一次创建时，<code>_MyHomePageState</code>类会被创建，当初始化完成后，Flutter框架会调用Widget的<code>build</code>方法来构建widget树，最终将widget树渲染到设备屏幕上。所以，我们看看<code>_MyHomePageState</code>的<code>build</code>方法中都干了什么事：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Scaffold</span><span class=\"token punctuation\">(</span>\n      appBar<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AppBar</span><span class=\"token punctuation\">(</span>\n        title<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span>widget<span class=\"token punctuation\">.</span>title<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      body<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Center</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Column</span><span class=\"token punctuation\">(</span>\n          mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span>\n              <span class=\"token string\">'You have pushed the button this many times:'</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span>\n              <span class=\"token string\">'$_counter'</span><span class=\"token punctuation\">,</span>\n              style<span class=\"token punctuation\">:</span> Theme<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>textTheme<span class=\"token punctuation\">.</span>headline4<span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      floatingActionButton<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">FloatingActionButton</span><span class=\"token punctuation\">(</span>\n        onPressed<span class=\"token punctuation\">:</span> _incrementCounter<span class=\"token punctuation\">,</span>\n        tooltip<span class=\"token punctuation\">:</span> <span class=\"token string\">'Increment'</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>add<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n</code></pre></div><ul><li><code>Scaffold</code> 是 Material 库中提供的页面脚手架，它提供了默认的导航栏、标题和包含主屏幕widget树（后同“组件树”或“部件树”）的<code>body</code>属性，组件树可以很复杂。本书后面示例中，路由默认都是通过<code>Scaffold</code>创建。</li> <li><code>body</code>的组件树中包含了一个<code>Center</code> 组件，<code>Center</code> 可以将其子组件树对齐到屏幕中心。此例中， <code>Center</code> 子组件是一个<code>Column</code> 组件，<code>Column</code>的作用是将其所有子组件沿屏幕垂直方向依次排列； 此例中<code>Column</code>子组件是两个 <code>Text</code>，第一个<code>Text</code> 显示固定文本 “You have pushed the button this many times:”，第二个<code>Text</code> 显示<code>_counter</code>状态的数值。</li> <li><code>floatingActionButton</code>是页面右下角的带“+”的悬浮按钮，它的<code>onPressed</code>属性接受一个回调函数，代表它被点击后的处理器，本例中直接将<code>_incrementCounter</code>方法作为其处理函数。</li></ul></li></ol> <p>现在，我们将整个计数器执行流程串起来：当右下角的<code>floatingActionButton</code>按钮被点击之后，会调用<code>_incrementCounter</code>方法。在<code>_incrementCounter</code>方法中，首先会自增<code>_counter</code>计数器（状态），然后<code>setState</code>会通知Flutter框架状态发生变化，接着，Flutter框架会调用<code>build</code>方法以新的状态重新构建UI，最终显示在设备屏幕上。</p> <h4 id=\"为什么要将build方法放在state中-而不是放在statefulwidget中\"><a href=\"#为什么要将build方法放在state中-而不是放在statefulwidget中\" class=\"header-anchor\">#</a> 为什么要将build方法放在State中，而不是放在StatefulWidget中？</h4> <p>现在，我们回答之前提出的问题，为什么<code>build()</code>方法放在State（而不是<code>StatefulWidget</code>）中 ？这主要是为了提高开发的灵活性。如果将<code>build()</code>方法放在<code>StatefulWidget</code>中则会有两个问题：</p> <ul><li><p>状态访问不便</p> <p>试想一下，如果我们的<code>StatefulWidget</code>有很多状态，而每次状态改变都要调用<code>build</code>方法，由于状态是保存在State中的，如果<code>build</code>方法在<code>StatefulWidget</code>中，那么<code>build</code>方法和状态分别在两个类中，那么构建时读取状态将会很不方便！试想一下，如果真的将<code>build</code>方法放在StatefulWidget中的话，由于构建用户界面过程需要依赖State，所以<code>build</code>方法将必须加一个<code>State</code>参数，大概是下面这样：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> State state<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">//state.counter</span>\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token punctuation\">}</span>\n</code></pre></div><p>这样的话就只能将State的所有状态声明为公开的状态，这样才能在State类外部访问状态！但是，将状态设置为公开后，状态将不再具有私密性，这就会导致对状态的修改将会变的不可控。但如果将<code>build()</code>方法放在State中的话，构建过程不仅可以直接访问状态，而且也无需公开私有状态，这会非常方便。</p></li> <li><p>继承<code>StatefulWidget</code>不便</p> <p>例如，Flutter中有一个动画widget的基类<code>AnimatedWidget</code>，它继承自<code>StatefulWidget</code>类。<code>AnimatedWidget</code>中引入了一个抽象方法<code>build(BuildContext context)</code>，继承自<code>AnimatedWidget</code>的动画widget都要实现这个<code>build</code>方法。现在设想一下，如果<code>StatefulWidget</code> 类中已经有了一个<code>build</code>方法，正如上面所述，此时<code>build</code>方法需要接收一个state对象，这就意味着<code>AnimatedWidget</code>必须将自己的State对象(记为_animatedWidgetState)提供给其子类，因为子类需要在其<code>build</code>方法中调用父类的<code>build</code>方法，代码可能如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">MyAnimationWidget</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">AnimatedWidget</span><span class=\"token punctuation\">{</span>\n    <span class=\"token metadata symbol\">@override</span>\n    Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> State state<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">//由于子类要用到AnimatedWidget的状态对象_animatedWidgetState，</span>\n      <span class=\"token comment\">//所以AnimatedWidget必须通过某种方式将其状态对象_animatedWidgetState</span>\n      <span class=\"token comment\">//暴露给其子类   </span>\n      <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">build</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> _animatedWidgetState<span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>这样很显然是不合理的，因为</p> <ol><li><code>AnimatedWidget</code>的状态对象是<code>AnimatedWidget</code>内部实现细节，不应该暴露给外部。</li> <li>如果要将父类状态暴露给子类，那么必须得有一种传递机制，而做这一套传递机制是无意义的，因为父子类之间状态的传递和子类本身逻辑是无关的。</li></ol></li></ul> <p>综上所述，可以发现，对于<code>StatefulWidget</code>，将<code>build</code>方法放在State中，可以给开发带来很大的灵活性。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter1/dart.html\" class=\"prev\">\n        1.4 Dart语言简介\n      </a></span> <span class=\"next\"><a href=\"/chapter2/flutter_router.html\">\n        2.2 路由管理\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/94.c6e7a2f7.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter2/flutter_app_debug.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>2.5 调试Flutter应用 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/156.f4128841.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable open\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" aria-current=\"page\" class=\"active sidebar-link\">2.5 调试Flutter应用</a><ul class=\"sidebar-sub-headers\"></ul></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_2-5-调试flutter应用\"><a href=\"#_2-5-调试flutter应用\" class=\"header-anchor\">#</a> 2.5 调试Flutter应用</h1> <p>有各种各样的工具和功能来帮助调试Flutter应用程序。</p> <h3 id=\"dart-分析器\"><a href=\"#dart-分析器\" class=\"header-anchor\">#</a> Dart 分析器</h3> <p>在运行应用程序前，请运行<code>flutter analyze</code>测试你的代码。这个工具是一个静态代码检查工具，它是<code>dartanalyzer</code>工具的一个包装，主要用于分析代码并帮助开发者发现可能的错误，比如，Dart分析器大量使用了代码中的类型注释来帮助追踪问题，避免<code>var</code>、无类型的参数、无类型的列表文字等。</p> <p>如果你使用IntelliJ的Flutter插件，那么分析器在打开IDE时就已经自动启用了，如果读者使用的是其它IDE，强烈建议读者启用Dart 分析器，因为在大多数时候，Dart 分析器可以在代码运行前发现大多数问题。</p> <h3 id=\"dart-observatory-语句级的单步调试和分析器\"><a href=\"#dart-observatory-语句级的单步调试和分析器\" class=\"header-anchor\">#</a> Dart Observatory (语句级的单步调试和分析器)</h3> <p>如果我们使用<code>flutter run</code>启动应用程序，那么当它运行时，我们可以打开Observatory工具的Web页面，例如Observatory默认监听<a href=\"http://127.0.0.1:8100/\" target=\"_blank\" rel=\"noopener noreferrer\">http://127.0.0.1:8100/<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>，可以在浏览器中直接打开该链接。直接使用语句级单步调试器连接到您的应用程序。如果您使用的是IntelliJ，则还可以使用其内置的调试器来调试您的应用程序。</p> <p>Observatory 同时支持分析、检查堆等。有关Observatory的更多信息请参考<a href=\"https://dart-lang.github.io/observatory/\" target=\"_blank\" rel=\"noopener noreferrer\">Observatory 文档<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>。</p> <p>如果您使用Observatory进行分析，请确保通过<code>--profile</code>选项来运行<code>flutter run</code>命令来运行应用程序。 否则，配置文件中将出现的主要问题将是调试断言，以验证框架的各种不变量（请参阅下面的“调试模式断言”）。</p> <h3 id=\"debugger-声明\"><a href=\"#debugger-声明\" class=\"header-anchor\">#</a> <code>debugger()</code> 声明</h3> <p>当使用Dart Observatory（或另一个Dart调试器，例如IntelliJ IDE中的调试器）时，可以使用该<code>debugger()</code>语句插入编程式断点。要使用这个，你必须添加<code>import 'dart:developer';</code>到相关文件顶部。</p> <p><code>debugger()</code>语句采用一个可选<code>when</code>参数，您可以指定该参数仅在特定条件为真时中断，如下所示：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">someFunction</span><span class=\"token punctuation\">(</span>double offset<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">debugger</span><span class=\"token punctuation\">(</span>when<span class=\"token punctuation\">:</span> offset <span class=\"token operator\">&gt;</span> <span class=\"token number\">30.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">// ...</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h3 id=\"print、debugprint、flutter-logs\"><a href=\"#print、debugprint、flutter-logs\" class=\"header-anchor\">#</a> <code>print</code>、<code>debugPrint</code>、<code>flutter logs</code></h3> <p>Dart <code>print()</code>功能将输出到系统控制台，您可以使用<code>flutter logs</code>来查看它（基本上是一个包装<code>adb logcat</code>）。</p> <p>如果你一次输出太多，那么Android有时会丢弃一些日志行。为了避免这种情况，您可以使用Flutter的<code>foundation</code>库中的<a href=\"https://docs.flutter.io/flutter/foundation/debugPrint.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>debugPrint()</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>。 这是一个封装print，它将输出限制在一个级别，避免被Android内核丢弃。</p> <p>Flutter框架中的许多类都有<code>toString</code>实现。按照惯例，这些输出通常包括对象的<code>runtimeType</code>单行输出，通常在表单中ClassName(more information about this instance…)。 树中使用的一些类也具有<code>toStringDeep</code>，从该点返回整个子树的多行描述。已一些具有详细信息<code>toString</code>的类会实现一个<code>toStringShort</code>，它只返回对象的类型或其他非常简短的（一个或两个单词）描述。</p> <h3 id=\"调试模式断言\"><a href=\"#调试模式断言\" class=\"header-anchor\">#</a> 调试模式断言</h3> <p>在Flutter应用调试过程中，Dart <code>assert</code>语句被启用，并且Flutter框架使用它来执行许多运行时检查来验证是否违反一些不可变的规则。</p> <p>当一个不可变的规则被违反时，它被报告给控制台，并带有一些上下文信息来帮助追踪问题的根源。</p> <p>要关闭调试模式并使用发布模式，请使用<code>flutter run --release</code>运行您的应用程序。 这也关闭了Observatory调试器。一个中间模式可以关闭除Observatory之外所有调试辅助工具的，称为“profile mode”，用<code>--profile</code>替代<code>--release</code>即可。</p> <h3 id=\"调试应用程序层\"><a href=\"#调试应用程序层\" class=\"header-anchor\">#</a> 调试应用程序层</h3> <p>Flutter框架的每一层都提供了将其当前状态或事件转储(dump)到控制台（使用<code>debugPrint</code>）的功能。</p> <h4 id=\"widget-树\"><a href=\"#widget-树\" class=\"header-anchor\">#</a> Widget 树</h4> <p>要转储Widgets树的状态，请调用<a href=\"https://docs.flutter.io/flutter/widgets/debugDumpApp.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>debugDumpApp()</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>。 只要应用程序已经构建了至少一次（即在调用<code>build()</code>之后的任何时间），您可以在应用程序未处于构建阶段（即，不在<code>build()</code>方法内调用 ）的任何时间调用此方法（在调用<code>runApp()</code>之后）。</p> <p>如, 这个应用程序:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">void</span> <span class=\"token function\">main</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">runApp</span><span class=\"token punctuation\">(</span>\n    <span class=\"token keyword\">new</span> <span class=\"token class-name\">MaterialApp</span><span class=\"token punctuation\">(</span>\n      home<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AppHome</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">AppHome</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Material</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Center</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">FlatButton</span><span class=\"token punctuation\">(</span>\n          onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token function\">debugDumpApp</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Dump App'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>…会输出这样的内容（精确的细节会根据框架的版本、设备的大小等等而变化）：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>I/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>: WidgetsFlutterBinding - CHECKED MODE\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>: RenderObjectToWidgetAdapter<span class=\"token operator\">&lt;</span>RenderBox<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span>GlobalObjectKey RenderView<span class=\"token punctuation\">(</span><span class=\"token number\">497039273</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span> renderObject: RenderView<span class=\"token punctuation\">)</span>\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>: └MaterialApp<span class=\"token punctuation\">(</span>state: _MaterialAppState<span class=\"token punctuation\">(</span><span class=\"token number\">1009803148</span><span class=\"token punctuation\">))</span>\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:  └ScrollConfiguration<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:   └AnimatedTheme<span class=\"token punctuation\">(</span>duration: 200ms<span class=\"token punctuation\">;</span> state: _AnimatedThemeState<span class=\"token punctuation\">(</span><span class=\"token number\">543295893</span><span class=\"token punctuation\">;</span> ticker inactive<span class=\"token punctuation\">;</span> ThemeDataTween<span class=\"token punctuation\">(</span>ThemeData<span class=\"token punctuation\">(</span>Brightness.light Color<span class=\"token punctuation\">(</span>0xff2196f3<span class=\"token punctuation\">)</span> etc<span class=\"token punctuation\">..</span>.<span class=\"token punctuation\">)</span> → null<span class=\"token punctuation\">))</span><span class=\"token punctuation\">)</span>\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:    └Theme<span class=\"token punctuation\">(</span>ThemeData<span class=\"token punctuation\">(</span>Brightness.light Color<span class=\"token punctuation\">(</span>0xff2196f3<span class=\"token punctuation\">)</span> etc<span class=\"token punctuation\">..</span>.<span class=\"token punctuation\">))</span>\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:     └WidgetsApp<span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span>GlobalObjectKey _MaterialAppState<span class=\"token punctuation\">(</span><span class=\"token number\">1009803148</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span> state: _WidgetsAppState<span class=\"token punctuation\">(</span><span class=\"token number\">552902158</span><span class=\"token punctuation\">))</span>\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:      └CheckedModeBanner<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:       └Banner<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:        └CustomPaint<span class=\"token punctuation\">(</span>renderObject: RenderCustomPaint<span class=\"token punctuation\">)</span>\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:         └DefaultTextStyle<span class=\"token punctuation\">(</span>inherit: <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span> color: Color<span class=\"token punctuation\">(</span>0xd0ff0000<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> family: <span class=\"token string\">&quot;monospace&quot;</span><span class=\"token punctuation\">;</span> size: <span class=\"token number\">48.0</span><span class=\"token punctuation\">;</span> weight: <span class=\"token number\">900</span><span class=\"token punctuation\">;</span> decoration: double Color<span class=\"token punctuation\">(</span>0xffffff00<span class=\"token punctuation\">)</span> TextDecoration.underline<span class=\"token punctuation\">)</span>\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:          └MediaQuery<span class=\"token punctuation\">(</span>MediaQueryData<span class=\"token punctuation\">(</span>size: Size<span class=\"token punctuation\">(</span><span class=\"token number\">411.4</span>, <span class=\"token number\">683.4</span><span class=\"token punctuation\">)</span>, devicePixelRatio: <span class=\"token number\">2.625</span>, textScaleFactor: <span class=\"token number\">1.0</span>, padding: EdgeInsets<span class=\"token punctuation\">(</span><span class=\"token number\">0.0</span>, <span class=\"token number\">24.0</span>, <span class=\"token number\">0.0</span>, <span class=\"token number\">0.0</span><span class=\"token punctuation\">))</span><span class=\"token punctuation\">)</span>\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:           └LocaleQuery<span class=\"token punctuation\">(</span>null<span class=\"token punctuation\">)</span>\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:            └Title<span class=\"token punctuation\">(</span>color: Color<span class=\"token punctuation\">(</span>0xff2196f3<span class=\"token punctuation\">))</span>\n<span class=\"token punctuation\">..</span>. <span class=\"token comment\">#省略剩余内容</span>\n</code></pre></div><p>这是一个“扁平化”的树，显示了通过各种构建函数投影的所有widget（如果你在widget树的根中调用<code>toStringDeepwidget</code>，这是你获得的树）。 你会看到很多在你的应用源代码中没有出现的widget，因为它们是被框架中widget的<code>build()</code>函数插入的。例如，<a href=\"https://docs.flutter.io/flutter/material/InkFeature-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>InkFeature</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>是Material widget的一个实现细节 。</p> <p>当按钮从被按下变为被释放时debugDumpApp()被调用，FlatButton对象同时调用<code>setState()</code>，并将自己标记为&quot;dirty&quot;。 这就是为什么如果你看转储，你会看到特定的对象标记为“dirty”。您还可以查看已注册了哪些手势监听器; 在这种情况下，一个单一的GestureDetector被列出，并且监听“tap”手势（“tap”是<code>TapGestureDetector</code>的<code>toStringShort</code>函数输出的）</p> <p>如果您编写自己的widget，则可以通过覆盖<a href=\"https://docs.flutter.io/flutter/widgets/Widget/debugFillProperties.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>debugFillProperties()</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>来添加信息。 将<a href=\"https://docs.flutter.io/flutter/foundation/DiagnosticsProperty-class.html\" target=\"_blank\" rel=\"noopener noreferrer\">DiagnosticsProperty<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>对象作为方法参数，并调用父类方法。 该函数是该<code>toString</code>方法用来填充小部件描述信息的。</p> <h4 id=\"渲染树\"><a href=\"#渲染树\" class=\"header-anchor\">#</a> 渲染树</h4> <p>如果您尝试调试布局问题，那么Widget树可能不够详细。在这种情况下，您可以通过调用<code>debugDumpRenderTree()</code>转储渲染树。 正如<code>debugDumpApp()</code>，除布局或绘制阶段外，您可以随时调用此函数。作为一般规则，从<a href=\"https://docs.flutter.io/flutter/scheduler/SchedulerBinding/addPersistentFrameCallback.html\" target=\"_blank\" rel=\"noopener noreferrer\">frame 回调<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 或事件处理器中调用它是最佳解决方案。</p> <p>要调用<code>debugDumpRenderTree()</code>，您需要添加<code>import'package:flutter/rendering.dart';</code>到您的源文件。</p> <p>上面这个小例子的输出结果如下所示：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>I/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>: RenderView\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:  │ debug mode enabled - android\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:  │ window size: Size<span class=\"token punctuation\">(</span><span class=\"token number\">1080.0</span>, <span class=\"token number\">1794.0</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">(</span>in physical pixels<span class=\"token punctuation\">)</span>\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:  │ device pixel ratio: <span class=\"token number\">2.625</span> <span class=\"token punctuation\">(</span>physical pixels per logical pixel<span class=\"token punctuation\">)</span>\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:  │ configuration: Size<span class=\"token punctuation\">(</span><span class=\"token number\">411.4</span>, <span class=\"token number\">683.4</span><span class=\"token punctuation\">)</span> at <span class=\"token number\">2</span>.625x <span class=\"token punctuation\">(</span>in logical pixels<span class=\"token punctuation\">)</span>\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:  │\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:  └─child: RenderCustomPaint\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:    │ creator: CustomPaint ← Banner ← CheckedModeBanner ←\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:    │   WidgetsApp-<span class=\"token punctuation\">[</span>GlobalObjectKey _MaterialAppState<span class=\"token punctuation\">(</span><span class=\"token number\">1009803148</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">]</span> ←\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:    │   Theme ← AnimatedTheme ← ScrollConfiguration ← MaterialApp ←\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:    │   <span class=\"token punctuation\">[</span>root<span class=\"token punctuation\">]</span>\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:    │ parentData: <span class=\"token operator\">&lt;</span>none<span class=\"token operator\">&gt;</span>\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:    │ constraints: BoxConstraints<span class=\"token punctuation\">(</span>w<span class=\"token operator\">=</span><span class=\"token number\">411.4</span>, <span class=\"token assign-left variable\">h</span><span class=\"token operator\">=</span><span class=\"token number\">683.4</span><span class=\"token punctuation\">)</span>\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:    │ size: Size<span class=\"token punctuation\">(</span><span class=\"token number\">411.4</span>, <span class=\"token number\">683.4</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">..</span>. <span class=\"token comment\"># 省略</span>\n</code></pre></div><p>这是根<code>RenderObject</code>对象的<code>toStringDeep</code>函数的输出。</p> <p>当调试布局问题时，关键要看的是<code>size</code>和<code>constraints</code>字段。约束沿着树向下传递，尺寸向上传递。</p> <p>如果您编写自己的渲染对象，则可以通过覆盖<a href=\"https://docs.flutter.io/flutter/rendering/Layer/debugFillProperties.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>debugFillProperties()</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>将信息添加到转储。 将<a href=\"https://docs.flutter.io/flutter/foundation/DiagnosticsProperty-class.html\" target=\"_blank\" rel=\"noopener noreferrer\">DiagnosticsProperty<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>对象作为方法的参数，并调用父类方法。</p> <h4 id=\"layer树\"><a href=\"#layer树\" class=\"header-anchor\">#</a> Layer树</h4> <p>读者可以理解为渲染树是可以分层的，而最终绘制需要将不同的层合成起来，而Layer则是绘制时需要合成的层，如果您尝试调试合成问题，则可以使用<a href=\"https://docs.flutter.io/flutter/rendering/debugDumpLayerTree.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>debugDumpLayerTree()</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>。对于上面的例子，它会输出：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>I/flutter : TransformLayer\nI/flutter :  │ creator: [root]\nI/flutter :  │ offset: Offset(0.0, 0.0)\nI/flutter :  │ transform:\nI/flutter :  │   [0] 3.5,0.0,0.0,0.0\nI/flutter :  │   [1] 0.0,3.5,0.0,0.0\nI/flutter :  │   [2] 0.0,0.0,1.0,0.0\nI/flutter :  │   [3] 0.0,0.0,0.0,1.0\nI/flutter :  │\nI/flutter :  ├─child 1: OffsetLayer\nI/flutter :  │ │ creator: RepaintBoundary ← _FocusScope ← Semantics ← Focus-[GlobalObjectKey MaterialPageRoute(560156430)] ← _ModalScope-[GlobalKey 328026813] ← _OverlayEntry-[GlobalKey 388965355] ← Stack ← Overlay-[GlobalKey 625702218] ← Navigator-[GlobalObjectKey _MaterialAppState(859106034)] ← Title ← ⋯\nI/flutter :  │ │ offset: Offset(0.0, 0.0)\nI/flutter :  │ │\nI/flutter :  │ └─child 1: PictureLayer\nI/flutter :  │\nI/flutter :  └─child 2: PictureLayer\n</code></pre></div><p>这是根<code>Layer</code>的<code>toStringDeep</code>输出的。</p> <p>根部的变换是应用设备像素比的变换; 在这种情况下，每个逻辑像素代表3.5个设备像素。</p> <p><code>RepaintBoundary</code> widget在渲染树的层中创建了一个<code>RenderRepaintBoundary</code>。这用于减少需要重绘的需求量。</p> <h3 id=\"语义\"><a href=\"#语义\" class=\"header-anchor\">#</a> 语义</h3> <p>您还可以调用<a href=\"https://docs.flutter.io/flutter/rendering/debugDumpSemanticsTree.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>debugDumpSemanticsTree()</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>获取语义树（呈现给系统可访问性API的树）的转储。 要使用此功能，必须首先启用辅助功能，例如启用系统辅助工具或<code>SemanticsDebugger</code> （下面讨论）。</p> <p>对于上面的例子，它会输出:</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>I/flutter : SemanticsNode(0; Rect.fromLTRB(0.0, 0.0, 411.4, 683.4))\nI/flutter :  ├SemanticsNode(1; Rect.fromLTRB(0.0, 0.0, 411.4, 683.4))\nI/flutter :  │ └SemanticsNode(2; Rect.fromLTRB(0.0, 0.0, 411.4, 683.4); canBeTapped)\nI/flutter :  └SemanticsNode(3; Rect.fromLTRB(0.0, 0.0, 411.4, 683.4))\nI/flutter :    └SemanticsNode(4; Rect.fromLTRB(0.0, 0.0, 82.0, 36.0); canBeTapped; &quot;Dump App&quot;)\n</code></pre></div><h3 id=\"调度\"><a href=\"#调度\" class=\"header-anchor\">#</a> 调度</h3> <p>要找出相对于帧的开始/结束事件发生的位置，可以切换<a href=\"https://docs.flutter.io/flutter/scheduler/debugPrintBeginFrameBanner.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>debugPrintBeginFrameBanner</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>和<a href=\"https://docs.flutter.io/flutter/scheduler/debugPrintEndFrameBanner.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>debugPrintEndFrameBanner</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>布尔值以将帧的开始和结束打印到控制台。</p> <p>例如:</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>I/flutter : ▄▄▄▄▄▄▄▄ Frame 12         30s 437.086ms ▄▄▄▄▄▄▄▄\nI/flutter : Debug print: Am I performing this work more than once per frame?\nI/flutter : Debug print: Am I performing this work more than once per frame?\nI/flutter : ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀\n</code></pre></div><p><a href=\"https://docs.flutter.io/flutter/scheduler/debugPrintScheduleFrameStacks.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>debugPrintScheduleFrameStacks</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>还可以用来打印导致当前帧被调度的调用堆栈。</p> <h3 id=\"可视化调试\"><a href=\"#可视化调试\" class=\"header-anchor\">#</a> 可视化调试</h3> <p>您也可以通过设置<code>debugPaintSizeEnabled</code>为<code>true</code>以可视方式调试布局问题。 这是来自<code>rendering</code>库的布尔值。它可以在任何时候启用，并在为true时影响绘制。 设置它的最简单方法是在<code>void main()</code>的顶部设置。</p> <p>当它被启用时，所有的盒子都会得到一个明亮的深青色边框，padding（来自widget如Padding）显示为浅蓝色，子widget周围有一个深蓝色框， 对齐方式（来自widget如Center和Align）显示为黄色箭头. 空白（如没有任何子节点的Container）以灰色显示。</p> <p><a href=\"https://docs.flutter.io/flutter/rendering/debugPaintBaselinesEnabled.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>debugPaintBaselinesEnabled</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>做了类似的事情，但对于具有基线的对象，文字基线以绿色显示，表意(ideographic)基线以橙色显示。</p> <p><a href=\"https://docs.flutter.io/flutter/rendering/debugPaintPointersEnabled.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>debugPaintPointersEnabled</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>标志打开一个特殊模式，任何正在点击的对象都会以深青色突出显示。 这可以帮助您确定某个对象是否以某种不正确的方式进行hit测试（Flutter检测点击的位置是否有能响应用户操作的widget）,例如，如果它实际上超出了其父项的范围，首先不会考虑通过hit测试。</p> <p>如果您尝试调试合成图层，例如以确定是否以及在何处添加<code>RepaintBoundary</code> widget，则可以使用<a href=\"https://docs.flutter.io/flutter/rendering/debugPaintLayerBordersEnabled.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>debugPaintLayerBordersEnabled</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 标志， 该标志用橙色或轮廓线标出每个层的边界，或者使用<a href=\"https://docs.flutter.io/flutter/rendering/debugRepaintRainbowEnabled.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>debugRepaintRainbowEnabled</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>标志， 只要他们重绘时，这会使该层被一组旋转色所覆盖。</p> <p>所有这些标志只能在调试模式下工作。通常，Flutter框架中以“<code>debug...</code>” 开头的任何内容都只能在调试模式下工作。</p> <h3 id=\"调试动画\"><a href=\"#调试动画\" class=\"header-anchor\">#</a> 调试动画</h3> <p>调试动画最简单的方法是减慢它们的速度。为此，请将<a href=\"https://docs.flutter.io/flutter/scheduler/timeDilation.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>timeDilation</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>变量（在scheduler库中）设置为大于1.0的数字，例如50.0。 最好在应用程序启动时只设置一次。如果您在运行中更改它，尤其是在动画运行时将其值改小，则在观察时可能会出现倒退，这可能会导致断言命中，并且这通常会干扰我们的开发工作。</p> <h3 id=\"调试性能问题\"><a href=\"#调试性能问题\" class=\"header-anchor\">#</a> 调试性能问题</h3> <p>要了解您的应用程序导致重新布局或重新绘制的原因，您可以分别设置<a href=\"https://docs.flutter.io/flutter/rendering/debugPrintMarkNeedsLayoutStacks.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>debugPrintMarkNeedsLayoutStacks</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>和 <a href=\"https://docs.flutter.io/flutter/rendering/debugPrintMarkNeedsPaintStacks.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>debugPrintMarkNeedsPaintStacks</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>标志。 每当渲染盒被要求重新布局和重新绘制时，这些都会将堆栈跟踪记录到控制台。如果这种方法对您有用，您可以使用<code>services</code>库中的<code>debugPrintStack()</code>方法按需打印堆栈痕迹。</p> <h3 id=\"统计应用启动时间\"><a href=\"#统计应用启动时间\" class=\"header-anchor\">#</a> 统计应用启动时间</h3> <p>要收集有关Flutter应用程序启动所需时间的详细信息，可以在运行<code>flutter run</code>时使用<code>trace-startup</code>和<code>profile</code>选项。</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>$ flutter run --trace-startup --profile\n</code></pre></div><p>跟踪输出保存为<code>start_up_info.json</code>，在Flutter工程目录在build目录下。输出列出了从应用程序启动到这些跟踪事件（以微秒捕获）所用的时间：</p> <ul><li>进入Flutter引擎时.</li> <li>展示应用第一帧时.</li> <li>初始化Flutter框架时.</li> <li>完成Flutter框架初始化时.</li></ul> <p>如 :</p> <div class=\"language-json extra-class\"><pre class=\"language-json\"><code><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;engineEnterTimestampMicros&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">96025565262</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;timeToFirstFrameMicros&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">2171978</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;timeToFrameworkInitMicros&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">514585</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;timeAfterFrameworkInitMicros&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">1657393</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h3 id=\"跟踪dart代码性能\"><a href=\"#跟踪dart代码性能\" class=\"header-anchor\">#</a> 跟踪Dart代码性能</h3> <p>要执行自定义性能跟踪和测量Dart任意代码段的wall/CPU时间（类似于在Android上使用<a href=\"https://developer.android.com/studio/profile/systrace.html\" target=\"_blank\" rel=\"noopener noreferrer\">systrace<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>）。 使用<code>dart:developer</code>的<a href=\"https://api.dartlang.org/stable/dart-developer/Timeline-class.html\" target=\"_blank\" rel=\"noopener noreferrer\">Timeline<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>工具来包含你想测试的代码块，例如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Timeline<span class=\"token punctuation\">.</span><span class=\"token function\">startSync</span><span class=\"token punctuation\">(</span><span class=\"token string\">'interesting function'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">// iWonderHowLongThisTakes();</span>\nTimeline<span class=\"token punctuation\">.</span><span class=\"token function\">finishSync</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>然后打开你应用程序的Observatory timeline页面，在“Recorded Streams”中选择‘Dart’复选框，并执行你想测量的功能。</p> <p>刷新页面将在Chrome的<a href=\"https://www.chromium.org/developers/how-tos/trace-event-profiling-tool\" target=\"_blank\" rel=\"noopener noreferrer\">跟踪工具<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>中显示应用按时间顺序排列的timeline记录。</p> <p>请确保运行<code>flutter run</code>时带有<code>--profile</code>标志，以确保运行时性能特征与您的最终产品差异最小。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter2/flutter_assets_mgr.html\" class=\"prev\">\n        2.4 资源管理\n      </a></span> <span class=\"next\"><a href=\"/chapter2/thread_model_and_error_report.html\">\n        2.6 Flutter异常捕获\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/156.f4128841.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter2/flutter_assets_mgr.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>2.4 资源管理 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/26.e656381b.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable open\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" aria-current=\"page\" class=\"active sidebar-link\">2.4 资源管理</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/chapter2/flutter_assets_mgr.html#指定-assets\" class=\"sidebar-link\">指定 assets</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter2/flutter_assets_mgr.html#asset-变体-variant\" class=\"sidebar-link\">Asset 变体（variant）</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter2/flutter_assets_mgr.html#加载-assets\" class=\"sidebar-link\">加载 assets</a></li></ul></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_2-4-资源管理\"><a href=\"#_2-4-资源管理\" class=\"header-anchor\">#</a> 2.4 资源管理</h1> <p>Flutter APP安装包中会包含代码和 assets（资源）两部分。Assets是会打包到程序安装包中的，可在运行时访问。常见类型的assets包括静态数据（例如JSON文件）、配置文件、图标和图片（JPEG，WebP，GIF，动画WebP / GIF，PNG，BMP和WBMP）等。</p> <h2 id=\"指定-assets\"><a href=\"#指定-assets\" class=\"header-anchor\">#</a> 指定 assets</h2> <p>和包管理一样，Flutter也使用<a href=\"https://www.dartlang.org/tools/pub/pubspec\" target=\"_blank\" rel=\"noopener noreferrer\"><code>pubspec.yaml</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>文件来管理应用程序所需的资源，举个例子:</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code><span class=\"token key atrule\">flutter</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">assets</span><span class=\"token punctuation\">:</span>\n    <span class=\"token punctuation\">-</span> assets/my_icon.png\n    <span class=\"token punctuation\">-</span> assets/background.png\n</code></pre></div><p><code>assets</code>指定应包含在应用程序中的文件， 每个asset都通过相对于<code>pubspec.yaml</code>文件所在的文件系统路径来标识自身的路径。asset的声明顺序是无关紧要的，asset的实际目录可以是任意文件夹（在本示例中是assets文件夹）。</p> <p>在构建期间，Flutter将asset放置到称为 <em>asset bundle</em> 的特殊存档中，应用程序可以在运行时读取它们（但不能修改）。</p> <h2 id=\"asset-变体-variant\"><a href=\"#asset-变体-variant\" class=\"header-anchor\">#</a> Asset 变体（variant）</h2> <p>构建过程支持“asset变体”的概念：不同版本的asset可能会显示在不同的上下文中。 在<code>pubspec.yaml</code>的assets部分中指定asset路径时，构建过程中，会在相邻子目录中查找具有相同名称的任何文件。这些文件随后会与指定的asset一起被包含在asset bundle中。</p> <p>例如，如果应用程序目录中有以下文件:</p> <ul><li>…/pubspec.yaml</li> <li>…/graphics/my_icon.png</li> <li>…/graphics/background.png</li> <li>…/graphics/dark/background.png</li> <li>…etc.</li></ul> <p>然后<code>pubspec.yaml</code>文件中只需包含:</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>flutter:\n  assets:\n    - graphics/background.png\n</code></pre></div><p>那么这两个<code>graphics/background.png</code>和<code>graphics/dark/background.png</code> 都将包含在您的asset bundle中。前者被认为是_main asset_ （主资源），后者被认为是一种变体（variant）。</p> <p>在选择匹配当前设备分辨率的图片时，Flutter会使用到asset变体（见下文），将来，Flutter可能会将这种机制扩展到本地化、阅读提示等方面。</p> <h2 id=\"加载-assets\"><a href=\"#加载-assets\" class=\"header-anchor\">#</a> 加载 assets</h2> <p>您的应用可以通过<a href=\"https://docs.flutter.io/flutter/services/AssetBundle-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>AssetBundle</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>对象访问其asset 。有两种主要方法允许从Asset bundle中加载字符串或图片（二进制）文件。</p> <h3 id=\"加载文本assets\"><a href=\"#加载文本assets\" class=\"header-anchor\">#</a> 加载文本assets</h3> <ul><li>通过<a href=\"https://docs.flutter.io/flutter/services/rootBundle.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>rootBundle</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 对象加载：每个Flutter应用程序都有一个<a href=\"https://docs.flutter.io/flutter/services/rootBundle.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>rootBundle</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>对象， 通过它可以轻松访问主资源包，直接使用<code>package:flutter/services.dart</code>中全局静态的<code>rootBundle</code>对象来加载asset即可。</li> <li>通过 <a href=\"https://docs.flutter.io/flutter/widgets/DefaultAssetBundle-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>DefaultAssetBundle</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 加载：建议使用 <a href=\"https://docs.flutter.io/flutter/widgets/DefaultAssetBundle-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>DefaultAssetBundle</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 来获取当前BuildContext的AssetBundle。 这种方法不是使用应用程序构建的默认asset bundle，而是使父级widget在运行时动态替换的不同的AssetBundle，这对于本地化或测试场景很有用。</li></ul> <p>通常，可以使用<code>DefaultAssetBundle.of()</code>在应用运行时来间接加载asset（例如JSON文件），而在widget上下文之外，或其它<code>AssetBundle</code>句柄不可用时，可以使用<code>rootBundle</code>直接加载这些asset，例如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'dart:async'</span> <span class=\"token keyword\">show</span> Future<span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/services.dart'</span> <span class=\"token keyword\">show</span> rootBundle<span class=\"token punctuation\">;</span>\n\nFuture<span class=\"token operator\">&lt;</span>String<span class=\"token operator\">&gt;</span> <span class=\"token function\">loadAsset</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token keyword\">await</span> rootBundle<span class=\"token punctuation\">.</span><span class=\"token function\">loadString</span><span class=\"token punctuation\">(</span><span class=\"token string\">'assets/config.json'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h3 id=\"加载图片\"><a href=\"#加载图片\" class=\"header-anchor\">#</a> 加载图片</h3> <p>类似于原生开发，Flutter也可以为当前设备加载适合其分辨率的图像。</p> <h4 id=\"声明分辨率相关的图片-assets\"><a href=\"#声明分辨率相关的图片-assets\" class=\"header-anchor\">#</a> 声明分辨率相关的图片 assets</h4> <p><a href=\"https://docs.flutter.io/flutter/painting/AssetImage-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>AssetImage</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 可以将asset的请求逻辑映射到最接近当前设备像素比例（dpi）的asset。为了使这种映射起作用，必须根据特定的目录结构来保存asset：</p> <ul><li>…/image.png</li> <li>…/<strong>M</strong>x/image.png</li> <li>…/<strong>N</strong>x/image.png</li> <li>…etc.</li></ul> <p>其中M和N是数字标识符，对应于其中包含的图像的分辨率，也就是说，它们指定不同设备像素比例的图片。</p> <p>主资源默认对应于1.0倍的分辨率图片。看一个例子：</p> <ul><li>…/my_icon.png</li> <li>…/2.0x/my_icon.png</li> <li>…/3.0x/my_icon.png</li></ul> <p>在设备像素比率为1.8的设备上，<code>.../2.0x/my_icon.png</code> 将被选择。对于2.7的设备像素比率，<code>.../3.0x/my_icon.png</code>将被选择。</p> <p>如果未在<code>Image</code> widget上指定渲染图像的宽度和高度，那么<code>Image</code> widget将占用与主资源相同的屏幕空间大小。 也就是说，如果<code>.../my_icon.png</code>是72px乘72px，那么<code>.../3.0x/my_icon.png</code>应该是216px乘216px; 但如果未指定宽度和高度，它们都将渲染为72像素×72像素（以逻辑像素为单位）。</p> <p><code>pubspec.yaml</code>中asset部分中的每一项都应与实际文件相对应，但主资源项除外。当主资源缺少某个资源时，会按分辨率从低到高的顺序去选择 ，也就是说1x中没有的话会在2x中找，2x中还没有的话就在3x中找。</p> <h4 id=\"加载图片-2\"><a href=\"#加载图片-2\" class=\"header-anchor\">#</a> 加载图片</h4> <p>要加载图片，可以使用 <a href=\"https://docs.flutter.io/flutter/painting/AssetImage-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>AssetImage</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>类。例如，我们可以从上面的asset声明中加载背景图片：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">DecoratedBox</span><span class=\"token punctuation\">(</span>\n    decoration<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">BoxDecoration</span><span class=\"token punctuation\">(</span>\n      image<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">DecorationImage</span><span class=\"token punctuation\">(</span>\n        image<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AssetImage</span><span class=\"token punctuation\">(</span><span class=\"token string\">'graphics/background.png'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>注意，<code>AssetImage</code> 并非是一个widget， 它实际上是一个<code>ImageProvider</code>，有些时候你可能期望直接得到一个显示图片的widget，那么你可以使用<code>Image.asset()</code>方法，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> Image<span class=\"token punctuation\">.</span><span class=\"token function\">asset</span><span class=\"token punctuation\">(</span><span class=\"token string\">'graphics/background.png'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>使用默认的 asset bundle 加载资源时，内部会自动处理分辨率等，这些处理对开发者来说是无感知的。 (如果使用一些更低级别的类，如 <a href=\"https://docs.flutter.io/flutter/painting/ImageStream-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>ImageStream</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>或 <a href=\"https://docs.flutter.io/flutter/painting/ImageCache-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>ImageCache</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 时你会注意到有与缩放相关的参数)</p> <h4 id=\"依赖包中的资源图片\"><a href=\"#依赖包中的资源图片\" class=\"header-anchor\">#</a> 依赖包中的资源图片</h4> <p>要加载依赖包中的图像，必须给<code>AssetImage</code>提供<code>package</code>参数。</p> <p>例如，假设您的应用程序依赖于一个名为“my_icons”的包，它具有如下目录结构：</p> <ul><li>…/pubspec.yaml</li> <li>…/icons/heart.png</li> <li>…/icons/1.5x/heart.png</li> <li>…/icons/2.0x/heart.png</li> <li>…etc.</li></ul> <p>然后加载图像，使用:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AssetImage</span><span class=\"token punctuation\">(</span><span class=\"token string\">'icons/heart.png'</span><span class=\"token punctuation\">,</span> package<span class=\"token punctuation\">:</span> <span class=\"token string\">'my_icons'</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>或</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">new</span> <span class=\"token class-name\">Image<span class=\"token punctuation\">.</span>asset</span><span class=\"token punctuation\">(</span><span class=\"token string\">'icons/heart.png'</span><span class=\"token punctuation\">,</span> package<span class=\"token punctuation\">:</span> <span class=\"token string\">'my_icons'</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p><strong>注意：包在使用本身的资源时也应该加上<code>package</code>参数来获取</strong>。</p> <h5 id=\"打包包中的-assets\"><a href=\"#打包包中的-assets\" class=\"header-anchor\">#</a> 打包包中的 assets</h5> <p>如果在<code>pubspec.yaml</code>文件中声明了期望的资源，它将会打包到相应的package中。特别是，包本身使用的资源必须在<code>pubspec.yaml</code>中指定。</p> <p>包也可以选择在其<code>lib/</code>文件夹中包含未在其<code>pubspec.yaml</code>文件中声明的资源。在这种情况下，对于要打包的图片，应用程序必须在<code>pubspec.yaml</code>中指定包含哪些图像。 例如，一个名为“fancy_backgrounds”的包，可能包含以下文件：</p> <ul><li>…/lib/backgrounds/background1.png</li> <li>…/lib/backgrounds/background2.png</li> <li>…/lib/backgrounds/background3.png</li></ul> <p>要包含第一张图像，必须在<code>pubspec.yaml</code>的assets部分中声明它：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>flutter:\n  assets:\n    - packages/fancy_backgrounds/backgrounds/background1.png\n</code></pre></div><p><code>lib/</code>是隐含的，所以它不应该包含在资产路径中。</p> <h3 id=\"特定平台-assets\"><a href=\"#特定平台-assets\" class=\"header-anchor\">#</a> 特定平台 assets</h3> <p>上面的资源都是flutter应用中的，这些资源只有在Flutter框架运行之后才能使用，如果要给我们的应用设置APP图标或者添加启动图，那我们必须使用特定平台的assets。</p> <h4 id=\"设置app图标\"><a href=\"#设置app图标\" class=\"header-anchor\">#</a> 设置APP图标</h4> <p>更新Flutter应用程序启动图标的方式与在本机Android或iOS应用程序中更新启动图标的方式相同。</p> <ul><li><p>Android</p> <p>在Flutter项目的根目录中，导航到<code>.../android/app/src/main/res</code>目录，里面包含了各种资源文件夹（如<code>mipmap-hdpi</code>已包含占位符图像“ic_launcher.png”，见图2-8）。 只需按照<a href=\"https://developer.android.com/guide/practices/ui_guidelines/icon_design_launcher.html#size\" target=\"_blank\" rel=\"noopener noreferrer\">Android开发人员指南<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>中的说明， 将其替换为所需的资源，并遵守每种屏幕密度（dpi）的建议图标大小标准。</p> <p><img src=\"/assets/img/2-8.89d0af83.png\" alt=\"图2-8\"></p> <blockquote><p><strong>注意:</strong> 如果您重命名.png文件，则还必须在您<code>AndroidManifest.xml</code>的<code>&lt;application&gt;</code>标签的<code>android:icon</code>属性中更新名称。</p></blockquote></li> <li><p>iOS</p> <p>在Flutter项目的根目录中，导航到<code>.../ios/Runner</code>。该目录中<code>Assets.xcassets/AppIcon.appiconset</code>已经包含占位符图片（见图2-9）， 只需将它们替换为适当大小的图片，保留原始文件名称。</p> <p><img src=\"/assets/img/2-9.0a86cf44.png\" alt=\"图2-9\"></p></li></ul> <h4 id=\"更新启动页\"><a href=\"#更新启动页\" class=\"header-anchor\">#</a> 更新启动页</h4> <p><img src=\"/assets/img/2-10.e56b6689.png\" alt=\"图2-10\"></p> <p>在Flutter框架加载时，Flutter会使用本地平台机制绘制启动页。此启动页将持续到Flutter渲染应用程序的第一帧时。</p> <blockquote><p><strong>注意:</strong> 这意味着如果您不在应用程序的<code>main()</code>方法中调用<a href=\"https://docs.flutter.io/flutter/widgets/runApp.html\" target=\"_blank\" rel=\"noopener noreferrer\">runApp<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 函数 （或者更具体地说，如果您不调用<a href=\"https://docs.flutter.io/flutter/dart-ui/Window/render.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>window.render</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>去响应<a href=\"https://docs.flutter.io/flutter/dart-ui/Window/onDrawFrame.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>window.onDrawFrame</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>）的话， 启动屏幕将永远持续显示。</p></blockquote> <h5 id=\"android\"><a href=\"#android\" class=\"header-anchor\">#</a> Android</h5> <p>要将启动屏幕（splash screen）添加到您的Flutter应用程序， 请导航至<code>.../android/app/src/main</code>。在<code>res/drawable/launch_background.xml</code>，通过自定义drawable来实现自定义启动界面（你也可以直接换一张图片）。</p> <h5 id=\"ios\"><a href=\"#ios\" class=\"header-anchor\">#</a> iOS</h5> <p>要将图片添加到启动屏幕（splash screen）的中心，请导航至<code>.../ios/Runner</code>。在<code>Assets.xcassets/LaunchImage.imageset</code>， 拖入图片，并命名为<code>LaunchImage.png</code>、<code>LaunchImage@2x.png</code>、<code>LaunchImage@3x.png</code>。 如果你使用不同的文件名，那您还必须更新同一目录中的<code>Contents.json</code>文件，图片的具体尺寸可以查看苹果官方的标准。</p> <p>您也可以通过打开Xcode完全自定义storyboard。在Project Navigator中导航到<code>Runner/Runner</code>然后通过打开<code>Assets.xcassets</code>拖入图片，或者通过在LaunchScreen.storyboard中使用Interface Builder进行自定义，如图2-11所示。</p> <p><img src=\"/assets/img/2-11.9f54d13a.png\" alt=\"图2-11\"></p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter2/flutter_package_mgr.html\" class=\"prev\">\n        2.3 包管理\n      </a></span> <span class=\"next\"><a href=\"/chapter2/flutter_app_debug.html\">\n        2.5 调试Flutter应用\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/26.e656381b.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter2/flutter_package_mgr.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>2.3 包管理 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/42.057d8df6.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable open\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" aria-current=\"page\" class=\"active sidebar-link\">2.3 包管理</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/chapter2/flutter_package_mgr.html#pub仓库\" class=\"sidebar-link\">Pub仓库</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter2/flutter_package_mgr.html#示例\" class=\"sidebar-link\">示例</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter2/flutter_package_mgr.html#其它依赖方式\" class=\"sidebar-link\">其它依赖方式</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter2/flutter_package_mgr.html#总结\" class=\"sidebar-link\">总结</a></li></ul></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_2-3-包管理\"><a href=\"#_2-3-包管理\" class=\"header-anchor\">#</a> 2.3 包管理</h1> <p>在软件开发中，很多时候有一些公共的库或SDK可能会被很多项目用到，因此，将这些代码单独抽到一个独立模块，然后哪个项目需要使用时再直接集成这个模块，便可大大提高开发效率。很多编程语言或开发工具都支持这种“模块共享”机制，如Java语言中这种独立模块会被打成一个jar包，Android中的aar包，Web开发中的npm包等。为了方便表述，我们将这种可共享的独立模块统一称为“包”（ Package）。</p> <p>一个APP在实际开发中往往会依赖很多包，而这些包通常都有交叉依赖关系、版本依赖等，如果由开发者手动来管理应用中的依赖包将会非常麻烦。因此，各种开发生态或编程语言官方通常都会提供一些包管理工具，比如在Android提供了Gradle来管理依赖，iOS用Cocoapods或Carthage来管理依赖，Node中通过npm等。而在Flutter开发中也有自己的包管理工具。本节我们主要介绍一下flutter如何使用配置文件<code>pubspec.yaml</code>（位于项目根目录）来管理第三方依赖包。</p> <p>YAML是一种直观、可读性高并且容易被人类阅读的文件格式，它和xml或Json相比，它语法简单并非常容易解析，所以YAML常用于配置文件，Flutter也是用yaml文件作为其配置文件。Flutter项目默认的配置文件是<code>pubspec.yaml</code>，我们看一个简单的示例：</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code><span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> flutter_in_action\n<span class=\"token key atrule\">description</span><span class=\"token punctuation\">:</span> First Flutter application.\n\n<span class=\"token key atrule\">version</span><span class=\"token punctuation\">:</span> 1.0.0+1\n\n<span class=\"token key atrule\">dependencies</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">flutter</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">sdk</span><span class=\"token punctuation\">:</span> flutter\n  <span class=\"token key atrule\">cupertino_icons</span><span class=\"token punctuation\">:</span> ^0.1.2\n\n<span class=\"token key atrule\">dev_dependencies</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">flutter_test</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">sdk</span><span class=\"token punctuation\">:</span> flutter\n    \n<span class=\"token key atrule\">flutter</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">uses-material-design</span><span class=\"token punctuation\">:</span> <span class=\"token boolean important\">true</span>\n</code></pre></div><p>下面，我们逐一解释一下各个字段的意义：</p> <ul><li><code>name</code>：应用或包名称。</li> <li><code>description</code>: 应用或包的描述、简介。</li> <li><code>version</code>：应用或包的版本号。</li> <li><code>dependencies</code>：应用或包依赖的其它包或插件。</li> <li><code>dev_dependencies</code>：开发环境依赖的工具包（而不是flutter应用本身依赖的包）。</li> <li><code>flutter</code>：flutter相关的配置选项。</li></ul> <p>如果我们的Flutter应用本身依赖某个包，我们需要将所依赖的包添加到<code>dependencies</code> 下，接下来我们通过一个例子来演示一下如何添加、下载并使用第三方包。</p> <h2 id=\"pub仓库\"><a href=\"#pub仓库\" class=\"header-anchor\">#</a> Pub仓库</h2> <p>Pub（https://pub.dev/ ）是Google官方的Dart Packages仓库，类似于node中的npm仓库，android中的jcenter。我们可以在Pub上面查找我们需要的包和插件，也可以向Pub发布我们的包和插件。我们将在后面的章节中介绍如何向Pub发布我们的包和插件。</p> <h2 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h2> <p>接下来，我们实现一个显示随机字符串的widget。有一个名为“english_words”的开源软件包，其中包含数千个常用的英文单词以及一些实用功能。我们首先在pub上找到english_words这个包（如图2-5所示），确定其最新的版本号和是否支持Flutter。</p> <p><img src=\"/assets/img/2-5.7456ef8f.png\" alt=\"图2-5\"></p> <p>我们看到“english_words”包最新的版本是3.1.3，并且支持flutter，接下来：</p> <ol><li><p>将“english_words”（3.1.3版本）添加到依赖项列表，如下：</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code><span class=\"token key atrule\">dependencies</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">flutter</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">sdk</span><span class=\"token punctuation\">:</span> flutter\n\n  <span class=\"token key atrule\">cupertino_icons</span><span class=\"token punctuation\">:</span> ^0.1.0\n  <span class=\"token comment\"># 新添加的依赖</span>\n  <span class=\"token key atrule\">english_words</span><span class=\"token punctuation\">:</span> ^3.1.3\n</code></pre></div></li> <li><p>下载包。在Android Studio的编辑器视图中查看pubspec.yaml时（图2-6），单击右上角的 <strong>Packages get</strong> 。</p> <p><img src=\"/assets/img/2-6.fb55e91b.png\" alt=\"图2-6\"></p> <p>这会将依赖包安装到您的项目。我们可以在控制台中看到以下内容：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>flutter packages get\nRunning <span class=\"token string\">&quot;flutter packages get&quot;</span> <span class=\"token keyword\">in</span> flutter_in_action<span class=\"token punctuation\">..</span>.\nProcess finished with <span class=\"token builtin class-name\">exit</span> code <span class=\"token number\">0</span>\n</code></pre></div><p>我们也可以在控制台，定位到当前工程目录，然后手动运行<code>flutter packages get</code> 命令来下载依赖包。另外，需要注意<code>dependencies</code>和<code>dev_dependencies</code>的区别，前者的依赖包将作为APP的源码的一部分参与编译，生成最终的安装包。而后者的依赖包只是作为开发阶段的一些工具包，主要是用于帮助我们提高开发、测试效率，比如flutter的自动化测试包等。</p></li> <li><p>引入<code>english_words</code>包。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:english_words/english_words.dart'</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>在输入时，Android Studio会自动提供有关库导入的建议选项。导入后该行代码将会显示为灰色，表示导入的库尚未使用。</p></li> <li><p>使用<code>english_words</code>包来生成随机字符串。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">RandomWordsWidget</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n   <span class=\"token comment\">// 生成随机字符串</span>\n    <span class=\"token keyword\">final</span> wordPair <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">WordPair<span class=\"token punctuation\">.</span>random</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n      padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">8.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span>wordPair<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们将<code>RandomWordsWidget</code> 添加到 <code>_MyHomePageState.build</code> 的<code>Column</code>的子widget中。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n  mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n    <span class=\"token function\">RandomWordsWidget</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div></li> <li><p>如果应用程序正在运行，请使用热重载按钮（⚡️图标） 更新正在运行的应用程序。每次单击热重载或保存项目时，都会在正在运行的应用程序中随机选择不同的单词对。 这是因为单词对是在 <code>build</code> 方法内部生成的。每次热更新时，<code>build</code>方法都会被执行，运行效果如图2-7所示。</p> <p><img src=\"/assets/img/2-7.90b5e799.png\" alt=\"图2-7\"></p></li></ol> <h2 id=\"其它依赖方式\"><a href=\"#其它依赖方式\" class=\"header-anchor\">#</a> 其它依赖方式</h2> <p>上文所述的依赖方式是依赖Pub仓库的。但我们还可以依赖本地包和git仓库。</p> <ul><li><p>依赖本地包</p> <p>如果我们正在本地开发一个包，包名为pkg1，我们可以通过下面方式依赖：</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code><span class=\"token key atrule\">dependencies</span><span class=\"token punctuation\">:</span>\n\t<span class=\"token key atrule\">pkg1</span><span class=\"token punctuation\">:</span>\n        <span class=\"token key atrule\">path</span><span class=\"token punctuation\">:</span> ../../code/pkg1\n</code></pre></div><p>路径可以是相对的，也可以是绝对的。</p></li> <li><p>依赖Git：你也可以依赖存储在Git仓库中的包。如果软件包位于仓库的根目录中，请使用以下语法</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code><span class=\"token key atrule\">dependencies</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">pkg1</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">git</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">url</span><span class=\"token punctuation\">:</span> git<span class=\"token punctuation\">:</span>//github.com/xxx/pkg1.git\n</code></pre></div><p>上面假定包位于Git存储库的根目录中。如果不是这种情况，可以使用path参数指定相对位置，例如：</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code><span class=\"token key atrule\">dependencies</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">package1</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">git</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">url</span><span class=\"token punctuation\">:</span> git<span class=\"token punctuation\">:</span>//github.com/flutter/packages.git\n      <span class=\"token key atrule\">path</span><span class=\"token punctuation\">:</span> packages/package1        \n</code></pre></div></li></ul> <p>上面介绍的这些依赖方式是Flutter开发中常用的，但还有一些其它依赖方式，完整的内容读者可以自行查看：https://www.dartlang.org/tools/pub/dependencies 。</p> <h2 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h2> <p>本节介绍了Flutter中包管理、引用、下载的整体流程，我们将在后面的章节中介绍如何开发并发布我们自己的包。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter2/flutter_router.html\" class=\"prev\">\n        2.2 路由管理\n      </a></span> <span class=\"next\"><a href=\"/chapter2/flutter_assets_mgr.html\">\n        2.4 资源管理\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/42.057d8df6.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter2/flutter_router.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>2.2 路由管理 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/43.b17a6aae.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable open\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" aria-current=\"page\" class=\"active sidebar-link\">2.2 路由管理</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/chapter2/flutter_router.html#_2-2-1-一个简单示例\" class=\"sidebar-link\">2.2.1 一个简单示例</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter2/flutter_router.html#_2-2-2-materialpageroute\" class=\"sidebar-link\">2.2.2 MaterialPageRoute</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter2/flutter_router.html#_2-2-3-navigator\" class=\"sidebar-link\">2.2.3 Navigator</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter2/flutter_router.html#_2-2-4-路由传值\" class=\"sidebar-link\">2.2.4 路由传值</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter2/flutter_router.html#_2-2-5-命名路由\" class=\"sidebar-link\">2.2.5 命名路由</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter2/flutter_router.html#_2-2-6-路由生成钩子\" class=\"sidebar-link\">2.2.6 路由生成钩子</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter2/flutter_router.html#_2-2-7-总结\" class=\"sidebar-link\">2.2.7 总结</a></li></ul></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_2-2-路由管理\"><a href=\"#_2-2-路由管理\" class=\"header-anchor\">#</a> 2.2 路由管理</h1> <p>路由(Route)在移动开发中通常指页面（Page），这跟web开发中单页应用的Route概念意义是相同的，Route在Android中通常指一个Activity，在iOS中指一个ViewController。所谓路由管理，就是管理页面之间如何跳转，通常也可被称为导航管理。Flutter中的路由管理和原生开发类似，无论是Android还是iOS，导航管理都会维护一个路由栈，路由入栈(push)操作对应打开一个新页面，路由出栈(pop)操作对应页面关闭操作，而路由管理主要是指如何来管理路由栈。</p> <h2 id=\"_2-2-1-一个简单示例\"><a href=\"#_2-2-1-一个简单示例\" class=\"header-anchor\">#</a> 2.2.1 一个简单示例</h2> <p>我们在上一节“计数器”示例的基础上，做如下修改：</p> <ol><li><p>创建一个新路由，命名“NewRoute”</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">NewRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n      appBar<span class=\"token punctuation\">:</span> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>\n        title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;New route&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      body<span class=\"token punctuation\">:</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;This is new route&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>新路由继承自<code>StatelessWidget</code>，界面很简单，在页面中间显示一句&quot;This is new route&quot;。</p></li> <li><p>在<code>_MyHomePageState.build</code>方法中的<code>Column</code>的子widget中添加一个按钮（<code>FlatButton</code>） :</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n      mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n      children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n      <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n         child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;open new route&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n         textColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span>\n         onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token comment\">//导航到新路由   </span>\n          Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span> context<span class=\"token punctuation\">,</span>\n           <span class=\"token function\">MaterialPageRoute</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token keyword\">return</span> <span class=\"token function\">NewRoute</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n           <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n         <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n       <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n <span class=\"token punctuation\">)</span>\n</code></pre></div><p>我们添加了一个打开新路由的按钮，并将按钮文字颜色设置为蓝色，点击该按钮后就会打开新的路由页面，效果如图2-2和2-3所示。</p> <p><img src=\"/assets/img/2-2.de6feb35.png\" alt=\"图2-2\"> <img src=\"/assets/img/2-3.c20b3236.png\" alt=\"图2-3\"></p></li></ol> <h2 id=\"_2-2-2-materialpageroute\"><a href=\"#_2-2-2-materialpageroute\" class=\"header-anchor\">#</a> 2.2.2 MaterialPageRoute</h2> <p><code>MaterialPageRoute</code>继承自<code>PageRoute</code>类，<code>PageRoute</code>类是一个抽象类，表示占有整个屏幕空间的一个模态路由页面，它还定义了路由构建及切换时过渡动画的相关接口及属性。<code>MaterialPageRoute</code> 是Material组件库提供的组件，它可以针对不同平台，实现与平台页面切换动画风格一致的路由切换动画：</p> <ul><li>对于Android，当打开新页面时，新的页面会从屏幕底部滑动到屏幕顶部；当关闭页面时，当前页面会从屏幕顶部滑动到屏幕底部后消失，同时上一个页面会显示到屏幕上。</li> <li>对于iOS，当打开页面时，新的页面会从屏幕右侧边缘一致滑动到屏幕左边，直到新页面全部显示到屏幕上，而上一个页面则会从当前屏幕滑动到屏幕左侧而消失；当关闭页面时，正好相反，当前页面会从屏幕右侧滑出，同时上一个页面会从屏幕左侧滑入。</li></ul> <p>下面我们介绍一下<code>MaterialPageRoute</code> 构造函数的各个参数的意义：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>  <span class=\"token function\">MaterialPageRoute</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    WidgetBuilder builder<span class=\"token punctuation\">,</span>\n    RouteSettings settings<span class=\"token punctuation\">,</span>\n    bool maintainState <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n    bool fullscreenDialog <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><ul><li><code>builder</code> 是一个WidgetBuilder类型的回调函数，它的作用是构建路由页面的具体内容，返回值是一个widget。我们通常要实现此回调，返回新路由的实例。</li> <li><code>settings</code> 包含路由的配置信息，如路由名称、是否初始路由（首页）。</li> <li><code>maintainState</code>：默认情况下，当入栈一个新路由时，原来的路由仍然会被保存在内存中，如果想在路由没用的时候释放其所占用的所有资源，可以设置<code>maintainState</code>为false。</li> <li><code>fullscreenDialog</code>表示新的路由页面是否是一个全屏的模态对话框，在iOS中，如果<code>fullscreenDialog</code>为<code>true</code>，新页面将会从屏幕底部滑入（而不是水平方向）。</li></ul> <blockquote><p>如果想自定义路由切换动画，可以自己继承PageRoute来实现，我们将在后面介绍动画时，实现一个自定义的路由组件。</p></blockquote> <h2 id=\"_2-2-3-navigator\"><a href=\"#_2-2-3-navigator\" class=\"header-anchor\">#</a> 2.2.3 Navigator</h2> <p><code>Navigator</code>是一个路由管理的组件，它提供了打开和退出路由页方法。<code>Navigator</code>通过一个栈来管理活动路由集合。通常当前屏幕显示的页面就是栈顶的路由。<code>Navigator</code>提供了一系列方法来管理路由栈，在此我们只介绍其最常用的两个方法：</p> <h3 id=\"future-push-buildcontext-context-route-route\"><a href=\"#future-push-buildcontext-context-route-route\" class=\"header-anchor\">#</a> Future  push(BuildContext context, Route route)</h3> <p>将给定的路由入栈（即打开新的页面），返回值是一个<code>Future</code>对象，用以接收新路由出栈（即关闭）时的返回数据。</p> <h3 id=\"bool-pop-buildcontext-context-result\"><a href=\"#bool-pop-buildcontext-context-result\" class=\"header-anchor\">#</a> bool  pop(BuildContext context, [ result ])</h3> <p>将栈顶路由出栈，<code>result</code>为页面关闭时返回给上一个页面的数据。</p> <p><code>Navigator</code> 还有很多其它方法，如<code>Navigator.replace</code>、<code>Navigator.popUntil</code>等，详情请参考API文档或SDK源码注释，在此不再赘述。下面我们还需要介绍一下路由相关的另一个概念“命名路由”。</p> <h3 id=\"实例方法\"><a href=\"#实例方法\" class=\"header-anchor\">#</a> 实例方法</h3> <p>Navigator类中第一个参数为context的<strong>静态方法</strong>都对应一个Navigator的<strong>实例方法</strong>， 比如<code>Navigator.push(BuildContext context, Route route)</code>等价于<code>Navigator.of(context).push(Route route)</code> ，下面命名路由相关的方法也是一样的。</p> <h2 id=\"_2-2-4-路由传值\"><a href=\"#_2-2-4-路由传值\" class=\"header-anchor\">#</a> 2.2.4 路由传值</h2> <p>很多时候，在路由跳转时我们需要带一些参数，比如打开商品详情页时，我们需要带一个商品id，这样商品详情页才知道展示哪个商品信息；又比如我们在填写订单时需要选择收货地址，打开地址选择页并选择地址后，可以将用户选择的地址返回到订单页等等。下面我们通过一个简单的示例来演示新旧路由如何传参。</p> <h3 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h3> <p>我们创建一个<code>TipRoute</code>路由，它接受一个提示文本参数，负责将传入它的文本显示在页面上，另外<code>TipRoute</code>中我们添加一个“返回”按钮，点击后在返回上一个路由的同时会带上一个返回参数，下面我们看一下实现代码。</p> <p><code>TipRoute</code>实现代码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">TipRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">TipRoute</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n    <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>text<span class=\"token punctuation\">,</span>  <span class=\"token comment\">// 接收一个text参数</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> String text<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n      appBar<span class=\"token punctuation\">:</span> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>\n        title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;提示&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      body<span class=\"token punctuation\">:</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n        padding<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">18</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n            children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n              <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>text<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n                onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;我是返回值&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;返回&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>下面是打开新路由<code>TipRoute</code>的代码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">RouterTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n        onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token comment\">// 打开`TipRoute`，并等待返回结果</span>\n          <span class=\"token keyword\">var</span> result <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span>\n            context<span class=\"token punctuation\">,</span>\n            <span class=\"token function\">MaterialPageRoute</span><span class=\"token punctuation\">(</span>\n              builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token keyword\">return</span> <span class=\"token function\">TipRoute</span><span class=\"token punctuation\">(</span>\n                  <span class=\"token comment\">// 路由参数</span>\n                  text<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;我是提示xxxx&quot;</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token comment\">//输出`TipRoute`路由返回结果</span>\n          <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;路由返回值: $result&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;打开提示页&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行上面代码，点击<code>RouterTestRoute</code>页的“打开提示页”按钮，会打开<code>TipRoute</code>页，运行效果如图2-4所示下：</p> <p><img src=\"/assets/img/2-4.1abb1cab.png\" alt=\"图2-4\"></p> <p>需要说明：</p> <ol><li><p>提示文案“我是提示xxxx”是通过<code>TipRoute</code>的<code>text</code>参数传递给新路由页的。我们可以通过等待<code>Navigator.push(…)</code>返回的<code>Future</code>来获取新路由的返回数据。</p></li> <li><p>在<code>TipRoute</code>页中有两种方式可以返回到上一页；第一种方式时直接点击导航栏返回箭头，第二种方式是点击页面中的“返回”按钮。这两种返回方式的区别是前者不会返回数据给上一个路由，而后者会。下面是分别点击页面中的返回按钮和导航栏返回箭头后，<code>RouterTestRoute</code>页中<code>print</code>方法在控制台输出的内容：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>I/flutter (27896): 路由返回值: 我是返回值\nI/flutter (27896): 路由返回值: null\n</code></pre></div></li></ol> <p>上面介绍的是非命名路由的传值方式，命名路由的传值方式会有所不同，我们会在下面介绍命名路由时介绍。</p> <h2 id=\"_2-2-5-命名路由\"><a href=\"#_2-2-5-命名路由\" class=\"header-anchor\">#</a> 2.2.5 命名路由</h2> <p>所谓“命名路由”（Named Route）即有名字的路由，我们可以先给路由起一个名字，然后就可以通过路由名字直接打开新的路由了，这为路由管理带来了一种直观、简单的方式。</p> <h3 id=\"路由表\"><a href=\"#路由表\" class=\"header-anchor\">#</a> 路由表</h3> <p>要想使用命名路由，我们必须先提供并注册一个路由表（routing table），这样应用程序才知道哪个名字与哪个路由组件相对应。其实注册路由表就是给路由起名字，路由表的定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Map<span class=\"token operator\">&lt;</span>String<span class=\"token punctuation\">,</span> WidgetBuilder<span class=\"token operator\">&gt;</span> routes<span class=\"token punctuation\">;</span>\n</code></pre></div><p>它是一个<code>Map</code>，key为路由的名字，是个字符串；value是个<code>builder</code>回调函数，用于生成相应的路由widget。我们在通过路由名字打开新路由时，应用会根据路由名字在路由表中查找到对应的<code>WidgetBuilder</code>回调函数，然后调用该回调函数生成路由widget并返回。</p> <h3 id=\"注册路由表\"><a href=\"#注册路由表\" class=\"header-anchor\">#</a> 注册路由表</h3> <p>路由表的注册方式很简单，我们回到之前“计数器”的示例，然后在<code>MyApp</code>类的<code>build</code>方法中找到<code>MaterialApp</code>，添加<code>routes</code>属性，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">MaterialApp</span><span class=\"token punctuation\">(</span>\n  title<span class=\"token punctuation\">:</span> <span class=\"token string\">'Flutter Demo'</span><span class=\"token punctuation\">,</span>\n  theme<span class=\"token punctuation\">:</span> <span class=\"token function\">ThemeData</span><span class=\"token punctuation\">(</span>\n    primarySwatch<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token comment\">//注册路由表</span>\n  routes<span class=\"token punctuation\">:</span><span class=\"token punctuation\">{</span>\n   <span class=\"token string\">&quot;new_page&quot;</span><span class=\"token punctuation\">:</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">NewRoute</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">// 省略其它路由注册信息</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token punctuation\">,</span>\n  home<span class=\"token punctuation\">:</span> <span class=\"token function\">MyHomePage</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token string\">'Flutter Demo Home Page'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>现在我们就完成了路由表的注册。上面的代码中<code>home</code>路由并没有使用命名路由，如果我们也想将<code>home</code>注册为命名路由应该怎么做呢？其实很简单，直接看代码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">MaterialApp</span><span class=\"token punctuation\">(</span>\n  title<span class=\"token punctuation\">:</span> <span class=\"token string\">'Flutter Demo'</span><span class=\"token punctuation\">,</span>\n  initialRoute<span class=\"token punctuation\">:</span><span class=\"token string\">&quot;/&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//名为&quot;/&quot;的路由作为应用的home(首页)</span>\n  theme<span class=\"token punctuation\">:</span> <span class=\"token function\">ThemeData</span><span class=\"token punctuation\">(</span>\n    primarySwatch<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token comment\">//注册路由表</span>\n  routes<span class=\"token punctuation\">:</span><span class=\"token punctuation\">{</span>\n   <span class=\"token string\">&quot;new_page&quot;</span><span class=\"token punctuation\">:</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">NewRoute</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n   <span class=\"token string\">&quot;/&quot;</span><span class=\"token punctuation\">:</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">MyHomePage</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token string\">'Flutter Demo Home Page'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//注册首页路由</span>\n  <span class=\"token punctuation\">}</span> \n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>可以看到，我们只需在路由表中注册一下<code>MyHomePage</code>路由，然后将其名字作为<code>MaterialApp</code>的<code>initialRoute</code>属性值即可，该属性决定应用的初始路由页是哪一个命名路由。</p> <h3 id=\"通过路由名打开新路由页\"><a href=\"#通过路由名打开新路由页\" class=\"header-anchor\">#</a> 通过路由名打开新路由页</h3> <p>要通过路由名称来打开新路由，可以使用<code>Navigator</code> 的<code>pushNamed</code>方法：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Future <span class=\"token function\">pushNamed</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> String routeName<span class=\"token punctuation\">,</span><span class=\"token punctuation\">{</span>Object arguments<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p><code>Navigator</code> 除了<code>pushNamed</code>方法，还有<code>pushReplacementNamed</code>等其他管理命名路由的方法，读者可以自行查看API文档。接下来我们通过路由名来打开新的路由页，修改<code>FlatButton</code>的<code>onPressed</code>回调代码，改为：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">pushNamed</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;new_page&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">//Navigator.push(context,</span>\n  <span class=\"token comment\">//  MaterialPageRoute(builder: (context) {</span>\n  <span class=\"token comment\">//  return NewRoute();</span>\n  <span class=\"token comment\">//}));  </span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n</code></pre></div><p>热重载应用，再次点击“open new route”按钮，依然可以打开新的路由页。</p> <h3 id=\"命名路由参数传递\"><a href=\"#命名路由参数传递\" class=\"header-anchor\">#</a> 命名路由参数传递</h3> <p>在Flutter最初的版本中，命名路由是不能传递参数的，后来才支持了参数；下面展示命名路由如何传递并获取路由参数：</p> <p>我们先注册一个路由：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code> routes<span class=\"token punctuation\">:</span><span class=\"token punctuation\">{</span>\n   <span class=\"token string\">&quot;new_page&quot;</span><span class=\"token punctuation\">:</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">EchoRoute</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token punctuation\">,</span>\n</code></pre></div><p>在路由页通过<code>RouteSetting</code>对象获取路由参数：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">EchoRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//获取路由参数  </span>\n    <span class=\"token keyword\">var</span> args<span class=\"token operator\">=</span>ModalRoute<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>settings<span class=\"token punctuation\">.</span>arguments<span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//...省略无关代码</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>在打开路由时传递参数</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pushNamed</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;new_page&quot;</span><span class=\"token punctuation\">,</span> arguments<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;hi&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><h3 id=\"适配\"><a href=\"#适配\" class=\"header-anchor\">#</a> 适配</h3> <p>假设我们也想将上面路由传参示例中的<code>TipRoute</code>路由页注册到路由表中，以便也可以通过路由名来打开它。但是，由于<code>TipRoute</code>接受一个<code>text</code> 参数，我们如何在不改变<code>TipRoute</code>源码的前提下适配这种情况？其实很简单：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">MaterialApp</span><span class=\"token punctuation\">(</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n  routes<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span>\n   <span class=\"token string\">&quot;tip2&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n     <span class=\"token keyword\">return</span> <span class=\"token function\">TipRoute</span><span class=\"token punctuation\">(</span>text<span class=\"token punctuation\">:</span> ModalRoute<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>settings<span class=\"token punctuation\">.</span>arguments<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n   <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> \n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><h2 id=\"_2-2-6-路由生成钩子\"><a href=\"#_2-2-6-路由生成钩子\" class=\"header-anchor\">#</a> 2.2.6 路由生成钩子</h2> <p>假设我们要开发一个电商APP，当用户没有登录时可以看店铺、商品等信息，但交易记录、购物车、用户个人信息等页面需要登录后才能看。为了实现上述功能，我们需要在打开每一个路由页前判断用户登录状态！如果每次打开路由前我们都需要去判断一下将会非常麻烦，那有什么更好的办法吗？答案是有！</p> <p><code>MaterialApp</code>有一个<code>onGenerateRoute</code>属性，它在打开命名路由时可能会被调用，之所以说可能，是因为当调用<code>Navigator.pushNamed(...)</code>打开命名路由时，如果指定的路由名在路由表中已注册，则会调用路由表中的<code>builder</code>函数来生成路由组件；如果路由表中没有注册，才会调用<code>onGenerateRoute</code>来生成路由。<code>onGenerateRoute</code>回调签名如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Route<span class=\"token operator\">&lt;</span><span class=\"token keyword\">dynamic</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">Function</span><span class=\"token punctuation\">(</span>RouteSettings settings<span class=\"token punctuation\">)</span>\n</code></pre></div><p>有了<code>onGenerateRoute</code>回调，要实现上面控制页面权限的功能就非常容易：我们放弃使用路由表，取而代之的是提供一个<code>onGenerateRoute</code>回调，然后在该回调中进行统一的权限控制，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">MaterialApp</span><span class=\"token punctuation\">(</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n  onGenerateRoute<span class=\"token punctuation\">:</span><span class=\"token punctuation\">(</span>RouteSettings settings<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n\t  <span class=\"token keyword\">return</span> <span class=\"token function\">MaterialPageRoute</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n\t\t   String routeName <span class=\"token operator\">=</span> settings<span class=\"token punctuation\">.</span>name<span class=\"token punctuation\">;</span>\n       <span class=\"token comment\">// 如果访问的路由页需要登录，但当前未登录，则直接返回登录页路由，</span>\n       <span class=\"token comment\">// 引导用户登录；其它情况则正常打开路由。</span>\n     <span class=\"token punctuation\">}</span>\n   <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><blockquote><p>注意，<code>onGenerateRoute</code>只会对命名路由生效。</p></blockquote> <h2 id=\"_2-2-7-总结\"><a href=\"#_2-2-7-总结\" class=\"header-anchor\">#</a> 2.2.7 总结</h2> <p>本章先介绍了Flutter中路由管理、传参的方式，然后又着重介绍了命名路由相关内容。在此需要说明一点，由于命名路由只是一种可选的路由管理方式，在实际开发中，读者可能心中会犹豫到底使用哪种路由管理方式。在此，根据笔者经验，建议读者最好统一使用命名路由的管理方式，这将会带来如下好处：</p> <ol><li>语义化更明确。</li> <li>代码更好维护；如果使用匿名路由，则必须在调用<code>Navigator.push</code>的地方创建新路由页，这样不仅需要import新路由页的dart文件，而且这样的代码将会非常分散。</li> <li>可以通过<code>onGenerateRoute</code>做一些全局的路由跳转前置处理逻辑。</li></ol> <p>综上所述，笔者比较建议使用命名路由，当然这并不是什么金科玉律，读者可以根据自己偏好或实际情况来决定。</p> <p>另外，还有一些关于路由管理的内容我们没有介绍，比如路由MaterialApp中还有<code>navigatorObservers</code>和<code>onUnknownRoute</code>两个回调属性，前者可以监听所有路由跳转动作，后者在打开一个不存在的命名路由时会被调用，由于这些功能并不常用，而且也比较简单，我们便不再花费篇幅来介绍了，读者可以自行查看API文档。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter2/first_flutter_app.html\" class=\"prev\">\n        2.1 计数器应用示例\n      </a></span> <span class=\"next\"><a href=\"/chapter2/flutter_package_mgr.html\">\n        2.3 包管理\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/43.b17a6aae.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter2/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>简介 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/157.fb299d51.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h2 id=\"简介\"><a href=\"#简介\" class=\"header-anchor\">#</a> 简介</h2> <p>本章将通过一些简单的示例来一步步介绍Flutter的开发流程.</p> <h2 id=\"本章目录\"><a href=\"#本章目录\" class=\"header-anchor\">#</a> 本章目录</h2> <ul><li><a href=\"/chapter2/first_flutter_app.html\">2.1：计数器示例</a></li> <li><a href=\"/chapter2/flutter_router.html\">2.2：路由管理</a></li> <li><a href=\"/chapter2/flutter_package_mgr.html\">2.3：包管理</a></li> <li><a href=\"/chapter2/flutter_assets_mgr.html\">2.4：资源管理</a></li> <li><a href=\"/chapter2/flutter_app_debug.html\">2.5：调试Flutter APP</a></li> <li><a href=\"/chapter2/thread_model_and_error_report.html\">2.6：Dart线程模型及异常捕获</a></li></ul></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/157.fb299d51.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter2/thread_model_and_error_report.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>2.6 Flutter异常捕获 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/95.bf70fc45.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable open\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" aria-current=\"page\" class=\"active sidebar-link\">2.6 Flutter异常捕获</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/chapter2/thread_model_and_error_report.html#_2-6-1-dart单线程模型\" class=\"sidebar-link\">2.6.1 Dart单线程模型</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter2/thread_model_and_error_report.html#_2-6-2-flutter异常捕获\" class=\"sidebar-link\">2.6.2 Flutter异常捕获</a></li></ul></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_2-6-flutter异常捕获\"><a href=\"#_2-6-flutter异常捕获\" class=\"header-anchor\">#</a> 2.6 Flutter异常捕获</h1> <p>在介绍Flutter异常捕获之前必须先了解一下Dart单线程模型，只有了解了Dart的代码执行流程，我们才能知道该在什么地方去捕获异常。</p> <h2 id=\"_2-6-1-dart单线程模型\"><a href=\"#_2-6-1-dart单线程模型\" class=\"header-anchor\">#</a> 2.6.1 Dart单线程模型</h2> <p>在Java和Objective-C（以下简称“OC”）中，如果程序发生异常且没有被捕获，那么程序将会终止，但是这在Dart或JavaScript中则不会！究其原因，这和它们的运行机制有关系。Java和OC都是多线程模型的编程语言，任意一个线程触发异常且该异常未被捕获时，就会导致整个进程退出。但Dart和JavaScript不会，它们都是单线程模型，运行机制很相似(但有区别)，下面我们通过Dart官方提供的一张图来看看Dart大致运行原理：</p> <p><img src=\"/assets/img/2-12.eb7484c9.png\" alt=\"图2-12\"></p> <p>Dart 在单线程中是以消息循环机制来运行的，其中包含两个任务队列，一个是“微任务队列”  <strong>microtask queue</strong>，另一个叫做“事件队列” <strong>event queue</strong>。从图中可以发现，微任务队列的执行优先级高于事件队列。</p> <p>现在我们来介绍一下Dart线程运行过程，如上图中所示，入口函数 main() 执行完后，消息循环机制便启动了。首先会按照先进先出的顺序逐个执行微任务队列中的任务，事件任务执行完毕后程序便会退出，但是，在事件任务执行的过程中也可以插入新的微任务和事件任务，在这种情况下，整个线程的执行过程便是一直在循环，不会退出，而Flutter中，主线程的执行过程正是如此，永不终止。</p> <p>在Dart中，所有的外部事件任务都在事件队列中，如IO、计时器、点击、以及绘制事件等，而微任务通常来源于Dart内部，并且微任务非常少，之所以如此，是因为微任务队列优先级高，如果微任务太多，执行时间总和就越久，事件队列任务的延迟也就越久，对于GUI应用来说最直观的表现就是比较卡，所以必须得保证微任务队列不会太长。值得注意的是，我们可以通过<code>Future.microtask(…)</code>方法向微任务队列插入一个任务。</p> <p>在事件循环中，当某个任务发生异常并没有被捕获时，程序并不会退出，而直接导致的结果是<strong>当前任务</strong>的后续代码就不会被执行了，也就是说一个任务中的异常是不会影响其它任务执行的。</p> <h2 id=\"_2-6-2-flutter异常捕获\"><a href=\"#_2-6-2-flutter异常捕获\" class=\"header-anchor\">#</a> 2.6.2 Flutter异常捕获</h2> <p>Dart中可以通过<code>try/catch/finally</code>来捕获代码块异常，这个和其它编程语言类似，如果读者不清楚，可以查看Dart语言文档，不再赘述，下面我们看看Flutter中的异常捕获。</p> <h3 id=\"flutter框架异常捕获\"><a href=\"#flutter框架异常捕获\" class=\"header-anchor\">#</a> Flutter框架异常捕获</h3> <p>Flutter 框架为我们在很多关键的方法进行了异常捕获。这里举一个例子，当我们布局发生越界或不合规范时，Flutter就会自动弹出一个错误界面，这是因为Flutter已经在执行build方法时添加了异常捕获，最终的源码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token metadata symbol\">@override</span>\n<span class=\"token keyword\">void</span> <span class=\"token function\">performRebuild</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//执行build方法  </span>\n    built <span class=\"token operator\">=</span> <span class=\"token function\">build</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">,</span> stack<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 有异常时则弹出错误提示  </span>\n    built <span class=\"token operator\">=</span> ErrorWidget<span class=\"token punctuation\">.</span><span class=\"token function\">builder</span><span class=\"token punctuation\">(</span><span class=\"token function\">_debugReportException</span><span class=\"token punctuation\">(</span><span class=\"token string\">'building $this'</span><span class=\"token punctuation\">,</span> e<span class=\"token punctuation\">,</span> stack<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span> \n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span>      \n</code></pre></div><p>可以看到，在发生异常时，Flutter默认的处理方式是弹一个ErrorWidget，但如果我们想自己捕获异常并上报到报警平台的话应该怎么做？我们进入<code>_debugReportException()</code>方法看看：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>FlutterErrorDetails <span class=\"token function\">_debugReportException</span><span class=\"token punctuation\">(</span>\n  String context<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">dynamic</span> exception<span class=\"token punctuation\">,</span>\n  StackTrace stack<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n  InformationCollector informationCollector\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">//构建错误详情对象  </span>\n  <span class=\"token keyword\">final</span> FlutterErrorDetails details <span class=\"token operator\">=</span> <span class=\"token function\">FlutterErrorDetails</span><span class=\"token punctuation\">(</span>\n    exception<span class=\"token punctuation\">:</span> exception<span class=\"token punctuation\">,</span>\n    stack<span class=\"token punctuation\">:</span> stack<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">library</span><span class=\"token punctuation\">:</span> <span class=\"token string\">'widgets library'</span><span class=\"token punctuation\">,</span>\n    context<span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">,</span>\n    informationCollector<span class=\"token punctuation\">:</span> informationCollector<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">//报告错误 </span>\n  FlutterError<span class=\"token punctuation\">.</span><span class=\"token function\">reportError</span><span class=\"token punctuation\">(</span>details<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">return</span> details<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们发现，错误是通过<code>FlutterError.reportError</code>方法上报的，继续跟踪：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>\n<span class=\"token keyword\">static</span> <span class=\"token keyword\">void</span> <span class=\"token function\">reportError</span><span class=\"token punctuation\">(</span>FlutterErrorDetails details<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>onError <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span>\n    <span class=\"token function\">onError</span><span class=\"token punctuation\">(</span>details<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//调用了onError回调</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们发现<code>onError</code>是<code>FlutterError</code>的一个静态属性，它有一个默认的处理方法 <code>dumpErrorToConsole</code>，到这里就清晰了，如果我们想自己上报异常，只需要提供一个自定义的错误处理回调即可，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">main</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  FlutterError<span class=\"token punctuation\">.</span>onError <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>FlutterErrorDetails details<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">reportError</span><span class=\"token punctuation\">(</span>details<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>这样我们就可以处理那些Flutter为我们捕获的异常了，接下来我们看看如何捕获其它异常。</p> <h3 id=\"其它异常捕获与日志收集\"><a href=\"#其它异常捕获与日志收集\" class=\"header-anchor\">#</a> 其它异常捕获与日志收集</h3> <p>在Flutter中，还有一些Flutter没有为我们捕获的异常，如调用空对象方法异常、Future中的异常。在Dart中，异常分两类：同步异常和异步异常，同步异常可以通过<code>try/catch</code>捕获，而异步异常则比较麻烦，如下面的代码是捕获不了<code>Future</code>的异常的：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">try</span><span class=\"token punctuation\">{</span>\n    Future<span class=\"token punctuation\">.</span><span class=\"token function\">delayed</span><span class=\"token punctuation\">(</span><span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Future<span class=\"token punctuation\">.</span><span class=\"token function\">error</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;xxx&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>Dart中有一个<code>runZoned(...)</code> 方法，可以给执行对象指定一个Zone。Zone表示一个代码执行的环境范围，为了方便理解，读者可以将Zone类比为一个代码执行沙箱，不同沙箱的之间是隔离的，沙箱可以捕获、拦截或修改一些代码行为，如Zone中可以捕获日志输出、Timer创建、微任务调度的行为，同时Zone也可以捕获所有未处理的异常。下面我们看看<code>runZoned(...)</code>方法定义：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>R runZoned<span class=\"token operator\">&lt;</span>R<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>R <span class=\"token function\">body</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n    Map zoneValues<span class=\"token punctuation\">,</span> \n    ZoneSpecification zoneSpecification<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">Function</span> onError<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> \n</code></pre></div><ul><li><p><code>zoneValues</code>: Zone 的私有数据，可以通过实例<code>zone[key]</code>获取，可以理解为每个“沙箱”的私有数据。</p></li> <li><p><code>zoneSpecification</code>：Zone的一些配置，可以自定义一些代码行为，比如拦截日志输出行为等，举个例子：</p> <p>下面是拦截应用中所有调用<code>print</code>输出日志的行为。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">main</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">runZoned</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">runApp</span><span class=\"token punctuation\">(</span><span class=\"token function\">MyApp</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> zoneSpecification<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">ZoneSpecification</span><span class=\"token punctuation\">(</span>\n      print<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>Zone self<span class=\"token punctuation\">,</span> ZoneDelegate parent<span class=\"token punctuation\">,</span> Zone zone<span class=\"token punctuation\">,</span> String line<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        parent<span class=\"token punctuation\">.</span><span class=\"token function\">print</span><span class=\"token punctuation\">(</span>zone<span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;Intercepted: $line&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>这样一来，我们APP中所有调用<code>print</code>方法输出日志的行为都会被拦截，通过这种方式，我们也可以在应用中记录日志，等到应用触发未捕获的异常时，将异常信息和日志统一上报。ZoneSpecification还可以自定义一些其他行为，读者可以查看API文档。</p></li> <li><p><code>onError</code>：Zone中未捕获异常处理回调，如果开发者提供了onError回调或者通过<code>ZoneSpecification.handleUncaughtError</code>指定了错误处理回调，那么这个zone将会变成一个error-zone，该error-zone中发生未捕获异常(无论同步还是异步)时都会调用开发者提供的回调，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">runZoned</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">runApp</span><span class=\"token punctuation\">(</span><span class=\"token function\">MyApp</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> onError<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>Object obj<span class=\"token punctuation\">,</span> StackTrace stack<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">var</span> details<span class=\"token operator\">=</span><span class=\"token function\">makeDetails</span><span class=\"token punctuation\">(</span>obj<span class=\"token punctuation\">,</span>stack<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token function\">reportError</span><span class=\"token punctuation\">(</span>details<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>这样一来，结合上面的<code>FlutterError.onError</code>我们就可以捕获我们Flutter应用中全部错误了！需要注意的是，error-zone内部发生的错误是不会跨越当前error-zone的边界的，如果想跨越error-zone边界去捕获异常，可以通过共同的“源”zone来捕获，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">var</span> future <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Future<span class=\"token punctuation\">.</span>value</span><span class=\"token punctuation\">(</span><span class=\"token number\">499</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token function\">runZoned</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n\t<span class=\"token keyword\">var</span> future2 <span class=\"token operator\">=</span> future<span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>_<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span> <span class=\"token keyword\">throw</span> <span class=\"token string\">&quot;error in first error-zone&quot;</span><span class=\"token punctuation\">;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\t<span class=\"token function\">runZoned</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n\t\t<span class=\"token keyword\">var</span> future3 <span class=\"token operator\">=</span> future2<span class=\"token punctuation\">.</span><span class=\"token function\">catchError</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span> <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Never reached!&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\t<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> onError<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span> <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;unused error handler&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> onError<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span> <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;catches error of first error-zone.&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n</code></pre></div></li></ul> <h3 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h3> <p>我们最终的异常捕获和上报代码大致如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">collectLog</span><span class=\"token punctuation\">(</span>String line<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//收集日志</span>\n<span class=\"token punctuation\">}</span>\n<span class=\"token keyword\">void</span> <span class=\"token function\">reportErrorAndLog</span><span class=\"token punctuation\">(</span>FlutterErrorDetails details<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//上报错误和日志逻辑</span>\n<span class=\"token punctuation\">}</span>\n\nFlutterErrorDetails <span class=\"token function\">makeDetails</span><span class=\"token punctuation\">(</span>Object obj<span class=\"token punctuation\">,</span> StackTrace stack<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token comment\">// 构建错误信息</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">void</span> <span class=\"token function\">main</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  FlutterError<span class=\"token punctuation\">.</span>onError <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>FlutterErrorDetails details<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">reportErrorAndLog</span><span class=\"token punctuation\">(</span>details<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n  <span class=\"token function\">runZoned</span><span class=\"token punctuation\">(</span>\n    <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">runApp</span><span class=\"token punctuation\">(</span><span class=\"token function\">MyApp</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    zoneSpecification<span class=\"token punctuation\">:</span> <span class=\"token function\">ZoneSpecification</span><span class=\"token punctuation\">(</span>\n      print<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>Zone self<span class=\"token punctuation\">,</span> ZoneDelegate parent<span class=\"token punctuation\">,</span> Zone zone<span class=\"token punctuation\">,</span> String line<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">collectLog</span><span class=\"token punctuation\">(</span>line<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// 收集日志</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    onError<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>Object obj<span class=\"token punctuation\">,</span> StackTrace stack<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">var</span> details <span class=\"token operator\">=</span> <span class=\"token function\">makeDetails</span><span class=\"token punctuation\">(</span>obj<span class=\"token punctuation\">,</span> stack<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token function\">reportErrorAndLog</span><span class=\"token punctuation\">(</span>details<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter2/flutter_app_debug.html\" class=\"prev\">\n        2.5 调试Flutter应用\n      </a></span> <span class=\"next\"><a href=\"/chapter3/flutter_widget_intro.html\">\n        3.1 Widget简介\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/95.bf70fc45.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter3/buttons.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>3.4 按钮 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/7.9aacc405.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable open\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" aria-current=\"page\" class=\"active sidebar-link\">3.4 按钮</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/chapter3/buttons.html#_3-4-1-material组件库中的按钮\" class=\"sidebar-link\">3.4.1 Material组件库中的按钮</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter3/buttons.html#_3-4-2-自定义按钮外观\" class=\"sidebar-link\">3.4.2 自定义按钮外观</a></li></ul></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_3-4-按钮\"><a href=\"#_3-4-按钮\" class=\"header-anchor\">#</a> 3.4 按钮</h1> <h2 id=\"_3-4-1-material组件库中的按钮\"><a href=\"#_3-4-1-material组件库中的按钮\" class=\"header-anchor\">#</a> 3.4.1 Material组件库中的按钮</h2> <p>Material 组件库中提供了多种按钮组件如<code>RaisedButton</code>、<code>FlatButton</code>、<code>OutlineButton</code>等，它们都是直接或间接对<code>RawMaterialButton</code>组件的包装定制，所以他们大多数属性都和<code>RawMaterialButton</code>一样。在介绍各个按钮时我们先介绍其默认外观，而按钮的外观大都可以通过属性来自定义，我们在后面统一介绍这些属性。另外，所有Material 库中的按钮都有如下相同点：</p> <ol><li>按下时都会有“水波动画”（又称“涟漪动画”，就是点击时按钮上会出现水波荡漾的动画）。</li> <li>有一个<code>onPressed</code>属性来设置点击回调，当按钮按下时会执行该回调，如果不提供该回调则按钮会处于禁用状态，禁用状态不响应用户点击。</li></ol> <h3 id=\"raisedbutton\"><a href=\"#raisedbutton\" class=\"header-anchor\">#</a> RaisedButton</h3> <p><code>RaisedButton</code> 即&quot;漂浮&quot;按钮，它默认带有阴影和灰色背景。按下后，阴影会变大，如图3-10所示：</p> <p><img src=\"data:image/png;base64,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J598cvF5NIITjs6BoyOvvPKKGYzao+zJJ59MK6+8svVj5plrAy1da5R88cUX6eCDD07ff/990e8XXnghXX311QnnlBNiIwRynuISAj5833jjjfZxU/HM+a+//rrgGsfgHF9ORhZffHHLk+acfir77rvvpg8//NBsT5zC82mnnVZ8PPXtt99Od999dzrkkEMsENBGU6R2awB8yBJScfgbbrjBgMcQbr/9dsOUSHz44Ycb6eutt545JXW33Xbb9O233yaidVlwfp4KZ5xxRlphhRWGnN58882tjdtuuy0tsMAC9kWkY4891gIM7d58883m9BgbxEtOPPHEhPOfffbZ6b777rM25p133oQxvPzyy6pWKy2jrlWnKjpz/PHHm/PvvPPO6d5777UZ2hprrGFP8vPOO8+uwEl5YuP8zOjgigC96aabps8//zwdddRRabnllrMZxDLLLGPXnHrqqfYwqbilBQLugTz00ENFlcsuu8zS48ePt6DALOOUU04x57/ooovM8QlMBAcWFf3DpGikxonaBYClllrKouiCCy6Yll56aZuyg99TTz1lMAIwK+pzzDFHOv/889NCCy2UqIvTIhiEj/qUERxOOumktMUWW1iblEkgljaY/p9++ukqtkBCuzwZttlmGyt/8cUXi/O8XjCd3GSTTdJcc81lbfCqgNQ1ABSdr3ECx4YrsD3iiCPs89a8ox944IHW6zfeeMM0dsDskM+eM2ODK3gkMCPYCAGP1zYFPpy0/Bpnlf/3R68AOLaEmQei14pLL73UHhI77rhjWmedddJ8882Xll9++UQe4ZWlSVK7eaqmZAIRYhGe7ghTMqT8vXvIxcEfe+wxCxa777671eMPBHUjCy+8cFHN90Pvdrx6SFZccUXrE1NNHP7LL7+0g/M8JUKmDwGcFWx57+fpz5Se93N9rZoAgfzwww+mCb6amlPA6x/rAXJ6q9TlH2YMCG1zH3hkLYJXDq1B8TqK8O6voESemSuCLeyyyy6WrsMf4SBd7lPtAkC5g8qLeL1zewdVHQUL3vV6IeoDbTMDYNrJKwmGQ/CQUfbi3oPUJq9a7M/j9HBKANeakHBQQKiyA63TqG63GidZd911EzO99957z5ycazX9Jw3fiBYWLeP++HULV1zbZGMCgBDU05jIXBYt9LGT0EvhP9CQ89966622a8D9LrjgAivv5b1z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\" alt=\"图3-10\"></p> <p>使用<code>RaisedButton</code>非常简单，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;normal&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><h3 id=\"flatbutton\"><a href=\"#flatbutton\" class=\"header-anchor\">#</a> FlatButton</h3> <p><code>FlatButton</code>即扁平按钮，默认背景透明并不带阴影。按下后，会有背景色，如图3-11所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图3-11\"></p> <p>使用FlatButton也很简单，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;normal&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><h3 id=\"outlinebutton\"><a href=\"#outlinebutton\" class=\"header-anchor\">#</a> OutlineButton</h3> <p><code>OutlineButton</code>默认有一个边框，不带阴影且背景透明。按下后，边框颜色会变亮、同时出现背景和阴影(较弱)，如图3-12所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图3-12\"></p> <p>使用<code>OutlineButton</code>也很简单，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">OutlineButton</span><span class=\"token punctuation\">(</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;normal&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><h3 id=\"iconbutton\"><a href=\"#iconbutton\" class=\"header-anchor\">#</a> IconButton</h3> <p><code>IconButton</code>是一个可点击的Icon，不包括文字，默认没有背景，点击后会出现背景，如图3-13所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图3-13\"></p> <p>代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">IconButton</span><span class=\"token punctuation\">(</span>\n  icon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>thumb_up<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><h3 id=\"带图标的按钮\"><a href=\"#带图标的按钮\" class=\"header-anchor\">#</a> 带图标的按钮</h3> <p><code>RaisedButton</code>、<code>FlatButton</code>、<code>OutlineButton</code>都有一个<code>icon</code> 构造函数，通过它可以轻松创建带图标的按钮，如图3-14所示：</p> <p><img src=\"/assets/img/3-14.e8ad30dc.png\" alt=\"图3-14\"></p> <p>代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>RaisedButton<span class=\"token punctuation\">.</span><span class=\"token function\">icon</span><span class=\"token punctuation\">(</span>\n  icon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>send<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  label<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;发送&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  onPressed<span class=\"token punctuation\">:</span> _onPressed<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\nOutlineButton<span class=\"token punctuation\">.</span><span class=\"token function\">icon</span><span class=\"token punctuation\">(</span>\n  icon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>add<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  label<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;添加&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  onPressed<span class=\"token punctuation\">:</span> _onPressed<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\nFlatButton<span class=\"token punctuation\">.</span><span class=\"token function\">icon</span><span class=\"token punctuation\">(</span>\n  icon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>info<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  label<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;详情&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  onPressed<span class=\"token punctuation\">:</span> _onPressed<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</code></pre></div><h2 id=\"_3-4-2-自定义按钮外观\"><a href=\"#_3-4-2-自定义按钮外观\" class=\"header-anchor\">#</a> 3.4.2 自定义按钮外观</h2> <p>按钮外观可以通过其属性来定义，不同按钮属性大同小异，我们以FlatButton为例，介绍一下常见的按钮属性，详细的信息可以查看API文档。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">const</span> <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>  \n  <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>onPressed<span class=\"token punctuation\">,</span> <span class=\"token comment\">//按钮点击回调</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>textColor<span class=\"token punctuation\">,</span> <span class=\"token comment\">//按钮文字颜色</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>disabledTextColor<span class=\"token punctuation\">,</span> <span class=\"token comment\">//按钮禁用时的文字颜色</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>color<span class=\"token punctuation\">,</span> <span class=\"token comment\">//按钮背景颜色</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>disabledColor<span class=\"token punctuation\">,</span><span class=\"token comment\">//按钮禁用时的背景颜色</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>highlightColor<span class=\"token punctuation\">,</span> <span class=\"token comment\">//按钮按下时的背景颜色</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>splashColor<span class=\"token punctuation\">,</span> <span class=\"token comment\">//点击时，水波动画中水波的颜色</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>colorBrightness<span class=\"token punctuation\">,</span><span class=\"token comment\">//按钮主题，默认是浅色主题 </span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>padding<span class=\"token punctuation\">,</span> <span class=\"token comment\">//按钮的填充</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>shape<span class=\"token punctuation\">,</span> <span class=\"token comment\">//外形</span>\n  <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span> <span class=\"token comment\">//按钮的内容</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>其中大多数属性名都是自解释的，我们不赘述。下面我们通过一个示例来看看如何自定义按钮。</p> <h4 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h4> <p>定义一个背景蓝色，两边圆角的按钮。效果如图3-15所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图3-15\"></p> <p>代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n  color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span>\n  highlightColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">[</span><span class=\"token number\">700</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  colorBrightness<span class=\"token punctuation\">:</span> Brightness<span class=\"token punctuation\">.</span>dark<span class=\"token punctuation\">,</span>\n  splashColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Submit&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  shape<span class=\"token punctuation\">:</span><span class=\"token function\">RoundedRectangleBorder</span><span class=\"token punctuation\">(</span>borderRadius<span class=\"token punctuation\">:</span> BorderRadius<span class=\"token punctuation\">.</span><span class=\"token function\">circular</span><span class=\"token punctuation\">(</span><span class=\"token number\">20.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>很简单吧，在上面的代码中，我们主要通过<code>shape</code>来指定其外形为一个圆角矩形。因为按钮背景是蓝色(深色)，我们需要指定按钮主题<code>colorBrightness</code>为<code>Brightness.dark</code>，这是为了保证按钮文字颜色为浅色。</p> <p>Flutter 中没有提供去除背景的设置，假若我们需要去除背景，则可以通过将背景颜色设置为全透明来实现。对应上面的代码，便是将 <code>color: Colors.blue</code> 替换为 <code>color: Color(0x000000)</code>。</p> <p>细心的读者可能会发现这个按钮没有阴影(点击之后也没有)，这样会显得没有质感。其实这也很容易，将上面的<code>FlatButton</code>换成<code>RaisedButton</code>就行，其它代码不用改（这里 color 也不做更改），换了之后的效果如图3-16所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图3-16\"></p> <p>是不是有质感了！之所以会这样，是因为<code>RaisedButton</code>默认有配置阴影：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">const</span> <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>elevation <span class=\"token operator\">=</span> <span class=\"token number\">2.0</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//正常状态下的阴影</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>highlightElevation <span class=\"token operator\">=</span> <span class=\"token number\">8.0</span><span class=\"token punctuation\">,</span><span class=\"token comment\">//按下时的阴影</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>disabledElevation <span class=\"token operator\">=</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span><span class=\"token comment\">// 禁用时的阴影</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>值得注意的是，在Material 组件库中，我们会在很多组件中见到elevation相关的属性，它们都是用来控制阴影的，这是因为阴影在Material设计风格中是一种很重要的表现形式，以后在介绍其它组件时，便不再赘述。</p> <p>如果我们想实现一个背景渐变的圆角按钮，按钮有没有相应的属性呢？答案是否定的，但是，我们可以通过其它方式来实现，我们将在后面&quot;自定义组件&quot;一章中实现。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter3/text.html\" class=\"prev\">\n        3.3 文本及样式\n      </a></span> <span class=\"next\"><a href=\"/chapter3/img_and_icon.html\">\n        3.5 图片及ICON\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/7.9aacc405.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter3/flutter_widget_intro.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>3.1 Widget简介 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/21.c0363ec5.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable open\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" aria-current=\"page\" class=\"active sidebar-link\">3.1 Widget简介</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/chapter3/flutter_widget_intro.html#_3-1-1-概念\" class=\"sidebar-link\">3.1.1 概念</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter3/flutter_widget_intro.html#_3-1-2-widget与element\" class=\"sidebar-link\">3.1.2 Widget与Element</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter3/flutter_widget_intro.html#_3-1-3-widget主要接口\" class=\"sidebar-link\">3.1.3 Widget主要接口</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter3/flutter_widget_intro.html#_3-1-4-statelesswidget\" class=\"sidebar-link\">3.1.4 StatelessWidget</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter3/flutter_widget_intro.html#_3-1-5-statefulwidget\" class=\"sidebar-link\">3.1.5 StatefulWidget</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter3/flutter_widget_intro.html#_3-1-6-state\" class=\"sidebar-link\">3.1.6 State</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter3/flutter_widget_intro.html#_3-1-7-在widget树中获取state对象\" class=\"sidebar-link\">3.1.7 在Widget树中获取State对象</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter3/flutter_widget_intro.html#_3-1-8-flutter-sdk内置组件库介绍\" class=\"sidebar-link\">3.1.8 Flutter SDK内置组件库介绍</a></li></ul></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_3-1-widget简介\"><a href=\"#_3-1-widget简介\" class=\"header-anchor\">#</a> 3.1 Widget简介</h1> <h2 id=\"_3-1-1-概念\"><a href=\"#_3-1-1-概念\" class=\"header-anchor\">#</a> 3.1.1 概念</h2> <p>在前面的介绍中，我们知道在Flutter中几乎所有的对象都是一个Widget。与原生开发中“控件”不同的是，Flutter中的Widget的概念更广泛，它不仅可以表示UI元素，也可以表示一些功能性的组件如：用于手势检测的 <code>GestureDetector</code> widget、用于APP主题数据传递的<code>Theme</code>等等，而原生开发中的控件通常只是指UI元素。在后面的内容中，我们在描述UI元素时可能会用到“控件”、“组件”这样的概念，读者心里需要知道他们就是widget，只是在不同场景的不同表述而已。由于Flutter主要就是用于构建用户界面的，所以，在大多数时候，读者可以认为widget就是一个控件，不必纠结于概念。</p> <h2 id=\"_3-1-2-widget与element\"><a href=\"#_3-1-2-widget与element\" class=\"header-anchor\">#</a> 3.1.2 Widget与Element</h2> <p>在Flutter中，Widget的功能是“描述一个UI元素的配置数据”，它就是说，Widget其实并不是表示最终绘制在设备屏幕上的显示元素，而它只是描述显示元素的一个配置数据。</p> <p>实际上，Flutter中真正代表屏幕上显示元素的类是<code>Element</code>，也就是说Widget只是描述<code>Element</code>的配置数据！有关<code>Element</code>的详细介绍我们将在本书后面的高级部分深入介绍，现在，读者只需要知道：<strong>Widget只是UI元素的一个配置数据，并且一个Widget可以对应多个<code>Element</code></strong>。这是因为同一个Widget对象可以被添加到UI树的不同部分，而真正渲染时，UI树的每一个<code>Element</code>节点都会对应一个Widget对象。总结一下：</p> <ul><li>Widget实际上就是<code>Element</code>的配置数据，Widget树实际上是一个配置树，而真正的UI渲染树是由<code>Element</code>构成；不过，由于<code>Element</code>是通过Widget生成的，所以它们之间有对应关系，在大多数场景，我们可以宽泛地认为Widget树就是指UI控件树或UI渲染树。</li> <li>一个Widget对象可以对应多个<code>Element</code>对象。这很好理解，根据同一份配置（Widget），可以创建多个实例（Element）。</li></ul> <p>读者应该将这两点牢记在心中。</p> <h2 id=\"_3-1-3-widget主要接口\"><a href=\"#_3-1-3-widget主要接口\" class=\"header-anchor\">#</a> 3.1.3 Widget主要接口</h2> <p>我们先来看一下Widget类的声明：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token metadata symbol\">@immutable</span>\n<span class=\"token keyword\">abstract</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">Widget</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">DiagnosticableTree</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function\">Widget</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>key <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> Key key<span class=\"token punctuation\">;</span>\n    \n  <span class=\"token metadata symbol\">@protected</span>\n  Element <span class=\"token function\">createElement</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  String <span class=\"token function\">toStringShort</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> key <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">?</span> <span class=\"token string\">'$runtimeType'</span> <span class=\"token punctuation\">:</span> <span class=\"token string\">'$runtimeType-$key'</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">debugFillProperties</span><span class=\"token punctuation\">(</span>DiagnosticPropertiesBuilder properties<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">debugFillProperties</span><span class=\"token punctuation\">(</span>properties<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    properties<span class=\"token punctuation\">.</span>defaultDiagnosticsTreeStyle <span class=\"token operator\">=</span> DiagnosticsTreeStyle<span class=\"token punctuation\">.</span>dense<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  \n  <span class=\"token keyword\">static</span> bool <span class=\"token function\">canUpdate</span><span class=\"token punctuation\">(</span>Widget oldWidget<span class=\"token punctuation\">,</span> Widget newWidget<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> oldWidget<span class=\"token punctuation\">.</span>runtimeType <span class=\"token operator\">==</span> newWidget<span class=\"token punctuation\">.</span>runtimeType\n        <span class=\"token operator\">&amp;&amp;</span> oldWidget<span class=\"token punctuation\">.</span>key <span class=\"token operator\">==</span> newWidget<span class=\"token punctuation\">.</span>key<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><ul><li><code>Widget</code>类继承自<code>DiagnosticableTree</code>，<code>DiagnosticableTree</code>即“诊断树”，主要作用是提供调试信息。</li> <li><code>Key</code>: 这个<code>key</code>属性类似于React/Vue中的<code>key</code>，主要的作用是决定是否在下一次<code>build</code>时复用旧的widget，决定的条件在<code>canUpdate()</code>方法中。</li> <li><code>createElement()</code>：正如前文所述“一个Widget可以对应多个<code>Element</code>”；Flutter Framework在构建UI树时，会先调用此方法生成对应节点的<code>Element</code>对象。此方法是Flutter Framework隐式调用的，在我们开发过程中基本不会调用到。</li> <li><code>debugFillProperties(...)</code> 复写父类的方法，主要是设置诊断树的一些特性。</li> <li><code>canUpdate(...)</code>是一个静态方法，它主要用于在Widget树重新<code>build</code>时复用旧的widget，其实具体来说，应该是：是否用新的Widget对象去更新旧UI树上所对应的<code>Element</code>对象的配置；通过其源码我们可以看到，只要<code>newWidget</code>与<code>oldWidget</code>的<code>runtimeType</code>和<code>key</code>同时相等时就会用<code>newWidget</code>去更新<code>Element</code>对象的配置，否则就会创建新的<code>Element</code>。</li></ul> <p>有关Key和Widget复用的细节将会在本书后面高级部分深入讨论，读者现在只需知道，为Widget显式添加key的话可能（但不一定）会使UI在重新构建时变的高效，读者目前可以先忽略此参数。本书后面的示例中，只会在构建列表项UI时会显式指定Key。</p> <p>另外<code>Widget</code>类本身是一个抽象类，其中最核心的就是定义了<code>createElement()</code>接口，在Flutter开发中，我们一般都不用直接继承<code>Widget</code>类来实现一个新组件，相反，我们通常会通过继承<code>StatelessWidget</code>或<code>StatefulWidget</code>来间接继承<code>Widget</code>类来实现。<code>StatelessWidget</code>和<code>StatefulWidget</code>都是直接继承自<code>Widget</code>类，而这两个类也正是Flutter中非常重要的两个抽象类，它们引入了两种Widget模型，接下来我们将重点介绍一下这两个类。</p> <h2 id=\"_3-1-4-statelesswidget\"><a href=\"#_3-1-4-statelesswidget\" class=\"header-anchor\">#</a> 3.1.4 StatelessWidget</h2> <p>在之前的章节中，我们已经简单介绍过<code>StatelessWidget</code>，<code>StatelessWidget</code>相对比较简单，它继承自<code>Widget</code>类，重写了<code>createElement()</code>方法：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token metadata symbol\">@override</span>\nStatelessElement <span class=\"token function\">createElement</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">StatelessElement</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p><code>StatelessElement</code> 间接继承自<code>Element</code>类，与<code>StatelessWidget</code>相对应（作为其配置数据）。</p> <p><code>StatelessWidget</code>用于不需要维护状态的场景，它通常在<code>build</code>方法中通过嵌套其它Widget来构建UI，在构建过程中会递归的构建其嵌套的Widget。我们看一个简单的例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">Echo</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function\">Echo</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>  \n    <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>text<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>backgroundColor<span class=\"token punctuation\">:</span>Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span><span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span>key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    \n  <span class=\"token keyword\">final</span> String text<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> Color backgroundColor<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n        color<span class=\"token punctuation\">:</span> backgroundColor<span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>text<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面的代码，实现了一个回显字符串的<code>Echo</code> widget。</p> <blockquote><p>按照惯例，<code>widget</code>的构造函数参数应使用命名参数，命名参数中的必要参数要添加<code>@required</code>标注，这样有利于静态代码分析器进行检查。另外，在继承<code>widget</code>时，第一个参数通常应该是<code>Key</code>，另外，如果Widget需要接收子Widget，那么<code>child</code>或<code>children</code>参数通常应被放在参数列表的最后。同样是按照惯例，Widget的属性应尽可能的被声明为<code>final</code>，防止被意外改变。</p></blockquote> <p>然后我们可以通过如下方式使用它：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">Echo</span><span class=\"token punctuation\">(</span>text<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;hello world&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行后效果如图3-1所示：</p> <p><img src=\"/assets/img/3-1.587e85ad.png\" alt=\"图3-1\"></p> <h3 id=\"context\"><a href=\"#context\" class=\"header-anchor\">#</a> Context</h3> <p><code>build</code>方法有一个<code>context</code>参数，它是<code>BuildContext</code>类的一个实例，表示当前widget在widget树中的上下文，每一个widget都会对应一个context对象（因为每一个widget都是widget树上的一个节点）。实际上，<code>context</code>是当前widget在widget树中位置中执行”相关操作“的一个句柄，比如它提供了从当前widget开始向上遍历widget树以及按照widget类型查找父级widget的方法。下面是在子树中获取父级widget的一个示例：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">ContextRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n      appBar<span class=\"token punctuation\">:</span> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>\n        title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Context测试&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      body<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token comment\">// 在Widget树中向上查找最近的父级`Scaffold` widget</span>\n          Scaffold scaffold <span class=\"token operator\">=</span> context<span class=\"token punctuation\">.</span>findAncestorWidgetOfExactType<span class=\"token operator\">&lt;</span>Scaffold<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token comment\">// 直接返回 AppBar的title， 此处实际上是Text(&quot;Context测试&quot;)</span>\n          <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>scaffold<span class=\"token punctuation\">.</span>appBar <span class=\"token operator\">as</span> AppBar<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>title<span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行后效果如图3-1-1所示：</p> <p><img src=\"/assets/img/3-1-1.63daca67.png\" alt=\"图3-1-1\"></p> <blockquote><p><strong>注意</strong>：对于<code>BuildContext</code>读者现在可以先作了解，随着本书后面内容的展开，也会用到Context的一些方法，读者可以通过具体的场景对其有个直观的认识。关于<code>BuildContext</code>更多的内容，我们也将在后面高级部分再深入介绍。</p></blockquote> <h2 id=\"_3-1-5-statefulwidget\"><a href=\"#_3-1-5-statefulwidget\" class=\"header-anchor\">#</a> 3.1.5 StatefulWidget</h2> <p>和<code>StatelessWidget</code>一样，<code>StatefulWidget</code>也是继承自<code>Widget</code>类，并重写了<code>createElement()</code>方法，不同的是返回的<code>Element</code> 对象并不相同；另外<code>StatefulWidget</code>类中添加了一个新的接口<code>createState()</code>。</p> <p>下面我们看看<code>StatefulWidget</code>的类定义：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">abstract</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">Widget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function\">StatefulWidget</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> Key key <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    \n  <span class=\"token metadata symbol\">@override</span>\n  StatefulElement <span class=\"token function\">createElement</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">StatefulElement</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    \n  <span class=\"token metadata symbol\">@protected</span>\n  State <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><ul><li><p><code>StatefulElement</code> 间接继承自<code>Element</code>类，与StatefulWidget相对应（作为其配置数据）。<code>StatefulElement</code>中可能会多次调用<code>createState()</code>来创建状态(State)对象。</p></li> <li><p><code>createState()</code> 用于创建和Stateful widget相关的状态，它在Stateful widget的生命周期中可能会被多次调用。例如，当一个Stateful widget同时插入到widget树的多个位置时，Flutter framework就会调用该方法为每一个位置生成一个独立的State实例，其实，本质上就是一个<code>StatefulElement</code>对应一个State实例。</p> <blockquote><p>在本书中经常会出现“树”的概念，在不同的场景可能指不同的意思，在说“widget树”时它可以指widget结构树，但由于widget与Element有对应关系（一可能对多），在有些场景（Flutter的SDK文档中）也代指“UI树”的意思。而在stateful widget中，State对象也和<code>StatefulElement</code>具有对应关系（一对一），所以在Flutter的SDK文档中，可以经常看到“从树中移除State对象”或“插入State对象到树中”这样的描述。其实，无论哪种描述，其意思都是在描述“一棵构成用户界面的节点元素的树”，读者不必纠结于这些概念，还是那句话“得其神，忘其形”，因此，本书中出现的各种“树”，如果没有特别说明，读者都可抽象的认为它是“一棵构成用户界面的节点元素的树”。</p></blockquote></li></ul> <h2 id=\"_3-1-6-state\"><a href=\"#_3-1-6-state\" class=\"header-anchor\">#</a> 3.1.6 State</h2> <p>一个StatefulWidget类会对应一个State类，State表示与其对应的StatefulWidget要维护的状态，State中的保存的状态信息可以：</p> <ol><li>在widget 构建时可以被同步读取。</li> <li>在widget生命周期中可以被改变，当State被改变时，可以手动调用其<code>setState()</code>方法通知Flutter framework状态发生改变，Flutter framework在收到消息后，会重新调用其<code>build</code>方法重新构建widget树，从而达到更新UI的目的。</li></ol> <p>State中有两个常用属性：</p> <ol><li><p><code>widget</code>，它表示与该State实例关联的widget实例，由Flutter framework动态设置。注意，这种关联并非永久的，因为在应用生命周期中，UI树上的某一个节点的widget实例在重新构建时可能会变化，但State实例只会在第一次插入到树中时被创建，当在重新构建时，如果widget被修改了，Flutter framework会动态设置State.widget为新的widget实例。</p></li> <li><p><code>context</code>。StatefulWidget对应的BuildContext，作用同StatelessWidget的BuildContext。</p></li></ol> <h4 id=\"state生命周期\"><a href=\"#state生命周期\" class=\"header-anchor\">#</a> State生命周期</h4> <p>理解State的生命周期对flutter开发非常重要，为了加深读者印象，本节我们通过一个实例来演示一下State的生命周期。在接下来的示例中，我们实现一个计数器widget，点击它可以使计数器加1，由于要保存计数器的数值状态，所以我们应继承StatefulWidget，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">CounterWidget</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function\">CounterWidget</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>initValue<span class=\"token punctuation\">:</span> <span class=\"token number\">0</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> int initValue<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  _CounterWidgetState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_CounterWidgetState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p><code>CounterWidget</code>接收一个<code>initValue</code>整型参数，它表示计数器的初始值。下面我们看一下State的代码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">_CounterWidgetState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>CounterWidget<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>  \n  int _counter<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//初始化状态  </span>\n    _counter<span class=\"token operator\">=</span>widget<span class=\"token punctuation\">.</span>initValue<span class=\"token punctuation\">;</span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;initState&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;build&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n      body<span class=\"token punctuation\">:</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'$_counter'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token comment\">//点击后计数器自增</span>\n          onPressed<span class=\"token punctuation\">:</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span><span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token operator\">++</span>_counter<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">didUpdateWidget</span><span class=\"token punctuation\">(</span>CounterWidget oldWidget<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">didUpdateWidget</span><span class=\"token punctuation\">(</span>oldWidget<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;didUpdateWidget&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">deactivate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">deactivate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;deactive&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;dispose&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">reassemble</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">reassemble</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;reassemble&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">didChangeDependencies</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">didChangeDependencies</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;didChangeDependencies&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>接下来，我们创建一个新路由，在新路由中，我们只显示一个<code>CounterWidget</code>：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">CounterWidget</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们运行应用并打开该路由页面，在新路由页打开后，屏幕中央就会出现一个数字0，然后控制台日志输出：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>I/flutter ( 5436): initState\nI/flutter ( 5436): didChangeDependencies\nI/flutter ( 5436): build\n</code></pre></div><p>可以看到，在StatefulWidget插入到Widget树时首先<code>initState</code>方法会被调用。</p> <p>然后我们点击⚡️按钮热重载，控制台输出日志如下：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>I/flutter ( 5436): reassemble\nI/flutter ( 5436): didUpdateWidget\nI/flutter ( 5436): build\n</code></pre></div><p>可以看到此时<code>initState</code> 和<code>didChangeDependencies</code>都没有被调用，而此时<code>didUpdateWidget</code>被调用。</p> <p>接下来，我们在widget树中移除<code>CounterWidget</code>，将路由<code>build</code>方法改为：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">//移除计数器 </span>\n  <span class=\"token comment\">//return CounterWidget();</span>\n  <span class=\"token comment\">//随便返回一个Text()</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;xxx&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>然后热重载，日志如下：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>I/flutter ( 5436): reassemble\nI/flutter ( 5436): deactive\nI/flutter ( 5436): dispose\n</code></pre></div><p>我们可以看到，在<code>CounterWidget</code>从widget树中移除时，<code>deactive</code>和<code>dispose</code>会依次被调用。</p> <p>下面我们来看看各个回调函数：</p> <ul><li><p><code>initState</code>：当Widget第一次插入到Widget树时会被调用，对于每一个State对象，Flutter framework只会调用一次该回调，所以，通常在该回调中做一些一次性的操作，如状态初始化、订阅子树的事件通知等。不能在该回调中调用<code>BuildContext.dependOnInheritedWidgetOfExactType</code>（该方法用于在Widget树上获取离当前widget最近的一个父级<code>InheritFromWidget</code>，关于<code>InheritedWidget</code>我们将在后面章节介绍），原因是在初始化完成后，Widget树中的<code>InheritFromWidget</code>也可能会发生变化，所以正确的做法应该在在<code>build（）</code>方法或<code>didChangeDependencies()</code>中调用它。</p></li> <li><p><code>didChangeDependencies()</code>：当State对象的依赖发生变化时会被调用；例如：在之前<code>build()</code> 中包含了一个<code>InheritedWidget</code>，然后在之后的<code>build()</code> 中<code>InheritedWidget</code>发生了变化，那么此时<code>InheritedWidget</code>的子widget的<code>didChangeDependencies()</code>回调都会被调用。典型的场景是当系统语言Locale或应用主题改变时，Flutter framework会通知widget调用此回调。</p></li> <li><p><code>build()</code>：此回调读者现在应该已经相当熟悉了，它主要是用于构建Widget子树的，会在如下场景被调用：</p> <ol><li>在调用<code>initState()</code>之后。</li> <li>在调用<code>didUpdateWidget()</code>之后。</li> <li>在调用<code>setState()</code>之后。</li> <li>在调用<code>didChangeDependencies()</code>之后。</li> <li>在State对象从树中一个位置移除后（会调用deactivate）又重新插入到树的其它位置之后。</li></ol></li> <li><p><code>reassemble()</code>：此回调是专门为了开发调试而提供的，在热重载(hot reload)时会被调用，此回调在Release模式下永远不会被调用。</p></li> <li><p><code>didUpdateWidget()</code>：在widget重新构建时，Flutter framework会调用<code>Widget.canUpdate</code>来检测Widget树中同一位置的新旧节点，然后决定是否需要更新，如果<code>Widget.canUpdate</code>返回<code>true</code>则会调用此回调。正如之前所述，<code>Widget.canUpdate</code>会在新旧widget的key和runtimeType同时相等时会返回true，也就是说在在新旧widget的key和runtimeType同时相等时<code>didUpdateWidget()</code>就会被调用。</p></li> <li><p><code>deactivate()</code>：当State对象从树中被移除时，会调用此回调。在一些场景下，Flutter framework会将State对象重新插到树中，如包含此State对象的子树在树的一个位置移动到另一个位置时（可以通过GlobalKey来实现）。如果移除后没有重新插入到树中则紧接着会调用<code>dispose()</code>方法。</p></li> <li><p><code>dispose()</code>：当State对象从树中被永久移除时调用；通常在此回调中释放资源。</p></li></ul> <p>StatefulWidget生命周期如图3-2所示：</p> <p><img src=\"/assets/img/3-2.a59bef97.jpg\" alt=\"图3-2\"></p> <blockquote><p><strong>注意</strong>：在继承<code>StatefulWidget</code>重写其方法时，对于包含<code>@mustCallSuper</code>标注的父类方法，都要在子类方法中先调用父类方法。</p></blockquote> <h3 id=\"为什么要将build方法放在state中-而不是放在statefulwidget中\"><a href=\"#为什么要将build方法放在state中-而不是放在statefulwidget中\" class=\"header-anchor\">#</a> 为什么要将build方法放在State中，而不是放在StatefulWidget中？</h3> <p>现在，我们回答之前提出的问题，为什么<code>build()</code>方法放在State（而不是<code>StatefulWidget</code>）中 ？这主要是为了提高开发的灵活性。如果将<code>build()</code>方法在<code>StatefulWidget</code>中则会有两个问题：</p> <ul><li><p>状态访问不便。</p> <p>试想一下，如果我们的<code>StatefulWidget</code>有很多状态，而每次状态改变都要调用<code>build</code>方法，由于状态是保存在State中的，如果<code>build</code>方法在<code>StatefulWidget</code>中，那么<code>build</code>方法和状态分别在两个类中，那么构建时读取状态将会很不方便！试想一下，如果真的将<code>build</code>方法放在StatefulWidget中的话，由于构建用户界面过程需要依赖State，所以<code>build</code>方法将必须加一个<code>State</code>参数，大概是下面这样：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> State state<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">//state.counter</span>\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token punctuation\">}</span>\n</code></pre></div><p>这样的话就只能将State的所有状态声明为公开的状态，这样才能在State类外部访问状态！但是，将状态设置为公开后，状态将不再具有私密性，这就会导致对状态的修改将会变的不可控。但如果将<code>build()</code>方法放在State中的话，构建过程不仅可以直接访问状态，而且也无需公开私有状态，这会非常方便。</p></li> <li><p>继承<code>StatefulWidget</code>不便。</p> <p>例如，Flutter中有一个动画widget的基类<code>AnimatedWidget</code>，它继承自<code>StatefulWidget</code>类。<code>AnimatedWidget</code>中引入了一个抽象方法<code>build(BuildContext context)</code>，继承自<code>AnimatedWidget</code>的动画widget都要实现这个<code>build</code>方法。现在设想一下，如果<code>StatefulWidget</code> 类中已经有了一个<code>build</code>方法，正如上面所述，此时<code>build</code>方法需要接收一个state对象，这就意味着<code>AnimatedWidget</code>必须将自己的State对象(记为_animatedWidgetState)提供给其子类，因为子类需要在其<code>build</code>方法中调用父类的<code>build</code>方法，代码可能如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">MyAnimationWidget</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">AnimatedWidget</span><span class=\"token punctuation\">{</span>\n    <span class=\"token metadata symbol\">@override</span>\n    Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> State state<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">//由于子类要用到AnimatedWidget的状态对象_animatedWidgetState，</span>\n      <span class=\"token comment\">//所以AnimatedWidget必须通过某种方式将其状态对象_animatedWidgetState</span>\n      <span class=\"token comment\">//暴露给其子类   </span>\n      <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">build</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> _animatedWidgetState<span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>这样很显然是不合理的，因为</p> <ol><li><code>AnimatedWidget</code>的状态对象是<code>AnimatedWidget</code>内部实现细节，不应该暴露给外部。</li> <li>如果要将父类状态暴露给子类，那么必须得有一种传递机制，而做这一套传递机制是无意义的，因为父子类之间状态的传递和子类本身逻辑是无关的。</li></ol></li></ul> <p>综上所述，可以发现，对于<code>StatefulWidget</code>，将<code>build</code>方法放在State中，可以给开发带来很大的灵活性。</p> <h2 id=\"_3-1-7-在widget树中获取state对象\"><a href=\"#_3-1-7-在widget树中获取state对象\" class=\"header-anchor\">#</a> 3.1.7 在Widget树中获取State对象</h2> <p>由于StatefulWidget的的具体逻辑都在其State中，所以很多时候，我们需要获取StatefulWidget对应的State对象来调用一些方法，比如<code>Scaffold</code>组件对应的状态类<code>ScaffoldState</code>中就定义了打开SnackBar(路由页底部提示条)的方法。我们有两种方法在子widget树中获取父级StatefulWidget的State对象。</p> <h3 id=\"通过context获取\"><a href=\"#通过context获取\" class=\"header-anchor\">#</a> 通过Context获取</h3> <p><code>context</code>对象有一个<code>findAncestorStateOfType()</code>方法，该方法可以从当前节点沿着widget树向上查找指定类型的StatefulWidget对应的State对象。下面是实现打开SnackBar的示例：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n  appBar<span class=\"token punctuation\">:</span> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>\n    title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;子树中获取State对象&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  body<span class=\"token punctuation\">:</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n        onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token comment\">// 查找父级最近的Scaffold对应的ScaffoldState对象</span>\n          ScaffoldState _state <span class=\"token operator\">=</span> context<span class=\"token punctuation\">.</span>findAncestorStateOfType<span class=\"token operator\">&lt;</span>ScaffoldState<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token comment\">//调用ScaffoldState的showSnackBar来弹出SnackBar</span>\n          _state<span class=\"token punctuation\">.</span><span class=\"token function\">showSnackBar</span><span class=\"token punctuation\">(</span>\n            <span class=\"token function\">SnackBar</span><span class=\"token punctuation\">(</span>\n              content<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;我是SnackBar&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;显示SnackBar&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>上面示例运行后，点击”显示SnackBar“，效果如图3-1-2所示：</p> <p><img src=\"/assets/img/3-1-2.70506d6d.png\" alt=\"图3-1-2\"></p> <p>一般来说，如果StatefulWidget的状态是私有的（不应该向外部暴露），那么我们代码中就不应该去直接获取其State对象；如果StatefulWidget的状态是希望暴露出的（通常还有一些组件的操作方法），我们则可以去直接获取其State对象。但是通过<code>context.findAncestorStateOfType</code>获取StatefulWidget的状态的方法是通用的，我们并不能在语法层面指定StatefulWidget的状态是否私有，所以在Flutter开发中便有了一个默认的约定：如果StatefulWidget的状态是希望暴露出的，应当在StatefulWidget中提供一个<code>of</code>静态方法来获取其State对象，开发者便可直接通过该方法来获取；如果State不希望暴露，则不提供<code>of</code>方法。这个约定在Flutter SDK里随处可见。所以，上面示例中的<code>Scaffold</code>也提供了一个<code>of</code>方法，我们其实是可以直接调用它的：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token comment\">//省略无关代码</span>\n<span class=\"token comment\">// 直接通过of静态方法来获取ScaffoldState </span>\nScaffoldState _state<span class=\"token operator\">=</span>Scaffold<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> \n_state<span class=\"token punctuation\">.</span><span class=\"token function\">showSnackBar</span><span class=\"token punctuation\">(</span>\n  <span class=\"token function\">SnackBar</span><span class=\"token punctuation\">(</span>\n    content<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;我是SnackBar&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><h3 id=\"通过globalkey\"><a href=\"#通过globalkey\" class=\"header-anchor\">#</a> 通过GlobalKey</h3> <p>Flutter还有一种通用的获取<code>State</code>对象的方法——通过GlobalKey来获取！ 步骤分两步：</p> <ol><li><p>给目标<code>StatefulWidget</code>添加<code>GlobalKey</code>。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//定义一个globalKey, 由于GlobalKey要保持全局唯一性，我们使用静态变量存储</span>\n<span class=\"token keyword\">static</span> GlobalKey<span class=\"token operator\">&lt;</span>ScaffoldState<span class=\"token operator\">&gt;</span> _globalKey<span class=\"token operator\">=</span> <span class=\"token function\">GlobalKey</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n    key<span class=\"token punctuation\">:</span> _globalKey <span class=\"token punctuation\">,</span> <span class=\"token comment\">//设置key</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>  \n<span class=\"token punctuation\">)</span>\n</code></pre></div></li> <li><p>通过<code>GlobalKey</code>来获取<code>State</code>对象</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>_globalKey<span class=\"token punctuation\">.</span>currentState<span class=\"token punctuation\">.</span><span class=\"token function\">openDrawer</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n</code></pre></div></li></ol> <p>GlobalKey是Flutter提供的一种在整个APP中引用element的机制。如果一个widget设置了<code>GlobalKey</code>，那么我们便可以通过<code>globalKey.currentWidget</code>获得该widget对象、<code>globalKey.currentElement</code>来获得widget对应的element对象，如果当前widget是<code>StatefulWidget</code>，则可以通过<code>globalKey.currentState</code>来获得该widget对应的state对象。</p> <blockquote><p>注意：使用GlobalKey开销较大，如果有其他可选方案，应尽量避免使用它。另外同一个GlobalKey在整个widget树中必须是唯一的，不能重复。</p></blockquote> <h2 id=\"_3-1-8-flutter-sdk内置组件库介绍\"><a href=\"#_3-1-8-flutter-sdk内置组件库介绍\" class=\"header-anchor\">#</a> 3.1.8 Flutter SDK内置组件库介绍</h2> <p>Flutter提供了一套丰富、强大的基础组件，在基础组件库之上Flutter又提供了一套Material风格（Android默认的视觉风格）和一套Cupertino风格（iOS视觉风格）的组件库。要使用基础组件库，需要先导入：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/widgets.dart'</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>下面我们介绍一下常用的组件。</p> <h4 id=\"基础组件\"><a href=\"#基础组件\" class=\"header-anchor\">#</a> 基础组件</h4> <ul><li><a href=\"https://docs.flutter.io/flutter/widgets/Text-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>Text</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>：该组件可让您创建一个带格式的文本。</li> <li><a href=\"https://docs.flutter.io/flutter/widgets/Row-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>Row</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>、 <a href=\"https://docs.flutter.io/flutter/widgets/Column-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>Column</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>： 这些具有弹性空间的布局类Widget可让您在水平（Row）和垂直（Column）方向上创建灵活的布局。其设计是基于Web开发中的Flexbox布局模型。</li> <li><a href=\"https://docs.flutter.io/flutter/widgets/Stack-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>Stack</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>： 取代线性布局 (译者语：和Android中的<code>FrameLayout</code>相似)，<a href=\"https://docs.flutter.io/flutter/widgets/Stack-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>Stack</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>允许子 widget 堆叠， 你可以使用 <a href=\"https://docs.flutter.io/flutter/widgets/Positioned-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>Positioned</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 来定位他们相对于<code>Stack</code>的上下左右四条边的位置。Stacks是基于Web开发中的绝对定位（absolute positioning )布局模型设计的。</li> <li><a href=\"https://docs.flutter.io/flutter/widgets/Container-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>Container</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>： <a href=\"https://docs.flutter.io/flutter/widgets/Container-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>Container</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 可让您创建矩形视觉元素。container 可以装饰一个<a href=\"https://docs.flutter.io/flutter/painting/BoxDecoration-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>BoxDecoration</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>, 如 background、一个边框、或者一个阴影。 <a href=\"https://docs.flutter.io/flutter/widgets/Container-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>Container</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 也可以具有边距（margins）、填充(padding)和应用于其大小的约束(constraints)。另外， <a href=\"https://docs.flutter.io/flutter/widgets/Container-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>Container</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>可以使用矩阵在三维空间中对其进行变换。</li></ul> <h4 id=\"material组件\"><a href=\"#material组件\" class=\"header-anchor\">#</a> Material组件</h4> <p>Flutter提供了一套丰富的Material组件，它可以帮助我们构建遵循Material Design设计规范的应用程序。Material应用程序以<a href=\"https://docs.flutter.io/flutter/material/MaterialApp-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>MaterialApp</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 组件开始， 该组件在应用程序的根部创建了一些必要的组件，比如<code>Theme</code>组件，它用于配置应用的主题。 是否使用<a href=\"https://docs.flutter.io/flutter/material/MaterialApp-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>MaterialApp</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>完全是可选的，但是使用它是一个很好的做法。在之前的示例中，我们已经使用过多个Material 组件了，如：<code>Scaffold</code>、<code>AppBar</code>、<code>FlatButton</code>等。要使用Material 组件，需要先引入它：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n</code></pre></div><h4 id=\"cupertino组件\"><a href=\"#cupertino组件\" class=\"header-anchor\">#</a> Cupertino组件</h4> <p>Flutter也提供了一套丰富的Cupertino风格的组件，尽管目前还没有Material 组件那么丰富，但是它仍在不断的完善中。值得一提的是在Material 组件库中有一些组件可以根据实际运行平台来切换表现风格，比如<code>MaterialPageRoute</code>，在路由切换时，如果是Android系统，它将会使用Android系统默认的页面切换动画(从底向上)；如果是iOS系统，它会使用iOS系统默认的页面切换动画（从右向左）。由于在前面的示例中还没有Cupertino组件的示例，下面我们实现一个简单的Cupertino组件风格的页面：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//导入cupertino widget库</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/cupertino.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">CupertinoTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">CupertinoPageScaffold</span><span class=\"token punctuation\">(</span>\n      navigationBar<span class=\"token punctuation\">:</span> <span class=\"token function\">CupertinoNavigationBar</span><span class=\"token punctuation\">(</span>\n        middle<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Cupertino Demo&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">CupertinoButton</span><span class=\"token punctuation\">(</span>\n            color<span class=\"token punctuation\">:</span> CupertinoColors<span class=\"token punctuation\">.</span>activeBlue<span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Press&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>下面（图3-3）是在iPhoneX上页面效果截图：</p> <p><img src=\"/assets/img/3-3.3b5b2dd3.png\" alt=\"图3-3\"></p> <h3 id=\"关于示例\"><a href=\"#关于示例\" class=\"header-anchor\">#</a> 关于示例</h3> <p>本章后面章节的示例中会使用一些布局类组件，如<code>Scaffold</code>、<code>Row</code>、<code>Column</code>等，这些组件将在后面“布局类组件”一章中详细介绍，读者可以先不用关注。</p> <h3 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h3> <p>Flutter提供了丰富的组件，在实际的开发中你可以根据需要随意使用它们，而不必担心引入过多组件库会让你的应用安装包变大，这不是web开发，dart在编译时只会编译你使用了的代码。由于Material和Cupertino都是在基础组件库之上的，所以如果我们的应用中引入了这两者之一，则不需要再引入<code>flutter/widgets.dart</code>了，因为它们内部已经引入过了。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter2/thread_model_and_error_report.html\" class=\"prev\">\n        2.6 Flutter异常捕获\n      </a></span> <span class=\"next\"><a href=\"/chapter3/state_manage.html\">\n        3.2 状态管理\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/21.c0363ec5.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter3/img_and_icon.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>3.5 图片及ICON | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/13.e93e35ca.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable open\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" aria-current=\"page\" class=\"active sidebar-link\">3.5 图片及ICON</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/chapter3/img_and_icon.html#_3-5-图片及icon\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter3/img_and_icon.html#_3-5-1-图片\" class=\"sidebar-link\">3.5.1 图片</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter3/img_and_icon.html#_3-5-2-icon\" class=\"sidebar-link\">3.5.2 ICON</a></li></ul></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h2 id=\"_3-5-图片及icon\"><a href=\"#_3-5-图片及icon\" class=\"header-anchor\">#</a> 3.5 图片及ICON</h2> <h2 id=\"_3-5-1-图片\"><a href=\"#_3-5-1-图片\" class=\"header-anchor\">#</a> 3.5.1 图片</h2> <p>Flutter中，我们可以通过<code>Image</code>组件来加载并显示图片，<code>Image</code>的数据源可以是asset、文件、内存以及网络。</p> <h3 id=\"imageprovider\"><a href=\"#imageprovider\" class=\"header-anchor\">#</a> ImageProvider</h3> <p><code>ImageProvider</code> 是一个抽象类，主要定义了图片数据获取的接口<code>load()</code>，从不同的数据源获取图片需要实现不同的<code>ImageProvider</code> ，如<code>AssetImage</code>是实现了从Asset中加载图片的ImageProvider，而<code>NetworkImage</code>实现了从网络加载图片的ImageProvider。</p> <h3 id=\"image\"><a href=\"#image\" class=\"header-anchor\">#</a> Image</h3> <p><code>Image</code> widget有一个必选的<code>image</code>参数，它对应一个ImageProvider。下面我们分别演示一下如何从asset和网络加载图片。</p> <h4 id=\"从asset中加载图片\"><a href=\"#从asset中加载图片\" class=\"header-anchor\">#</a> 从asset中加载图片</h4> <ol><li><p>在工程根目录下创建一个<code>images目录</code>，并将图片avatar.png拷贝到该目录。</p></li> <li><p>在<code>pubspec.yaml</code>中的<code>flutter</code>部分添加如下内容：</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code>  <span class=\"token key atrule\">assets</span><span class=\"token punctuation\">:</span>\n    <span class=\"token punctuation\">-</span> images/avatar.png\n</code></pre></div></li></ol> <blockquote><p>注意: 由于 yaml 文件对缩进严格，所以必须严格按照每一层两个空格的方式进行缩进，此处assets前面应有两个空格。</p></blockquote> <ol start=\"3\"><li><p>加载该图片</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Image</span><span class=\"token punctuation\">(</span>\n  image<span class=\"token punctuation\">:</span> <span class=\"token function\">AssetImage</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;images/avatar.png&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  width<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>Image也提供了一个快捷的构造函数<code>Image.asset</code>用于从asset中加载、显示图片：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Image<span class=\"token punctuation\">.</span><span class=\"token function\">asset</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;images/avatar.png&quot;</span><span class=\"token punctuation\">,</span>\n  width<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div></li></ol> <h4 id=\"从网络加载图片\"><a href=\"#从网络加载图片\" class=\"header-anchor\">#</a> 从网络加载图片</h4> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Image</span><span class=\"token punctuation\">(</span>\n  image<span class=\"token punctuation\">:</span> <span class=\"token function\">NetworkImage</span><span class=\"token punctuation\">(</span>\n      <span class=\"token string\">&quot;https://avatars2.githubusercontent.com/u/20411648?s=460&amp;v=4&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  width<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>Image也提供了一个快捷的构造函数<code>Image.network</code>用于从网络加载、显示图片：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Image<span class=\"token punctuation\">.</span><span class=\"token function\">network</span><span class=\"token punctuation\">(</span>\n  <span class=\"token string\">&quot;https://avatars2.githubusercontent.com/u/20411648?s=460&amp;v=4&quot;</span><span class=\"token punctuation\">,</span>\n  width<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行上面两个示例，图片加载成功后如图3-17所示：</p> <p><img src=\"/assets/img/3-17.a063365a.png\" alt=\"图3-17\"></p> <h4 id=\"参数\"><a href=\"#参数\" class=\"header-anchor\">#</a> 参数</h4> <p><code>Image</code>在显示图片时定义了一系列参数，通过这些参数我们可以控制图片的显示外观、大小、混合效果等。我们看一下Image的主要参数：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">const</span> <span class=\"token function\">Image</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>width<span class=\"token punctuation\">,</span> <span class=\"token comment\">//图片的宽</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>height<span class=\"token punctuation\">,</span> <span class=\"token comment\">//图片高度</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>color<span class=\"token punctuation\">,</span> <span class=\"token comment\">//图片的混合色值</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>colorBlendMode<span class=\"token punctuation\">,</span> <span class=\"token comment\">//混合模式</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>fit<span class=\"token punctuation\">,</span><span class=\"token comment\">//缩放模式</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>alignment <span class=\"token operator\">=</span> Alignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span> <span class=\"token comment\">//对齐方式</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>repeat <span class=\"token operator\">=</span> ImageRepeat<span class=\"token punctuation\">.</span>noRepeat<span class=\"token punctuation\">,</span> <span class=\"token comment\">//重复方式</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><ul><li><p><code>width</code>、<code>height</code>：用于设置图片的宽、高，当不指定宽高时，图片会根据当前父容器的限制，尽可能的显示其原始大小，如果只设置<code>width</code>、<code>height</code>的其中一个，那么另一个属性默认会按比例缩放，但可以通过下面介绍的<code>fit</code>属性来指定适应规则。</p></li> <li><p><code>fit</code>：该属性用于在图片的显示空间和图片本身大小不同时指定图片的适应模式。适应模式是在<code>BoxFit</code>中定义，它是一个枚举类型，有如下值：</p> <ul><li><code>fill</code>：会拉伸填充满显示空间，图片本身长宽比会发生变化，图片会变形。</li> <li><code>cover</code>：会按图片的长宽比放大后居中填满显示空间，图片不会变形，超出显示空间部分会被剪裁。</li> <li><code>contain</code>：这是图片的默认适应规则，图片会在保证图片本身长宽比不变的情况下缩放以适应当前显示空间，图片不会变形。</li> <li><code>fitWidth</code>：图片的宽度会缩放到显示空间的宽度，高度会按比例缩放，然后居中显示，图片不会变形，超出显示空间部分会被剪裁。</li> <li><code>fitHeight</code>：图片的高度会缩放到显示空间的高度，宽度会按比例缩放，然后居中显示，图片不会变形，超出显示空间部分会被剪裁。</li> <li><code>none</code>：图片没有适应策略，会在显示空间内显示图片，如果图片比显示空间大，则显示空间只会显示图片中间部分。</li></ul> <p>一图胜万言！ 我们对一个宽高相同的头像图片应用不同的<code>fit</code>值，效果如图3-18所示：</p> <p><img src=\"/assets/img/3-18.cb8a4b0f.png\" alt=\"图3-18\"></p></li> <li><p><code>color</code>和 <code>colorBlendMode</code>：在图片绘制时可以对每一个像素进行颜色混合处理，<code>color</code>指定混合色，而<code>colorBlendMode</code>指定混合模式，下面是一个简单的示例：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Image</span><span class=\"token punctuation\">(</span>\n  image<span class=\"token punctuation\">:</span> <span class=\"token function\">AssetImage</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;images/avatar.png&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  width<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n  color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span>\n  colorBlendMode<span class=\"token punctuation\">:</span> BlendMode<span class=\"token punctuation\">.</span>difference<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div></li></ul> <p>运行效果如图3-19所示（彩色）:</p> <p><img src=\"/assets/img/3-19.feb336de.png\" alt=\"图3-19\"></p> <ul><li><p><code>repeat</code>：当图片本身大小小于显示空间时，指定图片的重复规则。简单示例如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Image</span><span class=\"token punctuation\">(</span>\n  image<span class=\"token punctuation\">:</span> <span class=\"token function\">AssetImage</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;images/avatar.png&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  width<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n  height<span class=\"token punctuation\">:</span> <span class=\"token number\">200.0</span><span class=\"token punctuation\">,</span>\n  repeat<span class=\"token punctuation\">:</span> ImageRepeat<span class=\"token punctuation\">.</span>repeatY <span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行后效果如图3-20所示：</p> <p><img src=\"/assets/img/3-20.9c7569a6.png\" alt=\"图3-20\"></p></li></ul> <p>完整的示例代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">ImageAndIconRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">var</span> img<span class=\"token operator\">=</span><span class=\"token function\">AssetImage</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;imgs/avatar.png&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">SingleChildScrollView</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n        children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Image<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n          <span class=\"token function\">Image</span><span class=\"token punctuation\">(</span>\n            image<span class=\"token punctuation\">:</span> img<span class=\"token punctuation\">,</span>\n            height<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n            width<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n            fit<span class=\"token punctuation\">:</span> BoxFit<span class=\"token punctuation\">.</span>fill<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">Image</span><span class=\"token punctuation\">(</span>\n            image<span class=\"token punctuation\">:</span> img<span class=\"token punctuation\">,</span>\n            height<span class=\"token punctuation\">:</span> <span class=\"token number\">50</span><span class=\"token punctuation\">,</span>\n            width<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n            fit<span class=\"token punctuation\">:</span> BoxFit<span class=\"token punctuation\">.</span>contain<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">Image</span><span class=\"token punctuation\">(</span>\n            image<span class=\"token punctuation\">:</span> img<span class=\"token punctuation\">,</span>\n            width<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n            height<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n            fit<span class=\"token punctuation\">:</span> BoxFit<span class=\"token punctuation\">.</span>cover<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">Image</span><span class=\"token punctuation\">(</span>\n            image<span class=\"token punctuation\">:</span> img<span class=\"token punctuation\">,</span>\n            width<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n            height<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n            fit<span class=\"token punctuation\">:</span> BoxFit<span class=\"token punctuation\">.</span>fitWidth<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">Image</span><span class=\"token punctuation\">(</span>\n            image<span class=\"token punctuation\">:</span> img<span class=\"token punctuation\">,</span>\n            width<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n            height<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n            fit<span class=\"token punctuation\">:</span> BoxFit<span class=\"token punctuation\">.</span>fitHeight<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">Image</span><span class=\"token punctuation\">(</span>\n            image<span class=\"token punctuation\">:</span> img<span class=\"token punctuation\">,</span>\n            width<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n            height<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n            fit<span class=\"token punctuation\">:</span> BoxFit<span class=\"token punctuation\">.</span>scaleDown<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">Image</span><span class=\"token punctuation\">(</span>\n            image<span class=\"token punctuation\">:</span> img<span class=\"token punctuation\">,</span>\n            height<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n            width<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n            fit<span class=\"token punctuation\">:</span> BoxFit<span class=\"token punctuation\">.</span>none<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">Image</span><span class=\"token punctuation\">(</span>\n            image<span class=\"token punctuation\">:</span> img<span class=\"token punctuation\">,</span>\n            width<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n            color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span>\n            colorBlendMode<span class=\"token punctuation\">:</span> BlendMode<span class=\"token punctuation\">.</span>difference<span class=\"token punctuation\">,</span>\n            fit<span class=\"token punctuation\">:</span> BoxFit<span class=\"token punctuation\">.</span>fill<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">Image</span><span class=\"token punctuation\">(</span>\n            image<span class=\"token punctuation\">:</span> img<span class=\"token punctuation\">,</span>\n            width<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n            height<span class=\"token punctuation\">:</span> <span class=\"token number\">200.0</span><span class=\"token punctuation\">,</span>\n            repeat<span class=\"token punctuation\">:</span> ImageRepeat<span class=\"token punctuation\">.</span>repeatY <span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">return</span> <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n            children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n              <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n                padding<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">16.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                child<span class=\"token punctuation\">:</span> <span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span>\n                  width<span class=\"token punctuation\">:</span> <span class=\"token number\">100</span><span class=\"token punctuation\">,</span>\n                  child<span class=\"token punctuation\">:</span> e<span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">.</span>fit<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toList</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h3 id=\"image缓存\"><a href=\"#image缓存\" class=\"header-anchor\">#</a> Image缓存</h3> <p>Flutter框架对加载过的图片是有缓存的（内存），默认最大缓存数量是1000，最大缓存空间为100M。关于Image的详细内容及原理我们将会在后面进阶部分深入介绍。</p> <h2 id=\"_3-5-2-icon\"><a href=\"#_3-5-2-icon\" class=\"header-anchor\">#</a> 3.5.2 ICON</h2> <p>Flutter中，可以像Web开发一样使用iconfont，iconfont即“字体图标”，它是将图标做成字体文件，然后通过指定不同的字符而显示不同的图片。</p> <blockquote><p>在字体文件中，每一个字符都对应一个位码，而每一个位码对应一个显示字形，不同的字体就是指字形不同，即字符对应的字形是不同的。而在iconfont中，只是将位码对应的字形做成了图标，所以不同的字符最终就会渲染成不同的图标。</p></blockquote> <p>在Flutter开发中，iconfont和图片相比有如下优势：</p> <ol><li>体积小：可以减小安装包大小。</li> <li>矢量的：iconfont都是矢量图标，放大不会影响其清晰度。</li> <li>可以应用文本样式：可以像文本一样改变字体图标的颜色、大小对齐等。</li> <li>可以通过TextSpan和文本混用。</li></ol> <h5 id=\"使用material-design字体图标\"><a href=\"#使用material-design字体图标\" class=\"header-anchor\">#</a> 使用Material Design字体图标</h5> <p>Flutter默认包含了一套Material Design的字体图标，在<code>pubspec.yaml</code>文件中的配置如下</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code><span class=\"token key atrule\">flutter</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">uses-material-design</span><span class=\"token punctuation\">:</span> <span class=\"token boolean important\">true</span>\n</code></pre></div><p>Material Design所有图标可以在其官网查看：https://material.io/tools/icons/</p> <p>我们看一个简单的例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>String icons <span class=\"token operator\">=</span> <span class=\"token string\">&quot;&quot;</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">// accessible: &amp;#xE914; or 0xE914 or E914</span>\nicons <span class=\"token operator\">+=</span> <span class=\"token string\">&quot;\\uE914&quot;</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">// error: &amp;#xE000; or 0xE000 or E000</span>\nicons <span class=\"token operator\">+=</span> <span class=\"token string\">&quot; \\uE000&quot;</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">// fingerprint: &amp;#xE90D; or 0xE90D or E90D</span>\nicons <span class=\"token operator\">+=</span> <span class=\"token string\">&quot; \\uE90D&quot;</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>icons<span class=\"token punctuation\">,</span>\n  style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>\n      fontFamily<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;MaterialIcons&quot;</span><span class=\"token punctuation\">,</span>\n      fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">24.0</span><span class=\"token punctuation\">,</span>\n      color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>green\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>运行效果如图3-21所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图3-21\"></p> <p>通过这个示例可以看到，使用图标就像使用文本一样，但是这种方式需要我们提供每个图标的码点，这并对开发者不友好，所以，Flutter封装了<code>IconData</code>和<code>Icon</code>来专门显示字体图标，上面的例子也可以用如下方式实现：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n  mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>accessible<span class=\"token punctuation\">,</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>green<span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>error<span class=\"token punctuation\">,</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>green<span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>fingerprint<span class=\"token punctuation\">,</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>green<span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p><code>Icons</code>类中包含了所有Material Design图标的<code>IconData</code>静态变量定义。</p> <h4 id=\"使用自定义字体图标\"><a href=\"#使用自定义字体图标\" class=\"header-anchor\">#</a> 使用自定义字体图标</h4> <p>我们也可以使用自定义字体图标。iconfont.cn上有很多字体图标素材，我们可以选择自己需要的图标打包下载后，会生成一些不同格式的字体文件，在Flutter中，我们使用ttf格式即可。</p> <p>假设我们项目中需要使用一个书籍图标和微信图标，我们打包下载后导入：</p> <ol><li><p>导入字体图标文件；这一步和导入字体文件相同，假设我们的字体图标文件保存在项目根目录下，路径为&quot;fonts/iconfont.ttf&quot;：</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code><span class=\"token key atrule\">fonts</span><span class=\"token punctuation\">:</span>\n  <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">family</span><span class=\"token punctuation\">:</span> myIcon  <span class=\"token comment\">#指定一个字体名</span>\n    <span class=\"token key atrule\">fonts</span><span class=\"token punctuation\">:</span>\n      <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">asset</span><span class=\"token punctuation\">:</span> fonts/iconfont.ttf\n</code></pre></div></li> <li><p>为了使用方便，我们定义一个<code>MyIcons</code>类，功能和<code>Icons</code>类一样：将字体文件中的所有图标都定义成静态变量：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">MyIcons</span><span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// book 图标</span>\n  <span class=\"token keyword\">static</span> <span class=\"token keyword\">const</span> IconData book <span class=\"token operator\">=</span> <span class=\"token keyword\">const</span> <span class=\"token function\">IconData</span><span class=\"token punctuation\">(</span>\n      <span class=\"token number\">0xe614</span><span class=\"token punctuation\">,</span> \n      fontFamily<span class=\"token punctuation\">:</span> <span class=\"token string\">'myIcon'</span><span class=\"token punctuation\">,</span> \n      matchTextDirection<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">// 微信图标</span>\n  <span class=\"token keyword\">static</span> <span class=\"token keyword\">const</span> IconData wechat <span class=\"token operator\">=</span> <span class=\"token keyword\">const</span> <span class=\"token function\">IconData</span><span class=\"token punctuation\">(</span>\n      <span class=\"token number\">0xec7d</span><span class=\"token punctuation\">,</span>  \n      fontFamily<span class=\"token punctuation\">:</span> <span class=\"token string\">'myIcon'</span><span class=\"token punctuation\">,</span> \n      matchTextDirection<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div></li> <li><p>使用</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n  mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>MyIcons<span class=\"token punctuation\">.</span>book<span class=\"token punctuation\">,</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>purple<span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>MyIcons<span class=\"token punctuation\">.</span>wechat<span class=\"token punctuation\">,</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>green<span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行后效果如图3-22所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图3-22\"></p></li></ol></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter3/buttons.html\" class=\"prev\">\n        3.4 按钮\n      </a></span> <span class=\"next\"><a href=\"/chapter3/radio_and_checkbox.html\">\n        3.6 单选开关和复选框\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/13.e93e35ca.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter3/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>基础Widget | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/158.49064f28.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"基础widget\"><a href=\"#基础widget\" class=\"header-anchor\">#</a> 基础Widget</h1> <p>本节介绍一下Flutter中常用的一些基础widget，由于大多数widget的属性都比较多，我们在介绍widget时会着重介绍常用的属性，而不会像API文档一样所有属性都介绍，关于属性详细的信息请参考Flutter SDK文档。</p> <h2 id=\"本章目录\"><a href=\"#本章目录\" class=\"header-anchor\">#</a> 本章目录</h2> <ul><li><a href=\"/chapter3/flutter_widget_intro.html\">3.1：Widget简介</a></li> <li><a href=\"/chapter3/state_manage.html\">3.2：状态管理</a></li> <li><a href=\"/chapter3/text.html\">3.3：文本、字体样式</a></li> <li><a href=\"/chapter3/buttons.html\">3.4：按钮</a></li> <li><a href=\"/chapter3/img_and_icon.html\">3.5：图片和Icon</a></li> <li><a href=\"/chapter3/radio_and_checkbox.html\">3.6：单选框和复选框</a></li> <li><a href=\"/chapter3/input_and_form.html\">3.7：输入框和表单</a></li> <li><a href=\"/chapter3/progress.html\">3.8：进度指示器</a></li></ul></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/158.49064f28.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter3/input_and_form.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>3.7 输入框及表单 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/14.4d0ac363.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable open\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" aria-current=\"page\" class=\"active sidebar-link\">3.7 输入框及表单</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/chapter3/input_and_form.html#_3-7-1-textfield\" class=\"sidebar-link\">3.7.1 TextField</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter3/input_and_form.html#_3-7-2-表单form\" class=\"sidebar-link\">3.7.2 表单Form</a></li></ul></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_3-7-输入框及表单\"><a href=\"#_3-7-输入框及表单\" class=\"header-anchor\">#</a> 3.7 输入框及表单</h1> <p>Material组件库中提供了输入框组件<code>TextField</code>和表单组件<code>Form</code>。下面我们分别介绍一下。</p> <h2 id=\"_3-7-1-textfield\"><a href=\"#_3-7-1-textfield\" class=\"header-anchor\">#</a> 3.7.1 TextField</h2> <p><code>TextField</code>用于文本输入，它提供了很多属性，我们先简单介绍一下主要属性的作用，然后通过几个示例来演示一下关键属性的用法。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">const</span> <span class=\"token function\">TextField</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  TextEditingController controller<span class=\"token punctuation\">,</span> \n  FocusNode focusNode<span class=\"token punctuation\">,</span>\n  InputDecoration decoration <span class=\"token operator\">=</span> <span class=\"token keyword\">const</span> <span class=\"token function\">InputDecoration</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  TextInputType keyboardType<span class=\"token punctuation\">,</span>\n  TextInputAction textInputAction<span class=\"token punctuation\">,</span>\n  TextStyle style<span class=\"token punctuation\">,</span>\n  TextAlign textAlign <span class=\"token operator\">=</span> TextAlign<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n  bool autofocus <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n  bool obscureText <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n  int maxLines <span class=\"token operator\">=</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n  int maxLength<span class=\"token punctuation\">,</span>\n  bool maxLengthEnforced <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n  ValueChanged<span class=\"token operator\">&lt;</span>String<span class=\"token operator\">&gt;</span> onChanged<span class=\"token punctuation\">,</span>\n  VoidCallback onEditingComplete<span class=\"token punctuation\">,</span>\n  ValueChanged<span class=\"token operator\">&lt;</span>String<span class=\"token operator\">&gt;</span> onSubmitted<span class=\"token punctuation\">,</span>\n  List<span class=\"token operator\">&lt;</span>TextInputFormatter<span class=\"token operator\">&gt;</span> inputFormatters<span class=\"token punctuation\">,</span>\n  bool enabled<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>cursorWidth <span class=\"token operator\">=</span> <span class=\"token number\">2.0</span><span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>cursorRadius<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>cursorColor<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><ul><li><p><code>controller</code>：编辑框的控制器，通过它可以设置/获取编辑框的内容、选择编辑内容、监听编辑文本改变事件。大多数情况下我们都需要显式提供一个<code>controller</code>来与文本框交互。如果没有提供<code>controller</code>，则<code>TextField</code>内部会自动创建一个。</p></li> <li><p><code>focusNode</code>：用于控制<code>TextField</code>是否占有当前键盘的输入焦点。它是我们和键盘交互的一个句柄（handle）。</p></li> <li><p><code>InputDecoration</code>：用于控制<code>TextField</code>的外观显示，如提示文本、背景颜色、边框等。</p></li> <li><p><code>keyboardType</code>：用于设置该输入框默认的键盘输入类型，取值如下：</p> <table><thead><tr><th>TextInputType枚举值</th> <th>含义</th></tr></thead> <tbody><tr><td>text</td> <td>文本输入键盘</td></tr> <tr><td>multiline</td> <td>多行文本，需和maxLines配合使用(设为null或大于1)</td></tr> <tr><td>number</td> <td>数字；会弹出数字键盘</td></tr> <tr><td>phone</td> <td>优化后的电话号码输入键盘；会弹出数字键盘并显示“* #”</td></tr> <tr><td>datetime</td> <td>优化后的日期输入键盘；Android上会显示“: -”</td></tr> <tr><td>emailAddress</td> <td>优化后的电子邮件地址；会显示“@ .”</td></tr> <tr><td>url</td> <td>优化后的url输入键盘； 会显示“/ .”</td></tr></tbody></table></li> <li><p><code>textInputAction</code>：键盘动作按钮图标(即回车键位图标)，它是一个枚举值，有多个可选值，全部的取值列表读者可以查看API文档，下面是当值为<code>TextInputAction.search</code>时，原生Android系统下键盘样式如图3-24所示：</p> <p><img src=\"/assets/img/3-24.68d03561.png\" alt=\"图3-24\"></p></li> <li><p><code>style</code>：正在编辑的文本样式。</p></li> <li><p><code>textAlign</code>: 输入框内编辑文本在水平方向的对齐方式。</p></li> <li><p><code>autofocus</code>: 是否自动获取焦点。</p></li> <li><p><code>obscureText</code>：是否隐藏正在编辑的文本，如用于输入密码的场景等，文本内容会用“•”替换。</p></li> <li><p><code>maxLines</code>：输入框的最大行数，默认为1；如果为<code>null</code>，则无行数限制。</p></li> <li><p><code>maxLength</code>和<code>maxLengthEnforced</code> ：<code>maxLength</code>代表输入框文本的最大长度，设置后输入框右下角会显示输入的文本计数。<code>maxLengthEnforced</code>决定当输入文本长度超过<code>maxLength</code>时是否阻止输入，为<code>true</code>时会阻止输入，为<code>false</code>时不会阻止输入但输入框会变红。</p></li> <li><p><code>onChange</code>：输入框内容改变时的回调函数；注：内容改变事件也可以通过<code>controller</code>来监听。</p></li> <li><p><code>onEditingComplete</code>和<code>onSubmitted</code>：这两个回调都是在输入框输入完成时触发，比如按了键盘的完成键（对号图标）或搜索键（🔍图标）。不同的是两个回调签名不同，<code>onSubmitted</code>回调是<code>ValueChanged&lt;String&gt;</code>类型，它接收当前输入内容做为参数，而<code>onEditingComplete</code>不接收参数。</p></li> <li><p><code>inputFormatters</code>：用于指定输入格式；当用户输入内容改变时，会根据指定的格式来校验。</p></li> <li><p><code>enable</code>：如果为<code>false</code>，则输入框会被禁用，禁用状态不接收输入和事件，同时显示禁用态样式（在其<code>decoration</code>中定义）。</p></li> <li><p><code>cursorWidth</code>、<code>cursorRadius</code>和<code>cursorColor</code>：这三个属性是用于自定义输入框光标宽度、圆角和颜色的。</p></li></ul> <h4 id=\"示例-登录输入框\"><a href=\"#示例-登录输入框\" class=\"header-anchor\">#</a> 示例：登录输入框</h4> <h5 id=\"布局\"><a href=\"#布局\" class=\"header-anchor\">#</a> 布局</h5> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n        children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n          <span class=\"token function\">TextField</span><span class=\"token punctuation\">(</span>\n            autofocus<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n            decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">InputDecoration</span><span class=\"token punctuation\">(</span>\n                labelText<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;用户名&quot;</span><span class=\"token punctuation\">,</span>\n                hintText<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;用户名或邮箱&quot;</span><span class=\"token punctuation\">,</span>\n                prefixIcon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>person<span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">TextField</span><span class=\"token punctuation\">(</span>\n            decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">InputDecoration</span><span class=\"token punctuation\">(</span>\n                labelText<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;密码&quot;</span><span class=\"token punctuation\">,</span>\n                hintText<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;您的登录密码&quot;</span><span class=\"token punctuation\">,</span>\n                prefixIcon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>lock<span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            obscureText<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>运行后，效果如图3-25所示：</p> <p><img src=\"/assets/img/3-25.18d09233.png\" alt=\"图3-25\"></p> <h5 id=\"获取输入内容\"><a href=\"#获取输入内容\" class=\"header-anchor\">#</a> 获取输入内容</h5> <p>获取输入内容有两种方式：</p> <ol><li>定义两个变量，用于保存用户名和密码，然后在<code>onChange</code>触发时，各自保存一下输入内容。</li> <li>通过<code>controller</code>直接获取。</li></ol> <p>第一种方式比较简单，不在举例，我们来重点看一下第二种方式，我们以用户名输入框举例：</p> <p>定义一个<code>controller</code>：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//定义一个controller</span>\nTextEditingController _unameController <span class=\"token operator\">=</span> <span class=\"token function\">TextEditingController</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>然后设置输入框controller：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">TextField</span><span class=\"token punctuation\">(</span>\n    autofocus<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n    controller<span class=\"token punctuation\">:</span> _unameController<span class=\"token punctuation\">,</span> <span class=\"token comment\">//设置controller</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>通过controller获取输入框内容</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">print</span><span class=\"token punctuation\">(</span>_unameController<span class=\"token punctuation\">.</span>text<span class=\"token punctuation\">)</span>\n</code></pre></div><h5 id=\"监听文本变化\"><a href=\"#监听文本变化\" class=\"header-anchor\">#</a> 监听文本变化</h5> <p>监听文本变化也有两种方式：</p> <ol><li><p>设置<code>onChange</code>回调，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">TextField</span><span class=\"token punctuation\">(</span>\n    autofocus<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n    onChanged<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>v<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;onChange: $v&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div></li> <li><p>通过<code>controller</code>监听，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token metadata symbol\">@override</span>\n<span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">//监听输入改变  </span>\n  _unameController<span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>_unameController<span class=\"token punctuation\">.</span>text<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div></li></ol> <p>两种方式相比，<code>onChanged</code>是专门用于监听文本变化，而<code>controller</code>的功能却多一些，除了能监听文本变化外，它还可以设置默认值、选择文本，下面我们看一个例子：</p> <p>创建一个<code>controller</code>:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>TextEditingController _selectionController <span class=\"token operator\">=</span>  <span class=\"token function\">TextEditingController</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>设置默认值，并从第三个字符开始选中后面的字符</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>_selectionController<span class=\"token punctuation\">.</span>text<span class=\"token operator\">=</span><span class=\"token string\">&quot;hello world!&quot;</span><span class=\"token punctuation\">;</span>\n_selectionController<span class=\"token punctuation\">.</span>selection<span class=\"token operator\">=</span><span class=\"token function\">TextSelection</span><span class=\"token punctuation\">(</span>\n    baseOffset<span class=\"token punctuation\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span>\n    extentOffset<span class=\"token punctuation\">:</span> _selectionController<span class=\"token punctuation\">.</span>text<span class=\"token punctuation\">.</span>length\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>设置<code>controlle</code>r:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">TextField</span><span class=\"token punctuation\">(</span>\n  controller<span class=\"token punctuation\">:</span> _selectionController<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行效果如图3-26所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图3-26\"></p> <h5 id=\"控制焦点\"><a href=\"#控制焦点\" class=\"header-anchor\">#</a> 控制焦点</h5> <p>焦点可以通过<code>FocusNode</code>和<code>FocusScopeNode</code>来控制，默认情况下，焦点由<code>FocusScope</code>来管理，它代表焦点控制范围，可以在这个范围内可以通过<code>FocusScopeNode</code>在输入框之间移动焦点、设置默认焦点等。我们可以通过<code>FocusScope.of(context)</code> 来获取Widget树中默认的<code>FocusScopeNode</code>。下面看一个示例，在此示例中创建两个<code>TextField</code>，第一个自动获取焦点，然后创建两个按钮：</p> <ul><li>点击第一个按钮可以将焦点从第一个<code>TextField</code>挪到第二个<code>TextField</code>。</li> <li>点击第二个按钮可以关闭键盘。</li></ul> <p>我们要实现的效果如图3-27所示：</p> <p><img src=\"/assets/img/3-27.6ff2b58c.png\" alt=\"图3-27\"></p> <p>代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">FocusTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _FocusTestRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_FocusTestRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_FocusTestRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>FocusTestRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  FocusNode focusNode1 <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">FocusNode</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  FocusNode focusNode2 <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">FocusNode</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  FocusScopeNode focusScopeNode<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n      padding<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">16.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n        children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n          <span class=\"token function\">TextField</span><span class=\"token punctuation\">(</span>\n            autofocus<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span> \n            focusNode<span class=\"token punctuation\">:</span> focusNode1<span class=\"token punctuation\">,</span><span class=\"token comment\">//关联focusNode1</span>\n            decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">InputDecoration</span><span class=\"token punctuation\">(</span>\n                labelText<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;input1&quot;</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">TextField</span><span class=\"token punctuation\">(</span>\n            focusNode<span class=\"token punctuation\">:</span> focusNode2<span class=\"token punctuation\">,</span><span class=\"token comment\">//关联focusNode2</span>\n            decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">InputDecoration</span><span class=\"token punctuation\">(</span>\n                labelText<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;input2&quot;</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>ctx<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword\">return</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n              children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n                  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;移动焦点&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                    <span class=\"token comment\">//将焦点从第一个TextField移到第二个TextField</span>\n                    <span class=\"token comment\">// 这是一种写法 FocusScope.of(context).requestFocus(focusNode2);</span>\n                    <span class=\"token comment\">// 这是第二种写法</span>\n                    <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">null</span> <span class=\"token operator\">==</span> focusScopeNode<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n                      focusScopeNode <span class=\"token operator\">=</span> FocusScope<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                    <span class=\"token punctuation\">}</span>\n                    focusScopeNode<span class=\"token punctuation\">.</span><span class=\"token function\">requestFocus</span><span class=\"token punctuation\">(</span>focusNode2<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n                  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;隐藏键盘&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                    <span class=\"token comment\">// 当所有编辑框都失去焦点时键盘就会收起  </span>\n                    focusNode1<span class=\"token punctuation\">.</span><span class=\"token function\">unfocus</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                    focusNode2<span class=\"token punctuation\">.</span><span class=\"token function\">unfocus</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p><code>FocusNode</code>和<code>FocusScopeNode</code>还有一些其它的方法，详情可以查看API文档。</p> <h5 id=\"监听焦点状态改变事件\"><a href=\"#监听焦点状态改变事件\" class=\"header-anchor\">#</a> 监听焦点状态改变事件</h5> <p><code>FocusNode</code>继承自<code>ChangeNotifier</code>，通过<code>FocusNode</code>可以监听焦点的改变事件，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token comment\">// 创建 focusNode   </span>\nFocusNode focusNode <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">FocusNode</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token comment\">// focusNode绑定输入框   </span>\n<span class=\"token function\">TextField</span><span class=\"token punctuation\">(</span>focusNode<span class=\"token punctuation\">:</span> focusNode<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token comment\">// 监听焦点变化    </span>\nfocusNode<span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n   <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>focusNode<span class=\"token punctuation\">.</span>hasFocus<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>获得焦点时<code>focusNode.hasFocus</code>值为<code>true</code>，失去焦点时为<code>false</code>。</p> <h5 id=\"自定义样式\"><a href=\"#自定义样式\" class=\"header-anchor\">#</a> 自定义样式</h5> <p>虽然我们可以通过<code>decoration</code>属性来定义输入框样式，下面以自定义输入框下划线颜色为例来介绍一下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">TextField</span><span class=\"token punctuation\">(</span>\n  decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">InputDecoration</span><span class=\"token punctuation\">(</span>\n    labelText<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;请输入用户名&quot;</span><span class=\"token punctuation\">,</span>\n    prefixIcon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>person<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token comment\">// 未获得焦点下划线设为灰色</span>\n    enabledBorder<span class=\"token punctuation\">:</span> <span class=\"token function\">UnderlineInputBorder</span><span class=\"token punctuation\">(</span>\n      borderSide<span class=\"token punctuation\">:</span> <span class=\"token function\">BorderSide</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token comment\">//获得焦点下划线设为蓝色</span>\n    focusedBorder<span class=\"token punctuation\">:</span> <span class=\"token function\">UnderlineInputBorder</span><span class=\"token punctuation\">(</span>\n      borderSide<span class=\"token punctuation\">:</span> <span class=\"token function\">BorderSide</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</code></pre></div><p>上面代码我们直接通过InputDecoration的enabledBorder和focusedBorder来分别设置了输入框在未获取焦点和获得焦点后的下划线颜色。另外，我们也可以通过主题来自定义输入框的样式，下面我们探索一下如何在不使用enabledBorder和focusedBorder的情况下来自定义下滑线颜色。</p> <p>由于<code>TextField</code>在绘制下划线时使用的颜色是主题色里面的<code>hintColor</code>，但提示文本颜色也是用的<code>hintColor</code>， 如果我们直接修改<code>hintColor</code>，那么下划线和提示文本的颜色都会变。值得高兴的是<code>decoration</code>中可以设置<code>hintStyle</code>，它可以覆盖<code>hintColor</code>，并且主题中可以通过<code>inputDecorationTheme</code>来设置输入框默认的<code>decoration</code>。所以我们可以通过主题来自定义，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Theme</span><span class=\"token punctuation\">(</span>\n  data<span class=\"token punctuation\">:</span> Theme<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">copyWith</span><span class=\"token punctuation\">(</span>\n      hintColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">[</span><span class=\"token number\">200</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//定义下划线颜色</span>\n      inputDecorationTheme<span class=\"token punctuation\">:</span> <span class=\"token function\">InputDecorationTheme</span><span class=\"token punctuation\">(</span>\n          labelStyle<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token comment\">//定义label字体样式</span>\n          hintStyle<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">,</span> fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">14.0</span><span class=\"token punctuation\">)</span><span class=\"token comment\">//定义提示文本样式</span>\n      <span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n    children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n      <span class=\"token function\">TextField</span><span class=\"token punctuation\">(</span>\n        decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">InputDecoration</span><span class=\"token punctuation\">(</span>\n            labelText<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;用户名&quot;</span><span class=\"token punctuation\">,</span>\n            hintText<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;用户名或邮箱&quot;</span><span class=\"token punctuation\">,</span>\n            prefixIcon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>person<span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token function\">TextField</span><span class=\"token punctuation\">(</span>\n        decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">InputDecoration</span><span class=\"token punctuation\">(</span>\n            prefixIcon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>lock<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            labelText<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;密码&quot;</span><span class=\"token punctuation\">,</span>\n            hintText<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;您的登录密码&quot;</span><span class=\"token punctuation\">,</span>\n            hintStyle<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">,</span> fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">13.0</span><span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        obscureText<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行效果如图3-28所示：</p> <p><img src=\"/assets/img/3-28.b6b9c9b2.png\" alt=\"图3-28\"></p> <p>我们成功的自定义了下划线颜色和提问文字样式，细心的读者可能已经发现，通过这种方式自定义后，输入框在获取焦点时，<code>labelText</code>不会高亮显示了，正如上图中的&quot;用户名&quot;本应该显示蓝色，但现在却显示为灰色，并且我们还是无法定义下划线宽度。另一种灵活的方式是直接隐藏掉<code>TextField</code>本身的下划线，然后通过<code>Container</code>去嵌套定义样式，如:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">TextField</span><span class=\"token punctuation\">(</span>\n    keyboardType<span class=\"token punctuation\">:</span> TextInputType<span class=\"token punctuation\">.</span>emailAddress<span class=\"token punctuation\">,</span>\n    decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">InputDecoration</span><span class=\"token punctuation\">(</span>\n        labelText<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;Email&quot;</span><span class=\"token punctuation\">,</span>\n        hintText<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;电子邮件地址&quot;</span><span class=\"token punctuation\">,</span>\n        prefixIcon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>email<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        border<span class=\"token punctuation\">:</span> InputBorder<span class=\"token punctuation\">.</span>none <span class=\"token comment\">//隐藏下划线</span>\n    <span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>\n      <span class=\"token comment\">// 下滑线浅灰色，宽度1像素</span>\n      border<span class=\"token punctuation\">:</span> <span class=\"token function\">Border</span><span class=\"token punctuation\">(</span>bottom<span class=\"token punctuation\">:</span> <span class=\"token function\">BorderSide</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">[</span><span class=\"token number\">200</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> width<span class=\"token punctuation\">:</span> <span class=\"token number\">1.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行效果：</p> <p><img src=\"https://cdn.jsdelivr.net/gh/flutterchina/flutter-in-action@1.0/docs/imgs/image-20180904150511545.png\" alt=\"image-20180904150511545\"></p> <p>通过这种组件组合的方式，也可以定义背景圆角等。一般来说，优先通过<code>decoration</code>来自定义样式，如果<code>decoration</code>实现不了，再用widget组合的方式。</p> <blockquote><p>思考题：在这个示例中，下划线颜色是固定的，所以获得焦点后颜色仍然为灰色，如何实现点击后下滑线也变色呢？</p></blockquote> <h2 id=\"_3-7-2-表单form\"><a href=\"#_3-7-2-表单form\" class=\"header-anchor\">#</a> 3.7.2 表单Form</h2> <p>实际业务中，在正式向服务器提交数据前，都会对各个输入框数据进行合法性校验，但是对每一个<code>TextField</code>都分别进行校验将会是一件很麻烦的事。还有，如果用户想清除一组<code>TextField</code>的内容，除了一个一个清除有没有什么更好的办法呢？为此，Flutter提供了一个<code>Form</code> 组件，它可以对输入框进行分组，然后进行一些统一操作，如输入内容校验、输入框重置以及输入内容保存。</p> <h4 id=\"form\"><a href=\"#form\" class=\"header-anchor\">#</a> Form</h4> <p><code>Form</code>继承自<code>StatefulWidget</code>对象，它对应的状态类为<code>FormState</code>。我们先看看<code>Form</code>类的定义：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Form</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@required</span> Widget child<span class=\"token punctuation\">,</span>\n  bool autovalidate <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n  WillPopCallback onWillPop<span class=\"token punctuation\">,</span>\n  VoidCallback onChanged<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><ul><li><code>autovalidate</code>：是否自动校验输入内容；当为<code>true</code>时，每一个子FormField内容发生变化时都会自动校验合法性，并直接显示错误信息。否则，需要通过调用<code>FormState.validate()</code>来手动校验。</li> <li><code>onWillPop</code>：决定<code>Form</code>所在的路由是否可以直接返回（如点击返回按钮），该回调返回一个<code>Future</code>对象，如果Future的最终结果是<code>false</code>，则当前路由不会返回；如果为<code>true</code>，则会返回到上一个路由。此属性通常用于拦截返回按钮。</li> <li><code>onChanged</code>：<code>Form</code>的任意一个子<code>FormField</code>内容发生变化时会触发此回调。</li></ul> <h4 id=\"formfield\"><a href=\"#formfield\" class=\"header-anchor\">#</a> FormField</h4> <p><code>Form</code>的子孙元素必须是<code>FormField</code>类型，<code>FormField</code>是一个抽象类，定义几个属性，<code>FormState</code>内部通过它们来完成操作，<code>FormField</code>部分定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">const</span> <span class=\"token function\">FormField</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  FormFieldSetter<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span> onSaved<span class=\"token punctuation\">,</span> <span class=\"token comment\">//保存回调</span>\n  FormFieldValidator<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span>  validator<span class=\"token punctuation\">,</span> <span class=\"token comment\">//验证回调</span>\n  T initialValue<span class=\"token punctuation\">,</span> <span class=\"token comment\">//初始值</span>\n  bool autovalidate <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//是否自动校验。</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>为了方便使用，Flutter提供了一个<code>TextFormField</code>组件，它继承自<code>FormField</code>类，也是<code>TextField</code>的一个包装类，所以除了<code>FormField</code>定义的属性之外，它还包括<code>TextField</code>的属性。</p> <h4 id=\"formstate\"><a href=\"#formstate\" class=\"header-anchor\">#</a> FormState</h4> <p><code>FormState</code>为<code>Form</code>的<code>State</code>类，可以通过<code>Form.of()</code>或<code>GlobalKey</code>获得。我们可以通过它来对<code>Form</code>的子孙<code>FormField</code>进行统一操作。我们看看其常用的三个方法：</p> <ul><li><code>FormState.validate()</code>：调用此方法后，会调用<code>Form</code>子孙<code>FormField的validate</code>回调，如果有一个校验失败，则返回false，所有校验失败项都会返回用户返回的错误提示。</li> <li><code>FormState.save()</code>：调用此方法后，会调用<code>Form</code>子孙<code>FormField</code>的<code>save</code>回调，用于保存表单内容</li> <li><code>FormState.reset()</code>：调用此方法后，会将子孙<code>FormField</code>的内容清空。</li></ul> <h4 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h4> <p>我们修改一下上面用户登录的示例，在提交之前校验：</p> <ol><li>用户名不能为空，如果为空则提示“用户名不能为空”。</li> <li>密码不能小于6位，如果小于6为则提示“密码不能少于6位”。</li></ol> <p>完整代码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">FormTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _FormTestRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_FormTestRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_FormTestRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>FormTestRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  TextEditingController _unameController <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">TextEditingController</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  TextEditingController _pwdController <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">TextEditingController</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  GlobalKey _formKey<span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">GlobalKey</span><span class=\"token operator\">&lt;</span>FormState<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n      appBar<span class=\"token punctuation\">:</span> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>\n        title<span class=\"token punctuation\">:</span><span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Form Test&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      body<span class=\"token punctuation\">:</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n        padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">symmetric</span><span class=\"token punctuation\">(</span>vertical<span class=\"token punctuation\">:</span> <span class=\"token number\">16.0</span><span class=\"token punctuation\">,</span> horizontal<span class=\"token punctuation\">:</span> <span class=\"token number\">24.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Form</span><span class=\"token punctuation\">(</span>\n          key<span class=\"token punctuation\">:</span> _formKey<span class=\"token punctuation\">,</span> <span class=\"token comment\">//设置globalKey，用于后面获取FormState</span>\n          autovalidate<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//开启自动校验</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n            children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n              <span class=\"token function\">TextFormField</span><span class=\"token punctuation\">(</span>\n                  autofocus<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n                  controller<span class=\"token punctuation\">:</span> _unameController<span class=\"token punctuation\">,</span>\n                  decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">InputDecoration</span><span class=\"token punctuation\">(</span>\n                      labelText<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;用户名&quot;</span><span class=\"token punctuation\">,</span>\n                      hintText<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;用户名或邮箱&quot;</span><span class=\"token punctuation\">,</span>\n                      icon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>person<span class=\"token punctuation\">)</span>\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token comment\">// 校验用户名</span>\n                  validator<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>v<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                    <span class=\"token keyword\">return</span> v\n                        <span class=\"token punctuation\">.</span><span class=\"token function\">trim</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n                        <span class=\"token punctuation\">.</span>length <span class=\"token operator\">&gt;</span> <span class=\"token number\">0</span> <span class=\"token operator\">?</span> <span class=\"token keyword\">null</span> <span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;用户名不能为空&quot;</span><span class=\"token punctuation\">;</span>\n                  <span class=\"token punctuation\">}</span>\n\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token function\">TextFormField</span><span class=\"token punctuation\">(</span>\n                  controller<span class=\"token punctuation\">:</span> _pwdController<span class=\"token punctuation\">,</span>\n                  decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">InputDecoration</span><span class=\"token punctuation\">(</span>\n                      labelText<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;密码&quot;</span><span class=\"token punctuation\">,</span>\n                      hintText<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;您的登录密码&quot;</span><span class=\"token punctuation\">,</span>\n                      icon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>lock<span class=\"token punctuation\">)</span>\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  obscureText<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token comment\">//校验密码</span>\n                  validator<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>v<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                    <span class=\"token keyword\">return</span> v\n                        <span class=\"token punctuation\">.</span><span class=\"token function\">trim</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n                        <span class=\"token punctuation\">.</span>length <span class=\"token operator\">&gt;</span> <span class=\"token number\">5</span> <span class=\"token operator\">?</span> <span class=\"token keyword\">null</span> <span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;密码不能少于6位&quot;</span><span class=\"token punctuation\">;</span>\n                  <span class=\"token punctuation\">}</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token comment\">// 登录按钮</span>\n              <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n                padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">only</span><span class=\"token punctuation\">(</span>top<span class=\"token punctuation\">:</span> <span class=\"token number\">28.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                child<span class=\"token punctuation\">:</span> <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n                  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                    <span class=\"token function\">Expanded</span><span class=\"token punctuation\">(</span>\n                      child<span class=\"token punctuation\">:</span> <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n                        padding<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">15.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;登录&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                        color<span class=\"token punctuation\">:</span> Theme\n                            <span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span>\n                            <span class=\"token punctuation\">.</span>primaryColor<span class=\"token punctuation\">,</span>\n                        textColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">,</span>\n                        onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                          <span class=\"token comment\">//在这里不能通过此方式获取FormState，context不对</span>\n                          <span class=\"token comment\">//print(Form.of(context));</span>\n                            \n                          <span class=\"token comment\">// 通过_formKey.currentState 获取FormState后，</span>\n                          <span class=\"token comment\">// 调用validate()方法校验用户名密码是否合法，校验</span>\n                          <span class=\"token comment\">// 通过后再提交数据。 </span>\n                          <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>_formKey<span class=\"token punctuation\">.</span>currentState <span class=\"token operator\">as</span> FormState<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">validate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n                            <span class=\"token comment\">//验证通过提交数据</span>\n                          <span class=\"token punctuation\">}</span>\n                        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n                      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行后效果如图3-29所示：</p> <p><img src=\"/assets/img/3-29.ff4de036.png\" alt=\"图3-29\"></p> <p>注意，登录按钮的<code>onPressed</code>方法中不能通过<code>Form.of(context)</code>来获取，原因是，此处的<code>context</code>为<code>FormTestRoute</code>的context，而<code>Form.of(context)</code>是根据所指定<code>context</code>向根去查找，而<code>FormState</code>是在<code>FormTestRoute</code>的子树中，所以不行。正确的做法是通过<code>Builder</code>来构建登录按钮，<code>Builder</code>会将<code>widget</code>节点的<code>context</code>作为回调参数：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Expanded</span><span class=\"token punctuation\">(</span>\n <span class=\"token comment\">// 通过Builder来获取RaisedButton所在widget树的真正context(Element) </span>\n  child<span class=\"token punctuation\">:</span><span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n      onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">//由于本widget也是Form的子代widget，所以可以通过下面方式获取FormState  </span>\n        <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>Form<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">validate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n          <span class=\"token comment\">//验证通过提交数据</span>\n        <span class=\"token punctuation\">}</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>其实<code>context</code>正是操作Widget所对应的<code>Element</code>的一个接口，由于Widget树对应的<code>Element</code>都是不同的，所以<code>context</code>也都是不同的，有关<code>context</code>的更多内容会在后面高级部分详细讨论。Flutter中有很多“of(context)”这种方法，读者在使用时一定要注意<code>context</code>是否正确。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter3/radio_and_checkbox.html\" class=\"prev\">\n        3.6 单选开关和复选框\n      </a></span> <span class=\"next\"><a href=\"/chapter3/progress.html\">\n        3.8 进度指示器\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/14.4d0ac363.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter3/progress.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>3.8 进度指示器 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/27.d1fa65c2.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable open\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" aria-current=\"page\" class=\"active sidebar-link\">3.8 进度指示器</a><ul class=\"sidebar-sub-headers\"></ul></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_3-8-进度指示器\"><a href=\"#_3-8-进度指示器\" class=\"header-anchor\">#</a> 3.8 进度指示器</h1> <p>Material 组件库中提供了两种进度指示器：<code>LinearProgressIndicator</code>和<code>CircularProgressIndicator</code>，它们都可以同时用于精确的进度指示和模糊的进度指示。精确进度通常用于任务进度可以计算和预估的情况，比如文件下载；而模糊进度则用户任务进度无法准确获得的情况，如下拉刷新，数据提交等。</p> <h3 id=\"linearprogressindicator\"><a href=\"#linearprogressindicator\" class=\"header-anchor\">#</a> LinearProgressIndicator</h3> <p><code>LinearProgressIndicator</code>是一个线性、条状的进度条，定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">LinearProgressIndicator</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  double value<span class=\"token punctuation\">,</span>\n  Color backgroundColor<span class=\"token punctuation\">,</span>\n  Animation<span class=\"token operator\">&lt;</span>Color<span class=\"token operator\">&gt;</span> valueColor<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><ul><li><code>value</code>：<code>value</code>表示当前的进度，取值范围为[0,1]；如果<code>value</code>为<code>null</code>时则指示器会执行一个循环动画（模糊进度）；当<code>value</code>不为<code>null</code>时，指示器为一个具体进度的进度条。</li> <li><code>backgroundColor</code>：指示器的背景色。</li> <li><code>valueColor</code>: 指示器的进度条颜色；值得注意的是，该值类型是<code>Animation&lt;Color&gt;</code>，这允许我们对进度条的颜色也可以指定动画。如果我们不需要对进度条颜色执行动画，换言之，我们想对进度条应用一种固定的颜色，此时我们可以通过<code>AlwaysStoppedAnimation</code>来指定。</li></ul> <h3 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h3> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">// 模糊进度条(会执行一个动画)</span>\n<span class=\"token function\">LinearProgressIndicator</span><span class=\"token punctuation\">(</span>\n  backgroundColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">[</span><span class=\"token number\">200</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  valueColor<span class=\"token punctuation\">:</span> <span class=\"token function\">AlwaysStoppedAnimation</span><span class=\"token punctuation\">(</span>Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token comment\">//进度条显示50%</span>\n<span class=\"token function\">LinearProgressIndicator</span><span class=\"token punctuation\">(</span>\n  backgroundColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">[</span><span class=\"token number\">200</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  valueColor<span class=\"token punctuation\">:</span> <span class=\"token function\">AlwaysStoppedAnimation</span><span class=\"token punctuation\">(</span>Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  value<span class=\"token punctuation\">:</span> <span class=\"token number\">.5</span><span class=\"token punctuation\">,</span> \n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行效果如图3-30所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图3-30\"></p> <p>第一个进度条在执行循环动画：蓝色条一直在移动，而第二个进度条是静止的，停在50%的位置。</p> <h3 id=\"circularprogressindicator\"><a href=\"#circularprogressindicator\" class=\"header-anchor\">#</a> CircularProgressIndicator</h3> <p><code>CircularProgressIndicator</code>是一个圆形进度条，定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code> <span class=\"token function\">CircularProgressIndicator</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  double value<span class=\"token punctuation\">,</span>\n  Color backgroundColor<span class=\"token punctuation\">,</span>\n  Animation<span class=\"token operator\">&lt;</span>Color<span class=\"token operator\">&gt;</span> valueColor<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>strokeWidth <span class=\"token operator\">=</span> <span class=\"token number\">4.0</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>   \n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> \n</code></pre></div><p>前三个参数和<code>LinearProgressIndicator</code>相同，不再赘述。<code>strokeWidth</code> 表示圆形进度条的粗细。示例如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">// 模糊进度条(会执行一个旋转动画)</span>\n<span class=\"token function\">CircularProgressIndicator</span><span class=\"token punctuation\">(</span>\n  backgroundColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">[</span><span class=\"token number\">200</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  valueColor<span class=\"token punctuation\">:</span> <span class=\"token function\">AlwaysStoppedAnimation</span><span class=\"token punctuation\">(</span>Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token comment\">//进度条显示50%，会显示一个半圆</span>\n<span class=\"token function\">CircularProgressIndicator</span><span class=\"token punctuation\">(</span>\n  backgroundColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">[</span><span class=\"token number\">200</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  valueColor<span class=\"token punctuation\">:</span> <span class=\"token function\">AlwaysStoppedAnimation</span><span class=\"token punctuation\">(</span>Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  value<span class=\"token punctuation\">:</span> <span class=\"token number\">.5</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</code></pre></div><p>运行效果如图3-31所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图3-31\"></p> <p>第一个进度条会执行旋转动画，而第二个进度条是静止的，它停在50%的位置。</p> <h3 id=\"自定义尺寸\"><a href=\"#自定义尺寸\" class=\"header-anchor\">#</a> 自定义尺寸</h3> <p>我们可以发现<code>LinearProgressIndicator</code>和<code>CircularProgressIndicator</code>，并没有提供设置圆形进度条尺寸的参数；如果我们希望<code>LinearProgressIndicator</code>的线细一些，或者希望<code>CircularProgressIndicator</code>的圆大一些该怎么做？</p> <p>其实<code>LinearProgressIndicator</code>和<code>CircularProgressIndicator</code>都是取父容器的尺寸作为绘制的边界的。知道了这点，我们便可以通过尺寸限制类Widget，如<code>ConstrainedBox</code>、<code>SizedBox</code> （我们将在后面容器类组件一章中介绍）来指定尺寸，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">// 线性进度条高度指定为3</span>\n<span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span>\n  height<span class=\"token punctuation\">:</span> <span class=\"token number\">3</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">LinearProgressIndicator</span><span class=\"token punctuation\">(</span>\n    backgroundColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">[</span><span class=\"token number\">200</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    valueColor<span class=\"token punctuation\">:</span> <span class=\"token function\">AlwaysStoppedAnimation</span><span class=\"token punctuation\">(</span>Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    value<span class=\"token punctuation\">:</span> <span class=\"token number\">.5</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token comment\">// 圆形进度条直径指定为100</span>\n<span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span>\n  height<span class=\"token punctuation\">:</span> <span class=\"token number\">100</span><span class=\"token punctuation\">,</span>\n  width<span class=\"token punctuation\">:</span> <span class=\"token number\">100</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">CircularProgressIndicator</span><span class=\"token punctuation\">(</span>\n    backgroundColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">[</span><span class=\"token number\">200</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    valueColor<span class=\"token punctuation\">:</span> <span class=\"token function\">AlwaysStoppedAnimation</span><span class=\"token punctuation\">(</span>Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    value<span class=\"token punctuation\">:</span> <span class=\"token number\">.7</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</code></pre></div><p>运行效果如图3-32所示：</p> <p><img src=\"/assets/img/3-32.11e62a02.png\" alt=\"图3-32\"></p> <p>注意，如果<code>CircularProgressIndicator</code>显示空间的宽高不同，则会显示为椭圆。如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">// 宽高不等</span>\n<span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span>\n  height<span class=\"token punctuation\">:</span> <span class=\"token number\">100</span><span class=\"token punctuation\">,</span>\n  width<span class=\"token punctuation\">:</span> <span class=\"token number\">130</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">CircularProgressIndicator</span><span class=\"token punctuation\">(</span>\n    backgroundColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">[</span><span class=\"token number\">200</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    valueColor<span class=\"token punctuation\">:</span> <span class=\"token function\">AlwaysStoppedAnimation</span><span class=\"token punctuation\">(</span>Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    value<span class=\"token punctuation\">:</span> <span class=\"token number\">.7</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</code></pre></div><p>运行效果如图3-33所示：</p> <p><img src=\"/assets/img/progress_oval.4912d1a4.png\" alt=\"progress_oval\"></p> <h3 id=\"进度色动画\"><a href=\"#进度色动画\" class=\"header-anchor\">#</a> 进度色动画</h3> <p>前面说过可以通过<code>valueColor</code>对进度条颜色做动画，关于动画我们将在后面专门的章节详细介绍，这里先给出一个例子，读者在了解了Flutter动画一章后再回过头来看。</p> <p>我们实现一个进度条在3秒内从灰色变成蓝色的动画：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">ProgressRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _ProgressRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">_ProgressRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_ProgressRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>ProgressRoute<span class=\"token operator\">&gt;</span>\n    <span class=\"token keyword\">with</span> SingleTickerProviderStateMixin <span class=\"token punctuation\">{</span>\n  AnimationController _animationController<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//动画执行时间3秒  </span>\n    _animationController <span class=\"token operator\">=</span>\n        <span class=\"token keyword\">new</span> <span class=\"token class-name\">AnimationController</span><span class=\"token punctuation\">(</span>vsync<span class=\"token punctuation\">:</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">,</span> duration<span class=\"token punctuation\">:</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">3</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    _animationController<span class=\"token punctuation\">.</span><span class=\"token function\">forward</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    _animationController<span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    _animationController<span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">SingleChildScrollView</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n        children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n            padding<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">16</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">LinearProgressIndicator</span><span class=\"token punctuation\">(</span>\n              backgroundColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">[</span><span class=\"token number\">200</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n              valueColor<span class=\"token punctuation\">:</span> <span class=\"token function\">ColorTween</span><span class=\"token punctuation\">(</span>begin<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">)</span>\n                <span class=\"token punctuation\">.</span><span class=\"token function\">animate</span><span class=\"token punctuation\">(</span>_animationController<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 从灰色变成蓝色</span>\n              value<span class=\"token punctuation\">:</span> _animationController<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h3 id=\"自定义进度指示器样式\"><a href=\"#自定义进度指示器样式\" class=\"header-anchor\">#</a> 自定义进度指示器样式</h3> <p>定制进度指示器风格样式，可以通过<code>CustomPainter</code> Widget 来自定义绘制逻辑，实际上<code>LinearProgressIndicator</code>和<code>CircularProgressIndicator</code>也正是通过<code>CustomPainter</code>来实现外观绘制的。关于<code>CustomPainter</code>，我们将在后面“自定义Widget”一章中详细介绍。</p> <blockquote><p><a href=\"https://pub.flutter-io.cn/packages/flutter_spinkit\" target=\"_blank\" rel=\"noopener noreferrer\">flutter_spinkit<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 包提供了多种风格的模糊进度指示器，读者若是感兴趣，可以参考。</p></blockquote></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter3/input_and_form.html\" class=\"prev\">\n        3.7 输入框及表单\n      </a></span> <span class=\"next\"><a href=\"/chapter4/intro.html\">\n        4.1 布局类组件简介\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/27.d1fa65c2.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter3/radio_and_checkbox.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>3.6 单选开关和复选框 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/96.b382caf8.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable open\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" aria-current=\"page\" class=\"active sidebar-link\">3.6 单选开关和复选框</a><ul class=\"sidebar-sub-headers\"></ul></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_3-6-单选开关和复选框\"><a href=\"#_3-6-单选开关和复选框\" class=\"header-anchor\">#</a> 3.6 单选开关和复选框</h1> <p>Material 组件库中提供了Material风格的单选开关<code>Switch</code>和复选框<code>Checkbox</code>，虽然它们都是继承自<code>StatefulWidget</code>，但它们本身不会保存当前选中状态，选中状态都是由父组件来管理的。当<code>Switch</code>或<code>Checkbox</code>被点击时，会触发它们的<code>onChanged</code>回调，我们可以在此回调中处理选中状态改变逻辑。下面看一个简单的例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">SwitchAndCheckBoxTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _SwitchAndCheckBoxTestRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_SwitchAndCheckBoxTestRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_SwitchAndCheckBoxTestRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>SwitchAndCheckBoxTestRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  bool _switchSelected<span class=\"token operator\">=</span><span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//维护单选开关状态</span>\n  bool _checkboxSelected<span class=\"token operator\">=</span><span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span><span class=\"token comment\">//维护复选框状态</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n      children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n        <span class=\"token function\">Switch</span><span class=\"token punctuation\">(</span>\n          value<span class=\"token punctuation\">:</span> _switchSelected<span class=\"token punctuation\">,</span><span class=\"token comment\">//当前状态</span>\n          onChanged<span class=\"token punctuation\">:</span><span class=\"token punctuation\">(</span>value<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">//重新构建页面  </span>\n            <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              _switchSelected<span class=\"token operator\">=</span>value<span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token function\">Checkbox</span><span class=\"token punctuation\">(</span>\n          value<span class=\"token punctuation\">:</span> _checkboxSelected<span class=\"token punctuation\">,</span>\n          activeColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span> <span class=\"token comment\">//选中时的颜色</span>\n          onChanged<span class=\"token punctuation\">:</span><span class=\"token punctuation\">(</span>value<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n            <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              _checkboxSelected<span class=\"token operator\">=</span>value<span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span> <span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面代码中，由于需要维护<code>Switch</code>和<code>Checkbox</code>的选中状态，所以<code>SwitchAndCheckBoxTestRoute</code>继承自<code>StatefulWidget</code> 。在其<code>build</code>方法中分别构建了一个<code>Switch</code>和<code>Checkbox</code>，初始状态都为选中状态，当用户点击时，会将状态置反，然后回调用<code>setState()</code>通知Flutter framework重新构建UI。</p> <p><img src=\"/assets/img/3-23.a249df4c.png\" alt=\"图3-23\"></p> <h3 id=\"属性及外观\"><a href=\"#属性及外观\" class=\"header-anchor\">#</a> 属性及外观</h3> <p><code>Switch</code>和<code>Checkbox</code>属性比较简单，读者可以查看API文档，它们都有一个<code>activeColor</code>属性，用于设置激活态的颜色。至于大小，到目前为止，<code>Checkbox</code>的大小是固定的，无法自定义，而<code>Switch</code>只能定义宽度，高度也是固定的。值得一提的是<code>Checkbox</code>有一个属性<code>tristate</code> ，表示是否为三态，其默认值为<code>false</code> ，这时Checkbox有两种状态即“选中”和“不选中”，对应的value值为<code>true</code>和<code>false</code> 。如果<code>tristate</code>值为<code>true</code>时，value的值会增加一个状态<code>null</code>，读者可以自行了解。</p> <h3 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h3> <p>通过<code>Switch</code>和<code>Checkbox</code>我们可以看到，虽然它们本身是与状态（是否选中）关联的，但它们却不是自己来维护状态，而是需要父组件来管理状态，然后当用户点击时，再通过事件通知给父组件，这样是合理的，因为<code>Switch</code>和<code>Checkbox</code>是否选中本就和用户数据关联，而这些用户数据也不可能是它们的私有状态。我们在自定义组件时也应该思考一下哪种状态的管理方式最为合理。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter3/img_and_icon.html\" class=\"prev\">\n        3.5 图片及ICON\n      </a></span> <span class=\"next\"><a href=\"/chapter3/input_and_form.html\">\n        3.7 输入框及表单\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/96.b382caf8.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter3/state_manage.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>3.2 状态管理 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/97.14909b27.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable open\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" aria-current=\"page\" class=\"active sidebar-link\">3.2 状态管理</a><ul class=\"sidebar-sub-headers\"></ul></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_3-2-状态管理\"><a href=\"#_3-2-状态管理\" class=\"header-anchor\">#</a> 3.2 状态管理</h1> <p>响应式的编程框架中都会有一个永恒的主题——“状态(State)管理”，无论是在React/Vue（两者都是支持响应式编程的Web开发框架）还是Flutter中，他们讨论的问题和解决的思想都是一致的。所以，如果你对React/Vue的状态管理有了解，可以跳过本节。言归正传，我们想一个问题，<code>StatefulWidget</code>的状态应该被谁管理？Widget本身？父Widget？都会？还是另一个对象？答案是取决于实际情况！以下是管理状态的最常见的方法：</p> <ul><li>Widget管理自己的状态。</li> <li>Widget管理子Widget状态。</li> <li>混合管理（父Widget和子Widget都管理状态）。</li></ul> <p>如何决定使用哪种管理方法？下面是官方给出的一些原则可以帮助你做决定：</p> <ul><li>如果状态是用户数据，如复选框的选中状态、滑块的位置，则该状态最好由父Widget管理。</li> <li>如果状态是有关界面外观效果的，例如颜色、动画，那么状态最好由Widget本身来管理。</li> <li>如果某一个状态是不同Widget共享的则最好由它们共同的父Widget管理。</li></ul> <p>在Widget内部管理状态封装性会好一些，而在父Widget中管理会比较灵活。有些时候，如果不确定到底该怎么管理状态，那么推荐的首选是在父widget中管理（灵活会显得更重要一些）。</p> <p>接下来，我们将通过创建三个简单示例TapboxA、TapboxB和TapboxC来说明管理状态的不同方式。 这些例子功能是相似的 ——创建一个盒子，当点击它时，盒子背景会在绿色与灰色之间切换。状态 <code>_active</code>确定颜色：绿色为<code>true</code> ，灰色为<code>false</code>，如图3-4所示。<img src=\"/assets/img/3-4.8e70e140.png\" alt=\"a large grey box with the text, 'Inactive'\"></p> <p>下面的例子将使用<code>GestureDetector</code>来识别点击事件，关于该<code>GestureDetector</code>的详细内容我们将在后面“事件处理”一章中介绍。</p> <h3 id=\"_3-2-1-widget管理自身状态\"><a href=\"#_3-2-1-widget管理自身状态\" class=\"header-anchor\">#</a> 3.2.1 Widget管理自身状态</h3> <p>_TapboxAState 类:</p> <ul><li>管理TapboxA的状态。</li> <li>定义<code>_active</code>：确定盒子的当前颜色的布尔值。</li> <li>定义<code>_handleTap()</code>函数，该函数在点击该盒子时更新<code>_active</code>，并调用<code>setState()</code>更新UI。</li> <li>实现widget的所有交互式行为。</li></ul> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">// TapboxA 管理自身状态.</span>\n\n<span class=\"token comment\">//------------------------- TapboxA ----------------------------------</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">TapboxA</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">TapboxA</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>Key key<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  _TapboxAState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_TapboxAState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_TapboxAState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>TapboxA<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  bool _active <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_handleTap</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _active <span class=\"token operator\">=</span> <span class=\"token operator\">!</span>_active<span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">GestureDetector</span><span class=\"token punctuation\">(</span>\n      onTap<span class=\"token punctuation\">:</span> _handleTap<span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Container</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Center</span><span class=\"token punctuation\">(</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span>\n            _active <span class=\"token operator\">?</span> <span class=\"token string\">'Active'</span> <span class=\"token punctuation\">:</span> <span class=\"token string\">'Inactive'</span><span class=\"token punctuation\">,</span>\n            style<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">TextStyle</span><span class=\"token punctuation\">(</span>fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">32.0</span><span class=\"token punctuation\">,</span> color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        width<span class=\"token punctuation\">:</span> <span class=\"token number\">200.0</span><span class=\"token punctuation\">,</span>\n        height<span class=\"token punctuation\">:</span> <span class=\"token number\">200.0</span><span class=\"token punctuation\">,</span>\n        decoration<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">BoxDecoration</span><span class=\"token punctuation\">(</span>\n          color<span class=\"token punctuation\">:</span> _active <span class=\"token operator\">?</span> Colors<span class=\"token punctuation\">.</span>lightGreen<span class=\"token punctuation\">[</span><span class=\"token number\">700</span><span class=\"token punctuation\">]</span> <span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">[</span><span class=\"token number\">600</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n</code></pre></div><h3 id=\"_3-2-2-父widget管理子widget的状态\"><a href=\"#_3-2-2-父widget管理子widget的状态\" class=\"header-anchor\">#</a> 3.2.2 父Widget管理子Widget的状态</h3> <p>对于父Widget来说，管理状态并告诉其子Widget何时更新通常是比较好的方式。 例如，<code>IconButton</code>是一个图标按钮，但它是一个无状态的Widget，因为我们认为父Widget需要知道该按钮是否被点击来采取相应的处理。</p> <p>在以下示例中，TapboxB通过回调将其状态导出到其父组件，状态由父组件管理，因此它的父组件为<code>StatefulWidget</code>。但是由于TapboxB不管理任何状态，所以<code>TapboxB</code>为<code>StatelessWidget</code>。</p> <p><code>ParentWidgetState</code> 类:</p> <ul><li>为TapboxB 管理<code>_active</code>状态。</li> <li>实现<code>_handleTapboxChanged()</code>，当盒子被点击时调用的方法。</li> <li>当状态改变时，调用<code>setState()</code>更新UI。</li></ul> <p>TapboxB 类:</p> <ul><li>继承<code>StatelessWidget</code>类，因为所有状态都由其父组件处理。</li> <li>当检测到点击时，它会通知父组件。</li></ul> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">// ParentWidget 为 TapboxB 管理状态.</span>\n\n<span class=\"token comment\">//------------------------ ParentWidget --------------------------------</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">ParentWidget</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _ParentWidgetState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_ParentWidgetState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_ParentWidgetState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>ParentWidget<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  bool _active <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_handleTapboxChanged</span><span class=\"token punctuation\">(</span>bool newValue<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _active <span class=\"token operator\">=</span> newValue<span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Container</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">TapboxB</span><span class=\"token punctuation\">(</span>\n        active<span class=\"token punctuation\">:</span> _active<span class=\"token punctuation\">,</span>\n        onChanged<span class=\"token punctuation\">:</span> _handleTapboxChanged<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">//------------------------- TapboxB ----------------------------------</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">TapboxB</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">TapboxB</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>Key key<span class=\"token punctuation\">,</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>active<span class=\"token punctuation\">:</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span> <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>onChanged<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> bool active<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> ValueChanged<span class=\"token operator\">&lt;</span>bool<span class=\"token operator\">&gt;</span> onChanged<span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_handleTap</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">onChanged</span><span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>active<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">GestureDetector</span><span class=\"token punctuation\">(</span>\n      onTap<span class=\"token punctuation\">:</span> _handleTap<span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Container</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Center</span><span class=\"token punctuation\">(</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span>\n            active <span class=\"token operator\">?</span> <span class=\"token string\">'Active'</span> <span class=\"token punctuation\">:</span> <span class=\"token string\">'Inactive'</span><span class=\"token punctuation\">,</span>\n            style<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">TextStyle</span><span class=\"token punctuation\">(</span>fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">32.0</span><span class=\"token punctuation\">,</span> color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        width<span class=\"token punctuation\">:</span> <span class=\"token number\">200.0</span><span class=\"token punctuation\">,</span>\n        height<span class=\"token punctuation\">:</span> <span class=\"token number\">200.0</span><span class=\"token punctuation\">,</span>\n        decoration<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">BoxDecoration</span><span class=\"token punctuation\">(</span>\n          color<span class=\"token punctuation\">:</span> active <span class=\"token operator\">?</span> Colors<span class=\"token punctuation\">.</span>lightGreen<span class=\"token punctuation\">[</span><span class=\"token number\">700</span><span class=\"token punctuation\">]</span> <span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">[</span><span class=\"token number\">600</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h3 id=\"_3-2-3-混合状态管理\"><a href=\"#_3-2-3-混合状态管理\" class=\"header-anchor\">#</a> 3.2.3 混合状态管理</h3> <p>对于一些组件来说，混合管理的方式会非常有用。在这种情况下，组件自身管理一些内部状态，而父组件管理一些其他外部状态。</p> <p>在下面TapboxC示例中，手指按下时，盒子的周围会出现一个深绿色的边框，抬起时，边框消失。点击完成后，盒子的颜色改变。 TapboxC将其<code>_active</code>状态导出到其父组件中，但在内部管理其<code>_highlight</code>状态。这个例子有两个状态对象<code>_ParentWidgetState</code>和<code>_TapboxCState</code>。</p> <p><code>_ParentWidgetStateC</code>类:</p> <ul><li>管理<code>_active</code> 状态。</li> <li>实现 <code>_handleTapboxChanged()</code> ，当盒子被点击时调用。</li> <li>当点击盒子并且<code>_active</code>状态改变时调用<code>setState()</code>更新UI。</li></ul> <p><code>_TapboxCState</code> 对象:</p> <ul><li>管理<code>_highlight</code> 状态。</li> <li><code>GestureDetector</code>监听所有tap事件。当用户点下时，它添加高亮（深绿色边框）；当用户释放时，会移除高亮。</li> <li>当按下、抬起、或者取消点击时更新<code>_highlight</code>状态，调用<code>setState()</code>更新UI。</li> <li>当点击时，将状态的改变传递给父组件。</li></ul> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//---------------------------- ParentWidget ----------------------------</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">ParentWidgetC</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _ParentWidgetCState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_ParentWidgetCState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_ParentWidgetCState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>ParentWidgetC<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  bool _active <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_handleTapboxChanged</span><span class=\"token punctuation\">(</span>bool newValue<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _active <span class=\"token operator\">=</span> newValue<span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Container</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">TapboxC</span><span class=\"token punctuation\">(</span>\n        active<span class=\"token punctuation\">:</span> _active<span class=\"token punctuation\">,</span>\n        onChanged<span class=\"token punctuation\">:</span> _handleTapboxChanged<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">//----------------------------- TapboxC ------------------------------</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">TapboxC</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">TapboxC</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>Key key<span class=\"token punctuation\">,</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>active<span class=\"token punctuation\">:</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span> <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>onChanged<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> bool active<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> ValueChanged<span class=\"token operator\">&lt;</span>bool<span class=\"token operator\">&gt;</span> onChanged<span class=\"token punctuation\">;</span>\n  \n  <span class=\"token metadata symbol\">@override</span>\n  _TapboxCState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_TapboxCState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_TapboxCState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>TapboxC<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  bool _highlight <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_handleTapDown</span><span class=\"token punctuation\">(</span>TapDownDetails details<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _highlight <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_handleTapUp</span><span class=\"token punctuation\">(</span>TapUpDetails details<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _highlight <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_handleTapCancel</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _highlight <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_handleTap</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    widget<span class=\"token punctuation\">.</span><span class=\"token function\">onChanged</span><span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>widget<span class=\"token punctuation\">.</span>active<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 在按下时添加绿色边框，当抬起时，取消高亮  </span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">GestureDetector</span><span class=\"token punctuation\">(</span>\n      onTapDown<span class=\"token punctuation\">:</span> _handleTapDown<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 处理按下事件</span>\n      onTapUp<span class=\"token punctuation\">:</span> _handleTapUp<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 处理抬起事件</span>\n      onTap<span class=\"token punctuation\">:</span> _handleTap<span class=\"token punctuation\">,</span>\n      onTapCancel<span class=\"token punctuation\">:</span> _handleTapCancel<span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Container</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Center</span><span class=\"token punctuation\">(</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span>widget<span class=\"token punctuation\">.</span>active <span class=\"token operator\">?</span> <span class=\"token string\">'Active'</span> <span class=\"token punctuation\">:</span> <span class=\"token string\">'Inactive'</span><span class=\"token punctuation\">,</span>\n              style<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">TextStyle</span><span class=\"token punctuation\">(</span>fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">32.0</span><span class=\"token punctuation\">,</span> color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        width<span class=\"token punctuation\">:</span> <span class=\"token number\">200.0</span><span class=\"token punctuation\">,</span>\n        height<span class=\"token punctuation\">:</span> <span class=\"token number\">200.0</span><span class=\"token punctuation\">,</span>\n        decoration<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">BoxDecoration</span><span class=\"token punctuation\">(</span>\n          color<span class=\"token punctuation\">:</span> widget<span class=\"token punctuation\">.</span>active <span class=\"token operator\">?</span> Colors<span class=\"token punctuation\">.</span>lightGreen<span class=\"token punctuation\">[</span><span class=\"token number\">700</span><span class=\"token punctuation\">]</span> <span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">[</span><span class=\"token number\">600</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n          border<span class=\"token punctuation\">:</span> _highlight\n              <span class=\"token operator\">?</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Border<span class=\"token punctuation\">.</span>all</span><span class=\"token punctuation\">(</span>\n                  color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>teal<span class=\"token punctuation\">[</span><span class=\"token number\">700</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                  width<span class=\"token punctuation\">:</span> <span class=\"token number\">10.0</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span>\n              <span class=\"token punctuation\">:</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>另一种实现可能会将高亮状态导出到父组件，但同时保持<code>_active</code>状态为内部状态，但如果你要将该TapBox给其它人使用，可能没有什么意义。 开发人员只会关心该框是否处于Active状态，而不在乎高亮显示是如何管理的，所以应该让TapBox内部处理这些细节。</p> <h3 id=\"_3-2-4-全局状态管理\"><a href=\"#_3-2-4-全局状态管理\" class=\"header-anchor\">#</a> 3.2.4 全局状态管理</h3> <p>当应用中需要一些跨组件（包括跨路由）的状态需要同步时，上面介绍的方法便很难胜任了。比如，我们有一个设置页，里面可以设置应用的语言，我们为了让设置实时生效，我们期望在语言状态发生改变时，APP中依赖应用语言的组件能够重新build一下，但这些依赖应用语言的组件和设置页并不在一起，所以这种情况用上面的方法很难管理。这时，正确的做法是通过一个全局状态管理器来处理这种相距较远的组件之间的通信。目前主要有两种办法：</p> <ol><li>实现一个全局的事件总线，将语言状态改变对应为一个事件，然后在APP中依赖应用语言的组件的<code>initState</code> 方法中订阅语言改变的事件。当用户在设置页切换语言后，我们发布语言改变事件，而订阅了此事件的组件就会收到通知，收到通知后调用<code>setState(...)</code>方法重新<code>build</code>一下自身即可。</li> <li>使用一些专门用于状态管理的包，如Provider、Redux，读者可以在pub上查看其详细信息。</li></ol> <p>本书将在&quot;功能型组件&quot;一章中介绍Provider包的实现原理及用法，同时也将会在&quot;事件处理与通知&quot;一章中实现一个全局事件总线，读者有需要可以直接翻看。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter3/flutter_widget_intro.html\" class=\"prev\">\n        3.1 Widget简介\n      </a></span> <span class=\"next\"><a href=\"/chapter3/text.html\">\n        3.3 文本及样式\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/97.14909b27.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter3/text.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>3.3 文本及样式 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/22.4fdaa56f.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable open\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" aria-current=\"page\" class=\"active sidebar-link\">3.3 文本及样式</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/chapter3/text.html#_3-3-文本及样式\" class=\"sidebar-link\">3.3 文本及样式</a></li></ul></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h2 id=\"_3-3-文本及样式\"><a href=\"#_3-3-文本及样式\" class=\"header-anchor\">#</a> 3.3 文本及样式</h2> <h3 id=\"_3-3-1-text\"><a href=\"#_3-3-1-text\" class=\"header-anchor\">#</a> 3.3.1 Text</h3> <p><code>Text</code>用于显示简单样式文本，它包含一些控制文本显示样式的一些属性，一个简单的例子如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hello world&quot;</span><span class=\"token punctuation\">,</span>\n  textAlign<span class=\"token punctuation\">:</span> TextAlign<span class=\"token punctuation\">.</span>left<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hello world! I'm Jack. &quot;</span><span class=\"token operator\">*</span><span class=\"token number\">4</span><span class=\"token punctuation\">,</span>\n  maxLines<span class=\"token punctuation\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n  overflow<span class=\"token punctuation\">:</span> TextOverflow<span class=\"token punctuation\">.</span>ellipsis<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hello world&quot;</span><span class=\"token punctuation\">,</span>\n  textScaleFactor<span class=\"token punctuation\">:</span> <span class=\"token number\">1.5</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>运行效果如图3-5所示：</p> <p><img src=\"/assets/img/3-5.e95eb747.png\" alt=\"image-20180829103242552\"></p> <ul><li><p><code>textAlign</code>：文本的对齐方式；可以选择左对齐、右对齐还是居中。注意，对齐的参考系是Text widget本身。本例中虽然是指定了居中对齐，但因为Text文本内容宽度不足一行，Text的宽度和文本内容长度相等，那么这时指定对齐方式是没有意义的，只有Text宽度大于文本内容长度时指定此属性才有意义。下面我们指定一个较长的字符串：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hello world &quot;</span><span class=\"token operator\">*</span><span class=\"token number\">6</span><span class=\"token punctuation\">,</span>  <span class=\"token comment\">//字符串重复六次</span>\n  textAlign<span class=\"token punctuation\">:</span> TextAlign<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>；\n</code></pre></div><p>运行效果如图3-6所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"image-20180829104807535\"></p></li></ul> <p>​      字符串内容超过一行，Text宽度等于屏幕宽度，第二行文本便会居中显示。</p> <ul><li><code>maxLines</code>、<code>overflow</code>：指定文本显示的最大行数，默认情况下，文本是自动折行的，如果指定此参数，则文本最多不会超过指定的行。如果有多余的文本，可以通过<code>overflow</code>来指定截断方式，默认是直接截断，本例中指定的截断方式<code>TextOverflow.ellipsis</code>，它会将多余文本截断后以省略符“...”表示；TextOverflow的其它截断方式请参考SDK文档。</li> <li><code>textScaleFactor</code>：代表文本相对于当前字体大小的缩放因子，相对于去设置文本的样式<code>style</code>属性的<code>fontSize</code>，它是调整字体大小的一个快捷方式。该属性的默认值可以通过<code>MediaQueryData.textScaleFactor</code>获得，如果没有<code>MediaQuery</code>，那么会默认值将为1.0。</li></ul> <h3 id=\"_3-3-2-textstyle\"><a href=\"#_3-3-2-textstyle\" class=\"header-anchor\">#</a> 3.3.2 TextStyle</h3> <p><code>TextStyle</code>用于指定文本显示的样式如颜色、字体、粗细、背景等。我们看一个示例：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hello world&quot;</span><span class=\"token punctuation\">,</span>\n  style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>\n    color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span>\n    fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">18.0</span><span class=\"token punctuation\">,</span>\n    height<span class=\"token punctuation\">:</span> <span class=\"token number\">1.2</span><span class=\"token punctuation\">,</span>  \n    fontFamily<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;Courier&quot;</span><span class=\"token punctuation\">,</span>\n    background<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Paint</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>color<span class=\"token operator\">=</span>Colors<span class=\"token punctuation\">.</span>yellow<span class=\"token punctuation\">,</span>\n    decoration<span class=\"token punctuation\">:</span>TextDecoration<span class=\"token punctuation\">.</span>underline<span class=\"token punctuation\">,</span>\n    decorationStyle<span class=\"token punctuation\">:</span> TextDecorationStyle<span class=\"token punctuation\">.</span>dashed\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>效果如图3-7所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"3-7\"></p> <p>此示例只展示了TextStyle的部分属性，它还有一些其它属性，属性名基本都是自解释的，在此不再赘述，读者可以查阅SDK文档。值得注意的是：</p> <ul><li><p><code>height</code>：该属性用于指定行高，但它并不是一个绝对值，而是一个因子，具体的行高等于<code>fontSize</code>*<code>height</code>。</p></li> <li><p><code>fontFamily</code> ：由于不同平台默认支持的字体集不同，所以在手动指定字体时一定要先在不同平台测试一下。</p></li> <li><p><code>fontSize</code>：该属性和Text的<code>textScaleFactor</code>都用于控制字体大小。但是有两个主要区别：</p> <ul><li><code>fontSize</code>可以精确指定字体大小，而<code>textScaleFactor</code>只能通过缩放比例来控制。</li> <li><code>textScaleFactor</code>主要是用于系统字体大小设置改变时对Flutter应用字体进行全局调整，而<code>fontSize</code>通常用于单个文本，字体大小不会跟随系统字体大小变化。</li></ul></li></ul> <h3 id=\"_3-3-3-textspan\"><a href=\"#_3-3-3-textspan\" class=\"header-anchor\">#</a> 3.3.3 TextSpan</h3> <p>在上面的例子中，Text的所有文本内容只能按同一种样式，如果我们需要对一个Text内容的不同部分按照不同的样式显示，这时就可以使用<code>TextSpan</code>，它代表文本的一个“片段”。我们看看TextSpan的定义:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">const</span> <span class=\"token function\">TextSpan</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  TextStyle style<span class=\"token punctuation\">,</span> \n  Sting text<span class=\"token punctuation\">,</span>\n  List<span class=\"token operator\">&lt;</span>TextSpan<span class=\"token operator\">&gt;</span> children<span class=\"token punctuation\">,</span>\n  GestureRecognizer recognizer<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>其中<code>style</code> 和 <code>text</code>属性代表该文本片段的样式和内容。  <code>children</code>是一个<code>TextSpan</code>的数组，也就是说<code>TextSpan</code>可以包括其他<code>TextSpan</code>。而<code>recognizer</code>用于对该文本片段上用于手势进行识别处理。下面我们看一个效果（图3-8），然后用<code>TextSpan</code>实现它。</p> <p><img src=\"/assets/img/3-8.cc415da2.png\" alt=\"3-8\"></p> <p>源码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Text<span class=\"token punctuation\">.</span><span class=\"token function\">rich</span><span class=\"token punctuation\">(</span><span class=\"token function\">TextSpan</span><span class=\"token punctuation\">(</span>\n    children<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>\n     <span class=\"token function\">TextSpan</span><span class=\"token punctuation\">(</span>\n       text<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;Home: &quot;</span>\n     <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n     <span class=\"token function\">TextSpan</span><span class=\"token punctuation\">(</span>\n       text<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;https://flutterchina.club&quot;</span><span class=\"token punctuation\">,</span>\n       style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>\n         color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue\n       <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>  \n       recognizer<span class=\"token punctuation\">:</span> _tapRecognizer\n     <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n</code></pre></div><ul><li>上面代码中，我们通过TextSpan实现了一个基础文本片段和一个链接片段，然后通过<code>Text.rich</code> 方法将<code>TextSpan</code> 添加到Text中，之所以可以这样做，是因为Text其实就是RichText的一个包装，而RichText是可以显示多种样式(富文本)的widget。</li> <li><code>_tapRecognizer</code>，它是点击链接后的一个处理器（代码已省略），关于手势识别的更多内容我们将在后面单独介绍。</li></ul> <h3 id=\"_3-3-4-defaulttextstyle\"><a href=\"#_3-3-4-defaulttextstyle\" class=\"header-anchor\">#</a> 3.3.4 DefaultTextStyle</h3> <p>在Widget树中，文本的样式默认是可以被继承的（子类文本类组件未指定具体样式时可以使用Widget树中父级设置的默认样式），因此，如果在Widget树的某一个节点处设置一个默认的文本样式，那么该节点的子树中所有文本都会默认使用这个样式，而<code>DefaultTextStyle</code>正是用于设置默认文本样式的。下面我们看一个例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">DefaultTextStyle</span><span class=\"token punctuation\">(</span>\n  <span class=\"token comment\">//1.设置文本默认样式  </span>\n  style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>\n    color<span class=\"token punctuation\">:</span>Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span>\n    fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">20.0</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  textAlign<span class=\"token punctuation\">:</span> TextAlign<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n    crossAxisAlignment<span class=\"token punctuation\">:</span> CrossAxisAlignment<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n    children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n      <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;hello world&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;I am Jack&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;I am Jack&quot;</span><span class=\"token punctuation\">,</span>\n        style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>\n          inherit<span class=\"token punctuation\">:</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//2.不继承默认样式</span>\n          color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>上面代码中，我们首先设置了一个默认的文本样式，即字体为20像素(逻辑像素)、颜色为红色。然后通过<code>DefaultTextStyle</code> 设置给了子树Column节点处，这样一来Column的所有子孙Text默认都会继承该样式，除非Text显示指定不继承样式，如代码中注释2。示例运行效果如图3-9：</p> <p><img src=\"/assets/img/3-9.bb214e70.png\" alt=\"3-9\"></p> <h3 id=\"_3-3-5-字体\"><a href=\"#_3-3-5-字体\" class=\"header-anchor\">#</a> 3.3.5 字体</h3> <p>可以在Flutter应用程序中使用不同的字体。例如，我们可能会使用设计人员创建的自定义字体，或者其它第三方的字体，如<a href=\"https://fonts.google.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Google Fonts<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>中的字体。本节将介绍如何为Flutter应用配置字体，并在渲染文本时使用它们。</p> <p>在Flutter中使用字体分两步完成。首先在<code>pubspec.yaml</code>中声明它们，以确保它们会打包到应用程序中。然后通过<a href=\"https://docs.flutter.io/flutter/painting/TextStyle-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>TextStyle</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>属性使用字体。</p> <h4 id=\"在asset中声明\"><a href=\"#在asset中声明\" class=\"header-anchor\">#</a> 在asset中声明</h4> <p>要将字体文件打包到应用中，和使用其它资源一样，要先在<code>pubspec.yaml</code>中声明它。然后将字体文件复制到在<code>pubspec.yaml</code>中指定的位置。如：</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code><span class=\"token key atrule\">flutter</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">fonts</span><span class=\"token punctuation\">:</span>\n    <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">family</span><span class=\"token punctuation\">:</span> Raleway\n      <span class=\"token key atrule\">fonts</span><span class=\"token punctuation\">:</span>\n        <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">asset</span><span class=\"token punctuation\">:</span> assets/fonts/Raleway<span class=\"token punctuation\">-</span>Regular.ttf\n        <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">asset</span><span class=\"token punctuation\">:</span> assets/fonts/Raleway<span class=\"token punctuation\">-</span>Medium.ttf\n          <span class=\"token key atrule\">weight</span><span class=\"token punctuation\">:</span> <span class=\"token number\">500</span>\n        <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">asset</span><span class=\"token punctuation\">:</span> assets/fonts/Raleway<span class=\"token punctuation\">-</span>SemiBold.ttf\n          <span class=\"token key atrule\">weight</span><span class=\"token punctuation\">:</span> <span class=\"token number\">600</span>\n    <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">family</span><span class=\"token punctuation\">:</span> AbrilFatface\n      <span class=\"token key atrule\">fonts</span><span class=\"token punctuation\">:</span>\n        <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">asset</span><span class=\"token punctuation\">:</span> assets/fonts/abrilfatface/AbrilFatface<span class=\"token punctuation\">-</span>Regular.ttf\n</code></pre></div><h4 id=\"使用字体\"><a href=\"#使用字体\" class=\"header-anchor\">#</a> 使用字体</h4> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">// 声明文本样式</span>\n<span class=\"token keyword\">const</span> textStyle <span class=\"token operator\">=</span> <span class=\"token keyword\">const</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>\n  fontFamily<span class=\"token punctuation\">:</span> <span class=\"token string\">'Raleway'</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// 使用文本样式</span>\n<span class=\"token keyword\">var</span> buttonText <span class=\"token operator\">=</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>\n  <span class=\"token string\">&quot;Use the font for this text&quot;</span><span class=\"token punctuation\">,</span>\n  style<span class=\"token punctuation\">:</span> textStyle<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><h4 id=\"package中的字体\"><a href=\"#package中的字体\" class=\"header-anchor\">#</a> Package中的字体</h4> <p>要使用Package中定义的字体，<strong>必须提供<code>package</code>参数</strong>。例如，假设上面的字体声明位于<code>my_package</code>包中。然后创建TextStyle的过程如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">const</span> textStyle <span class=\"token operator\">=</span> <span class=\"token keyword\">const</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>\n  fontFamily<span class=\"token punctuation\">:</span> <span class=\"token string\">'Raleway'</span><span class=\"token punctuation\">,</span>\n  package<span class=\"token punctuation\">:</span> <span class=\"token string\">'my_package'</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//指定包名</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>如果在package包内部使用它自己定义的字体，也应该在创建文本样式时指定<code>package</code>参数，如上例所示。</p> <p>一个包也可以只提供字体文件而不需要在pubspec.yaml中声明。 这些文件应该存放在包的<code>lib/</code>文件夹中。字体文件不会自动绑定到应用程序中，应用程序可以在声明字体时有选择地使用这些字体。假设一个名为my_package的包中有一个字体文件：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>lib/fonts/Raleway-Medium.ttf\n</code></pre></div><p>然后，应用程序可以声明一个字体，如下面的示例所示：</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code> <span class=\"token key atrule\">flutter</span><span class=\"token punctuation\">:</span>\n   <span class=\"token key atrule\">fonts</span><span class=\"token punctuation\">:</span>\n     <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">family</span><span class=\"token punctuation\">:</span> Raleway\n       <span class=\"token key atrule\">fonts</span><span class=\"token punctuation\">:</span>\n         <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">asset</span><span class=\"token punctuation\">:</span> assets/fonts/Raleway<span class=\"token punctuation\">-</span>Regular.ttf\n         <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">asset</span><span class=\"token punctuation\">:</span> packages/my_package/fonts/Raleway<span class=\"token punctuation\">-</span>Medium.ttf\n           <span class=\"token key atrule\">weight</span><span class=\"token punctuation\">:</span> <span class=\"token number\">500</span>\n</code></pre></div><p><code>lib/</code>是隐含的，所以它不应该包含在asset路径中。</p> <p>在这种情况下，由于应用程序本地定义了字体，所以在创建TextStyle时可以不指定<code>package</code>参数：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">const</span> textStyle <span class=\"token operator\">=</span> <span class=\"token keyword\">const</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>\n  fontFamily<span class=\"token punctuation\">:</span> <span class=\"token string\">'Raleway'</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter3/state_manage.html\" class=\"prev\">\n        3.2 状态管理\n      </a></span> <span class=\"next\"><a href=\"/chapter3/buttons.html\">\n        3.4 按钮\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/22.4fdaa56f.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter4/alignment.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>4.6 对齐与相对定位（Align） | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/28.fbba6f6f.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter4/chapter4/index.md\" class=\"sidebar-heading clickable open\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" aria-current=\"page\" class=\"active sidebar-link\">4.6 对齐与相对定位（Align）</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/chapter4/alignment.html#_4-6-1-align\" class=\"sidebar-link\">4.6.1 Align</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter4/alignment.html#_4-6-2-align和stack对比\" class=\"sidebar-link\">4.6.2 Align和Stack对比</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter4/alignment.html#_4-6-3-center组件\" class=\"sidebar-link\">4.6.3 Center组件</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter4/alignment.html#总结\" class=\"sidebar-link\">总结</a></li></ul></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_4-6-对齐与相对定位-align\"><a href=\"#_4-6-对齐与相对定位-align\" class=\"header-anchor\">#</a> 4.6 对齐与相对定位（Align）</h1> <p>在上一节中我们讲过通过<code>Stack</code>和<code>Positioned</code>，我们可以指定一个或多个子元素相对于父元素各个边的精确偏移，并且可以重叠。但如果我们只想简单的调整<strong>一个</strong>子元素在父元素中的位置的话，使用<code>Align</code>组件会更简单一些。</p> <h2 id=\"_4-6-1-align\"><a href=\"#_4-6-1-align\" class=\"header-anchor\">#</a> 4.6.1 Align</h2> <p><code>Align</code> 组件可以调整子组件的位置，并且可以根据子组件的宽高来确定自身的的宽高，定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Align</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  Key key<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>alignment <span class=\"token operator\">=</span> Alignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>widthFactor<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>heightFactor<span class=\"token punctuation\">,</span>\n  Widget child<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><ul><li><code>alignment</code> : 需要一个<code>AlignmentGeometry</code>类型的值，表示子组件在父组件中的起始位置。<code>AlignmentGeometry</code> 是一个抽象类，它有两个常用的子类：<code>Alignment</code>和 <code>FractionalOffset</code>，我们将在下面的示例中详细介绍。</li> <li><code>widthFactor</code>和<code>heightFactor</code>是用于确定<code>Align</code> 组件本身宽高的属性；它们是两个缩放因子，会分别乘以子元素的宽、高，最终的结果就是<code>Align</code> 组件的宽高。如果值为<code>null</code>，则组件的宽高将会占用尽可能多的空间。</li></ul> <h3 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h3> <p>我们先来看一个简单的例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n  height<span class=\"token punctuation\">:</span> <span class=\"token number\">120.0</span><span class=\"token punctuation\">,</span>\n  width<span class=\"token punctuation\">:</span> <span class=\"token number\">120.0</span><span class=\"token punctuation\">,</span>\n  color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">[</span><span class=\"token number\">50</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Align</span><span class=\"token punctuation\">(</span>\n    alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>topRight<span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">FlutterLogo</span><span class=\"token punctuation\">(</span>\n      size<span class=\"token punctuation\">:</span> <span class=\"token number\">60</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行效果如图4-11所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图4-11\"></p> <p><code>FlutterLogo</code> 是Flutter SDK提供的一个组件，内容就是Flutter的商标。在上面的例子中，我们显式指定了<code>Container</code>的宽、高都为120。如果我们不显式指定宽高，而通过同时指定<code>widthFactor</code>和<code>heightFactor</code> 为2也是可以达到同样的效果：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Align</span><span class=\"token punctuation\">(</span>\n  widthFactor<span class=\"token punctuation\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span>\n  heightFactor<span class=\"token punctuation\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span>\n  alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>topRight<span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">FlutterLogo</span><span class=\"token punctuation\">(</span>\n    size<span class=\"token punctuation\">:</span> <span class=\"token number\">60</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</code></pre></div><p>因为<code>FlutterLogo</code>的宽高为60，则<code>Align</code>的最终宽高都为<code>2*60=120</code>。</p> <p>另外，我们通过<code>Alignment.topRight</code>将<code>FlutterLogo</code>定位在<code>Container</code>的右上角。那<code>Alignment.topRight</code>是什么呢？通过源码我们可以看到其定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//右上角</span>\n<span class=\"token keyword\">static</span> <span class=\"token keyword\">const</span> Alignment topRight <span class=\"token operator\">=</span> <span class=\"token function\">Alignment</span><span class=\"token punctuation\">(</span><span class=\"token number\">1.0</span><span class=\"token punctuation\">,</span> <span class=\"token operator\">-</span><span class=\"token number\">1.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>可以看到它只是<code>Alignment</code>的一个实例，下面我们介绍一下<code>Alignment</code>。</p> <h3 id=\"alignment\"><a href=\"#alignment\" class=\"header-anchor\">#</a> Alignment</h3> <p><code>Alignment</code>继承自<code>AlignmentGeometry</code>，表示矩形内的一个点，他有两个属性<code>x</code>、<code>y</code>，分别表示在水平和垂直方向的偏移，<code>Alignment</code>定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Alignment</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>x<span class=\"token punctuation\">,</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>y<span class=\"token punctuation\">)</span>\n</code></pre></div><p><code>Alignment</code> Widget会以<strong>矩形的中心点作为坐标原点</strong>，即<code>Alignment(0.0, 0.0)</code> 。<code>x</code>、<code>y</code>的值从-1到1分别代表矩形左边到右边的距离和顶部到底边的距离，因此2个水平（或垂直）单位则等于矩形的宽（或高），如<code>Alignment(-1.0, -1.0)</code> 代表矩形的左侧顶点，而<code>Alignment(1.0, 1.0)</code>代表右侧底部终点，而<code>Alignment(1.0, -1.0)</code> 则正是右侧顶点，即<code>Alignment.topRight</code>。为了使用方便，矩形的原点、四个顶点，以及四条边的终点在<code>Alignment</code>类中都已经定义为了静态常量。</p> <p><code>Alignment</code>可以通过其<strong>坐标转换公式</strong>将其坐标转为子元素的具体偏移坐标：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>(Alignment.x*childWidth/2+childWidth/2, Alignment.y*childHeight/2+childHeight/2)\n</code></pre></div><p>其中<code>childWidth</code>为子元素的宽度，<code>childHeight</code>为子元素高度。</p> <p>现在我们再看看上面的示例，我们将<code>Alignment(1.0, -1.0)</code>带入上面公式，可得<code>FlutterLogo</code>的实际偏移坐标正是（60，0）。下面再看一个例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code> <span class=\"token function\">Align</span><span class=\"token punctuation\">(</span>\n  widthFactor<span class=\"token punctuation\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span>\n  heightFactor<span class=\"token punctuation\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span>\n  alignment<span class=\"token punctuation\">:</span> <span class=\"token function\">Alignment</span><span class=\"token punctuation\">(</span><span class=\"token number\">2</span><span class=\"token punctuation\">,</span><span class=\"token number\">0.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">FlutterLogo</span><span class=\"token punctuation\">(</span>\n    size<span class=\"token punctuation\">:</span> <span class=\"token number\">60</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>我们可以先想象一下运行效果：将<code>Alignment(2,0.0)</code>带入上述坐标转换公式，可以得到<code>FlutterLogo</code>的实际偏移坐标为（90，30）。实际运行如图4-12所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图4-12\"></p> <h3 id=\"fractionaloffset\"><a href=\"#fractionaloffset\" class=\"header-anchor\">#</a> FractionalOffset</h3> <p><code>FractionalOffset</code> 继承自 <code>Alignment</code>，它和 <code>Alignment</code>唯一的区别就是坐标原点不同！<code>FractionalOffset</code> 的坐标原点为矩形的左侧顶点，这和布局系统的一致，所以理解起来会比较容易。<code>FractionalOffset</code>的坐标转换公式为：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>实际偏移 = (FractionalOffse.x * childWidth, FractionalOffse.y * childHeight)\n</code></pre></div><p>下面看一个例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n  height<span class=\"token punctuation\">:</span> <span class=\"token number\">120.0</span><span class=\"token punctuation\">,</span>\n  width<span class=\"token punctuation\">:</span> <span class=\"token number\">120.0</span><span class=\"token punctuation\">,</span>\n  color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">[</span><span class=\"token number\">50</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Align</span><span class=\"token punctuation\">(</span>\n    alignment<span class=\"token punctuation\">:</span> <span class=\"token function\">FractionalOffset</span><span class=\"token punctuation\">(</span><span class=\"token number\">0.2</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0.6</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">FlutterLogo</span><span class=\"token punctuation\">(</span>\n      size<span class=\"token punctuation\">:</span> <span class=\"token number\">60</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>实际运行效果如图4-13所示下：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图4-13\"></p> <p>我们将<code>FractionalOffset(0.2, 0.6)</code>带入坐标转换公式得<code>FlutterLogo</code>实际偏移为（12，36），和实际运行效果吻合。</p> <h2 id=\"_4-6-2-align和stack对比\"><a href=\"#_4-6-2-align和stack对比\" class=\"header-anchor\">#</a> 4.6.2 Align和Stack对比</h2> <p>可以看到，<code>Align</code>和<code>Stack</code>/<code>Positioned</code>都可以用于指定子元素相对于父元素的偏移，但它们还是有两个主要区别：</p> <ol><li>定位参考系统不同；<code>Stack</code>/<code>Positioned</code>定位的的参考系可以是父容器矩形的四个顶点；而<code>Align</code>则需要先通过<code>alignment</code> 参数来确定坐标原点，不同的<code>alignment</code>会对应不同原点，最终的偏移是需要通过<code>alignment</code>的转换公式来计算出。</li> <li><code>Stack</code>可以有多个子元素，并且子元素可以堆叠，而<code>Align</code>只能有一个子元素，不存在堆叠。</li></ol> <h2 id=\"_4-6-3-center组件\"><a href=\"#_4-6-3-center组件\" class=\"header-anchor\">#</a> 4.6.3 Center组件</h2> <p>我们在前面章节的例子中已经使用过<code>Center</code>组件来居中子元素了，现在我们正式来介绍一下它。通过查找SDK源码，我们看到<code>Center</code>组件定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">Center</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">Align</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> Key key<span class=\"token punctuation\">,</span> double widthFactor<span class=\"token punctuation\">,</span> double heightFactor<span class=\"token punctuation\">,</span> Widget child <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">,</span> widthFactor<span class=\"token punctuation\">:</span> widthFactor<span class=\"token punctuation\">,</span> heightFactor<span class=\"token punctuation\">:</span> heightFactor<span class=\"token punctuation\">,</span> child<span class=\"token punctuation\">:</span> child<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以看到<code>Center</code>继承自<code>Align</code>，它比<code>Align</code>只少了一个<code>alignment</code> 参数；由于<code>Align</code>的构造函数中<code>alignment</code> 值为<code>Alignment.center</code>，所以，我们可以认为<code>Center</code>组件其实是对齐方式确定（<code>Alignment.center</code>）了的<code>Align</code>。</p> <p>上面我们讲过当<code>widthFactor</code>或<code>heightFactor</code>为<code>null</code>时组件的宽高将会占用尽可能多的空间，这一点需要特别注意，我们通过一个示例说明：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token comment\">//省略无关代码</span>\n<span class=\"token function\">DecoratedBox</span><span class=\"token punctuation\">(</span>\n  decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;xxx&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token function\">DecoratedBox</span><span class=\"token punctuation\">(</span>\n  decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n    widthFactor<span class=\"token punctuation\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n    heightFactor<span class=\"token punctuation\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;xxx&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行效果如图4-14所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图4-14\"></p> <h2 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h2> <p>本节重点介绍了<code>Align</code>组件及两种偏移类<code>Alignment</code> 和<code>FractionalOffset</code>，读者需要理解这两种偏移类的区别及各自的坐标转化公式。另外，在此建议读者在需要制定一些精确的偏移时应优先使用<code>FractionalOffset</code>，因为它的坐标原点和布局系统相同，能更容易算出实际偏移。</p> <p>在后面，我们又介绍了<code>Align</code>组件和<code>Stack</code>/<code>Positioned</code>、<code>Center</code>的关系，读者可以对比理解。</p> <p>还有，熟悉Web开发的同学可能会发现<code>Align</code>组件的特性和Web开发中相对定位（<code>position: relative</code>）非常像，是的！在大多数时候，我们可以直接使用<code>Align</code>组件来实现Web中相对定位的效果，读者可以类比记忆。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter4/stack.html\" class=\"prev\">\n        4.5 层叠布局 Stack、Positioned\n      </a></span> <span class=\"next\"><a href=\"/chapter5/padding.html\">\n        5.1 填充（Padding）\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/28.fbba6f6f.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter4/flex.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>4.3 弹性布局（Flex） | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/98.8023c3ac.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter4/chapter4/index.md\" class=\"sidebar-heading clickable open\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" aria-current=\"page\" class=\"active sidebar-link\">4.3 弹性布局（Flex）</a><ul class=\"sidebar-sub-headers\"></ul></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_4-3-弹性布局-flex\"><a href=\"#_4-3-弹性布局-flex\" class=\"header-anchor\">#</a> 4.3 弹性布局（Flex）</h1> <p>弹性布局允许子组件按照一定比例来分配父容器空间。弹性布局的概念在其它UI系统中也都存在，如H5中的弹性盒子布局，Android中的<code>FlexboxLayout</code>等。Flutter中的弹性布局主要通过<code>Flex</code>和<code>Expanded</code>来配合实现。</p> <h3 id=\"flex\"><a href=\"#flex\" class=\"header-anchor\">#</a> Flex</h3> <p><code>Flex</code>组件可以沿着水平或垂直方向排列子组件，如果你知道主轴方向，使用<code>Row</code>或<code>Column</code>会方便一些，因为<code>Row</code>和<code>Column</code>都继承自<code>Flex</code>，参数基本相同，所以能使用Flex的地方基本上都可以使用<code>Row</code>或<code>Column</code>。<code>Flex</code>本身功能是很强大的，它也可以和<code>Expanded</code>组件配合实现弹性布局。接下来我们只讨论<code>Flex</code>和弹性布局相关的属性(其它属性已经在介绍<code>Row</code>和<code>Column</code>时介绍过了)。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Flex</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>direction<span class=\"token punctuation\">,</span> <span class=\"token comment\">//弹性布局的方向, Row默认为水平方向，Column默认为垂直方向</span>\n  List<span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span> children <span class=\"token operator\">=</span> <span class=\"token keyword\">const</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p><code>Flex</code>继承自<code>MultiChildRenderObjectWidget</code>，对应的<code>RenderObject</code>为<code>RenderFlex</code>，<code>RenderFlex</code>中实现了其布局算法。</p> <h3 id=\"expanded\"><a href=\"#expanded\" class=\"header-anchor\">#</a> Expanded</h3> <p>可以按比例“扩伸” <code>Row</code>、<code>Column</code>和<code>Flex</code>子组件所占用的空间。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">const</span> <span class=\"token function\">Expanded</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  int flex <span class=\"token operator\">=</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span> \n  <span class=\"token metadata symbol\">@required</span> Widget child<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p><code>flex</code>参数为弹性系数，如果为0或<code>null</code>，则<code>child</code>是没有弹性的，即不会被扩伸占用的空间。如果大于0，所有的<code>Expanded</code>按照其flex的比例来分割主轴的全部空闲空间。下面我们看一个例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">FlexLayoutTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n      children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n        <span class=\"token comment\">//Flex的两个子widget按1：2来占据水平空间  </span>\n        <span class=\"token function\">Flex</span><span class=\"token punctuation\">(</span>\n          direction<span class=\"token punctuation\">:</span> Axis<span class=\"token punctuation\">.</span>horizontal<span class=\"token punctuation\">,</span>\n          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            <span class=\"token function\">Expanded</span><span class=\"token punctuation\">(</span>\n              flex<span class=\"token punctuation\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n                height<span class=\"token punctuation\">:</span> <span class=\"token number\">30.0</span><span class=\"token punctuation\">,</span>\n                color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token function\">Expanded</span><span class=\"token punctuation\">(</span>\n              flex<span class=\"token punctuation\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n                height<span class=\"token punctuation\">:</span> <span class=\"token number\">30.0</span><span class=\"token punctuation\">,</span>\n                color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>green<span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n          padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">only</span><span class=\"token punctuation\">(</span>top<span class=\"token punctuation\">:</span> <span class=\"token number\">20.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span>\n            height<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n            <span class=\"token comment\">//Flex的三个子widget，在垂直方向按2：1：1来占用100像素的空间  </span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Flex</span><span class=\"token punctuation\">(</span>\n              direction<span class=\"token punctuation\">:</span> Axis<span class=\"token punctuation\">.</span>vertical<span class=\"token punctuation\">,</span>\n              children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                <span class=\"token function\">Expanded</span><span class=\"token punctuation\">(</span>\n                  flex<span class=\"token punctuation\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span>\n                  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n                    height<span class=\"token punctuation\">:</span> <span class=\"token number\">30.0</span><span class=\"token punctuation\">,</span>\n                    color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token function\">Spacer</span><span class=\"token punctuation\">(</span>\n                  flex<span class=\"token punctuation\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token function\">Expanded</span><span class=\"token punctuation\">(</span>\n                  flex<span class=\"token punctuation\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n                  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n                    height<span class=\"token punctuation\">:</span> <span class=\"token number\">30.0</span><span class=\"token punctuation\">,</span>\n                    color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>green<span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行效果如图4-5所示：</p> <p><img src=\"/assets/img/4-5.67110f64.png\" alt=\"弹性布局\"></p> <p>示例中的<code>Spacer</code>的功能是占用指定比例的空间，实际上它只是<code>Expanded</code>的一个包装类，<code>Spacer</code>的源码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">Spacer</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function\">Spacer</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>Key key<span class=\"token punctuation\">,</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>flex <span class=\"token operator\">=</span> <span class=\"token number\">1</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">:</span> <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span>flex <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span>flex <span class=\"token operator\">&gt;</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  \n  <span class=\"token keyword\">final</span> int flex<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Expanded</span><span class=\"token punctuation\">(</span>\n      flex<span class=\"token punctuation\">:</span> flex<span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> SizedBox<span class=\"token punctuation\">.</span><span class=\"token function\">shrink</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h3 id=\"小结\"><a href=\"#小结\" class=\"header-anchor\">#</a> 小结</h3> <p>弹性布局比较简单，唯一需要注意的就是<code>Row</code>、<code>Column</code>以及<code>Flex</code>的关系。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter4/row_and_column.html\" class=\"prev\">\n        4.2 线性布局（Row和Column）\n      </a></span> <span class=\"next\"><a href=\"/chapter4/wrap_and_flow.html\">\n        4.4 流式布局\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/98.8023c3ac.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter4/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>本章目录 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/159.277037a1.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter4/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"本章目录\"><a href=\"#本章目录\" class=\"header-anchor\">#</a> 本章目录</h1> <ul><li><a href=\"/chapter4/intro.html\">4.1：布局类组件简介</a></li> <li><a href=\"/chapter4/row_and_column.html\">4.2：线性布局（Row、Column）</a></li> <li><a href=\"/chapter4/flex.html\">4.3：弹性布局（Flex）</a></li> <li><a href=\"/chapter4/wrap_and_flow.html\">4.4：流式布局（Wrap、Flow）</a></li> <li><a href=\"/chapter4/stack.html\">4.5：层叠布局（Stack、Positioned）</a></li> <li><a href=\"/chapter4/alignment.html\">4.6：对齐与相对定位（Align）</a></li></ul></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/159.277037a1.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter4/intro.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>4.1 布局类组件简介 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/160.0664832e.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter4/chapter4/index.md\" class=\"sidebar-heading clickable open\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" aria-current=\"page\" class=\"active sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_4-1-布局类组件简介\"><a href=\"#_4-1-布局类组件简介\" class=\"header-anchor\">#</a> 4.1 布局类组件简介</h1> <p>布局类组件都会包含一个或多个子组件，不同的布局类组件对子组件排版(layout)方式不同。我们在前面说过<code>Element</code>树才是最终的绘制树，<code>Element</code>树是通过Widget树来创建的（通过<code>Widget.createElement()</code>），Widget其实就是Element的配置数据。在Flutter中，根据Widget是否需要包含子节点将Widget分为了三类，分别对应三种Element，如下表：</p> <table><thead><tr><th>Widget</th> <th>对应的Element</th> <th>用途</th></tr></thead> <tbody><tr><td>LeafRenderObjectWidget</td> <td>LeafRenderObjectElement</td> <td>Widget树的叶子节点，用于没有子节点的widget，通常基础组件都属于这一类，如Image。</td></tr> <tr><td>SingleChildRenderObjectWidget</td> <td>SingleChildRenderObjectElement</td> <td>包含一个子Widget，如：ConstrainedBox、DecoratedBox等</td></tr> <tr><td>MultiChildRenderObjectWidget</td> <td>MultiChildRenderObjectElement</td> <td>包含多个子Widget，一般都有一个children参数，接受一个Widget数组。如Row、Column、Stack等</td></tr></tbody></table> <blockquote><p>注意，Flutter中的很多Widget是直接继承自StatelessWidget或StatefulWidget，然后在<code>build()</code>方法中构建真正的RenderObjectWidget，如Text，它其实是继承自StatelessWidget，然后在<code>build()</code>方法中通过RichText来构建其子树，而RichText才是继承自MultiChildRenderObjectWidget。所以为了方便叙述，我们也可以直接说Text属于MultiChildRenderObjectWidget（其它widget也可以这么描述），这才是本质。读到这里我们也会发现，其实<strong>StatelessWidget和StatefulWidget就是两个用于组合Widget的基类，它们本身并不关联最终的渲染对象（RenderObjectWidget）</strong>。</p></blockquote> <p>布局类组件就是指直接或间接继承(包含)<code>MultiChildRenderObjectWidget</code>的Widget，它们一般都会有一个<code>children</code>属性用于接收子Widget。我们看一下继承关系 Widget &gt; RenderObjectWidget &gt; (Leaf/SingleChild/MultiChild)RenderObjectWidget 。</p> <p><code>RenderObjectWidget</code>类中定义了创建、更新<code>RenderObject</code>的方法，子类必须实现他们，关于<code>RenderObject</code>我们现在只需要知道它是最终布局、渲染UI界面的对象即可，也就是说，对于布局类组件来说，其布局算法都是通过对应的<code>RenderObject</code>对象来实现的，所以读者如果对接下来介绍的某个布局类组件的原理感兴趣，可以查看其对应的<code>RenderObject</code>的实现，比如<code>Stack</code>（层叠布局）对应的<code>RenderObject</code>对象就是<code>RenderStack</code>，而层叠布局的实现就在<code>RenderStack</code>中。</p> <p>在本章中，为了让读者对布局类Widget有个快速的认识，所以我们并不会深入到<code>RenderObject</code>的细节中去。在学习本章时，读者的重点是掌握不同布局组件的布局特点，具体原理和细节等我们对Flutter整体入门后，感兴趣的话再去研究。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter3/progress.html\" class=\"prev\">\n        3.8 进度指示器\n      </a></span> <span class=\"next\"><a href=\"/chapter4/row_and_column.html\">\n        4.2 线性布局（Row和Column）\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/160.0664832e.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter4/row_and_column.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>4.2 线性布局（Row和Column） | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/29.e809e77a.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter4/chapter4/index.md\" class=\"sidebar-heading clickable open\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" aria-current=\"page\" class=\"active sidebar-link\">4.2 线性布局（Row和Column）</a><ul class=\"sidebar-sub-headers\"></ul></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_4-2-线性布局-row和column\"><a href=\"#_4-2-线性布局-row和column\" class=\"header-anchor\">#</a> 4.2 线性布局（Row和Column）</h1> <p>所谓线性布局，即指沿水平或垂直方向排布子组件。Flutter中通过<code>Row</code>和<code>Column</code>来实现线性布局，类似于Android中的<code>LinearLayout</code>控件。<code>Row</code>和<code>Column</code>都继承自<code>Flex</code>，我们将在弹性布局一节中详细介绍<code>Flex</code>。</p> <h3 id=\"主轴和纵轴\"><a href=\"#主轴和纵轴\" class=\"header-anchor\">#</a> 主轴和纵轴</h3> <p>对于线性布局，有主轴和纵轴之分，如果布局是沿水平方向，那么主轴就是指水平方向，而纵轴即垂直方向；如果布局沿垂直方向，那么主轴就是指垂直方向，而纵轴就是水平方向。在线性布局中，有两个定义对齐方式的枚举类<code>MainAxisAlignment</code>和<code>CrossAxisAlignment</code>，分别代表主轴对齐和纵轴对齐。</p> <h3 id=\"row\"><a href=\"#row\" class=\"header-anchor\">#</a> Row</h3> <p>Row可以在水平方向排列其子widget。定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Row</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>  \n  TextDirection textDirection<span class=\"token punctuation\">,</span>    \n  MainAxisSize mainAxisSize <span class=\"token operator\">=</span> MainAxisSize<span class=\"token punctuation\">.</span>max<span class=\"token punctuation\">,</span>    \n  MainAxisAlignment mainAxisAlignment <span class=\"token operator\">=</span> MainAxisAlignment<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n  VerticalDirection verticalDirection <span class=\"token operator\">=</span> VerticalDirection<span class=\"token punctuation\">.</span>down<span class=\"token punctuation\">,</span>  \n  CrossAxisAlignment crossAxisAlignment <span class=\"token operator\">=</span> CrossAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n  List<span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span> children <span class=\"token operator\">=</span> <span class=\"token keyword\">const</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><ul><li><code>textDirection</code>：表示水平方向子组件的布局顺序(是从左往右还是从右往左)，默认为系统当前Locale环境的文本方向(如中文、英语都是从左往右，而阿拉伯语是从右往左)。</li> <li><code>mainAxisSize</code>：表示<code>Row</code>在主轴(水平)方向占用的空间，默认是<code>MainAxisSize.max</code>，表示尽可能多的占用水平方向的空间，此时无论子widgets实际占用多少水平空间，<code>Row</code>的宽度始终等于水平方向的最大宽度；而<code>MainAxisSize.min</code>表示尽可能少的占用水平空间，当子组件没有占满水平剩余空间，则<code>Row</code>的实际宽度等于所有子组件占用的的水平空间；</li> <li><code>mainAxisAlignment</code>：表示子组件在<code>Row</code>所占用的水平空间内对齐方式，如果<code>mainAxisSize</code>值为<code>MainAxisSize.min</code>，则此属性无意义，因为子组件的宽度等于<code>Row</code>的宽度。只有当<code>mainAxisSize</code>的值为<code>MainAxisSize.max</code>时，此属性才有意义，<code>MainAxisAlignment.start</code>表示沿<code>textDirection</code>的初始方向对齐，如<code>textDirection</code>取值为<code>TextDirection.ltr</code>时，则<code>MainAxisAlignment.start</code>表示左对齐，<code>textDirection</code>取值为<code>TextDirection.rtl</code>时表示从右对齐。而<code>MainAxisAlignment.end</code>和<code>MainAxisAlignment.start</code>正好相反；<code>MainAxisAlignment.center</code>表示居中对齐。读者可以这么理解：<code>textDirection</code>是<code>mainAxisAlignment</code>的参考系。</li> <li><code>verticalDirection</code>：表示<code>Row</code>纵轴（垂直）的对齐方向，默认是<code>VerticalDirection.down</code>，表示从上到下。</li> <li><code>crossAxisAlignment</code>：表示子组件在纵轴方向的对齐方式，<code>Row</code>的高度等于子组件中最高的子元素高度，它的取值和<code>MainAxisAlignment</code>一样(包含<code>start</code>、<code>end</code>、 <code>center</code>三个值)，不同的是<code>crossAxisAlignment</code>的参考系是<code>verticalDirection</code>，即<code>verticalDirection</code>值为<code>VerticalDirection.down</code>时<code>crossAxisAlignment.start</code>指顶部对齐，<code>verticalDirection</code>值为<code>VerticalDirection.up</code>时，<code>crossAxisAlignment.start</code>指底部对齐；而<code>crossAxisAlignment.end</code>和<code>crossAxisAlignment.start</code>正好相反；</li> <li><code>children</code> ：子组件数组。</li></ul> <h3 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h3> <p>请阅读下面代码，先想象一下运行的结果：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n  <span class=\"token comment\">//测试Row对齐方式，排除Column默认居中对齐的干扰</span>\n  crossAxisAlignment<span class=\"token punctuation\">:</span> CrossAxisAlignment<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n      mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n      children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n        <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot; hello world &quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot; I am Jack &quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n      mainAxisSize<span class=\"token punctuation\">:</span> MainAxisSize<span class=\"token punctuation\">.</span>min<span class=\"token punctuation\">,</span>\n      mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n      children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n        <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot; hello world &quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot; I am Jack &quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n      mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>end<span class=\"token punctuation\">,</span>\n      textDirection<span class=\"token punctuation\">:</span> TextDirection<span class=\"token punctuation\">.</span>rtl<span class=\"token punctuation\">,</span>\n      children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n        <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot; hello world &quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot; I am Jack &quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n      crossAxisAlignment<span class=\"token punctuation\">:</span> CrossAxisAlignment<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>  \n      verticalDirection<span class=\"token punctuation\">:</span> VerticalDirection<span class=\"token punctuation\">.</span>up<span class=\"token punctuation\">,</span>\n      children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n        <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot; hello world &quot;</span><span class=\"token punctuation\">,</span> style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">30.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot; I am Jack &quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>实际运行结果如图4-1所示：</p> <p><img src=\"/assets/img/4-1.86777353.png\" alt=\"图4-1\"></p> <p>解释：第一个<code>Row</code>很简单，默认为居中对齐；第二个<code>Row</code>，由于<code>mainAxisSize</code>值为<code>MainAxisSize.min</code>，<code>Row</code>的宽度等于两个<code>Text</code>的宽度和，所以对齐是无意义的，所以会从左往右显示；第三个<code>Row</code>设置<code>textDirection</code>值为<code>TextDirection.rtl</code>，所以子组件会从右向左的顺序排列，而此时<code>MainAxisAlignment.end</code>表示左对齐，所以最终显示结果就是图中第三行的样子；第四个Row测试的是纵轴的对齐方式，由于两个子Text字体不一样，所以其高度也不同，我们指定了<code>verticalDirection</code>值为<code>VerticalDirection.up</code>，即从低向顶排列，而此时<code>crossAxisAlignment</code>值为<code>CrossAxisAlignment.start</code>表示底对齐。</p> <h3 id=\"column\"><a href=\"#column\" class=\"header-anchor\">#</a> Column</h3> <p><code>Column</code>可以在垂直方向排列其子组件。参数和<code>Row</code>一样，不同的是布局方向为垂直，主轴纵轴正好相反，读者可类比<code>Row</code>来理解，下面看一个例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">CenterColumnRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n      crossAxisAlignment<span class=\"token punctuation\">:</span> CrossAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n      children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n        <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;hi&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;world&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行效果如图4-2所示：</p> <p><img src=\"/assets/img/4-2.c3de517d.png\" alt=\"图4-2示例\"></p> <p>解释：</p> <ul><li>由于我们没有指定<code>Column</code>的<code>mainAxisSize</code>，所以使用默认值<code>MainAxisSize.max</code>，则<code>Column</code>会在垂直方向占用尽可能多的空间，此例中为屏幕高度。</li> <li>由于我们指定了 <code>crossAxisAlignment</code> 属性为<code>CrossAxisAlignment.center</code>，那么子项在<code>Column</code>纵轴方向（此时为水平方向）会居中对齐。注意，在水平方向对齐是有边界的，总宽度为<code>Column</code>占用空间的实际宽度，而实际的宽度取决于子项中宽度最大的Widget。在本例中，<code>Column</code>有两个子Widget，而显示“world”的<code>Text</code>宽度最大，所以<code>Column</code>的实际宽度则为<code>Text(&quot;world&quot;)</code> 的宽度，所以居中对齐后<code>Text(&quot;hi&quot;)</code>会显示在<code>Text(&quot;world&quot;)</code>的中间部分。</li></ul> <p><strong>实际上，<code>Row</code>和<code>Column</code>都只会在主轴方向占用尽可能大的空间，而纵轴的长度则取决于他们最大子元素的长度</strong>。如果我们想让本例中的两个文本控件在整个手机屏幕中间对齐，我们有两种方法：</p> <ul><li><p>将<code>Column</code>的宽度指定为屏幕宽度；这很简单，我们可以通过<code>ConstrainedBox</code>或<code>SizedBox</code>（我们将在后面章节中专门介绍这两个Widget）来强制更改宽度限制，例如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">ConstrainedBox</span><span class=\"token punctuation\">(</span>\n  constraints<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxConstraints</span><span class=\"token punctuation\">(</span>minWidth<span class=\"token punctuation\">:</span> double<span class=\"token punctuation\">.</span>infinity<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> \n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n    crossAxisAlignment<span class=\"token punctuation\">:</span> CrossAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n    children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n      <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;hi&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;world&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>将<code>minWidth</code>设为<code>double.infinity</code>，可以使宽度占用尽可能多的空间。</p></li> <li><p>使用<code>Center</code> Widget；我们将在后面章节中介绍。</p></li></ul> <h3 id=\"特殊情况\"><a href=\"#特殊情况\" class=\"header-anchor\">#</a> 特殊情况</h3> <p>如果<code>Row</code>里面嵌套<code>Row</code>，或者<code>Column</code>里面再嵌套<code>Column</code>，那么只有最外面的<code>Row</code>或<code>Column</code>会占用尽可能大的空间，里面<code>Row</code>或<code>Column</code>所占用的空间为实际大小，下面以<code>Column</code>为例说明：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n  color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>green<span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n    padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">16.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n      crossAxisAlignment<span class=\"token punctuation\">:</span> CrossAxisAlignment<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n      mainAxisSize<span class=\"token punctuation\">:</span> MainAxisSize<span class=\"token punctuation\">.</span>max<span class=\"token punctuation\">,</span> <span class=\"token comment\">//有效，外层Colum高度为整个屏幕</span>\n      children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n        <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n          color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n            mainAxisSize<span class=\"token punctuation\">:</span> MainAxisSize<span class=\"token punctuation\">.</span>max<span class=\"token punctuation\">,</span><span class=\"token comment\">//无效，内层Colum高度为实际高度  </span>\n            children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n              <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;hello world &quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;I am Jack &quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>运行效果如图4-3所示：</p> <p><img src=\"/assets/img/4-3.cc8d50f6.png\" alt=\"图4-3\"></p> <p>如果要让里面的<code>Column</code>占满外部<code>Column</code>，可以使用<code>Expanded</code> 组件：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Expanded</span><span class=\"token punctuation\">(</span> \n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n    color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n      mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span> <span class=\"token comment\">//垂直方向居中对齐</span>\n      children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n        <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;hello world &quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;I am Jack &quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行效果如图4-4所示：</p> <p><img src=\"/assets/img/4-4.00885ea7.png\" alt=\"图4-4\"></p> <p>我们将在介绍弹性布局时详细介绍Expanded。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter4/intro.html\" class=\"prev\">\n        4.1 布局类组件简介\n      </a></span> <span class=\"next\"><a href=\"/chapter4/flex.html\">\n        4.3 弹性布局（Flex）\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/29.e809e77a.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter4/stack.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>4.5 层叠布局 Stack、Positioned | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/63.ff70c9ee.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter4/chapter4/index.md\" class=\"sidebar-heading clickable open\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" aria-current=\"page\" class=\"active sidebar-link\">4.5 层叠布局 Stack、Positioned</a><ul class=\"sidebar-sub-headers\"></ul></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_4-5-层叠布局-stack、positioned\"><a href=\"#_4-5-层叠布局-stack、positioned\" class=\"header-anchor\">#</a> 4.5 层叠布局 Stack、Positioned</h1> <p>层叠布局和Web中的绝对定位、Android中的Frame布局是相似的，子组件可以根据距父容器四个角的位置来确定自身的位置。绝对定位允许子组件堆叠起来（按照代码中声明的顺序）。Flutter中使用<code>Stack</code>和<code>Positioned</code>这两个组件来配合实现绝对定位。<code>Stack</code>允许子组件堆叠，而<code>Positioned</code>用于根据<code>Stack</code>的四个角来确定子组件的位置。</p> <h3 id=\"stack\"><a href=\"#stack\" class=\"header-anchor\">#</a> Stack</h3> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Stack</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>alignment <span class=\"token operator\">=</span> AlignmentDirectional<span class=\"token punctuation\">.</span>topStart<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>textDirection<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>fit <span class=\"token operator\">=</span> StackFit<span class=\"token punctuation\">.</span>loose<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>overflow <span class=\"token operator\">=</span> Overflow<span class=\"token punctuation\">.</span>clip<span class=\"token punctuation\">,</span>\n  List<span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span> children <span class=\"token operator\">=</span> <span class=\"token keyword\">const</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><ul><li><code>alignment</code>：此参数决定如何去对齐没有定位（没有使用<code>Positioned</code>）或部分定位的子组件。所谓部分定位，在这里<strong>特指没有在某一个轴上定位：</strong><code>left</code>、<code>right</code>为横轴，<code>top</code>、<code>bottom</code>为纵轴，只要包含某个轴上的一个定位属性就算在该轴上有定位。</li> <li><code>textDirection</code>：和<code>Row</code>、<code>Wrap</code>的<code>textDirection</code>功能一样，都用于确定<code>alignment</code>对齐的参考系，即：<code>textDirection</code>的值为<code>TextDirection.ltr</code>，则<code>alignment</code>的<code>start</code>代表左，<code>end</code>代表右，即<code>从左往右</code>的顺序；<code>textDirection</code>的值为<code>TextDirection.rtl</code>，则alignment的<code>start</code>代表右，<code>end</code>代表左，即<code>从右往左</code>的顺序。</li> <li><code>fit</code>：此参数用于确定<strong>没有定位</strong>的子组件如何去适应<code>Stack</code>的大小。<code>StackFit.loose</code>表示使用子组件的大小，<code>StackFit.expand</code>表示扩伸到<code>Stack</code>的大小。</li> <li><code>overflow</code>：此属性决定如何显示超出<code>Stack</code>显示空间的子组件；值为<code>Overflow.clip</code>时，超出部分会被剪裁（隐藏），值为<code>Overflow.visible</code> 时则不会。</li></ul> <h3 id=\"positioned\"><a href=\"#positioned\" class=\"header-anchor\">#</a> Positioned</h3> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">const</span> <span class=\"token function\">Positioned</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  Key key<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>left<span class=\"token punctuation\">,</span> \n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>top<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>right<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>bottom<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>width<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>height<span class=\"token punctuation\">,</span>\n  <span class=\"token metadata symbol\">@required</span> Widget child<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p><code>left</code>、<code>top</code> 、<code>right</code>、 <code>bottom</code>分别代表离<code>Stack</code>左、上、右、底四边的距离。<code>width</code>和<code>height</code>用于指定需要定位元素的宽度和高度。注意，<code>Positioned</code>的<code>width</code>、<code>height</code> 和其它地方的意义稍微有点区别，此处用于配合<code>left</code>、<code>top</code> 、<code>right</code>、 <code>bottom</code>来定位组件，举个例子，在水平方向时，你只能指定<code>left</code>、<code>right</code>、<code>width</code>三个属性中的两个，如指定<code>left</code>和<code>width</code>后，<code>right</code>会自动算出(<code>left</code>+<code>width</code>)，如果同时指定三个属性则会报错，垂直方向同理。</p> <h3 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h3> <p>在下面的例子中，我们通过对几个<code>Text</code>组件的定位来演示<code>Stack</code>和<code>Positioned</code>的特性：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//通过ConstrainedBox来确保Stack占满屏幕</span>\n<span class=\"token function\">ConstrainedBox</span><span class=\"token punctuation\">(</span>\n  constraints<span class=\"token punctuation\">:</span> BoxConstraints<span class=\"token punctuation\">.</span><span class=\"token function\">expand</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Stack</span><span class=\"token punctuation\">(</span>\n    alignment<span class=\"token punctuation\">:</span>Alignment<span class=\"token punctuation\">.</span>center <span class=\"token punctuation\">,</span> <span class=\"token comment\">//指定未定位或部分定位widget的对齐方式</span>\n    children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n      <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hello world&quot;</span><span class=\"token punctuation\">,</span>style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token function\">Positioned</span><span class=\"token punctuation\">(</span>\n        left<span class=\"token punctuation\">:</span> <span class=\"token number\">18.0</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;I am Jack&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token function\">Positioned</span><span class=\"token punctuation\">(</span>\n        top<span class=\"token punctuation\">:</span> <span class=\"token number\">18.0</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Your friend&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span>        \n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>运行效果见图4-9：</p> <p><img src=\"/assets/img/4-9.7897e012.png\" alt=\"图4-9\"></p> <p>由于第一个子文本组件<code>Text(&quot;Hello world&quot;)</code>没有指定定位，并且<code>alignment</code>值为<code>Alignment.center</code>，所以它会居中显示。第二个子文本组件<code>Text(&quot;I am Jack&quot;)</code>只指定了水平方向的定位(<code>left</code>)，所以属于部分定位，即垂直方向上没有定位，那么它在垂直方向的对齐方式则会按照<code>alignment</code>指定的对齐方式对齐，即垂直方向居中。对于第三个子文本组件<code>Text(&quot;Your friend&quot;)</code>，和第二个<code>Text</code>原理一样，只不过是水平方向没有定位，则水平方向居中。</p> <p>我们给上例中的<code>Stack</code>指定一个<code>fit</code>属性，然后将三个子文本组件的顺序调整一下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Stack</span><span class=\"token punctuation\">(</span>\n  alignment<span class=\"token punctuation\">:</span>Alignment<span class=\"token punctuation\">.</span>center <span class=\"token punctuation\">,</span>\n  fit<span class=\"token punctuation\">:</span> StackFit<span class=\"token punctuation\">.</span>expand<span class=\"token punctuation\">,</span> <span class=\"token comment\">//未定位widget占满Stack整个空间</span>\n  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token function\">Positioned</span><span class=\"token punctuation\">(</span>\n      left<span class=\"token punctuation\">:</span> <span class=\"token number\">18.0</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;I am Jack&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hello world&quot;</span><span class=\"token punctuation\">,</span>style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Positioned</span><span class=\"token punctuation\">(</span>\n      top<span class=\"token punctuation\">:</span> <span class=\"token number\">18.0</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Your friend&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</code></pre></div><p>显示效果如图4-10所示：</p> <p><img src=\"/assets/img/4-10.9e758696.png\" alt=\"图4-10\"></p> <p>可以看到，由于第二个子文本组件没有定位，所以<code>fit</code>属性会对它起作用，就会占满<code>Stack</code>。由于<code>Stack</code>子元素是堆叠的，所以第一个子文本组件被第二个遮住了，而第三个在最上层，所以可以正常显示。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter4/wrap_and_flow.html\" class=\"prev\">\n        4.4 流式布局\n      </a></span> <span class=\"next\"><a href=\"/chapter4/alignment.html\">\n        4.6 对齐与相对定位（Align）\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/63.ff70c9ee.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter4/wrap_and_flow.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>4.4 流式布局 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/44.5f61dab7.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter4/chapter4/index.md\" class=\"sidebar-heading clickable open\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" aria-current=\"page\" class=\"active sidebar-link\">4.4 流式布局</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/chapter4/wrap_and_flow.html#_4-4-1-wrap\" class=\"sidebar-link\">4.4.1 Wrap</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter4/wrap_and_flow.html#_4-4-2-flow\" class=\"sidebar-link\">4.4.2 Flow</a></li></ul></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_4-4-流式布局\"><a href=\"#_4-4-流式布局\" class=\"header-anchor\">#</a> 4.4 流式布局</h1> <p>在介绍Row和Colum时，如果子widget超出屏幕范围，则会报溢出错误，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;xxx&quot;</span><span class=\"token operator\">*</span><span class=\"token number\">100</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>运行效果如图4-6所示：</p> <p><img src=\"/assets/img/4-6.563f1017.png\" alt=\"图4-6\"></p> <p>可以看到，右边溢出部分报错。这是因为Row默认只有一行，如果超出屏幕不会折行。我们把超出屏幕显示范围会自动折行的布局称为流式布局。Flutter中通过<code>Wrap</code>和<code>Flow</code>来支持流式布局，将上例中的Row换成Wrap后溢出部分则会自动折行，下面我们分别介绍<code>Wrap</code>和<code>Flow</code>.</p> <h2 id=\"_4-4-1-wrap\"><a href=\"#_4-4-1-wrap\" class=\"header-anchor\">#</a> 4.4.1 Wrap</h2> <p>下面是Wrap的定义:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Wrap</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>direction <span class=\"token operator\">=</span> Axis<span class=\"token punctuation\">.</span>horizontal<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>alignment <span class=\"token operator\">=</span> WrapAlignment<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>spacing <span class=\"token operator\">=</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>runAlignment <span class=\"token operator\">=</span> WrapAlignment<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>runSpacing <span class=\"token operator\">=</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>crossAxisAlignment <span class=\"token operator\">=</span> WrapCrossAlignment<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>textDirection<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>verticalDirection <span class=\"token operator\">=</span> VerticalDirection<span class=\"token punctuation\">.</span>down<span class=\"token punctuation\">,</span>\n  List<span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span> children <span class=\"token operator\">=</span> <span class=\"token keyword\">const</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>我们可以看到Wrap的很多属性在<code>Row</code>（包括<code>Flex</code>和<code>Column</code>）中也有，如<code>direction</code>、<code>crossAxisAlignment</code>、<code>textDirection</code>、<code>verticalDirection</code>等，这些参数意义是相同的，我们不再重复介绍，读者可以查阅前面介绍<code>Row</code>的部分。读者可以认为<code>Wrap</code>和<code>Flex</code>（包括<code>Row</code>和<code>Column</code>）除了超出显示范围后<code>Wrap</code>会折行外，其它行为基本相同。下面我们看一下<code>Wrap</code>特有的几个属性：</p> <ul><li><code>spacing</code>：主轴方向子widget的间距</li> <li><code>runSpacing</code>：纵轴方向的间距</li> <li><code>runAlignment</code>：纵轴方向的对齐方式</li></ul> <p>下面看一个示例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Wrap</span><span class=\"token punctuation\">(</span>\n  spacing<span class=\"token punctuation\">:</span> <span class=\"token number\">8.0</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 主轴(水平)方向间距</span>\n  runSpacing<span class=\"token punctuation\">:</span> <span class=\"token number\">4.0</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 纵轴（垂直）方向间距</span>\n  alignment<span class=\"token punctuation\">:</span> WrapAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span> <span class=\"token comment\">//沿主轴方向居中</span>\n  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token keyword\">new</span> <span class=\"token class-name\">Chip</span><span class=\"token punctuation\">(</span>\n      avatar<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">CircleAvatar</span><span class=\"token punctuation\">(</span>backgroundColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span> child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'A'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      label<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Hamilton'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">new</span> <span class=\"token class-name\">Chip</span><span class=\"token punctuation\">(</span>\n      avatar<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">CircleAvatar</span><span class=\"token punctuation\">(</span>backgroundColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span> child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'M'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      label<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Lafayette'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">new</span> <span class=\"token class-name\">Chip</span><span class=\"token punctuation\">(</span>\n      avatar<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">CircleAvatar</span><span class=\"token punctuation\">(</span>backgroundColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span> child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'H'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      label<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Mulligan'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">new</span> <span class=\"token class-name\">Chip</span><span class=\"token punctuation\">(</span>\n      avatar<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">CircleAvatar</span><span class=\"token punctuation\">(</span>backgroundColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span> child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'J'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      label<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Laurens'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行效果如图4-7所示：</p> <p><img src=\"/assets/img/4-7.632eedaf.png\" alt=\"图4-7\"></p> <h2 id=\"_4-4-2-flow\"><a href=\"#_4-4-2-flow\" class=\"header-anchor\">#</a> 4.4.2 Flow</h2> <p>我们一般很少会使用<code>Flow</code>，因为其过于复杂，需要自己实现子widget的位置转换，在很多场景下首先要考虑的是<code>Wrap</code>是否满足需求。<code>Flow</code>主要用于一些需要自定义布局策略或性能要求较高(如动画中)的场景。<code>Flow</code>有如下优点：</p> <ul><li>性能好；<code>Flow</code>是一个对子组件尺寸以及位置调整非常高效的控件，<code>Flow</code>用转换矩阵在对子组件进行位置调整的时候进行了优化：在<code>Flow</code>定位过后，如果子组件的尺寸或者位置发生了变化，在<code>FlowDelegate</code>中的<code>paintChildren()</code>方法中调用<code>context.paintChild</code> 进行重绘，而<code>context.paintChild</code>在重绘时使用了转换矩阵，并没有实际调整组件位置。</li> <li>灵活；由于我们需要自己实现<code>FlowDelegate</code>的<code>paintChildren()</code>方法，所以我们需要自己计算每一个组件的位置，因此，可以自定义布局策略。</li></ul> <p>缺点：</p> <ul><li>使用复杂。</li> <li>不能自适应子组件大小，必须通过指定父容器大小或实现<code>TestFlowDelegate</code>的<code>getSize</code>返回固定大小。</li></ul> <p>示例：</p> <p>我们对六个色块进行自定义流式布局：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Flow</span><span class=\"token punctuation\">(</span>\n  delegate<span class=\"token punctuation\">:</span> <span class=\"token function\">TestFlowDelegate</span><span class=\"token punctuation\">(</span>margin<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">10.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token keyword\">new</span> <span class=\"token class-name\">Container</span><span class=\"token punctuation\">(</span>width<span class=\"token punctuation\">:</span> <span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span> height<span class=\"token punctuation\">:</span><span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span> color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">new</span> <span class=\"token class-name\">Container</span><span class=\"token punctuation\">(</span>width<span class=\"token punctuation\">:</span> <span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span> height<span class=\"token punctuation\">:</span><span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span> color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>green<span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">new</span> <span class=\"token class-name\">Container</span><span class=\"token punctuation\">(</span>width<span class=\"token punctuation\">:</span> <span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span> height<span class=\"token punctuation\">:</span><span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span> color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">new</span> <span class=\"token class-name\">Container</span><span class=\"token punctuation\">(</span>width<span class=\"token punctuation\">:</span> <span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span> height<span class=\"token punctuation\">:</span><span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span>  color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>yellow<span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">new</span> <span class=\"token class-name\">Container</span><span class=\"token punctuation\">(</span>width<span class=\"token punctuation\">:</span> <span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span> height<span class=\"token punctuation\">:</span><span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span> color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>brown<span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">new</span> <span class=\"token class-name\">Container</span><span class=\"token punctuation\">(</span>width<span class=\"token punctuation\">:</span> <span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span> height<span class=\"token punctuation\">:</span><span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span>  color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>purple<span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>实现TestFlowDelegate:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">TestFlowDelegate</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">FlowDelegate</span> <span class=\"token punctuation\">{</span>\n  EdgeInsets margin <span class=\"token operator\">=</span> EdgeInsets<span class=\"token punctuation\">.</span>zero<span class=\"token punctuation\">;</span>\n  <span class=\"token function\">TestFlowDelegate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>margin<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">paintChildren</span><span class=\"token punctuation\">(</span>FlowPaintingContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">var</span> x <span class=\"token operator\">=</span> margin<span class=\"token punctuation\">.</span>left<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">var</span> y <span class=\"token operator\">=</span> margin<span class=\"token punctuation\">.</span>top<span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//计算每一个子widget的位置  </span>\n    <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span>int i <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span> i <span class=\"token operator\">&lt;</span> context<span class=\"token punctuation\">.</span>childCount<span class=\"token punctuation\">;</span> i<span class=\"token operator\">++</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">var</span> w <span class=\"token operator\">=</span> context<span class=\"token punctuation\">.</span><span class=\"token function\">getChildSize</span><span class=\"token punctuation\">(</span>i<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>width <span class=\"token operator\">+</span> x <span class=\"token operator\">+</span> margin<span class=\"token punctuation\">.</span>right<span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>w <span class=\"token operator\">&lt;</span> context<span class=\"token punctuation\">.</span>size<span class=\"token punctuation\">.</span>width<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        context<span class=\"token punctuation\">.</span><span class=\"token function\">paintChild</span><span class=\"token punctuation\">(</span>i<span class=\"token punctuation\">,</span>\n            transform<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Matrix4<span class=\"token punctuation\">.</span>translationValues</span><span class=\"token punctuation\">(</span>\n                x<span class=\"token punctuation\">,</span> y<span class=\"token punctuation\">,</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        x <span class=\"token operator\">=</span> w <span class=\"token operator\">+</span> margin<span class=\"token punctuation\">.</span>left<span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n        x <span class=\"token operator\">=</span> margin<span class=\"token punctuation\">.</span>left<span class=\"token punctuation\">;</span>\n        y <span class=\"token operator\">+=</span> context<span class=\"token punctuation\">.</span><span class=\"token function\">getChildSize</span><span class=\"token punctuation\">(</span>i<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>height <span class=\"token operator\">+</span> margin<span class=\"token punctuation\">.</span>top <span class=\"token operator\">+</span> margin<span class=\"token punctuation\">.</span>bottom<span class=\"token punctuation\">;</span>\n        <span class=\"token comment\">//绘制子widget(有优化)  </span>\n        context<span class=\"token punctuation\">.</span><span class=\"token function\">paintChild</span><span class=\"token punctuation\">(</span>i<span class=\"token punctuation\">,</span>\n            transform<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Matrix4<span class=\"token punctuation\">.</span>translationValues</span><span class=\"token punctuation\">(</span>\n                x<span class=\"token punctuation\">,</span> y<span class=\"token punctuation\">,</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n         x <span class=\"token operator\">+=</span> context<span class=\"token punctuation\">.</span><span class=\"token function\">getChildSize</span><span class=\"token punctuation\">(</span>i<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>width <span class=\"token operator\">+</span> margin<span class=\"token punctuation\">.</span>left <span class=\"token operator\">+</span> margin<span class=\"token punctuation\">.</span>right<span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token function\">getSize</span><span class=\"token punctuation\">(</span>BoxConstraints constraints<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//指定Flow的大小  </span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Size</span><span class=\"token punctuation\">(</span>double<span class=\"token punctuation\">.</span>infinity<span class=\"token punctuation\">,</span><span class=\"token number\">200.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  bool <span class=\"token function\">shouldRepaint</span><span class=\"token punctuation\">(</span>FlowDelegate oldDelegate<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> oldDelegate <span class=\"token operator\">!=</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行效果见图4-8：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图4-8\"></p> <p>可以看到我们主要的任务就是实现<code>paintChildren</code>，它的主要任务是确定每个子widget位置。由于Flow不能自适应子widget的大小，我们通过在<code>getSize</code>返回一个固定大小来指定Flow的大小。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter4/flex.html\" class=\"prev\">\n        4.3 弹性布局（Flex）\n      </a></span> <span class=\"next\"><a href=\"/chapter4/stack.html\">\n        4.5 层叠布局 Stack、Positioned\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/44.5f61dab7.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter5/clip.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>5.7 剪裁（Clip） | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/64.4621481d.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable open\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" aria-current=\"page\" class=\"active sidebar-link\">5.7 剪裁（Clip）</a><ul class=\"sidebar-sub-headers\"></ul></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_5-7-剪裁-clip\"><a href=\"#_5-7-剪裁-clip\" class=\"header-anchor\">#</a> 5.7 剪裁（Clip）</h1> <p>Flutter中提供了一些剪裁函数，用于对组件进行剪裁。</p> <table><thead><tr><th>剪裁Widget</th> <th>作用</th></tr></thead> <tbody><tr><td>ClipOval</td> <td>子组件为正方形时剪裁为内贴圆形，为矩形时，剪裁为内贴椭圆</td></tr> <tr><td>ClipRRect</td> <td>将子组件剪裁为圆角矩形</td></tr> <tr><td>ClipRect</td> <td>剪裁子组件到实际占用的矩形大小（溢出部分剪裁）</td></tr></tbody></table> <p>下面看一个例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">ClipTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 头像  </span>\n    Widget avatar <span class=\"token operator\">=</span> Image<span class=\"token punctuation\">.</span><span class=\"token function\">asset</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;imgs/avatar.png&quot;</span><span class=\"token punctuation\">,</span> width<span class=\"token punctuation\">:</span> <span class=\"token number\">60.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n        children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n          avatar<span class=\"token punctuation\">,</span> <span class=\"token comment\">//不剪裁</span>\n          <span class=\"token function\">ClipOval</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">:</span> avatar<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//剪裁为圆形</span>\n          <span class=\"token function\">ClipRRect</span><span class=\"token punctuation\">(</span> <span class=\"token comment\">//剪裁为圆角矩形</span>\n            borderRadius<span class=\"token punctuation\">:</span> BorderRadius<span class=\"token punctuation\">.</span><span class=\"token function\">circular</span><span class=\"token punctuation\">(</span><span class=\"token number\">5.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> avatar<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> \n          <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n            mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n            children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n              <span class=\"token function\">Align</span><span class=\"token punctuation\">(</span>\n                alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>topLeft<span class=\"token punctuation\">,</span>\n                widthFactor<span class=\"token punctuation\">:</span> <span class=\"token number\">.5</span><span class=\"token punctuation\">,</span><span class=\"token comment\">//宽度设为原来宽度一半，另一半会溢出</span>\n                child<span class=\"token punctuation\">:</span> avatar<span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;你好世界&quot;</span><span class=\"token punctuation\">,</span> style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>green<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n            mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n            children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n              <span class=\"token function\">ClipRect</span><span class=\"token punctuation\">(</span><span class=\"token comment\">//将溢出部分剪裁</span>\n                child<span class=\"token punctuation\">:</span> <span class=\"token function\">Align</span><span class=\"token punctuation\">(</span>\n                  alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>topLeft<span class=\"token punctuation\">,</span>\n                  widthFactor<span class=\"token punctuation\">:</span> <span class=\"token number\">.5</span><span class=\"token punctuation\">,</span><span class=\"token comment\">//宽度设为原来宽度一半</span>\n                  child<span class=\"token punctuation\">:</span> avatar<span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;你好世界&quot;</span><span class=\"token punctuation\">,</span>style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>green<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行效果如图5-24所示：</p> <p><img src=\"/assets/img/5-24.f113edc6.png\" alt=\"图5-24\"></p> <p>上面示例代码注释比较详细，在此不再赘述。但值得一提的是最后的两个<code>Row</code>！它们通过<code>Align</code>设置<code>widthFactor</code>为0.5后，图片的实际宽度等于60×0.5，即原宽度一半，但此时图片溢出部分依然会显示，所以第一个“你好世界”会和图片的另一部分重合，为了剪裁掉溢出部分，我们在第二个<code>Row</code>中通过<code>ClipRect</code>将溢出部分剪裁掉了。</p> <h3 id=\"customclipper\"><a href=\"#customclipper\" class=\"header-anchor\">#</a> CustomClipper</h3> <p>如果我们想剪裁子组件的特定区域，比如，在上面示例的图片中，如果我们只想截取图片中部40×30像素的范围应该怎么做？这时我们可以使用<code>CustomClipper</code>来自定义剪裁区域，实现代码如下：</p> <p>首先，自定义一个<code>CustomClipper</code>：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">MyClipper</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">CustomClipper</span><span class=\"token operator\">&lt;</span>Rect<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Rect <span class=\"token function\">getClip</span><span class=\"token punctuation\">(</span>Size size<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Rect<span class=\"token punctuation\">.</span><span class=\"token function\">fromLTWH</span><span class=\"token punctuation\">(</span><span class=\"token number\">10.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">15.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">40.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">30.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  bool <span class=\"token function\">shouldReclip</span><span class=\"token punctuation\">(</span>CustomClipper<span class=\"token operator\">&lt;</span>Rect<span class=\"token operator\">&gt;</span> oldClipper<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><ul><li><code>getClip()</code>是用于获取剪裁区域的接口，由于图片大小是60×60，我们返回剪裁区域为<code>Rect.fromLTWH(10.0, 15.0, 40.0, 30.0)</code>，即图片中部40×30像素的范围。</li> <li><code>shouldReclip()</code> 接口决定是否重新剪裁。如果在应用中，剪裁区域始终不会发生变化时应该返回<code>false</code>，这样就不会触发重新剪裁，避免不必要的性能开销。如果剪裁区域会发生变化（比如在对剪裁区域执行一个动画），那么变化后应该返回<code>true</code>来重新执行剪裁。</li></ul> <p>然后，我们通过<code>ClipRect</code>来执行剪裁，为了看清图片实际所占用的位置，我们设置一个红色背景：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">DecoratedBox</span><span class=\"token punctuation\">(</span>\n  decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>\n    color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">ClipRect</span><span class=\"token punctuation\">(</span>\n      clipper<span class=\"token punctuation\">:</span> <span class=\"token function\">MyClipper</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//使用自定义的clipper</span>\n      child<span class=\"token punctuation\">:</span> avatar\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行效果如图5-25所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图5-25\"></p> <p>可以看到我们的剪裁成功了，但是图片所占用的空间大小仍然是60×60（红色区域），这是因为剪裁是在layout完成后的绘制阶段进行的，所以不会影响组件的大小，这和<code>Transform</code>原理是相似的。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter5/material_scaffold.html\" class=\"prev\">\n        5.6 Scaffold、TabBar、底部导航\n      </a></span> <span class=\"next\"><a href=\"/chapter6/intro.html\">\n        6.1 可滚动组件简介\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/64.4621481d.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter5/constrainedbox_and_sizebox.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>5.2 尺寸限制类容器 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/8.cb5899ca.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable open\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" aria-current=\"page\" class=\"active sidebar-link\">5.2 尺寸限制类容器</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/chapter5/constrainedbox_and_sizebox.html#_5-2-1-constrainedbox\" class=\"sidebar-link\">5.2.1 ConstrainedBox</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter5/constrainedbox_and_sizebox.html#_5-2-2-sizedbox\" class=\"sidebar-link\">5.2.2 SizedBox</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter5/constrainedbox_and_sizebox.html#_5-2-3-多重限制\" class=\"sidebar-link\">5.2.3 多重限制</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter5/constrainedbox_and_sizebox.html#_5-2-4-unconstrainedbox\" class=\"sidebar-link\">5.2.4 UnconstrainedBox</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter5/constrainedbox_and_sizebox.html#_5-2-4-其它尺寸限制类容器\" class=\"sidebar-link\">5.2.4 其它尺寸限制类容器</a></li></ul></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_5-2-尺寸限制类容器\"><a href=\"#_5-2-尺寸限制类容器\" class=\"header-anchor\">#</a> 5.2 尺寸限制类容器</h1> <p>尺寸限制类容器用于限制容器大小，Flutter中提供了多种这样的容器，如<code>ConstrainedBox</code>、<code>SizedBox</code>、<code>UnconstrainedBox</code>、<code>AspectRatio</code>等，本节将介绍一些常用的。</p> <h2 id=\"_5-2-1-constrainedbox\"><a href=\"#_5-2-1-constrainedbox\" class=\"header-anchor\">#</a> 5.2.1 ConstrainedBox</h2> <p><code>ConstrainedBox</code>用于对子组件添加额外的约束。例如，如果你想让子组件的最小高度是80像素，你可以使用<code>const BoxConstraints(minHeight: 80.0)</code>作为子组件的约束。</p> <h4 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h4> <p>我们先定义一个<code>redBox</code>，它是一个背景颜色为红色的盒子，不指定它的宽度和高度：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Widget redBox<span class=\"token operator\">=</span><span class=\"token function\">DecoratedBox</span><span class=\"token punctuation\">(</span>\n  decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>我们实现一个最小高度为50，宽度尽可能大的红色容器。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">ConstrainedBox</span><span class=\"token punctuation\">(</span>\n  constraints<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxConstraints</span><span class=\"token punctuation\">(</span>\n    minWidth<span class=\"token punctuation\">:</span> double<span class=\"token punctuation\">.</span>infinity<span class=\"token punctuation\">,</span> <span class=\"token comment\">//宽度尽可能大</span>\n    minHeight<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span> <span class=\"token comment\">//最小高度为50像素</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n      height<span class=\"token punctuation\">:</span> <span class=\"token number\">5.0</span><span class=\"token punctuation\">,</span> \n      child<span class=\"token punctuation\">:</span> redBox \n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行效果如图5-2所示：</p> <p><img src=\"/assets/img/5-2.40b01667.png\" alt=\"图5-2\"></p> <p>可以看到，我们虽然将Container的高度设置为5像素，但是最终却是50像素，这正是ConstrainedBox的最小高度限制生效了。如果将Container的高度设置为80像素，那么最终红色区域的高度也会是80像素，因为在此示例中，ConstrainedBox只限制了最小高度，并未限制最大高度。</p> <h4 id=\"boxconstraints\"><a href=\"#boxconstraints\" class=\"header-anchor\">#</a> BoxConstraints</h4> <p>BoxConstraints用于设置限制条件，它的定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">const</span> <span class=\"token function\">BoxConstraints</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>minWidth <span class=\"token operator\">=</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//最小宽度</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>maxWidth <span class=\"token operator\">=</span> double<span class=\"token punctuation\">.</span>infinity<span class=\"token punctuation\">,</span> <span class=\"token comment\">//最大宽度</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>minHeight <span class=\"token operator\">=</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//最小高度</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>maxHeight <span class=\"token operator\">=</span> double<span class=\"token punctuation\">.</span>infinity <span class=\"token comment\">//最大高度</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>BoxConstraints还定义了一些便捷的构造函数，用于快速生成特定限制规则的BoxConstraints，如<code>BoxConstraints.tight(Size size)</code>，它可以生成给定大小的限制；<code>const BoxConstraints.expand()</code>可以生成一个尽可能大的用以填充另一个容器的BoxConstraints。除此之外还有一些其它的便捷函数，读者可以查看<a href=\"https://docs.flutter.io/flutter/rendering/BoxConstraints-class.html\" target=\"_blank\" rel=\"noopener noreferrer\">API文档<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>。</p> <h2 id=\"_5-2-2-sizedbox\"><a href=\"#_5-2-2-sizedbox\" class=\"header-anchor\">#</a> 5.2.2 SizedBox</h2> <p><code>SizedBox</code>用于给子元素指定固定的宽高，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span>\n  width<span class=\"token punctuation\">:</span> <span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span>\n  height<span class=\"token punctuation\">:</span> <span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> redBox\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行效果如图5-3所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图5-3\"></p> <p>实际上<code>SizedBox</code>只是<code>ConstrainedBox</code>的一个定制，上面代码等价于：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">ConstrainedBox</span><span class=\"token punctuation\">(</span>\n  constraints<span class=\"token punctuation\">:</span> BoxConstraints<span class=\"token punctuation\">.</span><span class=\"token function\">tightFor</span><span class=\"token punctuation\">(</span>width<span class=\"token punctuation\">:</span> <span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span>height<span class=\"token punctuation\">:</span> <span class=\"token number\">80.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> redBox<span class=\"token punctuation\">,</span> \n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>而<code>BoxConstraints.tightFor(width: 80.0,height: 80.0)</code>等价于：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">BoxConstraints</span><span class=\"token punctuation\">(</span>minHeight<span class=\"token punctuation\">:</span> <span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span>maxHeight<span class=\"token punctuation\">:</span> <span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span>minWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span>maxWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">80.0</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>而实际上<code>ConstrainedBox</code>和<code>SizedBox</code>都是通过<code>RenderConstrainedBox</code>来渲染的，我们可以看到<code>ConstrainedBox</code>和<code>SizedBox</code>的<code>createRenderObject()</code>方法都返回的是一个<code>RenderConstrainedBox</code>对象：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token metadata symbol\">@override</span>\nRenderConstrainedBox <span class=\"token function\">createRenderObject</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">RenderConstrainedBox</span><span class=\"token punctuation\">(</span>\n    additionalConstraints<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h2 id=\"_5-2-3-多重限制\"><a href=\"#_5-2-3-多重限制\" class=\"header-anchor\">#</a> 5.2.3 多重限制</h2> <p>如果某一个组件有多个父级<code>ConstrainedBox</code>限制，那么最终会是哪个生效？我们看一个例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">ConstrainedBox</span><span class=\"token punctuation\">(</span>\n    constraints<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxConstraints</span><span class=\"token punctuation\">(</span>minWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">60.0</span><span class=\"token punctuation\">,</span> minHeight<span class=\"token punctuation\">:</span> <span class=\"token number\">60.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//父</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">ConstrainedBox</span><span class=\"token punctuation\">(</span>\n      constraints<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxConstraints</span><span class=\"token punctuation\">(</span>minWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">90.0</span><span class=\"token punctuation\">,</span> minHeight<span class=\"token punctuation\">:</span> <span class=\"token number\">20.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token comment\">//子</span>\n      child<span class=\"token punctuation\">:</span> redBox<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>上面我们有父子两个<code>ConstrainedBox</code>，他们的限制条件不同，运行后效果如图5-4所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图5-4\"></p> <p>最终显示效果是宽90，高60，也就是说是子<code>ConstrainedBox</code>的<code>minWidth</code>生效，而<code>minHeight</code>是父<code>ConstrainedBox</code>生效。单凭这个例子，我们还总结不出什么规律，我们将上例中父子限制条件换一下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">ConstrainedBox</span><span class=\"token punctuation\">(</span>\n    constraints<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxConstraints</span><span class=\"token punctuation\">(</span>minWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">90.0</span><span class=\"token punctuation\">,</span> minHeight<span class=\"token punctuation\">:</span> <span class=\"token number\">20.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">ConstrainedBox</span><span class=\"token punctuation\">(</span>\n      constraints<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxConstraints</span><span class=\"token punctuation\">(</span>minWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">60.0</span><span class=\"token punctuation\">,</span> minHeight<span class=\"token punctuation\">:</span> <span class=\"token number\">60.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> redBox<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行效果如图5-5所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图5-5\"></p> <p>最终的显示效果仍然是90，高60，效果相同，但意义不同，因为此时<code>minWidth</code>生效的是父<code>ConstrainedBox</code>，而<code>minHeight</code>是子<code>ConstrainedBox</code>生效。</p> <p>通过上面示例，我们发现有多重限制时，对于<code>minWidth</code>和<code>minHeight</code>来说，是取父子中相应数值较大的。实际上，只有这样才能保证父限制与子限制不冲突。</p> <blockquote><p>思考题：对于<code>maxWidth</code>和<code>maxHeight</code>，多重限制的策略是什么样的呢？</p></blockquote> <h2 id=\"_5-2-4-unconstrainedbox\"><a href=\"#_5-2-4-unconstrainedbox\" class=\"header-anchor\">#</a> 5.2.4 UnconstrainedBox</h2> <p><code>UnconstrainedBox</code>不会对子组件产生任何限制，它允许其子组件按照其本身大小绘制。一般情况下，我们会很少直接使用此组件，但在&quot;去除&quot;多重限制的时候也许会有帮助，我们看下下面的代码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">ConstrainedBox</span><span class=\"token punctuation\">(</span>\n    constraints<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxConstraints</span><span class=\"token punctuation\">(</span>minWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">60.0</span><span class=\"token punctuation\">,</span> minHeight<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>  <span class=\"token comment\">//父</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">UnconstrainedBox</span><span class=\"token punctuation\">(</span> <span class=\"token comment\">//“去除”父级限制</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">ConstrainedBox</span><span class=\"token punctuation\">(</span>\n        constraints<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxConstraints</span><span class=\"token punctuation\">(</span>minWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">90.0</span><span class=\"token punctuation\">,</span> minHeight<span class=\"token punctuation\">:</span> <span class=\"token number\">20.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token comment\">//子</span>\n        child<span class=\"token punctuation\">:</span> redBox<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>上面代码中，如果没有中间的<code>UnconstrainedBox</code>，那么根据上面所述的多重限制规则，那么最终将显示一个90×100的红色框。但是由于<code>UnconstrainedBox</code> “去除”了父<code>ConstrainedBox</code>的限制，则最终会按照子<code>ConstrainedBox</code>的限制来绘制<code>redBox</code>，即90×20：</p> <p><img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJYAAABkCAYAAABkW8nwAAAMJWlDQ1BJQ0MgUHJvZmlsZQAASImVVwdUk8kWnr8kISGhBUKREnoTpUiXGloEAamCjZAEEkoMgSBiBVlUYC2oWLCiqyKKrgWQxYZdWRTsdUFERVlFXWyovEkC6LqvnHfPmZnv3Ln3znennRkAVCM5YnEGqgZApihHEhXsz5yckMgkPQIo0AbKgATGcrjZYr/IyDAAZbj9u7y7CRBZe81OFuuf/f9V1Hn8bC4ASCTEybxsbibEhwHAXbhiSQ4AhF6oN52VI4aYCFkCTQkkCLGZDKcqsJsMJytwmNwmJooFcRIASlQOR5IKgIqMFzOXmwrjqJRBbC/iCUUQN0HszRVweBB/hnh0ZuZMiFWtILZK/i5O6t9iJo/E5HBSR7AiF7koBQizxRmc2f/ndPxvycyQDo9hCgtVIAmJkuUsm7f0maEyTIX4gig5PAJiDYivC3lyexl+IpCGxA7Zf+Bms+CcAQYAKJXHCQiFWB9iE2l6rN8Q9uZI5L7QHk3MF8TEK+KjIsnMqKH4aL4oIzxsKE6ZgM8exlX87MDoYZsUYRAbYriGaIMwhx0zFPNCrjAuHGIViO9np0eHDvk+zxewwkfGkkbJOMM1x0Bm9nAumFmKJChKYY+5CITs8CF9WI4gJkThi03ncuQcdCBO42dPDhvmw+MHBCr4YIV8UewQT6xcnOMfNWS/Q5wROWSPNfEzgmV6E4hbs3Ojh337cuBmU+SCgzTOhEjFuLimOCcyRsENZ4IwwAIBgAmksCSDmSANCFt763vBcE8Q4AAJSAV8YDekGfaIl/eIYB0N8sGfEPFB9oifv7yXD3Kh/suIVlHbgRR5b67cIx08gTgT18O9cU88DNa+sDjibrj7sB9TdXhUYiAxgBhCDCJazxAWSn6IywRcmEEGLBIQCls+zErGQTTM/VscwhNCG+ER4Qahg3AHxIHH0E74jwy/RROO6CaCDhg1aCi75O+zwy0ga2fcH/eC/CF3nIHrATt8HMzED/eBuTlD7bdZ+3fcpcOsyfZklKxN9iVb/WinYqPiPOIjy+17ngpeySOZsEZ6fhyN9V1uPNiG/miJLcEOYeexU9hFrAmrB0zsBNaAtWDHZHhkbzyW743h0aLkfNJhHOGwjX2NfY/95x/G5gyNL5GvP8jh5+XIDg5rpni2RJgqyGH6wduaz2SLuGNGMx3tHeAtKrv7FVfLW4b8TkcYl77pCuYAMP7Q4OBg0zddGLxZjhgAQHn1TWf1Hh5newAuFHClklyFDpdVBEABqvCk6AJDeHdZwYwcgQvwBL4gEEwAESAGJIDpcJ4FIBOyngXmggJQDErBCrAGbABbwHawG+wDB0E9aAKnwDlwGVwFN8A9uFe6wQvQB96BAQRBSAgNoSO6iBFijtgijogb4o0EImFIFJKAJCGpiAiRInORRUgpUo5sQLYh1civyFHkFHIRaUPuIJ1ID/IG+YRiKBXVRA1QC3Qs6ob6oaFoDDoNTUWz0Hy0CF2GrkOr0L1oHXoKvYzeQDvQF2g/BjBljIEZY3aYG8bCIrBELAWTYPOxEqwCq8JqsUa40tewDqwX+4gTcTrOxO3gfg3BY3EunoXPx8vwDfhuvA4/g1/DO/E+/CuBRtAn2BI8CGzCZEIqYRahmFBB2Ek4QjgLz1Q34R2RSGQQLYmu8KwmENOIc4hlxE3E/cSTxDZiF7GfRCLpkmxJXqQIEoeUQyomrSftJZ0gtZO6SR+UlJWMlByVgpQSlURKhUoVSnuUjiu1Kz1VGiCrkc3JHuQIMo88m7ycvIPcSL5C7iYPUNQplhQvSgwljVJAWUeppZyl3Ke8VVZWNlF2V56kLFReqLxO+YDyBeVO5Y9UDaoNlUWdSpVSl1F3UU9S71Df0mg0C5ovLZGWQ1tGq6adpj2kfVChq4xRYavwVBaoVKrUqbSrvFQlq5qr+qlOV81XrVA9pHpFtVeNrGahxlLjqM1Xq1Q7qnZLrV+dru6gHqGeqV6mvkf9ovozDZKGhUagBk+jSGO7xmmNLjpGN6Wz6Fz6IvoO+ll6tyZR01KTrZmmWaq5T7NVs09LQ2ucVpxWnlal1jGtDgbGsGCwGRmM5YyDjJuMT9oG2n7afO2l2rXa7drvdUbp+OrwdUp09uvc0Pmky9QN1E3XXalbr/tAD9ez0ZukN0tvs95Zvd5RmqM8R3FHlYw6OOquPqpvox+lP0d/u36Lfr+BoUGwgdhgvcFpg15DhqGvYZrhasPjhj1GdCNvI6HRaqMTRs+ZWkw/ZgZzHfMMs89Y3zjEWGq8zbjVeMDE0iTWpNBkv8kDU4qpm2mK6WrTZtM+MyOziWZzzWrM7pqTzd3MBeZrzc+bv7ewtIi3WGxRb/HMUseSbZlvWWN534pm5WOVZVVldd2aaO1mnW69yfqqDWrjbCOwqbS5YovautgKbTfZto0mjHYfLRpdNfqWHdXOzy7XrsaucwxjTNiYwjH1Y16ONRubOHbl2PNjv9o722fY77C/56DhMMGh0KHR4Y2jjSPXsdLxuhPNKchpgVOD0+txtuP44zaPu+1Md57ovNi52fmLi6uLxKXWpcfVzDXJdaPrLTdNt0i3MrcL7gR3f/cF7k3uHz1cPHI8Dnq88rTzTPfc4/lsvOV4/vgd47u8TLw4Xtu8OryZ3kneW707fIx9OD5VPo98TX15vjt9n/pZ+6X57fV76W/vL/E/4v+e5cGaxzoZgAUEB5QEtAZqBMYGbgh8GGQSlBpUE9QX7Bw8J/hkCCEkNGRlyC22AZvLrmb3TXCdMG/CmVBqaHTohtBHYTZhkrDGiejECRNXTbwfbh4uCq+PABHsiFURDyItI7Mif5tEnBQ5qXLSkyiHqLlR56Pp0TOi90S/i/GPWR5zL9YqVhrbHKcaNzWuOu59fEB8eXzH5LGT502+nKCXIExoSCQlxiXuTOyfEjhlzZTuqc5Ti6fenGY5LW/axel60zOmH5uhOoMz41ASISk+aU/SZ04Ep4rTn8xO3pjcx2Vx13Jf8Hx5q3k9fC9+Of9pildKecqzVK/UVak9Ah9BhaBXyBJuEL5OC0nbkvY+PSJ9V/pgRnzG/kylzKTMoyINUbrozEzDmXkz28S24mJxR5ZH1pqsPkmoZGc2kj0tuyFHEz6yW6RW0p+knbneuZW5H2bFzTqUp54nymuZbTN76eyn+UH5v8zB53DnNM81nlswt3Oe37xt85H5yfObF5guKFrQvTB44e4CSkF6we+F9oXlhX8til/UWGRQtLCo66fgn2qKVYolxbcWey7esgRfIlzSutRp6fqlX0t4JZdK7UsrSj+Xccsu/ezw87qfB5elLGtd7rJ88wriCtGKmyt9Vu4uVy/PL+9aNXFV3Wrm6pLVf62ZseZixbiKLWspa6VrO9aFrWtYb7Z+xfrPGwQbblT6V+7fqL9x6cb3m3ib2jf7bq7dYrCldMunrcKtt7cFb6ursqiq2E7cnrv9yY64Hed/cfuleqfeztKdX3aJdnXsjtp9ptq1unqP/p7lNWiNtKZn79S9V/cF7Guotavdtp+xv/QAOCA98PzXpF9vHgw92HzI7VDtYfPDG4/Qj5TUIXWz6/rqBfUdDQkNbUcnHG1u9Gw88tuY33Y1GTdVHtM6tvw45XjR8cET+Sf6T4pP9p5KPdXVPKP53unJp6+fmXSm9Wzo2Qvngs6dPu93/sQFrwtNFz0uHr3kdqn+ssvluhbnliO/O/9+pNWlte6K65WGq+5XG9vGtx1v92k/dS3g2rnr7OuXb4TfaLsZe/P2ram3Om7zbj+7k3Hn9d3cuwP3Ft4n3C95oPag4qH+w6o/rP/Y3+HScawzoLPlUfSje13crhePsx9/7i56QntS8dToafUzx2dNPUE9V59Ped79QvxioLf4T/U/N760enn4le+rlr7Jfd2vJa8H35S91X27669xfzX3R/Y/fJf5buB9yQfdD7s/un08/yn+09OBWZ9Jn9d9sf7S+DX06/3BzMFBMUfCkT8FMFjQlBQA3uwCgJYAAP0qfD9MUfzN5IIo/pNyBP4TVvzf5OICQC1sZM9w1kkADsBi6QtjwyJ7jsf4AtTJaaQMSXaKk6MiFhX+cAgfBgffwncMqRGAL5LBwYFNg4NfdkCydwA4maX4E8pE9gfdKo/RzshbCH6QfwEOk3CtyyHjHgAAAAlwSFlzAAAWJQAAFiUBSVIk8AAAAnVJREFUeAHt27FNq0EQhVH7QQ28loioh4iAiB4RBUAJv5CMIJ/Ew9Wg0XG40s56vz2SI5+P47icfBT45QL/fnmecQr8FAALhEgBsCJZDQWLgUgBsCJZDQWLgUgBsCJZDQWLgUgBsCJZDQWLgUgBsCJZDQWLgUgBsCJZDQWLgUgBsCJZDQWLgUgBsCJZDQWLgUgBsCJZDQWLgUgBsCJZDQWLgUgBsCJZDQWLgUgBsCJZDQWLgUgBsCJZDQWLgUgBsCJZDQWLgUgBsCJZDQWLgUgBsCJZDQWLgUgBsCJZDQWLgUgBsCJZDQWLgUgBsCJZDQWLgUgBsCJZDQWLgUgBsCJZDQWLgUgBsCJZDQWLgUgBsCJZDQWLgUgBsCJZDQWLgUgBsCJZDb3tJLh8vJ8+nx87I+z9owVun15O57v/V3+7FqzvUy9vr1cfbuPeAn4K977t6M3AGs2/93Cw9r7t6M3AGs2/93Cw9r7t6M3AGs2/93Cw9r7t6M3AGs2/93Cw9r7t6M3AGs2/93Cw9r7t6M3AGs2/93Cw9r7t6M3AGs2/93Cw9r7t6M3AGs2/93Cw9r7t6M3AGs2/93Cw9r7t6M3AGs2/93Cw9r7t6M3AGs2/93Cw9r7t6M3AGs2/9/D2H1Zv7h/21nGzqwucj+O4XL3bRgWKAn4KizCWewXA6vWzuygAVhHGcq8AWL1+dhcFwCrCWO4VAKvXz+6iAFhFGMu9AmD1+tldFACrCGO5VwCsXj+7iwJgFWEs9wqA1etnd1EArCKM5V4BsHr97C4KgFWEsdwrAFavn91FAbCKMJZ7BcDq9bO7KABWEcZyrwBYvX52FwW+ACD6EfccMWb6AAAAAElFTkSuQmCC\" alt=\"图5-6\"></p> <p>但是，读者请注意，<code>UnconstrainedBox</code>对父组件限制的“去除”并非是真正的去除：上面例子中虽然红色区域大小是90×20，但上方仍然有80的空白空间。也就是说父限制的<code>minHeight</code>(100.0)仍然是生效的，只不过它不影响最终子元素<code>redBox</code>的大小，但仍然还是占有相应的空间，可以认为此时的父<code>ConstrainedBox</code>是作用于子<code>UnconstrainedBox</code>上，而<code>redBox</code>只受子<code>ConstrainedBox</code>限制，这一点请读者务必注意。</p> <p>那么有什么方法可以彻底去除父<code>ConstrainedBox</code>的限制吗？答案是否定的！所以在此提示读者，在定义一个通用的组件时，如果要对子组件指定限制，那么一定要注意，因为一旦指定限制条件，子组件如果要进行相关自定义大小时将可能非常困难，因为子组件在不更改父组件的代码的情况下无法彻底去除其限制条件。</p> <p>在实际开发中，当我们发现已经使用<code>SizedBox</code>或<code>ConstrainedBox</code>给子元素指定了宽高，但是仍然没有效果时，几乎可以断定：已经有父元素已经设置了限制！举个例子，如Material组件库中的<code>AppBar</code>（导航栏）的右侧菜单中，我们使用<code>SizedBox</code>指定了loading按钮的大小，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>\n   title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n   actions<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n         <span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span>\n             width<span class=\"token punctuation\">:</span> <span class=\"token number\">20</span><span class=\"token punctuation\">,</span> \n             height<span class=\"token punctuation\">:</span> <span class=\"token number\">20</span><span class=\"token punctuation\">,</span>\n             child<span class=\"token punctuation\">:</span> <span class=\"token function\">CircularProgressIndicator</span><span class=\"token punctuation\">(</span>\n                 strokeWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">3</span><span class=\"token punctuation\">,</span>\n                 valueColor<span class=\"token punctuation\">:</span> <span class=\"token function\">AlwaysStoppedAnimation</span><span class=\"token punctuation\">(</span>Colors<span class=\"token punctuation\">.</span>white70<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n             <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n         <span class=\"token punctuation\">)</span>\n   <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>上面代码运行后，效果如图5-7所示：</p> <p><img src=\"/assets/img/5-7.27479289.png\" alt=\"图5-6\"></p> <p>我们会发现右侧loading按钮大小并没有发生变化！这正是因为<code>AppBar</code>中已经指定了<code>actions</code>按钮的限制条件，所以我们要自定义loading按钮大小，就必须通过<code>UnconstrainedBox</code>来“去除”父元素的限制，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>\n  title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  actions<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n      <span class=\"token function\">UnconstrainedBox</span><span class=\"token punctuation\">(</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span>\n              width<span class=\"token punctuation\">:</span> <span class=\"token number\">20</span><span class=\"token punctuation\">,</span>\n              height<span class=\"token punctuation\">:</span> <span class=\"token number\">20</span><span class=\"token punctuation\">,</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">CircularProgressIndicator</span><span class=\"token punctuation\">(</span>\n                strokeWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">3</span><span class=\"token punctuation\">,</span>\n                valueColor<span class=\"token punctuation\">:</span> <span class=\"token function\">AlwaysStoppedAnimation</span><span class=\"token punctuation\">(</span>Colors<span class=\"token punctuation\">.</span>white70<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行后效果如图5-8所示：</p> <p><img src=\"/assets/img/5-8.5b913c51.png\" alt=\"图5-8\"></p> <p>生效了！</p> <h2 id=\"_5-2-4-其它尺寸限制类容器\"><a href=\"#_5-2-4-其它尺寸限制类容器\" class=\"header-anchor\">#</a> 5.2.4 其它尺寸限制类容器</h2> <p>除了上面介绍的这些常用的尺寸限制类容器外，还有一些其他的尺寸限制类容器，比如<code>AspectRatio</code>，它可以指定子组件的长宽比、<code>LimitedBox</code> 用于指定最大宽高、<code>FractionallySizedBox</code> 可以根据父容器宽高的百分比来设置子组件宽高等，由于这些容器使用起来都比较简单，我们便不再赘述，读者可以自行了解。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter5/padding.html\" class=\"prev\">\n        5.1 填充（Padding）\n      </a></span> <span class=\"next\"><a href=\"/chapter5/decoratedbox.html\">\n        5.3 装饰容器DecoratedBox\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/8.cb5899ca.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter5/container.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>5.5 Container | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/65.2d58a524.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable open\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" aria-current=\"page\" class=\"active sidebar-link\">5.5 Container</a><ul class=\"sidebar-sub-headers\"></ul></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_5-5-container\"><a href=\"#_5-5-container\" class=\"header-anchor\">#</a> 5.5 Container</h1> <p>我们在前面的章节示例中多次用到过<code>Container</code>组件，本节我们就详细介绍一下<code>Container</code>组件。<code>Container</code>是一个组合类容器，它本身不对应具体的<code>RenderObject</code>，它是<code>DecoratedBox</code>、<code>ConstrainedBox、Transform</code>、<code>Padding</code>、<code>Align</code>等组件组合的一个多功能容器，所以我们只需通过一个<code>Container</code>组件可以实现同时需要装饰、变换、限制的场景。下面是<code>Container</code>的定义：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Container</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>alignment<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>padding<span class=\"token punctuation\">,</span> <span class=\"token comment\">//容器内补白，属于decoration的装饰范围</span>\n  Color color<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 背景色</span>\n  Decoration decoration<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 背景装饰</span>\n  Decoration foregroundDecoration<span class=\"token punctuation\">,</span> <span class=\"token comment\">//前景装饰</span>\n  double width<span class=\"token punctuation\">,</span><span class=\"token comment\">//容器的宽度</span>\n  double height<span class=\"token punctuation\">,</span> <span class=\"token comment\">//容器的高度</span>\n  BoxConstraints constraints<span class=\"token punctuation\">,</span> <span class=\"token comment\">//容器大小的限制条件</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>margin<span class=\"token punctuation\">,</span><span class=\"token comment\">//容器外补白，不属于decoration的装饰范围</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>transform<span class=\"token punctuation\">,</span> <span class=\"token comment\">//变换</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p><code>Container</code>的大多数属性在介绍其它容器时都已经介绍过了，不再赘述，但有两点需要说明：</p> <ul><li>容器的大小可以通过<code>width</code>、<code>height</code>属性来指定，也可以通过<code>constraints</code>来指定；如果它们同时存在时，<code>width</code>、<code>height</code>优先。实际上Container内部会根据<code>width</code>、<code>height</code>来生成一个<code>constraints</code>。</li> <li><code>color</code>和<code>decoration</code>是互斥的，如果同时设置它们则会报错！实际上，当指定<code>color</code>时，<code>Container</code>内会自动创建一个<code>decoration</code>。</li></ul> <h3 id=\"实例\"><a href=\"#实例\" class=\"header-anchor\">#</a> 实例</h3> <p>我们通过<code>Container</code>来实现如图5-16所示的卡片：</p> <p><img src=\"/assets/img/5-16.24d30a6e.png\" alt=\"图5-16\"></p> <p>实现代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n  margin<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">only</span><span class=\"token punctuation\">(</span>top<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span> left<span class=\"token punctuation\">:</span> <span class=\"token number\">120.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//容器外填充</span>\n  constraints<span class=\"token punctuation\">:</span> BoxConstraints<span class=\"token punctuation\">.</span><span class=\"token function\">tightFor</span><span class=\"token punctuation\">(</span>width<span class=\"token punctuation\">:</span> <span class=\"token number\">200.0</span><span class=\"token punctuation\">,</span> height<span class=\"token punctuation\">:</span> <span class=\"token number\">150.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//卡片大小</span>\n  decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span><span class=\"token comment\">//背景装饰</span>\n      gradient<span class=\"token punctuation\">:</span> <span class=\"token function\">RadialGradient</span><span class=\"token punctuation\">(</span> <span class=\"token comment\">//背景径向渐变</span>\n          colors<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span> Colors<span class=\"token punctuation\">.</span>orange<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n          center<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>topLeft<span class=\"token punctuation\">,</span>\n          radius<span class=\"token punctuation\">:</span> <span class=\"token number\">.98</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      boxShadow<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span> <span class=\"token comment\">//卡片阴影</span>\n        <span class=\"token function\">BoxShadow</span><span class=\"token punctuation\">(</span>\n            color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>black54<span class=\"token punctuation\">,</span>\n            offset<span class=\"token punctuation\">:</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token number\">2.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">2.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            blurRadius<span class=\"token punctuation\">:</span> <span class=\"token number\">4.0</span>\n        <span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">]</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  transform<span class=\"token punctuation\">:</span> Matrix4<span class=\"token punctuation\">.</span><span class=\"token function\">rotationZ</span><span class=\"token punctuation\">(</span><span class=\"token number\">.2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//卡片倾斜变换</span>\n  alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span> <span class=\"token comment\">//卡片内文字居中</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span> <span class=\"token comment\">//卡片文字</span>\n    <span class=\"token string\">&quot;5.20&quot;</span><span class=\"token punctuation\">,</span> style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">,</span> fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">40.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>可以看到<code>Container</code>具备多种组件的功能，通过查看<code>Container</code>源码，我们会很容易发现它正是前面我们介绍过的多种组件组合而成。在Flutter中，<code>Container</code>组件也正是组合优先于继承的实例。</p> <h3 id=\"padding和margin\"><a href=\"#padding和margin\" class=\"header-anchor\">#</a> Padding和Margin</h3> <p>接下来我们来研究一下<code>Container</code>组件<code>margin</code>和<code>padding</code>属性的区别:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n  margin<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">20.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//容器外补白</span>\n  color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>orange<span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hello world!&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n  padding<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">20.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//容器内补白</span>\n  color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>orange<span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hello world!&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n</code></pre></div><p><img src=\"data:image/png;base64,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\" alt=\"图5-17\"></p> <p>可以发现，直观的感觉就是<code>margin</code>的留白是在容器外部，而<code>padding</code>的留白是在容器内部，读者需要记住这个差异。事实上，<code>Container</code>内<code>margin</code>和<code>padding</code>都是通过<code>Padding</code> 组件来实现的，上面的示例代码实际上等价于：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n  padding<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">20.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">DecoratedBox</span><span class=\"token punctuation\">(</span>\n    decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>orange<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hello world!&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token function\">DecoratedBox</span><span class=\"token punctuation\">(</span>\n  decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>orange<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n    padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">20.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hello world!&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>    \n</code></pre></div></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter5/transform.html\" class=\"prev\">\n        5.4 变换（Transform）\n      </a></span> <span class=\"next\"><a href=\"/chapter5/material_scaffold.html\">\n        5.6 Scaffold、TabBar、底部导航\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/65.2d58a524.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter5/decoratedbox.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>5.3 装饰容器DecoratedBox | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/99.3db30893.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable open\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" aria-current=\"page\" class=\"active sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_5-3-装饰容器decoratedbox\"><a href=\"#_5-3-装饰容器decoratedbox\" class=\"header-anchor\">#</a> 5.3 装饰容器DecoratedBox</h1> <p><code>DecoratedBox</code>可以在其子组件绘制前(或后)绘制一些装饰（Decoration），如背景、边框、渐变等。<code>DecoratedBox</code>定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">const</span> <span class=\"token function\">DecoratedBox</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  Decoration decoration<span class=\"token punctuation\">,</span>\n  DecorationPosition position <span class=\"token operator\">=</span> DecorationPosition<span class=\"token punctuation\">.</span>background<span class=\"token punctuation\">,</span>\n  Widget child\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><ul><li><code>decoration</code>：代表将要绘制的装饰，它的类型为<code>Decoration</code>。<code>Decoration</code>是一个抽象类，它定义了一个接口 <code>createBoxPainter()</code>，子类的主要职责是需要通过实现它来创建一个画笔，该画笔用于绘制装饰。</li> <li><code>position</code>：此属性决定在哪里绘制<code>Decoration</code>，它接收<code>DecorationPosition</code>的枚举类型，该枚举类有两个值：\n<ul><li><code>background</code>：在子组件之后绘制，即背景装饰。</li> <li><code>foreground</code>：在子组件之上绘制，即前景。</li></ul></li></ul> <h4 id=\"boxdecoration\"><a href=\"#boxdecoration\" class=\"header-anchor\">#</a> BoxDecoration</h4> <p>我们通常会直接使用<code>BoxDecoration</code>类，它是一个Decoration的子类，实现了常用的装饰元素的绘制。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  Color color<span class=\"token punctuation\">,</span> <span class=\"token comment\">//颜色</span>\n  DecorationImage image<span class=\"token punctuation\">,</span><span class=\"token comment\">//图片</span>\n  BoxBorder border<span class=\"token punctuation\">,</span> <span class=\"token comment\">//边框</span>\n  BorderRadiusGeometry borderRadius<span class=\"token punctuation\">,</span> <span class=\"token comment\">//圆角</span>\n  List<span class=\"token operator\">&lt;</span>BoxShadow<span class=\"token operator\">&gt;</span> boxShadow<span class=\"token punctuation\">,</span> <span class=\"token comment\">//阴影,可以指定多个</span>\n  Gradient gradient<span class=\"token punctuation\">,</span> <span class=\"token comment\">//渐变</span>\n  BlendMode backgroundBlendMode<span class=\"token punctuation\">,</span> <span class=\"token comment\">//背景混合模式</span>\n  BoxShape shape <span class=\"token operator\">=</span> BoxShape<span class=\"token punctuation\">.</span>rectangle<span class=\"token punctuation\">,</span> <span class=\"token comment\">//形状</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>各个属性名都是自解释的，详情读者可以查看API文档。下面我们实现一个带阴影的背景色渐变的按钮：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code> <span class=\"token function\">DecoratedBox</span><span class=\"token punctuation\">(</span>\n    decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>\n      gradient<span class=\"token punctuation\">:</span> <span class=\"token function\">LinearGradient</span><span class=\"token punctuation\">(</span>colors<span class=\"token punctuation\">:</span><span class=\"token punctuation\">[</span>Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span>Colors<span class=\"token punctuation\">.</span>orange<span class=\"token punctuation\">[</span><span class=\"token number\">700</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//背景渐变</span>\n      borderRadius<span class=\"token punctuation\">:</span> BorderRadius<span class=\"token punctuation\">.</span><span class=\"token function\">circular</span><span class=\"token punctuation\">(</span><span class=\"token number\">3.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//3像素圆角</span>\n      boxShadow<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span> <span class=\"token comment\">//阴影</span>\n        <span class=\"token function\">BoxShadow</span><span class=\"token punctuation\">(</span>\n            color<span class=\"token punctuation\">:</span>Colors<span class=\"token punctuation\">.</span>black54<span class=\"token punctuation\">,</span>\n            offset<span class=\"token punctuation\">:</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token number\">2.0</span><span class=\"token punctuation\">,</span><span class=\"token number\">2.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            blurRadius<span class=\"token punctuation\">:</span> <span class=\"token number\">4.0</span>\n        <span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">]</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>padding<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">symmetric</span><span class=\"token punctuation\">(</span>horizontal<span class=\"token punctuation\">:</span> <span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span> vertical<span class=\"token punctuation\">:</span> <span class=\"token number\">18.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Login&quot;</span><span class=\"token punctuation\">,</span> style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行后效果如图5-9所示：</p> <p><img src=\"/assets/img/5-9.47017753.png\" alt=\"图5-9\"></p> <p>怎么样，通过<code>BoxDecoration</code>我们实现了一个渐变按钮的外观，但此示例还不是一个标准的按钮，因为它还不能响应点击事件，我们将在后面“自定义组件”一章中实现一个完整功能的<code>GradientButton</code>。另外，上面的例子中使用了<code>LinearGradient</code>类，它用于定义线性渐变的类，Flutter中还提供了其它渐变配置类，如<code>RadialGradient</code>、<code>SweepGradient</code>，读者若有需要可以自行查看API文档。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"prev\">\n        5.2 尺寸限制类容器\n      </a></span> <span class=\"next\"><a href=\"/chapter5/transform.html\">\n        5.4 变换（Transform）\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/99.3db30893.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter5/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>容器类Widget | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/161.38ea1a1a.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"容器类widget\"><a href=\"#容器类widget\" class=\"header-anchor\">#</a> 容器类Widget</h1> <p>容器类Widget和布局类Widget都作用于其子Widget，不同的是：</p> <ul><li>布局类Widget一般都需要接收一个widget数组（children），他们直接或间接继承自（或包含）MultiChildRenderObjectWidget ；而容器类Widget一般只需要接收一个子Widget（child），他们直接或间接继承自（或包含）SingleChildRenderObjectWidget。</li> <li>布局类Widget是按照一定的排列方式来对其子Widget进行排列；而容器类Widget一般只是包装其子Widget，对其添加一些修饰（补白或背景色等）、变换(旋转或剪裁等)、或限制(大小等)。</li></ul> <p>注意，Flutter官方并没有对Widget进行官方分类，我们对其分类主要是为了方便讨论和对Widget功能区分的记忆。</p> <h2 id=\"本章目录\"><a href=\"#本章目录\" class=\"header-anchor\">#</a> 本章目录</h2> <ul><li><a href=\"/chapter5/padding.html\">5.1：填充（Padding）</a></li> <li><a href=\"/chapter5/constrainedbox_and_sizebox.html\">5.2：尺寸限制类容器（ConstrainedBox等）</a></li> <li><a href=\"/chapter5/decoratedbox.html\">5.3：装饰容器（DecoratedBox）</a></li> <li><a href=\"/chapter5/transform.html\">5.4：变换（Transform）</a></li> <li><a href=\"/chapter5/container.html\">5.5：Container容器</a></li> <li><a href=\"/chapter5/material_scaffold.html\">5.6：Scaffold、TabBar、底部导航</a></li> <li><a href=\"/chapter5/clip.html\">5.7：剪裁（Clip）</a></li></ul></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/161.38ea1a1a.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter5/material_scaffold.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>5.6 Scaffold、TabBar、底部导航 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/15.7f957f4b.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable open\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" aria-current=\"page\" class=\"active sidebar-link\">5.6 Scaffold、TabBar、底部导航</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/chapter5/material_scaffold.html#_5-6-1-scaffold\" class=\"sidebar-link\">5.6.1 Scaffold</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter5/material_scaffold.html#_5-6-2-appbar\" class=\"sidebar-link\">5.6.2 AppBar</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter5/material_scaffold.html#_5-6-3-抽屉菜单drawer\" class=\"sidebar-link\">5.6.3 抽屉菜单Drawer</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter5/material_scaffold.html#_5-6-4-floatingactionbutton\" class=\"sidebar-link\">5.6.4 FloatingActionButton</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter5/material_scaffold.html#_5-6-5-底部tab导航栏\" class=\"sidebar-link\">5.6.5  底部Tab导航栏</a></li></ul></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_5-6-scaffold、tabbar、底部导航\"><a href=\"#_5-6-scaffold、tabbar、底部导航\" class=\"header-anchor\">#</a> 5.6 Scaffold、TabBar、底部导航</h1> <p>Material组件库提供了丰富多样的组件，本节介绍一些常用的组件，其余的读者可以自行查看文档或Flutter Gallery中Material组件部分的示例。</p> <blockquote><p>Flutter Gallery是Flutter官方提供的Flutter Demo，源码位于flutter源码中的examples目录下，笔者强烈建议用户将Flutter Gallery示例跑起来，它是一个很全面的Flutter示例应用，是非常好的参考Demo，也是笔者学习Flutter的第一手资料。</p></blockquote> <h2 id=\"_5-6-1-scaffold\"><a href=\"#_5-6-1-scaffold\" class=\"header-anchor\">#</a> 5.6.1 Scaffold</h2> <p>一个完整的路由页可能会包含导航栏、抽屉菜单(Drawer)以及底部Tab导航菜单等。如果每个路由页面都需要开发者自己手动去实现这些，这会是一件非常麻烦且无聊的事。幸运的是，Flutter Material组件库提供了一些现成的组件来减少我们的开发任务。<code>Scaffold</code>是一个路由页的骨架，我们使用它可以很容易地拼装出一个完整的页面。</p> <h3 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h3> <p>我们实现一个页面，它包含：</p> <ol><li>一个导航栏</li> <li>导航栏右边有一个分享按钮</li> <li>有一个抽屉菜单</li> <li>有一个底部导航</li> <li>右下角有一个悬浮的动作按钮</li></ol> <p>最终效果如图5-18、图5-19所示：</p> <p><img src=\"/assets/img/5-18.f83914b2.png\" alt=\"图5-18\"> <img src=\"/assets/img/5-19.a2dab018.png\" alt=\"图5-19\"></p> <p>实现代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">ScaffoldRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _ScaffoldRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">_ScaffoldRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_ScaffoldRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>ScaffoldRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  int _selectedIndex <span class=\"token operator\">=</span> <span class=\"token number\">1</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n      appBar<span class=\"token punctuation\">:</span> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span> <span class=\"token comment\">//导航栏</span>\n        title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;App Name&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> \n        actions<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span> <span class=\"token comment\">//导航栏右侧菜单</span>\n          <span class=\"token function\">IconButton</span><span class=\"token punctuation\">(</span>icon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>share<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      drawer<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">MyDrawer</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//抽屉</span>\n      bottomNavigationBar<span class=\"token punctuation\">:</span> <span class=\"token function\">BottomNavigationBar</span><span class=\"token punctuation\">(</span> <span class=\"token comment\">// 底部导航</span>\n        items<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>BottomNavigationBarItem<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n          <span class=\"token function\">BottomNavigationBarItem</span><span class=\"token punctuation\">(</span>icon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>home<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Home'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">BottomNavigationBarItem</span><span class=\"token punctuation\">(</span>icon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>business<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Business'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">BottomNavigationBarItem</span><span class=\"token punctuation\">(</span>icon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>school<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'School'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        currentIndex<span class=\"token punctuation\">:</span> _selectedIndex<span class=\"token punctuation\">,</span>\n        fixedColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span>\n        onTap<span class=\"token punctuation\">:</span> _onItemTapped<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      floatingActionButton<span class=\"token punctuation\">:</span> <span class=\"token function\">FloatingActionButton</span><span class=\"token punctuation\">(</span> <span class=\"token comment\">//悬浮按钮</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>add<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          onPressed<span class=\"token punctuation\">:</span>_onAdd\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_onItemTapped</span><span class=\"token punctuation\">(</span>int index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _selectedIndex <span class=\"token operator\">=</span> index<span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_onAdd</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面代码中我们用到了如下组件：</p> <table><thead><tr><th>组件名称</th> <th>解释</th></tr></thead> <tbody><tr><td>AppBar</td> <td>一个导航栏骨架</td></tr> <tr><td>MyDrawer</td> <td>抽屉菜单</td></tr> <tr><td>BottomNavigationBar</td> <td>底部导航栏</td></tr> <tr><td>FloatingActionButton</td> <td>漂浮按钮</td></tr></tbody></table> <p>下面我们来分别介绍一下它们。</p> <h2 id=\"_5-6-2-appbar\"><a href=\"#_5-6-2-appbar\" class=\"header-anchor\">#</a> 5.6.2 AppBar</h2> <p><code>AppBar</code>是一个Material风格的导航栏，通过它可以设置导航栏标题、导航栏菜单、导航栏底部的Tab标题等。下面我们看看AppBar的定义：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  Key key<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>leading<span class=\"token punctuation\">,</span> <span class=\"token comment\">//导航栏最左侧Widget，常见为抽屉菜单按钮或返回按钮。</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>automaticallyImplyLeading <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//如果leading为null，是否自动实现默认的leading按钮</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>title<span class=\"token punctuation\">,</span><span class=\"token comment\">// 页面标题</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>actions<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 导航栏右侧菜单</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>bottom<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 导航栏底部菜单，通常为Tab按钮组</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>elevation <span class=\"token operator\">=</span> <span class=\"token number\">4.0</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 导航栏阴影</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>centerTitle<span class=\"token punctuation\">,</span> <span class=\"token comment\">//标题是否居中 </span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>backgroundColor<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>   <span class=\"token comment\">//其它属性见源码注释</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>如果给<code>Scaffold</code>添加了抽屉菜单，默认情况下<code>Scaffold</code>会自动将<code>AppBar</code>的<code>leading</code>设置为菜单按钮（如上面截图所示），点击它便可打开抽屉菜单。如果我们想自定义菜单图标，可以手动来设置<code>leading</code>，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n  appBar<span class=\"token punctuation\">:</span> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>\n    title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;App Name&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    leading<span class=\"token punctuation\">:</span> <span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token function\">IconButton</span><span class=\"token punctuation\">(</span>\n        icon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>dashboard<span class=\"token punctuation\">,</span> color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//自定义图标</span>\n        onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token comment\">// 打开抽屉菜单  </span>\n          Scaffold<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">openDrawer</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> \n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>  \n  <span class=\"token punctuation\">)</span>  \n</code></pre></div><p>代码运行效果如图5-20所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图5-20\"></p> <p>可以看到左侧菜单已经替换成功。</p> <p>代码中打开抽屉菜单的方法在<code>ScaffoldState</code>中，通过<code>Scaffold.of(context)</code>可以获取父级最近的<code>Scaffold</code> 组件的<code>State</code>对象。</p> <h3 id=\"tabbar\"><a href=\"#tabbar\" class=\"header-anchor\">#</a> TabBar</h3> <p>下面我们通过“bottom”属性来添加一个导航栏底部Tab按钮组，将要实现的效果如图5-21所示：</p> <p><img src=\"/assets/img/5-21.946b0c47.png\" alt=\"图5-21\"></p> <p>Material组件库中提供了一个<code>TabBar</code>组件，它可以快速生成<code>Tab</code>菜单，下面是上图对应的源码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">_ScaffoldRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>ScaffoldRoute<span class=\"token operator\">&gt;</span>\n    <span class=\"token keyword\">with</span> SingleTickerProviderStateMixin <span class=\"token punctuation\">{</span>\n\n  TabController _tabController<span class=\"token punctuation\">;</span> <span class=\"token comment\">//需要定义一个Controller</span>\n  List tabs <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token string\">&quot;新闻&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;历史&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;图片&quot;</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">// 创建Controller  </span>\n    _tabController <span class=\"token operator\">=</span> <span class=\"token function\">TabController</span><span class=\"token punctuation\">(</span>length<span class=\"token punctuation\">:</span> tabs<span class=\"token punctuation\">.</span>length<span class=\"token punctuation\">,</span> vsync<span class=\"token punctuation\">:</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n      appBar<span class=\"token punctuation\">:</span> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>\n        <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n        bottom<span class=\"token punctuation\">:</span> <span class=\"token function\">TabBar</span><span class=\"token punctuation\">(</span>   <span class=\"token comment\">//生成Tab菜单</span>\n          controller<span class=\"token punctuation\">:</span> _tabController<span class=\"token punctuation\">,</span>\n          tabs<span class=\"token punctuation\">:</span> tabs<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">Tab</span><span class=\"token punctuation\">(</span>text<span class=\"token punctuation\">:</span> e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toList</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n\n  <span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面代码首先创建了一个<code>TabController</code> ，它是用于控制/监听<code>Tab</code>菜单切换的。接下来通过TabBar生成了一个底部菜单栏，<code>TabBar</code>的<code>tabs</code>属性接受一个Widget数组，表示每一个Tab子菜单，我们可以自定义，也可以像示例中一样直接使用<code>Tab</code> 组件，它是Material组件库提供的Material风格的Tab菜单。</p> <p><code>Tab</code>组件有三个可选参数，除了可以指定文字外，还可以指定Tab菜单图标，或者直接自定义组件样式。<code>Tab</code>组件定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Tab</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  Key key<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>text<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 菜单文本</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>icon<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 菜单图标</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 自定义组件样式</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>开发者可以根据实际需求来定制。</p> <h3 id=\"tabbarview\"><a href=\"#tabbarview\" class=\"header-anchor\">#</a> TabBarView</h3> <p>通过<code>TabBar</code>我们只能生成一个静态的菜单，真正的Tab页还没有实现。由于<code>Tab</code>菜单和Tab页的切换需要同步，我们需要通过<code>TabController</code>去监听Tab菜单的切换去切换Tab页，代码如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>_tabController<span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>  \n  <span class=\"token keyword\">switch</span><span class=\"token punctuation\">(</span>_tabController<span class=\"token punctuation\">.</span>index<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">case</span> <span class=\"token number\">1</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">case</span> <span class=\"token number\">2</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token punctuation\">;</span>   \n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>如果我们Tab页可以滑动切换的话，还需要在滑动过程中更新TabBar指示器的偏移！显然，要手动处理这些是很麻烦的，为此，Material库提供了一个<code>TabBarView</code>组件，通过它不仅可以轻松的实现Tab页，而且可以非常容易的配合TabBar来实现同步切换和滑动状态同步，示例如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n  appBar<span class=\"token punctuation\">:</span> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n    bottom<span class=\"token punctuation\">:</span> <span class=\"token function\">TabBar</span><span class=\"token punctuation\">(</span>\n      controller<span class=\"token punctuation\">:</span> _tabController<span class=\"token punctuation\">,</span>\n      tabs<span class=\"token punctuation\">:</span> tabs<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">Tab</span><span class=\"token punctuation\">(</span>text<span class=\"token punctuation\">:</span> e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toList</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  drawer<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">MyDrawer</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  body<span class=\"token punctuation\">:</span> <span class=\"token function\">TabBarView</span><span class=\"token punctuation\">(</span>\n    controller<span class=\"token punctuation\">:</span> _tabController<span class=\"token punctuation\">,</span>\n    children<span class=\"token punctuation\">:</span> tabs<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span> <span class=\"token comment\">//创建3个Tab页</span>\n      <span class=\"token keyword\">return</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n        alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">,</span> textScaleFactor<span class=\"token punctuation\">:</span> <span class=\"token number\">5</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toList</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">// 省略无关代码  </span>\n<span class=\"token punctuation\">)</span>    \n</code></pre></div><p>运行后效果如图5-22所示：</p> <p><img src=\"/assets/img/5-22.e38796ed.png\" alt=\"图5-22\"></p> <p>现在，无论是点击导航栏Tab菜单还是在页面上左右滑动，Tab页面都会切换，并且Tab菜单的状态和Tab页面始终保持同步！那它们是如何实现同步的呢？细心的读者可能已经发现，上例中<code>TabBar</code>和<code>TabBarView</code>的<code>controller</code>是同一个！正是如此，<code>TabBar</code>和<code>TabBarView</code>正是通过同一个<code>controller</code>来实现菜单切换和滑动状态同步的，有关<code>TabController</code>的详细信息，我们不在本书做过多介绍，使用时读者直接查看SDK即可。</p> <p>另外，Material组件库也提供了一个<code>PageView</code> 组件，它和<code>TabBarView</code>功能相似，读者可以自行了解一下。</p> <h2 id=\"_5-6-3-抽屉菜单drawer\"><a href=\"#_5-6-3-抽屉菜单drawer\" class=\"header-anchor\">#</a> 5.6.3 抽屉菜单Drawer</h2> <p><code>Scaffold</code>的<code>drawer</code>和<code>endDrawer</code>属性可以分别接受一个Widget来作为页面的左、右抽屉菜单。如果开发者提供了抽屉菜单，那么当用户手指从屏幕左（或右）侧向里滑动时便可打开抽屉菜单。本节开始部分的示例中实现了一个左抽屉菜单<code>MyDrawer</code>，它的源码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">MyDrawer</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function\">MyDrawer</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Drawer</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> MediaQuery<span class=\"token punctuation\">.</span><span class=\"token function\">removePadding</span><span class=\"token punctuation\">(</span>\n        context<span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">,</span>\n        <span class=\"token comment\">//移除抽屉菜单顶部默认留白</span>\n        removeTop<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n          crossAxisAlignment<span class=\"token punctuation\">:</span> CrossAxisAlignment<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n              padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">only</span><span class=\"token punctuation\">(</span>top<span class=\"token punctuation\">:</span> <span class=\"token number\">38.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n                children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                  <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n                    padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">symmetric</span><span class=\"token punctuation\">(</span>horizontal<span class=\"token punctuation\">:</span> <span class=\"token number\">16.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                    child<span class=\"token punctuation\">:</span> <span class=\"token function\">ClipOval</span><span class=\"token punctuation\">(</span>\n                      child<span class=\"token punctuation\">:</span> Image<span class=\"token punctuation\">.</span><span class=\"token function\">asset</span><span class=\"token punctuation\">(</span>\n                        <span class=\"token string\">&quot;imgs/avatar.png&quot;</span><span class=\"token punctuation\">,</span>\n                        width<span class=\"token punctuation\">:</span> <span class=\"token number\">80</span><span class=\"token punctuation\">,</span>\n                      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>\n                    <span class=\"token string\">&quot;Wendux&quot;</span><span class=\"token punctuation\">,</span>\n                    style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>fontWeight<span class=\"token punctuation\">:</span> FontWeight<span class=\"token punctuation\">.</span>bold<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">)</span>\n                <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token function\">Expanded</span><span class=\"token punctuation\">(</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">ListView</span><span class=\"token punctuation\">(</span>\n                children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                  <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>\n                    leading<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>add<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                    title<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Add account'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>\n                    leading<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>settings<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                    title<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Manage accounts'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>抽屉菜单通常将<code>Drawer</code>组件作为根节点，它实现了Material风格的菜单面板，<code>MediaQuery.removePadding</code>可以移除Drawer默认的一些留白（比如Drawer默认顶部会留和手机状态栏等高的留白），读者可以尝试传递不同的参数来看看实际效果。抽屉菜单页由顶部和底部组成，顶部由用户头像和昵称组成，底部是一个菜单列表，用ListView实现，关于ListView我们将在后面“可滚动组件”一节详细介绍。</p> <h2 id=\"_5-6-4-floatingactionbutton\"><a href=\"#_5-6-4-floatingactionbutton\" class=\"header-anchor\">#</a> 5.6.4 FloatingActionButton</h2> <p><code>FloatingActionButton</code>是Material设计规范中的一种特殊Button，通常悬浮在页面的某一个位置作为某种常用动作的快捷入口，如本节示例中页面右下角的&quot;➕&quot;号按钮。我们可以通过<code>Scaffold</code>的<code>floatingActionButton</code>属性来设置一个<code>FloatingActionButton</code>，同时通过<code>floatingActionButtonLocation</code>属性来指定其在页面中悬浮的位置，这个比较简单，不再赘述。</p> <h2 id=\"_5-6-5-底部tab导航栏\"><a href=\"#_5-6-5-底部tab导航栏\" class=\"header-anchor\">#</a> 5.6.5  底部Tab导航栏</h2> <p>我们可以通过<code>Scaffold</code>的<code>bottomNavigationBar</code>属性来设置底部导航，如本节开始示例所示，我们通过Material组件库提供的<code>BottomNavigationBar</code>和<code>BottomNavigationBarItem</code>两种组件来实现Material风格的底部导航栏。可以看到上面的实现代码非常简单，所以不再赘述，但是如果我们想实现如图5-23所示效果的底部导航栏应该怎么做呢？</p> <p><img src=\"/assets/img/5-23.9b16ea45.png\" alt=\"图5-23\"></p> <p>Material组件库中提供了一个<code>BottomAppBar</code> 组件，它可以和<code>FloatingActionButton</code>配合实现这种“打洞”效果，源码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>bottomNavigationBar<span class=\"token punctuation\">:</span> <span class=\"token function\">BottomAppBar</span><span class=\"token punctuation\">(</span>\n  color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">,</span>\n  shape<span class=\"token punctuation\">:</span> <span class=\"token function\">CircularNotchedRectangle</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 底部导航栏打一个圆形的洞</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n    children<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>\n      <span class=\"token function\">IconButton</span><span class=\"token punctuation\">(</span>icon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>home<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//中间位置空出</span>\n      <span class=\"token function\">IconButton</span><span class=\"token punctuation\">(</span>icon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>business<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>spaceAround<span class=\"token punctuation\">,</span> <span class=\"token comment\">//均分底部导航栏横向空间</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>可以看到，上面代码中没有控制打洞位置的属性，实际上，打洞的位置取决于<code>FloatingActionButton</code>的位置，上面<code>FloatingActionButton</code>的位置为：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>floatingActionButtonLocation<span class=\"token punctuation\">:</span> FloatingActionButtonLocation<span class=\"token punctuation\">.</span>centerDocked<span class=\"token punctuation\">,</span>\n</code></pre></div><p>所以打洞位置在底部导航栏的正中间。</p> <p><code>BottomAppBar</code>的<code>shape</code>属性决定洞的外形，<code>CircularNotchedRectangle</code>实现了一个圆形的外形，我们也可以自定义外形，比如，Flutter Gallery示例中就有一个“钻石”形状的示例，读者感兴趣可以自行查看。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter5/container.html\" class=\"prev\">\n        5.5 Container\n      </a></span> <span class=\"next\"><a href=\"/chapter5/clip.html\">\n        5.7 剪裁（Clip）\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/15.7f957f4b.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter5/padding.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>5.1 填充（Padding） | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/100.ce32ed9e.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable open\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" aria-current=\"page\" class=\"active sidebar-link\">5.1 填充（Padding）</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/chapter5/padding.html#_5-1-填充-padding\" class=\"sidebar-link\">5.1 填充（Padding）</a></li></ul></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h2 id=\"_5-1-填充-padding\"><a href=\"#_5-1-填充-padding\" class=\"header-anchor\">#</a> 5.1 填充（Padding）</h2> <p><code>Padding</code>可以给其子节点添加填充（留白），和边距效果类似。我们在前面很多示例中都已经使用过它了，现在来看看它的定义：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  EdgeInsetsGeometry padding<span class=\"token punctuation\">,</span>\n  Widget child<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p><code>EdgeInsetsGeometry</code>是一个抽象类，开发中，我们一般都使用<code>EdgeInsets</code>类，它是<code>EdgeInsetsGeometry</code>的一个子类，定义了一些设置填充的便捷方法。</p> <h3 id=\"edgeinsets\"><a href=\"#edgeinsets\" class=\"header-anchor\">#</a> EdgeInsets</h3> <p>我们看看<code>EdgeInsets</code>提供的便捷方法：</p> <ul><li><code>fromLTRB(double left, double top, double right, double bottom)</code>：分别指定四个方向的填充。</li> <li><code>all(double value)</code> : 所有方向均使用相同数值的填充。</li> <li><code>only({left, top, right ,bottom })</code>：可以设置具体某个方向的填充(可以同时指定多个方向)。</li> <li><code>symmetric({ vertical, horizontal })</code>：用于设置对称方向的填充，<code>vertical</code>指<code>top</code>和<code>bottom</code>，<code>horizontal</code>指<code>left</code>和<code>right</code>。</li></ul> <h3 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h3> <p>下面的示例主要展示了<code>EdgeInsets</code>的不同用法，比较简单，源码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">PaddingTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n      <span class=\"token comment\">//上下左右各添加16像素补白</span>\n      padding<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">16.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n        <span class=\"token comment\">//显式指定对齐方式为左对齐，排除对齐干扰</span>\n        crossAxisAlignment<span class=\"token punctuation\">:</span> CrossAxisAlignment<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n        children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n          <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n            <span class=\"token comment\">//左边添加8像素补白</span>\n            padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">only</span><span class=\"token punctuation\">(</span>left<span class=\"token punctuation\">:</span> <span class=\"token number\">8.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hello world&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n            <span class=\"token comment\">//上下各添加8像素补白</span>\n            padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">symmetric</span><span class=\"token punctuation\">(</span>vertical<span class=\"token punctuation\">:</span> <span class=\"token number\">8.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;I am Jack&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n            <span class=\"token comment\">// 分别指定四个方向的补白</span>\n            padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">fromLTRB</span><span class=\"token punctuation\">(</span><span class=\"token number\">20.0</span><span class=\"token punctuation\">,</span><span class=\"token number\">.0</span><span class=\"token punctuation\">,</span><span class=\"token number\">20.0</span><span class=\"token punctuation\">,</span><span class=\"token number\">20.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Your friend&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行效果如图5-1所示：</p> <p><img src=\"/assets/img/5-1.239dadc0.png\" alt=\"图5-1\"></p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter4/alignment.html\" class=\"prev\">\n        4.6 对齐与相对定位（Align）\n      </a></span> <span class=\"next\"><a href=\"/chapter5/constrainedbox_and_sizebox.html\">\n        5.2 尺寸限制类容器\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/100.ce32ed9e.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter5/transform.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>5.4 变换（Transform） | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/16.2d42d5bc.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable open\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" aria-current=\"page\" class=\"active sidebar-link\">5.4 变换（Transform）</a><ul class=\"sidebar-sub-headers\"></ul></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_5-4-变换-transform\"><a href=\"#_5-4-变换-transform\" class=\"header-anchor\">#</a> 5.4 变换（Transform）</h1> <p><code>Transform</code>可以在其子组件绘制时对其应用一些矩阵变换来实现一些特效。<code>Matrix4</code>是一个4D矩阵，通过它我们可以实现各种矩阵操作，下面是一个例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n  color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>black<span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Transform</span><span class=\"token punctuation\">(</span>\n    alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>topRight<span class=\"token punctuation\">,</span> <span class=\"token comment\">//相对于坐标系原点的对齐方式</span>\n    transform<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Matrix4<span class=\"token punctuation\">.</span>skewY</span><span class=\"token punctuation\">(</span><span class=\"token number\">0.3</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//沿Y轴倾斜0.3弧度</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Container</span><span class=\"token punctuation\">(</span>\n      padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">8.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>deepOrange<span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Apartment for rent!'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>运行效果如图5-10所示：</p> <p><img src=\"/assets/img/5-10.62f0e5d6.png\" alt=\"图5-10\"></p> <blockquote><p>关于矩阵变换的相关内容属于线性代数范畴，本书不做讨论，读者有兴趣可以自行了解。本书中，我们把焦点放在Flutter中一些常见的变换效果上。另外，由于矩阵变化时发生在绘制时，而无需重新布局和构建等过程，所以性能很好。</p></blockquote> <h3 id=\"平移\"><a href=\"#平移\" class=\"header-anchor\">#</a> 平移</h3> <p><code>Transform.translate</code>接收一个<code>offset</code>参数，可以在绘制时沿<code>x</code>、<code>y</code>轴对子组件平移指定的距离。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">DecoratedBox</span><span class=\"token punctuation\">(</span>\n  decoration<span class=\"token punctuation\">:</span><span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token comment\">//默认原点为左上角，左移20像素，向上平移5像素  </span>\n  child<span class=\"token punctuation\">:</span> Transform<span class=\"token punctuation\">.</span><span class=\"token function\">translate</span><span class=\"token punctuation\">(</span>\n    offset<span class=\"token punctuation\">:</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token operator\">-</span><span class=\"token number\">20.0</span><span class=\"token punctuation\">,</span> <span class=\"token operator\">-</span><span class=\"token number\">5.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hello world&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>效果如图5-11所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图5-11\"></p> <h3 id=\"旋转\"><a href=\"#旋转\" class=\"header-anchor\">#</a> 旋转</h3> <p><code>Transform.rotate</code>可以对子组件进行旋转变换，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">DecoratedBox</span><span class=\"token punctuation\">(</span>\n  decoration<span class=\"token punctuation\">:</span><span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> Transform<span class=\"token punctuation\">.</span><span class=\"token function\">rotate</span><span class=\"token punctuation\">(</span>\n    <span class=\"token comment\">//旋转90度</span>\n    angle<span class=\"token punctuation\">:</span>math<span class=\"token punctuation\">.</span>pi<span class=\"token operator\">/</span><span class=\"token number\">2</span> <span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hello world&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>；\n</code></pre></div><blockquote><p>注意：要使用<code>math.pi</code>需先进行如下导包。</p></blockquote> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'dart:math'</span> <span class=\"token operator\">as</span> math<span class=\"token punctuation\">;</span>  \n</code></pre></div><p>效果如图5-12所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图5-12\"></p> <h3 id=\"缩放\"><a href=\"#缩放\" class=\"header-anchor\">#</a> 缩放</h3> <p><code>Transform.scale</code>可以对子组件进行缩小或放大，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">DecoratedBox</span><span class=\"token punctuation\">(</span>\n  decoration<span class=\"token punctuation\">:</span><span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> Transform<span class=\"token punctuation\">.</span><span class=\"token function\">scale</span><span class=\"token punctuation\">(</span>\n      scale<span class=\"token punctuation\">:</span> <span class=\"token number\">1.5</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//放大到1.5倍</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hello world&quot;</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>效果如图5-13所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图5-13\"></p> <h3 id=\"注意\"><a href=\"#注意\" class=\"header-anchor\">#</a> 注意</h3> <ul><li><p><code>Transform</code>的变换是应用在绘制阶段，而并不是应用在布局(layout)阶段，所以无论对子组件应用何种变化，其占用空间的大小和在屏幕上的位置都是固定不变的，因为这些是在布局阶段就确定的。下面我们具体说明：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code> <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n  mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token function\">DecoratedBox</span><span class=\"token punctuation\">(</span>\n      decoration<span class=\"token punctuation\">:</span><span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> Transform<span class=\"token punctuation\">.</span><span class=\"token function\">scale</span><span class=\"token punctuation\">(</span>scale<span class=\"token punctuation\">:</span> <span class=\"token number\">1.5</span><span class=\"token punctuation\">,</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hello world&quot;</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;你好&quot;</span><span class=\"token punctuation\">,</span> style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>green<span class=\"token punctuation\">,</span> fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">18.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行效果如图5-14所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图5-14\"></p> <p>由于第一个<code>Text</code>应用变换(放大)后，其在绘制时会放大，但其占用的空间依然为红色部分，所以第二个<code>Text</code>会紧挨着红色部分，最终就会出现文字重合。</p></li> <li><p>由于矩阵变化只会作用在绘制阶段，所以在某些场景下，在UI需要变化时，可以直接通过矩阵变化来达到视觉上的UI改变，而不需要去重新触发build流程，这样会节省layout的开销，所以性能会比较好。如之前介绍的<code>Flow</code>组件，它内部就是用矩阵变换来更新UI，除此之外，Flutter的动画组件中也大量使用了<code>Transform</code>以提高性能。</p></li></ul> <blockquote><p>思考题：使用<code>Transform</code>对其子组件先进行平移然后再旋转和先旋转再平移，两者最终的效果一样吗？为什么？</p></blockquote> <h3 id=\"rotatedbox\"><a href=\"#rotatedbox\" class=\"header-anchor\">#</a> RotatedBox</h3> <p><code>RotatedBox</code>和<code>Transform.rotate</code>功能相似，它们都可以对子组件进行旋转变换，但是有一点不同：<code>RotatedBox</code>的变换是在layout阶段，会影响在子组件的位置和大小。我们将上面介绍<code>Transform.rotate</code>时的示例改一下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n  mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token function\">DecoratedBox</span><span class=\"token punctuation\">(</span>\n      decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token comment\">//将Transform.rotate换成RotatedBox  </span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">RotatedBox</span><span class=\"token punctuation\">(</span>\n        quarterTurns<span class=\"token punctuation\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//旋转90度(1/4圈)</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hello world&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;你好&quot;</span><span class=\"token punctuation\">,</span> style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>green<span class=\"token punctuation\">,</span> fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">18.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</code></pre></div><p>效果如图5-15所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图5-15\"></p> <p>由于<code>RotatedBox</code>是作用于layout阶段，所以子组件会旋转90度（而不只是绘制的内容），<code>decoration</code>会作用到子组件所占用的实际空间上，所以最终就是上图的效果，读者可以和前面<code>Transform.rotate</code>示例对比理解。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter5/decoratedbox.html\" class=\"prev\">\n        5.3 装饰容器DecoratedBox\n      </a></span> <span class=\"next\"><a href=\"/chapter5/container.html\">\n        5.5 Container\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/16.2d42d5bc.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter6/custom_scrollview.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>6.5 CustomScrollView | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/66.5df03e62.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable open\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" aria-current=\"page\" class=\"active sidebar-link\">6.5 CustomScrollView</a><ul class=\"sidebar-sub-headers\"></ul></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_6-5-customscrollview\"><a href=\"#_6-5-customscrollview\" class=\"header-anchor\">#</a> 6.5 CustomScrollView</h1> <p><code>CustomScrollView</code>是可以使用Sliver来自定义滚动模型（效果）的组件。它可以包含多种滚动模型，举个例子，假设有一个页面，顶部需要一个<code>GridView</code>，底部需要一个<code>ListView</code>，而要求整个页面的滑动效果是统一的，即它们看起来是一个整体。如果使用<code>GridView</code>+<code>ListView</code>来实现的话，就不能保证一致的滑动效果，因为它们的滚动效果是分离的，所以这时就需要一个&quot;胶水&quot;，把这些彼此独立的可滚动组件&quot;粘&quot;起来，而<code>CustomScrollView</code>的功能就相当于“胶水”。</p> <h3 id=\"可滚动组件的sliver版\"><a href=\"#可滚动组件的sliver版\" class=\"header-anchor\">#</a> 可滚动组件的Sliver版</h3> <p>Sliver在前面讲过，有细片、薄片之意，在Flutter中，Sliver通常指可滚动组件子元素（就像一个个薄片一样）。但是在<code>CustomScrollView</code>中，需要粘起来的可滚动组件就是<code>CustomScrollView</code>的Sliver了，如果直接将<code>ListView</code>、<code>GridView</code>作为<code>CustomScrollView</code>是不行的，因为它们本身是可滚动组件而并不是Sliver！因此，为了能让可滚动组件能和<code>CustomScrollView</code>配合使用，Flutter提供了一些可滚动组件的Sliver版，如SliverList、SliverGrid等。实际上Sliver版的可滚动组件和非Sliver版的可滚动组件最大的区别就是<strong>前者不包含滚动模型（自身不能再滚动），而后者包含滚动模型</strong> ，也正因如此，<code>CustomScrollView</code>才可以将多个Sliver&quot;粘&quot;在一起，这些Sliver共用<code>CustomScrollView</code>的<code>Scrollable</code>，所以最终才实现了统一的滑动效果。</p> <blockquote><p>Sliver系列Widget比较多，我们不会一一介绍，读者只需记住它的特点，需要时再去查看文档即可。上面之所以说“大多数”Sliver都和可滚动组件对应，是由于还有一些如SliverPadding、SliverAppBar等是和可滚动组件无关的，它们主要是为了结合CustomScrollView一起使用，这是因为<strong>CustomScrollView的子组件必须都是Sliver</strong>。</p></blockquote> <h3 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h3> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">CustomScrollViewTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//因为本路由没有使用Scaffold，为了让子级Widget(如Text)使用</span>\n    <span class=\"token comment\">//Material Design 默认的样式风格,我们使用Material作为本路由的根。</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Material</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">CustomScrollView</span><span class=\"token punctuation\">(</span>\n        slivers<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n          <span class=\"token comment\">//AppBar，包含一个导航栏</span>\n          <span class=\"token function\">SliverAppBar</span><span class=\"token punctuation\">(</span>\n            pinned<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n            expandedHeight<span class=\"token punctuation\">:</span> <span class=\"token number\">250.0</span><span class=\"token punctuation\">,</span>\n            flexibleSpace<span class=\"token punctuation\">:</span> <span class=\"token function\">FlexibleSpaceBar</span><span class=\"token punctuation\">(</span>\n              title<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Demo'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              background<span class=\"token punctuation\">:</span> Image<span class=\"token punctuation\">.</span><span class=\"token function\">asset</span><span class=\"token punctuation\">(</span>\n                <span class=\"token string\">&quot;./images/avatar.png&quot;</span><span class=\"token punctuation\">,</span> fit<span class=\"token punctuation\">:</span> BoxFit<span class=\"token punctuation\">.</span>cover<span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n\n          <span class=\"token function\">SliverPadding</span><span class=\"token punctuation\">(</span>\n            padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">8.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            sliver<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">SliverGrid</span><span class=\"token punctuation\">(</span> <span class=\"token comment\">//Grid</span>\n              gridDelegate<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">SliverGridDelegateWithFixedCrossAxisCount</span><span class=\"token punctuation\">(</span>\n                crossAxisCount<span class=\"token punctuation\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//Grid按两列显示</span>\n                mainAxisSpacing<span class=\"token punctuation\">:</span> <span class=\"token number\">10.0</span><span class=\"token punctuation\">,</span>\n                crossAxisSpacing<span class=\"token punctuation\">:</span> <span class=\"token number\">10.0</span><span class=\"token punctuation\">,</span>\n                childAspectRatio<span class=\"token punctuation\">:</span> <span class=\"token number\">4.0</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              delegate<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">SliverChildBuilderDelegate</span><span class=\"token punctuation\">(</span>\n                    <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> int index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                  <span class=\"token comment\">//创建子widget      </span>\n                  <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Container</span><span class=\"token punctuation\">(</span>\n                    alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n                    color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>cyan<span class=\"token punctuation\">[</span><span class=\"token number\">100</span> <span class=\"token operator\">*</span> <span class=\"token punctuation\">(</span>index <span class=\"token operator\">%</span> <span class=\"token number\">9</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                    child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'grid item $index'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n                childCount<span class=\"token punctuation\">:</span> <span class=\"token number\">20</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token comment\">//List</span>\n          <span class=\"token keyword\">new</span> <span class=\"token class-name\">SliverFixedExtentList</span><span class=\"token punctuation\">(</span>\n            itemExtent<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n            delegate<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">SliverChildBuilderDelegate</span><span class=\"token punctuation\">(</span>\n                    <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> int index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                  <span class=\"token comment\">//创建列表项      </span>\n                  <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Container</span><span class=\"token punctuation\">(</span>\n                    alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n                    color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>lightBlue<span class=\"token punctuation\">[</span><span class=\"token number\">100</span> <span class=\"token operator\">*</span> <span class=\"token punctuation\">(</span>index <span class=\"token operator\">%</span> <span class=\"token number\">9</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                    child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'list item $index'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n                childCount<span class=\"token punctuation\">:</span> <span class=\"token number\">50</span> <span class=\"token comment\">//50个列表项</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>代码分为三部分：</p> <ul><li>头部<code>SliverAppBar</code>：<code>SliverAppBar</code>对应<code>AppBar</code>，两者不同之处在于<code>SliverAppBar</code>可以集成到<code>CustomScrollView</code>。<code>SliverAppBar</code>可以结合<code>FlexibleSpaceBar</code>实现Material Design中头部伸缩的模型，具体效果，读者可以运行该示例查看。</li> <li>中间的<code>SliverGrid</code>：它用<code>SliverPadding</code>包裹以给<code>SliverGrid</code>添加补白。<code>SliverGrid</code>是一个两列，宽高比为4的网格，它有20个子组件。</li> <li>底部<code>SliverFixedExtentList</code>：它是一个所有子元素高度都为50像素的列表。</li></ul> <p>运行效果如图：</p> <p><img src=\"/assets/img/6-12.d5a5a078.png\" alt=\"图6-12\"><img src=\"/assets/img/6-13.7ffa2d43.png\" alt=\"图6-13\"></p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter6/gridview.html\" class=\"prev\">\n        6.4 GridView\n      </a></span> <span class=\"next\"><a href=\"/chapter6/scroll_controller.html\">\n        6.6 滚动监听及控制\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/66.5df03e62.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter6/gridview.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>6.4 GridView | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/45.381b04c9.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable open\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" aria-current=\"page\" class=\"active sidebar-link\">6.4 GridView</a><ul class=\"sidebar-sub-headers\"></ul></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_6-4-gridview\"><a href=\"#_6-4-gridview\" class=\"header-anchor\">#</a> 6.4 GridView</h1> <p><code>GridView</code>可以构建一个二维网格列表，其默认构造函数定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">GridView</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  Axis scrollDirection <span class=\"token operator\">=</span> Axis<span class=\"token punctuation\">.</span>vertical<span class=\"token punctuation\">,</span>\n  bool reverse <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n  ScrollController controller<span class=\"token punctuation\">,</span>\n  bool primary<span class=\"token punctuation\">,</span>\n  ScrollPhysics physics<span class=\"token punctuation\">,</span>\n  bool shrinkWrap <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n  EdgeInsetsGeometry padding<span class=\"token punctuation\">,</span>\n  <span class=\"token metadata symbol\">@required</span> SliverGridDelegate gridDelegate<span class=\"token punctuation\">,</span> <span class=\"token comment\">//控制子widget layout的委托</span>\n  bool addAutomaticKeepAlives <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n  bool addRepaintBoundaries <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n  double cacheExtent<span class=\"token punctuation\">,</span>\n  List<span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span> children <span class=\"token operator\">=</span> <span class=\"token keyword\">const</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>我们可以看到，<code>GridView</code>和<code>ListView</code>的大多数参数都是相同的，它们的含义也都相同的，如有疑惑读者可以翻阅ListView一节，在此不再赘述。我们唯一需要关注的是<code>gridDelegate</code>参数，类型是<code>SliverGridDelegate</code>，它的作用是控制<code>GridView</code>子组件如何排列(layout)。</p> <p><code>SliverGridDelegate</code>是一个抽象类，定义了<code>GridView</code> Layout相关接口，子类需要通过实现它们来实现具体的布局算法。Flutter中提供了两个<code>SliverGridDelegate</code>的子类<code>SliverGridDelegateWithFixedCrossAxisCount</code>和<code>SliverGridDelegateWithMaxCrossAxisExtent</code>，我们可以直接使用，下面我们分别来介绍一下它们。</p> <h3 id=\"slivergriddelegatewithfixedcrossaxiscount\"><a href=\"#slivergriddelegatewithfixedcrossaxiscount\" class=\"header-anchor\">#</a> SliverGridDelegateWithFixedCrossAxisCount</h3> <p>该子类实现了一个横轴为固定数量子元素的layout算法，其构造函数为：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">SliverGridDelegateWithFixedCrossAxisCount</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@required</span> double crossAxisCount<span class=\"token punctuation\">,</span> \n  double mainAxisSpacing <span class=\"token operator\">=</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span>\n  double crossAxisSpacing <span class=\"token operator\">=</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span>\n  double childAspectRatio <span class=\"token operator\">=</span> <span class=\"token number\">1.0</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><ul><li><code>crossAxisCount</code>：横轴子元素的数量。此属性值确定后子元素在横轴的长度就确定了，即ViewPort横轴长度除以<code>crossAxisCount</code>的商。</li> <li><code>mainAxisSpacing</code>：主轴方向的间距。</li> <li><code>crossAxisSpacing</code>：横轴方向子元素的间距。</li> <li><code>childAspectRatio</code>：子元素在横轴长度和主轴长度的比例。由于<code>crossAxisCount</code>指定后，子元素横轴长度就确定了，然后通过此参数值就可以确定子元素在主轴的长度。</li></ul> <p>可以发现，子元素的大小是通过<code>crossAxisCount</code>和<code>childAspectRatio</code>两个参数共同决定的。注意，这里的子元素指的是子组件的最大显示空间，注意确保子组件的实际大小不要超出子元素的空间。</p> <p>下面看一个例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">GridView</span><span class=\"token punctuation\">(</span>\n  gridDelegate<span class=\"token punctuation\">:</span> <span class=\"token function\">SliverGridDelegateWithFixedCrossAxisCount</span><span class=\"token punctuation\">(</span>\n      crossAxisCount<span class=\"token punctuation\">:</span> <span class=\"token number\">3</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//横轴三个子widget</span>\n      childAspectRatio<span class=\"token punctuation\">:</span> <span class=\"token number\">1.0</span> <span class=\"token comment\">//宽高比为1时，子widget</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  children<span class=\"token punctuation\">:</span><span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>ac_unit<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>airport_shuttle<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>all_inclusive<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>beach_access<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>cake<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>free_breakfast<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p><img src=\"/assets/img/6-9.865c35f8.png\" alt=\"图6-9\"></p> <h4 id=\"gridview-count\"><a href=\"#gridview-count\" class=\"header-anchor\">#</a> GridView.count</h4> <p><code>GridView.count</code>构造函数内部使用了<code>SliverGridDelegateWithFixedCrossAxisCount</code>，我们通过它可以快速的创建横轴固定数量子元素的<code>GridView</code>，上面的示例代码等价于：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>GridView<span class=\"token punctuation\">.</span><span class=\"token function\">count</span><span class=\"token punctuation\">(</span> \n  crossAxisCount<span class=\"token punctuation\">:</span> <span class=\"token number\">3</span><span class=\"token punctuation\">,</span>\n  childAspectRatio<span class=\"token punctuation\">:</span> <span class=\"token number\">1.0</span><span class=\"token punctuation\">,</span>\n  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>ac_unit<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>airport_shuttle<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>all_inclusive<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>beach_access<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>cake<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>free_breakfast<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><h3 id=\"slivergriddelegatewithmaxcrossaxisextent\"><a href=\"#slivergriddelegatewithmaxcrossaxisextent\" class=\"header-anchor\">#</a> SliverGridDelegateWithMaxCrossAxisExtent</h3> <p>该子类实现了一个横轴子元素为固定最大长度的layout算法，其构造函数为：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">SliverGridDelegateWithMaxCrossAxisExtent</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  double maxCrossAxisExtent<span class=\"token punctuation\">,</span>\n  double mainAxisSpacing <span class=\"token operator\">=</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span>\n  double crossAxisSpacing <span class=\"token operator\">=</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span>\n  double childAspectRatio <span class=\"token operator\">=</span> <span class=\"token number\">1.0</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p><code>maxCrossAxisExtent</code>为子元素在横轴上的最大长度，之所以是“最大”长度，是<strong>因为横轴方向每个子元素的长度仍然是等分的</strong>，举个例子，如果ViewPort的横轴长度是450，那么当<code>maxCrossAxisExtent</code>的值在区间[450/4，450/3)内的话，子元素最终实际长度都为112.5，而<code>childAspectRatio</code>所指的子元素横轴和主轴的长度比为<strong>最终的长度比</strong>。其它参数和<code>SliverGridDelegateWithFixedCrossAxisCount</code>相同。</p> <p>下面我们看一个例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">GridView</span><span class=\"token punctuation\">(</span>\n  padding<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span>zero<span class=\"token punctuation\">,</span>\n  gridDelegate<span class=\"token punctuation\">:</span> <span class=\"token function\">SliverGridDelegateWithMaxCrossAxisExtent</span><span class=\"token punctuation\">(</span>\n      maxCrossAxisExtent<span class=\"token punctuation\">:</span> <span class=\"token number\">120.0</span><span class=\"token punctuation\">,</span>\n      childAspectRatio<span class=\"token punctuation\">:</span> <span class=\"token number\">2.0</span> <span class=\"token comment\">//宽高比为2</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>ac_unit<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>airport_shuttle<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>all_inclusive<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>beach_access<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>cake<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>free_breakfast<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p><img src=\"/assets/img/6-10.202cef73.png\" alt=\"图6-10\"></p> <h4 id=\"gridview-extent\"><a href=\"#gridview-extent\" class=\"header-anchor\">#</a> GridView.extent</h4> <p>GridView.extent构造函数内部使用了SliverGridDelegateWithMaxCrossAxisExtent，我们通过它可以快速的创建纵轴子元素为固定最大长度的的GridView，上面的示例代码等价于：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>GridView<span class=\"token punctuation\">.</span><span class=\"token function\">extent</span><span class=\"token punctuation\">(</span>\n   maxCrossAxisExtent<span class=\"token punctuation\">:</span> <span class=\"token number\">120.0</span><span class=\"token punctuation\">,</span>\n   childAspectRatio<span class=\"token punctuation\">:</span> <span class=\"token number\">2.0</span><span class=\"token punctuation\">,</span>\n   children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n     <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>ac_unit<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n     <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>airport_shuttle<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n     <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>all_inclusive<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n     <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>beach_access<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n     <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>cake<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n     <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>free_breakfast<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n   <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><h3 id=\"gridview-builder\"><a href=\"#gridview-builder\" class=\"header-anchor\">#</a> GridView.builder</h3> <p>上面我们介绍的GridView都需要一个widget数组作为其子元素，这些方式都会提前将所有子widget都构建好，所以只适用于子widget数量比较少时，当子widget比较多时，我们可以通过<code>GridView.builder</code>来动态创建子widget。<code>GridView.builder</code> 必须指定的参数有两个：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>GridView<span class=\"token punctuation\">.</span><span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>\n <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n <span class=\"token metadata symbol\">@required</span> SliverGridDelegate gridDelegate<span class=\"token punctuation\">,</span> \n <span class=\"token metadata symbol\">@required</span> IndexedWidgetBuilder itemBuilder<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>其中<code>itemBuilder</code>为子widget构建器。</p> <h4 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h4> <p>假设我们需要从一个异步数据源（如网络）分批获取一些<code>Icon</code>，然后用<code>GridView</code>来展示：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">InfiniteGridView</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _InfiniteGridViewState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_InfiniteGridViewState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_InfiniteGridViewState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>InfiniteGridView<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n\n  List<span class=\"token operator\">&lt;</span>IconData<span class=\"token operator\">&gt;</span> _icons <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//保存Icon数据</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 初始化数据  </span>\n    <span class=\"token function\">_retrieveIcons</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> GridView<span class=\"token punctuation\">.</span><span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>\n        gridDelegate<span class=\"token punctuation\">:</span> <span class=\"token function\">SliverGridDelegateWithFixedCrossAxisCount</span><span class=\"token punctuation\">(</span>\n            crossAxisCount<span class=\"token punctuation\">:</span> <span class=\"token number\">3</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//每行三列</span>\n            childAspectRatio<span class=\"token punctuation\">:</span> <span class=\"token number\">1.0</span> <span class=\"token comment\">//显示区域宽高相等</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        itemCount<span class=\"token punctuation\">:</span> _icons<span class=\"token punctuation\">.</span>length<span class=\"token punctuation\">,</span>\n        itemBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token comment\">//如果显示到最后一个并且Icon总数小于200时继续获取数据</span>\n          <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>index <span class=\"token operator\">==</span> _icons<span class=\"token punctuation\">.</span>length <span class=\"token operator\">-</span> <span class=\"token number\">1</span> <span class=\"token operator\">&amp;&amp;</span> _icons<span class=\"token punctuation\">.</span>length <span class=\"token operator\">&lt;</span> <span class=\"token number\">200</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token function\">_retrieveIcons</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span>\n          <span class=\"token keyword\">return</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>_icons<span class=\"token punctuation\">[</span>index<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">//模拟异步获取数据</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_retrieveIcons</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    Future<span class=\"token punctuation\">.</span><span class=\"token function\">delayed</span><span class=\"token punctuation\">(</span><span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>milliseconds<span class=\"token punctuation\">:</span> <span class=\"token number\">200</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        _icons<span class=\"token punctuation\">.</span><span class=\"token function\">addAll</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span>\n          Icons<span class=\"token punctuation\">.</span>ac_unit<span class=\"token punctuation\">,</span>\n          Icons<span class=\"token punctuation\">.</span>airport_shuttle<span class=\"token punctuation\">,</span>\n          Icons<span class=\"token punctuation\">.</span>all_inclusive<span class=\"token punctuation\">,</span>\n          Icons<span class=\"token punctuation\">.</span>beach_access<span class=\"token punctuation\">,</span> Icons<span class=\"token punctuation\">.</span>cake<span class=\"token punctuation\">,</span>\n          Icons<span class=\"token punctuation\">.</span>free_breakfast\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><ul><li><code>_retrieveIcons()</code>：在此方法中我们通过<code>Future.delayed</code>来模拟从异步数据源获取数据，每次获取数据需要200毫秒，获取成功后将新数据添加到_icons，然后调用setState重新构建。</li> <li>在itemBuilder中，如果显示到最后一个时，判断是否需要继续获取数据，然后返回一个Icon。</li></ul> <h3 id=\"更多\"><a href=\"#更多\" class=\"header-anchor\">#</a> 更多</h3> <p>Flutter的<code>GridView</code>默认子元素显示空间是相等的，但在实际开发中，你可能会遇到子元素大小不等的情况，如下面这样的布局：</p> <p><img src=\"/assets/img/6-11.a491f457.png\" alt=\"图6-11\"></p> <p>Pub上有一个包“flutter_staggered_grid_view” ，它实现了一个交错GridView的布局模型，可以很轻松的实现这种布局，详情读者可以自行了解。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter6/listview.html\" class=\"prev\">\n        6.3 ListView\n      </a></span> <span class=\"next\"><a href=\"/chapter6/custom_scrollview.html\">\n        6.5 CustomScrollView\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/45.381b04c9.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter6/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>本章目录 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/162.f96e9c80.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"本章目录\"><a href=\"#本章目录\" class=\"header-anchor\">#</a> 本章目录</h1> <ul><li><a href=\"/chapter6/intro.html\">6.1：可滚动组件简介</a></li> <li><a href=\"/chapter6/single_child_scrollview.html\">6.2：SingleChildScrollView</a></li> <li><a href=\"/chapter6/listview.html\">6.3：ListView</a></li> <li><a href=\"/chapter6/gridview.html\">6.4：GridView</a></li> <li><a href=\"/chapter6/custom_scrollview.html\">6.5：CustomScrollView</a></li> <li><a href=\"/chapter6/scroll_controller.html\">6.6：滚动监听及控制（ScrollController）</a></li></ul></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/162.f96e9c80.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter6/intro.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>6.1 可滚动组件简介 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/163.cfb15292.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable open\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" aria-current=\"page\" class=\"active sidebar-link\">6.1 可滚动组件简介</a><ul class=\"sidebar-sub-headers\"></ul></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_6-1-可滚动组件简介\"><a href=\"#_6-1-可滚动组件简介\" class=\"header-anchor\">#</a> 6.1 可滚动组件简介</h1> <p>当组件内容超过当前显示视口(ViewPort)时，如果没有特殊处理，Flutter则会提示Overflow错误。为此，Flutter提供了多种可滚动组件（Scrollable Widget）用于显示列表和长布局。在本章中，我们先介绍一下常用的可滚动组件（如<code>ListView</code>、<code>GridView</code>等），然后介绍一下<code>ScrollController</code>。可滚动组件都直接或间接包含一个<code>Scrollable</code>组件，因此它们包括一些共同的属性，为了避免重复介绍，我们在此统一介绍一下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Scrollable</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>axisDirection <span class=\"token operator\">=</span> AxisDirection<span class=\"token punctuation\">.</span>down<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>controller<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>physics<span class=\"token punctuation\">,</span>\n  <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>viewportBuilder<span class=\"token punctuation\">,</span> <span class=\"token comment\">//后面介绍</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><ul><li><code>axisDirection</code>滚动方向。</li> <li><code>physics</code>：此属性接受一个<code>ScrollPhysics</code>类型的对象，它决定可滚动组件如何响应用户操作，比如用户滑动完抬起手指后，继续执行动画；或者滑动到边界时，如何显示。默认情况下，Flutter会根据具体平台分别使用不同的<code>ScrollPhysics</code>对象，应用不同的显示效果，如当滑动到边界时，继续拖动的话，在iOS上会出现弹性效果，而在Android上会出现微光效果。如果你想在所有平台下使用同一种效果，可以显式指定一个固定的<code>ScrollPhysics</code>，Flutter SDK中包含了两个<code>ScrollPhysics</code>的子类，他们可以直接使用：\n<ul><li><code>ClampingScrollPhysics</code>：Android下微光效果。</li> <li><code>BouncingScrollPhysics</code>：iOS下弹性效果。</li></ul></li> <li><code>controller</code>：此属性接受一个<code>ScrollController</code>对象。<code>ScrollController</code>的主要作用是控制滚动位置和监听滚动事件。默认情况下，Widget树中会有一个默认的<code>PrimaryScrollController</code>，如果子树中的可滚动组件没有显式的指定<code>controller</code>，并且<code>primary</code>属性值为<code>true</code>时（默认就为<code>true</code>），可滚动组件会使用这个默认的<code>PrimaryScrollController</code>。这种机制带来的好处是父组件可以控制子树中可滚动组件的滚动行为，例如，<code>Scaffold</code>正是使用这种机制在iOS中实现了点击导航栏回到顶部的功能。我们将在本章后面“滚动控制”一节详细介绍<code>ScrollController</code>。</li></ul> <h3 id=\"scrollbar\"><a href=\"#scrollbar\" class=\"header-anchor\">#</a> Scrollbar</h3> <p><code>Scrollbar</code>是一个Material风格的滚动指示器（滚动条），如果要给可滚动组件添加滚动条，只需将<code>Scrollbar</code>作为可滚动组件的任意一个父级组件即可，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Scrollbar</span><span class=\"token punctuation\">(</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">SingleChildScrollView</span><span class=\"token punctuation\">(</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p><code>Scrollbar</code>和<code>CupertinoScrollbar</code>都是通过监听滚动通知来确定滚动条位置的。关于的滚动通知的详细内容我们将在本章最后一节中专门介绍。</p> <h4 id=\"cupertinoscrollbar\"><a href=\"#cupertinoscrollbar\" class=\"header-anchor\">#</a> CupertinoScrollbar</h4> <p><code>CupertinoScrollbar</code>是iOS风格的滚动条，如果你使用的是<code>Scrollbar</code>，那么在iOS平台它会自动切换为<code>CupertinoScrollbar</code>。</p> <h3 id=\"viewport视口\"><a href=\"#viewport视口\" class=\"header-anchor\">#</a> ViewPort视口</h3> <p>在很多布局系统中都有ViewPort的概念，在Flutter中，术语ViewPort（视口），如无特别说明，则是指一个Widget的实际显示区域。例如，一个<code>ListView</code>的显示区域高度是800像素，虽然其列表项总高度可能远远超过800像素，但是其ViewPort仍然是800像素。</p> <h3 id=\"基于sliver的延迟构建\"><a href=\"#基于sliver的延迟构建\" class=\"header-anchor\">#</a> 基于Sliver的延迟构建</h3> <p>通常可滚动组件的子组件可能会非常多、占用的总高度也会非常大；如果要一次性将子组件全部构建出将会非常昂贵！为此，Flutter中提出一个Sliver（中文为“薄片”的意思）概念，如果一个可滚动组件支持Sliver模型，那么该滚动可以将子组件分成好多个“薄片”（Sliver），只有当Sliver出现在视口中时才会去构建它，这种模型也称为“基于Sliver的延迟构建模型”。可滚动组件中有很多都支持基于Sliver的延迟构建模型，如<code>ListView</code>、<code>GridView</code>，但是也有不支持该模型的，如<code>SingleChildScrollView</code>。</p> <h3 id=\"主轴和纵轴\"><a href=\"#主轴和纵轴\" class=\"header-anchor\">#</a> 主轴和纵轴</h3> <p>在可滚动组件的坐标描述中，通常将滚动方向称为主轴，非滚动方向称为纵轴。由于可滚动组件的默认方向一般都是沿垂直方向，所以默认情况下主轴就是指垂直方向，水平方向同理。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter5/clip.html\" class=\"prev\">\n        5.7 剪裁（Clip）\n      </a></span> <span class=\"next\"><a href=\"/chapter6/single_child_scrollview.html\">\n        6.2 SingleChildScrollView\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/163.cfb15292.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter6/listview.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>6.3 ListView | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/9.f995bbe8.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable open\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" aria-current=\"page\" class=\"active sidebar-link\">6.3 ListView</a><ul class=\"sidebar-sub-headers\"></ul></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_6-3-listview\"><a href=\"#_6-3-listview\" class=\"header-anchor\">#</a> 6.3 ListView</h1> <p><code>ListView</code>是最常用的可滚动组件之一，它可以沿一个方向线性排布所有子组件，并且它也支持基于Sliver的延迟构建模型。我们看看ListView的默认构造函数定义：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">ListView</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>  \n  <span class=\"token comment\">//可滚动widget公共参数</span>\n  Axis scrollDirection <span class=\"token operator\">=</span> Axis<span class=\"token punctuation\">.</span>vertical<span class=\"token punctuation\">,</span>\n  bool reverse <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n  ScrollController controller<span class=\"token punctuation\">,</span>\n  bool primary<span class=\"token punctuation\">,</span>\n  ScrollPhysics physics<span class=\"token punctuation\">,</span>\n  EdgeInsetsGeometry padding<span class=\"token punctuation\">,</span>\n  \n  <span class=\"token comment\">//ListView各个构造函数的共同参数  </span>\n  double itemExtent<span class=\"token punctuation\">,</span>\n  bool shrinkWrap <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n  bool addAutomaticKeepAlives <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n  bool addRepaintBoundaries <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n  double cacheExtent<span class=\"token punctuation\">,</span>\n    \n  <span class=\"token comment\">//子widget列表</span>\n  List<span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span> children <span class=\"token operator\">=</span> <span class=\"token keyword\">const</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>上面参数分为两组：第一组是可滚动组件的公共参数，本章第一节中已经介绍过，不再赘述；第二组是<code>ListView</code>各个构造函数（<code>ListView</code>有多个构造函数）的共同参数，我们重点来看看这些参数，：</p> <ul><li><code>itemExtent</code>：该参数如果不为<code>null</code>，则会强制<code>children</code>的“长度”为<code>itemExtent</code>的值；这里的“长度”是指滚动方向上子组件的长度，也就是说如果滚动方向是垂直方向，则<code>itemExtent</code>代表子组件的高度；如果滚动方向为水平方向，则<code>itemExtent</code>就代表子组件的宽度。在<code>ListView</code>中，指定<code>itemExtent</code>比让子组件自己决定自身长度会更高效，这是因为指定<code>itemExtent</code>后，滚动系统可以提前知道列表的长度，而无需每次构建子组件时都去再计算一下，尤其是在滚动位置频繁变化时（滚动系统需要频繁去计算列表高度）。</li> <li><code>shrinkWrap</code>：该属性表示是否根据子组件的总长度来设置<code>ListView</code>的长度，默认值为<code>false</code> 。默认情况下，<code>ListView</code>的会在滚动方向尽可能多的占用空间。当<code>ListView</code>在一个无边界(滚动方向上)的容器中时，<code>shrinkWrap</code>必须为<code>true</code>。</li> <li><code>addAutomaticKeepAlives</code>：该属性表示是否将列表项（子组件）包裹在<code>AutomaticKeepAlive</code> 组件中；典型地，在一个懒加载列表中，如果将列表项包裹在<code>AutomaticKeepAlive</code>中，在该列表项滑出视口时它也不会被GC（垃圾回收），它会使用<code>KeepAliveNotification</code>来保存其状态。如果列表项自己维护其<code>KeepAlive</code>状态，那么此参数必须置为<code>false</code>。</li> <li><code>addRepaintBoundaries</code>：该属性表示是否将列表项（子组件）包裹在<code>RepaintBoundary</code>组件中。当可滚动组件滚动时，将列表项包裹在<code>RepaintBoundary</code>中可以避免列表项重绘，但是当列表项重绘的开销非常小（如一个颜色块，或者一个较短的文本）时，不添加<code>RepaintBoundary</code>反而会更高效。和<code>addAutomaticKeepAlive</code>一样，如果列表项自己维护其<code>KeepAlive</code>状态，那么此参数必须置为<code>false</code>。</li></ul> <blockquote><p>注意：上面这些参数并非<code>ListView</code>特有，在本章后面介绍的其它可滚动组件也可能会拥有这些参数，它们的含义是相同的。</p></blockquote> <h3 id=\"默认构造函数\"><a href=\"#默认构造函数\" class=\"header-anchor\">#</a> 默认构造函数</h3> <p>默认构造函数有一个<code>children</code>参数，它接受一个Widget列表（List<Widget>）。这种方式适合只有少量的子组件的情况，因为这种方式需要将所有<code>children</code>都提前创建好（这需要做大量工作），而不是等到子widget真正显示的时候再创建，也就是说通过默认构造函数构建的ListView没有应用基于Sliver的懒加载模型。实际上通过此方式创建的<code>ListView</code>和使用<code>SingleChildScrollView</code>+<code>Column</code>的方式没有本质的区别。下面是一个例子：</Widget></p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">ListView</span><span class=\"token punctuation\">(</span>\n  shrinkWrap<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span> \n  padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">20.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token keyword\">const</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'I\\'m dedicating every day to you'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">const</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Domestic life was never quite my style'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">const</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'When you smile, you knock me out, I fall apart'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">const</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'And I thought I was so smart'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><blockquote><p>再次强调，可滚动组件通过一个List<Widget>来作为其children属性时，只适用于子组件较少的情况，这是一个通用规律，并非<code>ListView</code>自己的特性，像<code>GridView</code>也是如此。</Widget></p></blockquote> <h3 id=\"listview-builder\"><a href=\"#listview-builder\" class=\"header-anchor\">#</a> ListView.builder</h3> <p><code>ListView.builder</code>适合列表项比较多（或者无限）的情况，因为只有当子组件真正显示的时候才会被创建，也就说通过该构造函数创建的<code>ListView</code>是支持基于Sliver的懒加载模型的。下面看一下<code>ListView.builder</code>的核心参数列表：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>ListView<span class=\"token punctuation\">.</span><span class=\"token function\">builder</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// ListView公共参数已省略  </span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token metadata symbol\">@required</span> IndexedWidgetBuilder itemBuilder<span class=\"token punctuation\">,</span>\n  int itemCount<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><ul><li><code>itemBuilder</code>：它是列表项的构建器，类型为<code>IndexedWidgetBuilder</code>，返回值为一个widget。当列表滚动到具体的<code>index</code>位置时，会调用该构建器构建列表项。</li> <li><code>itemCount</code>：列表项的数量，如果为<code>null</code>，则为无限列表。</li></ul> <blockquote><p>可滚动组件的构造函数如果需要一个列表项Builder，那么通过该构造函数构建的可滚动组件通常就是支持基于Sliver的懒加载模型的，反之则不支持，这是个一般规律。我们在后面在介绍可滚动组件的构造函数时将不再专门说明其是否支持基于Sliver的懒加载模型了。</p></blockquote> <p>下面看一个例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>ListView<span class=\"token punctuation\">.</span><span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>\n    itemCount<span class=\"token punctuation\">:</span> <span class=\"token number\">100</span><span class=\"token punctuation\">,</span>\n    itemExtent<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//强制高度为50.0</span>\n    itemBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> int index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;$index&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>运行效果如图6-2所示：</p> <p><img src=\"/assets/img/6-2.1d35c6fc.png\" alt=\"图6-2\"></p> <h3 id=\"listview-separated\"><a href=\"#listview-separated\" class=\"header-anchor\">#</a> ListView.separated</h3> <p><code>ListView.separated</code>可以在生成的列表项之间添加一个分割组件，它比<code>ListView.builder</code>多了一个<code>separatorBuilder</code>参数，该参数是一个分割组件生成器。</p> <p>下面我们看一个例子：奇数行添加一条蓝色下划线，偶数行添加一条绿色下划线。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">ListView3</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//下划线widget预定义以供复用。  </span>\n    Widget divider1<span class=\"token operator\">=</span><span class=\"token function\">Divider</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    Widget divider2<span class=\"token operator\">=</span><span class=\"token function\">Divider</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>green<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> ListView<span class=\"token punctuation\">.</span><span class=\"token function\">separated</span><span class=\"token punctuation\">(</span>\n        itemCount<span class=\"token punctuation\">:</span> <span class=\"token number\">100</span><span class=\"token punctuation\">,</span>\n        <span class=\"token comment\">//列表项构造器</span>\n        itemBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> int index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">return</span> <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;$index&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token comment\">//分割器构造器</span>\n        separatorBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> int index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">return</span> index<span class=\"token operator\">%</span><span class=\"token number\">2</span><span class=\"token operator\">==</span><span class=\"token number\">0</span><span class=\"token operator\">?</span>divider1<span class=\"token punctuation\">:</span>divider2<span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p><img src=\"/assets/img/6-3.e023acf1.png\" alt=\"图6-3\"></p> <h3 id=\"实例-无限加载列表\"><a href=\"#实例-无限加载列表\" class=\"header-anchor\">#</a> 实例：无限加载列表</h3> <p>假设我们要从数据源异步分批拉取一些数据，然后用<code>ListView</code>展示，当我们滑动到列表末尾时，判断是否需要再去拉取数据，如果是，则去拉取，拉取过程中在表尾显示一个loading，拉取成功后将数据插入列表；如果不需要再去拉取，则在表尾提示&quot;没有更多&quot;。代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">InfiniteListView</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _InfiniteListViewState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_InfiniteListViewState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_InfiniteListViewState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>InfiniteListView<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">static</span> <span class=\"token keyword\">const</span> loadingTag <span class=\"token operator\">=</span> <span class=\"token string\">&quot;##loading##&quot;</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//表尾标记</span>\n  <span class=\"token keyword\">var</span> _words <span class=\"token operator\">=</span> <span class=\"token operator\">&lt;</span>String<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>loadingTag<span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token function\">_retrieveData</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> ListView<span class=\"token punctuation\">.</span><span class=\"token function\">separated</span><span class=\"token punctuation\">(</span>\n      itemCount<span class=\"token punctuation\">:</span> _words<span class=\"token punctuation\">.</span>length<span class=\"token punctuation\">,</span>\n      itemBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">//如果到了表尾</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>_words<span class=\"token punctuation\">[</span>index<span class=\"token punctuation\">]</span> <span class=\"token operator\">==</span> loadingTag<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token comment\">//不足100条，继续获取数据</span>\n          <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>_words<span class=\"token punctuation\">.</span>length <span class=\"token operator\">-</span> <span class=\"token number\">1</span> <span class=\"token operator\">&lt;</span> <span class=\"token number\">100</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">//获取数据</span>\n            <span class=\"token function\">_retrieveData</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token comment\">//加载时显示loading</span>\n            <span class=\"token keyword\">return</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n              padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">16.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span>\n                  width<span class=\"token punctuation\">:</span> <span class=\"token number\">24.0</span><span class=\"token punctuation\">,</span>\n                  height<span class=\"token punctuation\">:</span> <span class=\"token number\">24.0</span><span class=\"token punctuation\">,</span>\n                  child<span class=\"token punctuation\">:</span> <span class=\"token function\">CircularProgressIndicator</span><span class=\"token punctuation\">(</span>strokeWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">2.0</span><span class=\"token punctuation\">)</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">//已经加载了100条数据，不再获取数据。</span>\n            <span class=\"token keyword\">return</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n                alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n                padding<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">16.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;没有更多了&quot;</span><span class=\"token punctuation\">,</span> style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span>\n        <span class=\"token punctuation\">}</span>\n        <span class=\"token comment\">//显示单词列表项</span>\n        <span class=\"token keyword\">return</span> <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>_words<span class=\"token punctuation\">[</span>index<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      separatorBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> index<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">Divider</span><span class=\"token punctuation\">(</span>height<span class=\"token punctuation\">:</span> <span class=\"token number\">.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_retrieveData</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    Future<span class=\"token punctuation\">.</span><span class=\"token function\">delayed</span><span class=\"token punctuation\">(</span><span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">//重新构建列表</span>\n\t\t_words<span class=\"token punctuation\">.</span><span class=\"token function\">insertAll</span><span class=\"token punctuation\">(</span>_words<span class=\"token punctuation\">.</span>length <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n          <span class=\"token comment\">//每次生成20个单词</span>\n          <span class=\"token function\">generateWordPairs</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">take</span><span class=\"token punctuation\">(</span><span class=\"token number\">20</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> e<span class=\"token punctuation\">.</span>asPascalCase<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toList</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n      \t<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行后效果如图6-4、6-5所示：</p> <p><img src=\"/assets/img/6-4.288d3ff1.png\" alt=\"图6-4\"><img src=\"/assets/img/6-5.2947b7f7.png\" alt=\"图6-5\"></p> <p>代码比较简单，读者可以参照代码中的注释理解，故不再赘述。需要说明的是，<code>_retrieveData()</code>的功能是模拟从数据源异步获取数据，我们使用english_words包的<code>generateWordPairs()</code>方法每次生成20个单词。</p> <h3 id=\"添加固定列表头\"><a href=\"#添加固定列表头\" class=\"header-anchor\">#</a> 添加固定列表头</h3> <p>很多时候我们需要给列表添加一个固定表头，比如我们想实现一个商品列表，需要在列表顶部添加一个“商品列表”标题，期望的效果如图6-6所示：</p> <p><img src=\"/assets/img/6-6.e48bfae5.png\" alt=\"图6-6\"></p> <p>我们按照之前经验，写出如下代码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token metadata symbol\">@override</span>\nWidget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span><span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;商品列表&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    ListView<span class=\"token punctuation\">.</span><span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>itemBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> int index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">return</span> <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;$index&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>然后运行，发现并没有出现我们期望的效果，相反触发了一个异常；</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>Error caught by rendering library, thrown during performResize()。\nVertical viewport was given unbounded height ...\n</code></pre></div><p>从异常信息中我们可以看到是因为<code>ListView</code>高度边界无法确定引起，所以解决的办法也很明显，我们需要给<code>ListView</code>指定边界，我们通过<code>SizedBox</code>指定一个列表高度看看是否生效：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n<span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span>\n    height<span class=\"token punctuation\">:</span> <span class=\"token number\">400</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//指定列表高度为400</span>\n    child<span class=\"token punctuation\">:</span> ListView<span class=\"token punctuation\">.</span><span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>itemBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> int index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">return</span> <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;$index&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n</code></pre></div><p>运行效果如图6-7所示：</p> <p><img src=\"/assets/img/6-7.c443522f.png\" alt=\"图6-7\"></p> <p>可以看到，现在没有触发异常并且列表已经显示出来了，但是我们的手机屏幕高度要大于400，所以底部会有一些空白。那如果我们要实现列表铺满除表头以外的屏幕空间应该怎么做？直观的方法是我们去动态计算，用屏幕高度减去状态栏、导航栏、表头的高度即为剩余屏幕高度，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n<span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span>\n  <span class=\"token comment\">//Material设计规范中状态栏、导航栏、ListTile高度分别为24、56、56 </span>\n  height<span class=\"token punctuation\">:</span> MediaQuery<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>size<span class=\"token punctuation\">.</span>height<span class=\"token operator\">-</span><span class=\"token number\">24</span><span class=\"token operator\">-</span><span class=\"token number\">56</span><span class=\"token operator\">-</span><span class=\"token number\">56</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> ListView<span class=\"token punctuation\">.</span><span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>itemBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> int index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;$index&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>    \n</code></pre></div><p>运行效果如下图6-8所示：</p> <p><img src=\"/assets/img/6-8.e48bfae5.png\" alt=\"图6-8\"></p> <p>可以看到，我们期望的效果实现了，但是这种方法并不优雅，如果页面布局发生变化，比如表头布局调整导致表头高度改变，那么剩余空间的高度就得重新计算。那么有什么方法可以自动拉伸<code>ListView</code>以填充屏幕剩余空间的方法吗？当然有！答案就是<code>Flex</code>。前面已经介绍过在弹性布局中，可以使用<code>Expanded</code>自动拉伸组件大小，并且我们也说过<code>Column</code>是继承自<code>Flex</code>的，所以我们可以直接使用<code>Column</code>+<code>Expanded</code>来实现，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token metadata symbol\">@override</span>\nWidget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span><span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;商品列表&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Expanded</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> ListView<span class=\"token punctuation\">.</span><span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>itemBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> int index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">return</span> <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;$index&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行后，和上图一样，完美实现了！</p> <h3 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h3> <p>本节主要介绍了<code>ListView</code>的一些公共参数以及常用的构造函数。不同的构造函数对应了不同的列表项生成模型，如果需要自定义列表项生成模型，可以通过<code>ListView.custom</code>来自定义，它需要实现一个<code>SliverChildDelegate</code>用来给ListView生成列表项组件，更多详情请参考API文档。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter6/single_child_scrollview.html\" class=\"prev\">\n        6.2 SingleChildScrollView\n      </a></span> <span class=\"next\"><a href=\"/chapter6/gridview.html\">\n        6.4 GridView\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/9.f995bbe8.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter6/scroll_controller.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>6.6 滚动监听及控制 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/46.1c911d7c.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable open\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" aria-current=\"page\" class=\"active sidebar-link\">6.6 滚动监听及控制</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/chapter6/scroll_controller.html#_6-6-1-scrollcontroller\" class=\"sidebar-link\">6.6.1 ScrollController</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter6/scroll_controller.html#_6-6-2-滚动监听\" class=\"sidebar-link\">6.6.2 滚动监听</a></li></ul></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_6-6-滚动监听及控制\"><a href=\"#_6-6-滚动监听及控制\" class=\"header-anchor\">#</a> 6.6 滚动监听及控制</h1> <p>在前几节中，我们介绍了Flutter中常用的可滚动组件，也说过可以用<code>ScrollController</code>来控制可滚动组件的滚动位置，本节先介绍一下<code>ScrollController</code>，然后以<code>ListView</code>为例，展示一下<code>ScrollController</code>的具体用法。最后，再介绍一下路由切换时如何来保存滚动位置。</p> <h2 id=\"_6-6-1-scrollcontroller\"><a href=\"#_6-6-1-scrollcontroller\" class=\"header-anchor\">#</a> 6.6.1 ScrollController</h2> <p><code>ScrollController</code>构造函数如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">ScrollController</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  double initialScrollOffset <span class=\"token operator\">=</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//初始滚动位置</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>keepScrollOffset <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span><span class=\"token comment\">//是否保存滚动位置</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>我们介绍一下<code>ScrollController</code>常用的属性和方法：</p> <ul><li><code>offset</code>：可滚动组件当前的滚动位置。</li> <li><code>jumpTo(double offset)</code>、<code>animateTo(double offset,...)</code>：这两个方法用于跳转到指定的位置，它们不同之处在于，后者在跳转时会执行一个动画，而前者不会。</li></ul> <p><code>ScrollController</code>还有一些属性和方法，我们将在后面原理部分解释。</p> <h4 id=\"滚动监听\"><a href=\"#滚动监听\" class=\"header-anchor\">#</a> 滚动监听</h4> <p><code>ScrollController</code>间接继承自<code>Listenable</code>，我们可以根据<code>ScrollController</code>来监听滚动事件，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>controller<span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span><span class=\"token function\">print</span><span class=\"token punctuation\">(</span>controller<span class=\"token punctuation\">.</span>offset<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n</code></pre></div><h3 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h3> <p>我们创建一个<code>ListView</code>，当滚动位置发生变化时，我们先打印出当前滚动位置，然后判断当前位置是否超过1000像素，如果超过则在屏幕右下角显示一个“返回顶部”的按钮，该按钮点击后可以使ListView恢复到初始位置；如果没有超过1000像素，则隐藏“返回顶部”按钮。代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">ScrollControllerTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  ScrollControllerTestRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">ScrollControllerTestRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">ScrollControllerTestRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>ScrollControllerTestRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  ScrollController _controller <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">ScrollController</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  bool showToTopBtn <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//是否显示“返回到顶部”按钮</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//监听滚动事件，打印滚动位置</span>\n    _controller<span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>_controller<span class=\"token punctuation\">.</span>offset<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//打印滚动位置</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>_controller<span class=\"token punctuation\">.</span>offset <span class=\"token operator\">&lt;</span> <span class=\"token number\">1000</span> <span class=\"token operator\">&amp;&amp;</span> showToTopBtn<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          showToTopBtn <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>_controller<span class=\"token punctuation\">.</span>offset <span class=\"token operator\">&gt;=</span> <span class=\"token number\">1000</span> <span class=\"token operator\">&amp;&amp;</span> showToTopBtn <span class=\"token operator\">==</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          showToTopBtn <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//为了避免内存泄露，需要调用_controller.dispose</span>\n    _controller<span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n      appBar<span class=\"token punctuation\">:</span> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;滚动控制&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      body<span class=\"token punctuation\">:</span> <span class=\"token function\">Scrollbar</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> ListView<span class=\"token punctuation\">.</span><span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>\n            itemCount<span class=\"token punctuation\">:</span> <span class=\"token number\">100</span><span class=\"token punctuation\">,</span>\n            itemExtent<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//列表项高度固定时，显式指定高度是一个好习惯(性能消耗小)</span>\n            controller<span class=\"token punctuation\">:</span> _controller<span class=\"token punctuation\">,</span>\n            itemBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token keyword\">return</span> <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;$index&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      floatingActionButton<span class=\"token punctuation\">:</span> <span class=\"token operator\">!</span>showToTopBtn <span class=\"token operator\">?</span> <span class=\"token keyword\">null</span> <span class=\"token punctuation\">:</span> <span class=\"token function\">FloatingActionButton</span><span class=\"token punctuation\">(</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>arrow_upward<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">//返回到顶部时执行动画</span>\n            _controller<span class=\"token punctuation\">.</span><span class=\"token function\">animateTo</span><span class=\"token punctuation\">(</span><span class=\"token number\">.0</span><span class=\"token punctuation\">,</span>\n                duration<span class=\"token punctuation\">:</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>milliseconds<span class=\"token punctuation\">:</span> <span class=\"token number\">200</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                curve<span class=\"token punctuation\">:</span> Curves<span class=\"token punctuation\">.</span>ease\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>代码说明已经包含在注释里，下面我们看看运行效果：</p> <p><img src=\"/assets/img/6-14.1b612437.png\" alt=\"图6-14\"><img src=\"/assets/img/6-15.00ac68b6.png\" alt=\"图6-15\"></p> <p>由于列表项高度为50像素，当滑动到第20个列表项后，右下角“返回顶部”按钮会显示，点击该按钮，ListView会在返回顶部的过程中执行一个滚动动画，动画时间是200毫秒，动画曲线是<code>Curves.ease</code>，关于动画的详细内容我们将在后面“动画”一章中详细介绍。</p> <h3 id=\"滚动位置恢复\"><a href=\"#滚动位置恢复\" class=\"header-anchor\">#</a> 滚动位置恢复</h3> <p><code>PageStorage</code>是一个用于保存页面(路由)相关数据的组件，它并不会影响子树的UI外观，其实，<code>PageStorage</code>是一个功能型组件，它拥有一个存储桶（bucket），子树中的Widget可以通过指定不同的<code>PageStorageKey</code>来存储各自的数据或状态。</p> <p>每次滚动结束，可滚动组件都会将滚动位置<code>offset</code>存储到<code>PageStorage</code>中，当可滚动组件重新创建时再恢复。如果<code>ScrollController.keepScrollOffset</code>为<code>false</code>，则滚动位置将不会被存储，可滚动组件重新创建时会使用<code>ScrollController.initialScrollOffset</code>；<code>ScrollController.keepScrollOffset</code>为<code>true</code>时，可滚动组件在<strong>第一次</strong>创建时，会滚动到<code>initialScrollOffset</code>处，因为这时还没有存储过滚动位置。在接下来的滚动中就会存储、恢复滚动位置，而<code>initialScrollOffset</code>会被忽略。</p> <p>当一个路由中包含多个可滚动组件时，如果你发现在进行一些跳转或切换操作后，滚动位置不能正确恢复，这时你可以通过显式指定<code>PageStorageKey</code>来分别跟踪不同的可滚动组件的位置，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">ListView</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> <span class=\"token function\">PageStorageKey</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token function\">ListView</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> <span class=\"token function\">PageStorageKey</span><span class=\"token punctuation\">(</span><span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>不同的<code>PageStorageKey</code>，需要不同的值，这样才可以为不同可滚动组件保存其滚动位置。</p> <blockquote><p>注意：一个路由中包含多个可滚动组件时，如果要分别跟踪它们的滚动位置，并非一定就得给他们分别提供<code>PageStorageKey</code>。这是因为Scrollable本身是一个StatefulWidget，它的状态中也会保存当前滚动位置，所以，只要可滚动组件本身没有被从树上detach掉，那么其State就不会销毁(dispose)，滚动位置就不会丢失。只有当Widget发生结构变化，导致可滚动组件的State销毁或重新构建时才会丢失状态，这种情况就需要显式指定<code>PageStorageKey</code>，通过<code>PageStorage</code>来存储滚动位置，一个典型的场景是在使用<code>TabBarView</code>时，在Tab发生切换时，Tab页中的可滚动组件的State就会销毁，这时如果想恢复滚动位置就需要指定<code>PageStorageKey</code>。</p></blockquote> <h3 id=\"scrollposition\"><a href=\"#scrollposition\" class=\"header-anchor\">#</a> ScrollPosition</h3> <p>ScrollPosition是用来保存可滚动组件的滚动位置的。一个<code>ScrollController</code>对象可以同时被多个可滚动组件使用，<code>ScrollController</code>会为每一个可滚动组件创建一个<code>ScrollPosition</code>对象，这些<code>ScrollPosition</code>保存在<code>ScrollController</code>的<code>positions</code>属性中（<code>List&lt;ScrollPosition&gt;</code>）。<code>ScrollPosition</code>是真正保存滑动位置信息的对象，<code>offset</code>只是一个便捷属性：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>double <span class=\"token keyword\">get</span> offset <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> position<span class=\"token punctuation\">.</span>pixels<span class=\"token punctuation\">;</span>\n</code></pre></div><p>一个<code>ScrollController</code>虽然可以对应多个可滚动组件，但是有一些操作，如读取滚动位置<code>offset</code>，则需要一对一！但是我们仍然可以在一对多的情况下，通过其它方法读取滚动位置，举个例子，假设一个<code>ScrollController</code>同时被两个可滚动组件使用，那么我们可以通过如下方式分别读取他们的滚动位置：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\ncontroller<span class=\"token punctuation\">.</span>positions<span class=\"token punctuation\">.</span><span class=\"token function\">elementAt</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>pixels\ncontroller<span class=\"token punctuation\">.</span>positions<span class=\"token punctuation\">.</span><span class=\"token function\">elementAt</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>pixels\n<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>    \n</code></pre></div><p>我们可以通过<code>controller.positions.length</code>来确定<code>controller</code>被几个可滚动组件使用。</p> <h4 id=\"scrollposition的方法\"><a href=\"#scrollposition的方法\" class=\"header-anchor\">#</a> ScrollPosition的方法</h4> <p><code>ScrollPosition</code>有两个常用方法：<code>animateTo()</code> 和 <code>jumpTo()</code>，它们是真正来控制跳转滚动位置的方法，<code>ScrollController</code>的这两个同名方法，内部最终都会调用<code>ScrollPosition</code>的。</p> <h3 id=\"scrollcontroller控制原理\"><a href=\"#scrollcontroller控制原理\" class=\"header-anchor\">#</a> ScrollController控制原理</h3> <p>我们来介绍一下<code>ScrollController</code>的另外三个方法：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>ScrollPosition <span class=\"token function\">createScrollPosition</span><span class=\"token punctuation\">(</span>\n    ScrollPhysics physics<span class=\"token punctuation\">,</span>\n    ScrollContext context<span class=\"token punctuation\">,</span>\n    ScrollPosition oldPosition<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">void</span> <span class=\"token function\">attach</span><span class=\"token punctuation\">(</span>ScrollPosition position<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">void</span> <span class=\"token function\">detach</span><span class=\"token punctuation\">(</span>ScrollPosition position<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">;</span>\n</code></pre></div><p>当<code>ScrollController</code>和可滚动组件关联时，可滚动组件首先会调用<code>ScrollController</code>的<code>createScrollPosition()</code>方法来创建一个<code>ScrollPosition</code>来存储滚动位置信息，接着，可滚动组件会调用<code>attach()</code>方法，将创建的<code>ScrollPosition</code>添加到<code>ScrollController</code>的<code>positions</code>属性中，这一步称为“注册位置”，只有注册后<code>animateTo()</code> 和 <code>jumpTo()</code>才可以被调用。</p> <p>当可滚动组件销毁时，会调用<code>ScrollController</code>的<code>detach()</code>方法，将其<code>ScrollPosition</code>对象从<code>ScrollController</code>的<code>positions</code>属性中移除，这一步称为“注销位置”，注销后<code>animateTo()</code> 和 <code>jumpTo()</code> 将不能再被调用。</p> <p>需要注意的是，<code>ScrollController</code>的<code>animateTo()</code> 和 <code>jumpTo()</code>内部会调用所有<code>ScrollPosition</code>的<code>animateTo()</code> 和 <code>jumpTo()</code>，以实现所有和该<code>ScrollController</code>关联的可滚动组件都滚动到指定的位置。</p> <h2 id=\"_6-6-2-滚动监听\"><a href=\"#_6-6-2-滚动监听\" class=\"header-anchor\">#</a> 6.6.2 滚动监听</h2> <p>Flutter Widget树中子Widget可以通过发送通知（Notification）与父(包括祖先)Widget通信。父级组件可以通过<code>NotificationListener</code>组件来监听自己关注的通知，这种通信方式类似于Web开发中浏览器的事件冒泡，我们在Flutter中沿用“冒泡”这个术语，关于通知冒泡我们将在后面“事件处理与通知”一章中详细介绍。</p> <p>可滚动组件在滚动时会发送<code>ScrollNotification</code>类型的通知，<code>ScrollBar</code>正是通过监听滚动通知来实现的。通过<code>NotificationListener</code>监听滚动事件和通过<code>ScrollController</code>有两个主要的不同：</p> <ol><li>通过NotificationListener可以在从可滚动组件到widget树根之间任意位置都能监听。而<code>ScrollController</code>只能和具体的可滚动组件关联后才可以。</li> <li>收到滚动事件后获得的信息不同；<code>NotificationListener</code>在收到滚动事件时，通知中会携带当前滚动位置和ViewPort的一些信息，而<code>ScrollController</code>只能获取当前滚动位置。</li></ol> <h3 id=\"示例-2\"><a href=\"#示例-2\" class=\"header-anchor\">#</a> 示例</h3> <p>下面，我们监听<code>ListView</code>的滚动通知，然后显示当前滚动进度百分比：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">ScrollNotificationTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _ScrollNotificationTestRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span>\n      <span class=\"token keyword\">new</span> <span class=\"token class-name\">_ScrollNotificationTestRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_ScrollNotificationTestRouteState</span>\n    <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>ScrollNotificationTestRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  String _progress <span class=\"token operator\">=</span> <span class=\"token string\">&quot;0%&quot;</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//保存进度百分比</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Scrollbar</span><span class=\"token punctuation\">(</span> <span class=\"token comment\">//进度条</span>\n      <span class=\"token comment\">// 监听滚动通知</span>\n      child<span class=\"token punctuation\">:</span> NotificationListener<span class=\"token operator\">&lt;</span>ScrollNotification<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n        onNotification<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>ScrollNotification notification<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          double progress <span class=\"token operator\">=</span> notification<span class=\"token punctuation\">.</span>metrics<span class=\"token punctuation\">.</span>pixels <span class=\"token operator\">/</span>\n              notification<span class=\"token punctuation\">.</span>metrics<span class=\"token punctuation\">.</span>maxScrollExtent<span class=\"token punctuation\">;</span>\n          <span class=\"token comment\">//重新构建</span>\n          <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            _progress <span class=\"token operator\">=</span> <span class=\"token string\">&quot;${(progress * 100).toInt()}%&quot;</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;BottomEdge: ${notification.metrics.extentAfter == 0}&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token comment\">//return true; //放开此行注释后，进度条将失效</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Stack</span><span class=\"token punctuation\">(</span>\n          alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            ListView<span class=\"token punctuation\">.</span><span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>\n                itemCount<span class=\"token punctuation\">:</span> <span class=\"token number\">100</span><span class=\"token punctuation\">,</span>\n                itemExtent<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n                itemBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                  <span class=\"token keyword\">return</span> <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;$index&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                <span class=\"token punctuation\">}</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token function\">CircleAvatar</span><span class=\"token punctuation\">(</span>  <span class=\"token comment\">//显示进度百分比</span>\n              radius<span class=\"token punctuation\">:</span> <span class=\"token number\">30.0</span><span class=\"token punctuation\">,</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>_progress<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              backgroundColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>black54<span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行结果如图6-16所示：</p> <p><img src=\"/assets/img/6-16.20d0839c.png\" alt=\"图6-16\"></p> <p>在接收到滚动事件时，参数类型为<code>ScrollNotification</code>，它包括一个<code>metrics</code>属性，它的类型是<code>ScrollMetrics</code>，该属性包含当前ViewPort及滚动位置等信息：</p> <ul><li><code>pixels</code>：当前滚动位置。</li> <li><code>maxScrollExtent</code>：最大可滚动长度。</li> <li><code>extentBefore</code>：滑出ViewPort顶部的长度；此示例中相当于顶部滑出屏幕上方的列表长度。</li> <li><code>extentInside</code>：ViewPort内部长度；此示例中屏幕显示的列表部分的长度。</li> <li><code>extentAfter</code>：列表中未滑入ViewPort部分的长度；此示例中列表底部未显示到屏幕范围部分的长度。</li> <li><code>atEdge</code>：是否滑到了可滚动组件的边界（此示例中相当于列表顶或底部）。</li></ul> <p>ScrollMetrics还有一些其它属性，读者可以自行查阅API文档。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter6/custom_scrollview.html\" class=\"prev\">\n        6.5 CustomScrollView\n      </a></span> <span class=\"next\"><a href=\"/chapter7/willpopscope.html\">\n        7.1 导航返回拦截（WillPopScope）\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/46.1c911d7c.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter6/single_child_scrollview.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>6.2 SingleChildScrollView | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/101.60ad385d.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable open\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" aria-current=\"page\" class=\"active sidebar-link\">6.2 SingleChildScrollView</a><ul class=\"sidebar-sub-headers\"></ul></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_6-2-singlechildscrollview\"><a href=\"#_6-2-singlechildscrollview\" class=\"header-anchor\">#</a> 6.2 SingleChildScrollView</h1> <p><code>SingleChildScrollView</code>类似于Android中的<code>ScrollView</code>，它只能接收一个子组件。定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">SingleChildScrollView</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>scrollDirection <span class=\"token operator\">=</span> Axis<span class=\"token punctuation\">.</span>vertical<span class=\"token punctuation\">,</span> <span class=\"token comment\">//滚动方向，默认是垂直方向</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>reverse <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span> \n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>padding<span class=\"token punctuation\">,</span> \n  bool primary<span class=\"token punctuation\">,</span> \n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>physics<span class=\"token punctuation\">,</span> \n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>controller<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>除了上一节我们介绍过的可滚动组件的通用属性外，我们重点看一下<code>reverse</code>和<code>primary</code>两个属性：</p> <ul><li><code>reverse</code>：该属性API文档解释是：是否按照阅读方向相反的方向滑动，如：<code>scrollDirection</code>值为<code>Axis.horizontal</code>，如果阅读方向是从左到右(取决于语言环境，阿拉伯语就是从右到左)。<code>reverse</code>为<code>true</code>时，那么滑动方向就是从右往左。其实此属性本质上是决定可滚动组件的初始滚动位置是在“头”还是“尾”，取<code>false</code>时，初始滚动位置在“头”，反之则在“尾”，读者可以自己试验。</li> <li><code>primary</code>：指是否使用widget树中默认的<code>PrimaryScrollController</code>；当滑动方向为垂直方向（<code>scrollDirection</code>值为<code>Axis.vertical</code>）并且没有指定<code>controller</code>时，<code>primary</code>默认为<code>true</code>.</li></ul> <p>需要注意的是，通常<code>SingleChildScrollView</code>只应在期望的内容不会超过屏幕太多时使用，这是因为<code>SingleChildScrollView</code>不支持基于Sliver的延迟实例化模型，所以如果预计视口可能包含超出屏幕尺寸太多的内容时，那么使用<code>SingleChildScrollView</code>将会非常昂贵（性能差），此时应该使用一些支持Sliver延迟加载的可滚动组件，如<code>ListView</code>。</p> <h3 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h3> <p>下面是一个将大写字母A-Z沿垂直方向显示的例子，由于垂直方向空间会超过屏幕视口高度，所以我们使用<code>SingleChildScrollView</code>：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">SingleChildScrollViewTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    String str <span class=\"token operator\">=</span> <span class=\"token string\">&quot;ABCDEFGHIJKLMNOPQRSTUVWXYZ&quot;</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Scrollbar</span><span class=\"token punctuation\">(</span> <span class=\"token comment\">// 显示进度条</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">SingleChildScrollView</span><span class=\"token punctuation\">(</span>\n        padding<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">16.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span> \n            <span class=\"token comment\">//动态创建一个List&lt;Widget&gt;  </span>\n            children<span class=\"token punctuation\">:</span> str<span class=\"token punctuation\">.</span><span class=\"token function\">split</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;&quot;</span><span class=\"token punctuation\">)</span> \n                <span class=\"token comment\">//每一个字母都用一个Text显示,字体为原来的两倍</span>\n                <span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>c<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>c<span class=\"token punctuation\">,</span> textScaleFactor<span class=\"token punctuation\">:</span> <span class=\"token number\">2.0</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> \n                <span class=\"token punctuation\">.</span><span class=\"token function\">toList</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行效果如图6-1所示：</p> <p><img src=\"/assets/img/6-1.a5c8558b.png\" alt=\"图6-1\"></p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter6/intro.html\" class=\"prev\">\n        6.1 可滚动组件简介\n      </a></span> <span class=\"next\"><a href=\"/chapter6/listview.html\">\n        6.3 ListView\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/101.60ad385d.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter7/dailog.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>7.6 对话框详解 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/3.cc1736a7.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable open\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" aria-current=\"page\" class=\"active sidebar-link\">7.6 对话框详解</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/chapter7/dailog.html#_7-6-1-使用对话框\" class=\"sidebar-link\">7.6.1 使用对话框</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter7/dailog.html#_7-6-2-对话框打开动画及遮罩\" class=\"sidebar-link\">7.6.2 对话框打开动画及遮罩</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter7/dailog.html#_7-6-3-对话框实现原理\" class=\"sidebar-link\">7.6.3 对话框实现原理</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter7/dailog.html#_7-6-4-对话框状态管理\" class=\"sidebar-link\">7.6.4 对话框状态管理</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter7/dailog.html#_7-6-5-其它类型的对话框\" class=\"sidebar-link\">7.6.5 其它类型的对话框</a></li></ul></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_7-6-对话框详解\"><a href=\"#_7-6-对话框详解\" class=\"header-anchor\">#</a> 7.6 对话框详解</h1> <p>本节将详细介绍一下Flutter中对话框的使用方式、实现原理、样式定制及状态管理。</p> <h2 id=\"_7-6-1-使用对话框\"><a href=\"#_7-6-1-使用对话框\" class=\"header-anchor\">#</a> 7.6.1 使用对话框</h2> <p>对话框本质上也是UI布局，通常一个对话框会包含标题、内容，以及一些操作按钮，为此，Material库中提供了一些现成的对话框组件来用于快速的构建出一个完整的对话框。</p> <h3 id=\"alertdialog\"><a href=\"#alertdialog\" class=\"header-anchor\">#</a> AlertDialog</h3> <p>下面我们主要介绍一下Material库中的<code>AlertDialog</code>组件，它的构造函数定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">const</span> <span class=\"token function\">AlertDialog</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  Key key<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>title<span class=\"token punctuation\">,</span> <span class=\"token comment\">//对话框标题组件</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>titlePadding<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 标题填充</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>titleTextStyle<span class=\"token punctuation\">,</span> <span class=\"token comment\">//标题文本样式</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>content<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 对话框内容组件</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>contentPadding <span class=\"token operator\">=</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">fromLTRB</span><span class=\"token punctuation\">(</span><span class=\"token number\">24.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">20.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">24.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">24.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//内容的填充</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>contentTextStyle<span class=\"token punctuation\">,</span><span class=\"token comment\">// 内容文本样式</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>actions<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 对话框操作按钮组</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>backgroundColor<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 对话框背景色</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>elevation<span class=\"token punctuation\">,</span><span class=\"token comment\">// 对话框的阴影</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>semanticLabel<span class=\"token punctuation\">,</span> <span class=\"token comment\">//对话框语义化标签(用于读屏软件)</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>shape<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 对话框外形</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>参数都比较简单，不在赘述。下面我们看一个例子，假如我们要在删除文件时弹出一个确认对话框，该对话框如图7-10所示：</p> <p><img src=\"/assets/img/7-10.47645234.png\" alt=\"图7-10\"></p> <p>该对话框样式代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">AlertDialog</span><span class=\"token punctuation\">(</span>\n  title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;提示&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  content<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;您确定要删除当前文件吗?&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  actions<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;取消&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//关闭对话框</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;删除&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">// ... 执行删除操作</span>\n        Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span><span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//关闭对话框</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>实现代码很简单，不在赘述。唯一需要注意的是我们是通过<code>Navigator.of(context).pop(…)</code>方法来关闭对话框的，这和路由返回的方式是一致的，并且都可以返回一个结果数据。现在，对话框我们已经构建好了，那么如何将它弹出来呢？还有对话框返回的数据应如何被接收呢？这些问题的答案都在<code>showDialog()</code>方法中。</p> <p><code>showDialog()</code>是Material组件库提供的一个用于弹出Material风格对话框的方法，签名如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Future<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span> showDialog<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@required</span> BuildContext context<span class=\"token punctuation\">,</span>\n  bool barrierDismissible <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//点击对话框barrier(遮罩)时是否关闭它</span>\n  WidgetBuilder builder<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 对话框UI的builder</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>该方法只有两个参数，含义见注释。该方法返回一个<code>Future</code>，它正是用于接收对话框的返回值：如果我们是通过点击对话框遮罩关闭的，则<code>Future</code>的值为<code>null</code>，否则为我们通过<code>Navigator.of(context).pop(result)</code>返回的result值，下面我们看一下整个示例：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//点击该按钮后弹出对话框</span>\n<span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;对话框1&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//弹出对话框并等待其关闭</span>\n    bool delete <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">showDeleteConfirmDialog1</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>delete <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;取消删除&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;已确认删除&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token comment\">//... 删除文件</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n\n<span class=\"token comment\">// 弹出对话框</span>\nFuture<span class=\"token operator\">&lt;</span>bool<span class=\"token operator\">&gt;</span> <span class=\"token function\">showDeleteConfirmDialog1</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> showDialog<span class=\"token operator\">&lt;</span>bool<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n    context<span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">,</span>\n    builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token function\">AlertDialog</span><span class=\"token punctuation\">(</span>\n        title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;提示&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        content<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;您确定要删除当前文件吗?&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        actions<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n          <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;取消&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 关闭对话框</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;删除&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token comment\">//关闭对话框并返回true</span>\n              Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span><span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>示例运行后，我们点击对话框“取消”按钮或遮罩，控制台就会输出&quot;取消删除&quot;，如果点击“删除”按钮，控制台就会输出&quot;已确认删除&quot;。</p> <blockquote><p>注意：如果<code>AlertDialog</code>的内容过长，内容将会溢出，这在很多时候可能不是我们期望的，所以如果对话框内容过长时，可以用<code>SingleChildScrollView</code>将内容包裹起来。</p></blockquote> <h3 id=\"simpledialog\"><a href=\"#simpledialog\" class=\"header-anchor\">#</a> SimpleDialog</h3> <p><code>SimpleDialog</code>也是Material组件库提供的对话框，它会展示一个列表，用于列表选择的场景。下面是一个选择APP语言的示例，运行结果如图7-11。</p> <p><img src=\"/assets/img/7-11.8cffb68f.png\" alt=\"图7-11\"></p> <p>实现代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Future<span class=\"token operator\">&lt;</span><span class=\"token keyword\">void</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">changeLanguage</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n  int i <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> showDialog<span class=\"token operator\">&lt;</span>int<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n      context<span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">,</span>\n      builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">return</span> <span class=\"token function\">SimpleDialog</span><span class=\"token punctuation\">(</span>\n          title<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'请选择语言'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            <span class=\"token function\">SimpleDialogOption</span><span class=\"token punctuation\">(</span>\n              onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token comment\">// 返回1</span>\n                Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n                padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">symmetric</span><span class=\"token punctuation\">(</span>vertical<span class=\"token punctuation\">:</span> <span class=\"token number\">6</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'中文简体'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token function\">SimpleDialogOption</span><span class=\"token punctuation\">(</span>\n              onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token comment\">// 返回2</span>\n                Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n                padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">symmetric</span><span class=\"token punctuation\">(</span>vertical<span class=\"token punctuation\">:</span> <span class=\"token number\">6</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'美国英语'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>i <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;选择了：${i == 1 ? &quot;</span>中文简体<span class=\"token string\">&quot; : &quot;</span>美国英语<span class=\"token string\">&quot;}&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>列表项组件我们使用了<code>SimpleDialogOption</code>组件来包装了一下，它相当于一个FlatButton，只不过按钮文案是左对齐的，并且padding较小。上面示例运行后，用户选择一种语言后，控制台就会打印出它。</p> <h3 id=\"dialog\"><a href=\"#dialog\" class=\"header-anchor\">#</a> Dialog</h3> <p>实际上<code>AlertDialog</code>和<code>SimpleDialog</code>都使用了<code>Dialog</code>类。由于<code>AlertDialog</code>和<code>SimpleDialog</code>中使用了<code>IntrinsicWidth</code>来尝试通过子组件的实际尺寸来调整自身尺寸，这就导致他们的子组件不能是延迟加载模型的组件（如<code>ListView</code>、<code>GridView</code> 、 <code>CustomScrollView</code>等），如下面的代码运行后会报错。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">AlertDialog</span><span class=\"token punctuation\">(</span>\n  content<span class=\"token punctuation\">:</span> <span class=\"token function\">ListView</span><span class=\"token punctuation\">(</span>\n    children<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token comment\">//省略</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>如果我们就是需要嵌套一个<code>ListView</code>应该怎么做？这时，我们可以直接使用<code>Dialog</code>类，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Dialog</span><span class=\"token punctuation\">(</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">ListView</span><span class=\"token punctuation\">(</span>\n    children<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token comment\">//省略</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>下面我们看一个弹出一个有30个列表项的对话框示例，运行效果如图7-12所示：</p> <p><img src=\"/assets/img/7-12.df06c0b8.png\" alt=\"图7-12\"></p> <p>实现代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Future<span class=\"token operator\">&lt;</span><span class=\"token keyword\">void</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">showListDialog</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n  int index <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> showDialog<span class=\"token operator\">&lt;</span>int<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n    context<span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">,</span>\n    builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">var</span> child <span class=\"token operator\">=</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n        children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n          <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;请选择&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">Expanded</span><span class=\"token punctuation\">(</span>\n              child<span class=\"token punctuation\">:</span> ListView<span class=\"token punctuation\">.</span><span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>\n            itemCount<span class=\"token punctuation\">:</span> <span class=\"token number\">30</span><span class=\"token punctuation\">,</span>\n            itemBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> int index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token keyword\">return</span> <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>\n                title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;$index&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                onTap<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span>index<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token comment\">//使用AlertDialog会报错</span>\n      <span class=\"token comment\">//return AlertDialog(content: child);</span>\n      <span class=\"token keyword\">return</span> <span class=\"token function\">Dialog</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">:</span> child<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>index <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;点击了：$index&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>现在，我们己经介绍完了<code>AlertDialog</code>、<code>SimpleDialog</code>以及<code>Dialog</code>。上面的示例中，我们在调用<code>showDialog</code>时，在<code>builder</code>中都是构建了这三个对话框组件的一种，可能有些读者会惯性的以为在<code>builder</code>中只能返回这三者之一，其实这不是必须的！就拿<code>Dialog</code>的示例来举例，我们完全可以用下面的代码来替代<code>Dialog</code>：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">// return Dialog(child: child) </span>\n<span class=\"token keyword\">return</span> <span class=\"token function\">UnconstrainedBox</span><span class=\"token punctuation\">(</span>\n  constrainedAxis<span class=\"token punctuation\">:</span> Axis<span class=\"token punctuation\">.</span>vertical<span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">ConstrainedBox</span><span class=\"token punctuation\">(</span>\n    constraints<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxConstraints</span><span class=\"token punctuation\">(</span>maxWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">280</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">Material</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> child<span class=\"token punctuation\">,</span>\n      type<span class=\"token punctuation\">:</span> MaterialType<span class=\"token punctuation\">.</span>card<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>上面代码运行后可以实现一样的效果。现在我们总结一下：<code>AlertDialog</code>、<code>SimpleDialog</code>以及<code>Dialog</code>是Material组件库提供的三种对话框，旨在帮助开发者快速构建出符合Material设计规范的对话框，但读者完全可以自定义对话框样式，因此，我们仍然可以实现各种样式的对话框，这样即带来了易用性，又有很强的扩展性。</p> <h2 id=\"_7-6-2-对话框打开动画及遮罩\"><a href=\"#_7-6-2-对话框打开动画及遮罩\" class=\"header-anchor\">#</a> 7.6.2 对话框打开动画及遮罩</h2> <p>我们可以把对话框分为内部样式和外部样式两部分。内部样式指对话框中显示的具体内容，这部分内容我们已经在上面介绍过了；外部样式包含对话框遮罩样式、打开动画等，本节主要介绍如何自定义这些外部样式。</p> <blockquote><p>关于动画相关内容我们将在本书后面章节介绍，下面内容读者可以先了解一下（不必深究），读者可以在学习完动画相关内容后再回头来看。</p></blockquote> <p>我们已经介绍过了<code>showDialog</code>方法，它是Material组件库中提供的一个打开Material风格对话框的方法。那如何打开一个普通风格的对话框呢（非Material风格）？ Flutter 提供了一个<code>showGeneralDialog</code>方法，签名如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Future<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span> showGeneralDialog<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@required</span> BuildContext context<span class=\"token punctuation\">,</span>\n  <span class=\"token metadata symbol\">@required</span> RoutePageBuilder pageBuilder<span class=\"token punctuation\">,</span> <span class=\"token comment\">//构建对话框内部UI</span>\n  bool barrierDismissible<span class=\"token punctuation\">,</span> <span class=\"token comment\">//点击遮罩是否关闭对话框</span>\n  String barrierLabel<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 语义化标签(用于读屏软件)</span>\n  Color barrierColor<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 遮罩颜色</span>\n  Duration transitionDuration<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 对话框打开/关闭的动画时长</span>\n  RouteTransitionsBuilder transitionBuilder<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 对话框打开/关闭的动画</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>实际上，<code>showDialog</code>方法正是<code>showGeneralDialog</code>的一个封装，定制了Material风格对话框的遮罩颜色和动画。Material风格对话框打开/关闭动画是一个Fade（渐隐渐显）动画，如果我们想使用一个缩放动画就可以通过<code>transitionBuilder</code>来自定义。下面我们自己封装一个<code>showCustomDialog</code>方法，它定制的对话框动画为缩放动画，并同时制定遮罩颜色为<code>Colors.black87</code>：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Future<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span> showCustomDialog<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@required</span> BuildContext context<span class=\"token punctuation\">,</span>\n  bool barrierDismissible <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n  WidgetBuilder builder<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">final</span> ThemeData theme <span class=\"token operator\">=</span> Theme<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> shadowThemeOnly<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">showGeneralDialog</span><span class=\"token punctuation\">(</span>\n    context<span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">,</span>\n    pageBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext buildContext<span class=\"token punctuation\">,</span> Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> animation<span class=\"token punctuation\">,</span>\n        Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> secondaryAnimation<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">final</span> Widget pageChild <span class=\"token operator\">=</span> <span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> builder<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">return</span> <span class=\"token function\">SafeArea</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">return</span> theme <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span>\n              <span class=\"token operator\">?</span> <span class=\"token function\">Theme</span><span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">:</span> theme<span class=\"token punctuation\">,</span> child<span class=\"token punctuation\">:</span> pageChild<span class=\"token punctuation\">)</span>\n              <span class=\"token punctuation\">:</span> pageChild<span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    barrierDismissible<span class=\"token punctuation\">:</span> barrierDismissible<span class=\"token punctuation\">,</span>\n    barrierLabel<span class=\"token punctuation\">:</span> MaterialLocalizations<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>modalBarrierDismissLabel<span class=\"token punctuation\">,</span>\n    barrierColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>black87<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 自定义遮罩颜色</span>\n    transitionDuration<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>milliseconds<span class=\"token punctuation\">:</span> <span class=\"token number\">150</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    transitionBuilder<span class=\"token punctuation\">:</span> _buildMaterialDialogTransitions<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\nWidget <span class=\"token function\">_buildMaterialDialogTransitions</span><span class=\"token punctuation\">(</span>\n    BuildContext context<span class=\"token punctuation\">,</span>\n    Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> animation<span class=\"token punctuation\">,</span>\n    Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> secondaryAnimation<span class=\"token punctuation\">,</span>\n    Widget child<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// 使用缩放动画</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">ScaleTransition</span><span class=\"token punctuation\">(</span>\n    scale<span class=\"token punctuation\">:</span> <span class=\"token function\">CurvedAnimation</span><span class=\"token punctuation\">(</span>\n      parent<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">,</span>\n      curve<span class=\"token punctuation\">:</span> Curves<span class=\"token punctuation\">.</span>easeOut<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> child<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>现在，我们使用<code>showCustomDialog</code>打开文件删除确认对话框，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\nshowCustomDialog<span class=\"token operator\">&lt;</span>bool<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n  context<span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">,</span>\n  builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">AlertDialog</span><span class=\"token punctuation\">(</span>\n      title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;提示&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      content<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;您确定要删除当前文件吗?&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      actions<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n        <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;取消&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;删除&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">// 执行删除操作</span>\n            Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span><span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>运行效果如图7-13所示：</p> <p><img src=\"/assets/img/7-13.45803ea4.png\" alt=\"图7-13\"></p> <p>可以发现，遮罩颜色比通过<code>showDialog</code>方法打开的对话框更深。另外对话框打开/关闭的动画已变为缩放动画了，读者可以亲自运行示例查看效果。</p> <h2 id=\"_7-6-3-对话框实现原理\"><a href=\"#_7-6-3-对话框实现原理\" class=\"header-anchor\">#</a> 7.6.3 对话框实现原理</h2> <p>我们已经知道对话框最终都是由<code>showGeneralDialog</code>方法打开的，我们来看看它的具体实现：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Future<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span> showGeneralDialog<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@required</span> BuildContext context<span class=\"token punctuation\">,</span>\n  <span class=\"token metadata symbol\">@required</span> RoutePageBuilder pageBuilder<span class=\"token punctuation\">,</span>\n  bool barrierDismissible<span class=\"token punctuation\">,</span>\n  String barrierLabel<span class=\"token punctuation\">,</span>\n  Color barrierColor<span class=\"token punctuation\">,</span>\n  Duration transitionDuration<span class=\"token punctuation\">,</span>\n  RouteTransitionsBuilder transitionBuilder<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> rootNavigator<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>push<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>_DialogRoute<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n    pageBuilder<span class=\"token punctuation\">:</span> pageBuilder<span class=\"token punctuation\">,</span>\n    barrierDismissible<span class=\"token punctuation\">:</span> barrierDismissible<span class=\"token punctuation\">,</span>\n    barrierLabel<span class=\"token punctuation\">:</span> barrierLabel<span class=\"token punctuation\">,</span>\n    barrierColor<span class=\"token punctuation\">:</span> barrierColor<span class=\"token punctuation\">,</span>\n    transitionDuration<span class=\"token punctuation\">:</span> transitionDuration<span class=\"token punctuation\">,</span>\n    transitionBuilder<span class=\"token punctuation\">:</span> transitionBuilder<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>实现很简单，直接调用<code>Navigator</code>的<code>push</code>方法打开了一个新的对话框路由<code>_DialogRoute</code>，然后返回了<code>push</code>的返回值。可见对话框实际上正是通过路由的形式实现的，这也是为什么我们可以使用<code>Navigator</code>的<code>pop</code> 方法来退出对话框的原因。关于对话框的样式定制在<code>_DialogRoute</code>中，没有什么新的东西，读者可以自行查看。</p> <h2 id=\"_7-6-4-对话框状态管理\"><a href=\"#_7-6-4-对话框状态管理\" class=\"header-anchor\">#</a> 7.6.4 对话框状态管理</h2> <p>我们在用户选择删除一个文件时，会询问是否删除此文件；在用户选择一个文件夹是，应该再让用户确认是否删除子文件夹。为了在用户选择了文件夹时避免二次弹窗确认是否删除子目录，我们在确认对话框底部添加一个“同时删除子目录？”的复选框，如图7-14所示：</p> <p><img src=\"/assets/img/7-14.edea3e0f.png\" alt=\"图7-14\"></p> <p>现在就有一个问题：如何管理复选框的选中状态？习惯上，我们会在路由页的State中来管理选中状态，我们可能会写出如下这样的代码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">_DialogRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>DialogRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  bool withTree <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// 复选框选中状态</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n      children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n        <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;对话框2&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n            bool delete <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">showDeleteConfirmDialog2</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>delete <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;取消删除&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;同时删除子目录: $delete&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  Future<span class=\"token operator\">&lt;</span>bool<span class=\"token operator\">&gt;</span> <span class=\"token function\">showDeleteConfirmDialog2</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    withTree <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// 默认复选框不选中</span>\n    <span class=\"token keyword\">return</span> showDialog<span class=\"token operator\">&lt;</span>bool<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n      context<span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">,</span>\n      builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">return</span> <span class=\"token function\">AlertDialog</span><span class=\"token punctuation\">(</span>\n          title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;提示&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          content<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n            crossAxisAlignment<span class=\"token punctuation\">:</span> CrossAxisAlignment<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n            mainAxisSize<span class=\"token punctuation\">:</span> MainAxisSize<span class=\"token punctuation\">.</span>min<span class=\"token punctuation\">,</span>\n            children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n              <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;您确定要删除当前文件吗?&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n                children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                  <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;同时删除子目录？&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token function\">Checkbox</span><span class=\"token punctuation\">(</span>\n                    value<span class=\"token punctuation\">:</span> withTree<span class=\"token punctuation\">,</span>\n                    onChanged<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>bool value<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                      <span class=\"token comment\">//复选框选中状态发生变化时重新构建UI</span>\n                      <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                        <span class=\"token comment\">//更新复选框状态</span>\n                        withTree <span class=\"token operator\">=</span> <span class=\"token operator\">!</span>withTree<span class=\"token punctuation\">;</span>\n                      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          actions<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;取消&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;删除&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token comment\">//执行删除操作</span>\n                Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span>withTree<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>然后，当我们运行上面的代码时我们会发现复选框根本选不中！为什么会这样呢？其实原因很简单，我们知道<code>setState</code>方法只会针对当前context的子树重新build，但是我们的对话框并不是在<code>_DialogRouteState</code>的<code>build</code> 方法中构建的，而是通过<code>showDialog</code>单独构建的，所以在<code>_DialogRouteState</code>的context中调用<code>setState</code>是无法影响通过<code>showDialog</code>构建的UI的。另外，我们可以从另外一个角度来理解这个现象，前面说过对话框也是通过路由的方式来实现的，那么上面的代码实际上就等同于企图在父路由中调用<code>setState</code>来让子路由更新，这显然是不行的！简尔言之，根本原因就是context不对。那如何让复选框可点击呢？通常有如下三种方法：</p> <h3 id=\"单独抽离出statefulwidget\"><a href=\"#单独抽离出statefulwidget\" class=\"header-anchor\">#</a> 单独抽离出StatefulWidget</h3> <p>既然是context不对，那么直接的思路就是将复选框的选中逻辑单独封装成一个<code>StatefulWidget</code>，然后在其内部管理复选状态。我们先来看看这种方法，下面是实现代码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">// 单独封装一个内部管理选中状态的复选框组件</span>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">DialogCheckbox</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">DialogCheckbox</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span>\n    <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>onChanged<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> ValueChanged<span class=\"token operator\">&lt;</span>bool<span class=\"token operator\">&gt;</span> onChanged<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> bool value<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  _DialogCheckboxState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">_DialogCheckboxState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_DialogCheckboxState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>DialogCheckbox<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  bool value<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    value <span class=\"token operator\">=</span> widget<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Checkbox</span><span class=\"token punctuation\">(</span>\n      value<span class=\"token punctuation\">:</span> value<span class=\"token punctuation\">,</span>\n      onChanged<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>v<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">//将选中状态通过事件的形式抛出</span>\n        widget<span class=\"token punctuation\">.</span><span class=\"token function\">onChanged</span><span class=\"token punctuation\">(</span>v<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token comment\">//更新自身选中状态</span>\n          value <span class=\"token operator\">=</span> v<span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>下面是弹出对话框的代码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Future<span class=\"token operator\">&lt;</span>bool<span class=\"token operator\">&gt;</span> <span class=\"token function\">showDeleteConfirmDialog3</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  bool _withTree <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//记录复选框是否选中</span>\n  <span class=\"token keyword\">return</span> showDialog<span class=\"token operator\">&lt;</span>bool<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n    context<span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">,</span>\n    builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token function\">AlertDialog</span><span class=\"token punctuation\">(</span>\n        title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;提示&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        content<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n          crossAxisAlignment<span class=\"token punctuation\">:</span> CrossAxisAlignment<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n          mainAxisSize<span class=\"token punctuation\">:</span> MainAxisSize<span class=\"token punctuation\">.</span>min<span class=\"token punctuation\">,</span>\n          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;您确定要删除当前文件吗?&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n              children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;同时删除子目录？&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token function\">DialogCheckbox</span><span class=\"token punctuation\">(</span>\n                  value<span class=\"token punctuation\">:</span> _withTree<span class=\"token punctuation\">,</span> <span class=\"token comment\">//默认不选中</span>\n                  onChanged<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>bool value<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                    <span class=\"token comment\">//更新选中状态</span>\n                    _withTree <span class=\"token operator\">=</span> <span class=\"token operator\">!</span>_withTree<span class=\"token punctuation\">;</span>\n                  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        actions<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n          <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;取消&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;删除&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token comment\">// 将选中状态返回</span>\n              Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span>_withTree<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>最后，就是使用：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;话框3（复选框可点击）&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//弹出删除确认对话框，等待用户确认</span>\n    bool deleteTree <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">showDeleteConfirmDialog3</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>deleteTree <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;取消删除&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;同时删除子目录: $deleteTree&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</code></pre></div><p>运行后效果如图7-15所示：</p> <p><img src=\"/assets/img/7-15.8624d4d8.png\" alt=\"图7-15\"></p> <p>可见复选框能选中了，点击“取消”或“删除”后，控制台就会打印出最终的确认状态。</p> <h3 id=\"使用statefulbuilder方法\"><a href=\"#使用statefulbuilder方法\" class=\"header-anchor\">#</a> 使用StatefulBuilder方法</h3> <p>上面的方法虽然能解决对话框状态更新的问题，但是有一个明显的缺点——对话框上所有可能会改变状态的组件都得单独封装在一个在内部管理状态的<code>StatefulWidget</code>中，这样不仅麻烦，而且复用性不大。因此，我们来想想能不能找到一种更简单的方法？上面的方法本质上就是将对话框的状态置于一个<code>StatefulWidget</code>的上下文中，由<code>StatefulWidget</code>在内部管理，那么我们有没有办法在不需要单独抽离组件的情况下创建一个<code>StatefulWidget</code>的上下文呢？想到这里，我们可以从<code>Builder</code>组件的实现获得灵感。在前面介绍过<code>Builder</code>组件可以获得组件所在位置的真正的Context，那它是怎么实现的呢，我们看看它的源码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">Builder</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n    <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>builder<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span>builder <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n       <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> WidgetBuilder builder<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以看到，<code>Builder</code>实际上只是继承了<code>StatelessWidget</code>，然后在<code>build</code>方法中获取当前context后将构建方法代理到了<code>builder</code>回调，可见，<code>Builder</code>实际上是获取了<code>StatelessWidget</code> 的上下文（context）。那么我们能否用相同的方法获取<code>StatefulWidget</code> 的上下文，并代理其<code>build</code>方法呢？下面我们照猫画虎，来封装一个<code>StatefulBuilder</code>方法：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">StatefulBuilder</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function\">StatefulBuilder</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n    <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>builder<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span>builder <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n       <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> StatefulWidgetBuilder builder<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  _StatefulBuilderState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">_StatefulBuilderState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_StatefulBuilderState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>StatefulBuilder<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> widget<span class=\"token punctuation\">.</span><span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> setState<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>代码很简单，<code>StatefulBuilder</code>获取了<code>StatefulWidget</code>的上下文，并代理了其构建过程。下面我们就可以通过<code>StatefulBuilder</code>来重构上面的代码了（变动只在<code>DialogCheckbox</code>部分）：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n<span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;同时删除子目录？&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token comment\">//使用StatefulBuilder来构建StatefulWidget上下文</span>\n    <span class=\"token function\">StatefulBuilder</span><span class=\"token punctuation\">(</span>\n      builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> _setState<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">return</span> <span class=\"token function\">Checkbox</span><span class=\"token punctuation\">(</span>\n          value<span class=\"token punctuation\">:</span> _withTree<span class=\"token punctuation\">,</span> <span class=\"token comment\">//默认不选中</span>\n          onChanged<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>bool value<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">//_setState方法实际就是该StatefulWidget的setState方法，</span>\n            <span class=\"token comment\">//调用后builder方法会重新被调用</span>\n            <span class=\"token function\">_setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token comment\">//更新选中状态</span>\n              _withTree <span class=\"token operator\">=</span> <span class=\"token operator\">!</span>_withTree<span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</code></pre></div><p>实际上，这种方法本质上就是子组件通知父组件（StatefulWidget）重新build子组件本身来实现UI更新的，读者可以对比代码理解。实际上<code>StatefulBuilder</code>正是Flutter SDK中提供的一个类，它和<code>Builder</code>的原理是一样的，在此，提醒读者一定要将<code>StatefulBuilder</code>和<code>Builder</code>理解透彻，因为它们在Flutter中是非常实用的。</p> <h3 id=\"精妙的解法\"><a href=\"#精妙的解法\" class=\"header-anchor\">#</a> 精妙的解法</h3> <p>是否还有更简单的解决方案呢？要确认这个问题，我们就得先搞清楚UI是怎么更新的，我们知道在调用<code>setState</code>方法后<code>StatefulWidget</code>就会重新build，那<code>setState</code>方法做了什么呢？我们能不能从中找到方法？顺着这个思路，我们就得看一下<code>setState</code>的核心源码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span>VoidCallback fn<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n  _element<span class=\"token punctuation\">.</span><span class=\"token function\">markNeedsBuild</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以发现，<code>setState</code>中调用了<code>Element</code>的<code>markNeedsBuild()</code>方法，我们前面说过，Flutter是一个响应式框架，要更新UI只需改变状态后通知框架页面需要重构即可，而<code>Element</code>的<code>markNeedsBuild()</code>方法正是来实现这个功能的！<code>markNeedsBuild()</code>方法会将当前的<code>Element</code>对象标记为“dirty”（脏的），在每一个Frame，Flutter都会重新构建被标记为“dirty”<code>Element</code>对象。既然如此，我们有没有办法获取到对话框内部UI的<code>Element</code>对象，然后将其标示为为“dirty”呢？答案是肯定的！我们可以通过Context来得到<code>Element</code>对象，至于<code>Element</code>与<code>Context</code>的关系我们将会在后面“Flutter核心原理”一章中再深入介绍，现在只需要简单的认为：在组件树中，<code>context</code>实际上就是<code>Element</code>对象的引用。知道这个后，那么解决的方案就呼之欲出了，我们可以通过如下方式来让复选框可以更新：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Future<span class=\"token operator\">&lt;</span>bool<span class=\"token operator\">&gt;</span> <span class=\"token function\">showDeleteConfirmDialog4</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  bool _withTree <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">return</span> showDialog<span class=\"token operator\">&lt;</span>bool<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n    context<span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">,</span>\n    builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token function\">AlertDialog</span><span class=\"token punctuation\">(</span>\n        title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;提示&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        content<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n          crossAxisAlignment<span class=\"token punctuation\">:</span> CrossAxisAlignment<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n          mainAxisSize<span class=\"token punctuation\">:</span> MainAxisSize<span class=\"token punctuation\">.</span>min<span class=\"token punctuation\">,</span>\n          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;您确定要删除当前文件吗?&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n              children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;同时删除子目录？&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token function\">Checkbox</span><span class=\"token punctuation\">(</span> <span class=\"token comment\">// 依然使用Checkbox组件</span>\n                  value<span class=\"token punctuation\">:</span> _withTree<span class=\"token punctuation\">,</span>\n                  onChanged<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>bool value<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                    <span class=\"token comment\">// 此时context为对话框UI的根Element，我们 </span>\n                    <span class=\"token comment\">// 直接将对话框UI对应的Element标记为dirty</span>\n                    <span class=\"token punctuation\">(</span>context <span class=\"token operator\">as</span> Element<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">markNeedsBuild</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                    _withTree <span class=\"token operator\">=</span> <span class=\"token operator\">!</span>_withTree<span class=\"token punctuation\">;</span>\n                  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        actions<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n          <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;取消&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;删除&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token comment\">// 执行删除操作</span>\n              Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span>_withTree<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面的代码运行后复选框也可以正常选中。可以看到，我们只用了一行代码便解决了这个问题！当然上面的代码并不是最优，因为我们只需要更新复选框的状态，而此时的<code>context</code>我们用的是对话框的根<code>context</code>，所以会导致整个对话框UI组件全部rebuild，因此最好的做法是将<code>context</code>的“范围”缩小，也就是说只将<code>Checkbox</code>的Element标记为<code>dirty</code>，优化后的代码为：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n<span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;同时删除子目录？&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token comment\">// 通过Builder来获得构建Checkbox的`context`，</span>\n    <span class=\"token comment\">// 这是一种常用的缩小`context`范围的方式</span>\n    <span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span>\n      builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">return</span> <span class=\"token function\">Checkbox</span><span class=\"token punctuation\">(</span>\n          value<span class=\"token punctuation\">:</span> _withTree<span class=\"token punctuation\">,</span>\n          onChanged<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>bool value<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token punctuation\">(</span>context <span class=\"token operator\">as</span> Element<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">markNeedsBuild</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            _withTree <span class=\"token operator\">=</span> <span class=\"token operator\">!</span>_withTree<span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</code></pre></div><h2 id=\"_7-6-5-其它类型的对话框\"><a href=\"#_7-6-5-其它类型的对话框\" class=\"header-anchor\">#</a> 7.6.5 其它类型的对话框</h2> <h3 id=\"底部菜单列表\"><a href=\"#底部菜单列表\" class=\"header-anchor\">#</a> 底部菜单列表</h3> <p><code>showModalBottomSheet</code>方法可以弹出一个Material风格的底部菜单列表模态对话框，示例如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">// 弹出底部菜单列表模态对话框</span>\nFuture<span class=\"token operator\">&lt;</span>int<span class=\"token operator\">&gt;</span> <span class=\"token function\">_showModalBottomSheet</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> showModalBottomSheet<span class=\"token operator\">&lt;</span>int<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n    context<span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">,</span>\n    builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> ListView<span class=\"token punctuation\">.</span><span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>\n        itemCount<span class=\"token punctuation\">:</span> <span class=\"token number\">30</span><span class=\"token punctuation\">,</span>\n        itemBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> int index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">return</span> <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>\n            title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;$index&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            onTap<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span>index<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>点击按钮，弹出该对话框：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;显示底部菜单列表&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    int type <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">_showModalBottomSheet</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>type<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</code></pre></div><p>运行后效果如图7-16所示：</p> <p><img src=\"/assets/img/7-16.da05101a.png\" alt=\"图7-16\"></p> <p><code>showModalBottomSheet</code>的实现原理和<code>showGeneralDialog</code>实现原理相同，都是通过路由的方式来实现的，读者可以查看源码对比。但值得一提的是还有一个<code>showBottomSheet</code>方法，该方法会从设备底部向上弹出一个全屏的菜单列表，示例如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">// 返回的是一个controller</span>\nPersistentBottomSheetController<span class=\"token operator\">&lt;</span>int<span class=\"token operator\">&gt;</span> <span class=\"token function\">_showBottomSheet</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> showBottomSheet<span class=\"token operator\">&lt;</span>int<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n    context<span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">,</span>\n    builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> ListView<span class=\"token punctuation\">.</span><span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>\n        itemCount<span class=\"token punctuation\">:</span> <span class=\"token number\">30</span><span class=\"token punctuation\">,</span>\n        itemBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> int index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">return</span> <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>\n            title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;$index&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            onTap<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n              <span class=\"token comment\">// do something</span>\n              <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;$index&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行效果如图7-17所示：</p> <p><img src=\"/assets/img/7-17.c0e2d9be.png\" alt=\"图7-17\"></p> <p><code>PersistentBottomSheetController</code>中包含了一些控制对话框的方法比如<code>close</code>方法可以关闭该对话框，功能比较简单，读者可以自行查看源码。唯一需要注意的是，<code>showBottomSheet</code>和我们上面介绍的弹出对话框的方法原理不同：<code>showBottomSheet</code>是调用widget树顶部的<code>Scaffold</code>组件的<code>ScaffoldState</code>的<code>showBottomSheet</code>同名方法实现，也就是说要调用<code>showBottomSheet</code>方法就必须得保证父级组件中有<code>Scaffold</code>。</p> <h3 id=\"loading框\"><a href=\"#loading框\" class=\"header-anchor\">#</a> Loading框</h3> <p>其实Loading框可以直接通过<code>showDialog</code>+<code>AlertDialog</code>来自定义：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">showLoadingDialog</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">showDialog</span><span class=\"token punctuation\">(</span>\n    context<span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">,</span>\n    barrierDismissible<span class=\"token punctuation\">:</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//点击遮罩不关闭对话框</span>\n    builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token function\">AlertDialog</span><span class=\"token punctuation\">(</span>\n        content<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n          mainAxisSize<span class=\"token punctuation\">:</span> MainAxisSize<span class=\"token punctuation\">.</span>min<span class=\"token punctuation\">,</span>\n          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            <span class=\"token function\">CircularProgressIndicator</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n              padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">only</span><span class=\"token punctuation\">(</span>top<span class=\"token punctuation\">:</span> <span class=\"token number\">26.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;正在加载，请稍后...&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>显示效果如图7-18所示：</p> <p><img src=\"/assets/img/7-18.d0ccba9d.png\" alt=\"图7-18\"></p> <p>如果我们嫌Loading框太宽，想自定义对话框宽度，这时只使用<code>SizedBox</code>或<code>ConstrainedBox</code>是不行的，原因是<code>showDialog</code>中已经给对话框设置了宽度限制，根据我们在第五章“尺寸限制类容器”一节中所述，我们可以使用<code>UnconstrainedBox</code>先抵消<code>showDialog</code>对宽度的限制，然后再使用<code>SizedBox</code>指定宽度，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n<span class=\"token function\">UnconstrainedBox</span><span class=\"token punctuation\">(</span>\n  constrainedAxis<span class=\"token punctuation\">:</span> Axis<span class=\"token punctuation\">.</span>vertical<span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span>\n    width<span class=\"token punctuation\">:</span> <span class=\"token number\">280</span><span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">AlertDialog</span><span class=\"token punctuation\">(</span>\n      content<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n        mainAxisSize<span class=\"token punctuation\">:</span> MainAxisSize<span class=\"token punctuation\">.</span>min<span class=\"token punctuation\">,</span>\n        children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n          <span class=\"token function\">CircularProgressIndicator</span><span class=\"token punctuation\">(</span>value<span class=\"token punctuation\">:</span> <span class=\"token number\">.8</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n            padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">only</span><span class=\"token punctuation\">(</span>top<span class=\"token punctuation\">:</span> <span class=\"token number\">26.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;正在加载，请稍后...&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>代码运行后，效果如图7-19所示：</p> <p><img src=\"/assets/img/7-19.4c3306a3.png\" alt=\"图7-19\"></p> <h3 id=\"日历选择\"><a href=\"#日历选择\" class=\"header-anchor\">#</a> 日历选择</h3> <p>我们先看一下Material风格的日历选择器，如图7-20所示：</p> <p><img src=\"/assets/img/7-20.f8ec9897.png\" alt=\"图7-20\"></p> <p>实现代码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Future<span class=\"token operator\">&lt;</span>DateTime<span class=\"token operator\">&gt;</span> <span class=\"token function\">_showDatePicker1</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">var</span> date <span class=\"token operator\">=</span> DateTime<span class=\"token punctuation\">.</span><span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">showDatePicker</span><span class=\"token punctuation\">(</span>\n    context<span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">,</span>\n    initialDate<span class=\"token punctuation\">:</span> date<span class=\"token punctuation\">,</span>\n    firstDate<span class=\"token punctuation\">:</span> date<span class=\"token punctuation\">,</span>\n    lastDate<span class=\"token punctuation\">:</span> date<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span> <span class=\"token comment\">//未来30天可选</span>\n      <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>days<span class=\"token punctuation\">:</span> <span class=\"token number\">30</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>iOS风格的日历选择器需要使用<code>showCupertinoModalPopup</code>方法和<code>CupertinoDatePicker</code>组件来实现：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Future<span class=\"token operator\">&lt;</span>DateTime<span class=\"token operator\">&gt;</span> <span class=\"token function\">_showDatePicker2</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">var</span> date <span class=\"token operator\">=</span> DateTime<span class=\"token punctuation\">.</span><span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">showCupertinoModalPopup</span><span class=\"token punctuation\">(</span>\n    context<span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">,</span>\n    builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>ctx<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span>\n        height<span class=\"token punctuation\">:</span> <span class=\"token number\">200</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">CupertinoDatePicker</span><span class=\"token punctuation\">(</span>\n          mode<span class=\"token punctuation\">:</span> CupertinoDatePickerMode<span class=\"token punctuation\">.</span>dateAndTime<span class=\"token punctuation\">,</span>\n          minimumDate<span class=\"token punctuation\">:</span> date<span class=\"token punctuation\">,</span>\n          maximumDate<span class=\"token punctuation\">:</span> date<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span>\n            <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>days<span class=\"token punctuation\">:</span> <span class=\"token number\">30</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          maximumYear<span class=\"token punctuation\">:</span> date<span class=\"token punctuation\">.</span>year <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n          onDateTimeChanged<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>DateTime value<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>value<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行效果如图7-21所示：</p> <p><img src=\"/assets/img/7-21.d3d1d15f.png\" alt=\"图7-21\"></p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"prev\">\n        7.5 异步UI更新（FutureBuilder、StreamBuilder）\n      </a></span> <span class=\"next\"><a href=\"/chapter8/listener.html\">\n        8.1 原始指针事件处理\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/3.cc1736a7.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter7/futurebuilder_and_streambuilder.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>7.5 异步UI更新（FutureBuilder、StreamBuilder） | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/67.73c671d9.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable open\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" aria-current=\"page\" class=\"active sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/chapter7/futurebuilder_and_streambuilder.html#_7-5-1-futurebuilder\" class=\"sidebar-link\">7.5.1 FutureBuilder</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter7/futurebuilder_and_streambuilder.html#_7-5-2-streambuilder\" class=\"sidebar-link\">7.5.2 StreamBuilder</a></li></ul></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_7-5-异步ui更新-futurebuilder、streambuilder\"><a href=\"#_7-5-异步ui更新-futurebuilder、streambuilder\" class=\"header-anchor\">#</a> 7.5 异步UI更新（FutureBuilder、StreamBuilder）</h1> <p>很多时候我们会依赖一些异步数据来动态更新UI，比如在打开一个页面时我们需要先从互联网上获取数据，在获取数据的过程中我们显示一个加载框，等获取到数据时我们再渲染页面；又比如我们想展示Stream（比如文件流、互联网数据接收流）的进度。当然，通过StatefulWidget我们完全可以实现上述这些功能。但由于在实际开发中依赖异步数据更新UI的这种场景非常常见，因此Flutter专门提供了<code>FutureBuilder</code>和<code>StreamBuilder</code>两个组件来快速实现这种功能。</p> <h2 id=\"_7-5-1-futurebuilder\"><a href=\"#_7-5-1-futurebuilder\" class=\"header-anchor\">#</a> 7.5.1 FutureBuilder</h2> <p><code>FutureBuilder</code>会依赖一个<code>Future</code>，它会根据所依赖的<code>Future</code>的状态来动态构建自身。我们看一下<code>FutureBuilder</code>构造函数：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">FutureBuilder</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>future<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>initialData<span class=\"token punctuation\">,</span>\n  <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>builder<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><ul><li><p><code>future</code>：<code>FutureBuilder</code>依赖的<code>Future</code>，通常是一个异步耗时任务。</p></li> <li><p><code>initialData</code>：初始数据，用户设置默认数据。</p></li> <li><p><code>builder</code>：Widget构建器；该构建器会在<code>Future</code>执行的不同阶段被多次调用，构建器签名如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">Function</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> AsyncSnapshot snapshot<span class=\"token punctuation\">)</span> \n</code></pre></div><p><code>snapshot</code>会包含当前异步任务的状态信息及结果信息 ，比如我们可以通过<code>snapshot.connectionState</code>获取异步任务的状态信息、通过<code>snapshot.hasError</code>判断异步任务是否有错误等等，完整的定义读者可以查看<code>AsyncSnapshot</code>类定义。</p> <p>另外，<code>FutureBuilder</code>的<code>builder</code>函数签名和<code>StreamBuilder</code>的<code>builder</code>是相同的。</p></li></ul> <h3 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h3> <p>我们实现一个路由，当该路由打开时我们从网上获取数据，获取数据时弹一个加载框；获取结束时，如果成功则显示获取到的数据，如果失败则显示错误。由于我们还没有介绍在flutter中如何发起网络请求，所以在这里我们不真正去网络请求数据，而是模拟一下这个过程，隔3秒后返回一个字符串：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Future<span class=\"token operator\">&lt;</span>String<span class=\"token operator\">&gt;</span> <span class=\"token function\">mockNetworkData</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> Future<span class=\"token punctuation\">.</span><span class=\"token function\">delayed</span><span class=\"token punctuation\">(</span><span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token string\">&quot;我是从互联网上获取的数据&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p><code>FutureBuilder</code>使用代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\nWidget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n    child<span class=\"token punctuation\">:</span> FutureBuilder<span class=\"token operator\">&lt;</span>String<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n      future<span class=\"token punctuation\">:</span> <span class=\"token function\">mockNetworkData</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> AsyncSnapshot snapshot<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">// 请求已结束</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>snapshot<span class=\"token punctuation\">.</span>connectionState <span class=\"token operator\">==</span> ConnectionState<span class=\"token punctuation\">.</span>done<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>snapshot<span class=\"token punctuation\">.</span>hasError<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">// 请求失败，显示错误</span>\n            <span class=\"token keyword\">return</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Error: ${snapshot.error}&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">// 请求成功，显示数据</span>\n            <span class=\"token keyword\">return</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Contents: ${snapshot.data}&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span>\n        <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token comment\">// 请求未结束，显示loading</span>\n          <span class=\"token keyword\">return</span> <span class=\"token function\">CircularProgressIndicator</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行结果如图7-8、7-9所示：</p> <p><img src=\"/assets/img/7-8.c316cc7f.png\" alt=\"图7-8\"><img src=\"/assets/img/7-9.62d054b0.png\" alt=\"图7-9\"></p> <p>上面代码中我们在<code>builder</code>中根据当前异步任务状态<code>ConnectionState</code>来返回不同的widget。<code>ConnectionState</code>是一个枚举类，定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">enum</span> ConnectionState <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">/// 当前没有异步任务，比如[FutureBuilder]的[future]为null时</span>\n  none<span class=\"token punctuation\">,</span>\n\n  <span class=\"token comment\">/// 异步任务处于等待状态</span>\n  waiting<span class=\"token punctuation\">,</span>\n\n  <span class=\"token comment\">/// Stream处于激活状态（流上已经有数据传递了），对于FutureBuilder没有该状态。</span>\n  active<span class=\"token punctuation\">,</span>\n\n  <span class=\"token comment\">/// 异步任务已经终止.</span>\n  done<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>注意，<code>ConnectionState.active</code>只在<code>StreamBuilder</code>中才会出现。</p> <h2 id=\"_7-5-2-streambuilder\"><a href=\"#_7-5-2-streambuilder\" class=\"header-anchor\">#</a> 7.5.2 StreamBuilder</h2> <p>我们知道，在Dart中<code>Stream</code> 也是用于接收异步事件数据，和<code>Future</code> 不同的是，它可以接收多个异步操作的结果，它常用于会多次读取数据的异步任务场景，如网络内容下载、文件读写等。<code>StreamBuilder</code>正是用于配合<code>Stream</code>来展示流上事件（数据）变化的UI组件。下面看一下<code>StreamBuilder</code>的默认构造函数：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">StreamBuilder</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  Key key<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>initialData<span class=\"token punctuation\">,</span>\n  Stream<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span> stream<span class=\"token punctuation\">,</span>\n  <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>builder<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> \n</code></pre></div><p>可以看到和<code>FutureBuilder</code>的构造函数只有一点不同：前者需要一个<code>future</code>，而后者需要一个<code>stream</code>。</p> <h3 id=\"示例-2\"><a href=\"#示例-2\" class=\"header-anchor\">#</a> 示例</h3> <p>我们创建一个计时器的示例：每隔1秒，计数加1。这里，我们使用<code>Stream</code>来实现每隔一秒生成一个数字:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Stream<span class=\"token operator\">&lt;</span>int<span class=\"token operator\">&gt;</span> <span class=\"token function\">counter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> Stream<span class=\"token punctuation\">.</span><span class=\"token function\">periodic</span><span class=\"token punctuation\">(</span><span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span>i<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> i<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p><code>StreamBuilder</code>使用代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>  \n Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> StreamBuilder<span class=\"token operator\">&lt;</span>int<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n      stream<span class=\"token punctuation\">:</span> <span class=\"token function\">counter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//</span>\n      <span class=\"token comment\">//initialData: ,// a Stream&lt;int&gt; or null</span>\n      builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> AsyncSnapshot<span class=\"token operator\">&lt;</span>int<span class=\"token operator\">&gt;</span> snapshot<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>snapshot<span class=\"token punctuation\">.</span>hasError<span class=\"token punctuation\">)</span>\n          <span class=\"token keyword\">return</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Error: ${snapshot.error}'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">switch</span> <span class=\"token punctuation\">(</span>snapshot<span class=\"token punctuation\">.</span>connectionState<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">case</span> ConnectionState<span class=\"token punctuation\">.</span>none<span class=\"token punctuation\">:</span>\n            <span class=\"token keyword\">return</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'没有Stream'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token keyword\">case</span> ConnectionState<span class=\"token punctuation\">.</span>waiting<span class=\"token punctuation\">:</span>\n            <span class=\"token keyword\">return</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'等待数据...'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token keyword\">case</span> ConnectionState<span class=\"token punctuation\">.</span>active<span class=\"token punctuation\">:</span>\n            <span class=\"token keyword\">return</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'active: ${snapshot.data}'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token keyword\">case</span> ConnectionState<span class=\"token punctuation\">.</span>done<span class=\"token punctuation\">:</span>\n            <span class=\"token keyword\">return</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Stream已关闭'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n        <span class=\"token keyword\">return</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// unreachable</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n <span class=\"token punctuation\">}</span>\n</code></pre></div><p>读者可以自己运行本示例查看运行结果。注意，本示例只是为了演示<code>StreamBuilder</code>的使用，在实战中，凡是UI会依赖多个异步数据而发生变化的场景都可以使用<code>StreamBuilder</code>。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter7/theme.html\" class=\"prev\">\n        7.4 颜色和主题\n      </a></span> <span class=\"next\"><a href=\"/chapter7/dailog.html\">\n        7.6 对话框详解\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/67.73c671d9.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter7/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>功能型Widget简介 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/164.46dd9498.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h2 id=\"功能型widget简介\"><a href=\"#功能型widget简介\" class=\"header-anchor\">#</a> 功能型Widget简介</h2> <p>功能型Widget指的是不会影响UI布局及外观的Widget，它们通常具有一定的功能，如事件监听、数据存储等，我们之前介绍过的FocusScope（焦点控制）、PageStorage（数据存储）、NotificationListener（事件监听）都属于功能型Widget。由于Widget是Flutter的一等公民，功能型Widget非常多，我们不会去一一介绍，本章中主要介绍几种常用的功能型Widget。</p> <h2 id=\"本章目录\"><a href=\"#本章目录\" class=\"header-anchor\">#</a> 本章目录</h2> <ul><li><a href=\"/chapter7/willpopscope.html\">7.1：导航返回拦截（WillPopScope）</a></li> <li><a href=\"/chapter7/inherited_widget.html\">7.2：数据共享（InheritedWidget）</a></li> <li><a href=\"/chapter7/provider.html\">7.3： 跨组件状态共享（Provider）</a></li> <li><a href=\"/chapter7/theme.html\">7.4：颜色和主题（Theme）</a></li> <li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\">7.5：异步UI更新（FutureBuilder、StreamBuilder）</a></li> <li><a href=\"/chapter7/dailog.html\">7.6：对话框详解</a></li></ul></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/164.46dd9498.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter7/inherited_widget.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>7.2 数据共享（InheritedWidget） | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/102.53edb23b.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable open\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" aria-current=\"page\" class=\"active sidebar-link\">7.2 数据共享（InheritedWidget）</a><ul class=\"sidebar-sub-headers\"></ul></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_7-2-数据共享-inheritedwidget\"><a href=\"#_7-2-数据共享-inheritedwidget\" class=\"header-anchor\">#</a> 7.2 数据共享（InheritedWidget）</h1> <p><code>InheritedWidget</code>是Flutter中非常重要的一个功能型组件，它提供了一种数据在widget树中从上到下传递、共享的方式，比如我们在应用的根widget中通过<code>InheritedWidget</code>共享了一个数据，那么我们便可以在任意子widget中来获取该共享的数据！这个特性在一些需要在widget树中共享数据的场景中非常方便！如Flutter SDK中正是通过InheritedWidget来共享应用主题（<code>Theme</code>）和Locale (当前语言环境)信息的。</p> <blockquote><p><code>InheritedWidget</code>和React中的context功能类似，和逐级传递数据相比，它们能实现组件跨级传递数据。<code>InheritedWidget</code>的在widget树中数据传递方向是从上到下的，这和通知<code>Notification</code>（将在下一章中介绍）的传递方向正好相反。</p></blockquote> <h3 id=\"didchangedependencies\"><a href=\"#didchangedependencies\" class=\"header-anchor\">#</a> didChangeDependencies</h3> <p>在之前介绍<code>StatefulWidget</code>时，我们提到<code>State</code>对象有一个<code>didChangeDependencies</code>回调，它会在“依赖”发生变化时被Flutter Framework调用。而这个“依赖”指的就是子widget是否使用了父widget中<code>InheritedWidget</code>的数据！如果使用了，则代表子widget依赖有依赖<code>InheritedWidget</code>；如果没有使用则代表没有依赖。这种机制可以使子组件在所依赖的<code>InheritedWidget</code>变化时来更新自身！比如当主题、locale(语言)等发生变化时，依赖其的子widget的<code>didChangeDependencies</code>方法将会被调用。</p> <p>下面我们看一下之前“计数器”示例应用程序的<code>InheritedWidget</code>版本。需要说明的是，本示例主要是为了演示<code>InheritedWidget</code>的功能特性，并不是计数器的推荐实现方式。</p> <p>首先，我们通过继承<code>InheritedWidget</code>，将当前计数器点击次数保存在<code>ShareDataWidget</code>的<code>data</code>属性中：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">ShareDataWidget</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">InheritedWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">ShareDataWidget</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">,</span>\n    Widget child\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span><span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">:</span> child<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    \n  <span class=\"token keyword\">final</span> int data<span class=\"token punctuation\">;</span> <span class=\"token comment\">//需要在子树中共享的数据，保存点击次数</span>\n    \n  <span class=\"token comment\">//定义一个便捷方法，方便子树中的widget获取共享数据  </span>\n  <span class=\"token keyword\">static</span> ShareDataWidget <span class=\"token function\">of</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> context<span class=\"token punctuation\">.</span>dependOnInheritedWidgetOfExactType<span class=\"token operator\">&lt;</span>ShareDataWidget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">//该回调决定当data发生变化时，是否通知子树中依赖data的Widget  </span>\n  <span class=\"token metadata symbol\">@override</span>\n  bool <span class=\"token function\">updateShouldNotify</span><span class=\"token punctuation\">(</span>ShareDataWidget old<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//如果返回true，则子树中依赖(build函数中有调用)本widget</span>\n    <span class=\"token comment\">//的子widget的`state.didChangeDependencies`会被调用</span>\n    <span class=\"token keyword\">return</span> old<span class=\"token punctuation\">.</span>data <span class=\"token operator\">!=</span> data<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>然后我们实现一个子组件<code>_TestWidget</code>，在其<code>build</code>方法中引用<code>ShareDataWidget</code>中的数据。同时，在其<code>didChangeDependencies()</code> 回调中打印日志：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">_TestWidget</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  __TestWidgetState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">__TestWidgetState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">__TestWidgetState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>_TestWidget<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//使用InheritedWidget中的共享数据</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>ShareDataWidget\n        <span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">.</span>data\n        <span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">didChangeDependencies</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">didChangeDependencies</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//父或祖先widget中的InheritedWidget改变(updateShouldNotify返回true)时会被调用。</span>\n    <span class=\"token comment\">//如果build中没有依赖InheritedWidget，则此回调不会被调用。</span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Dependencies change&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>最后，我们创建一个按钮，每点击一次，就将<code>ShareDataWidget</code>的值自增：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">InheritedWidgetTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _InheritedWidgetTestRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_InheritedWidgetTestRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_InheritedWidgetTestRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>InheritedWidgetTestRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  int count <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span>  <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">ShareDataWidget</span><span class=\"token punctuation\">(</span> <span class=\"token comment\">//使用ShareDataWidget</span>\n        data<span class=\"token punctuation\">:</span> count<span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n          mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n              padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">only</span><span class=\"token punctuation\">(</span>bottom<span class=\"token punctuation\">:</span> <span class=\"token number\">20.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">_TestWidget</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token comment\">//子widget中依赖ShareDataWidget</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Increment&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token comment\">//每点击一次，将count自增，然后重新build,ShareDataWidget的data将被更新  </span>\n              onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token operator\">++</span>count<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行后界面如图7-1所示：</p> <p><img src=\"/assets/img/7-1.fc1ee2fb.png\" alt=\"图7-1\"></p> <p>每点击一次按钮，计数器就会自增，控制台就会打印一句日志：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>I/flutter ( 8513): Dependencies change\n</code></pre></div><p>可见依赖发生变化后，其<code>didChangeDependencies()</code>会被调用。但是读者要注意，<strong>如果_TestWidget的build方法中没有使用ShareDataWidget的数据，那么它的<code>didChangeDependencies()</code>将不会被调用，因为它并没有依赖ShareDataWidget</strong>。例如，我们将<code>__TestWidgetState</code>代码改为下面这样，<code>didChangeDependencies()</code>将不会被调用:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">__TestWidgetState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>_TestWidget<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 使用InheritedWidget中的共享数据</span>\n    <span class=\"token comment\">//    return Text(ShareDataWidget</span>\n    <span class=\"token comment\">//        .of(context)</span>\n    <span class=\"token comment\">//        .data</span>\n    <span class=\"token comment\">//        .toString());</span>\n     <span class=\"token keyword\">return</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;text&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">didChangeDependencies</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">didChangeDependencies</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">// build方法中没有依赖InheritedWidget，此回调不会被调用。</span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Dependencies change&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面的代码中，我们将<code>build()</code>方法中依赖<code>ShareDataWidget</code>的代码注释掉了，然后返回一个固定<code>Text</code>，这样一来，当点击Increment按钮后，<code>ShareDataWidget</code>的<code>data</code>虽然发生变化，但由于<code>__TestWidgetState</code>并未依赖<code>ShareDataWidget</code>，所以<code>__TestWidgetState</code>的<code>didChangeDependencies</code>方法不会被调用。其实，这个机制很好理解，因为在数据发生变化时只对使用该数据的Widget更新是合理并且性能友好的。</p> <blockquote><p>思考题：Flutter framework是怎么知道子widget有没有依赖InheritedWidget的？</p></blockquote> <h4 id=\"应该在didchangedependencies-中做什么\"><a href=\"#应该在didchangedependencies-中做什么\" class=\"header-anchor\">#</a> 应该在didChangeDependencies()中做什么？</h4> <p>一般来说，子widget很少会重写此方法，因为在依赖改变后framework也都会调用<code>build()</code>方法。但是，如果你需要在依赖改变后执行一些昂贵的操作，比如网络请求，这时最好的方式就是在此方法中执行，这样可以避免每次<code>build()</code>都执行这些昂贵操作。</p> <h3 id=\"深入了解inheritedwidget\"><a href=\"#深入了解inheritedwidget\" class=\"header-anchor\">#</a> 深入了解InheritedWidget</h3> <p>现在来思考一下，如果我们只想在<code>__TestWidgetState</code>中引用<code>ShareDataWidget</code>数据，但却不希望在<code>ShareDataWidget</code>发生变化时调用<code>__TestWidgetState</code>的<code>didChangeDependencies()</code>方法应该怎么办？其实答案很简单，我们只需要将<code>ShareDataWidget.of()</code>的实现改一下即可：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//定义一个便捷方法，方便子树中的widget获取共享数据</span>\n<span class=\"token keyword\">static</span> ShareDataWidget <span class=\"token function\">of</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">//return context.dependOnInheritedWidgetOfExactType&lt;ShareDataWidget&gt;();</span>\n  <span class=\"token keyword\">return</span> context<span class=\"token punctuation\">.</span>getElementForInheritedWidgetOfExactType<span class=\"token operator\">&lt;</span>ShareDataWidget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>widget<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>唯一的改动就是获取<code>ShareDataWidget</code>对象的方式，把<code>dependOnInheritedWidgetOfExactType()</code>方法换成了<code>context.getElementForInheritedWidgetOfExactType&lt;ShareDataWidget&gt;().widget</code>，那么他们到底有什么区别呢，我们看一下这两个方法的源码（实现代码在<code>Element</code>类中，<code>Context</code>和<code>Element</code>的关系我们将在后面专门介绍）：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token metadata symbol\">@override</span>\nInheritedElement getElementForInheritedWidgetOfExactType<span class=\"token operator\">&lt;</span>T <span class=\"token keyword\">extends</span> <span class=\"token class-name\">InheritedWidget</span><span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span><span class=\"token function\">_debugCheckStateIsActiveForAncestorLookup</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> InheritedElement ancestor <span class=\"token operator\">=</span> _inheritedWidgets <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">?</span> <span class=\"token keyword\">null</span> <span class=\"token punctuation\">:</span> _inheritedWidgets<span class=\"token punctuation\">[</span>T<span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">return</span> ancestor<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n<span class=\"token metadata symbol\">@override</span>\nInheritedWidget <span class=\"token function\">dependOnInheritedWidgetOfExactType</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> Object aspect <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span><span class=\"token function\">_debugCheckStateIsActiveForAncestorLookup</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> InheritedElement ancestor <span class=\"token operator\">=</span> _inheritedWidgets <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">?</span> <span class=\"token keyword\">null</span> <span class=\"token punctuation\">:</span> _inheritedWidgets<span class=\"token punctuation\">[</span>T<span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">//多出的部分</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>ancestor <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span>ancestor <span class=\"token operator\">is</span> InheritedElement<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">dependOnInheritedElement</span><span class=\"token punctuation\">(</span>ancestor<span class=\"token punctuation\">,</span> aspect<span class=\"token punctuation\">:</span> aspect<span class=\"token punctuation\">)</span> <span class=\"token operator\">as</span> T<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  _hadUnsatisfiedDependencies <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">return</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们可以看到，<code>dependOnInheritedWidgetOfExactType()</code> 比 <code>getElementForInheritedWidgetOfExactType()</code>多调了<code>dependOnInheritedElement</code>方法，<code>dependOnInheritedElement</code>源码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>  <span class=\"token metadata symbol\">@override</span>\n  InheritedWidget <span class=\"token function\">dependOnInheritedElement</span><span class=\"token punctuation\">(</span>InheritedElement ancestor<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> Object aspect <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span>ancestor <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    _dependencies <span class=\"token operator\">?</span><span class=\"token operator\">?</span><span class=\"token operator\">=</span> HashSet<span class=\"token operator\">&lt;</span>InheritedElement<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    _dependencies<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span>ancestor<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    ancestor<span class=\"token punctuation\">.</span><span class=\"token function\">updateDependencies</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">,</span> aspect<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> ancestor<span class=\"token punctuation\">.</span>widget<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以看到<code>dependOnInheritedElement</code>方法中主要是注册了依赖关系！看到这里也就清晰了，<strong>调用<code>dependOnInheritedWidgetOfExactType()</code> 和 <code>getElementForInheritedWidgetOfExactType()</code>的区别就是前者会注册依赖关系，而后者不会</strong>，所以在调用<code>dependOnInheritedWidgetOfExactType()</code>时，<code>InheritedWidget</code>和依赖它的子孙组件关系便完成了注册，之后当<code>InheritedWidget</code>发生变化时，就会更新依赖它的子孙组件，也就是会调这些子孙组件的<code>didChangeDependencies()</code>方法和<code>build()</code>方法。而当调用的是 <code>getElementForInheritedWidgetOfExactType()</code>时，由于没有注册依赖关系，所以之后当<code>InheritedWidget</code>发生变化时，就不会更新相应的子孙Widget。</p> <p>注意，如果将上面示例中<code>ShareDataWidget.of()</code>方法实现改成调用<code>getElementForInheritedWidgetOfExactType()</code>，运行示例后，点击&quot;Increment&quot;按钮，会发现<code>__TestWidgetState</code>的<code>didChangeDependencies()</code>方法确实不会再被调用，但是其<code>build()</code>仍然会被调用！造成这个的原因其实是，点击&quot;Increment&quot;按钮后，会调用<code>_InheritedWidgetTestRouteState</code>的<code>setState()</code>方法，此时会重新构建整个页面，由于示例中，<code>__TestWidget</code> 并没有任何缓存，所以它也都会被重新构建，所以也会调用<code>build()</code>方法。</p> <p>那么，现在就带来了一个问题：实际上，我们只想更新子树中依赖了<code>ShareDataWidget</code>的组件，而现在只要调用<code>_InheritedWidgetTestRouteState</code>的<code>setState()</code>方法，所有子节点都会被重新build，这很没必要，那么有什么办法可以避免呢？答案是缓存！一个简单的做法就是通过封装一个<code>StatefulWidget</code>，将子Widget树缓存起来，具体做法下一节我们将通过实现一个<code>Provider</code> Widget 来演示如何缓存，以及如何利用<code>InheritedWidget</code> 来实现Flutter全局状态共享。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter7/willpopscope.html\" class=\"prev\">\n        7.1 导航返回拦截（WillPopScope）\n      </a></span> <span class=\"next\"><a href=\"/chapter7/provider.html\">\n        7.3 跨组件状态共享（Provider）\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/102.53edb23b.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter7/provider.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>7.3 跨组件状态共享（Provider） | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/68.4de1c271.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable open\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" aria-current=\"page\" class=\"active sidebar-link\">7.3 跨组件状态共享（Provider）</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/chapter7/provider.html#provider\" class=\"sidebar-link\">Provider</a></li></ul></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_7-3-跨组件状态共享-provider\"><a href=\"#_7-3-跨组件状态共享-provider\" class=\"header-anchor\">#</a> 7.3 跨组件状态共享（Provider）</h1> <p>在Flutter开发中，状态管理是一个永恒的话题。一般的原则是：如果状态是组件私有的，则应该由组件自己管理；如果状态要跨组件共享，则该状态应该由各个组件共同的父元素来管理。对于组件私有的状态管理很好理解，但对于跨组件共享的状态，管理的方式就比较多了，如使用全局事件总线EventBus（将在下一章中介绍），它是一个观察者模式的实现，通过它就可以实现跨组件状态同步：状态持有方（发布者）负责更新、发布状态，状态使用方（观察者）监听状态改变事件来执行一些操作。下面我们看一个登陆状态同步的简单示例：</p> <p>定义事件：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">enum</span> Event<span class=\"token punctuation\">{</span>\n  login<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略其它事件</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>登录页代码大致如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">// 登录状态改变后发布状态改变事件</span>\nbus<span class=\"token punctuation\">.</span><span class=\"token function\">emit</span><span class=\"token punctuation\">(</span>Event<span class=\"token punctuation\">.</span>login<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>依赖登录状态的页面：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">onLoginChanged</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">//登录状态变化处理逻辑</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token metadata symbol\">@override</span>\n<span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">//订阅登录状态改变事件</span>\n  bus<span class=\"token punctuation\">.</span><span class=\"token keyword\">on</span><span class=\"token punctuation\">(</span>Event<span class=\"token punctuation\">.</span>login<span class=\"token punctuation\">,</span>onLogin<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token metadata symbol\">@override</span>\n<span class=\"token keyword\">void</span> <span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">//取消订阅</span>\n  bus<span class=\"token punctuation\">.</span><span class=\"token function\">off</span><span class=\"token punctuation\">(</span>Event<span class=\"token punctuation\">.</span>login<span class=\"token punctuation\">,</span>onLogin<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们可以发现，通过观察者模式来实现跨组件状态共享有一些明显的缺点：</p> <ol><li>必须显式定义各种事件，不好管理</li> <li>订阅者必须需显式注册状态改变回调，也必须在组件销毁时手动去解绑回调以避免内存泄露。</li></ol> <p>在Flutter当中有没有更好的跨组件状态管理方式了呢？答案是肯定的，那怎么做的？我们想想前面介绍的<code>InheritedWidget</code>，它的天生特性就是能绑定<code>InheritedWidget</code>与依赖它的子孙组件的依赖关系，并且当<code>InheritedWidget</code>数据发生变化时，可以自动更新依赖的子孙组件！利用这个特性，我们可以将需要跨组件共享的状态保存在<code>InheritedWidget</code>中，然后在子组件中引用<code>InheritedWidget</code>即可，Flutter社区著名的Provider包正是基于这个思想实现的一套跨组件状态共享解决方案，接下来我们便详细介绍一下Provider的用法及原理。</p> <h2 id=\"provider\"><a href=\"#provider\" class=\"header-anchor\">#</a> Provider</h2> <p>为了加强读者的理解，我们不直接去看Provider包的源代码，相反，我会带着你根据上面描述的通过<code>InheritedWidget</code>实现的思路来一步一步地实现一个最小功能的Provider。</p> <p>首先，我们需要一个保存需要共享的数据<code>InheritedWidget</code>，由于具体业务数据类型不可预期，为了通用性，我们使用泛型，定义一个通用的<code>InheritedProvider</code>类，它继承自<code>InheritedWidget</code>：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">// 一个通用的InheritedWidget，保存任需要跨组件共享的状态</span>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">InheritedProvider</span><span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">InheritedWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">InheritedProvider</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span><span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">,</span> Widget child<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">:</span> child<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  \n  <span class=\"token comment\">//共享状态使用泛型</span>\n  <span class=\"token keyword\">final</span> T data<span class=\"token punctuation\">;</span>\n  \n  <span class=\"token metadata symbol\">@override</span>\n  bool <span class=\"token function\">updateShouldNotify</span><span class=\"token punctuation\">(</span>InheritedProvider<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span> old<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//在此简单返回true，则每次更新都会调用依赖其的子孙节点的`didChangeDependencies`。</span>\n    <span class=\"token keyword\">return</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>数据保存的地方有了，那么接下来我们需要做的就是在数据发生变化的时候来重新构建<code>InheritedProvider</code>，那么现在就面临两个问题：</p> <ol><li>数据发生变化怎么通知？</li> <li>谁来重新构建<code>InheritedProvider</code>？</li></ol> <p>第一个问题其实很好解决，我们当然可以使用之前介绍的eventBus来进行事件通知，但是为了更贴近Flutter开发，我们使用Flutter SDK中提供的<code>ChangeNotifier</code>类 ，它继承自<code>Listenable</code>，也实现了一个Flutter风格的发布者-订阅者模式，<code>ChangeNotifier</code>定义大致如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">ChangeNotifier</span> <span class=\"token keyword\">implements</span> <span class=\"token class-name\">Listenable</span> <span class=\"token punctuation\">{</span>\n  List listeners<span class=\"token operator\">=</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span>VoidCallback listener<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n     <span class=\"token comment\">//添加监听器</span>\n     listeners<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span>listener<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">removeListener</span><span class=\"token punctuation\">(</span>VoidCallback listener<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//移除监听器</span>\n    listeners<span class=\"token punctuation\">.</span><span class=\"token function\">remove</span><span class=\"token punctuation\">(</span>listener<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  \n  <span class=\"token keyword\">void</span> <span class=\"token function\">notifyListeners</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//通知所有监听器，触发监听器回调 </span>\n    listeners<span class=\"token punctuation\">.</span><span class=\"token function\">forEach</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>item<span class=\"token punctuation\">)</span><span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span><span class=\"token function\">item</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n   \n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n<span class=\"token punctuation\">}</span>\n\n</code></pre></div><p>我们可以通过调用<code>addListener()</code>和<code>removeListener()</code>来添加、移除监听器（订阅者）；通过调用<code>notifyListeners()</code> 可以触发所有监听器回调。</p> <p>现在，我们将要共享的状态放到一个Model类中，然后让它继承自<code>ChangeNotifier</code>，这样当共享的状态改变时，我们只需要调用<code>notifyListeners()</code> 来通知订阅者，然后由订阅者来重新构建<code>InheritedProvider</code>，这也是第二个问题的答案！接下来我们便实现这个订阅者类：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">ChangeNotifierProvider</span><span class=\"token operator\">&lt;</span>T <span class=\"token keyword\">extends</span> <span class=\"token class-name\">ChangeNotifier</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">ChangeNotifierProvider</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> Widget child<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> T data<span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">//定义一个便捷方法，方便子树中的widget获取共享数据</span>\n  <span class=\"token keyword\">static</span> T of<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">final</span> type <span class=\"token operator\">=</span> _typeOf<span class=\"token operator\">&lt;</span>InheritedProvider<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">final</span> provider <span class=\"token operator\">=</span>  context<span class=\"token punctuation\">.</span>dependOnInheritedWidgetOfExactType<span class=\"token operator\">&lt;</span>InheritedProvider<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> provider<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  _ChangeNotifierProviderState<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span> <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _ChangeNotifierProviderState<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>该类继承<code>StatefulWidget</code>，然后定义了一个<code>of()</code>静态方法供子类方便获取Widget树中的<code>InheritedProvider</code>中保存的共享状态(model)，下面我们实现该类对应的<code>_ChangeNotifierProviderState</code>类：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">_ChangeNotifierProviderState</span><span class=\"token operator\">&lt;</span>T <span class=\"token keyword\">extends</span> <span class=\"token class-name\">ChangeNotifier</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>ChangeNotifierProvider<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">update</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//如果数据发生变化（model类调用了notifyListeners），重新构建InheritedProvider</span>\n    <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">didUpdateWidget</span><span class=\"token punctuation\">(</span>ChangeNotifierProvider<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span> oldWidget<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//当Provider更新时，如果新旧数据不&quot;==&quot;，则解绑旧数据监听，同时添加新数据监听</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>widget<span class=\"token punctuation\">.</span>data <span class=\"token operator\">!=</span> oldWidget<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      oldWidget<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">.</span><span class=\"token function\">removeListener</span><span class=\"token punctuation\">(</span>update<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      widget<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span>update<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">didUpdateWidget</span><span class=\"token punctuation\">(</span>oldWidget<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 给model添加监听器</span>\n    widget<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span>update<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 移除model的监听器</span>\n    widget<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">.</span><span class=\"token function\">removeListener</span><span class=\"token punctuation\">(</span>update<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> InheritedProvider<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n      data<span class=\"token punctuation\">:</span> widget<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> widget<span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以看到<code>_ChangeNotifierProviderState</code>类的主要作用就是监听到共享状态（model）改变时重新构建Widget树。注意，在<code>_ChangeNotifierProviderState</code>类中调用<code>setState()</code>方法，<code>widget.child</code>始终是同一个，所以执行build时，<code>InheritedProvider</code>的child引用的始终是同一个子widget，所以<code>widget.child</code>并不会重新<code>build</code>，这也就相当于对<code>child</code>进行了缓存！当然如果<code>ChangeNotifierProvider</code>父级Widget重新build时，则其传入的<code>child</code>便有可能会发生变化。</p> <p>现在我们所需要的各个工具类都已完成，下面我们通过一个购物车的例子来看看怎么使用上面的这些类。</p> <h3 id=\"购物车示例\"><a href=\"#购物车示例\" class=\"header-anchor\">#</a> 购物车示例</h3> <p>我们需要实现一个显示购物车中所有商品总价的功能：</p> <ol><li>向购物车中添加新商品时总价更新</li></ol> <p>定义一个<code>Item</code>类，用于表示商品信息：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">Item</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">Item</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>price<span class=\"token punctuation\">,</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>count<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  double price<span class=\"token punctuation\">;</span> <span class=\"token comment\">//商品单价</span>\n  int count<span class=\"token punctuation\">;</span> <span class=\"token comment\">// 商品份数</span>\n  <span class=\"token comment\">//... 省略其它属性</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>定义一个保存购物车内商品数据的<code>CartModel</code>类:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">CartModel</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">ChangeNotifier</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// 用于保存购物车中商品列表</span>\n  <span class=\"token keyword\">final</span> List<span class=\"token operator\">&lt;</span>Item<span class=\"token operator\">&gt;</span> _items <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// 禁止改变购物车里的商品信息</span>\n  UnmodifiableListView<span class=\"token operator\">&lt;</span>Item<span class=\"token operator\">&gt;</span> <span class=\"token keyword\">get</span> items <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">UnmodifiableListView</span><span class=\"token punctuation\">(</span>_items<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// 购物车中商品的总价</span>\n  double <span class=\"token keyword\">get</span> totalPrice <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span>\n      _items<span class=\"token punctuation\">.</span><span class=\"token function\">fold</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span>value<span class=\"token punctuation\">,</span> item<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> value <span class=\"token operator\">+</span> item<span class=\"token punctuation\">.</span>count <span class=\"token operator\">*</span> item<span class=\"token punctuation\">.</span>price<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// 将 [item] 添加到购物车。这是唯一一种能从外部改变购物车的方法。</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">add</span><span class=\"token punctuation\">(</span>Item item<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    _items<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span>item<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">// 通知监听器（订阅者），重新构建InheritedProvider， 更新状态。</span>\n    <span class=\"token function\">notifyListeners</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p><code>CartModel</code>即要跨组件共享的model类。最后我们构建示例页面：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">ProviderRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _ProviderRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">_ProviderRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_ProviderRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>ProviderRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> ChangeNotifierProvider<span class=\"token operator\">&lt;</span>CartModel<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n        data<span class=\"token punctuation\">:</span> <span class=\"token function\">CartModel</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">return</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n            children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n              <span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n                <span class=\"token keyword\">var</span> cart<span class=\"token operator\">=</span>ChangeNotifierProvider<span class=\"token punctuation\">.</span>of<span class=\"token operator\">&lt;</span>CartModel<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                <span class=\"token keyword\">return</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;总价: ${cart.totalPrice}&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n                <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;RaisedButton build&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//在后面优化部分会用到</span>\n                <span class=\"token keyword\">return</span> <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n                  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;添加商品&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                    <span class=\"token comment\">//给购物车中添加商品，添加后总价会更新</span>\n                    ChangeNotifierProvider<span class=\"token punctuation\">.</span>of<span class=\"token operator\">&lt;</span>CartModel<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token function\">Item</span><span class=\"token punctuation\">(</span><span class=\"token number\">20.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行示例后效果如图7-2所示：</p> <p><img src=\"/assets/img/7-2.20458eff.png\" alt=\"provider\"></p> <p>每次点击”添加商品“按钮，总价就会增加20，我们期望的功能实现了！可能有些读者会疑惑，我们饶了一大圈实现这么简单的功能有意义么？其实，就这个例子来看，只是更新同一个路由页中的一个状态，我们使用<code>ChangeNotifierProvider</code>的优势并不明显，但是如果我们是做一个购物APP呢？由于购物车数据是通常是会在整个APP中共享的，比如会跨路由共享。如果我们将<code>ChangeNotifierProvider</code>放在整个应用的Widget树的根上，那么整个APP就可以共享购物车的数据了，这时<code>ChangeNotifierProvider</code>的优势将会非常明显。</p> <p>虽然上面的例子比较简单，但它却将Provider的原理和流程体现的很清楚，图7-3是Provider的原理图：</p> <p><img src=\"/assets/img/7-3.531c5fdf.png\" alt=\"图7-3\"></p> <p>Model变化后会自动通知<code>ChangeNotifierProvider</code>（订阅者），<code>ChangeNotifierProvider</code>内部会重新构建<code>InheritedWidget</code>，而依赖该<code>InheritedWidget</code>的子孙Widget就会更新。</p> <p>我们可以发现使用Provider，将会带来如下收益：</p> <ol><li>我们的业务代码更关注数据了，只要更新Model，则UI会自动更新，而不用在状态改变后再去手动调用<code>setState()</code>来显式更新页面。</li> <li>数据改变的消息传递被屏蔽了，我们无需手动去处理状态改变事件的发布和订阅了，这一切都被封装在Provider中了。这真的很棒，帮我们省掉了大量的工作！</li> <li>在大型复杂应用中，尤其是需要全局共享的状态非常多时，使用Provider将会大大简化我们的代码逻辑，降低出错的概率，提高开发效率。</li></ol> <h3 id=\"优化\"><a href=\"#优化\" class=\"header-anchor\">#</a> 优化</h3> <p>我们上面实现的<code>ChangeNotifierProvider</code>是有两个明显缺点：代码组织问题和性能问题，下面我们一一讨论。</p> <h4 id=\"代码组织问题\"><a href=\"#代码组织问题\" class=\"header-anchor\">#</a> 代码组织问题</h4> <p>我们先看一下构建显示总价Text的代码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">var</span> cart<span class=\"token operator\">=</span>ChangeNotifierProvider<span class=\"token punctuation\">.</span>of<span class=\"token operator\">&lt;</span>CartModel<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;总价: ${cart.totalPrice}&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>这段代码有两点可以优化：</p> <ol><li>需要显式调用<code>ChangeNotifierProvider.of</code>，当APP内部依赖<code>CartModel</code>很多时，这样的代码将很冗余。</li> <li>语义不明确；由于<code>ChangeNotifierProvider</code>是订阅者，那么依赖<code>CartModel</code>的Widget自然就是订阅者，其实也就是状态的消费者，如果我们用<code>Builder</code> 来构建，语义就不是很明确；如果我们能使用一个具有明确语义的Widget，比如就叫<code>Consumer</code>，这样最终的代码语义将会很明确，只要看到<code>Consumer</code>，我们就知道它是依赖某个跨组件或全局的状态。</li></ol> <p>为了优化这两个问题，我们可以封装一个<code>Consumer</code> Widget，实现如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">// 这是一个便捷类，会获得当前context和指定数据类型的Provider</span>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">Consumer</span><span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">Consumer</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n    <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>builder<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>  <span class=\"token punctuation\">:</span> <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span>builder <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> Widget child<span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> Widget <span class=\"token keyword\">Function</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> T value<span class=\"token punctuation\">)</span> builder<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>\n      context<span class=\"token punctuation\">,</span>\n      ChangeNotifierProvider<span class=\"token punctuation\">.</span>of<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//自动获取Model</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p><code>Consumer</code>实现非常简单，它通过指定模板参数，然后再内部自动调用<code>ChangeNotifierProvider.of</code>获取相应的Model，并且<code>Consumer</code>这个名字本身也是具有确切语义（消费者）。现在上面的代码块可以优化为如下这样：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Consumer<span class=\"token operator\">&lt;</span>CartModel<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n  builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> cart<span class=\"token punctuation\">)</span><span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;总价: ${cart.totalPrice}&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>是不是很优雅！</p> <h4 id=\"性能问题\"><a href=\"#性能问题\" class=\"header-anchor\">#</a> 性能问题</h4> <p>上面的代码还有一个性能问题，就在构建”添加按钮“的代码处：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;RaisedButton build&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// 构建时输出日志</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;添加商品&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      ChangeNotifierProvider<span class=\"token punctuation\">.</span>of<span class=\"token operator\">&lt;</span>CartModel<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token function\">Item</span><span class=\"token punctuation\">(</span><span class=\"token number\">20.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们点击”添加商品“按钮后，由于购物车商品总价会变化，所以显示总价的Text更新是符合预期的，但是”添加商品“按钮本身没有变化，是不应该被重新build的。但是我们运行示例，每次点击”添加商品“按钮，控制台都会输出&quot;RaisedButton build&quot;日志，也就是说”添加商品“按钮在每次点击时其自身都会重新build！这是为什么呢？如果你已经理解了<code>InheritedWidget</code>的更新机制，那么答案一眼就能看出：这是因为构建<code>RaisedButton</code>的<code>Builder</code>中调用了<code>ChangeNotifierProvider.of</code>，也就是说依赖了Widget树上面的<code>InheritedWidget</code>（即<code>InheritedProvider</code> ）Widget，所以当添加完商品后，<code>CartModel</code>发生变化，会通知<code>ChangeNotifierProvider</code>, 而<code>ChangeNotifierProvider</code>则会重新构建子树，所以<code>InheritedProvider</code>将会更新，此时依赖它的子孙Widget就会被重新构建。</p> <p>问题的原因搞清楚了，那么我们如何避免这不必要重构呢？既然按钮重新被build是因为按钮和<code>InheritedWidget</code>建立了依赖关系，那么我们只要打破或解除这种依赖关系就可以了。那么如何解除按钮和<code>InheritedWidget</code>的依赖关系呢？我们上一节介绍<code>InheritedWidget</code>时已经讲过了：调用<code>dependOnInheritedWidgetOfExactType()</code> 和 <code>getElementForInheritedWidgetOfExactType()</code>的区别就是前者会注册依赖关系，而后者不会。所以我们只需要将<code>ChangeNotifierProvider.of</code>的实现改为下面这样即可：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code> <span class=\"token comment\">//添加一个listen参数，表示是否建立依赖关系</span>\n  <span class=\"token keyword\">static</span> T of<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>bool listen <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">final</span> type <span class=\"token operator\">=</span> _typeOf<span class=\"token operator\">&lt;</span>InheritedProvider<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">final</span> provider <span class=\"token operator\">=</span> listen\n        <span class=\"token operator\">?</span> context<span class=\"token punctuation\">.</span>dependOnInheritedWidgetOfExactType<span class=\"token operator\">&lt;</span>InheritedProvider<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">.</span>getElementForInheritedWidgetOfExactType<span class=\"token operator\">&lt;</span>InheritedProvider<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">?</span><span class=\"token punctuation\">.</span>widget\n            <span class=\"token operator\">as</span> InheritedProvider<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> provider<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n</code></pre></div><p>然后我们将调用部分代码改为：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n    children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n      Consumer<span class=\"token operator\">&lt;</span>CartModel<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n        builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> cart<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span><span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;总价: ${cart.totalPrice}&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;RaisedButton build&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">return</span> <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;添加商品&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">// listen 设为false，不建立依赖关系</span>\n            ChangeNotifierProvider<span class=\"token punctuation\">.</span>of<span class=\"token operator\">&lt;</span>CartModel<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> listen<span class=\"token punctuation\">:</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span>\n                <span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token function\">Item</span><span class=\"token punctuation\">(</span><span class=\"token number\">20.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span>\n</code></pre></div><p>修改后再次运行上面的示例，我们会发现点击”添加商品“按钮后，控制台不会再输出&quot;RaisedButton build&quot;了，即按钮不会被重新构建了。而总价仍然会更新，这是因为<code>Consumer</code>中调用<code>ChangeNotifierProvider.of</code>时<code>listen</code>值为默认值true，所以还是会建立依赖关系。</p> <p>至此我们便实现了一个迷你的Provider，它具备Pub上Provider Package中的核心功能；但是我们的迷你版功能并不全面，如只实现了一个可监听的ChangeNotifierProvider，并没有实现只用于数据共享的Provider；另外，我们的实现有些边界也没有考虑的到，比如如何保证在Widget树重新build时Model始终是单例等。所以建议读者在实战中还是使用Provider Package，而本节实现这个迷你Provider的主要目的主要是为了帮助读者了解Provider Package底层的原理。</p> <h3 id=\"其它状态管理包\"><a href=\"#其它状态管理包\" class=\"header-anchor\">#</a> 其它状态管理包</h3> <p>现在Flutter社区已经有很多专门用于状态管理的包了，在此我们列出几个相对评分比较高的：</p> <table><thead><tr><th>包名</th> <th>介绍</th></tr></thead> <tbody><tr><td><a href=\"https://pub.flutter-io.cn/packages/provider\" target=\"_blank\" rel=\"noopener noreferrer\">Provider<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> &amp; <a href=\"https://pub.flutter-io.cn/packages/scoped_model\" target=\"_blank\" rel=\"noopener noreferrer\">Scoped Model<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></td> <td>这两个包都是基于<code>InheritedWidget</code>的，原理相似</td></tr> <tr><td><a href=\"https://pub.flutter-io.cn/packages/flutter_redux\" target=\"_blank\" rel=\"noopener noreferrer\">Redux<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></td> <td>是Web开发中React生态链中Redux包的Flutter实现</td></tr> <tr><td><a href=\"https://pub.dev/packages/flutter_mobx\" target=\"_blank\" rel=\"noopener noreferrer\">MobX<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></td> <td>是Web开发中React生态链中MobX包的Flutter实现</td></tr> <tr><td><a href=\"https://pub.dev/packages/flutter_bloc\" target=\"_blank\" rel=\"noopener noreferrer\">BLoC<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></td> <td>是BLoC模式的Flutter实现</td></tr></tbody></table> <p>在此笔者不对这些包做推荐，读者有兴趣都可以研究一下，了解它们各自的思想。</p> <h3 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h3> <p>本节通过介绍事件总线在跨组件共享中的一些缺点引出了通过<code>InheritedWidget</code>来实现状态的共享的思想，然后基于该思想实现了一个简单的Provider，在实现的过程中也更深入的探索了<code>InheritedWidget</code>与其依赖项的注册机制和更新机制。通过本节的学习，读者应该达到两个目标，首先是对<code>InheritedWidget</code>彻底吃透，其次是Provider的设计思想。</p> <p><code>InheritedWidget</code>是Flutter中非常重要的一个Widget，像国际化、主题等都是通过它来实现，所以我们也不惜篇幅，通过好几节来介绍它的，在下一节中，我们将介绍另一个基于<code>InheritedWidget</code>的组件Theme(主题)。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter7/inherited_widget.html\" class=\"prev\">\n        7.2 数据共享（InheritedWidget）\n      </a></span> <span class=\"next\"><a href=\"/chapter7/theme.html\">\n        7.4 颜色和主题\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/68.4de1c271.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter7/theme.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>7.4 颜色和主题 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/30.b84a8f21.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable open\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" aria-current=\"page\" class=\"active sidebar-link\">7.4 颜色和主题</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/chapter7/theme.html#_7-4-1-颜色\" class=\"sidebar-link\">7.4.1 颜色</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter7/theme.html#_7-4-2-theme\" class=\"sidebar-link\">7.4.2 Theme</a></li></ul></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_7-4-颜色和主题\"><a href=\"#_7-4-颜色和主题\" class=\"header-anchor\">#</a> 7.4 颜色和主题</h1> <h2 id=\"_7-4-1-颜色\"><a href=\"#_7-4-1-颜色\" class=\"header-anchor\">#</a> 7.4.1 颜色</h2> <p>在介绍主题前我们先了解一些Flutter中的Color类。Color类中颜色以一个int值保存，我们知道显示器颜色是由红、绿、蓝三基色组成，每种颜色占8比特，存储结构如下：</p> <table><thead><tr><th>Bit（位）</th> <th>颜色</th></tr></thead> <tbody><tr><td>0-7</td> <td>蓝色</td></tr> <tr><td>8-15</td> <td>绿色</td></tr> <tr><td>16-23</td> <td>红色</td></tr> <tr><td>24-31</td> <td>Alpha (不透明度)</td></tr></tbody></table> <p>上面表格中的的字段在Color类中都有对应的属性，而Color中的众多方法也就是操作这些属性的，由于大多比较简单，读者可以查看类定义了解。在此我们主要讨论两点：色值转换和亮度。</p> <h3 id=\"如何将颜色字符串转成color对象\"><a href=\"#如何将颜色字符串转成color对象\" class=\"header-anchor\">#</a> <strong>如何将颜色字符串转成Color对象</strong></h3> <p>如Web开发中的色值通常是一个字符串如&quot;#dc380d&quot;，它是一个RGB值，我们可以通过下面这些方法将其转为Color类：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Color</span><span class=\"token punctuation\">(</span><span class=\"token number\">0xffdc380d</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//如果颜色固定可以直接使用整数值</span>\n<span class=\"token comment\">//颜色是一个字符串变量</span>\n<span class=\"token keyword\">var</span> c <span class=\"token operator\">=</span> <span class=\"token string\">&quot;dc380d&quot;</span><span class=\"token punctuation\">;</span>\n<span class=\"token function\">Color</span><span class=\"token punctuation\">(</span>int<span class=\"token punctuation\">.</span><span class=\"token function\">parse</span><span class=\"token punctuation\">(</span>c<span class=\"token punctuation\">,</span>radix<span class=\"token punctuation\">:</span><span class=\"token number\">16</span><span class=\"token punctuation\">)</span><span class=\"token operator\">|</span><span class=\"token number\">0xFF000000</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//通过位运算符将Alpha设置为FF</span>\n<span class=\"token function\">Color</span><span class=\"token punctuation\">(</span>int<span class=\"token punctuation\">.</span><span class=\"token function\">parse</span><span class=\"token punctuation\">(</span>c<span class=\"token punctuation\">,</span>radix<span class=\"token punctuation\">:</span><span class=\"token number\">16</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">withAlpha</span><span class=\"token punctuation\">(</span><span class=\"token number\">255</span><span class=\"token punctuation\">)</span>  <span class=\"token comment\">//通过方法将Alpha设置为FF</span>\n</code></pre></div><h3 id=\"颜色亮度\"><a href=\"#颜色亮度\" class=\"header-anchor\">#</a> 颜色亮度</h3> <p>假如，我们要实现一个背景颜色和Title可以自定义的导航栏，并且背景色为深色时我们应该让Title显示为浅色；背景色为浅色时，Title显示为深色。要实现这个功能，我们就需要来计算背景色的亮度，然后动态来确定Title的颜色。Color类中提供了一个<code>computeLuminance()</code>方法，它可以返回一个[0-1]的一个值，数字越大颜色就越浅，我们可以根据它来动态确定Title的颜色，下面是导航栏NavBar的简单实现：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">NavBar</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">final</span> String title<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> Color color<span class=\"token punctuation\">;</span> <span class=\"token comment\">//背景颜色</span>\n\n  <span class=\"token function\">NavBar</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>color<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>title<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n      constraints<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxConstraints</span><span class=\"token punctuation\">(</span>\n        minHeight<span class=\"token punctuation\">:</span> <span class=\"token number\">52</span><span class=\"token punctuation\">,</span>\n        minWidth<span class=\"token punctuation\">:</span> double<span class=\"token punctuation\">.</span>infinity<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>\n        color<span class=\"token punctuation\">:</span> color<span class=\"token punctuation\">,</span>\n        boxShadow<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>\n          <span class=\"token comment\">//阴影</span>\n          <span class=\"token function\">BoxShadow</span><span class=\"token punctuation\">(</span>\n            color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>black26<span class=\"token punctuation\">,</span>\n            offset<span class=\"token punctuation\">:</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">3</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            blurRadius<span class=\"token punctuation\">:</span> <span class=\"token number\">3</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>\n        title<span class=\"token punctuation\">,</span>\n        style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>\n          fontWeight<span class=\"token punctuation\">:</span> FontWeight<span class=\"token punctuation\">.</span>bold<span class=\"token punctuation\">,</span>\n          <span class=\"token comment\">//根据背景色亮度来确定Title颜色</span>\n          color<span class=\"token punctuation\">:</span> color<span class=\"token punctuation\">.</span><span class=\"token function\">computeLuminance</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">&lt;</span> <span class=\"token number\">0.5</span> <span class=\"token operator\">?</span> Colors<span class=\"token punctuation\">.</span>white <span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>black<span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>测试代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token comment\">//背景为蓝色，则title自动为白色</span>\n    <span class=\"token function\">NavBar</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span> title<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;标题&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> \n    <span class=\"token comment\">//背景为白色，则title自动为黑色</span>\n    <span class=\"token function\">NavBar</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">,</span> title<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;标题&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行效果如图7-4所示：</p> <p><img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgoAAACwCAYAAABuD0ZvAAAMSWlDQ1BJQ0MgUHJvZmlsZQAASImVVwdYU8kWnltSSWiBCEgJvYnSq5QQWgQBqYKNkAQSSowJQcTOsqjg2kUE1BVdFXHRtQCyVtS1Loq9PxRRWVkXCzZU3qTAuvq99753vm/u/XPmnP+UzL13BgCdWp5UmofqApAvKZAlRIayJqals0hdAAEY0AIUYMHjy6Xs+PgYAGXo/k95cx1aQ7niouT6dv6/ip5AKOcDgMRDnCmQ8/Mh3g8AXsqXygoAIPpCvfXMAqkST4bYQAYThFiqxNlqXKrEmWpcpbJJSuBAvAsAMo3Hk2UDoN0C9axCfjbk0b4JsatEIJYAoEOGOIgv4gkgjoJ4VH7+dCWGdsAh8wue7H9wZg5z8njZw1hdi0rIYWK5NI836/9sx/+W/DzFUAw7OGgiWVSCsmbYt5u506OVmAZxryQzNg5ifYjfiQUqe4hRqkgRlay2R035cg7sGWBC7CrghUVDbApxhCQvNkajz8wSR3AhhisELRIXcJM0vouF8vBEDWetbHpC3BDOknHYGt9GnkwVV2l/UpGbzNbw3xQJuUP8r4tFSanqnDFqoTglFmJtiJny3MRotQ1mUyzixA7ZyBQJyvxtIPYXSiJD1fzY1CxZRILGXpYvH6oXWywSc2M1uLpAlBSl4dnF56nyN4K4RShhJw/xCOUTY4ZqEQjDwtW1Y5eEkmRNvVintCA0QeP7UpoXr7HHqcK8SKXeCmJTeWGixhcPKoALUs2Px0oL4pPUeeKZObxx8ep88CIQAzggDLCAAo5MMB3kAHF7b3Mv/KWeiQA8IAPZQAhcNJohj1TVjAReE0Ex+BMiIZAP+4WqZoWgEOo/DWvVVxeQpZotVHnkgscQ54NokAd/K1RekuFoKeAR1Ii/ic6HuebBoZz7VseGmhiNRjHEy9IZsiSGE8OIUcQIoiNuggfhAXgMvIbA4Y774n5D2f5tT3hM6CA8JFwjdBJuTROXyL6qhwXGg04YIUJTc+aXNeN2kNULD8UDIT/kxpm4CXDBPWEkNh4MY3tBLUeTubL6r7n/UcMXXdfYUVwpKGUEJYTi8LWntpO21zCLsqdfdkida+ZwXznDM1/H53zRaQG8R39tiS3G9mGnsePYWewQ1gxY2FGsBbuAHVbi4VX0SLWKhqIlqPLJhTzib+LxNDGVnZS7Nrj2uH5UzxUIi5TvR8CZLp0lE2eLClhs+OYXsrgS/uhRLHdXNz8AlN8R9WvqFVP1fUCY5/7WlfwAQKDn4ODgob91MToA7IfPBrXrb52DP3wdFAFwZhlfIStU63DlhQCoQAc+UcbAHFgDB1iPO/AGASAEhINxIA4kgTQwFXZZBNezDMwEc8BCUAYqwAqwFlSDTWAL2AF+BntBMzgEjoPfwHlwCVwDd+Dq6QbPQB94AwYQBCEhdISBGCMWiC3ijLgjvkgQEo7EIAlIGpKBZCMSRIHMQb5DKpBVSDWyGalHfkEOIseRs0gHcgt5gPQgL5EPKIbSUAPUDLVDx6C+KBuNRpPQKWg2OgMtRkvRZWgVWofuQpvQ4+h59BraiT5D+zGAaWFMzBJzwXwxDhaHpWNZmAybh5VjlVgd1oi1wv/5CtaJ9WLvcSLOwFm4C1zBUXgyzsdn4PPwpXg1vgNvwk/iV/AHeB/+mUAnmBKcCf4ELmEiIZswk1BGqCRsIxwgnIJPUzfhDZFIZBLtiT7waUwj5hBnE5cSNxB3E48RO4hdxH4SiWRMciYFkuJIPFIBqYy0nrSLdJR0mdRNekfWIluQ3ckR5HSyhFxCriTvJB8hXyY/IQ9QdCm2FH9KHEVAmUVZTtlKaaVcpHRTBqh6VHtqIDWJmkNdSK2iNlJPUe9SX2lpaVlp+WlN0BJrLdCq0tqjdUbrgdZ7mj7NicahTaYpaMto22nHaLdor+h0uh09hJ5OL6Avo9fTT9Dv099pM7RHa3O1BdrztWu0m7Qvaz/XoejY6rB1puoU61Tq7NO5qNOrS9G10+Xo8nTn6dboHtS9oduvx9Bz04vTy9dbqrdT76zeU32Svp1+uL5Av1R/i/4J/S4GxrBmcBh8xneMrYxTjG4DooG9Adcgx6DC4GeDdoM+Q31DT8MUwyLDGsPDhp1MjGnH5DLzmMuZe5nXmR9GmI1gjxCOWDKiccTlEW+NRhqFGAmNyo12G10z+mDMMg43zjVeadxsfM8EN3EymWAy02SjySmT3pEGIwNG8keWj9w78rYpaupkmmA623SL6QXTfjNzs0gzqdl6sxNmveZM8xDzHPM15kfMeywYFkEWYos1Fkct/mAZstisPFYV6ySrz9LUMspSYbnZst1ywMreKtmqxGq31T1rqrWvdZb1Gus26z4bC5vxNnNsGmxu21JsfW1FtutsT9u+tbO3S7VbZNds99TeyJ5rX2zfYH/Xge4Q7DDDoc7hqiPR0dcx13GD4yUn1MnLSeRU43TRGXX2dhY7b3DuGEUY5TdKMqpu1A0XmgvbpdClweXBaObomNElo5tHPx9jMyZ9zMoxp8d8dvVyzXPd6nrHTd9tnFuJW6vbS3cnd757jftVD7pHhMd8jxaPF57OnkLPjZ43vRhe470WebV5ffL28ZZ5N3r3+Nj4ZPjU+tzwNfCN913qe8aP4BfqN9/vkN97f2//Av+9/n8FuATkBuwMeDrWfqxw7NaxXYFWgbzAzYGdQaygjKAfgzqDLYN5wXXBD0OsQwQh20KesB3ZOexd7OehrqGy0AOhbzn+nLmcY2FYWGRYeVh7uH54cnh1+P0Iq4jsiIaIvkivyNmRx6IIUdFRK6NucM24fG49t2+cz7i5405G06ITo6ujH8Y4xchiWsej48eNXz3+bqxtrCS2OQ7EceNWx92Lt4+fEf/rBOKE+Ak1Ex4nuCXMSTidyEiclrgz8U1SaNLypDvJDsmK5LYUnZTJKfUpb1PDUleldk4cM3HuxPNpJmnitJZ0UnpK+rb0/knhk9ZO6p7sNbls8vUp9lOKppydajI1b+rhaTrTeNP2ZRAyUjN2ZnzkxfHqeP2Z3MzazD4+h7+O/0wQIlgj6BEGClcJn2QFZq3KepodmL06u0cULKoU9Yo54mrxi5yonE05b3PjcrfnDual5u3OJ+dn5B+U6EtyJSenm08vmt4hdZaWSTtn+M9YO6NPFi3bJkfkU+QtBQZww35B4aD4XvGgMKiwpvDdzJSZ+4r0iiRFF2Y5zVoy60lxRPFPs/HZ/NltcyznLJzzYC577uZ5yLzMeW3zreeXzu9eELlgx0LqwtyFv5e4lqwqef1d6netpWalC0q7vo/8vqFMu0xWdmNRwKJNi/HF4sXtSzyWrF/yuVxQfq7CtaKy4uNS/tJzP7j9UPXD4LKsZe3LvZdvXEFcIVlxfWXwyh2r9FYVr+paPX510xrWmvI1r9dOW3u20rNy0zrqOsW6zqqYqpb1NutXrP9YLaq+VhNas7vWtHZJ7dsNgg2XN4ZsbNxktqli04cfxT/e3By5uanOrq5yC3FL4ZbHW1O2nv7J96f6bSbbKrZ92i7Z3rkjYcfJep/6+p2mO5c3oA2Khp5dk3dd+jns55ZGl8bNu5m7K/aAPYo9f/yS8cv1vdF72/b57mvcb7u/9gDjQHkT0jSrqa9Z1NzZktbScXDcwbbWgNYDv47+dfshy0M1hw0PLz9CPVJ6ZPBo8dH+Y9Jjvcezj3e1TWu7c2LiiasnJ5xsPxV96sxvEb+dOM0+ffRM4JlDZ/3PHjzne675vPf5pgteFw787vX7gXbv9qaLPhdbLvldau0Y23HkcvDl41fCrvx2lXv1/LXYax3Xk6/fvDH5RudNwc2nt/JuvbhdeHvgzoK7hLvl93TvVd43vV/3L8d/7e707jz8IOzBhYeJD+908buePZI/+thd+pj+uPKJxZP6p+5PD/VE9Fz6Y9If3c+kzwZ6y/7U+7P2ucPz/X+F/HWhb2Jf9wvZi8GXS18Zv9r+2vN1W398//03+W8G3pa/M363473v+9MfUj88GZj5kfSx6pPjp9bP0Z/vDuYPDkp5Mp5qK4DBgWZlAfByOwD0NAAYl+D+YZL6nKcSRH02VSHwn7D6LKgSbwAa4U25XeccA2APHHZw0BcAoNyqJ4UA1MNjeGhEnuXhruaiwRMP4d3g4CszAEitAHySDQ4ObBgc/LQVJnsLgGMz1OdLpRDh2eBHTyW6zCxaAL6SfwMHzn7YUOijZAAAAAlwSFlzAAAWJQAAFiUBSVIk8AAAGOxJREFUeAHt3QmMlGWex/F/Vb19cAuIIggMIgh4rce6OuqKwioZdQ/jZGbUVWN0Y4xmdY2ZxGRjsia7ibrj6sSNZ1bjtWq8rzXeoiN4IQYPVg6B4ZBLDjm6u6pr/7+neZum6QLHlbf6qfq+pqnq962q93k/T7Xvr57jrdzSpUvLpVLJyuVy+DFfcrmcblgQQAABBBBAoM4ElAe0KAvoJ2lsbLT29vbOkJB6EBZSCW4RQAABBBCoD4E0JKRHG4JCkiS7hIT0AdwigAACCCCAQH0LeE5I6luAo0cAAQQQQACBigIhKHRvaqj4aDYggAACCCCAQF0JJPl8vq4OmINFAAEEEEAAgR8ukFQatFhp/Q9/aR6JAAIIIIAAArEJdO9l2CUoEBBiq1LKiwACCCCAwE8noBzQNSzkuwaDrvd/ul3ySggggAACCCAQk0DXPJB0TQ1d78d0QJQVAQQQQAABBPaOQAgKaUBIb/fOrnhVBBBAAAEEEOjtAmlrQnqbnPdc0cvsl2vsuGJjby8/5UMAAQQQQACBvS0Qvsmh4+sckjlrSQh725vXRwABBBBAID6BjnyQtJMT4qs7SowAAggggEBGAlxtKSNodoMAAggggECMAgSFGGuNMiOAAAIIIJCRAEEhI2h2gwACCCCAQIwCBIUYa40yI4AAAgggkJEAQSEjaHaDAAIIIIBAjAIEhRhrjTIjgAACCCCQkQBBISNodoMAAggggECMAgSFGGuNMiOAAAIIIJCRAEEhI2h2gwACCCCAQIwCBIUYa40yI4AAAgggkJEAQSEjaHaDAAIIIIBAjAIEhRhrjTIjgAACCCCQkQBBISNodoMAAggggECMAgSFGGuNMiOAAAIIIJCRAEEhI2h2gwACCCCAQIwCBIUYa40yI4AAAgggkJEAQSEjaHaDAAIIIIBAjAIEhRhrjTIjgAACCCCQkQBBISNodoMAAggggECMAgSFGGuNMiOAAAIIIJCRAEEhI2h2gwACCCCAQIwCSYyFpswIIFBZ4NDBeZv+s4IVkpy9tLBoc9e2V34wWxBAAIE9CBAU9gDEZgRiExjRN2enjU4saTSbvarkQcHs6KF5m7hP3hp304b48IKitXmmyOfMfje12QY0+p1dlrIl/oD+/tqVlrkrSvbIF0Wbt4GAUsmI9QjEJEBQiKm2KCsCP0Cg4Of3Jv/LbvAWBd3XcsLogv3ykAbr1+PJv+Mxj39T8qBQNj3l+BEFG9wnZ5tayqbTvV6nf0POWn37d9t039d56Gjevo9txbK1ljpeZ60HhKZCx33+RQCB+AUICvHXIUeAwB4FNmwr2/JN7dbXT/aVlnK53Lkp8RDgGaBj8dU5v69gsGx92f71vRZb8n3Z/nx43v7x2Ebb1lq2/5rTZu9929GCsK3Nw4QHDBYEEKgNgdzY/9zEX3Rt1CVHUccC6m749cQGm3JQEj7tD++X85N7zlZubrdNrWZ3zW61mctK4YRfiWnt1rLpfwZqPZh1UT8b3Jyz9WpR8JVaN7ApZ1+uLtmjc9vs14c3eteE2Yj+eWsrlX0/ZdtSNFuypmQPfN5mH66m26GSM+sRiE2AFoXYaozyItCDgJr6DxyQs8n75kPXgVoAtIwemA8n/wbfvtZbFf6URV0Jb37dZlu8q2Ef74aYOrHjfxf9vftC+2n1gFD0FNHsrRQjfN8a29DoXRADdtNq8afsn8cigEDvECAo9I56oBQI/L8EVmwp252fttmLPiDxFxMS+9sJDf4Jv2xPfNFm5p/0/+HwBrvauwn2tEx9dEvnQzTO4a8PbTT1SCh4qDtCizJI0RsM3llcsmXenfF33pKx0AdNakyDwgILAgjUlgBBobbqk6OpU4Ft/un/ax9E2McHEuzrn+h1Yu/nt395YGJLvLtgkLcCHDBo+5l+d0b+PAWDRz9u9efv+sDl3sWwfnuvQpO/3AAfyKCuCQWHLkMcdn0iaxBAIFoBgkK0VUfBEdhZQB0LY/waChP22zHl4CCfFrnOuxx+90GrLd3YcYb/zaENNm1cYvNWluweH4QYxiH4c/X8dn/I//yqbxifsPOr7/itcfvLqzuj2X9aveWixQcwdrQ17Hgc9xBAoDYECAq1UY8cBQI20gc0Hj+8YMN8IKOWjdvHJEwclrdxQ/J2yCBNd/Rpk9tbDYreJTHZg8XBvu2fZ7bauu0zFbb6+kF+3tcsB3U3aBDjFg8CGuyoRRMom/3/HP29NSHfbNbmvRstzHIINvyDQC0KEBRqsVY5proT0FTGYw4o2JSxBdM1DbRoSuSCtSVb7FMah/hf+ik+bmGdn+y1XkvZ+wx+4QMUxwwp2Nfr2u0On62gCy6d89QWu39ak00amdjsb0v2c799/48l++1bnjJ8+fnIgv27X5CpvweIvv66G3165PqOTWE7/yCAQG0JEBRqqz45mjoVmOQtA78cn9hAn9L4zRpdL8FbBDw9vLyoZK/4hZT+6S8abaAPNnz566Lt6y0P6TLDBz/uNyDvgaHB7v/fYuiG0NY+/joa46CplepiOGlUYq/8qmBbPYToIky6VsIgf50mnze53sPHCO/iaPTHr/fpmCUNWmBBAIGaEfgBo5tq5lg5EARqVqDgJ/O8/zV/5pdP/nBJMYw10MFu8hP6eO9yOMxP5GotmOHjEtZsb1HQ9icXlmylXzxpnG8/Zf+OqZVaP6iP3/fEoDEIGqSorofFPsbhjxvL9rVfI+H9Rd4/4esVKnR1xmM9SCQeFGb7RZdW+IBHFgQQqB0BgkLt1CVHUscCn/sXP93zWZv997yizVrVMWhRHLrs8nQfuDjeg8Cc5SVb7if6HVv92greOqBwoVkLf+MtEgoHes7IgTlr8P87nODdDNo2yy/WdN6zW+1fZrTYfO+m0IBJDWp8dX7R/uMPLfaHxcXw/Q9H+ePH7OE7Jeq4mjh0BKIUoOshymqj0AjsLBBaC7ZfeXGaf09DuvyZB4SJflJv8+mTM5Z6IPCLJPXzv3oFgnT5eFnRpvu1Fw4cWggtBH/lJ3u1EqzzazOo2yHnj9c1Ek4eVbC/n9xgJ41JwiyH2b6/t/TtlD5fcoOPU+jrjz3SxzOcMTax+b5uERd9TYm5RSBqAYJC1NVH4RHYIeAZIHQH7FhjttpP9hv8hP6JdzmM7p+zq49q9NaAvAeAnOnaC3rKjOXt9pG3Kiz3E7uut/Cbw5LQTfHs3FYr+hWUpviJf5IHjn+b0mwl34m6IjZ7MOjnweBCn2qZLgN8XIPGL3ivhV/PQUmELojUhlsEYhYgKMRce5QdgR4ENJZQLQzqY/jSL8L0js9Y0GlbX+B0xs+SMLXxuy3t9oGPJ9DVGzf59Mbfvt5iq/0kf6hflGlwv7wt8RaBR70bY4kPVHzLuxWmevfFCL8c9P4+gHFjS3v47oehPgiy+7LVA8QC32fXcRDdH8PvCCAQlwBBIa76orQI7FFgubcivLakZBrgqCspquVAy7u+rsXHIDb6X/0qP5k/4zMiFBK0rPKQoGWur799VkvoeliwfVDiTB+8OHN1qzV6Lhjl12gY4T/N3mIwqIcrQiukzPExDKu6DJgML8w/CCAQrQDfHhlt1VFwBBBAAAEE9r7Arm2He3+f7AEBBBBAAAEEIhEgKERSURQTAQQQQACBaggQFKqhzj4RQAABBBCIRICgEElFUUwEEEAAAQSqIUBQqIY6+0QAAQQQQCASAYJCJBVFMRFAAAEEEKiGAEGhGursEwEEEEAAgUgECAqRVBTFRAABBBBAoBoCBIVqqLNPBBBAAAEEIhEgKERSURQTAQQQQACBaggQFKqhzj4RQAABBBCIRICgEElFUUwEEEAAAQSqIUBQqIY6+0QAAQQQQCASAYJCJBVFMRFAAAEEEKiGAEGhGursEwEEEEAAgUgECAqRVBTFRAABBBBAoBoCBIVqqLNPBBBAAAEEIhEgKERSURQTAQQQQACBaggQFKqhzj4RQAABBBCIRICgEElFUUwEEEAAAQSqIUBQqIY6+0QAAQQQQCASAYJCJBVFMRFAAAEEEKiGQLJvYynst1wud9ya33bcrUZ52CcCCCCAAAIIVEMgZ5bz/7Tkcjtuc0+9u6jc2la0YrFopfZ2a/efcnvZswJpoRr1xD4RQAABBBDIWkABIZfPWd4DQqFQsCQpWENDgzUkiSWHDmu11tZWa2trC2EhBAVvXUhbGLIuLPtDAAEEEEAAgWwF1IKgn3w+7yHBw4H/NDWVPSyULVFiUCjQA5QiCArZVg57QwABBBBAoDcIpDmgo0WhIyyE0KCgoEUbSqVSZ1DoDYWmDAgggAACCCCQjUDaoqA8sFNYUFDQRrUkpD/ZFIm9IIAAAggggEBvElDXQ/qj1oTQFaGgoOSQhgTGJvSmKqMsCCCAAAIIZCeQtiqkYSEEBSUGhYOuP9kViT0hgAACCCCAQG8RUFDo+rNLUFBBaVHoLdVFORBAAAEEEMhWQCFBS9ewkCgtaCEgBAb+QQABBBBAoO4FugaGEBQUEtKVda8DAAIIIIAAAgh05oJEFmlIoFWBdwYCCCCAAAL1LZBmglQh6R4Ouv+ePpBbBBBAAAEEEKhtAYWErjlAv3d2PaSH3j1JpOu5RQABBBBAAIH6EUjzQKJrKLAggAACCCCAAALdBRQWct7EwNdEdpfhdwQQQAABBBAIAh1zI8FAAAEEEEAAAQR6ECAo9IDCKgQQQAABBBDoECAo8E5AAAEEEEAAgYoCBIWKNGxAAAEEEEAAAYIC7wEEEEAAAQQQqChAUKhIwwYEEEAAAQQQICjwHkAAAQQQQACBigIEhYo0bEAAAQQQQAABggLvAQQQQAABBBCoKEBQqEjDBgQQQAABBBAgKPAeQAABBBBAAIGKAgSFijRsQAABBBBAAAGCAu8BBBBAAAEEEKgoQFCoSMMGBBBAAAEEECAo8B5AAAEEEEAAgYoCBIWKNGxAAAEEEEAAAYIC7wEEEEAAAQQQqChAUKhIwwYEEEAAAQQQICjwHkAAAQQQQACBigIEhYo0bEAAAQQQQAABggLvAQQQQAABBBCoKJBU3MIGBBCIUmDz5s22ePFiKxaLNmbMGBs0aFCUx0GhEUCgdwgQFHpHPVAKBH4ygXXr1tnjjz9uCgyXXnppCAobN26077//3trb2yvuZ+TIkZbL5axcLtvLL79smzZt2uWx2r5t2zZbsWLFLtvSFccee6wdf/zx1q9fv3QVtwggELEAQSHiyqPoCPQkUCqVbOHChSEYtLa2hod89tln9vzzz9uaNWt6ekpYd8cdd1hzc3MIE/fcc499/vnnNmrUKMvn89bW1marVq2yxsZG22+//WzBggWm/axfv960jyFDhlhTU1N4HQWJww47jKBQUZoNCMQlQFCIq74oLQI/SmDr1q3hpP7dd99VfL5aEtJFAeCbb76xESNGhKCgboxly5aF4HDJJZeElge1Kjz55JPW0tJi559/vg0fPjw8fdy4cTZgwID0pbhFAIHIBQgKkVcgxUdAAvoUP2fOHPv4449t7dq1Nm/evHACf+yxx2zGjBl2xBFH2NVXXx3GLVQSS1sEKm3X+sGDB9ukSZPsvffeCy0MCh56nropGhoabPz48XbwwQdb3759d/cybEMAgYgECAoRVRZFRaCSgD7Vv/vuu3brrbeGLgGNSVALwb333mtJkthtt91mJ510UqWn97he3Qynn3566EJQl8XSpUvD4+bPn2833XST9enTJwSELVu2hJYFdUGcccYZNnHiRNtnn316fE1WIoBAfAIEhfjqjBIjsIuAxhacfPLJ4VP922+/bS+88EIYS3D22WeHVgCNUdAJfk/Ldddd1/kQjUt47bXXwrgEhYENGzaEbWq90P1TTjklhAU9RiHkq6++CsGhaxdG54txBwEEohUgKERbdRQcgR0C+vR/9NFHh+mQ+uSvgYbqDlC3wAknnGD33Xefvf/++zueUOHetddeG8YkXHDBBTZ16tSdHqXWBQ1u1Gtqf6NHjw5dDGo9mDx5cmeQ2OlJ/IIAAtELEBSir0IOAAELgwvVxbB69eow40FBQZ/6NUZh4MCBdt5559m5554bqGbPnm0PPvignXnmmaEloOvYhEKhELop1PrQU8uAXj+dGqkBjury0HgEBi/yLkSgdgUICrVbtxxZnQnopD1r1qzOloNDDjkkCLzyyit21FFHhYGGagnQdMennnrKjjzySNM1DzTD4ZhjjgndFrpOgrop1CqhoKBWiUWLFoUZDfvvv394vXSKpbom9BgFEQ1yZEEAgdoUICjUZr1yVHUooOmMb775ZggCmtao2Qe68JFO4goIN998cwgL6YWVtO6JJ54I4xnuv/9+O/zww4Pa9ddfby+99JI988wzIUBoBsX06dPt4osvDtvfeecdu+GGG0JrgsYraHwEsxzq8A3HIdeNAEGhbqqaA61lAU1T1Il95syZYaaCPvWrK+LUU08N4weee+45+/DDD8OJX7MV0kUtDXfddZe9+uqr4XFqbTjooINMXRBffPGFTZs2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\" alt=\"NavBar\"></p> <h3 id=\"materialcolor\"><a href=\"#materialcolor\" class=\"header-anchor\">#</a> MaterialColor</h3> <p><code>MaterialColor</code>是实现Material Design中的颜色的类，它包含一种颜色的10个级别的渐变色。<code>MaterialColor</code>通过&quot;[]&quot;运算符的索引值来代表颜色的深度，有效的索引有：50，100，200，…，900，数字越大，颜色越深。<code>MaterialColor</code>的默认值为索引等于500的颜色。举个例子，<code>Colors.blue</code>是预定义的一个<code>MaterialColor</code>类对象，定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">static</span> <span class=\"token keyword\">const</span> MaterialColor blue <span class=\"token operator\">=</span> <span class=\"token function\">MaterialColor</span><span class=\"token punctuation\">(</span>\n  _bluePrimaryValue<span class=\"token punctuation\">,</span>\n  <span class=\"token operator\">&lt;</span>int<span class=\"token punctuation\">,</span> Color<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">{</span>\n     <span class=\"token number\">50</span><span class=\"token punctuation\">:</span> <span class=\"token function\">Color</span><span class=\"token punctuation\">(</span><span class=\"token number\">0xFFE3F2FD</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token number\">100</span><span class=\"token punctuation\">:</span> <span class=\"token function\">Color</span><span class=\"token punctuation\">(</span><span class=\"token number\">0xFFBBDEFB</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token number\">200</span><span class=\"token punctuation\">:</span> <span class=\"token function\">Color</span><span class=\"token punctuation\">(</span><span class=\"token number\">0xFF90CAF9</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token number\">300</span><span class=\"token punctuation\">:</span> <span class=\"token function\">Color</span><span class=\"token punctuation\">(</span><span class=\"token number\">0xFF64B5F6</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token number\">400</span><span class=\"token punctuation\">:</span> <span class=\"token function\">Color</span><span class=\"token punctuation\">(</span><span class=\"token number\">0xFF42A5F5</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token number\">500</span><span class=\"token punctuation\">:</span> <span class=\"token function\">Color</span><span class=\"token punctuation\">(</span>_bluePrimaryValue<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token number\">600</span><span class=\"token punctuation\">:</span> <span class=\"token function\">Color</span><span class=\"token punctuation\">(</span><span class=\"token number\">0xFF1E88E5</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token number\">700</span><span class=\"token punctuation\">:</span> <span class=\"token function\">Color</span><span class=\"token punctuation\">(</span><span class=\"token number\">0xFF1976D2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token number\">800</span><span class=\"token punctuation\">:</span> <span class=\"token function\">Color</span><span class=\"token punctuation\">(</span><span class=\"token number\">0xFF1565C0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token number\">900</span><span class=\"token punctuation\">:</span> <span class=\"token function\">Color</span><span class=\"token punctuation\">(</span><span class=\"token number\">0xFF0D47A1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">static</span> <span class=\"token keyword\">const</span> int _bluePrimaryValue <span class=\"token operator\">=</span> <span class=\"token number\">0xFF2196F3</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p><code>Colors.blue[50]</code>到<code>Colors.blue[900]</code>的色值从浅蓝到深蓝渐变，效果如图7-5所示：</p> <p><img src=\"/assets/img/7-5.6f1c5012.jpeg\" alt=\"NavBar\"></p> <h2 id=\"_7-4-2-theme\"><a href=\"#_7-4-2-theme\" class=\"header-anchor\">#</a> 7.4.2 Theme</h2> <p><code>Theme</code>组件可以为Material APP定义主题数据（ThemeData）。Material组件库里很多组件都使用了主题数据，如导航栏颜色、标题字体、Icon样式等。<code>Theme</code>内会使用<code>InheritedWidget</code>来为其子树共享样式数据。</p> <h3 id=\"themedata\"><a href=\"#themedata\" class=\"header-anchor\">#</a> ThemeData</h3> <p><code>ThemeData</code>用于保存是Material 组件库的主题数据，Material组件需要遵守相应的设计规范，而这些规范可自定义部分都定义在ThemeData中了，所以我们可以通过ThemeData来自定义应用主题。在子组件中，我们可以通过<code>Theme.of</code>方法来获取当前的<code>ThemeData</code>。</p> <blockquote><p>注意：Material Design 设计规范中有些是不能自定义的，如导航栏高度，ThemeData只包含了可自定义部分。</p></blockquote> <p>我们看看<code>ThemeData</code>部分数据定义：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">ThemeData</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  Brightness brightness<span class=\"token punctuation\">,</span> <span class=\"token comment\">//深色还是浅色</span>\n  MaterialColor primarySwatch<span class=\"token punctuation\">,</span> <span class=\"token comment\">//主题颜色样本，见下面介绍</span>\n  Color primaryColor<span class=\"token punctuation\">,</span> <span class=\"token comment\">//主色，决定导航栏颜色</span>\n  Color accentColor<span class=\"token punctuation\">,</span> <span class=\"token comment\">//次级色，决定大多数Widget的颜色，如进度条、开关等。</span>\n  Color cardColor<span class=\"token punctuation\">,</span> <span class=\"token comment\">//卡片颜色</span>\n  Color dividerColor<span class=\"token punctuation\">,</span> <span class=\"token comment\">//分割线颜色</span>\n  ButtonThemeData buttonTheme<span class=\"token punctuation\">,</span> <span class=\"token comment\">//按钮主题</span>\n  Color cursorColor<span class=\"token punctuation\">,</span> <span class=\"token comment\">//输入框光标颜色</span>\n  Color dialogBackgroundColor<span class=\"token punctuation\">,</span><span class=\"token comment\">//对话框背景颜色</span>\n  String fontFamily<span class=\"token punctuation\">,</span> <span class=\"token comment\">//文字字体</span>\n  TextTheme textTheme<span class=\"token punctuation\">,</span><span class=\"token comment\">// 字体主题，包括标题、body等文字样式</span>\n  IconThemeData iconTheme<span class=\"token punctuation\">,</span> <span class=\"token comment\">// Icon的默认样式</span>\n  TargetPlatform platform<span class=\"token punctuation\">,</span> <span class=\"token comment\">//指定平台，应用特定平台控件风格</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>上面只是<code>ThemeData</code>的一小部分属性，完整的数据定义读者可以查看SDK。上面属性中需要说明的是<code>primarySwatch</code>，它是主题颜色的一个&quot;样本色&quot;，通过这个样本色可以在一些条件下生成一些其它的属性，例如，如果没有指定<code>primaryColor</code>，并且当前主题不是深色主题，那么<code>primaryColor</code>就会默认为<code>primarySwatch</code>指定的颜色，还有一些相似的属性如<code>accentColor</code> 、<code>indicatorColor</code>等也会受<code>primarySwatch</code>影响。</p> <h3 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h3> <p>我们实现一个路由换肤功能：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">ThemeTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _ThemeTestRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_ThemeTestRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_ThemeTestRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>ThemeTestRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  Color _themeColor <span class=\"token operator\">=</span> Colors<span class=\"token punctuation\">.</span>teal<span class=\"token punctuation\">;</span> <span class=\"token comment\">//当前路由主题色</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    ThemeData themeData <span class=\"token operator\">=</span> Theme<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Theme</span><span class=\"token punctuation\">(</span>\n      data<span class=\"token punctuation\">:</span> <span class=\"token function\">ThemeData</span><span class=\"token punctuation\">(</span>\n          primarySwatch<span class=\"token punctuation\">:</span> _themeColor<span class=\"token punctuation\">,</span> <span class=\"token comment\">//用于导航栏、FloatingActionButton的背景色等</span>\n          iconTheme<span class=\"token punctuation\">:</span> <span class=\"token function\">IconThemeData</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> _themeColor<span class=\"token punctuation\">)</span> <span class=\"token comment\">//用于Icon颜色</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n        appBar<span class=\"token punctuation\">:</span> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;主题测试&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        body<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n          mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            <span class=\"token comment\">//第一行Icon使用主题中的iconTheme</span>\n            <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n                mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n                children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                  <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>favorite<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>airport_shuttle<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;  颜色跟随主题&quot;</span><span class=\"token punctuation\">)</span>\n                <span class=\"token punctuation\">]</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token comment\">//为第二行Icon自定义颜色（固定为黑色)</span>\n            <span class=\"token function\">Theme</span><span class=\"token punctuation\">(</span>\n              data<span class=\"token punctuation\">:</span> themeData<span class=\"token punctuation\">.</span><span class=\"token function\">copyWith</span><span class=\"token punctuation\">(</span>\n                iconTheme<span class=\"token punctuation\">:</span> themeData<span class=\"token punctuation\">.</span>iconTheme<span class=\"token punctuation\">.</span><span class=\"token function\">copyWith</span><span class=\"token punctuation\">(</span>\n                    color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>black\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n                  mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n                  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>favorite<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>airport_shuttle<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                    <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;  颜色固定黑色&quot;</span><span class=\"token punctuation\">)</span>\n                  <span class=\"token punctuation\">]</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        floatingActionButton<span class=\"token punctuation\">:</span> <span class=\"token function\">FloatingActionButton</span><span class=\"token punctuation\">(</span>\n            onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span>  <span class=\"token comment\">//切换主题</span>\n                <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span>\n                _themeColor <span class=\"token operator\">=</span>\n                _themeColor <span class=\"token operator\">==</span> Colors<span class=\"token punctuation\">.</span>teal <span class=\"token operator\">?</span> Colors<span class=\"token punctuation\">.</span>blue <span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>teal\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>palette<span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行后点击右下角悬浮按钮则可以切换主题，如图7-6、7-7所示：</p> <p><img src=\"/assets/img/7-6.3bce9f21.png\" alt=\"图7-6\"><img src=\"/assets/img/7-7.b9c5d9fe.png\" alt=\"图7-7\"></p> <p>需要注意的有三点：</p> <ul><li><p>可以通过局部主题覆盖全局主题，正如代码中通过Theme为第二行图标指定固定颜色（黑色）一样，这是一种常用的技巧，Flutter中会经常使用这种方法来自定义子树主题。那么为什么局部主题可以覆盖全局主题？这主要是因为widget中使用主题样式时是通过<code>Theme.of(BuildContext context)</code>来获取的，我们看看其简化后的代码：</p></li> <li><div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">static</span> ThemeData <span class=\"token function\">of</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> bool shadowThemeOnly <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n   <span class=\"token comment\">// 简化代码，并非源码  </span>\n   <span class=\"token keyword\">return</span> context<span class=\"token punctuation\">.</span>dependOnInheritedWidgetOfExactType<span class=\"token operator\">&lt;</span>_InheritedTheme<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>theme<span class=\"token punctuation\">.</span>data\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p><code>context.dependOnInheritedWidgetOfExactType</code> 会在widget树中从当前位置向上查找第一个类型为<code>_InheritedTheme</code>的widget。所以当局部指定<code>Theme</code>后，其子树中通过<code>Theme.of()</code>向上查找到的第一个<code>_InheritedTheme</code>便是我们指定的<code>Theme</code>。</p></li> <li><p>本示例是对单个路由换肤，如果想要对整个应用换肤，则可以去修改<code>MaterialApp</code>的<code>theme</code>属性。</p></li></ul></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter7/provider.html\" class=\"prev\">\n        7.3 跨组件状态共享（Provider）\n      </a></span> <span class=\"next\"><a href=\"/chapter7/futurebuilder_and_streambuilder.html\">\n        7.5 异步UI更新（FutureBuilder、StreamBuilder）\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/30.b84a8f21.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter7/willpopscope.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>7.1 导航返回拦截（WillPopScope） | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/165.86c8b8c1.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable open\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" aria-current=\"page\" class=\"active sidebar-link\">7.1 导航返回拦截（WillPopScope）</a><ul class=\"sidebar-sub-headers\"></ul></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_7-1-导航返回拦截-willpopscope\"><a href=\"#_7-1-导航返回拦截-willpopscope\" class=\"header-anchor\">#</a> 7.1 导航返回拦截（WillPopScope）</h1> <p>为了避免用户误触返回按钮而导致APP退出，在很多APP中都拦截了用户点击返回键的按钮，然后进行一些防误触判断，比如当用户在某一个时间段内点击两次时，才会认为用户是要退出（而非误触）。Flutter中可以通过<code>WillPopScope</code>来实现返回按钮拦截，我们看看<code>WillPopScope</code>的默认构造函数：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">const</span> <span class=\"token function\">WillPopScope</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token metadata symbol\">@required</span> WillPopCallback onWillPop<span class=\"token punctuation\">,</span>\n  <span class=\"token metadata symbol\">@required</span> Widget child\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p><code>onWillPop</code>是一个回调函数，当用户点击返回按钮时被调用（包括导航返回按钮及Android物理返回按钮）。该回调需要返回一个<code>Future</code>对象，如果返回的<code>Future</code>最终值为<code>false</code>时，则当前路由不出栈(不会返回)；最终值为<code>true</code>时，当前路由出栈退出。我们需要提供这个回调来决定是否退出。</p> <h3 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h3> <p>为了防止用户误触返回键退出，我们拦截返回事件。当用户在1秒内点击两次返回按钮时，则退出；如果间隔超过1秒则不退出，并重新记时。代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">WillPopScopeTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  WillPopScopeTestRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">WillPopScopeTestRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">WillPopScopeTestRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>WillPopScopeTestRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  DateTime _lastPressedAt<span class=\"token punctuation\">;</span> <span class=\"token comment\">//上次点击时间</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">WillPopScope</span><span class=\"token punctuation\">(</span>\n        onWillPop<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>_lastPressedAt <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">||</span>\n              DateTime<span class=\"token punctuation\">.</span><span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">difference</span><span class=\"token punctuation\">(</span>_lastPressedAt<span class=\"token punctuation\">)</span> <span class=\"token operator\">&gt;</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">//两次点击间隔超过1秒则重新计时</span>\n            _lastPressedAt <span class=\"token operator\">=</span> DateTime<span class=\"token punctuation\">.</span><span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token keyword\">return</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span>\n          <span class=\"token keyword\">return</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n          alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;1秒内连续按两次返回键退出&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>读者可以运行示例看看效果。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter6/scroll_controller.html\" class=\"prev\">\n        6.6 滚动监听及控制\n      </a></span> <span class=\"next\"><a href=\"/chapter7/inherited_widget.html\">\n        7.2 数据共享（InheritedWidget）\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/165.86c8b8c1.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter8/eventbus.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>8.3 事件总线 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/166.3938fc5f.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable open\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" aria-current=\"page\" class=\"active sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_8-3-事件总线\"><a href=\"#_8-3-事件总线\" class=\"header-anchor\">#</a> 8.3 事件总线</h1> <p>在APP中，我们经常会需要一个广播机制，用以跨页面事件通知，比如一个需要登录的APP中，页面会关注用户登录或注销事件，来进行一些状态更新。这时候，一个事件总线便会非常有用，事件总线通常实现了订阅者模式，订阅者模式包含发布者和订阅者两种角色，可以通过事件总线来触发事件和监听事件，本节我们实现一个简单的全局事件总线，我们使用单例模式，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//订阅者回调签名</span>\n<span class=\"token keyword\">typedef</span> <span class=\"token keyword\">void</span> <span class=\"token function\">EventCallback</span><span class=\"token punctuation\">(</span>arg<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">EventBus</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">//私有构造函数</span>\n  EventBus<span class=\"token punctuation\">.</span><span class=\"token function\">_internal</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">//保存单例</span>\n  <span class=\"token keyword\">static</span> EventBus _singleton <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">EventBus<span class=\"token punctuation\">.</span>_internal</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">//工厂构造函数</span>\n  <span class=\"token keyword\">factory</span> <span class=\"token function\">EventBus</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _singleton<span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">//保存事件订阅者队列，key:事件名(id)，value: 对应事件的订阅者队列</span>\n  <span class=\"token keyword\">var</span> _emap <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Map</span><span class=\"token operator\">&lt;</span>Object<span class=\"token punctuation\">,</span> List<span class=\"token operator\">&lt;</span>EventCallback<span class=\"token operator\">&gt;&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">//添加订阅者</span>\n  <span class=\"token keyword\">void</span> <span class=\"token keyword\">on</span><span class=\"token punctuation\">(</span>eventName<span class=\"token punctuation\">,</span> EventCallback f<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>eventName <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">||</span> f <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span><span class=\"token punctuation\">;</span>\n    _emap<span class=\"token punctuation\">[</span>eventName<span class=\"token punctuation\">]</span> <span class=\"token operator\">?</span><span class=\"token operator\">?</span><span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">List</span><span class=\"token operator\">&lt;</span>EventCallback<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    _emap<span class=\"token punctuation\">[</span>eventName<span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span>f<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">//移除订阅者</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">off</span><span class=\"token punctuation\">(</span>eventName<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>EventCallback f<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">var</span> list <span class=\"token operator\">=</span> _emap<span class=\"token punctuation\">[</span>eventName<span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>eventName <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">||</span> list <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>f <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _emap<span class=\"token punctuation\">[</span>eventName<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n      list<span class=\"token punctuation\">.</span><span class=\"token function\">remove</span><span class=\"token punctuation\">(</span>f<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">//触发事件，事件触发后该事件所有订阅者会被调用</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">emit</span><span class=\"token punctuation\">(</span>eventName<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>arg<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">var</span> list <span class=\"token operator\">=</span> _emap<span class=\"token punctuation\">[</span>eventName<span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>list <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span><span class=\"token punctuation\">;</span>\n    int len <span class=\"token operator\">=</span> list<span class=\"token punctuation\">.</span>length <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//反向遍历，防止订阅者在回调中移除自身带来的下标错位 </span>\n    <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">var</span> i <span class=\"token operator\">=</span> len<span class=\"token punctuation\">;</span> i <span class=\"token operator\">&gt;</span> <span class=\"token operator\">-</span><span class=\"token number\">1</span><span class=\"token punctuation\">;</span> <span class=\"token operator\">--</span>i<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      list<span class=\"token punctuation\">[</span>i<span class=\"token punctuation\">]</span><span class=\"token punctuation\">(</span>arg<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">//定义一个top-level（全局）变量，页面引入该文件后可以直接使用bus</span>\n<span class=\"token keyword\">var</span> bus <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">EventBus</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>使用示例：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//页面A中</span>\n<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n <span class=\"token comment\">//监听登录事件</span>\nbus<span class=\"token punctuation\">.</span><span class=\"token keyword\">on</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;login&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span>arg<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// do something</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">//登录页B中</span>\n<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token comment\">//登录成功后触发登录事件，页面A中订阅者会被调用</span>\nbus<span class=\"token punctuation\">.</span><span class=\"token function\">emit</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;login&quot;</span><span class=\"token punctuation\">,</span> userInfo<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n</code></pre></div><blockquote><p>注意：Dart中实现单例模式的标准做法就是使用static变量+工厂构造函数的方式，这样就可以保证<code>new EventBus()</code>始终返回都是同一个实例，读者应该理解并掌握这种方法。</p></blockquote> <p>事件总线通常用于组件之间状态共享，但关于组件之间状态共享也有一些专门的包如redux、以及前面介绍过的Provider。对于一些简单的应用，事件总线是足以满足业务需求的，如果你决定使用状态管理包的话，一定要想清楚您的APP是否真的有必要使用它，防止“化简为繁”、过度设计。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter8/gesture.html\" class=\"prev\">\n        8.2 手势识别\n      </a></span> <span class=\"next\"><a href=\"/chapter8/notification.html\">\n        8.4 Notification\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/166.3938fc5f.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter8/gesture.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>8.2 手势识别 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/31.dca209c1.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable open\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" aria-current=\"page\" class=\"active sidebar-link\">8.2 手势识别</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/chapter8/gesture.html#_8-2-1-gesturedetector\" class=\"sidebar-link\">8.2.1 GestureDetector</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter8/gesture.html#_8-2-2-gesturerecognizer\" class=\"sidebar-link\">8.2.2 GestureRecognizer</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter8/gesture.html#_8-2-3-手势竞争与冲突\" class=\"sidebar-link\">8.2.3 手势竞争与冲突</a></li></ul></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_8-2-手势识别\"><a href=\"#_8-2-手势识别\" class=\"header-anchor\">#</a> 8.2 手势识别</h1> <p>本节先介绍一些Flutter中用于处理手势的<code>GestureDetector</code>和<code>GestureRecognizer</code>，然后再仔细讨论一下手势竞争与冲突问题。</p> <h2 id=\"_8-2-1-gesturedetector\"><a href=\"#_8-2-1-gesturedetector\" class=\"header-anchor\">#</a> 8.2.1 GestureDetector</h2> <p><code>GestureDetector</code>是一个用于手势识别的功能性组件，我们通过它可以来识别各种手势。<code>GestureDetector</code>实际上是指针事件的语义化封装，接下来我们详细介绍一下各种手势识别。</p> <h3 id=\"点击、双击、长按\"><a href=\"#点击、双击、长按\" class=\"header-anchor\">#</a> 点击、双击、长按</h3> <p>我们通过<code>GestureDetector</code>对<code>Container</code>进行手势识别，触发相应事件后，在<code>Container</code>上显示事件名，为了增大点击区域，将<code>Container</code>设置为200×100，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">GestureDetectorTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _GestureDetectorTestRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span>\n      <span class=\"token keyword\">new</span> <span class=\"token class-name\">_GestureDetectorTestRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_GestureDetectorTestRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>GestureDetectorTestRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  String _operation <span class=\"token operator\">=</span> <span class=\"token string\">&quot;No Gesture detected!&quot;</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//保存事件名</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">GestureDetector</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n          alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n          color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span>\n          width<span class=\"token punctuation\">:</span> <span class=\"token number\">200.0</span><span class=\"token punctuation\">,</span> \n          height<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>_operation<span class=\"token punctuation\">,</span>\n            style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        onTap<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">updateText</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Tap&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token comment\">//点击</span>\n        onDoubleTap<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">updateText</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;DoubleTap&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//双击</span>\n        onLongPress<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">updateText</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;LongPress&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//长按</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">updateText</span><span class=\"token punctuation\">(</span>String text<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//更新显示的事件名</span>\n    <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _operation <span class=\"token operator\">=</span> text<span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行效果如图8-2所示：</p> <p><img src=\"/assets/img/8-2.52bd91bd.png\" alt=\"图8-2\"></p> <blockquote><p><strong>注意</strong>： 当同时监听<code>onTap</code>和<code>onDoubleTap</code>事件时，当用户触发tap事件时，会有200毫秒左右的延时，这是因为当用户点击完之后很可能会再次点击以触发双击事件，所以<code>GestureDetector</code>会等一段时间来确定是否为双击事件。如果用户只监听了<code>onTap</code>（没有监听<code>onDoubleTap</code>）事件时，则没有延时。</p></blockquote> <h3 id=\"拖动、滑动\"><a href=\"#拖动、滑动\" class=\"header-anchor\">#</a> 拖动、滑动</h3> <p>一次完整的手势过程是指用户手指按下到抬起的整个过程，期间，用户按下手指后可能会移动，也可能不会移动。<code>GestureDetector</code>对于拖动和滑动事件是没有区分的，他们本质上是一样的。<code>GestureDetector</code>会将要监听的组件的原点（左上角）作为本次手势的原点，当用户在监听的组件上按下手指时，手势识别就会开始。下面我们看一个拖动圆形字母A的示例：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">_Drag</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _DragState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_DragState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_DragState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>_Drag<span class=\"token operator\">&gt;</span> <span class=\"token keyword\">with</span> SingleTickerProviderStateMixin <span class=\"token punctuation\">{</span>\n  double _top <span class=\"token operator\">=</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//距顶部的偏移</span>\n  double _left <span class=\"token operator\">=</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">;</span><span class=\"token comment\">//距左边的偏移</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Stack</span><span class=\"token punctuation\">(</span>\n      children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n        <span class=\"token function\">Positioned</span><span class=\"token punctuation\">(</span>\n          top<span class=\"token punctuation\">:</span> _top<span class=\"token punctuation\">,</span>\n          left<span class=\"token punctuation\">:</span> _left<span class=\"token punctuation\">,</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">GestureDetector</span><span class=\"token punctuation\">(</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">CircleAvatar</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;A&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token comment\">//手指按下时会触发此回调</span>\n            onPanDown<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>DragDownDetails e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token comment\">//打印手指按下的位置(相对于屏幕)</span>\n              <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;用户手指按下：${e.globalPosition}&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n            <span class=\"token comment\">//手指滑动时会触发此回调</span>\n            onPanUpdate<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>DragUpdateDetails e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token comment\">//用户手指滑动时，更新偏移，重新构建</span>\n              <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                _left <span class=\"token operator\">+=</span> e<span class=\"token punctuation\">.</span>delta<span class=\"token punctuation\">.</span>dx<span class=\"token punctuation\">;</span>\n                _top <span class=\"token operator\">+=</span> e<span class=\"token punctuation\">.</span>delta<span class=\"token punctuation\">.</span>dy<span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n            onPanEnd<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>DragEndDetails e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n              <span class=\"token comment\">//打印滑动结束时在x、y轴上的速度</span>\n              <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">.</span>velocity<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行后，就可以在任意方向拖动了，运行效果如图8-3所示：</p> <p><img src=\"/assets/img/8-3.975afea6.png\" alt=\"图8-3\"></p> <p>日志：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>I/flutter ( 8513): 用户手指按下：Offset(26.3, 101.8)\nI/flutter ( 8513): Velocity(235.5, 125.8)\n</code></pre></div><p>代码解释：</p> <ul><li><code>DragDownDetails.globalPosition</code>：当用户按下时，此属性为用户按下的位置相对于<strong>屏幕</strong>（而非父组件）原点(左上角)的偏移。</li> <li><code>DragUpdateDetails.delta</code>：当用户在屏幕上滑动时，会触发多次Update事件，<code>delta</code>指一次Update事件的滑动的偏移量。</li> <li><code>DragEndDetails.velocity</code>：该属性代表用户抬起手指时的滑动速度(包含x、y两个轴的），示例中并没有处理手指抬起时的速度，常见的效果是根据用户抬起手指时的速度做一个减速动画。</li></ul> <h3 id=\"单一方向拖动\"><a href=\"#单一方向拖动\" class=\"header-anchor\">#</a> 单一方向拖动</h3> <p>在本示例中，是可以朝任意方向拖动的，但是在很多场景，我们只需要沿一个方向来拖动，如一个垂直方向的列表，<code>GestureDetector</code>可以只识别特定方向的手势事件，我们将上面的例子改为只能沿垂直方向拖动：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">_DragVertical</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _DragVerticalState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_DragVerticalState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_DragVerticalState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>_DragVertical<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  double _top <span class=\"token operator\">=</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Stack</span><span class=\"token punctuation\">(</span>\n      children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n        <span class=\"token function\">Positioned</span><span class=\"token punctuation\">(</span>\n          top<span class=\"token punctuation\">:</span> _top<span class=\"token punctuation\">,</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">GestureDetector</span><span class=\"token punctuation\">(</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">CircleAvatar</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;A&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token comment\">//垂直方向拖动事件</span>\n            onVerticalDragUpdate<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>DragUpdateDetails details<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                _top <span class=\"token operator\">+=</span> details<span class=\"token punctuation\">.</span>delta<span class=\"token punctuation\">.</span>dy<span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>这样就只能在垂直方向拖动了，如果只想在水平方向滑动同理。</p> <h3 id=\"缩放\"><a href=\"#缩放\" class=\"header-anchor\">#</a> 缩放</h3> <p><code>GestureDetector</code>可以监听缩放事件，下面示例演示了一个简单的图片缩放效果：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">_ScaleTestRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>_ScaleTestRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  double _width <span class=\"token operator\">=</span> <span class=\"token number\">200.0</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//通过修改图片宽度来达到缩放效果</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n   <span class=\"token keyword\">return</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n     child<span class=\"token punctuation\">:</span> <span class=\"token function\">GestureDetector</span><span class=\"token punctuation\">(</span>\n        <span class=\"token comment\">//指定宽度，高度自适应</span>\n        child<span class=\"token punctuation\">:</span> Image<span class=\"token punctuation\">.</span><span class=\"token function\">asset</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;./images/sea.png&quot;</span><span class=\"token punctuation\">,</span> width<span class=\"token punctuation\">:</span> _width<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        onScaleUpdate<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>ScaleUpdateDetails details<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">//缩放倍数在0.8到10倍之间</span>\n            _width<span class=\"token operator\">=</span><span class=\"token number\">200</span><span class=\"token operator\">*</span>details<span class=\"token punctuation\">.</span>scale<span class=\"token punctuation\">.</span><span class=\"token function\">clamp</span><span class=\"token punctuation\">(</span><span class=\"token number\">.8</span><span class=\"token punctuation\">,</span> <span class=\"token number\">10.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n   <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行效果如图8-4所示：</p> <p><img src=\"/assets/img/8-4.6948ddf4.png\" alt=\"图8-4\"></p> <p>现在在图片上双指张开、收缩就可以放大、缩小图片。本示例比较简单，实际中我们通常还需要一些其它功能，如双击放大或缩小一定倍数、双指张开离开屏幕时执行一个减速放大动画等，读者可以在学习完后面“动画”一章中的内容后自己来尝试实现一下。</p> <h2 id=\"_8-2-2-gesturerecognizer\"><a href=\"#_8-2-2-gesturerecognizer\" class=\"header-anchor\">#</a> 8.2.2 GestureRecognizer</h2> <p><code>GestureDetector</code>内部是使用一个或多个<code>GestureRecognizer</code>来识别各种手势的，而<code>GestureRecognizer</code>的作用就是通过<code>Listener</code>来将原始指针事件转换为语义手势，<code>GestureDetector</code>直接可以接收一个子widget。<code>GestureRecognizer</code>是一个抽象类，一种手势的识别器对应一个<code>GestureRecognizer</code>的子类，Flutter实现了丰富的手势识别器，我们可以直接使用。</p> <h4 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h4> <p>假设我们要给一段富文本（<code>RichText</code>）的不同部分分别添加点击事件处理器，但是<code>TextSpan</code>并不是一个widget，这时我们不能用<code>GestureDetector</code>，但<code>TextSpan</code>有一个<code>recognizer</code>属性，它可以接收一个<code>GestureRecognizer</code>。</p> <p>假设我们需要在点击时给文本变色:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/gestures.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_GestureRecognizerTestRouteState</span>\n    <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>_GestureRecognizerTestRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  TapGestureRecognizer _tapGestureRecognizer <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">TapGestureRecognizer</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  bool _toggle <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//变色开关</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n     <span class=\"token comment\">//用到GestureRecognizer的话一定要调用其dispose方法释放资源</span>\n    _tapGestureRecognizer<span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> Text<span class=\"token punctuation\">.</span><span class=\"token function\">rich</span><span class=\"token punctuation\">(</span>\n          <span class=\"token function\">TextSpan</span><span class=\"token punctuation\">(</span>\n              children<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>\n                <span class=\"token function\">TextSpan</span><span class=\"token punctuation\">(</span>text<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;你好世界&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token function\">TextSpan</span><span class=\"token punctuation\">(</span>\n                  text<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;点我变色&quot;</span><span class=\"token punctuation\">,</span>\n                  style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>\n                      fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">30.0</span><span class=\"token punctuation\">,</span>\n                      color<span class=\"token punctuation\">:</span> _toggle <span class=\"token operator\">?</span> Colors<span class=\"token punctuation\">.</span>blue <span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  recognizer<span class=\"token punctuation\">:</span> _tapGestureRecognizer\n                    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>onTap <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                      <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                        _toggle <span class=\"token operator\">=</span> <span class=\"token operator\">!</span>_toggle<span class=\"token punctuation\">;</span>\n                      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token function\">TextSpan</span><span class=\"token punctuation\">(</span>text<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;你好世界&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">]</span>\n          <span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行效果：</p> <p><img src=\"/assets/img/8-5.ba487ffd.png\" alt=\"图8-5\"></p> <blockquote><p>注意：使用<code>GestureRecognizer</code>后一定要调用其<code>dispose()</code>方法来释放资源（主要是取消内部的计时器）。</p></blockquote> <h2 id=\"_8-2-3-手势竞争与冲突\"><a href=\"#_8-2-3-手势竞争与冲突\" class=\"header-anchor\">#</a> 8.2.3 手势竞争与冲突</h2> <h3 id=\"竞争\"><a href=\"#竞争\" class=\"header-anchor\">#</a> 竞争</h3> <p>如果在上例中我们同时监听水平和垂直方向的拖动事件，那么我们斜着拖动时哪个方向会生效？实际上取决于第一次移动时两个轴上的位移分量，哪个轴的大，哪个轴在本次滑动事件竞争中就胜出。实际上Flutter中的手势识别引入了一个Arena的概念，Arena直译为“竞技场”的意思，每一个手势识别器（<code>GestureRecognizer</code>）都是一个“竞争者”（<code>GestureArenaMember</code>），当发生滑动事件时，他们都要在“竞技场”去竞争本次事件的处理权，而最终只有一个“竞争者”会胜出(win)。例如，假设有一个<code>ListView</code>，它的第一个子组件也是<code>ListView</code>，如果现在滑动这个子<code>ListView</code>，父<code>ListView</code>会动吗？答案是否定的，这时只有子<code>ListView</code>会动，因为这时子<code>ListView</code>会胜出而获得滑动事件的处理权。</p> <h3 id=\"示例-2\"><a href=\"#示例-2\" class=\"header-anchor\">#</a> <strong>示例</strong></h3> <p>我们以拖动手势为例，同时识别水平和垂直方向的拖动手势，当用户按下手指时就会触发竞争（水平方向和垂直方向），一旦某个方向“获胜”，则直到当次拖动手势结束都会沿着该方向移动。代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">BothDirectionTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  BothDirectionTestRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span>\n      <span class=\"token keyword\">new</span> <span class=\"token class-name\">BothDirectionTestRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">BothDirectionTestRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>BothDirectionTestRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  double _top <span class=\"token operator\">=</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">;</span>\n  double _left <span class=\"token operator\">=</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Stack</span><span class=\"token punctuation\">(</span>\n      children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n        <span class=\"token function\">Positioned</span><span class=\"token punctuation\">(</span>\n          top<span class=\"token punctuation\">:</span> _top<span class=\"token punctuation\">,</span>\n          left<span class=\"token punctuation\">:</span> _left<span class=\"token punctuation\">,</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">GestureDetector</span><span class=\"token punctuation\">(</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">CircleAvatar</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;A&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token comment\">//垂直方向拖动事件</span>\n            onVerticalDragUpdate<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>DragUpdateDetails details<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                _top <span class=\"token operator\">+=</span> details<span class=\"token punctuation\">.</span>delta<span class=\"token punctuation\">.</span>dy<span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n            onHorizontalDragUpdate<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>DragUpdateDetails details<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                _left <span class=\"token operator\">+=</span> details<span class=\"token punctuation\">.</span>delta<span class=\"token punctuation\">.</span>dx<span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>此示例运行后，每次拖动只会沿一个方向移动（水平或垂直），而竞争发生在手指按下后首次移动（move）时，此例中具体的“获胜”条件是：首次移动时的位移在水平和垂直方向上的分量大的一个获胜。</p> <h3 id=\"手势冲突\"><a href=\"#手势冲突\" class=\"header-anchor\">#</a> 手势冲突</h3> <p>由于手势竞争最终只有一个胜出者，所以，当有多个手势识别器时，可能会产生冲突。假设有一个widget，它可以左右拖动，现在我们也想检测在它上面手指按下和抬起的事件，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">GestureConflictTestRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>GestureConflictTestRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  double _left <span class=\"token operator\">=</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">;</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Stack</span><span class=\"token punctuation\">(</span>\n      children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n        <span class=\"token function\">Positioned</span><span class=\"token punctuation\">(</span>\n          left<span class=\"token punctuation\">:</span> _left<span class=\"token punctuation\">,</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">GestureDetector</span><span class=\"token punctuation\">(</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">CircleAvatar</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;A&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//要拖动和点击的widget</span>\n              onHorizontalDragUpdate<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>DragUpdateDetails details<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                  _left <span class=\"token operator\">+=</span> details<span class=\"token punctuation\">.</span>delta<span class=\"token punctuation\">.</span>dx<span class=\"token punctuation\">;</span>\n                <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n              onHorizontalDragEnd<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>details<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n                <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;onHorizontalDragEnd&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n              onTapDown<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>details<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n                <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;down&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n              onTapUp<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>details<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n                <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;up&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>现在我们按住圆形“A”拖动然后抬起手指，控制台日志如下:</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>I/flutter (17539): down\nI/flutter (17539): onHorizontalDragEnd\n</code></pre></div><p>我们发现没有打印&quot;up&quot;，这是因为在拖动时，刚开始按下手指时在没有移动时，拖动手势还没有完整的语义，此时TapDown手势胜出(win)，此时打印&quot;down&quot;，而拖动时，拖动手势会胜出，当手指抬起时，<code>onHorizontalDragEnd</code> 和 <code>onTapUp</code>发生了冲突，但是因为是在拖动的语义中，所以<code>onHorizontalDragEnd</code>胜出，所以就会打印 “onHorizontalDragEnd”。如果我们的代码逻辑中，对于手指按下和抬起是强依赖的，比如在一个轮播图组件中，我们希望手指按下时，暂停轮播，而抬起时恢复轮播，但是由于轮播图组件中本身可能已经处理了拖动手势（支持手动滑动切换），甚至可能也支持了缩放手势，这时我们如果在外部再用<code>onTapDown</code>、<code>onTapUp</code>来监听的话是不行的。这时我们应该怎么做？其实很简单，通过Listener监听原始指针事件就行：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Positioned</span><span class=\"token punctuation\">(</span>\n  top<span class=\"token punctuation\">:</span><span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span>\n  left<span class=\"token punctuation\">:</span> _leftB<span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Listener</span><span class=\"token punctuation\">(</span>\n    onPointerDown<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>details<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;down&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    onPointerUp<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>details<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">//会触发</span>\n      <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;up&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">GestureDetector</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">CircleAvatar</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;B&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      onHorizontalDragUpdate<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>DragUpdateDetails details<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          _leftB <span class=\"token operator\">+=</span> details<span class=\"token punctuation\">.</span>delta<span class=\"token punctuation\">.</span>dx<span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      onHorizontalDragEnd<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>details<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;onHorizontalDragEnd&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>手势冲突只是手势级别的，而手势是对原始指针的语义化的识别，所以在遇到复杂的冲突场景时，都可以通过<code>Listener</code>直接识别原始指针事件来解决冲突。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter8/listener.html\" class=\"prev\">\n        8.1 原始指针事件处理\n      </a></span> <span class=\"next\"><a href=\"/chapter8/eventbus.html\">\n        8.3 事件总线\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/31.dca209c1.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter8/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>事件处理与通知 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/167.09cbb683.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h2 id=\"事件处理与通知\"><a href=\"#事件处理与通知\" class=\"header-anchor\">#</a> 事件处理与通知</h2> <p>Flutter中的手势系统有两个独立的层。第一层为原始指针(pointer)事件，它描述了屏幕上指针（例如，触摸、鼠标和触控笔）的位置和移动。 第二层为手势，描述由一个或多个指针移动组成的语义动作，如拖动、缩放、双击等。本章将先分别介绍如何处理这两种事件，最后再介绍一下Flutter中重要的Notification机制。</p> <h2 id=\"本章目录\"><a href=\"#本章目录\" class=\"header-anchor\">#</a> 本章目录</h2> <ul><li><a href=\"/chapter8/listener.html\">原始指针事件处理</a></li> <li><a href=\"/chapter8/gesture.html\">手势识别</a></li> <li><a href=\"/chapter8/eventbus.html\">全局事件总线</a></li> <li><a href=\"/chapter8/notification.html\">通知Notification</a></li></ul></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/167.09cbb683.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter8/listener.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>8.1 原始指针事件处理 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/103.21193c45.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable open\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" aria-current=\"page\" class=\"active sidebar-link\">8.1 原始指针事件处理</a><ul class=\"sidebar-sub-headers\"></ul></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_8-1-原始指针事件处理\"><a href=\"#_8-1-原始指针事件处理\" class=\"header-anchor\">#</a> 8.1 原始指针事件处理</h1> <p>本节先来介绍一下原始指针事件(Pointer Event，在移动设备上通常为触摸事件)，下一节再介绍手势处理。</p> <p>在移动端，各个平台或UI系统的原始指针事件模型基本都是一致，即：一次完整的事件分为三个阶段：手指按下、手指移动、和手指抬起，而更高级别的手势（如点击、双击、拖动等）都是基于这些原始事件的。</p> <p>当指针按下时，Flutter会对应用程序执行<strong>命中测试(Hit Test)</strong>，以确定指针与屏幕接触的位置存在哪些组件（widget）， 指针按下事件（以及该指针的后续事件）然后被分发到由命中测试发现的最内部的组件，然后从那里开始，事件会在组件树中向上冒泡，这些事件会从最内部的组件被分发到组件树根的路径上的所有组件，这和Web开发中浏览器的<strong>事件冒泡</strong>机制相似， 但是Flutter中没有机制取消或停止“冒泡”过程，而浏览器的冒泡是可以停止的。注意，只有通过命中测试的组件才能触发事件。</p> <p>Flutter中可以使用<code>Listener</code>来监听原始触摸事件，按照本书对组件的分类，则<code>Listener</code>也是一个功能性组件。下面是<code>Listener</code>的构造函数定义：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Listener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  Key key<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>onPointerDown<span class=\"token punctuation\">,</span> <span class=\"token comment\">//手指按下回调</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>onPointerMove<span class=\"token punctuation\">,</span> <span class=\"token comment\">//手指移动回调</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>onPointerUp<span class=\"token punctuation\">,</span><span class=\"token comment\">//手指抬起回调</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>onPointerCancel<span class=\"token punctuation\">,</span><span class=\"token comment\">//触摸事件取消回调</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>behavior <span class=\"token operator\">=</span> HitTestBehavior<span class=\"token punctuation\">.</span>deferToChild<span class=\"token punctuation\">,</span> <span class=\"token comment\">//在命中测试期间如何表现</span>\n  Widget child\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>我们先看一个示例，后面再单独讨论一下<code>behavior</code>属性。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token comment\">//定义一个状态，保存当前指针位置</span>\nPointerEvent _event<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token function\">Listener</span><span class=\"token punctuation\">(</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n    alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n    color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span>\n    width<span class=\"token punctuation\">:</span> <span class=\"token number\">300.0</span><span class=\"token punctuation\">,</span>\n    height<span class=\"token punctuation\">:</span> <span class=\"token number\">150.0</span><span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>_event<span class=\"token operator\">?</span><span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">?</span><span class=\"token operator\">?</span><span class=\"token string\">&quot;&quot;</span><span class=\"token punctuation\">,</span>style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  onPointerDown<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>PointerDownEvent event<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span>_event<span class=\"token operator\">=</span>event<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  onPointerMove<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>PointerMoveEvent event<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span>_event<span class=\"token operator\">=</span>event<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  onPointerUp<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>PointerUpEvent event<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span>_event<span class=\"token operator\">=</span>event<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</code></pre></div><p>运行后效果如图8-1所示：</p> <p><img src=\"/assets/img/8-1.fd0e65f1.png\" alt=\"图8-1\"></p> <p>手指在蓝色矩形区域内移动即可看到当前指针偏移，当触发指针事件时，参数<code>PointerDownEvent</code>、<code>PointerMoveEvent</code>、<code>PointerUpEvent</code>都是<code>PointerEvent</code>的一个子类，<code>PointerEvent</code>类中包括当前指针的一些信息，如：</p> <ul><li><code>position</code>：它是鼠标相对于当对于全局坐标的偏移。</li> <li><code>delta</code>：两次指针移动事件（<code>PointerMoveEvent</code>）的距离。</li> <li><code>pressure</code>：按压力度，如果手机屏幕支持压力传感器(如iPhone的3D Touch)，此属性会更有意义，如果手机不支持，则始终为1。</li> <li><code>orientation</code>：指针移动方向，是一个角度值。</li></ul> <p>上面只是<code>PointerEvent</code>一些常用属性，除了这些它还有很多属性，读者可以查看API文档。</p> <p>现在，我们重点来介绍一下<code>behavior</code>属性，它决定子组件如何响应命中测试，它的值类型为<code>HitTestBehavior</code>，这是一个枚举类，有三个枚举值：</p> <ul><li><p><code>deferToChild</code>：子组件会一个接一个的进行命中测试，如果子组件中有测试通过的，则当前组件通过，这就意味着，如果指针事件作用于子组件上时，其父级组件也肯定可以收到该事件。</p></li> <li><p><code>opaque</code>：在命中测试时，将当前组件当成不透明处理(即使本身是透明的)，最终的效果相当于当前Widget的整个区域都是点击区域。举个例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Listener</span><span class=\"token punctuation\">(</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">ConstrainedBox</span><span class=\"token punctuation\">(</span>\n        constraints<span class=\"token punctuation\">:</span> BoxConstraints<span class=\"token punctuation\">.</span><span class=\"token function\">tight</span><span class=\"token punctuation\">(</span><span class=\"token function\">Size</span><span class=\"token punctuation\">(</span><span class=\"token number\">300.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">150.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Box A&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token comment\">//behavior: HitTestBehavior.opaque,</span>\n    onPointerDown<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;down A&quot;</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</code></pre></div><p>上例中，只有点击文本内容区域才会触发点击事件，因为 <code>deferToChild</code> 会去子组件判断是否命中测试，而该例中子组件就是 <code>Text(&quot;Box A&quot;)</code> 。\n如果我们想让整个300×150的矩形区域都能点击我们可以将<code>behavior</code>设为<code>HitTestBehavior.opaque</code>。注意，该属性并不能用于在组件树中拦截（忽略）事件，它只是决定命中测试时的组件大小。</p></li> <li><p><code>translucent</code>：当点击组件透明区域时，可以对自身边界内及底部可视区域都进行命中测试，这意味着点击顶部组件透明区域时，顶部组件和底部组件都可以接收到事件，例如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Stack</span><span class=\"token punctuation\">(</span>\n  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token function\">Listener</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">ConstrainedBox</span><span class=\"token punctuation\">(</span>\n        constraints<span class=\"token punctuation\">:</span> BoxConstraints<span class=\"token punctuation\">.</span><span class=\"token function\">tight</span><span class=\"token punctuation\">(</span><span class=\"token function\">Size</span><span class=\"token punctuation\">(</span><span class=\"token number\">300.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">200.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">DecoratedBox</span><span class=\"token punctuation\">(</span>\n            decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      onPointerDown<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;down0&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Listener</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">ConstrainedBox</span><span class=\"token punctuation\">(</span>\n        constraints<span class=\"token punctuation\">:</span> BoxConstraints<span class=\"token punctuation\">.</span><span class=\"token function\">tight</span><span class=\"token punctuation\">(</span><span class=\"token function\">Size</span><span class=\"token punctuation\">(</span><span class=\"token number\">200.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;左上角200*100范围内非文本区域点击&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      onPointerDown<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;down1&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token comment\">//behavior: HitTestBehavior.translucent, //放开此行注释后可以&quot;点透&quot;</span>\n    <span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>上例中，当注释掉最后一行代码后，在左上角200*100范围内非文本区域点击时（顶部组件透明区域），控制台只会打印“down0”，也就是说顶部组件没有接收到事件，而只有底部接收到了。当放开注释后，再点击时顶部和底部都会接收到事件，此时会打印：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>I/flutter ( 3039): down1\nI/flutter ( 3039): down0\n</code></pre></div><p>如果<code>behavior</code>值改为<code>HitTestBehavior.opaque</code>，则只会打印&quot;down1&quot;。</p></li></ul> <h3 id=\"忽略pointerevent\"><a href=\"#忽略pointerevent\" class=\"header-anchor\">#</a> 忽略PointerEvent</h3> <p>假如我们不想让某个子树响应<code>PointerEvent</code>的话，我们可以使用<code>IgnorePointer</code>和<code>AbsorbPointer</code>，这两个组件都能阻止子树接收指针事件，不同之处在于<code>AbsorbPointer</code>本身会参与命中测试，而<code>IgnorePointer</code>本身不会参与，这就意味着<code>AbsorbPointer</code>本身是可以接收指针事件的(但其子树不行)，而<code>IgnorePointer</code>不可以。一个简单的例子如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Listener</span><span class=\"token punctuation\">(</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">AbsorbPointer</span><span class=\"token punctuation\">(</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">Listener</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n        color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span>\n        width<span class=\"token punctuation\">:</span> <span class=\"token number\">200.0</span><span class=\"token punctuation\">,</span>\n        height<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      onPointerDown<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span><span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span><span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;in&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  onPointerDown<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span><span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span><span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;up&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>点击<code>Container</code>时，由于它在<code>AbsorbPointer</code>的子树上，所以不会响应指针事件，所以日志不会输出&quot;in&quot;，但<code>AbsorbPointer</code>本身是可以接收指针事件的，所以会输出&quot;up&quot;。如果将<code>AbsorbPointer</code>换成<code>IgnorePointer</code>，那么两个都不会输出。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter7/dailog.html\" class=\"prev\">\n        7.6 对话框详解\n      </a></span> <span class=\"next\"><a href=\"/chapter8/gesture.html\">\n        8.2 手势识别\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/103.21193c45.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter8/notification.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>8.4 Notification | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/104.02d5761d.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable open\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" aria-current=\"page\" class=\"active sidebar-link\">8.4 Notification</a><ul class=\"sidebar-sub-headers\"></ul></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_8-4-notification\"><a href=\"#_8-4-notification\" class=\"header-anchor\">#</a> 8.4 Notification</h1> <p>通知（Notification）是Flutter中一个重要的机制，在widget树中，每一个节点都可以分发通知，通知会沿着当前节点向上传递，所有父节点都可以通过<code>NotificationListener</code>来监听通知。Flutter中将这种由子向父的传递通知的机制称为<strong>通知冒泡</strong>（Notification Bubbling）。通知冒泡和用户触摸事件冒泡是相似的，但有一点不同：通知冒泡可以中止，但用户触摸事件不行。</p> <blockquote><p>通知冒泡和Web开发中浏览器事件冒泡原理是相似的，都是事件从出发源逐层向上传递，我们可以在上层节点任意位置来监听通知/事件，也可以终止冒泡过程，终止冒泡后，通知将不会再向上传递。</p></blockquote> <p>Flutter中很多地方使用了通知，如可滚动组件（Scrollable Widget）滑动时就会分发<strong>滚动通知</strong>（ScrollNotification），而Scrollbar正是通过监听ScrollNotification来确定滚动条位置的。</p> <p>下面是一个监听可滚动组件滚动通知的例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">NotificationListener</span><span class=\"token punctuation\">(</span>\n  onNotification<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>notification<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">switch</span> <span class=\"token punctuation\">(</span>notification<span class=\"token punctuation\">.</span>runtimeType<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">case</span> ScrollStartNotification<span class=\"token punctuation\">:</span> <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;开始滚动&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">case</span> ScrollUpdateNotification<span class=\"token punctuation\">:</span> <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;正在滚动&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">case</span> ScrollEndNotification<span class=\"token punctuation\">:</span> <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;滚动停止&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">case</span> OverscrollNotification<span class=\"token punctuation\">:</span> <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;滚动到边界&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> ListView<span class=\"token punctuation\">.</span><span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>\n      itemCount<span class=\"token punctuation\">:</span> <span class=\"token number\">100</span><span class=\"token punctuation\">,</span>\n      itemBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">return</span> <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;$index&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>上例中的滚动通知如<code>ScrollStartNotification</code>、<code>ScrollUpdateNotification</code>等都是继承自<code>ScrollNotification</code>类，不同类型的通知子类会包含不同的信息，比如<code>ScrollUpdateNotification</code>有一个<code>scrollDelta</code>属性，它记录了移动的位移，其它通知属性读者可以自己查看SDK文档。</p> <p>上例中，我们通过<code>NotificationListener</code>来监听子<code>ListView</code>的滚动通知的，<code>NotificationListener</code>定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">NotificationListener</span><span class=\"token operator\">&lt;</span>T <span class=\"token keyword\">extends</span> <span class=\"token class-name\">Notification</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function\">NotificationListener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n    <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>onNotification<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token comment\">//省略无关代码 </span>\n<span class=\"token punctuation\">}</span>  \n</code></pre></div><p>我们可以看到：</p> <ol><li><p><code>NotificationListener</code> 继承自<code>StatelessWidget</code>类，所以它可以直接嵌套到Widget树中。</p></li> <li><p><code>NotificationListener</code> 可以指定一个模板参数，该模板参数类型必须是继承自<code>Notification</code>；当显式指定模板参数时，<code>NotificationListener</code> 便只会接收该参数类型的通知。举个例子，如果我们将上例子代码改为：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//指定监听通知的类型为滚动结束通知(ScrollEndNotification)</span>\nNotificationListener<span class=\"token operator\">&lt;</span>ScrollEndNotification<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n  onNotification<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>notification<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//只会在滚动结束时才会触发此回调</span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>notification<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> ListView<span class=\"token punctuation\">.</span><span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>\n      itemCount<span class=\"token punctuation\">:</span> <span class=\"token number\">100</span><span class=\"token punctuation\">,</span>\n      itemBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">return</span> <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;$index&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>上面代码运行后便只会在滚动结束时在控制台打印出通知的信息。</p></li> <li><p><code>onNotification</code>回调为通知处理回调，其函数签名如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">typedef</span> NotificationListenerCallback<span class=\"token operator\">&lt;</span>T <span class=\"token keyword\">extends</span> <span class=\"token class-name\">Notification</span><span class=\"token operator\">&gt;</span> <span class=\"token operator\">=</span> bool <span class=\"token keyword\">Function</span><span class=\"token punctuation\">(</span>T notification<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>它的返回值类型为布尔值，当返回值为<code>true</code>时，阻止冒泡，其父级Widget将再也收不到该通知；当返回值为<code>false</code> 时继续向上冒泡通知。</p></li></ol> <p>Flutter的UI框架实现中，除了在可滚动组件在滚动过程中会发出<code>ScrollNotification</code>之外，还有一些其它的通知，如<code>SizeChangedLayoutNotification</code>、<code>KeepAliveNotification</code> 、<code>LayoutChangedNotification</code>等，Flutter正是通过这种通知机制来使父元素可以在一些特定时机来做一些事情。</p> <h4 id=\"自定义通知\"><a href=\"#自定义通知\" class=\"header-anchor\">#</a> 自定义通知</h4> <p>除了Flutter内部通知，我们也可以自定义通知，下面我们看看如何实现自定义通知：</p> <ol><li><p>定义一个通知类，要继承自Notification类；</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">MyNotification</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">Notification</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">MyNotification</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>msg<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> String msg<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div></li> <li><p>分发通知。</p> <p><code>Notification</code>有一个<code>dispatch(context)</code>方法，它是用于分发通知的，我们说过<code>context</code>实际上就是操作<code>Element</code>的一个接口，它与<code>Element</code>树上的节点是对应的，通知会从<code>context</code>对应的<code>Element</code>节点向上冒泡。</p></li></ol> <p>下面我们看一个完整的例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">NotificationRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  NotificationRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">NotificationRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">NotificationRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>NotificationRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  String _msg<span class=\"token operator\">=</span><span class=\"token string\">&quot;&quot;</span><span class=\"token punctuation\">;</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//监听通知  </span>\n    <span class=\"token keyword\">return</span> NotificationListener<span class=\"token operator\">&lt;</span>MyNotification<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n      onNotification<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>notification<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          _msg<span class=\"token operator\">+=</span>notification<span class=\"token punctuation\">.</span>msg<span class=\"token operator\">+</span><span class=\"token string\">&quot;  &quot;</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n       <span class=\"token keyword\">return</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n          mainAxisSize<span class=\"token punctuation\">:</span> MainAxisSize<span class=\"token punctuation\">.</span>min<span class=\"token punctuation\">,</span>\n          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n<span class=\"token comment\">//          RaisedButton(</span>\n<span class=\"token comment\">//           onPressed: () =&gt; MyNotification(&quot;Hi&quot;).dispatch(context),</span>\n<span class=\"token comment\">//           child: Text(&quot;Send Notification&quot;),</span>\n<span class=\"token comment\">//          ),  </span>\n            <span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span>\n              builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token keyword\">return</span> <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n                  <span class=\"token comment\">//按钮点击时分发通知  </span>\n                  onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">MyNotification</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hi&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">dispatch</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Send Notification&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>_msg<span class=\"token punctuation\">)</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">MyNotification</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">Notification</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">MyNotification</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>msg<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> String msg<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面代码中，我们每点一次按钮就会分发一个<code>MyNotification</code>类型的通知，我们在Widget根上监听通知，收到通知后我们将通知通过Text显示在屏幕上。</p> <blockquote><p>注意：代码中注释的部分是不能正常工作的，因为这个<code>context</code>是根Context，而NotificationListener是监听的子树，所以我们通过<code>Builder</code>来构建RaisedButton，来获得按钮位置的context。</p></blockquote> <p>运行效果如图8-6所示：</p> <p><img src=\"/assets/img/8-6.ce4b57e6.png\" alt=\"图8-6\"></p> <h3 id=\"阻止冒泡\"><a href=\"#阻止冒泡\" class=\"header-anchor\">#</a> 阻止冒泡</h3> <p>我们将上面的例子改为：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">NotificationRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>NotificationRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  String _msg<span class=\"token operator\">=</span><span class=\"token string\">&quot;&quot;</span><span class=\"token punctuation\">;</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//监听通知</span>\n    <span class=\"token keyword\">return</span> NotificationListener<span class=\"token operator\">&lt;</span>MyNotification<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n      onNotification<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>notification<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n        <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>notification<span class=\"token punctuation\">.</span>msg<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//打印通知</span>\n        <span class=\"token keyword\">return</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> NotificationListener<span class=\"token operator\">&lt;</span>MyNotification<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n        onNotification<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>notification<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            _msg<span class=\"token operator\">+=</span>notification<span class=\"token punctuation\">.</span>msg<span class=\"token operator\">+</span><span class=\"token string\">&quot;  &quot;</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token keyword\">return</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span> \n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token comment\">//省略重复代码</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>上列中两个<code>NotificationListener</code>进行了嵌套，子<code>NotificationListener</code>的<code>onNotification</code>回调返回了<code>false</code>，表示不阻止冒泡，所以父<code>NotificationListener</code>仍然会受到通知，所以控制台会打印出通知信息；如果将子<code>NotificationListener</code>的<code>onNotification</code>回调的返回值改为<code>true</code>，则父<code>NotificationListener</code>便不会再打印通知了，因为子<code>NotificationListener</code>已经终止通知冒泡了。</p> <h3 id=\"通知冒泡原理\"><a href=\"#通知冒泡原理\" class=\"header-anchor\">#</a> 通知冒泡原理</h3> <p>我们在上面介绍了通知冒泡的现象及使用，现在我们更深入一些，介绍一下Flutter框架中是如何实现通知冒泡的。为了搞清楚这个问题，就必须看一下源码，我们从通知分发的的源头出发，然后再顺藤摸瓜。由于通知是通过<code>Notification</code>的<code>dispatch(context)</code>方法发出的，那我们先看看<code>dispatch(context)</code>方法中做了什么，下面是相关源码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">dispatch</span><span class=\"token punctuation\">(</span>BuildContext target<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  target<span class=\"token operator\">?</span><span class=\"token punctuation\">.</span><span class=\"token function\">visitAncestorElements</span><span class=\"token punctuation\">(</span>visitAncestor<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p><code>dispatch(context)</code>中调用了当前context的<code>visitAncestorElements</code>方法，该方法会从当前Element开始向上遍历父级元素；<code>visitAncestorElements</code>有一个遍历回调参数，在遍历过程中对遍历到的父级元素都会执行该回调。遍历的终止条件是：已经遍历到根Element或某个遍历回调返回<code>false</code>。源码中传给<code>visitAncestorElements</code>方法的遍历回调为<code>visitAncestor</code>方法，我们看看<code>visitAncestor</code>方法的实现：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//遍历回调，会对每一个父级Element执行此回调</span>\nbool <span class=\"token function\">visitAncestor</span><span class=\"token punctuation\">(</span>Element element<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">//判断当前element对应的Widget是否是NotificationListener。</span>\n  \n  <span class=\"token comment\">//由于NotificationListener是继承自StatelessWidget，</span>\n  <span class=\"token comment\">//故先判断是否是StatelessElement</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>element <span class=\"token operator\">is</span> StatelessElement<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//是StatelessElement，则获取element对应的Widget，判断</span>\n    <span class=\"token comment\">//是否是NotificationListener 。</span>\n    <span class=\"token keyword\">final</span> StatelessWidget widget <span class=\"token operator\">=</span> element<span class=\"token punctuation\">.</span>widget<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>widget <span class=\"token operator\">is</span> NotificationListener<span class=\"token operator\">&lt;</span>Notification<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">//是NotificationListener，则调用该NotificationListener的_dispatch方法</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>widget<span class=\"token punctuation\">.</span><span class=\"token function\">_dispatch</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">,</span> element<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> \n        <span class=\"token keyword\">return</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token keyword\">return</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p><code>visitAncestor</code>会判断每一个遍历到的父级Widget是否是<code>NotificationListener</code>，如果不是，则返回<code>true</code>继续向上遍历，如果是，则调用<code>NotificationListener</code>的<code>_dispatch</code>方法，我们看看<code>_dispatch</code>方法的源码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>  bool <span class=\"token function\">_dispatch</span><span class=\"token punctuation\">(</span>Notification notification<span class=\"token punctuation\">,</span> Element element<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 如果通知监听器不为空，并且当前通知类型是该NotificationListener</span>\n    <span class=\"token comment\">// 监听的通知类型，则调用当前NotificationListener的onNotification</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>onNotification <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">&amp;&amp;</span> notification <span class=\"token operator\">is</span> T<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">final</span> bool result <span class=\"token operator\">=</span> <span class=\"token function\">onNotification</span><span class=\"token punctuation\">(</span>notification<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token comment\">// 返回值决定是否继续向上遍历</span>\n      <span class=\"token keyword\">return</span> result <span class=\"token operator\">==</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span> \n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">return</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们可以看到<code>NotificationListener</code>的<code>onNotification</code>回调最终是在<code>_dispatch</code>方法中执行的，然后会根据返回值来确定是否继续向上冒泡。上面的源码实现其实并不复杂，通过阅读这些源码，一些额外的点读者可以注意一下：</p> <ol><li><code>Context</code>上也提供了遍历Element树的方法。</li> <li>我们可以通过<code>Element.widget</code>得到<code>element</code>节点对应的widget；我们已经反复讲过Widget和Element的对应关系，读者通过这些源码来加深理解。</li></ol> <h3 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h3> <p>Flutter中通过通知冒泡实现了一套自低向上的消息传递机制，这个和Web开发中浏览器的事件冒泡原理类似，Web开发者可以类比学习。另外我们通过源码了解了Flutter 通知冒泡的流程和原理，便于读者加深理解和学习Flutter的框架设计思想，在此，再次建议读者在平时学习中能多看看源码，定会受益匪浅。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter8/eventbus.html\" class=\"prev\">\n        8.3 事件总线\n      </a></span> <span class=\"next\"><a href=\"/chapter9/intro.html\">\n        9.1 Flutter动画简介\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/104.02d5761d.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter9/animated_switcher.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>9.6 通用“动画切换”组件（AnimatedSwitcher） | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/47.692e2e27.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable open\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" aria-current=\"page\" class=\"active sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/chapter9/animated_switcher.html#_9-6-1-animatedswitcher\" class=\"sidebar-link\">9.6.1 AnimatedSwitcher</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter9/animated_switcher.html#_9-6-2-animatedswitcher高级用法\" class=\"sidebar-link\">9.6.2 AnimatedSwitcher高级用法</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter9/animated_switcher.html#总结\" class=\"sidebar-link\">总结</a></li></ul></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_9-6-通用-动画切换-组件-animatedswitcher\"><a href=\"#_9-6-通用-动画切换-组件-animatedswitcher\" class=\"header-anchor\">#</a> 9.6 通用“动画切换”组件（AnimatedSwitcher）</h1> <p>实际开发中，我们经常会遇到切换UI元素的场景，比如Tab切换、路由切换。为了增强用户体验，通常在切换时都会指定一个动画，以使切换过程显得平滑。Flutter SDK组件库中已经提供了一些常用的切换组件，如<code>PageView</code>、<code>TabView</code>等，但是，这些组件并不能覆盖全部的需求场景，为此，Flutter SDK中提供了一个<code>AnimatedSwitcher</code>组件，它定义了一种通用的UI切换抽象。</p> <h2 id=\"_9-6-1-animatedswitcher\"><a href=\"#_9-6-1-animatedswitcher\" class=\"header-anchor\">#</a> 9.6.1 AnimatedSwitcher</h2> <p><code>AnimatedSwitcher</code> 可以同时对其新、旧子元素添加显示、隐藏动画。也就是说在<code>AnimatedSwitcher</code>的子元素发生变化时，会对其旧元素和新元素，我们先看看<code>AnimatedSwitcher</code> 的定义：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">const</span> <span class=\"token function\">AnimatedSwitcher</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  Key key<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span>\n  <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>duration<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 新child显示动画时长</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>reverseDuration<span class=\"token punctuation\">,</span><span class=\"token comment\">// 旧child隐藏的动画时长</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>switchInCurve <span class=\"token operator\">=</span> Curves<span class=\"token punctuation\">.</span>linear<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 新child显示的动画曲线</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>switchOutCurve <span class=\"token operator\">=</span> Curves<span class=\"token punctuation\">.</span>linear<span class=\"token punctuation\">,</span><span class=\"token comment\">// 旧child隐藏的动画曲线</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>transitionBuilder <span class=\"token operator\">=</span> AnimatedSwitcher<span class=\"token punctuation\">.</span>defaultTransitionBuilder<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 动画构建器</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>layoutBuilder <span class=\"token operator\">=</span> AnimatedSwitcher<span class=\"token punctuation\">.</span>defaultLayoutBuilder<span class=\"token punctuation\">,</span> <span class=\"token comment\">//布局构建器</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>当<code>AnimatedSwitcher</code>的child发生变化时（类型或Key不同），旧child会执行隐藏动画，新child会执行执行显示动画。究竟执行何种动画效果则由<code>transitionBuilder</code>参数决定，该参数接受一个<code>AnimatedSwitcherTransitionBuilder</code>类型的builder，定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">typedef</span> AnimatedSwitcherTransitionBuilder <span class=\"token operator\">=</span>\n  Widget <span class=\"token keyword\">Function</span><span class=\"token punctuation\">(</span>Widget child<span class=\"token punctuation\">,</span> Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> animation<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>该<code>builder</code>在<code>AnimatedSwitcher</code>的child切换时会分别对新、旧child绑定动画：</p> <ol><li>对旧child，绑定的动画会反向执行（reverse）</li> <li>对新child，绑定的动画会正向指向（forward）</li></ol> <p>这样一下，便实现了对新、旧child的动画绑定。<code>AnimatedSwitcher</code>的默认值是<code>AnimatedSwitcher.defaultTransitionBuilder</code> ：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Widget <span class=\"token function\">defaultTransitionBuilder</span><span class=\"token punctuation\">(</span>Widget child<span class=\"token punctuation\">,</span> Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> animation<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">FadeTransition</span><span class=\"token punctuation\">(</span>\n    opacity<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> child<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以看到，返回了<code>FadeTransition</code>对象，也就是说默认情况，<code>AnimatedSwitcher</code>会对新旧child执行“渐隐”和“渐显”动画。</p> <h3 id=\"例子\"><a href=\"#例子\" class=\"header-anchor\">#</a> 例子</h3> <p>下面我们看一个列子：实现一个计数器，然后再每一次自增的过程中，旧数字执行缩小动画隐藏，新数字执行放大动画显示，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">AnimatedSwitcherCounterRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n   <span class=\"token keyword\">const</span> <span class=\"token function\">AnimatedSwitcherCounterRoute</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>Key key<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n   <span class=\"token metadata symbol\">@override</span>\n   _AnimatedSwitcherCounterRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">_AnimatedSwitcherCounterRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n <span class=\"token punctuation\">}</span>\n\n <span class=\"token keyword\">class</span> <span class=\"token class-name\">_AnimatedSwitcherCounterRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>AnimatedSwitcherCounterRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n   int _count <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span>\n\n   <span class=\"token metadata symbol\">@override</span>\n   Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n     <span class=\"token keyword\">return</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n       child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n         mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n         children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n           <span class=\"token function\">AnimatedSwitcher</span><span class=\"token punctuation\">(</span>\n             duration<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>milliseconds<span class=\"token punctuation\">:</span> <span class=\"token number\">500</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n             transitionBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>Widget child<span class=\"token punctuation\">,</span> Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> animation<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n               <span class=\"token comment\">//执行缩放动画</span>\n               <span class=\"token keyword\">return</span> <span class=\"token function\">ScaleTransition</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">:</span> child<span class=\"token punctuation\">,</span> scale<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n             <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n             child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>\n               <span class=\"token string\">'$_count'</span><span class=\"token punctuation\">,</span>\n               <span class=\"token comment\">//显示指定key，不同的key会被认为是不同的Text，这样才能执行动画</span>\n               key<span class=\"token punctuation\">:</span> ValueKey<span class=\"token operator\">&lt;</span>int<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>_count<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n               style<span class=\"token punctuation\">:</span> Theme<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>textTheme<span class=\"token punctuation\">.</span>headline4<span class=\"token punctuation\">,</span>\n             <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n           <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n           <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n             child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'+1'</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n             onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n               <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                 _count <span class=\"token operator\">+=</span> <span class=\"token number\">1</span><span class=\"token punctuation\">;</span>\n               <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n             <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n           <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n         <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n       <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n     <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n   <span class=\"token punctuation\">}</span>\n <span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行示例代码，当点击“+1”按钮时，原先的数字会逐渐缩小直至隐藏，而新数字会逐渐放大，我截取了动画执行过程的一帧，如图9-5所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图9-5\"></p> <p>上图是第一次点击“+1”按钮后切换动画的一帧，此时“0”正在逐渐缩小，而“1”正在“0”的中间，正在逐渐放大。</p> <blockquote><p>注意：AnimatedSwitcher的新旧child，如果类型相同，则Key必须不相等。</p></blockquote> <h3 id=\"animatedswitcher实现原理\"><a href=\"#animatedswitcher实现原理\" class=\"header-anchor\">#</a> AnimatedSwitcher实现原理</h3> <p>实际上，<code>AnimatedSwitcher</code>的实现原理是比较简单的，我们根据<code>AnimatedSwitcher</code>的使用方式也可以猜个大概。要想实现新旧child切换动画，只需要明确两个问题：动画执行的时机是和如何对新旧child执行动画。从<code>AnimatedSwitcher</code>的使用方式我们可以看到，当child发生变化时（子widget的key和类型<strong>不</strong>同时相等则认为发生变化），则重新会重新执行<code>build</code>，然后动画开始执行。我们可以通过继承StatefulWidget来实现<code>AnimatedSwitcher</code>，具体做法是在<code>didUpdateWidget</code> 回调中判断其新旧child是否发生变化，如果发生变化，则对旧child执行反向退场（reverse）动画，对新child执行正向（forward）入场动画即可。下面是<code>AnimatedSwitcher</code>实现的部分核心伪代码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Widget _widget<span class=\"token punctuation\">;</span> <span class=\"token comment\">//</span>\n<span class=\"token keyword\">void</span> <span class=\"token function\">didUpdateWidget</span><span class=\"token punctuation\">(</span>AnimatedSwitcher oldWidget<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">didUpdateWidget</span><span class=\"token punctuation\">(</span>oldWidget<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">// 检查新旧child是否发生变化(key和类型同时相等则返回true，认为没变化)</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>Widget<span class=\"token punctuation\">.</span><span class=\"token function\">canUpdate</span><span class=\"token punctuation\">(</span>widget<span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span> oldWidget<span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// child没变化，...</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//child发生了变化，构建一个Stack来分别给新旧child执行动画</span>\n   _widget<span class=\"token operator\">=</span> <span class=\"token function\">Stack</span><span class=\"token punctuation\">(</span>\n      alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n      children<span class=\"token punctuation\">:</span><span class=\"token punctuation\">[</span>\n        <span class=\"token comment\">//旧child应用FadeTransition</span>\n        <span class=\"token function\">FadeTransition</span><span class=\"token punctuation\">(</span>\n         opacity<span class=\"token punctuation\">:</span> _controllerOldAnimation<span class=\"token punctuation\">,</span>\n         child <span class=\"token punctuation\">:</span> oldWidget<span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token comment\">//新child应用FadeTransition</span>\n        <span class=\"token function\">FadeTransition</span><span class=\"token punctuation\">(</span>\n         opacity<span class=\"token punctuation\">:</span> _controllerNewAnimation<span class=\"token punctuation\">,</span>\n         child <span class=\"token punctuation\">:</span> widget<span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">]</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">// 给旧child执行反向退场动画</span>\n    _controllerOldAnimation<span class=\"token punctuation\">.</span><span class=\"token function\">reverse</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//给新child执行正向入场动画</span>\n    _controllerNewAnimation<span class=\"token punctuation\">.</span><span class=\"token function\">forward</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">//build方法</span>\nWidget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> _widget<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面伪代码展示了<code>AnimatedSwitcher</code>实现的核心逻辑，当然<code>AnimatedSwitcher</code>真正的实现比这个复杂，它可以自定义进退场过渡动画以及执行动画时的布局等。在此，我们删繁就简，通过伪代码形式让读者能够清楚看到主要的实现思路，具体的实现读者可以参考<code>AnimatedSwitcher</code>源码。</p> <p>另外，Flutter SDK中还提供了一个<code>AnimatedCrossFade</code>组件，它也可以切换两个子元素，切换过程执行渐隐渐显的动画，和<code>AnimatedSwitcher</code>不同的是<code>AnimatedCrossFade</code>是针对两个子元素，而<code>AnimatedSwitcher</code>是在一个子元素的新旧值之间切换。<code>AnimatedCrossFade</code>实现原理比较简单，也有和<code>AnimatedSwitcher</code>类似的地方，因此不再赘述，读者有兴趣可以查看其源码。</p> <h2 id=\"_9-6-2-animatedswitcher高级用法\"><a href=\"#_9-6-2-animatedswitcher高级用法\" class=\"header-anchor\">#</a> 9.6.2 AnimatedSwitcher高级用法</h2> <p>假设现在我们想实现一个类似路由平移切换的动画：旧页面屏幕中向左侧平移退出，新页面重屏幕右侧平移进入。如果要用AnimatedSwitcher的话，我们很快就会发现一个问题：做不到！我们可能会写出下面的代码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">AnimatedSwitcher</span><span class=\"token punctuation\">(</span>\n  duration<span class=\"token punctuation\">:</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>milliseconds<span class=\"token punctuation\">:</span> <span class=\"token number\">200</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  transitionBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>Widget child<span class=\"token punctuation\">,</span> Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> animation<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">var</span> tween<span class=\"token operator\">=</span>Tween<span class=\"token operator\">&lt;</span>Offset<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>begin<span class=\"token punctuation\">:</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">:</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n     <span class=\"token keyword\">return</span> <span class=\"token function\">SlideTransition</span><span class=\"token punctuation\">(</span>\n       child<span class=\"token punctuation\">:</span> child<span class=\"token punctuation\">,</span>\n       position<span class=\"token punctuation\">:</span> tween<span class=\"token punctuation\">.</span><span class=\"token function\">animate</span><span class=\"token punctuation\">(</span>animation<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token comment\">//省略</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>上面的代码有什么问题呢？我们前面说过在<code>AnimatedSwitcher</code>的child切换时会分别对新child执行正向动画（forward），而对旧child执行反向动画（reverse），所以真正的效果便是：新child确实从屏幕右侧平移进入了，但旧child却会从屏幕<strong>右侧</strong>（而不是左侧）退出。其实也很容易理解，因为在没有特殊处理的情况下，同一个动画的正向和逆向正好是相反（对称）的。</p> <p>那么问题来了，难道就不能使用<code>AnimatedSwitcher</code>了？答案当然是否定的！仔细想想这个问题，究其原因，就是因为同一个<code>Animation</code>正向（forward）和反向（reverse）是对称的。所以如果我们可以打破这种对称性，那么便可以实现这个功能了，下面我们来封装一个<code>MySlideTransition</code>，它与<code>SlideTransition</code>唯一的不同就是对动画的反向执行进行了定制（从左边滑出隐藏），代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">MySlideTransition</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">AnimatedWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">MySlideTransition</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n    <span class=\"token metadata symbol\">@required</span> Animation<span class=\"token operator\">&lt;</span>Offset<span class=\"token operator\">&gt;</span> position<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>transformHitTests <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">:</span> <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span>position <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">,</span> listenable<span class=\"token punctuation\">:</span> position<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">;</span>\n\n  Animation<span class=\"token operator\">&lt;</span>Offset<span class=\"token operator\">&gt;</span> <span class=\"token keyword\">get</span> position <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> listenable<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> bool transformHitTests<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> Widget child<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    Offset offset<span class=\"token operator\">=</span>position<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//动画反向执行时，调整x偏移，实现“从左边滑出隐藏”</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>position<span class=\"token punctuation\">.</span>status <span class=\"token operator\">==</span> AnimationStatus<span class=\"token punctuation\">.</span>reverse<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n         offset <span class=\"token operator\">=</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token operator\">-</span>offset<span class=\"token punctuation\">.</span>dx<span class=\"token punctuation\">,</span> offset<span class=\"token punctuation\">.</span>dy<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">FractionalTranslation</span><span class=\"token punctuation\">(</span>\n      translation<span class=\"token punctuation\">:</span> offset<span class=\"token punctuation\">,</span>\n      transformHitTests<span class=\"token punctuation\">:</span> transformHitTests<span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> child<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>调用时，将<code>SlideTransition</code>替换成<code>MySlideTransition</code>即可：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">AnimatedSwitcher</span><span class=\"token punctuation\">(</span>\n  duration<span class=\"token punctuation\">:</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>milliseconds<span class=\"token punctuation\">:</span> <span class=\"token number\">200</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  transitionBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>Widget child<span class=\"token punctuation\">,</span> Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> animation<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">var</span> tween<span class=\"token operator\">=</span>Tween<span class=\"token operator\">&lt;</span>Offset<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>begin<span class=\"token punctuation\">:</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">:</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n     <span class=\"token keyword\">return</span> <span class=\"token function\">MySlideTransition</span><span class=\"token punctuation\">(</span>\n              child<span class=\"token punctuation\">:</span> child<span class=\"token punctuation\">,</span>\n              position<span class=\"token punctuation\">:</span> tween<span class=\"token punctuation\">.</span><span class=\"token function\">animate</span><span class=\"token punctuation\">(</span>animation<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    \t      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token comment\">//省略</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行后，我截取动画执行过程中的一帧，如图9-6所示：</p> <p><img src=\"/assets/img/9-6.7d1d25c1.png\" alt=\"图9-6\"></p> <p>上图中“0”从左侧滑出，而“1”从右侧滑入。可以看到，我们通过这种巧妙的方式实现了类似路由进场切换的动画，实际上Flutter路由切换也正是通过<code>AnimatedSwitcher</code>来实现的。</p> <h3 id=\"slidetransitionx\"><a href=\"#slidetransitionx\" class=\"header-anchor\">#</a> SlideTransitionX</h3> <p>上面的示例我们实现了“左出右入”的动画，那如果要实现“右入左出”、“上入下出”或者 “下入上出”怎么办？当然，我们可以分别修改上面的代码，但是这样每种动画都得单独定义一个“Transition”，这很麻烦。本节将封装一个通用的<code>SlideTransitionX</code> 来实现这种“出入滑动动画”，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">SlideTransitionX</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">AnimatedWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">SlideTransitionX</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n    <span class=\"token metadata symbol\">@required</span> Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> position<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>transformHitTests <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>direction <span class=\"token operator\">=</span> AxisDirection<span class=\"token punctuation\">.</span>down<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">:</span> <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span>position <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">,</span> listenable<span class=\"token punctuation\">:</span> position<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 偏移在内部处理      </span>\n    <span class=\"token keyword\">switch</span> <span class=\"token punctuation\">(</span>direction<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">case</span> AxisDirection<span class=\"token punctuation\">.</span>up<span class=\"token punctuation\">:</span>\n        _tween <span class=\"token operator\">=</span> <span class=\"token function\">Tween</span><span class=\"token punctuation\">(</span>begin<span class=\"token punctuation\">:</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">:</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">case</span> AxisDirection<span class=\"token punctuation\">.</span>right<span class=\"token punctuation\">:</span>\n        _tween <span class=\"token operator\">=</span> <span class=\"token function\">Tween</span><span class=\"token punctuation\">(</span>begin<span class=\"token punctuation\">:</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token operator\">-</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">:</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">case</span> AxisDirection<span class=\"token punctuation\">.</span>down<span class=\"token punctuation\">:</span>\n        _tween <span class=\"token operator\">=</span> <span class=\"token function\">Tween</span><span class=\"token punctuation\">(</span>begin<span class=\"token punctuation\">:</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token operator\">-</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">:</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">case</span> AxisDirection<span class=\"token punctuation\">.</span>left<span class=\"token punctuation\">:</span>\n        _tween <span class=\"token operator\">=</span> <span class=\"token function\">Tween</span><span class=\"token punctuation\">(</span>begin<span class=\"token punctuation\">:</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">:</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n\n  Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> <span class=\"token keyword\">get</span> position <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> listenable<span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> bool transformHitTests<span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> Widget child<span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">//退场（出）方向</span>\n  <span class=\"token keyword\">final</span> AxisDirection direction<span class=\"token punctuation\">;</span>\n\n  Tween<span class=\"token operator\">&lt;</span>Offset<span class=\"token operator\">&gt;</span> _tween<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    Offset offset <span class=\"token operator\">=</span> _tween<span class=\"token punctuation\">.</span><span class=\"token function\">evaluate</span><span class=\"token punctuation\">(</span>position<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>position<span class=\"token punctuation\">.</span>status <span class=\"token operator\">==</span> AnimationStatus<span class=\"token punctuation\">.</span>reverse<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">switch</span> <span class=\"token punctuation\">(</span>direction<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">case</span> AxisDirection<span class=\"token punctuation\">.</span>up<span class=\"token punctuation\">:</span>\n          offset <span class=\"token operator\">=</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span>offset<span class=\"token punctuation\">.</span>dx<span class=\"token punctuation\">,</span> <span class=\"token operator\">-</span>offset<span class=\"token punctuation\">.</span>dy<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">case</span> AxisDirection<span class=\"token punctuation\">.</span>right<span class=\"token punctuation\">:</span>\n          offset <span class=\"token operator\">=</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token operator\">-</span>offset<span class=\"token punctuation\">.</span>dx<span class=\"token punctuation\">,</span> offset<span class=\"token punctuation\">.</span>dy<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">case</span> AxisDirection<span class=\"token punctuation\">.</span>down<span class=\"token punctuation\">:</span>\n          offset <span class=\"token operator\">=</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span>offset<span class=\"token punctuation\">.</span>dx<span class=\"token punctuation\">,</span> <span class=\"token operator\">-</span>offset<span class=\"token punctuation\">.</span>dy<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">case</span> AxisDirection<span class=\"token punctuation\">.</span>left<span class=\"token punctuation\">:</span>\n          offset <span class=\"token operator\">=</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token operator\">-</span>offset<span class=\"token punctuation\">.</span>dx<span class=\"token punctuation\">,</span> offset<span class=\"token punctuation\">.</span>dy<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">FractionalTranslation</span><span class=\"token punctuation\">(</span>\n      translation<span class=\"token punctuation\">:</span> offset<span class=\"token punctuation\">,</span>\n      transformHitTests<span class=\"token punctuation\">:</span> transformHitTests<span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> child<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>现在如果我们想实现各种“滑动出入动画”便非常容易，只需给<code>direction</code>传递不同的方向值即可，比如要实现“上入下出”，则：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">AnimatedSwitcher</span><span class=\"token punctuation\">(</span>\n  duration<span class=\"token punctuation\">:</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>milliseconds<span class=\"token punctuation\">:</span> <span class=\"token number\">200</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  transitionBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>Widget child<span class=\"token punctuation\">,</span> Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> animation<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">var</span> tween<span class=\"token operator\">=</span>Tween<span class=\"token operator\">&lt;</span>Offset<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>begin<span class=\"token punctuation\">:</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">:</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n     <span class=\"token keyword\">return</span> <span class=\"token function\">SlideTransitionX</span><span class=\"token punctuation\">(</span>\n              child<span class=\"token punctuation\">:</span> child<span class=\"token punctuation\">,</span>\n     \t\t\t\t  direction<span class=\"token punctuation\">:</span> AxisDirection<span class=\"token punctuation\">.</span>down<span class=\"token punctuation\">,</span> <span class=\"token comment\">//上入下出</span>\n              position<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">,</span>\n    \t      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token comment\">//省略其余代码</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行后，我截取动画执行过程中的一帧，如图9-7所示：</p> <p><img src=\"/assets/img/9-7.d8052e62.png\" alt=\"图9-7\"></p> <p>上图中“1”从底部滑出，而“2”从顶部滑入。读者可以尝试给<code>SlideTransitionX</code>的<code>direction</code>取不同的值来查看运行效果。</p> <h2 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h2> <p>本节我们学习了<code>AnimatedSwitcher</code>的详细用法，同时也介绍了打破<code>AnimatedSwitcher</code>动画对称性的方法。我们可以发现：在需要切换新旧UI元素的场景，<code>AnimatedSwitcher</code>将十分实用。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter9/stagger_animation.html\" class=\"prev\">\n        9.5 交织动画\n      </a></span> <span class=\"next\"><a href=\"/chapter9/animated_widgets.html\">\n        9.7 动画过渡组件\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/47.692e2e27.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter9/animated_widgets.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>9.7 动画过渡组件 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/48.6747a87a.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable open\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" aria-current=\"page\" class=\"active sidebar-link\">9.7 动画过渡组件</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/chapter9/animated_widgets.html#_9-7-1-自定义动画过渡组件\" class=\"sidebar-link\">9.7.1 自定义动画过渡组件</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter9/animated_widgets.html#_9-7-2-flutter预置的动画过渡组件\" class=\"sidebar-link\">9.7.2 Flutter预置的动画过渡组件</a></li></ul></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_9-7-动画过渡组件\"><a href=\"#_9-7-动画过渡组件\" class=\"header-anchor\">#</a> 9.7 动画过渡组件</h1> <p>为了表述方便，本书约定，将在Widget属性发生变化时会执行过渡动画的组件统称为”动画过渡组件“，而动画过渡组件最明显的一个特征就是它会在内部自管理<code>AnimationController</code>。我们知道，为了方便使用者可以自定义动画的曲线、执行时长、方向等，在前面介绍过的动画封装方法中，通常都需要使用者自己提供一个<code>AnimationController</code>对象来自定义这些属性值。但是，如此一来，使用者就必须得手动管理<code>AnimationController</code>，这又会增加使用的复杂性。因此，如果也能将<code>AnimationController</code>进行封装，则会大大提高动画组件的易用性。</p> <h2 id=\"_9-7-1-自定义动画过渡组件\"><a href=\"#_9-7-1-自定义动画过渡组件\" class=\"header-anchor\">#</a> 9.7.1 自定义动画过渡组件</h2> <p>我们要实现一个<code>AnimatedDecoratedBox</code>，它可以在<code>decoration</code>属性发生变化时，从旧状态变成新状态的过程可以执行一个过渡动画。根据前面所学的知识，我们实现了一个<code>AnimatedDecoratedBox1</code>组件：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">AnimatedDecoratedBox1</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">AnimatedDecoratedBox1</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n    <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>decoration<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>curve <span class=\"token operator\">=</span> Curves<span class=\"token punctuation\">.</span>linear<span class=\"token punctuation\">,</span>\n    <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>duration<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>reverseDuration<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> BoxDecoration decoration<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> Widget child<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> Duration duration<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> Curve curve<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> Duration reverseDuration<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  _AnimatedDecoratedBox1State <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">_AnimatedDecoratedBox1State</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_AnimatedDecoratedBox1State</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>AnimatedDecoratedBox1<span class=\"token operator\">&gt;</span>\n    <span class=\"token keyword\">with</span> SingleTickerProviderStateMixin <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@protected</span>\n  AnimationController <span class=\"token keyword\">get</span> controller <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _controller<span class=\"token punctuation\">;</span>\n  AnimationController _controller<span class=\"token punctuation\">;</span>\n\n  Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> <span class=\"token keyword\">get</span> animation <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _animation<span class=\"token punctuation\">;</span>\n  Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> _animation<span class=\"token punctuation\">;</span>\n\n  DecorationTween _tween<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">AnimatedBuilder</span><span class=\"token punctuation\">(</span>\n      animation<span class=\"token punctuation\">:</span> _animation<span class=\"token punctuation\">,</span>\n      builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> child<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">return</span> <span class=\"token function\">DecoratedBox</span><span class=\"token punctuation\">(</span>\n          decoration<span class=\"token punctuation\">:</span> _tween<span class=\"token punctuation\">.</span><span class=\"token function\">animate</span><span class=\"token punctuation\">(</span>_animation<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span>\n          child<span class=\"token punctuation\">:</span> child<span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> widget<span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    _controller <span class=\"token operator\">=</span> <span class=\"token function\">AnimationController</span><span class=\"token punctuation\">(</span>\n      duration<span class=\"token punctuation\">:</span> widget<span class=\"token punctuation\">.</span>duration<span class=\"token punctuation\">,</span>\n      reverseDuration<span class=\"token punctuation\">:</span> widget<span class=\"token punctuation\">.</span>reverseDuration<span class=\"token punctuation\">,</span>\n      vsync<span class=\"token punctuation\">:</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    _tween <span class=\"token operator\">=</span> <span class=\"token function\">DecorationTween</span><span class=\"token punctuation\">(</span>begin<span class=\"token punctuation\">:</span> widget<span class=\"token punctuation\">.</span>decoration<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token function\">_updateCurve</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_updateCurve</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>widget<span class=\"token punctuation\">.</span>curve <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span>\n      _animation <span class=\"token operator\">=</span> <span class=\"token function\">CurvedAnimation</span><span class=\"token punctuation\">(</span>parent<span class=\"token punctuation\">:</span> _controller<span class=\"token punctuation\">,</span> curve<span class=\"token punctuation\">:</span> widget<span class=\"token punctuation\">.</span>curve<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">else</span>\n      _animation <span class=\"token operator\">=</span> _controller<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">didUpdateWidget</span><span class=\"token punctuation\">(</span>AnimatedDecoratedBox1 oldWidget<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">didUpdateWidget</span><span class=\"token punctuation\">(</span>oldWidget<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>widget<span class=\"token punctuation\">.</span>curve <span class=\"token operator\">!=</span> oldWidget<span class=\"token punctuation\">.</span>curve<span class=\"token punctuation\">)</span>\n      <span class=\"token function\">_updateCurve</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    _controller<span class=\"token punctuation\">.</span>duration <span class=\"token operator\">=</span> widget<span class=\"token punctuation\">.</span>duration<span class=\"token punctuation\">;</span>\n    _controller<span class=\"token punctuation\">.</span>reverseDuration <span class=\"token operator\">=</span> widget<span class=\"token punctuation\">.</span>reverseDuration<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>widget<span class=\"token punctuation\">.</span>decoration<span class=\"token operator\">!=</span> <span class=\"token punctuation\">(</span>_tween<span class=\"token punctuation\">.</span>end <span class=\"token operator\">?</span><span class=\"token operator\">?</span> _tween<span class=\"token punctuation\">.</span>begin<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n      _tween\n        <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>begin <span class=\"token operator\">=</span> _tween<span class=\"token punctuation\">.</span><span class=\"token function\">evaluate</span><span class=\"token punctuation\">(</span>_animation<span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>end <span class=\"token operator\">=</span> widget<span class=\"token punctuation\">.</span>decoration<span class=\"token punctuation\">;</span>\n      _controller\n        <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>value <span class=\"token operator\">=</span> <span class=\"token number\">0.0</span>\n        <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token function\">forward</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    _controller<span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>下面我们来使用<code>AnimatedDecoratedBox1</code>来实现按钮点击后背景色从蓝色过渡到红色的效果：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Color _decorationColor <span class=\"token operator\">=</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">var</span> duration <span class=\"token operator\">=</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token comment\">//省略无关代码</span>\n<span class=\"token function\">AnimatedDecoratedBox</span><span class=\"token punctuation\">(</span>\n  duration<span class=\"token punctuation\">:</span> duration<span class=\"token punctuation\">,</span>\n  decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> _decorationColor<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n    onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        _decorationColor <span class=\"token operator\">=</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>\n      <span class=\"token string\">&quot;AnimatedDecoratedBox&quot;</span><span class=\"token punctuation\">,</span>\n      style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>点击前效果如图9-8所示，点击后截取了过渡过程的一帧如图9-9所示： <img src=\"data:image/png;base64,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Ow40Fzl2XaO5Ns9TBXb6hjrZov1lZSAU7atpnpqLrtU+fLYwLfvOsb+0JaFtAPWjJs6yWefZm5vG7MzcZZO61jVysPvhKZGgJg4aX3boZ72s436al9ZHvytToCOZWg/VHLRc0z+xj2t1ZFF9i2vLNRU/nV5oXnfxlZjRtoPs0PXqn5dCCr5Kwzez4uXsWqkuQT9rfN0HndivF3PNSf1Ww1m2tmxirvXUmus+ajl3ZRKaGWb75bqD8FFBpGwQuw66x+hQRz4lHDRHfTWzxlOfuh++7PbYxaV8RwXFj192x+6v6AlYABT9En95Bn7ZAuDLm5l7MoHoErAAiO7an67l6FI3EzABEzABEzCBqBGwAIjaitteEzABEzABE4CABYBvAxMwARMwAROIIAELgAguuk02ARMwARMwAQsA3wMmYAImYAImEEECFgARXHSbbAImYAImYAIWAL4HTMAETMAETCCCBCwAIrjoNtkETMAETMAELAB8D5iACZiACZhABAlYAERw0W2yCZiACZiACVgA+B4wARMwARMwgQgSsACI4KLbZBMwARMwAROwAPA9YAImYAImYAIRJGABEMFFt8kmYAImYAImYAHge8AETMAETMAEIkjAAiCCi26TTcAETMAETMACwPeACZiACZiACUSQgAVABBfdJpuACZiACZiABYDvARMwARMwAROIIAELgAguuk02ARMwARMwAQsA3wMmYAImYAImEEECFgARXHSbbAImYAImYAKxZDLZaAwm0BoCW/c3hnc2N7TmUl9jAiZwhghM610SaipjZ2g0D1NMBCwAimk182xLBt+ftlzMM2V3bwJtI5DA98edy20bNF+dJZAwBxNoLQE9ZOKtvdjXmYAJmIAJFDQB68aCXh5PzgRMwARMwATyQ8ACID9c3asJmIAJmIAJFDQBC4CCXh5PzgRMwARMwATyQ8ACID9c3asJmIAJmIAJFDQBC4CCXh5PzgRMwARMwATyQ8ACID9c3asJmIAJmIAJFDQBC4CCXh5PzgRMwARMwATyQ8ACID9c3asJmIAJmIAJFDQBC4CCXh5PzgRMwARMwATyQ8ACID9c3asJmIAJmIAJFDQBC4CCXh5PzgRMwARMwATyQ8ACID9c3asJmIAJmIAJFDQBC4CCXh5PzgRMwARMwATyQ8ACID9c3asJmIAJmIAJFDQBC4CCXh5PzgRMwARMwATyQ8ACID9c3asJmIAJmIAJFDSB/wN9/AZcme7YQQAAAABJRU5ErkJggg==\" alt=\"img\"><img src=\"data:image/png;base64,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+XFQoXmzf25v5Tx4PYrcn7p9/M+Hffvpj6l5+KnHouu9nQQkAN7OdYvqqNdCAER1AupMBAJIQAIggIsa5SkpBRBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSaQMD8/vxzlPtXdW0Zg/NGU3bs98JaNWsMVgWATqGkotbyCrGBPUrNbUwISAGuKNxgXX1patqWlpWBMRrMQgYAQSExMtMTEhIDMRtNYDwLJ69Gp+ny7CPAhk5iY9HYNWqMVAREQARGISEA1ABHx6KAIiIAIiIAIBJOABEAw11WzEgEREAEREIGIBCQAIuLRQREQAREQAREIJgEJgGCuq2YlAiIgAiIgAhEJSABExKODIiACIiACIhBMAhIAwVxXzUoEREAEREAEIhKQAIiIRwdFQAREQAREIJgEJACCua6alQiIgAiIgAhEJCABEBGPDoqACIiACIhAMAlIAARzXTUrERABERABEYhIQAIgIh4dFAEREAEREIFgEpAACOa6alYiIAIiIAIiEJGABEBEPDooAiIgAiIgAsEkIAEQzHXVrERABERABEQgIgEJgIh4dFAEREAEREAEgklAAiCY66pZiYAIiIAIiEBEAv8PGQS+vtZoAOcAAAAASUVORK5CYII=\" alt=\"img\"></p> <p>点击后，按钮背景色会从蓝色向红色过渡，图9-9是过渡过程中的一帧，有点偏紫色，整个过渡动画结束后背景会变为红色。</p> <p>上面的代码虽然实现了我们期望的功能，但是代码却比较复杂。稍加思考后，我们就可以发现，<code>AnimationController</code>的管理以及Tween更新部分的代码都是可以抽象出来的，如果我们这些通用逻辑封装成基类，那么要实现动画过渡组件只需要继承这些基类，然后定制自身不同的代码（比如动画每一帧的构建方法）即可，这样将会简化代码。</p> <p>为了方便开发者来实现动画过渡组件的封装，Flutter提供了一个<code>ImplicitlyAnimatedWidget</code>抽象类，它继承自StatefulWidget，同时提供了一个对应的<code>ImplicitlyAnimatedWidgetState</code>类，<code>AnimationController</code>的管理就在<code>ImplicitlyAnimatedWidgetState</code>类中。开发者如果要封装动画，只需要分别继承<code>ImplicitlyAnimatedWidget</code>和<code>ImplicitlyAnimatedWidgetState</code>类即可，下面我们演示一下具体如何实现。</p> <p>我们需要分两步实现：</p> <ol><li><p>继承<code>ImplicitlyAnimatedWidget</code>类。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">AnimatedDecoratedBox</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">ImplicitlyAnimatedWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">AnimatedDecoratedBox</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n    <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>decoration<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span>\n    Curve curve <span class=\"token operator\">=</span> Curves<span class=\"token punctuation\">.</span>linear<span class=\"token punctuation\">,</span> <span class=\"token comment\">//动画曲线</span>\n    <span class=\"token metadata symbol\">@required</span> Duration duration<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 正向动画执行时长</span>\n    Duration reverseDuration<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 反向动画执行时长</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>\n          key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">,</span>\n          curve<span class=\"token punctuation\">:</span> curve<span class=\"token punctuation\">,</span>\n          duration<span class=\"token punctuation\">:</span> duration<span class=\"token punctuation\">,</span>\n          reverseDuration<span class=\"token punctuation\">:</span> reverseDuration<span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> BoxDecoration decoration<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> Widget child<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  _AnimatedDecoratedBoxState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">_AnimatedDecoratedBoxState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>其中<code>curve</code>、<code>duration</code>、<code>reverseDuration</code>三个属性在<code>ImplicitlyAnimatedWidget</code>中已定义。 可以看到<code>AnimatedDecoratedBox</code>类和普通继承自<code>StatefulWidget</code>的类没有什么不同。</p></li> <li><p>State类继承自<code>AnimatedWidgetBaseState</code>（该类继承自<code>ImplicitlyAnimatedWidgetState</code>类）。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">_AnimatedDecoratedBoxState</span>\n    <span class=\"token keyword\">extends</span> <span class=\"token class-name\">AnimatedWidgetBaseState</span><span class=\"token operator\">&lt;</span>AnimatedDecoratedBox<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  DecorationTween _decoration<span class=\"token punctuation\">;</span> <span class=\"token comment\">//定义一个Tween</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">DecoratedBox</span><span class=\"token punctuation\">(</span>\n      decoration<span class=\"token punctuation\">:</span> _decoration<span class=\"token punctuation\">.</span><span class=\"token function\">evaluate</span><span class=\"token punctuation\">(</span>animation<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> widget<span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">forEachTween</span><span class=\"token punctuation\">(</span>visitor<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 在需要更新Tween时，基类会调用此方法</span>\n    _decoration <span class=\"token operator\">=</span> <span class=\"token function\">visitor</span><span class=\"token punctuation\">(</span>_decoration<span class=\"token punctuation\">,</span> widget<span class=\"token punctuation\">.</span>decoration<span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">(</span>value<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">DecorationTween</span><span class=\"token punctuation\">(</span>begin<span class=\"token punctuation\">:</span> value<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以看到我们实现了<code>build</code>和<code>forEachTween</code>两个方法。在动画执行过程中，每一帧都会调用<code>build</code>方法（调用逻辑在<code>ImplicitlyAnimatedWidgetState</code>中），所以在<code>build</code>方法中我们需要构建每一帧的<code>DecoratedBox</code>状态，因此得算出每一帧的<code>decoration</code> 状态，这个我们可以通过<code>_decoration.evaluate(animation)</code> 来算出，其中<code>animation</code>是<code>ImplicitlyAnimatedWidgetState</code>基类中定义的对象，<code>_decoration</code>是我们自定义的一个<code>DecorationTween</code>类型的对象，那么现在的问题就是它是在什么时候被赋值的呢？要回答这个问题，我们就得搞清楚什么时候需要对<code>_decoration</code>赋值。我们知道<code>_decoration</code>是一个Tween，而Tween的主要职责就是定义动画的起始状态（begin）和终止状态(end)。对于<code>AnimatedDecoratedBox</code>来说，<code>decoration</code>的终止状态就是用户传给它的值，而起始状态是不确定的，有以下两种情况：</p> <ol><li><code>AnimatedDecoratedBox</code>首次build，此时直接将其<code>decoration</code>值置为起始状态，即<code>_decoration</code>值为<code>DecorationTween(begin: decoration)</code> 。</li> <li><code>AnimatedDecoratedBox</code>的<code>decoration</code>更新时，则起始状态为<code>_decoration.animate(animation)</code>，即<code>_decoration</code>值为<code>DecorationTween(begin: _decoration.animate(animation)，end:decoration)</code>。</li></ol></li></ol> <p>现在<code>forEachTween</code>的作用就很明显了，它正是用于来更新Tween的初始值的，在上述两种情况下会被调用，而开发者只需重写此方法，并在此方法中更新Tween的起始状态值即可。而一些更新的逻辑被屏蔽在了<code>visitor</code>回调，我们只需要调用它并给它传递正确的参数即可，<code>visitor</code>方法签名如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>   Tween <span class=\"token function\">visitor</span><span class=\"token punctuation\">(</span>\n     Tween<span class=\"token operator\">&lt;</span><span class=\"token keyword\">dynamic</span><span class=\"token operator\">&gt;</span> tween<span class=\"token punctuation\">,</span> <span class=\"token comment\">//当前的tween，第一次调用为null</span>\n     <span class=\"token keyword\">dynamic</span> targetValue<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 终止状态</span>\n     TweenConstructor<span class=\"token operator\">&lt;</span><span class=\"token keyword\">dynamic</span><span class=\"token operator\">&gt;</span> constructor，<span class=\"token comment\">//Tween构造器，在上述三种情况下会被调用以更新tween</span>\n   <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>可以看到，通过继承<code>ImplicitlyAnimatedWidget</code>和<code>ImplicitlyAnimatedWidgetState</code>类可以快速的实现动画过渡组件的封装，这和我们纯手工实现相比，代码简化了很多。</p> <blockquote><p>如果读者还有疑惑，建议查看<code>ImplicitlyAnimatedWidgetState</code>的源码并结合本示例代码对比理解。</p></blockquote> <h3 id=\"动画过渡组件的反向动画\"><a href=\"#动画过渡组件的反向动画\" class=\"header-anchor\">#</a> 动画过渡组件的反向动画</h3> <p>在使用动画过渡组件，我们只需要在改变一些属性值后重新build组件即可，所以要实现状态反向过渡，只需要将前后状态值互换即可实现，这本来是不需要再浪费笔墨的。但是<code>ImplicitlyAnimatedWidget</code>构造函数中却有一个<code>reverseDuration</code>属性用于设置反向动画的执行时长，这貌似在告诉读者<code>ImplicitlyAnimatedWidget</code>本身也提供了执行反向动画的接口，于是笔者查看了<code>ImplicitlyAnimatedWidgetState</code>源码并未发现有执行反向动画的接口，唯一有用的是它暴露了控制动画的<code>controller</code>。所以如果要让<code>reverseDuration</code>生效，我们只能先获取<code>controller</code>，然后再通过<code>controller.reverse()</code>来启动反向动画，比如我们在上面示例的基础上实现一个循环的点击背景颜色变换效果，要求从蓝色变为红色时动画执行时间为400ms，从红变蓝为2s，如果要使<code>reverseDuration</code>生效，我们需要这么做：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">AnimatedDecoratedBox</span><span class=\"token punctuation\">(</span>\n  duration<span class=\"token punctuation\">:</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span> milliseconds<span class=\"token punctuation\">:</span> <span class=\"token number\">400</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> _decorationColor<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  reverseDuration<span class=\"token punctuation\">:</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n      onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>_decorationColor <span class=\"token operator\">==</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          ImplicitlyAnimatedWidgetState _state <span class=\"token operator\">=</span>\n              context<span class=\"token punctuation\">.</span>findAncestorStateOfType<span class=\"token operator\">&lt;</span>ImplicitlyAnimatedWidgetState<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n           <span class=\"token comment\">// 通过controller来启动反向动画</span>\n          _state<span class=\"token punctuation\">.</span>controller<span class=\"token punctuation\">.</span><span class=\"token function\">reverse</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">// 经验证必须调用setState来触发rebuild，否则状态同步会有问题</span>\n            <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              _decorationColor <span class=\"token operator\">=</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            _decorationColor <span class=\"token operator\">=</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>\n        <span class=\"token string\">&quot;AnimatedDecoratedBox toggle&quot;</span><span class=\"token punctuation\">,</span>\n        style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>上面的代码实际上是非常糟糕且没必要的，它需要我们了解<code>ImplicitlyAnimatedWidgetState</code>内部实现，并且要手动去启动反向动画。我们完全可以通过如下代码实现相同的效果：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">AnimatedDecoratedBox</span><span class=\"token punctuation\">(</span>\n  duration<span class=\"token punctuation\">:</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>\n      milliseconds<span class=\"token punctuation\">:</span> _decorationColor <span class=\"token operator\">==</span> Colors<span class=\"token punctuation\">.</span>red <span class=\"token operator\">?</span> <span class=\"token number\">400</span> <span class=\"token punctuation\">:</span> <span class=\"token number\">2000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> _decorationColor<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n      onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          _decorationColor <span class=\"token operator\">=</span> _decorationColor <span class=\"token operator\">==</span> Colors<span class=\"token punctuation\">.</span>blue\n              <span class=\"token operator\">?</span> Colors<span class=\"token punctuation\">.</span>red\n              <span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>\n        <span class=\"token string\">&quot;AnimatedDecoratedBox toggle&quot;</span><span class=\"token punctuation\">,</span>\n        style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>这样的代码是不是优雅的多！那么现在问题来了，为什么<code>ImplicitlyAnimatedWidgetState</code>要提供一个<code>reverseDuration</code>参数呢？笔者仔细研究了<code>ImplicitlyAnimatedWidgetState</code>的实现，发现唯一的解释就是该参数并非是给<code>ImplicitlyAnimatedWidgetState</code>用的，而是给子类用的！原因正如我们前面说的，要使<code>reverseDuration</code> 有用就必须得获取<code>controller</code> 属性来手动启动反向动画，<code>ImplicitlyAnimatedWidgetState</code>中的<code>controller</code> 属性是一个保护属性，定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code> <span class=\"token metadata symbol\">@protected</span>\n  AnimationController <span class=\"token keyword\">get</span> controller <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _controller<span class=\"token punctuation\">;</span>\n</code></pre></div><p>而保护属性原则上只应该在子类中使用，而不应该像上面示例代码一样在外部使用。综上，我们可以得出两条结论：</p> <ol><li><p>使用动画过渡组件时如果需要执行反向动画的场景，应尽量使用状态互换的方法，而不应该通过获取<code>ImplicitlyAnimatedWidgetState</code>中<code>controller</code>的方式。</p></li> <li><p>如果我们自定义的动画过渡组件用不到<code>reverseDuration</code> ，那么最好就不要暴露此参数，比如我们上面自定义的<code>AnimatedDecoratedBox</code>定义中就可以去除<code>reverseDuration</code> 可选参数，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">AnimatedDecoratedBox</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">ImplicitlyAnimatedWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">AnimatedDecoratedBox</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n    <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>decoration<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span>\n    Curve curve <span class=\"token operator\">=</span> Curves<span class=\"token punctuation\">.</span>linear<span class=\"token punctuation\">,</span>\n    <span class=\"token metadata symbol\">@required</span> Duration duration<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>\n          key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">,</span>\n          curve<span class=\"token punctuation\">:</span> curve<span class=\"token punctuation\">,</span>\n          duration<span class=\"token punctuation\">:</span> duration<span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div></li></ol> <h2 id=\"_9-7-2-flutter预置的动画过渡组件\"><a href=\"#_9-7-2-flutter预置的动画过渡组件\" class=\"header-anchor\">#</a> 9.7.2 Flutter预置的动画过渡组件</h2> <p>Flutter SDK中也预置了很多动画过渡组件，实现方式和大都和<code>AnimatedDecoratedBox</code>差不多，如表9-1所示：</p> <table><thead><tr><th>组件名</th> <th>功能</th></tr></thead> <tbody><tr><td>AnimatedPadding</td> <td>在padding发生变化时会执行过渡动画到新状态</td></tr> <tr><td>AnimatedPositioned</td> <td>配合Stack一起使用，当定位状态发生变化时会执行过渡动画到新的状态。</td></tr> <tr><td>AnimatedOpacity</td> <td>在透明度opacity发生变化时执行过渡动画到新状态</td></tr> <tr><td>AnimatedAlign</td> <td>当<code>alignment</code>发生变化时会执行过渡动画到新的状态。</td></tr> <tr><td>AnimatedContainer</td> <td>当Container属性发生变化时会执行过渡动画到新的状态。</td></tr> <tr><td>AnimatedDefaultTextStyle</td> <td>当字体样式发生变化时，子组件中继承了该样式的文本组件会动态过渡到新样式。</td></tr></tbody></table> <center>表9-1：Flutter预置的动画过渡组件</center>\n下面我们通过一个示例来感受一下这些预置的动画过渡组件效果：\n<div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">AnimatedWidgetsTest</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _AnimatedWidgetsTestState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">_AnimatedWidgetsTestState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_AnimatedWidgetsTestState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>AnimatedWidgetsTest<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  double _padding <span class=\"token operator\">=</span> <span class=\"token number\">10</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">var</span> _align <span class=\"token operator\">=</span> Alignment<span class=\"token punctuation\">.</span>topRight<span class=\"token punctuation\">;</span>\n  double _height <span class=\"token operator\">=</span> <span class=\"token number\">100</span><span class=\"token punctuation\">;</span>\n  double _left <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span>\n  Color _color <span class=\"token operator\">=</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">;</span>\n  TextStyle _style <span class=\"token operator\">=</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>black<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  Color _decorationColor <span class=\"token operator\">=</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">var</span> duration <span class=\"token operator\">=</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">5</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">SingleChildScrollView</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n        children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n          <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n            onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                _padding <span class=\"token operator\">=</span> <span class=\"token number\">20</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">AnimatedPadding</span><span class=\"token punctuation\">(</span>\n              duration<span class=\"token punctuation\">:</span> duration<span class=\"token punctuation\">,</span>\n              padding<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span>_padding<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;AnimatedPadding&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span>\n            height<span class=\"token punctuation\">:</span> <span class=\"token number\">50</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Stack</span><span class=\"token punctuation\">(</span>\n              children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                <span class=\"token function\">AnimatedPositioned</span><span class=\"token punctuation\">(</span>\n                  duration<span class=\"token punctuation\">:</span> duration<span class=\"token punctuation\">,</span>\n                  left<span class=\"token punctuation\">:</span> _left<span class=\"token punctuation\">,</span>\n                  child<span class=\"token punctuation\">:</span> <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n                    onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                      <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                        _left <span class=\"token operator\">=</span> <span class=\"token number\">100</span><span class=\"token punctuation\">;</span>\n                      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n                    child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;AnimatedPositioned&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span>\n              <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n            height<span class=\"token punctuation\">:</span> <span class=\"token number\">100</span><span class=\"token punctuation\">,</span>\n            color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">AnimatedAlign</span><span class=\"token punctuation\">(</span>\n              duration<span class=\"token punctuation\">:</span> duration<span class=\"token punctuation\">,</span>\n              alignment<span class=\"token punctuation\">:</span> _align<span class=\"token punctuation\">,</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n                onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                  <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                    _align <span class=\"token operator\">=</span> Alignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">;</span>\n                  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n                child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;AnimatedAlign&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">AnimatedContainer</span><span class=\"token punctuation\">(</span>\n            duration<span class=\"token punctuation\">:</span> duration<span class=\"token punctuation\">,</span>\n            height<span class=\"token punctuation\">:</span> _height<span class=\"token punctuation\">,</span>\n            color<span class=\"token punctuation\">:</span> _color<span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n              onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                  _height <span class=\"token operator\">=</span> <span class=\"token number\">150</span><span class=\"token punctuation\">;</span>\n                  _color <span class=\"token operator\">=</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">;</span>\n                <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>\n                <span class=\"token string\">&quot;AnimatedContainer&quot;</span><span class=\"token punctuation\">,</span>\n                style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">AnimatedDefaultTextStyle</span><span class=\"token punctuation\">(</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">GestureDetector</span><span class=\"token punctuation\">(</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;hello world&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              onTap<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                  _style <span class=\"token operator\">=</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>\n                    color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span>\n                    decorationStyle<span class=\"token punctuation\">:</span> TextDecorationStyle<span class=\"token punctuation\">.</span>solid<span class=\"token punctuation\">,</span>\n                    decorationColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            style<span class=\"token punctuation\">:</span> _style<span class=\"token punctuation\">,</span>\n            duration<span class=\"token punctuation\">:</span> duration<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">AnimatedDecoratedBox</span><span class=\"token punctuation\">(</span>\n            duration<span class=\"token punctuation\">:</span> duration<span class=\"token punctuation\">,</span>\n            decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> _decorationColor<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n              onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                  _decorationColor <span class=\"token operator\">=</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">;</span>\n                <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>\n                <span class=\"token string\">&quot;AnimatedDecoratedBox&quot;</span><span class=\"token punctuation\">,</span>\n                style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">return</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n            padding<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">symmetric</span><span class=\"token punctuation\">(</span>vertical<span class=\"token punctuation\">:</span> <span class=\"token number\">16</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> e<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toList</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行后效果如图9-10所示：</p> <p><img src=\"/assets/img/9-10.fefb9fd0.png\" alt=\"图9-10\"></p> <p>读者可以点击一下相应组件来查看一下实际的运行效果。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter9/animated_switcher.html\" class=\"prev\">\n        9.6 通用“动画切换”组件（AnimatedSwitcher）\n      </a></span> <span class=\"next\"><a href=\"/chapter10/intro.html\">\n        10.1 自定义组件方法简介\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/48.6747a87a.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter9/animation_structure.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>9.2 动画基本结构及状态监听 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/69.46ed47d5.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable open\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" aria-current=\"page\" class=\"active sidebar-link\">9.2 动画基本结构及状态监听</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/chapter9/animation_structure.html#_9-2-1-动画基本结构\" class=\"sidebar-link\">9.2.1 动画基本结构</a></li><li class=\"sidebar-sub-header\"><a href=\"/chapter9/animation_structure.html#_9-2-2-动画状态监听\" class=\"sidebar-link\">9.2.2 动画状态监听</a></li></ul></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_9-2-动画基本结构及状态监听\"><a href=\"#_9-2-动画基本结构及状态监听\" class=\"header-anchor\">#</a> 9.2 动画基本结构及状态监听</h1> <h2 id=\"_9-2-1-动画基本结构\"><a href=\"#_9-2-1-动画基本结构\" class=\"header-anchor\">#</a> 9.2.1 动画基本结构</h2> <p>在Flutter中我们可以通过多种方式来实现动画，下面通过一个图片逐渐放大示例的不同实现来演示Flutter中动画的不同实现方式的区别。</p> <h3 id=\"基础版本\"><a href=\"#基础版本\" class=\"header-anchor\">#</a> 基础版本</h3> <p>下面我们演示一下最基础的动画实现方式：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">ScaleAnimationRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _ScaleAnimationRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_ScaleAnimationRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">//需要继承TickerProvider，如果有多个AnimationController，则应该使用TickerProviderStateMixin。</span>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_ScaleAnimationRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>ScaleAnimationRoute<span class=\"token operator\">&gt;</span>  <span class=\"token keyword\">with</span> SingleTickerProviderStateMixin<span class=\"token punctuation\">{</span> \n    \n  Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> animation<span class=\"token punctuation\">;</span>\n  AnimationController controller<span class=\"token punctuation\">;</span>\n    \n  <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    controller <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AnimationController</span><span class=\"token punctuation\">(</span>\n        duration<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">3</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> vsync<span class=\"token punctuation\">:</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//图片宽高从0变到300</span>\n    animation <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Tween</span><span class=\"token punctuation\">(</span>begin<span class=\"token punctuation\">:</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">:</span> <span class=\"token number\">300.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">animate</span><span class=\"token punctuation\">(</span>controller<span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//启动动画(正向执行)</span>\n    controller<span class=\"token punctuation\">.</span><span class=\"token function\">forward</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Center</span><span class=\"token punctuation\">(</span>\n       child<span class=\"token punctuation\">:</span> Image<span class=\"token punctuation\">.</span><span class=\"token function\">asset</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;imgs/avatar.png&quot;</span><span class=\"token punctuation\">,</span>\n          width<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span>\n          height<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">.</span>value\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//路由销毁时需要释放动画资源</span>\n    controller<span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面代码中<code>addListener()</code>函数调用了<code>setState()</code>，所以每次动画生成一个新的数字时，当前帧被标记为脏(dirty)，这会导致widget的<code>build()</code>方法再次被调用，而在<code>build()</code>中，改变Image的宽高，因为它的高度和宽度现在使用的是<code>animation.value</code> ，所以就会逐渐放大。值得注意的是动画完成时要释放控制器(调用<code>dispose()</code>方法)以防止内存泄漏。</p> <p>上面的例子中并没有指定Curve，所以放大的过程是线性的（匀速），下面我们指定一个Curve，来实现一个类似于弹簧效果的动画过程，我们只需要将<code>initState</code>中的代码改为下面这样即可：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>  <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    controller <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AnimationController</span><span class=\"token punctuation\">(</span>\n        duration<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">3</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> vsync<span class=\"token punctuation\">:</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//使用弹性曲线</span>\n    animation<span class=\"token operator\">=</span><span class=\"token function\">CurvedAnimation</span><span class=\"token punctuation\">(</span>parent<span class=\"token punctuation\">:</span> controller<span class=\"token punctuation\">,</span> curve<span class=\"token punctuation\">:</span> Curves<span class=\"token punctuation\">.</span>bounceIn<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//图片宽高从0变到300</span>\n    animation <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Tween</span><span class=\"token punctuation\">(</span>begin<span class=\"token punctuation\">:</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">:</span> <span class=\"token number\">300.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">animate</span><span class=\"token punctuation\">(</span>animation<span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//启动动画</span>\n    controller<span class=\"token punctuation\">.</span><span class=\"token function\">forward</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面代码执行后截取了其中的两帧，效果如图9-1、9-2所示：</p> <p><img src=\"/assets/img/9-1.67235fc3.png\" alt=\"图9-1\"><img src=\"/assets/img/9-2.53c1a9c2.png\" alt=\"图9-2\"></p> <h3 id=\"使用animatedwidget简化\"><a href=\"#使用animatedwidget简化\" class=\"header-anchor\">#</a> 使用AnimatedWidget简化</h3> <p>细心的读者可能已经发现上面示例中通过<code>addListener()</code>和<code>setState()</code> 来更新UI这一步其实是通用的，如果每个动画中都加这么一句是比较繁琐的。<code>AnimatedWidget</code>类封装了调用<code>setState()</code>的细节，并允许我们将widget分离出来，重构后的代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">AnimatedImage</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">AnimatedWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">AnimatedImage</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>Key key<span class=\"token punctuation\">,</span> Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> animation<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">,</span> listenable<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">final</span> Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> animation <span class=\"token operator\">=</span> listenable<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Center</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> Image<span class=\"token punctuation\">.</span><span class=\"token function\">asset</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;imgs/avatar.png&quot;</span><span class=\"token punctuation\">,</span>\n          width<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span>\n          height<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">.</span>value\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">ScaleAnimationRoute1</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _ScaleAnimationRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_ScaleAnimationRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_ScaleAnimationRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>ScaleAnimationRoute1<span class=\"token operator\">&gt;</span>\n    <span class=\"token keyword\">with</span> SingleTickerProviderStateMixin <span class=\"token punctuation\">{</span>\n\n  Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> animation<span class=\"token punctuation\">;</span>\n  AnimationController controller<span class=\"token punctuation\">;</span>\n\n  <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    controller <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AnimationController</span><span class=\"token punctuation\">(</span>\n        duration<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">3</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> vsync<span class=\"token punctuation\">:</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//图片宽高从0变到300</span>\n    animation <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Tween</span><span class=\"token punctuation\">(</span>begin<span class=\"token punctuation\">:</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">:</span> <span class=\"token number\">300.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">animate</span><span class=\"token punctuation\">(</span>controller<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//启动动画</span>\n    controller<span class=\"token punctuation\">.</span><span class=\"token function\">forward</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">AnimatedImage</span><span class=\"token punctuation\">(</span>animation<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//路由销毁时需要释放动画资源</span>\n    controller<span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h3 id=\"用animatedbuilder重构\"><a href=\"#用animatedbuilder重构\" class=\"header-anchor\">#</a> 用AnimatedBuilder重构</h3> <p>用AnimatedWidget可以从动画中分离出widget，而动画的渲染过程（即设置宽高）仍然在AnimatedWidget中，假设如果我们再添加一个widget透明度变化的动画，那么我们需要再实现一个AnimatedWidget，这样不是很优雅，如果我们能把渲染过程也抽象出来，那就会好很多，而AnimatedBuilder正是将渲染逻辑分离出来, 上面的build方法中的代码可以改为：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token metadata symbol\">@override</span>\nWidget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">//return AnimatedImage(animation: animation,);</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">AnimatedBuilder</span><span class=\"token punctuation\">(</span>\n      animation<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> Image<span class=\"token punctuation\">.</span><span class=\"token function\">asset</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;images/avatar.png&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext ctx<span class=\"token punctuation\">,</span> Widget child<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Center</span><span class=\"token punctuation\">(</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n              height<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span> \n              width<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span> \n              child<span class=\"token punctuation\">:</span> child<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面的代码中有一个迷惑的问题是，<code>child</code>看起来像被指定了两次。但实际发生的事情是：将外部引用<code>child</code>传递给<code>AnimatedBuilder</code>后<code>AnimatedBuilder</code>再将其传递给匿名构造器， 然后将该对象用作其子对象。最终的结果是<code>AnimatedBuilder</code>返回的对象插入到widget树中。</p> <p>也许你会说这和我们刚开始的示例差不了多少，其实它会带来三个好处：</p> <ol><li><p>不用显式的去添加帧监听器，然后再调用<code>setState()</code> 了，这个好处和<code>AnimatedWidget</code>是一样的。</p></li> <li><p>动画构建的范围缩小了，如果没有<code>builder</code>，<code>setState()</code>将会在父组件上下文中调用，这将会导致父组件的<code>build</code>方法重新调用；而有了<code>builder</code>之后，只会导致动画widget自身的<code>build</code>重新调用，避免不必要的rebuild。</p></li> <li><p>通过<code>AnimatedBuilder</code>可以封装常见的过渡效果来复用动画。下面我们通过封装一个<code>GrowTransition</code>来说明，它可以对子widget实现放大动画：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">GrowTransition</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">GrowTransition</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>animation<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> Widget child<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> animation<span class=\"token punctuation\">;</span>\n    \n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Center</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AnimatedBuilder</span><span class=\"token punctuation\">(</span>\n          animation<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">,</span>\n          builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> Widget child<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Container</span><span class=\"token punctuation\">(</span>\n                height<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span> \n                width<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span> \n                child<span class=\"token punctuation\">:</span> child\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          child<span class=\"token punctuation\">:</span> child\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>这样，最初的示例就可以改为：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\nWidget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">GrowTransition</span><span class=\"token punctuation\">(</span>\n    child<span class=\"token punctuation\">:</span> Image<span class=\"token punctuation\">.</span><span class=\"token function\">asset</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;images/avatar.png&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> \n    animation<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p><strong>Flutter中正是通过这种方式封装了很多动画，如：FadeTransition、ScaleTransition、SizeTransition等，很多时候都可以复用这些预置的过渡类。</strong></p></li></ol> <h2 id=\"_9-2-2-动画状态监听\"><a href=\"#_9-2-2-动画状态监听\" class=\"header-anchor\">#</a> 9.2.2 动画状态监听</h2> <p>上面说过，我们可以通过<code>Animation</code>的<code>addStatusListener()</code>方法来添加动画状态改变监听器。Flutter中，有四种动画状态，在<code>AnimationStatus</code>枚举类中定义，下面我们逐个说明：</p> <table><thead><tr><th>枚举值</th> <th>含义</th></tr></thead> <tbody><tr><td><code>dismissed</code></td> <td>动画在起始点停止</td></tr> <tr><td><code>forward</code></td> <td>动画正在正向执行</td></tr> <tr><td><code>reverse</code></td> <td>动画正在反向执行</td></tr> <tr><td><code>completed</code></td> <td>动画在终点停止</td></tr></tbody></table> <h4 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h4> <p>我们将上面图片放大的示例改为先放大再缩小再放大……这样的循环动画。要实现这种效果，我们只需要监听动画状态的改变即可，即：在动画正向执行结束时反转动画，在动画反向执行结束时再正向执行动画。代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>  <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    controller <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AnimationController</span><span class=\"token punctuation\">(</span>\n        duration<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> vsync<span class=\"token punctuation\">:</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//图片宽高从0变到300</span>\n    animation <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Tween</span><span class=\"token punctuation\">(</span>begin<span class=\"token punctuation\">:</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">:</span> <span class=\"token number\">300.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">animate</span><span class=\"token punctuation\">(</span>controller<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    animation<span class=\"token punctuation\">.</span><span class=\"token function\">addStatusListener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>status<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>status <span class=\"token operator\">==</span> AnimationStatus<span class=\"token punctuation\">.</span>completed<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">//动画执行结束时反向执行动画</span>\n        controller<span class=\"token punctuation\">.</span><span class=\"token function\">reverse</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>status <span class=\"token operator\">==</span> AnimationStatus<span class=\"token punctuation\">.</span>dismissed<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">//动画恢复到初始状态时执行动画（正向）</span>\n        controller<span class=\"token punctuation\">.</span><span class=\"token function\">forward</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token comment\">//启动动画（正向）</span>\n    controller<span class=\"token punctuation\">.</span><span class=\"token function\">forward</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n</code></pre></div></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter9/intro.html\" class=\"prev\">\n        9.1 Flutter动画简介\n      </a></span> <span class=\"next\"><a href=\"/chapter9/route_transition.html\">\n        9.3 自定义路由切换动画\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/69.46ed47d5.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter9/hero.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>9.4 Hero动画 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/168.aab68e4f.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable open\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" aria-current=\"page\" class=\"active sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_9-4-hero动画\"><a href=\"#_9-4-hero动画\" class=\"header-anchor\">#</a> 9.4 Hero动画</h1> <p>Hero指的是可以在路由(页面)之间“飞行”的widget，简单来说Hero动画就是在路由切换时，有一个共享的widget可以在新旧路由间切换。由于共享的widget在新旧路由页面上的位置、外观可能有所差异，所以在路由切换时会从旧路逐渐过渡到新路由中的指定位置，这样就会产生一个Hero动画。</p> <p>你可能多次看到过 hero 动画。例如，一个路由中显示待售商品的缩略图列表，选择一个条目会将其跳转到一个新路由，新路由中包含该商品的详细信息和“购买”按钮。 在Flutter中将图片从一个路由“飞”到另一个路由称为<strong>hero动画</strong>，尽管相同的动作有时也称为 <strong>共享元素转换</strong>。下面我们通过一个示例来体验一下hero 动画。</p> <blockquote><p>为什么要将这种可飞行的共享组件称为hero（英雄），有一种说法是说美国文化中的超人是可以飞的，那是美国人心中的大英雄，还有漫威中的超级英雄基本上都是会飞的，所以Flutter开发人员就对这种“会飞的widget”就起了一个富有浪漫主义的名字hero。当然这种说法并非官方解释，但却很有意思。</p></blockquote> <h4 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h4> <p>假设有两个路由A和B，他们的内容交互如下：</p> <p>A：包含一个用户头像，圆形，点击后跳到B路由，可以查看大图。</p> <p>B：显示用户头像原图，矩形；</p> <p>在AB两个路由之间跳转的时候，用户头像会逐渐过渡到目标路由页的头像上，接下来我们先看看代码，然后再解析：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">// 路由A</span>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">HeroAnimationRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n      alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>topCenter<span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">InkWell</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Hero</span><span class=\"token punctuation\">(</span>\n          tag<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;avatar&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//唯一标记，前后两个路由页Hero的tag必须相同</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">ClipOval</span><span class=\"token punctuation\">(</span>\n            child<span class=\"token punctuation\">:</span> Image<span class=\"token punctuation\">.</span><span class=\"token function\">asset</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;images/avatar.png&quot;</span><span class=\"token punctuation\">,</span>\n              width<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        onTap<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token comment\">//打开B路由  </span>\n          Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> <span class=\"token function\">PageRouteBuilder</span><span class=\"token punctuation\">(</span>\n              pageBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> Animation animation<span class=\"token punctuation\">,</span>\n                  Animation secondaryAnimation<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">FadeTransition</span><span class=\"token punctuation\">(</span>\n                  opacity<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">,</span>\n                  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n                    appBar<span class=\"token punctuation\">:</span> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>\n                      title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;原图&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                    body<span class=\"token punctuation\">:</span> <span class=\"token function\">HeroAnimationRouteB</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>路由B:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">HeroAnimationRouteB</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Hero</span><span class=\"token punctuation\">(</span>\n          tag<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;avatar&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//唯一标记，前后两个路由页Hero的tag必须相同</span>\n          child<span class=\"token punctuation\">:</span> Image<span class=\"token punctuation\">.</span><span class=\"token function\">asset</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;images/avatar.png&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们可以看到，实现Hero动画只需要用<code>Hero</code>组件将要共享的widget包装起来，并提供一个相同的tag即可，中间的过渡帧都是Flutter Framework自动完成的。必须要注意， 前后路由页的共享<code>Hero</code>的tag必须是相同的，Flutter Framework内部正是通过tag来确定新旧路由页widget的对应关系的。</p> <p>Hero动画的原理比较简单，Flutter Framework知道新旧路由页中共享元素的位置和大小，所以根据这两个端点，在动画执行过程中求出过渡时的插值（中间态）即可，而感到幸运的是，这些事情不需要我们自己动手，Flutter已经帮我们做了！</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter9/route_transition.html\" class=\"prev\">\n        9.3 自定义路由切换动画\n      </a></span> <span class=\"next\"><a href=\"/chapter9/stagger_animation.html\">\n        9.5 交织动画\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/168.aab68e4f.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter9/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>简介 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/169.b01d210b.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h2 id=\"简介\"><a href=\"#简介\" class=\"header-anchor\">#</a> 简介</h2> <p>精心设计的动画会让用户界面感觉更直观、流畅，能改善用户体验。 Flutter可以轻松实现各种动画类型，对于许多widget，特别是<a href=\"https://flutter.io/docs/reference/widgets/material\" target=\"_blank\" rel=\"noopener noreferrer\">Material Design widgets<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>，都带有在其设计规范中定义的标准动画效果(但也可以自定义这些效果)。本章将详细介绍Flutter的动画系统，并会通过几个小实例来演示，以帮助开发者迅速理解并掌握动画的开发流程与原理。</p> <h2 id=\"本章目录\"><a href=\"#本章目录\" class=\"header-anchor\">#</a> 本章目录</h2> <ul><li><a href=\"/chapter9/intro.html\">9.1：Flutter动画简介</a></li> <li><a href=\"/chapter9/animation_structure.html\">9.2：动画结构</a></li> <li><a href=\"/chapter9/route_transition.html\">9.3：自定义路由过渡动画</a></li> <li><a href=\"/chapter9/hero.html\">9.4：Hero动画</a></li> <li><a href=\"/chapter9/stagger_animation.html\">9.5：交织动画</a></li> <li><a href=\"/chapter9/animated_switcher.html\">9.6：通用“动画切换”组件（AnimatedSwitcher）</a></li> <li><a href=\"/chapter9/animated_widgets.html\">9.7：动画过渡组件</a></li></ul></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/169.b01d210b.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter9/intro.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>9.1 Flutter动画简介 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/170.e68bf3e7.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable open\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" aria-current=\"page\" class=\"active sidebar-link\">9.1 Flutter动画简介</a><ul class=\"sidebar-sub-headers\"></ul></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_9-1-flutter动画简介\"><a href=\"#_9-1-flutter动画简介\" class=\"header-anchor\">#</a> 9.1 Flutter动画简介</h1> <p>在任何系统的UI框架中，动画实现的原理都是相同的，即：在一段时间内，快速地多次改变UI外观；由于人眼会产生视觉暂留，所以最终看到的就是一个“连续”的动画，这和电影的原理是一样的。我们将UI的一次改变称为一个动画帧，对应一次屏幕刷新，而决定动画流畅度的一个重要指标就是帧率FPS（Frame Per Second），即每秒的动画帧数。很明显，帧率越高则动画就会越流畅！一般情况下，对于人眼来说，动画帧率超过16FPS，就比较流畅了，超过32FPS就会非常的细腻平滑，而超过32FPS，人眼基本上就感受不到差别了。由于动画的每一帧都是要改变UI输出，所以在一个时间段内连续的改变UI输出是比较耗资源的，对设备的软硬件系统要求都较高，所以在UI系统中，动画的平均帧率是重要的性能指标，而在Flutter中，理想情况下是可以实现60FPS的，这和原生应用能达到的帧率是基本是持平的。</p> <h3 id=\"flutter中动画抽象\"><a href=\"#flutter中动画抽象\" class=\"header-anchor\">#</a> Flutter中动画抽象</h3> <p>为了方便开发者创建动画，不同的UI系统对动画都进行了一些抽象，比如在Android中可以通过XML来描述一个动画然后设置给View。Flutter中也对动画进行了抽象，主要涉及Animation、Curve、Controller、Tween这四个角色，它们一起配合来完成一个完整动画，下面我们一一来介绍它们。</p> <h3 id=\"animation\"><a href=\"#animation\" class=\"header-anchor\">#</a> Animation</h3> <p><code>Animation</code>是一个抽象类，它本身和UI渲染没有任何关系，而它主要的功能是保存动画的插值和状态；其中一个比较常用的<code>Animation</code>类是<code>Animation&lt;double&gt;</code>。<code>Animation</code>对象是一个在一段时间内依次生成一个区间(Tween)之间值的类。<code>Animation</code>对象在整个动画执行过程中输出的值可以是线性的、曲线的、一个步进函数或者任何其他曲线函数等等，这由<code>Curve</code>来决定。 根据<code>Animation</code>对象的控制方式，动画可以正向运行（从起始状态开始，到终止状态结束），也可以反向运行，甚至可以在中间切换方向。<code>Animation</code>还可以生成除<code>double</code>之外的其他类型值，如：<code>Animation&lt;Color&gt;</code> 或<code>Animation&lt;Size&gt;</code>。在动画的每一帧中，我们可以通过<code>Animation</code>对象的<code>value</code>属性获取动画的当前状态值。</p> <h4 id=\"动画通知\"><a href=\"#动画通知\" class=\"header-anchor\">#</a> 动画通知</h4> <p>我们可以通过<code>Animation</code>来监听动画每一帧以及执行状态的变化，<code>Animation</code>有如下两个方法：</p> <ol><li><code>addListener()</code>；它可以用于给<code>Animation</code>添加帧监听器，在每一帧都会被调用。帧监听器中最常见的行为是改变状态后调用<code>setState()</code>来触发UI重建。</li> <li><code>addStatusListener()</code>；它可以给<code>Animation</code>添加“动画状态改变”监听器；动画开始、结束、正向或反向（见<code>AnimationStatus</code>定义）时会调用状态改变的监听器。</li></ol> <p>读者在此只需要知道帧监听器和状态监听器的区别，在后面的章节中我们将会举例说明。</p> <h3 id=\"curve\"><a href=\"#curve\" class=\"header-anchor\">#</a> Curve</h3> <p>动画过程可以是匀速的、匀加速的或者先加速后减速等。Flutter中通过<code>Curve</code>（曲线）来描述动画过程，我们把匀速动画称为线性的(Curves.linear)，而非匀速动画称为非线性的。</p> <p>我们可以通过<code>CurvedAnimation</code>来指定动画的曲线，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">final</span> CurvedAnimation curve <span class=\"token operator\">=</span>\n    <span class=\"token keyword\">new</span> <span class=\"token class-name\">CurvedAnimation</span><span class=\"token punctuation\">(</span>parent<span class=\"token punctuation\">:</span> controller<span class=\"token punctuation\">,</span> curve<span class=\"token punctuation\">:</span> Curves<span class=\"token punctuation\">.</span>easeIn<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p><code>CurvedAnimation</code>和<code>AnimationController</code>（下面介绍）都是<code>Animation&lt;double&gt;</code>类型。<code>CurvedAnimation</code>可以通过包装<code>AnimationController</code>和<code>Curve</code>生成一个新的动画对象 ，我们正是通过这种方式来将动画和动画执行的曲线关联起来的。我们指定动画的曲线为<code>Curves.easeIn</code>，它表示动画开始时比较慢，结束时比较快。 <a href=\"https://docs.flutter.io/flutter/animation/Curves-class.html\" target=\"_blank\" rel=\"noopener noreferrer\">Curves<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 类是一个预置的枚举类，定义了许多常用的曲线，下面列几种常用的：</p> <table><thead><tr><th>Curves曲线</th> <th>动画过程</th></tr></thead> <tbody><tr><td>linear</td> <td>匀速的</td></tr> <tr><td>decelerate</td> <td>匀减速</td></tr> <tr><td>ease</td> <td>开始加速，后面减速</td></tr> <tr><td>easeIn</td> <td>开始慢，后面快</td></tr> <tr><td>easeOut</td> <td>开始快，后面慢</td></tr> <tr><td>easeInOut</td> <td>开始慢，然后加速，最后再减速</td></tr></tbody></table> <p>除了上面列举的， <a href=\"https://docs.flutter.io/flutter/animation/Curves-class.html\" target=\"_blank\" rel=\"noopener noreferrer\">Curves<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 类中还定义了许多其它的曲线，在此便不一一介绍，读者可以自行查看Curves类定义。</p> <p>当然我们也可以创建自己Curve，例如我们定义一个正弦曲线：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">ShakeCurve</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">Curve</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  double <span class=\"token function\">transform</span><span class=\"token punctuation\">(</span>double t<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> math<span class=\"token punctuation\">.</span><span class=\"token function\">sin</span><span class=\"token punctuation\">(</span>t <span class=\"token operator\">*</span> math<span class=\"token punctuation\">.</span>PI <span class=\"token operator\">*</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h3 id=\"animationcontroller\"><a href=\"#animationcontroller\" class=\"header-anchor\">#</a> AnimationController</h3> <p><code>AnimationController</code>用于控制动画，它包含动画的启动<code>forward()</code>、停止<code>stop()</code> 、反向播放 <code>reverse()</code>等方法。<code>AnimationController</code>会在动画的每一帧，就会生成一个新的值。默认情况下，<code>AnimationController</code>在给定的时间段内线性的生成从0.0到1.0（默认区间）的数字。 例如，下面代码创建一个<code>Animation</code>对象（但不会启动动画）：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">final</span> AnimationController controller <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AnimationController</span><span class=\"token punctuation\">(</span>\n    duration<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>milliseconds<span class=\"token punctuation\">:</span> <span class=\"token number\">2000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> vsync<span class=\"token punctuation\">:</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p><code>AnimationController</code>生成数字的区间可以通过<code>lowerBound</code>和<code>upperBound</code>来指定，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">final</span> AnimationController controller <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AnimationController</span><span class=\"token punctuation\">(</span> \n duration<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>milliseconds<span class=\"token punctuation\">:</span> <span class=\"token number\">2000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> \n lowerBound<span class=\"token punctuation\">:</span> <span class=\"token number\">10.0</span><span class=\"token punctuation\">,</span>\n upperBound<span class=\"token punctuation\">:</span> <span class=\"token number\">20.0</span><span class=\"token punctuation\">,</span>\n vsync<span class=\"token punctuation\">:</span> <span class=\"token keyword\">this</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p><code>AnimationController</code>派生自<code>Animation&lt;double&gt;</code>，因此可以在需要<code>Animation</code>对象的任何地方使用。 但是，<code>AnimationController</code>具有控制动画的其他方法，例如<code>forward()</code>方法可以启动正向动画，<code>reverse()</code>可以启动反向动画。在动画开始执行后开始生成动画帧，屏幕每刷新一次就是一个动画帧，在动画的每一帧，会随着根据动画的曲线来生成当前的动画值（<code>Animation.value</code>），然后根据当前的动画值去构建UI，当所有动画帧依次触发时，动画值会依次改变，所以构建的UI也会依次变化，所以最终我们可以看到一个完成的动画。 另外在动画的每一帧，<code>Animation</code>对象会调用其帧监听器，等动画状态发生改变时（如动画结束）会调用状态改变监听器。</p> <p><code>duration</code>表示动画执行的时长，通过它我们可以控制动画的速度。</p> <blockquote><p><strong>注意</strong>： 在某些情况下，动画值可能会超出<code>AnimationController</code>的[0.0，1.0]的范围，这取决于具体的曲线。例如，<code>fling()</code>函数可以根据我们手指滑动（甩出）的速度(velocity)、力量(force)等来模拟一个手指甩出动画，因此它的动画值可以在[0.0，1.0]范围之外 。也就是说，根据选择的曲线，<code>CurvedAnimation</code>的输出可以具有比输入更大的范围。例如，Curves.elasticIn等弹性曲线会生成大于或小于默认范围的值。</p></blockquote> <h4 id=\"ticker\"><a href=\"#ticker\" class=\"header-anchor\">#</a> Ticker</h4> <p>当创建一个<code>AnimationController</code>时，需要传递一个<code>vsync</code>参数，它接收一个<code>TickerProvider</code>类型的对象，它的主要职责是创建<code>Ticker</code>，定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">abstract</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">TickerProvider</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">//通过一个回调创建一个Ticker</span>\n  Ticker <span class=\"token function\">createTicker</span><span class=\"token punctuation\">(</span>TickerCallback onTick<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>Flutter应用在启动时都会绑定一个<code>SchedulerBinding</code>，通过<code>SchedulerBinding</code>可以给每一次屏幕刷新添加回调，而<code>Ticker</code>就是通过<code>SchedulerBinding</code>来添加屏幕刷新回调，这样一来，每次屏幕刷新都会调用<code>TickerCallback</code>。使用<code>Ticker</code>(而不是<code>Timer</code>)来驱动动画会防止屏幕外动画（动画的UI不在当前屏幕时，如锁屏时）消耗不必要的资源，因为Flutter中屏幕刷新时会通知到绑定的<code>SchedulerBinding</code>，而<code>Ticker</code>是受<code>SchedulerBinding</code>驱动的，由于锁屏后屏幕会停止刷新，所以<code>Ticker</code>就不会再触发。</p> <p>通常我们会将<code>SingleTickerProviderStateMixin</code>添加到<code>State</code>的定义中，然后将State对象作为<code>vsync</code>的值，这在后面的例子中可以见到。</p> <h3 id=\"tween\"><a href=\"#tween\" class=\"header-anchor\">#</a> Tween</h3> <p>默认情况下，<code>AnimationController</code>对象值的范围是[0.0，1.0]。如果我们需要构建UI的动画值在不同的范围或不同的数据类型，则可以使用<code>Tween</code>来添加映射以生成不同的范围或数据类型的值。例如，像下面示例，<code>Tween</code>生成[-200.0，0.0]的值：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">final</span> Tween doubleTween <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Tween</span><span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>begin<span class=\"token punctuation\">:</span> <span class=\"token operator\">-</span><span class=\"token number\">200.0</span><span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">:</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p><code>Tween</code>构造函数需要<code>begin</code>和<code>end</code>两个参数。<code>Tween</code>的唯一职责就是定义从输入范围到输出范围的映射。输入范围通常为[0.0，1.0]，但这不是必须的，我们可以自定义需要的范围。</p> <p><code>Tween</code>继承自<code>Animatable&lt;T&gt;</code>，而不是继承自<code>Animation&lt;T&gt;</code>，<code>Animatable</code>中主要定义动画值的映射规则。</p> <p>下面我们看一个ColorTween将动画输入范围映射为两种颜色值之间过渡输出的例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">final</span> Tween colorTween <span class=\"token operator\">=</span>\n    <span class=\"token keyword\">new</span> <span class=\"token class-name\">ColorTween</span><span class=\"token punctuation\">(</span>begin<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>transparent<span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>black54<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p><code>Tween</code>对象不存储任何状态，相反，它提供了<code>evaluate(Animation&lt;double&gt; animation)</code>方法，它可以获取动画当前映射值。 <code>Animation</code>对象的当前值可以通过<code>value()</code>方法取到。<code>evaluate</code>函数还执行一些其它处理，例如分别确保在动画值为0.0和1.0时返回开始和结束状态。</p> <h4 id=\"tween-animate\"><a href=\"#tween-animate\" class=\"header-anchor\">#</a> Tween.animate</h4> <p>要使用Tween对象，需要调用其<code>animate()</code>方法，然后传入一个控制器对象。例如，以下代码在500毫秒内生成从0到255的整数值。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">final</span> AnimationController controller <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AnimationController</span><span class=\"token punctuation\">(</span>\n    duration<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>milliseconds<span class=\"token punctuation\">:</span> <span class=\"token number\">500</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> vsync<span class=\"token punctuation\">:</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\nAnimation<span class=\"token operator\">&lt;</span>int<span class=\"token operator\">&gt;</span> alpha <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">IntTween</span><span class=\"token punctuation\">(</span>begin<span class=\"token punctuation\">:</span> <span class=\"token number\">0</span><span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">:</span> <span class=\"token number\">255</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">animate</span><span class=\"token punctuation\">(</span>controller<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>注意<code>animate()</code>返回的是一个<code>Animation</code>，而不是一个<code>Animatable</code>。</p> <p>以下示例构建了一个控制器、一条曲线和一个Tween：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">final</span> AnimationController controller <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AnimationController</span><span class=\"token punctuation\">(</span>\n    duration<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>milliseconds<span class=\"token punctuation\">:</span> <span class=\"token number\">500</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> vsync<span class=\"token punctuation\">:</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">final</span> Animation curve <span class=\"token operator\">=</span>\n    <span class=\"token keyword\">new</span> <span class=\"token class-name\">CurvedAnimation</span><span class=\"token punctuation\">(</span>parent<span class=\"token punctuation\">:</span> controller<span class=\"token punctuation\">,</span> curve<span class=\"token punctuation\">:</span> Curves<span class=\"token punctuation\">.</span>easeOut<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\nAnimation<span class=\"token operator\">&lt;</span>int<span class=\"token operator\">&gt;</span> alpha <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">IntTween</span><span class=\"token punctuation\">(</span>begin<span class=\"token punctuation\">:</span> <span class=\"token number\">0</span><span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">:</span> <span class=\"token number\">255</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">animate</span><span class=\"token punctuation\">(</span>curve<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter8/notification.html\" class=\"prev\">\n        8.4 Notification\n      </a></span> <span class=\"next\"><a href=\"/chapter9/animation_structure.html\">\n        9.2 动画基本结构及状态监听\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/170.e68bf3e7.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter9/route_transition.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>9.3 自定义路由切换动画 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/171.4f0e7d6b.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable open\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" aria-current=\"page\" class=\"active sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_9-3-自定义路由切换动画\"><a href=\"#_9-3-自定义路由切换动画\" class=\"header-anchor\">#</a> 9.3 自定义路由切换动画</h1> <p>我们在第二章“路由管理”一节中讲过：Material组件库中提供了一个<code>MaterialPageRoute</code>组件，它可以使用和平台风格一致的路由切换动画，如在iOS上会左右滑动切换，而在Android上会上下滑动切换。现在，我们如果在Android上也想使用左右切换风格，该怎么做？一个简单的作法是可以直接使用<code>CupertinoPageRoute</code>，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code> Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> <span class=\"token function\">CupertinoPageRoute</span><span class=\"token punctuation\">(</span>  \n   builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span><span class=\"token function\">PageB</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n <span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p><code>CupertinoPageRoute</code>是Cupertino组件库提供的iOS风格的路由切换组件，它实现的就是左右滑动切换。那么我们如何来自定义路由切换动画呢？答案就是<code>PageRouteBuilder</code>。下面我们来看看如何使用<code>PageRouteBuilder</code>来自定义路由切换动画。例如我们想以渐隐渐入动画来实现路由过渡，实现代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span>\n  context<span class=\"token punctuation\">,</span>\n  <span class=\"token function\">PageRouteBuilder</span><span class=\"token punctuation\">(</span>\n    transitionDuration<span class=\"token punctuation\">:</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>milliseconds<span class=\"token punctuation\">:</span> <span class=\"token number\">500</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//动画时间为500毫秒</span>\n    pageBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> Animation animation<span class=\"token punctuation\">,</span>\n        Animation secondaryAnimation<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">FadeTransition</span><span class=\"token punctuation\">(</span>\n        <span class=\"token comment\">//使用渐隐渐入过渡,</span>\n        opacity<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">PageB</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//路由B</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>我们可以看到<code>pageBuilder</code> 有一个<code>animation</code>参数，这是Flutter路由管理器提供的，在路由切换时<code>pageBuilder</code>在每个动画帧都会被回调，因此我们可以通过<code>animation</code>对象来自定义过渡动画。</p> <p>无论是<code>MaterialPageRoute</code>、<code>CupertinoPageRoute</code>，还是<code>PageRouteBuilder</code>，它们都继承自PageRoute类，而<code>PageRouteBuilder</code>其实只是<code>PageRoute</code>的一个包装，我们可以直接继承<code>PageRoute</code>类来实现自定义路由，上面的例子可以通过如下方式实现：</p> <ol><li><p>定义一个路由类<code>FadeRoute</code></p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">FadeRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">PageRoute</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">FadeRoute</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>builder<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>transitionDuration <span class=\"token operator\">=</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>milliseconds<span class=\"token punctuation\">:</span> <span class=\"token number\">300</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>opaque <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>barrierDismissible <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>barrierColor<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>barrierLabel<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>maintainState <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> WidgetBuilder builder<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">final</span> Duration transitionDuration<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">final</span> bool opaque<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">final</span> bool barrierDismissible<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">final</span> Color barrierColor<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">final</span> String barrierLabel<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">final</span> bool maintainState<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">buildPage</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> animation<span class=\"token punctuation\">,</span>\n      Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> secondaryAnimation<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">buildTransitions</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> animation<span class=\"token punctuation\">,</span>\n      Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> secondaryAnimation<span class=\"token punctuation\">,</span> Widget child<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n     <span class=\"token keyword\">return</span> <span class=\"token function\">FadeTransition</span><span class=\"token punctuation\">(</span> \n       opacity<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">,</span>\n       child<span class=\"token punctuation\">:</span> <span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n     <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div></li> <li><p>使用<code>FadeRoute</code></p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> <span class=\"token function\">FadeRoute</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">PageB</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div></li></ol> <p>虽然上面的两种方法都可以实现自定义切换动画，但实际使用时应优先考虑使用PageRouteBuilder，这样无需定义一个新的路由类，使用起来会比较方便。但是有些时候<code>PageRouteBuilder</code>是不能满足需求的，例如在应用过渡动画时我们需要读取当前路由的一些属性，这时就只能通过继承<code>PageRoute</code>的方式了，举个例子，假如我们只想在打开新路由时应用动画，而在返回时不使用动画，那么我们在构建过渡动画时就必须判断当前路由<code>isActive</code>属性是否为<code>true</code>，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token metadata symbol\">@override</span>\nWidget <span class=\"token function\">buildTransitions</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> animation<span class=\"token punctuation\">,</span>\n    Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> secondaryAnimation<span class=\"token punctuation\">,</span> Widget child<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token comment\">//当前路由被激活，是打开新路由</span>\n <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>isActive<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n   <span class=\"token keyword\">return</span> <span class=\"token function\">FadeTransition</span><span class=\"token punctuation\">(</span>\n     opacity<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">,</span>\n     child<span class=\"token punctuation\">:</span> <span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n   <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n <span class=\"token punctuation\">}</span><span class=\"token keyword\">else</span><span class=\"token punctuation\">{</span>\n   <span class=\"token comment\">//是返回，则不应用过渡动画</span>\n   <span class=\"token keyword\">return</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>padding<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span>zero<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>关于路由参数的详细信息读者可以自行查阅API文档，比较简单，不再赘述。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter9/animation_structure.html\" class=\"prev\">\n        9.2 动画基本结构及状态监听\n      </a></span> <span class=\"next\"><a href=\"/chapter9/hero.html\">\n        9.4 Hero动画\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/171.4f0e7d6b.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chapter9/stagger_animation.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>9.5 交织动画 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/70.a85fca0c.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable open\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" aria-current=\"page\" class=\"active sidebar-link\">9.5 交织动画</a><ul class=\"sidebar-sub-headers\"></ul></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_9-5-交织动画\"><a href=\"#_9-5-交织动画\" class=\"header-anchor\">#</a> 9.5 交织动画</h1> <p>有些时候我们可能会需要一些复杂的动画，这些动画可能由一个动画序列或重叠的动画组成，比如：有一个柱状图，需要在高度增长的同时改变颜色，等到增长到最大高度后，我们需要在X轴上平移一段距离。可以发现上述场景在不同阶段包含了多种动画，要实现这种效果，使用交织动画（Stagger Animation）会非常简单。交织动画需要注意以下几点：</p> <ol><li>要创建交织动画，需要使用多个动画对象（<code>Animation</code>）。</li> <li>一个<code>AnimationController</code>控制所有的动画对象。</li> <li>给每一个动画对象指定时间间隔（Interval）</li></ol> <p>所有动画都由同一个<a href=\"https://docs.flutter.io/flutter/animation/AnimationController-class.html\" target=\"_blank\" rel=\"noopener noreferrer\">AnimationController<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>驱动，无论动画需要持续多长时间，控制器的值必须在0.0到1.0之间，而每个动画的间隔（Interval）也必须介于0.0和1.0之间。对于在间隔中设置动画的每个属性，需要分别创建一个<a href=\"https://docs.flutter.io/flutter/animation/Tween-class.html\" target=\"_blank\" rel=\"noopener noreferrer\">Tween<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 用于指定该属性的开始值和结束值。也就是说0.0到1.0代表整个动画过程，我们可以给不同动画指定不同的起始点和终止点来决定它们的开始时间和终止时间。</p> <h3 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h3> <p>下面我们看一个例子，实现一个柱状图增长的动画：</p> <ol><li>开始时高度从0增长到300像素，同时颜色由绿色渐变为红色；这个过程占据整个动画时间的60%。</li> <li>高度增长到300后，开始沿X轴向右平移100像素；这个过程占用整个动画时间的40%。</li></ol> <p>我们将执行动画的Widget分离出来：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">StaggerAnimation</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">StaggerAnimation</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> Key key<span class=\"token punctuation\">,</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>controller <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//高度动画</span>\n    height <span class=\"token operator\">=</span> Tween<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n      begin<span class=\"token punctuation\">:</span><span class=\"token number\">.0</span> <span class=\"token punctuation\">,</span>\n      end<span class=\"token punctuation\">:</span> <span class=\"token number\">300.0</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">animate</span><span class=\"token punctuation\">(</span>\n      <span class=\"token function\">CurvedAnimation</span><span class=\"token punctuation\">(</span>\n        parent<span class=\"token punctuation\">:</span> controller<span class=\"token punctuation\">,</span>\n        curve<span class=\"token punctuation\">:</span> <span class=\"token function\">Interval</span><span class=\"token punctuation\">(</span>\n          <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0.6</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//间隔，前60%的动画时间</span>\n          curve<span class=\"token punctuation\">:</span> Curves<span class=\"token punctuation\">.</span>ease<span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    color <span class=\"token operator\">=</span> <span class=\"token function\">ColorTween</span><span class=\"token punctuation\">(</span>\n      begin<span class=\"token punctuation\">:</span>Colors<span class=\"token punctuation\">.</span>green <span class=\"token punctuation\">,</span>\n      end<span class=\"token punctuation\">:</span>Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">animate</span><span class=\"token punctuation\">(</span>\n      <span class=\"token function\">CurvedAnimation</span><span class=\"token punctuation\">(</span>\n        parent<span class=\"token punctuation\">:</span> controller<span class=\"token punctuation\">,</span>\n        curve<span class=\"token punctuation\">:</span> <span class=\"token function\">Interval</span><span class=\"token punctuation\">(</span>\n          <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0.6</span><span class=\"token punctuation\">,</span><span class=\"token comment\">//间隔，前60%的动画时间</span>\n          curve<span class=\"token punctuation\">:</span> Curves<span class=\"token punctuation\">.</span>ease<span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    padding <span class=\"token operator\">=</span> Tween<span class=\"token operator\">&lt;</span>EdgeInsets<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n      begin<span class=\"token punctuation\">:</span>EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">only</span><span class=\"token punctuation\">(</span>left<span class=\"token punctuation\">:</span> <span class=\"token number\">.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      end<span class=\"token punctuation\">:</span>EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">only</span><span class=\"token punctuation\">(</span>left<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">animate</span><span class=\"token punctuation\">(</span>\n      <span class=\"token function\">CurvedAnimation</span><span class=\"token punctuation\">(</span>\n        parent<span class=\"token punctuation\">:</span> controller<span class=\"token punctuation\">,</span>\n        curve<span class=\"token punctuation\">:</span> <span class=\"token function\">Interval</span><span class=\"token punctuation\">(</span>\n          <span class=\"token number\">0.6</span><span class=\"token punctuation\">,</span> <span class=\"token number\">1.0</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//间隔，后40%的动画时间</span>\n          curve<span class=\"token punctuation\">:</span> Curves<span class=\"token punctuation\">.</span>ease<span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n\n  <span class=\"token keyword\">final</span> Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> controller<span class=\"token punctuation\">;</span>\n  Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> height<span class=\"token punctuation\">;</span>\n  Animation<span class=\"token operator\">&lt;</span>EdgeInsets<span class=\"token operator\">&gt;</span> padding<span class=\"token punctuation\">;</span>\n  Animation<span class=\"token operator\">&lt;</span>Color<span class=\"token operator\">&gt;</span> color<span class=\"token punctuation\">;</span>\n\n  Widget <span class=\"token function\">_buildAnimation</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> Widget child<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n      alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>bottomCenter<span class=\"token punctuation\">,</span>\n      padding<span class=\"token punctuation\">:</span>padding<span class=\"token punctuation\">.</span>value <span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n        color<span class=\"token punctuation\">:</span> color<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span>\n        width<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n        height<span class=\"token punctuation\">:</span> height<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">AnimatedBuilder</span><span class=\"token punctuation\">(</span>\n      builder<span class=\"token punctuation\">:</span> _buildAnimation<span class=\"token punctuation\">,</span>\n      animation<span class=\"token punctuation\">:</span> controller<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p><code>StaggerAnimation</code>中定义了三个动画，分别是对<code>Container</code>的<code>height</code>、<code>color</code>、<code>padding</code>属性设置的动画，然后通过<code>Interval</code>来为每个动画指定在整个动画过程中的起始点和终点。下面我们来实现启动动画的路由：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">StaggerRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _StaggerRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">_StaggerRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_StaggerRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>StaggerRoute<span class=\"token operator\">&gt;</span> <span class=\"token keyword\">with</span> TickerProviderStateMixin <span class=\"token punctuation\">{</span>\n  AnimationController _controller<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    _controller <span class=\"token operator\">=</span> <span class=\"token function\">AnimationController</span><span class=\"token punctuation\">(</span>\n        duration<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>milliseconds<span class=\"token punctuation\">:</span> <span class=\"token number\">2000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        vsync<span class=\"token punctuation\">:</span> <span class=\"token keyword\">this</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n\n  Future<span class=\"token operator\">&lt;</span>Null<span class=\"token operator\">&gt;</span> <span class=\"token function\">_playAnimation</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">//先正向执行动画</span>\n      <span class=\"token keyword\">await</span> _controller<span class=\"token punctuation\">.</span><span class=\"token function\">forward</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>orCancel<span class=\"token punctuation\">;</span>\n      <span class=\"token comment\">//再反向执行动画</span>\n      <span class=\"token keyword\">await</span> _controller<span class=\"token punctuation\">.</span><span class=\"token function\">reverse</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>orCancel<span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">on</span> TickerCanceled <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">// the animation got canceled, probably because we were disposed</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span>  <span class=\"token function\">GestureDetector</span><span class=\"token punctuation\">(</span>\n      behavior<span class=\"token punctuation\">:</span> HitTestBehavior<span class=\"token punctuation\">.</span>opaque<span class=\"token punctuation\">,</span>\n      onTap<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">_playAnimation</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n          width<span class=\"token punctuation\">:</span> <span class=\"token number\">300.0</span><span class=\"token punctuation\">,</span>\n          height<span class=\"token punctuation\">:</span> <span class=\"token number\">300.0</span><span class=\"token punctuation\">,</span>\n          decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>\n            color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>black<span class=\"token punctuation\">.</span><span class=\"token function\">withOpacity</span><span class=\"token punctuation\">(</span><span class=\"token number\">0.1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            border<span class=\"token punctuation\">:</span> Border<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span>\n              color<span class=\"token punctuation\">:</span>  Colors<span class=\"token punctuation\">.</span>black<span class=\"token punctuation\">.</span><span class=\"token function\">withOpacity</span><span class=\"token punctuation\">(</span><span class=\"token number\">0.5</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token comment\">//调用我们定义的交织动画Widget</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">StaggerAnimation</span><span class=\"token punctuation\">(</span>\n              controller<span class=\"token punctuation\">:</span> _controller\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>执行效果如图，点击图9-3灰色矩形，就可以看到整个动画效果，图9-4是动画执行过程中的一帧。</p> <p><img src=\"/assets/img/9-3.ede423b7.png\" alt=\"图9-3\"><img src=\"/assets/img/9-4.cc3139b7.png\" alt=\"图9-4\"></p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><span class=\"prev\">\n      ←\n      <a href=\"/chapter9/hero.html\" class=\"prev\">\n        9.4 Hero动画\n      </a></span> <span class=\"next\"><a href=\"/chapter9/animated_switcher.html\">\n        9.6 通用“动画切换”组件（AnimatedSwitcher）\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/70.a85fca0c.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/imgs/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>前言 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/172.ae542eee.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/imgs/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"前言\"><a href=\"#前言\" class=\"header-anchor\">#</a> 前言</h1> <p>本书是第一本系统介绍Flutter技术的中文书籍，它是Flutter中文社区发起的开源项目之一，旨在帮助开发者入门，系统地、循序渐进的了解Flutter。</p> <blockquote><p>本书官网访问量较大，由于服务器配置有限，所以某些时段访问本站可能略有延迟，如果延迟较久，读者可以直接在<a href=\"https://github.com/flutterchina/flutter-in-action/blob/master/docs/SUMMARY.md\" target=\"_blank\" rel=\"noopener noreferrer\">Github上阅读本书<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>。</p></blockquote> <h2 id=\"缘起\"><a href=\"#缘起\" class=\"header-anchor\">#</a> 缘起</h2> <p>在全球，随着Flutter被越来越多的知名公司应用在自己的商业APP中，Flutter这门新技术也逐渐进入了移动开发者的视野，尤其是当Google在2018年IO大会上发布了第一个Preview版本后，国内刮起来一股学习Flutter的热潮。</p> <p>在Flutter发布之初，当时，我在看完Flutter原理介绍后，就对它产生了浓厚的兴趣。当时笔者身边也一些人比较关注Flutter，我也被经常问到关于Flutter的一些问题，比如Flutter怎么样？和RN有什么区别？Flutter为什么要用Dart？当时也听到了一些批评的声音，比如有些人说Flutter只是重复造轮子，没啥亮点、Flutter最大的缺点就是使用了Dart语言等。在听到这些问题及论调后，我深知这是对Flutter的不了解而造成的，这和当时国内缺乏Flutter中文文档和教程有直接关系，很多人对Flutter的了解都只停留在Google的发布会介绍（有中文翻译）。</p> <p>在笔者深入的了解Flutter后，深知Flutter必将是一个会改变移动开发格局的里程碑级作品，它从设计之初就对性能和开发效率兼顾，并且借鉴了React（一个Web开发框架）的响应式的UI框架设计思想等，总之，很难用一两句话说完Flutter的优点，同时我也很快成为了Flutter的路转粉。</p> <p>为了更好的帮助中国开发者了解这门新技术，我在2018年初开始翻译Flutter官网文档，同年4月份上线了<a href=\"https://flutterchina.club/\" target=\"_blank\" rel=\"noopener noreferrer\">Flutter中文网<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>，上线后反响很强烈，Flutter中文网也很快被传播开，百度搜索排名迅速蹿升到前三，截止目前，Flutter中文官网日PV在7万左右，每日独立访问人数近一万多。</p> <p>虽然Flutter中文网给中国开发者提供了很好的第一手了解Flutter的资料，但是笔者还会经常遇到一些对Flutter技术处于围观而不愿尝试的开发者。这主要是因为当时Flutter在国内没有成功案例，再加上新技术都有学习成本，所以即使有文档，也会有一些开发者犹豫是否来学习。为了解决这部分开发者的疑虑，我就想如果能用Flutter开发一个完整的APP发布到应用商店，这样开发者就可以在犹豫的时候可以先实际感受一下Flutter应用，这样有个直观的了解后，就会容易做出判断，为此，我开发了<a href=\"https://flutterchina.club/app/gm.html\" target=\"_blank\" rel=\"noopener noreferrer\">Gitme<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>，它是一个Github客户端，它支持了源码浏览、Issue、Label等Github的大多数功能，到目前为止，通过Gitme登录过Github账号的用户有8000多人，日活用户有1000人。更重要的是，有很多人正是用了Gitme后，才来学Flutter的。</p> <p>无论是做Flutter中文网，还是写Gitme，主要目标都是帮助开发者学习Flutter，同时消除围观开发者疑虑。但当开发者们真正开始动手时，Flutter的生态问题就变得尤为突出。由于在2018年初Flutter刚起步时，很多基础的包和库都是空白，少数已有的一些库也大都是预览版（未到1.0），存在很多bug。这个状况不是一两个人花一两天能搞定的，这是需要聚整个Flutter开发者社群之力，耗费数年时间才可能有所改善。因此，在2018年4月份，我以Flutter中文网名义发起了Flutter开源计划，该计划主要是开发一些常用的包来丰富Flutter生态，帮助开发者提高开发效率。自在github建立<a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\">Flutter中文开发者社区账号<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>以来，前后开源了dio、cookieJar、flukit等多个项目，而dio在开源两周后，就迅速成为Flutter第三方包中Star排名第一的开源库。</p> <p>虽然做的事情已经够多了，但是仍有一些很有必要去做的事情，由于时间原因，一直被搁置。</p> <p>随着学习Flutter的人越来越多，一部分开发者通过查看官网的文档就能入门，但也有很多开发者感觉学习时仍然有些吃力，主要原因有两个，首先官网的文档主要是为了引导开发者快速上手的，讲的并不是很细；其次是我们虽然翻译了官方文档，但是对于Flutter SDK文档并没有翻译，而在开发中遇到的一些具体问题通常都得去查看SDK文档。所以，要解决这两个问题，必须得有一个系统化的Flutter教程，它不仅可以快速引导开发者入门，而且也能触及到一些细节和原理，最好也能提供一些学习和研究Flutter的方法。因此，如果能有一本能系统地介绍Flutter的书籍便是便是非常棒的！但是，当时没有一本关于Flutter的中文书籍，因此，我便计划写一本能由浅入深、系统介绍Flutter的书。2018年12月，《Flutter实战》完成初稿，并在Github上开源，同时上线了<a href=\"https://book.flutterchina.club/\" target=\"_blank\" rel=\"noopener noreferrer\">《Flutter实战》电子书官网<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> ，至今每天有3000多人在线浏览本书。那为什么不直接出版？如果直接出版，不仅有稿费，而且也能保护知识产权，而直接开源，不就只能当雷锋了？其实，无论是做中文网、写Gitme、做Flutter开源项目，我的出发点都是为了能帮助中国开发者了解、学习Flutter，而这是一件非常有意义的事，只要坚持做对别人有价值的事，那么上帝迟早会奖赏你；当然我们也在网站中留了打赏的按钮，如果读者觉得有帮助，可以扫码打赏，请笔者喝一杯咖啡。另外由于成书比较仓促，当时书中也有很多错误，开源后，有很多读者提PR来纠正书中的错别字，时至今日，有78名开发者为本书提过PR，我在此，衷心的感谢你们。</p> <p>起初，我没有出版实体书的打算，当时我以为开发者直接通过在线访问本书官网即方便又不用付费，何乐而不为。但在本书上线后，很多读者来添加微信好友，表示非常有收益，很期待出版纸质书，甚至有比较热心的读者想提前付定金预定！我理解，这是大家对我所做之事的认可和鼓励。考虑到，确实有一部分读者，尤其是还没毕业的同学，可能更喜欢通过书籍去学习，为此，我已经和机械工业出版社合作，目前本书纸质版正在出版中，敬请关注。</p> <h3 id=\"本书组织结构\"><a href=\"#本书组织结构\" class=\"header-anchor\">#</a> 本书组织结构</h3> <p>本书采用由浅入深的方式介绍Flutter技术原理，分为三篇，总计15章，各篇的主要内容如下。</p> <ul><li>第一篇，入门篇（第1章~第7章），包括Flutter技术的出现背景和简介、Flutter的各种类型的Widget以及如何构建UI。通过学习本章，读者可以掌握如何使用Flutter来构建UI界面。</li> <li>第二篇，进阶篇（第8章~第14章），包括Flutter中的事件机制、动画、自定义组件、文件和网络、插件、国际化以及Flutter核心原理等。通过本章内容，读者可以对Flutter整体构建及原理有一个深入的认识。</li> <li>第三篇，实例篇（第15章），本章主要通过一个简版的Github APP来将前面介绍的内容串起来，让开发者对一个完整的Flutter APP开发流有个了解。</li></ul> <h2 id=\"本书特色\"><a href=\"#本书特色\" class=\"header-anchor\">#</a> 本书特色</h2> <p>笔者在大学时读过候捷（真名侯俊杰）写的一些C++相关书籍，在他的《深入浅出MFC》一书中，有一句话我印象像非常深 “唯有深入，方能浅出”。我非常认同这句话，对于一门技术，只有了解的深入，才能用最浅显、通俗的话语描述出。我在写作本书时，深入浅出就是一个主要目标。所以，本书的目标不仅是想告诉读者如何使用Flutter，而且也非常关注各个知识点的底层实现以及设计思想。从本书章节划分上来看，入门篇为“浅出”，进阶篇则是“深入”。另外由于PC客户端开发、移动开发、Web开发这些经验我都有，而Flutter本质上是一个UI系统，而UI系统的设计和实现在”大前端“下有很多相通之处，所以在本书中的一些知识点我也会对比一些其他UI系统（主要是Android或Web）相应的实现，便于有相关开发经验的读者对比理解。</p> <h2 id=\"本书读者对象\"><a href=\"#本书读者对象\" class=\"header-anchor\">#</a> 本书读者对象</h2> <ul><li>读者至少熟悉一种编程语言。</li> <li>读者最好接触过PC客户端、移动开发或Web前端开发中的一种。</li> <li>本书不适合做为编程的入门读物。</li></ul> <h2 id=\"关于随书源码\"><a href=\"#关于随书源码\" class=\"header-anchor\">#</a> 关于随书源码</h2> <p>由于篇幅所限，本书中大多数示例代码都只是部分核心代码，读者可以去<a href=\"https://github.com/wendux/flutter_in_action_source_code\" target=\"_blank\" rel=\"noopener noreferrer\">这里<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>查看下载。</p> <h2 id=\"致谢\"><a href=\"#致谢\" class=\"header-anchor\">#</a> 致谢</h2> <p>感谢一直以来支持Flutter中文网、Flutter开源项目的人以及所有对本书提过PR的人，正是因为有你们，才有这本书。另外尤其感谢给本书打赏过的同学，你们的支持给了我很大的鼓励。</p> <h2 id=\"权益\"><a href=\"#权益\" class=\"header-anchor\">#</a> 权益</h2> <p>最后，知识是应该付费的，创作不易，开源不等于免费，如果您是本书读者并手头宽裕，可以点击下面打赏按钮打赏；当然，如果您囊中羞涩，您也可以阅读本书，但我对您有个小小的要求，希望您在阅读的过程中能积极参与到本书的纠错以及未完成内容的创作上来，也算是有所付出。</p> <p>近来在网上发现很多<strong>原封不动复制本书</strong>的镜像网站和大量复制或<strong>引用了本书但未注明出处</strong>的博客、文章甚至书籍；对此，笔者在此声明，本书著作权归wendux所有，任何组织或个人在未经授权的情况下复制、拷贝、抄袭本书用于商业目的，笔者保留追究其法律责任的权利。如果是非商业目的的转载和引用，请注明出处并附上本书网址。另外如有出版机构愿意为本书出版实体书或者想转载本书内容，亦或是想合作，请加微信Demons-du.</p> <h3 id=\"勘误\"><a href=\"#勘误\" class=\"header-anchor\">#</a> 勘误</h3> <p>由于Flutter SDK在不断更新，本书中的部分内容（如类的继承关系、参数等）可能会和新版本的Flutter不一致，读者以最新的Flutter SDK为准。另外，由于时间仓促，书中难免有错误之处，如果您发现本书中的错误，欢迎点击右上角的”编辑按钮“，提PR。如果您想一起参与本书创作，可以参考<a href=\"https://github.com/flutterchina/flutter-in-action#%E8%B4%A1%E7%8C%AE%E9%A1%BB%E7%9F%A5\" target=\"_blank\" rel=\"noopener noreferrer\">《Flutter实战》贡献指南<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/172.ae542eee.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>前言 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/173.c985ca5c.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" aria-current=\"page\" class=\"home-link router-link-exact-active router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"active sidebar-link\">首页</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/#缘起\" class=\"sidebar-link\">缘起</a></li><li class=\"sidebar-sub-header\"><a href=\"/#本书特色\" class=\"sidebar-link\">本书特色</a></li><li class=\"sidebar-sub-header\"><a href=\"/#本书读者对象\" class=\"sidebar-link\">本书读者对象</a></li><li class=\"sidebar-sub-header\"><a href=\"/#关于随书源码\" class=\"sidebar-link\">关于随书源码</a></li><li class=\"sidebar-sub-header\"><a href=\"/#致谢\" class=\"sidebar-link\">致谢</a></li><li class=\"sidebar-sub-header\"><a href=\"/#权益\" class=\"sidebar-link\">权益</a></li></ul></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"前言\"><a href=\"#前言\" class=\"header-anchor\">#</a> 前言</h1> <p>本书是第一本系统介绍Flutter技术的中文书籍，它是Flutter中文社区发起的开源项目之一，旨在帮助开发者入门，系统地、循序渐进的了解Flutter。</p> <blockquote><p>本书官网访问量较大，由于服务器配置有限，所以某些时段访问本站可能略有延迟，如果延迟较久，读者可以直接在<a href=\"https://github.com/flutterchina/flutter-in-action/blob/master/docs/SUMMARY.md\" target=\"_blank\" rel=\"noopener noreferrer\">Github上阅读本书<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>。</p></blockquote> <h2 id=\"缘起\"><a href=\"#缘起\" class=\"header-anchor\">#</a> 缘起</h2> <p>在全球，随着Flutter被越来越多的知名公司应用在自己的商业APP中，Flutter这门新技术也逐渐进入了移动开发者的视野，尤其是当Google在2018年IO大会上发布了第一个Preview版本后，国内刮起来一股学习Flutter的热潮。</p> <p>在Flutter发布之初，当时，我在看完Flutter原理介绍后，就对它产生了浓厚的兴趣。当时笔者身边也一些人比较关注Flutter，我也被经常问到关于Flutter的一些问题，比如Flutter怎么样？和RN有什么区别？Flutter为什么要用Dart？当时也听到了一些批评的声音，比如有些人说Flutter只是重复造轮子，没啥亮点、Flutter最大的缺点就是使用了Dart语言等。在听到这些问题及论调后，我深知这是对Flutter的不了解而造成的，这和当时国内缺乏Flutter中文文档和教程有直接关系，很多人对Flutter的了解都只停留在Google的发布会介绍（有中文翻译）。</p> <p>在笔者深入的了解Flutter后，深知Flutter必将是一个会改变移动开发格局的里程碑级作品，它从设计之初就对性能和开发效率兼顾，并且借鉴了React（一个Web开发框架）的响应式的UI框架设计思想等，总之，很难用一两句话说完Flutter的优点，同时我也很快成为了Flutter的路转粉。</p> <p>为了更好的帮助中国开发者了解这门新技术，我在2018年初开始翻译Flutter官网文档，同年4月份上线了<a href=\"https://flutterchina.club/\" target=\"_blank\" rel=\"noopener noreferrer\">Flutter中文网<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>，上线后反响很强烈，Flutter中文网也很快被传播开，百度搜索排名迅速蹿升到前三，截止目前，Flutter中文官网日PV在7万左右，每日独立访问人数近一万多。</p> <p>虽然Flutter中文网给中国开发者提供了很好的第一手了解Flutter的资料，但是笔者还会经常遇到一些对Flutter技术处于围观而不愿尝试的开发者。这主要是因为当时Flutter在国内没有成功案例，再加上新技术都有学习成本，所以即使有文档，也会有一些开发者犹豫是否来学习。为了解决这部分开发者的疑虑，我就想如果能用Flutter开发一个完整的APP发布到应用商店，这样开发者就可以在犹豫的时候可以先实际感受一下Flutter应用，这样有个直观的了解后，就会容易做出判断，为此，我开发了<a href=\"https://flutterchina.club/app/gm.html\" target=\"_blank\" rel=\"noopener noreferrer\">Gitme<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>，它是一个Github客户端，它支持了源码浏览、Issue、Label等Github的大多数功能，到目前为止，通过Gitme登录过Github账号的用户有8000多人，日活用户有1000人。更重要的是，有很多人正是用了Gitme后，才来学Flutter的。</p> <p>无论是做Flutter中文网，还是写Gitme，主要目标都是帮助开发者学习Flutter，同时消除围观开发者疑虑。但当开发者们真正开始动手时，Flutter的生态问题就变得尤为突出。由于在2018年初Flutter刚起步时，很多基础的包和库都是空白，少数已有的一些库也大都是预览版（未到1.0），存在很多bug。这个状况不是一两个人花一两天能搞定的，这是需要聚整个Flutter开发者社群之力，耗费数年时间才可能有所改善。因此，在2018年4月份，我以Flutter中文网名义发起了Flutter开源计划，该计划主要是开发一些常用的包来丰富Flutter生态，帮助开发者提高开发效率。自在github建立<a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\">Flutter中文开发者社区账号<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>以来，前后开源了dio、cookieJar、flukit等多个项目，而dio在开源两周后，就迅速成为Flutter第三方包中Star排名第一的开源库。</p> <p>虽然做的事情已经够多了，但是仍有一些很有必要去做的事情，由于时间原因，一直被搁置。</p> <p>随着学习Flutter的人越来越多，一部分开发者通过查看官网的文档就能入门，但也有很多开发者感觉学习时仍然有些吃力，主要原因有两个，首先官网的文档主要是为了引导开发者快速上手的，讲的并不是很细；其次是我们虽然翻译了官方文档，但是对于Flutter SDK文档并没有翻译，而在开发中遇到的一些具体问题通常都得去查看SDK文档。所以，要解决这两个问题，必须得有一个系统化的Flutter教程，它不仅可以快速引导开发者入门，而且也能触及到一些细节和原理，最好也能提供一些学习和研究Flutter的方法。因此，如果能有一本能系统地介绍Flutter的书籍便是便是非常棒的！但是，当时没有一本关于Flutter的中文书籍，因此，我便计划写一本能由浅入深、系统介绍Flutter的书。2018年12月，《Flutter实战》完成初稿，并在Github上开源，同时上线了<a href=\"https://book.flutterchina.club/\" target=\"_blank\" rel=\"noopener noreferrer\">《Flutter实战》电子书官网<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> ，至今每天有3000多人在线浏览本书。那为什么不直接出版？如果直接出版，不仅有稿费，而且也能保护知识产权，而直接开源，不就只能当雷锋了？其实，无论是做中文网、写Gitme、做Flutter开源项目，我的出发点都是为了能帮助中国开发者了解、学习Flutter，而这是一件非常有意义的事，只要坚持做对别人有价值的事，那么上帝迟早会奖赏你；当然我们也在网站中留了打赏的按钮，如果读者觉得有帮助，可以扫码打赏，请笔者喝一杯咖啡。另外由于成书比较仓促，当时书中也有很多错误，开源后，有很多读者提PR来纠正书中的错别字，时至今日，有78名开发者为本书提过PR，我在此，衷心的感谢你们。</p> <p>起初，我没有出版实体书的打算，当时我以为开发者直接通过在线访问本书官网即方便又不用付费，何乐而不为。但在本书上线后，很多读者来添加微信好友，表示非常有收益，很期待出版纸质书，甚至有比较热心的读者想提前付定金预定！我理解，这是大家对我所做之事的认可和鼓励。考虑到，确实有一部分读者，尤其是还没毕业的同学，可能更喜欢通过书籍去学习，为此，我已经和机械工业出版社合作，目前本书纸质版正在出版中，敬请关注。</p> <h3 id=\"本书组织结构\"><a href=\"#本书组织结构\" class=\"header-anchor\">#</a> 本书组织结构</h3> <p>本书采用由浅入深的方式介绍Flutter技术原理，分为三篇，总计15章，各篇的主要内容如下。</p> <ul><li>第一篇，入门篇（第1章~第7章），包括Flutter技术的出现背景和简介、Flutter的各种类型的Widget以及如何构建UI。通过学习本章，读者可以掌握如何使用Flutter来构建UI界面。</li> <li>第二篇，进阶篇（第8章~第14章），包括Flutter中的事件机制、动画、自定义组件、文件和网络、插件、国际化以及Flutter核心原理等。通过本章内容，读者可以对Flutter整体构建及原理有一个深入的认识。</li> <li>第三篇，实例篇（第15章），本章主要通过一个简版的Github APP来将前面介绍的内容串起来，让开发者对一个完整的Flutter APP开发流有个了解。</li></ul> <h2 id=\"本书特色\"><a href=\"#本书特色\" class=\"header-anchor\">#</a> 本书特色</h2> <p>笔者在大学时读过候捷（真名侯俊杰）写的一些C++相关书籍，在他的《深入浅出MFC》一书中，有一句话我印象像非常深 “唯有深入，方能浅出”。我非常认同这句话，对于一门技术，只有了解的深入，才能用最浅显、通俗的话语描述出。我在写作本书时，深入浅出就是一个主要目标。所以，本书的目标不仅是想告诉读者如何使用Flutter，而且也非常关注各个知识点的底层实现以及设计思想。从本书章节划分上来看，入门篇为“浅出”，进阶篇则是“深入”。另外由于PC客户端开发、移动开发、Web开发这些经验我都有，而Flutter本质上是一个UI系统，而UI系统的设计和实现在”大前端“下有很多相通之处，所以在本书中的一些知识点我也会对比一些其他UI系统（主要是Android或Web）相应的实现，便于有相关开发经验的读者对比理解。</p> <h2 id=\"本书读者对象\"><a href=\"#本书读者对象\" class=\"header-anchor\">#</a> 本书读者对象</h2> <ul><li>读者至少熟悉一种编程语言。</li> <li>读者最好接触过PC客户端、移动开发或Web前端开发中的一种。</li> <li>本书不适合做为编程的入门读物。</li></ul> <h2 id=\"关于随书源码\"><a href=\"#关于随书源码\" class=\"header-anchor\">#</a> 关于随书源码</h2> <p>由于篇幅所限，本书中大多数示例代码都只是部分核心代码，读者可以去<a href=\"https://github.com/wendux/flutter_in_action_source_code\" target=\"_blank\" rel=\"noopener noreferrer\">这里<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>查看下载。</p> <h2 id=\"致谢\"><a href=\"#致谢\" class=\"header-anchor\">#</a> 致谢</h2> <p>感谢一直以来支持Flutter中文网、Flutter开源项目的人以及所有对本书提过PR的人，正是因为有你们，才有这本书。另外尤其感谢给本书打赏过的同学，你们的支持给了我很大的鼓励。</p> <h2 id=\"权益\"><a href=\"#权益\" class=\"header-anchor\">#</a> 权益</h2> <p>最后，知识是应该付费的，创作不易，开源不等于免费，如果您是本书读者并手头宽裕，可以点击下面打赏按钮打赏；当然，如果您囊中羞涩，您也可以阅读本书，但我对您有个小小的要求，希望您在阅读的过程中能积极参与到本书的纠错以及未完成内容的创作上来，也算是有所付出。</p> <p>近来在网上发现很多<strong>原封不动复制本书</strong>的镜像网站和大量复制或<strong>引用了本书但未注明出处</strong>的博客、文章甚至书籍；对此，笔者在此声明，本书著作权归wendux所有，任何组织或个人在未经授权的情况下复制、拷贝、抄袭本书用于商业目的，笔者保留追究其法律责任的权利。如果是非商业目的的转载和引用，请注明出处并附上本书网址。另外如有出版机构愿意为本书出版实体书或者想转载本书内容，亦或是想合作，请加微信Demons-du.</p> <h3 id=\"勘误\"><a href=\"#勘误\" class=\"header-anchor\">#</a> 勘误</h3> <p>由于Flutter SDK在不断更新，本书中的部分内容（如类的继承关系、参数等）可能会和新版本的Flutter不一致，读者以最新的Flutter SDK为准。另外，由于时间仓促，书中难免有错误之处，如果您发现本书中的错误，欢迎点击右上角的”编辑按钮“，提PR。如果您想一起参与本书创作，可以参考<a href=\"https://github.com/flutterchina/flutter-in-action#%E8%B4%A1%E7%8C%AE%E9%A1%BB%E7%9F%A5\" target=\"_blank\" rel=\"noopener noreferrer\">《Flutter实战》贡献指南<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <div class=\"page-nav\"><p class=\"inner\"><!----> <span class=\"next\"><a href=\"/chapter1/mobile_development_intro.html\">\n        1.1 移动开发技术简介\n      </a>\n      →\n    </span></p></div> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/173.c985ca5c.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/intro.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>前言 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/174.ece20a6a.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"前言\"><a href=\"#前言\" class=\"header-anchor\">#</a> 前言</h1> <p>本书是第一本系统介绍Flutter技术的中文书籍，它是Flutter中文社区发起的开源项目之一，旨在帮助开发者入门，系统地、循序渐进的了解Flutter。</p> <blockquote><p>本书官网访问量较大，由于服务器配置有限，所以某些时段访问本站可能略有延迟，如果延迟较久，读者可以直接在<a href=\"https://github.com/flutterchina/flutter-in-action/blob/master/docs/SUMMARY.md\" target=\"_blank\" rel=\"noopener noreferrer\">Github上阅读本书<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>。</p></blockquote> <h2 id=\"缘起\"><a href=\"#缘起\" class=\"header-anchor\">#</a> 缘起</h2> <p>在全球，随着Flutter被越来越多的知名公司应用在自己的商业APP中，Flutter这门新技术也逐渐进入了移动开发者的视野，尤其是当Google在2018年IO大会上发布了第一个Preview版本后，国内刮起来一股学习Flutter的热潮。</p> <p>在Flutter发布之初，当时，我在看完Flutter原理介绍后，就对它产生了浓厚的兴趣。当时笔者身边也一些人比较关注Flutter，我也被经常问到关于Flutter的一些问题，比如Flutter怎么样？和RN有什么区别？Flutter为什么要用Dart？当时也听到了一些批评的声音，比如有些人说Flutter只是重复造轮子，没啥亮点、Flutter最大的缺点就是使用了Dart语言等。在听到这些问题及论调后，我深知这是对Flutter的不了解而造成的，这和当时国内缺乏Flutter中文文档和教程有直接关系，很多人对Flutter的了解都只停留在Google的发布会介绍（有中文翻译）。</p> <p>在笔者深入的了解Flutter后，深知Flutter必将是一个会改变移动开发格局的里程碑级作品，它从设计之初就对性能和开发效率兼顾，并且借鉴了React（一个Web开发框架）的响应式的UI框架设计思想等，总之，很难用一两句话说完Flutter的优点，同时我也很快成为了Flutter的路转粉。</p> <p>为了更好的帮助中国开发者了解这门新技术，我在2018年初开始翻译Flutter官网文档，同年4月份上线了<a href=\"https://flutterchina.club/\" target=\"_blank\" rel=\"noopener noreferrer\">Flutter中文网<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>，上线后反响很强烈，Flutter中文网也很快被传播开，百度搜索排名迅速蹿升到前三，截止目前，Flutter中文官网日PV在7万左右，每日独立访问人数近一万多。</p> <p>虽然Flutter中文网给中国开发者提供了很好的第一手了解Flutter的资料，但是笔者还会经常遇到一些对Flutter技术处于围观而不愿尝试的开发者。这主要是因为当时Flutter在国内没有成功案例，再加上新技术都有学习成本，所以即使有文档，也会有一些开发者犹豫是否来学习。为了解决这部分开发者的疑虑，我就想如果能用Flutter开发一个完整的APP发布到应用商店，这样开发者就可以在犹豫的时候可以先实际感受一下Flutter应用，这样有个直观的了解后，就会容易做出判断，为此，我开发了<a href=\"https://flutterchina.club/app/gm.html\" target=\"_blank\" rel=\"noopener noreferrer\">Gitme<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>，它是一个Github客户端，它支持了源码浏览、Issue、Label等Github的大多数功能，到目前为止，通过Gitme登录过Github账号的用户有8000多人，日活用户有1000人。更重要的是，有很多人正是用了Gitme后，才来学Flutter的。</p> <p>无论是做Flutter中文网，还是写Gitme，主要目标都是帮助开发者学习Flutter，同时消除围观开发者疑虑。但当开发者们真正开始动手时，Flutter的生态问题就变得尤为突出。由于在2018年初Flutter刚起步时，很多基础的包和库都是空白，少数已有的一些库也大都是预览版（未到1.0），存在很多bug。这个状况不是一两个人花一两天能搞定的，这是需要聚整个Flutter开发者社群之力，耗费数年时间才可能有所改善。因此，在2018年4月份，我以Flutter中文网名义发起了Flutter开源计划，该计划主要是开发一些常用的包来丰富Flutter生态，帮助开发者提高开发效率。自在github建立<a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\">Flutter中文开发者社区账号<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>以来，前后开源了dio、cookieJar、flukit等多个项目，而dio在开源两周后，就迅速成为Flutter第三方包中Star排名第一的开源库。</p> <p>虽然做的事情已经够多了，但是仍有一些很有必要去做的事情，由于时间原因，一直被搁置。</p> <p>随着学习Flutter的人越来越多，一部分开发者通过查看官网的文档就能入门，但也有很多开发者感觉学习时仍然有些吃力，主要原因有两个，首先官网的文档主要是为了引导开发者快速上手的，讲的并不是很细；其次是我们虽然翻译了官方文档，但是对于Flutter SDK文档并没有翻译，而在开发中遇到的一些具体问题通常都得去查看SDK文档。所以，要解决这两个问题，必须得有一个系统化的Flutter教程，它不仅可以快速引导开发者入门，而且也能触及到一些细节和原理，最好也能提供一些学习和研究Flutter的方法。因此，如果能有一本能系统地介绍Flutter的书籍便是便是非常棒的！但是，当时没有一本关于Flutter的中文书籍，因此，我便计划写一本能由浅入深、系统介绍Flutter的书。2018年12月，《Flutter实战》完成初稿，并在Github上开源，同时上线了<a href=\"https://book.flutterchina.club/\" target=\"_blank\" rel=\"noopener noreferrer\">《Flutter实战》电子书官网<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> ，至今每天有3000多人在线浏览本书。那为什么不直接出版？如果直接出版，不仅有稿费，而且也能保护知识产权，而直接开源，不就只能当雷锋了？其实，无论是做中文网、写Gitme、做Flutter开源项目，我的出发点都是为了能帮助中国开发者了解、学习Flutter，而这是一件非常有意义的事，只要坚持做对别人有价值的事，那么上帝迟早会奖赏你；当然我们也在网站中留了打赏的按钮，如果读者觉得有帮助，可以扫码打赏，请笔者喝一杯咖啡。另外由于成书比较仓促，当时书中也有很多错误，开源后，有很多读者提PR来纠正书中的错别字，时至今日，有78名开发者为本书提过PR，我在此，衷心的感谢你们。</p> <p>起初，我没有出版实体书的打算，当时我以为开发者直接通过在线访问本书官网即方便又不用付费，何乐而不为。但在本书上线后，很多读者来添加微信好友，表示非常有收益，很期待出版纸质书，甚至有比较热心的读者想提前付定金预定！我理解，这是大家对我所做之事的认可和鼓励。考虑到，确实有一部分读者，尤其是还没毕业的同学，可能更喜欢通过书籍去学习，为此，我已经和机械工业出版社合作，目前本书纸质版正在出版中，敬请关注。</p> <h3 id=\"本书组织结构\"><a href=\"#本书组织结构\" class=\"header-anchor\">#</a> 本书组织结构</h3> <p>本书采用由浅入深的方式介绍Flutter技术原理，分为三篇，总计15章，各篇的主要内容如下。</p> <ul><li>第一篇，入门篇（第1章~第7章），包括Flutter技术的出现背景和简介、Flutter的各种类型的Widget以及如何构建UI。通过学习本章，读者可以掌握如何使用Flutter来构建UI界面。</li> <li>第二篇，进阶篇（第8章~第14章），包括Flutter中的事件机制、动画、自定义组件、文件和网络、插件、国际化以及Flutter核心原理等。通过本章内容，读者可以对Flutter整体构建及原理有一个深入的认识。</li> <li>第三篇，实例篇（第15章），本章主要通过一个简版的Github APP来将前面介绍的内容串起来，让开发者对一个完整的Flutter APP开发流有个了解。</li></ul> <h2 id=\"本书特色\"><a href=\"#本书特色\" class=\"header-anchor\">#</a> 本书特色</h2> <p>笔者在大学时读过候捷（真名侯俊杰）写的一些C++相关书籍，在他的《深入浅出MFC》一书中，有一句话我印象像非常深 “唯有深入，方能浅出”。我非常认同这句话，对于一门技术，只有了解的深入，才能用最浅显、通俗的话语描述出。我在写作本书时，深入浅出就是一个主要目标。所以，本书的目标不仅是想告诉读者如何使用Flutter，而且也非常关注各个知识点的底层实现以及设计思想。从本书章节划分上来看，入门篇为“浅出”，进阶篇则是“深入”。另外由于PC客户端开发、移动开发、Web开发这些经验我都有，而Flutter本质上是一个UI系统，而UI系统的设计和实现在”大前端“下有很多相通之处，所以在本书中的一些知识点我也会对比一些其他UI系统（主要是Android或Web）相应的实现，便于有相关开发经验的读者对比理解。</p> <h2 id=\"本书读者对象\"><a href=\"#本书读者对象\" class=\"header-anchor\">#</a> 本书读者对象</h2> <ul><li>读者至少熟悉一种编程语言。</li> <li>读者最好接触过PC客户端、移动开发或Web前端开发中的一种。</li> <li>本书不适合做为编程的入门读物。</li></ul> <h2 id=\"关于随书源码\"><a href=\"#关于随书源码\" class=\"header-anchor\">#</a> 关于随书源码</h2> <p>由于篇幅所限，本书中大多数示例代码都只是部分核心代码，读者可以去<a href=\"https://github.com/wendux/flutter_in_action_source_code\" target=\"_blank\" rel=\"noopener noreferrer\">这里<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>查看下载。</p> <h2 id=\"致谢\"><a href=\"#致谢\" class=\"header-anchor\">#</a> 致谢</h2> <p>感谢一直以来支持Flutter中文网、Flutter开源项目的人以及所有对本书提过PR的人，正是因为有你们，才有这本书。另外尤其感谢给本书打赏过的同学，你们的支持给了我很大的鼓励。</p> <h2 id=\"权益\"><a href=\"#权益\" class=\"header-anchor\">#</a> 权益</h2> <p>最后，知识是应该付费的，创作不易，开源不等于免费，如果您是本书读者并手头宽裕，可以点击下面打赏按钮打赏；当然，如果您囊中羞涩，您也可以阅读本书，但我对您有个小小的要求，希望您在阅读的过程中能积极参与到本书的纠错以及未完成内容的创作上来，也算是有所付出。</p> <p>近来在网上发现很多<strong>原封不动复制本书</strong>的镜像网站和大量复制或<strong>引用了本书但未注明出处</strong>的博客、文章甚至书籍；对此，笔者在此声明，本书著作权归wendux所有，任何组织或个人在未经授权的情况下复制、拷贝、抄袭本书用于商业目的，笔者保留追究其法律责任的权利。如果是非商业目的的转载和引用，请注明出处并附上本书网址。另外如有出版机构愿意为本书出版实体书或者想转载本书内容，亦或是想合作，请加微信Demons-du.</p> <h3 id=\"勘误\"><a href=\"#勘误\" class=\"header-anchor\">#</a> 勘误</h3> <p>由于Flutter SDK在不断更新，本书中的部分内容（如类的继承关系、参数等）可能会和新版本的Flutter不一致，读者以最新的Flutter SDK为准。另外，由于时间仓促，书中难免有错误之处，如果您发现本书中的错误，欢迎点击右上角的”编辑按钮“，提PR。如果您想一起参与本书创作，可以参考<a href=\"https://github.com/flutterchina/flutter-in-action#%E8%B4%A1%E7%8C%AE%E9%A1%BB%E7%9F%A5\" target=\"_blank\" rel=\"noopener noreferrer\">《Flutter实战》贡献指南<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/174.ece20a6a.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/join_us.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>字节跳动-内推 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/175.891c0a2f.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" aria-current=\"page\" class=\"nav-link router-link-exact-active router-link-active\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" aria-current=\"page\" class=\"nav-link router-link-exact-active router-link-active\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading open\"><span>字节跳动-内推</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/join_us.html#前端团队介绍\" class=\"sidebar-link\">前端团队介绍</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/join_us.html#业务线介绍\" class=\"sidebar-link\">业务线介绍</a></li><li class=\"sidebar-sub-header\"><a href=\"/join_us.html#团队福利\" class=\"sidebar-link\">团队福利</a></li></ul></li><li><a href=\"/join_us.html#职位介绍\" class=\"sidebar-link\">职位介绍</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/join_us.html#职位一-前端开发工程师-校招-社招-急\" class=\"sidebar-link\">职位一：前端开发工程师(校招/社招)急</a></li><li class=\"sidebar-sub-header\"><a href=\"/join_us.html#职位二-前端开发实习生-可转正\" class=\"sidebar-link\">职位二：前端开发实习生(可转正)</a></li><li class=\"sidebar-sub-header\"><a href=\"/join_us.html#职位三-web3d开发工程师-急\" class=\"sidebar-link\">职位三：web3D开发工程师（急）</a></li></ul></li><li><a href=\"/join_us.html#其它职位-实习-校招-社招均可\" class=\"sidebar-link\">其它职位（实习/校招/社招均可）</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/join_us.html#android开发工程师\" class=\"sidebar-link\">Android开发工程师</a></li><li class=\"sidebar-sub-header\"><a href=\"/join_us.html#ios开发工程师\" class=\"sidebar-link\">iOS开发工程师</a></li><li class=\"sidebar-sub-header\"><a href=\"/join_us.html#后端开发工程师\" class=\"sidebar-link\">后端开发工程师</a></li></ul></li><li><a href=\"/join_us.html#返回书籍菜单列表\" class=\"sidebar-link\">返回书籍菜单列表</a><ul class=\"sidebar-sub-headers\"></ul></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"字节跳动-内推\"><a href=\"#字节跳动-内推\" class=\"header-anchor\">#</a> 字节跳动-内推</h1> <p>欢迎来字节跳动，和作者（wendux） 一起做同事！我们有大量HC，社招、校招、实习，前端、客户端、后端都有，欢迎对技术有热情的同学来投递！风里雨里，我在字节跳动等你~</p> <blockquote><p>投递方式：加微信（Demons-du），好友申请时请按 &quot;姓名+学校+职位+来自flutter社区&quot; 备注信息，微信请求通过后，字节跳动VIP通道就建立了（后续发简历、进度跟进、问题咨询都可以直接微信联系哦）。</p></blockquote> <h2 id=\"前端团队介绍\"><a href=\"#前端团队介绍\" class=\"header-anchor\">#</a> 前端团队介绍</h2> <p>我们是字节跳动-幸福里FE团队，诞生于2018年9月，从最初的4人组成长到今天的30多人，成员年龄跨度从90后到00后。技术栈覆盖当下前端主流全方向（Vue、React、Typescript、nodejs、webgl、flutter、小程序)，团队内大牛多多，技术氛围浓厚，有VR专家老吴、3D渲染一哥博哥、《Flutter实战》作者wendux、深谙 Web 框架及工程化的董老师、以及技能树满点的杰哥等等，还有，团队经常组织线下学习及娱乐活动，是一个开心且战斗力极强的team。只要你觉得自己够出色，或想让自己变得更出色，还等什么，放肆地加入我们吧。</p> <h3 id=\"业务线介绍\"><a href=\"#业务线介绍\" class=\"header-anchor\">#</a> 业务线介绍</h3> <p>幸福里是字节跳动旗下集内容、社区、工具于一体的房产信息、服务、交易平台。产品基于个性化推荐引擎向用户推荐优质的房产内容和全面、真实的房源信息，致力于为用户提供全面、专业、可靠的购房决策支持。</p> <p>幸福里始于2018年8月，是国内发展最快的，集内容、社区、工具于一体的房产信息与服务平台，业务覆盖一二线共23城，现累积注册用户千万，目前进入高速增长期。</p> <h3 id=\"团队福利\"><a href=\"#团队福利\" class=\"header-anchor\">#</a> 团队福利</h3> <p>五险一金、补充医疗保险、定期体检、年终奖、股票期权、带薪年假、员工旅游、交通补助、包吃、节日福利、住房补贴，不限量零食下午茶、弹性工作制</p> <h4 id=\"实习生\"><a href=\"#实习生\" class=\"header-anchor\">#</a> 实习生</h4> <ol><li>团队为每一位实习生提供专职mentor，手把手带入工作业务。</li> <li>团队为没有基础的实习生提供“筑基计划”的课程学习，轻松进阶前端技能。</li></ol> <h2 id=\"职位介绍\"><a href=\"#职位介绍\" class=\"header-anchor\">#</a> 职位介绍</h2> <h3 id=\"职位一-前端开发工程师-校招-社招-急\"><a href=\"#职位一-前端开发工程师-校招-社招-急\" class=\"header-anchor\">#</a> 职位一：前端开发工程师(校招/社招)急</h3> <p><strong>职位描述:</strong></p> <ol><li><p>负责移动端 /PC 端业务系统、小程序、跨端页面开发；</p></li> <li><p>负责推动与优化业务线中前端基础架构、组件抽象、技术调研；</p></li> <li><p>积极推动改进产品，包括技术、用户体验、产品等各个维度的改进。</p></li></ol> <p><strong>职位要求:</strong></p> <ol><li><p>本科及以上学历，计算机、通信等相关专业；</p></li> <li><p>熟练掌握 EcmaScript，CSS，HTML，DOM、绘图、动画、协议、安全、网络、性能优化等前端技术，对主流前端框架（ React \\ Vue 等）至少一种有深入应用并深入理解其设计原理；</p></li> <li><p>有安卓、iOS 开发经验或跨端技术flutter者优先；</p></li> <li><p>对用户体验、交互操作流程，及用户需求有一定了解；</p></li> <li><p>积极乐观，责任心强，工作认真细致，具备良好的服务意识，具有良好的团队沟通与协作能力；</p></li> <li><p>热爱前端技术，有较强的学习能力，有强烈的求知欲、好奇心和进取心 ，能及时关注和学习业界最新的前端技术。</p></li></ol> <h3 id=\"职位二-前端开发实习生-可转正\"><a href=\"#职位二-前端开发实习生-可转正\" class=\"header-anchor\">#</a> 职位二：前端开发实习生(可转正)</h3> <p><strong>职位描述：</strong></p> <p>1、负责字节跳动-幸福里业务h5、小程序、中台系统的维护与开发；</p> <p>2、负责根据已有前端项目的基础架构进行合理的技术优化；</p> <p>3、积极推动改进产品，包括技术、用户体验、产品等各个维度。</p> <p><strong>职位要求：</strong></p> <p>1、计算机基础扎实：数据结构、算法、操作系统；</p> <p>2、熟悉掌握javascript、ES6 语言特性，熟练掌握css中常见布局方式，以及CSS3动画技术；</p> <p>3、熟练掌握VUE或React技术（非必须）；</p> <p>4、有ACM竞赛且获奖者优先；</p> <p>5、具较强的学习能力、主动、自驱、有责任心。</p> <p><strong>实习生培养计划</strong>：</p> <p>我们会为每一位实习生配备一名mentor进行日常决疑解惑和指导，同时我们针对不太熟悉前端的同学进行一个专门的【筑基培训】，旨在帮助快速补齐前端基础，以及明确后续学习和成长路线，有老司机带，不迷路！</p> <h3 id=\"职位三-web3d开发工程师-急\"><a href=\"#职位三-web3d开发工程师-急\" class=\"header-anchor\">#</a> 职位三：web3D开发工程师（急）</h3> <p><strong>职位描述：</strong></p> <ol><li>负责 VR 看房相关的业务开发，包括渲染SDK、标注平台等。</li> <li>负责 VR 数据平台相关开发：包括数据预处理</li></ol> <p><strong>职位要求：</strong></p> <ol><li><p>熟练掌握 JavaScript，WebGL；</p></li> <li><p>熟悉计算机图形学，渲染管线/线性代数；</p></li> <li><p>熟悉常用 Shader 原理及编写；</p></li> <li><p>熟悉至少一款 H5 渲染引擎，如ThreeJS，Babylon等；</p></li> <li><p>热爱钻研新技术，有强烈的好奇心和求知欲，有良好的编码规范；</p></li> <li><p>加分项：</p> <ul><li>熟悉VR看房相关业务</li> <li>熟悉后端开发（Node）、对服务稳定性、并发了解同学优先（VR数据平台）。</li> <li>熟悉 ThreeJS，Babylon, Unity3D 有相关 3D 作品或 DEMO。</li> <li>各大前端技术社区活跃者、有自己的开源项目；</li></ul></li></ol> <h2 id=\"其它职位-实习-校招-社招均可\"><a href=\"#其它职位-实习-校招-社招均可\" class=\"header-anchor\">#</a> 其它职位（实习/校招/社招均可）</h2> <h3 id=\"android开发工程师\"><a href=\"#android开发工程师\" class=\"header-anchor\">#</a> Android开发工程师</h3> <p>职位描述</p> <p>1、负责公司移动产品的研发, 编写高质量的代码；</p> <p>2、和产品经理配合, 深度参与手机产品需求讨论, 功能定义等；</p> <p>3、设计良好的代码结构, 不断迭代重构 。</p> <p>职位要求</p> <p>1、智能手机爱好者和使用者, 追求良好的用户体验；</p> <p>2、热爱移动产品研发, 愿意在移动开发领域深入钻研, 并成为专家；</p> <p>3、熟练掌握JAVA, 熟悉Android SDK；</p> <p>4、一年以上Android开发经验, 能独立开发Android App； 5、对软件产品有强烈的责任心, 具备良好的沟通能力和优秀的团队协作能力。</p> <p>5、有flutter开发经验者加分。</p> <h3 id=\"ios开发工程师\"><a href=\"#ios开发工程师\" class=\"header-anchor\">#</a> iOS开发工程师</h3> <p>职位描述</p> <p>1、负责公司移动产品的研发，编写高质量的代码；</p> <p>2、和产品经理配合，深度参与手机产品需求讨论，功能定义等；</p> <p>3、设计良好的代码结构，不断迭代重构 ；</p> <p>4、导并带领初级工程师共同完成研发任务。</p> <p>职位要求</p> <p>1、有强烈的求知欲和进取心；</p> <p>2、具有扎实的编程工底，良好的设计能力和编程习惯；</p> <p>3、至少精通一门编程语言 ，熟练掌握Objective-C，熟悉Swift的优先 ；</p> <p>4、一年以上iOS开发经验，能独立开发iPhoneApp者先。</p> <p>5、有flutter开发经验者加分。</p> <h3 id=\"后端开发工程师\"><a href=\"#后端开发工程师\" class=\"header-anchor\">#</a> 后端开发工程师</h3> <p><strong>职位描述</strong></p> <p>1、主导或参与系统设计、研发、部署等相关工作；</p> <p>2、研发基础服务组件，解决共性需求，减少重复开发与运维；</p> <p>3、有较强的系统问题分析经验和能力，能够解决复杂的系统问题；</p> <p>4、参与部分生产系统维护工作，解决生产系统问题及进行系统调优。</p> <p><strong>职位要求</strong></p> <p>1、本科及以上学历，计算机相关专业；</p> <p>2、热爱计算机科学和互联网技术，精通至少一门编程语言，包括但不仅限于：Java、C、C++、PHP、 Python、Go；</p> <p>3、掌握扎实的计算机基础知识，深入理解数据结构、算法和操作系统知识；</p> <p>4、有优秀的逻辑分析能力，能够对业务逻辑进行合理的抽象和拆分；</p> <p>5、有强烈的求知欲，优秀的学习和沟通能力。</p> <h2 id=\"返回书籍菜单列表\"><a href=\"#返回书籍菜单列表\" class=\"header-anchor\">#</a> <a href=\"/index\">返回书籍菜单列表</a></h2></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/175.891c0a2f.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/next.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>下一步 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/176.93a74e23.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"下一步\"><a href=\"#下一步\" class=\"header-anchor\">#</a> 下一步</h1> <h3 id=\"其它平台\"><a href=\"#其它平台\" class=\"header-anchor\">#</a> 其它平台</h3> <p>本书主要讲的是Flutter在移动端开发</p> <ul><li></li></ul></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/176.93a74e23.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/preface.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>前言 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/177.20c492a7.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"前言\"><a href=\"#前言\" class=\"header-anchor\">#</a> 前言</h1> <h3 id=\"缘起\"><a href=\"#缘起\" class=\"header-anchor\">#</a> 缘起</h3> <p>在全球，随着Flutter被越来越多的知名公司应用在自己的商业APP中，Flutter这门新技术也逐渐进入了移动开发者的视野，尤其是当Google在2018年IO大会上发布了第一个Preview版本后，国内刮起来一股学习Flutter的热潮。</p> <p>在Flutter发布之初，当时，我在看完Flutter原理介绍后，就对它产生了浓厚的兴趣。当时笔者身边也一些人比较关注Flutter，我也被经常问到关于Flutter的一些问题，比如Flutter怎么样？和RN有什么区别？Flutter为什么要用Dart？当时也听到了一些批评的声音，比如有些人说Flutter只是重复造轮子，没啥亮点、Flutter最大的缺点就是使用了Dart语言等。在听到这些问题及论调后，我深知这是对Flutter的不了解而造成的，这和当时国内缺乏Flutter中文文档和教程有直接关系，很多人对Flutter的了解都只停留在Google的发布会介绍（有中文翻译）。</p> <p>在笔者深入的了解Flutter后，深知Flutter必将是一个会改变移动开发格局的里程碑级作品，它从设计之初就对性能和开发效率兼顾，并且借鉴了React（一个Web开发框架）的响应式的UI框架设计思想等，总之，很难用一两句话说完Flutter的优点，同时我也很快成为了Flutter的路转粉。</p> <p>为了更好的帮助中国开发者了解这门新技术，我在2018年初开始翻译Flutter官网文档，同年4月份上线了<a href=\"https://flutterchina.club/\" target=\"_blank\" rel=\"noopener noreferrer\">Flutter中文网<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>，上线后反响很强烈，Flutter中文网也很快被传播开，百度搜索排名迅速蹿升到前三，截止目前，Flutter中文官网日PV在7万左右，每日独立访问人数近一万多。</p> <p>虽然Flutter中文网给中国开发者提供了很好的第一手了解Flutter的资料，但是笔者还会经常遇到一些对Flutter技术处于围观而不愿尝试的开发者。这主要是因为当时Flutter在国内没有成功案例，再加上新技术都有学习成本，所以即使有文档，也会有一些开发者犹豫是否来学习。为了解决这部分开发者的疑虑，我就想如果能用Flutter开发一个完整的APP发布到应用商店，这样开发者就可以在犹豫的时候可以先实际感受一下Flutter应用，这样有个直观的了解后，就会容易做出判断，为此，我开发了<a href=\"https://flutterchina.club/app/gm.html\" target=\"_blank\" rel=\"noopener noreferrer\">Gitme<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>，它是一个Github客户端，它支持了源码浏览、Issue、Label等Github的大多数功能，到目前为止，通过Gitme登录过Github账号的用户有8000多人，日活用户有1000人。更重要的是，有很多人正是用了Gitme后，才来学Flutter的。</p> <p>无论是做Flutter中文网，还是写Gitme，主要目标都是帮助开发者学习Flutter，同时消除围观开发者疑虑。但当开发者们真正开始动手时，Flutter的生态问题就变得尤为突出。由于在2018年初Flutter刚起步时，很多基础的包和库都是空白，少数已有的一些库也大都是预览版（未到1.0），存在很多bug。这个状况不是一两个人花一两天能搞定的，这是需要聚整个Flutter开发者社群之力，耗费数年时间才可能有所改善。因此，在2018年4月份，我以Flutter中文网名义发起了Flutter开源计划，该计划主要是开发一些常用的包来丰富Flutter生态，帮助开发者提高开发效率。自在github建立<a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\">Flutter中文开发者社区官方账号<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>以来，前后开源了dio、cookieJar、flukit等多个项目，而dio在开源两周后，就迅速成为Flutter第三方包中Star排名第一的开源库。</p> <p>虽然做的事情已经够多了，但是仍有一些很有必要去做的事情，由于时间原因，一直被搁置。</p> <p>随着学习Flutter的人越来越多，一部分开发者通过查看官网的文档就能入门，但也有很多开发者感觉学习时仍然有些吃力，主要原因有两个，首先官网的文档主要是为了引导开发者快速上手的，讲的并不是很细；其次是我们虽然翻译了官方文档，但是对于Flutter SDK文档并没有翻译，而在开发中遇到的一些具体问题通常都得去查看SDK文档。所以，要解决这两个问题，必须得有一个系统化的Flutter教程，它不仅可以快速引导开发者入门，而且也能触及到一些细节和原理，最好也能提供一些学习和研究Flutter的方法。因此，如果能有一本能系统地介绍Flutter的书籍便是便是非常棒的！但是，当时没有一本关于Flutter的中文书籍，因此，我便计划写一本能由浅入深、系统介绍Flutter的书。2018年12月，《Flutter实战》完成初稿，并在Github上开源，同时上线了《Flutter实战》电子书官网（https://book.flutterchina.club/） ，至今每天有3000多人在线浏览本书。那为什么不直接出版？如果直接出版，不仅有稿费，而且也能保护知识产权，而直接开源，不就就只能当雷锋了？其实，无论是做中文网、写Gitme、做Flutter开源项目，我的出发点都是为了能帮助中国开发者了解、学习Flutter，而这是一件非常有意义的事，只要坚持做对别人有价值的事，那么上帝迟早会奖赏你；当然我们也在网站中留了打赏的按钮，如果读者觉得有帮助，可以扫码打赏，请笔者喝一杯咖啡。另外由于成书比较仓促，当时书中也有很多错误，开源后，有很多读者提PR来纠正书中的错别字，时至今日，有78名开发者为本书提过PR，我在此，衷心的感谢你们。</p> <p>起初，我没有出版实体书的打算，当时我以为开发者直接通过在线访问本书官网即方便又不用付费，何乐而不为。但在本书上线后，很多读者来添加微信好友，表示非常有收益，很期待出版纸质书，甚至有比较热心的读者想提前付定金预定！我理解，这是大家对我所做之事的认可和鼓励。考虑到，确实有一部分读者，尤其是还没毕业的同学，可能更喜欢通过书籍去学习，为此，我已经和机械工业出版社合作，目前本书纸质版正在出版中，敬请关注。</p> <h3 id=\"本书组织结构\"><a href=\"#本书组织结构\" class=\"header-anchor\">#</a> 本书组织结构</h3> <p>本书采用由浅入深的方式介绍Flutter技术原理，分为三篇，总计15章，各篇的主要内容如下。</p> <ul><li>第一篇，入门篇（第1章~第7章），包括Flutter技术的出现背景和简介、Flutter的各种类型的Widget以及如何构建UI。通过学习本章，读者可以掌握如何使用Flutter来构建UI界面。</li> <li>第二篇，进阶篇（第8章~第14章），包括Flutter中的事件机制、动画、自定义组件、文件和网络、插件、国际化以及Flutter核心原理等。通过本章内容，读者可以对Flutter整体构建及原理有一个深入的认识。</li> <li>第三篇，实例篇（第15张），本章主要通过一个简版的Github APP来将前面介绍的内容串起来，让开发者对一个完整的Flutter APP开发流有个了解。</li></ul> <p>由于Flutter的很多知识点是相互交织的，很难将它们彻底划分开，所以本书中也难免会出现一些在前面章节会使用在后面章节的场景，比如我们在入门篇介绍进度指示器时会用到在进阶篇中才介绍的动画相关知识。本书中对于这种情况会在相应的章节进行说明。读者可以直接跳到后面相应知识点章节阅读后再返回，也可以先有个印象，待学习到后面相关章节后再回头来看。</p> <h3 id=\"本书特色\"><a href=\"#本书特色\" class=\"header-anchor\">#</a> 本书特色</h3> <p>笔者在大学时读过候捷（真名侯俊杰）写的一些C++相关书籍，在他的《深入浅出MFC》一书中，有一句话我印象非常深 “唯有深入，方能浅出”。我非常认同这句话，对于一门技术，只有了解的深入，才能用最浅显、通俗的话语描述出。我在写作本书时，深入浅出就是一个主要目标。所以，本书的目标不仅是想告诉读者如何使用Flutter，而且也非常关注各个知识点的底层实现以及设计思想。从本书章节划分上来看，入门篇为“浅出”，进阶篇则是“深入”。另外由于PC客户端开发、移动开发、Web开发这些经验我都有，而Flutter本质上是一个UI系统，而UI系统的设计和实现在”大前端“下有很多相通之处，所以在本书中的一些知识点我也会对比一些其他UI系统（主要是Android或Web）相应的实现，便于有相关开发经验的读者对比理解。</p> <h3 id=\"本书读者对象\"><a href=\"#本书读者对象\" class=\"header-anchor\">#</a> 本书读者对象</h3> <ul><li>读者至少熟悉一种编程语言。</li> <li>读者最好接触过PC客户端、移动开发或Web前端开发中的一种。</li> <li>本书不适合做为编程的入门读物。</li></ul> <h3 id=\"关于随书源码\"><a href=\"#关于随书源码\" class=\"header-anchor\">#</a> 关于随书源码</h3> <p>由于篇幅所限，本书中大多数示例代码都只是部分核心代码，读者可以去https://github.com/wendux/flutter_in_action_source_code 查看</p> <h3 id=\"勘误\"><a href=\"#勘误\" class=\"header-anchor\">#</a> 勘误</h3> <p>由于Flutter SDK在不断更新，本书中的部分内容（如类的继承关系、参数等）可能会和新版本的Flutter不一致，读者以最新的Flutter SDK为准。另外，由于时间仓促，书中难免有错误之处，如果发现错误，可以在本书Github项目issue列表中去反馈，地址是https://github.com/flutterchina/flutter-in-action/issues 。</p> <h3 id=\"致谢\"><a href=\"#致谢\" class=\"header-anchor\">#</a> 致谢</h3> <p>感谢一直以来支持Flutter中文网、Flutter开源项目的人以及所有对本书提过PR的人，正是因为有你们，才有这本书。另外尤其感谢给本书打赏过的同学，你们的支持给了我很大的鼓励。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/177.20c492a7.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/reference.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>参考文献 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/178.a9c56f1f.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"参考文献\"><a href=\"#参考文献\" class=\"header-anchor\">#</a> 参考文献</h1> <ul><li><p>React Native官网：https://facebook.github.io/react-native/</p></li> <li><p>Weex：https://weex.apache.org/zh/guide/introduction.html</p></li> <li><p>快应用：https://www.quickapp.cn/</p></li> <li><p>QT for mobile：https://www.qt.io/mobile-app-development/</p></li> <li><p>Flutter官网：https://flutter.dev/</p></li> <li><p>Flutter中文网社区：https://flutterchina.club/docs/</p></li> <li><p>Dart Packages官网：https://pub.dev/</p></li> <li><p>Flutter中文开发者社区开源项目：https://github.com/flutterchina</p></li> <li><p>Material Design：https://material.io/</p></li> <li><p>Github 开发者中心官网：https://developer.github.com/v3/</p></li> <li><p>Android开发者中心官网：https://developer.android.google.cn/</p></li> <li><p>Apple开发者中心官网：https://developer.apple.com/</p></li></ul></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/178.a9c56f1f.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/summary.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>Summary | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/179.b42bb139.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"summary\"><a href=\"#summary\" class=\"header-anchor\">#</a> Summary</h1> <ul><li><a href=\"/\" class=\"router-link-active\">简介</a></li> <li><a href=\"/intro.html\">前言</a></li></ul> <h2 id=\"入门篇\"><a href=\"#入门篇\" class=\"header-anchor\">#</a> 入门篇</h2> <ul><li><p><a href=\"/chapter1/\">第一章：起步</a></p> <ul><li><a href=\"/chapter1/mobile_development_intro.html\">1.1：移动开发技术简介</a></li> <li><a href=\"/chapter1/flutter_intro.html\">1.2：初识Flutter</a></li> <li><a href=\"/chapter1/install_flutter.html\">1.3：搭建Flutter开发环境</a></li> <li><a href=\"/chapter1/dart.html\">1.4：Dart语言简介</a></li></ul></li> <li><p><a href=\"/chapter2/\">第二章：第一个Flutter应用</a></p> <ul><li><a href=\"/chapter2/first_flutter_app.html\">2.1：计数器示例</a></li> <li><a href=\"/chapter2/flutter_router.html\">2.2：路由管理</a></li> <li><a href=\"/chapter2/flutter_package_mgr.html\">2.3：包管理</a></li> <li><a href=\"/chapter2/flutter_assets_mgr.html\">2.4：资源管理</a></li> <li><a href=\"/chapter2/flutter_app_debug.html\">2.5：调试Flutter APP</a></li> <li><a href=\"/chapter2/thread_model_and_error_report.html\">2.6：Dart线程模型及异常捕获</a></li></ul></li> <li><p><a href=\"/chapter3/\">第三章：基础组件</a></p> <ul><li><a href=\"/chapter3/flutter_widget_intro.html\">3.1：Widget简介</a></li> <li><a href=\"/chapter3/state_manage.html\">3.2：状态管理</a></li> <li><a href=\"/chapter3/text.html\">3.3：文本、字体样式</a></li> <li><a href=\"/chapter3/buttons.html\">3.4：按钮</a></li> <li><a href=\"/chapter3/img_and_icon.html\">3.5：图片和Icon</a></li> <li><a href=\"/chapter3/radio_and_checkbox.html\">3.6：单选框和复选框</a></li> <li><a href=\"/chapter3/input_and_form.html\">3.7：输入框和表单</a></li> <li><a href=\"/chapter3/progress.html\">3.8：进度指示器</a></li></ul></li> <li><p><a href=\"/chapter4/\">第四章：布局类组件</a></p> <ul><li><a href=\"/chapter4/intro.html\">4.1：布局类组件简介</a></li> <li><a href=\"/chapter4/row_and_column.html\">4.2：线性布局（Row、Column）</a></li> <li><a href=\"/chapter4/flex.html\">4.3：弹性布局（Flex）</a></li> <li><a href=\"/chapter4/wrap_and_flow.html\">4.4：流式布局（Wrap、Flow）</a></li> <li><a href=\"/chapter4/stack.html\">4.5：层叠布局（Stack、Positioned）</a></li> <li><a href=\"/chapter4/alignment.html\">4.6：对齐与相对定位（Align）</a></li></ul></li> <li><p><a href=\"/chapter5/\">第五章：容器类组件</a></p> <ul><li><a href=\"/chapter5/padding.html\">5.1：填充（Padding）</a></li> <li><a href=\"/chapter5/constrainedbox_and_sizebox.html\">5.2：尺寸限制类容器（ConstrainedBox等）</a></li> <li><a href=\"/chapter5/decoratedbox.html\">5.3：装饰容器（DecoratedBox）</a></li> <li><a href=\"/chapter5/transform.html\">5.4：变换（Transform）</a></li> <li><a href=\"/chapter5/container.html\">5.5：Container容器</a></li> <li><a href=\"/chapter5/material_scaffold.html\">5.6：Scaffold、TabBar、底部导航</a></li> <li><a href=\"/chapter5/clip.html\">5.7：剪裁（Clip）</a></li></ul></li> <li><p><a href=\"/chapter6/\">第六章：可滚动组件</a></p> <ul><li><a href=\"/chapter6/intro.html\">6.1：可滚动组件简介</a></li> <li><a href=\"/chapter6/single_child_scrollview.html\">6.2：SingleChildScrollView</a></li> <li><a href=\"/chapter6/listview.html\">6.3：ListView</a></li> <li><a href=\"/chapter6/gridview.html\">6.4：GridView</a></li> <li><a href=\"/chapter6/custom_scrollview.html\">6.5：CustomScrollView</a></li> <li><a href=\"/chapter6/scroll_controller.html\">6.6：滚动监听及控制（ScrollController）</a></li></ul></li> <li><p><a href=\"/chapter7/\">第七章：功能型组件</a></p> <ul><li><a href=\"/chapter7/willpopscope.html\">7.1：导航返回拦截（WillPopScope）</a></li> <li><a href=\"/chapter7/inherited_widget.html\">7.2：数据共享（InheritedWidget）</a></li> <li><a href=\"/chapter7/provider.html\">7.3： 跨组件状态共享（Provider）</a></li> <li><a href=\"/chapter7/theme.html\">7.4：颜色和主题（Theme）</a></li> <li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\">7.5：异步UI更新（FutureBuilder、StreamBuilder）</a></li> <li><a href=\"/chapter7/dailog.html\">7.6：对话框详解</a></li></ul></li></ul> <h2 id=\"进阶篇\"><a href=\"#进阶篇\" class=\"header-anchor\">#</a> 进阶篇</h2> <ul><li><p><a href=\"/chapter8/\">第八章：事件处理与通知</a></p> <ul><li><a href=\"/chapter8/listener.html\">8.1：原始指针事件处理</a></li> <li><a href=\"/chapter8/gesture.html\">8.2：手势识别</a></li> <li><a href=\"/chapter8/eventbus.html\">8.3：全局事件总线</a></li> <li><a href=\"/chapter8/notification.html\">8.4：通知(Notification)</a></li></ul></li> <li><p><a href=\"/chapter9/\">第九章：动画</a></p> <ul><li><a href=\"/chapter9/intro.html\">9.1：Flutter动画简介</a></li> <li><a href=\"/chapter9/animation_structure.html\">9.2：动画结构</a></li> <li><a href=\"/chapter9/route_transition.html\">9.3：自定义路由过渡动画</a></li> <li><a href=\"/chapter9/hero.html\">9.4：Hero动画</a></li> <li><a href=\"/chapter9/stagger_animation.html\">9.5：交织动画</a></li> <li><a href=\"/chapter9/animated_switcher.html\">9.6：通用“动画切换”组件（AnimatedSwitcher）</a></li> <li><a href=\"/chapter9/animated_widgets.html\">9.7：动画过渡组件</a></li></ul></li> <li><p><a href=\"/chapter10/\">第十章：自定义组件</a></p> <ul><li><a href=\"/chapter10/intro.html\">10.1：自定义组件方法简介</a></li> <li><a href=\"/chapter10/combine.html\">10.2：组合现有组件</a></li> <li><a href=\"/chapter10/turn_box.html\">10.3：组合实例：TurnBox</a></li> <li><a href=\"/chapter10/custom_paint.html\">10.4：自绘组件（CustomPaint与Canvas）</a></li> <li><a href=\"/chapter10/gradient_circular_progress_demo.html\">10.5：自绘实例：圆形渐变进度条(自绘)</a></li></ul></li> <li><p><a href=\"/chapter11/\">第十一章：文件操作与网络请求</a></p> <ul><li><a href=\"/chapter11/file_operation.html\">11.1：文件操作</a></li> <li><a href=\"/chapter11/http.html\">11.2：Http请求-HttpClient</a></li> <li><a href=\"/chapter11/dio.html\">11.3：Http请求-Dio package</a></li> <li><a href=\"/chapter11/download_with_chunks.html\">11.4：实例：Http分块下载</a></li> <li><a href=\"/chapter11/websocket.html\">11.5：WebSocket</a></li> <li><a href=\"/chapter11/socket.html\">11.6：使用Socket API</a></li> <li><a href=\"/chapter11/json_model.html\">11.7：Json转Dart Model类</a></li></ul></li> <li><p><a href=\"/chapter12/\">第十二章：包与插件</a></p> <ul><li><a href=\"/chapter12/develop_package.html\">12.1：开发package</a></li> <li><a href=\"/chapter12/platform-channel.html\">12.2：平台通道简介</a></li> <li><a href=\"/chapter12/develop_plugin.html\">12.3：开发Flutter插件</a></li> <li><a href=\"/chapter12/android_implement.html\">12.4：插件开发：实现Android端API</a></li> <li><a href=\"/chapter12/ios_implement.html\">12.5：插件开发：实现IOS端API</a></li> <li><a href=\"/chapter12/texture_platformview.html\">12.6：Texture和PlatformView</a></li></ul></li> <li><p><a href=\"/chapter13/\">第十三章：国际化</a></p> <ul><li><a href=\"/chapter13/multi_languages_support.html\">13.1：让App支持多语言</a></li> <li><a href=\"/chapter13/locallization_implement.html\">13.2：实现Localizations</a></li> <li><a href=\"/chapter13/intl.html\">13.3：使用Intl包</a></li> <li><a href=\"/chapter13/faq.html\">13.4：国际化常见问题</a></li></ul></li> <li><p><a href=\"/chapter14/\">第十四章：Flutter核心原理</a></p> <ul><li><a href=\"/chapter14/flutter_ui_system.html\">14.1：Flutter UI系统</a></li> <li><a href=\"/chapter14/element_buildcontext.html\">14.2：Element和BuildContext</a></li> <li><a href=\"/chapter14/render_object.html\">14.3：RenderObject与RenderBox</a></li> <li><a href=\"/chapter14/flutter_app_startup.html\">14.4：Flutter从启动到显示</a></li> <li><a href=\"/chapter14/image_and_cache.html\">14.5：Flutter图片加载与缓存</a></li></ul></li></ul> <h2 id=\"实例篇\"><a href=\"#实例篇\" class=\"header-anchor\">#</a> 实例篇</h2> <ul><li><a href=\"/chapter15/intro.html\">第十五章：一个完整的Flutter应用</a> <ul><li><a href=\"/chapter15/intro.html\">15.1：应用简介</a></li> <li><a href=\"/chapter15/code_structure.html\">15.2：APP代码结构</a></li> <li><a href=\"/chapter15/models.html\">15.3：Model类定义</a></li> <li><a href=\"/chapter15/globals.html\">15.4：全局变量及共享状态</a></li> <li><a href=\"/chapter15/network.html\">15.5：网络请求封装</a></li> <li><a href=\"/chapter15/entry.html\">15.6：App入口及首页</a></li> <li><a href=\"/chapter15/login_page.html\">15.7：登录页</a></li> <li><a href=\"/chapter15/language_and_theme_setting.html\">15.8：多语言和多主题</a></li></ul></li></ul></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/179.b42bb139.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter1/dart.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>1.4 Dart语言简介 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/180.d048122a.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter1/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_1-4-dart语言简介\"><a href=\"#_1-4-dart语言简介\" class=\"header-anchor\">#</a> 1.4 Dart语言简介</h1> <p>在之前我们已经介绍过Dart语言的相关特性，读者可以翻看一下，如果读者已经熟悉Dart语法，可以跳过本节，如果你还不了解Dart，也不用担心，按照笔者经验，如果你有过其他编程语言经验（尤其是Java和JavaScript）的话会非常容易上手Dart。当然，如果你是iOS开发者，也不用担心，Dart中也有一些与Swift比较相似的特性，如命名参数等，笔者当时学习Dart时，只是花了一个小时，看完Dart官网的Language Tour，就开始动手写Flutter了。</p> <p>在笔者看来，Dart的设计目标应该是同时借鉴了Java和JavaScript。Dart在静态语法方面和Java非常相似，如类型定义、函数声明、泛型等，而在动态特性方面又和JavaScript很像，如函数式特性、异步支持等。除了融合Java和JavaScript语言之所长之外，Dart也具有一些其它具有表现力的语法，如可选命名参数、<code>..</code>（级联运算符）和<code>?.</code>（条件成员访问运算符）以及<code>??</code>（判空赋值运算符）。其实，对编程语言了解比较多的读者会发现，在Dart中其实看到的不仅有Java和JavaScript的影子，它还具有其它编程语言中的身影，如命名参数在Objective-C和Swift中早就很普遍，而<code>??</code>操作符在PHP 7.0语法中就已经存在了，因此我们可以看到Google对Dart语言给予厚望，是想把Dart打造成一门集百家之所长的编程语言。</p> <p>接下来，我们先对Dart语法做一个简单的介绍，然后再将Dart与JavaScript和Java做一个简要的对比，方便读者更好的理解。</p> <blockquote><p>注意：由于本书并非专门介绍Dart语言的书籍，所以本章主要会介绍一下在Flutter开发中常用的语法特性，如果想更多了解Dart，读者可以去Dart官网学习，现在互联网上Dart相关资料已经很多了。另外Dart 2.0已经正式发布，所以本书所有示例均采用Dart 2.0语法。</p></blockquote> <h2 id=\"_1-4-1-变量声明\"><a href=\"#_1-4-1-变量声明\" class=\"header-anchor\">#</a> 1.4.1 变量声明</h2> <ol><li><p><strong>var</strong></p> <p>类似于JavaScript中的<code>var</code>，它可以接收任何类型的变量，但最大的不同是Dart中var变量一旦赋值，类型便会确定，则不能再改变其类型，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">var</span> t<span class=\"token punctuation\">;</span>\nt <span class=\"token operator\">=</span> <span class=\"token string\">&quot;hi world&quot;</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">// 下面代码在dart中会报错，因为变量t的类型已经确定为String，</span>\n<span class=\"token comment\">// 类型一旦确定后则不能再更改其类型。</span>\nt <span class=\"token operator\">=</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>上面的代码在JavaScript是没有问题的，前端开发者需要注意一下，之所以有此差异是因为Dart本身是一个强类型语言，任何变量都是有确定类型的，在Dart中，当用<code>var</code>声明一个变量后，Dart在编译时会根据第一次赋值数据的类型来推断其类型，编译结束后其类型就已经被确定，而JavaScript是纯粹的弱类型脚本语言，var只是变量的声明方式而已。</p></li> <li><p><strong>dynamic</strong>和<strong>Object</strong></p> <p><code>Object</code> 是Dart所有对象的根基类，也就是说所有类型都是<code>Object</code>的子类(包括Function和Null)，所以任何类型的数据都可以赋值给<code>Object</code>声明的对象.\n<code>dynamic</code>与<code>var</code>一样都是关键词,声明的变量可以赋值任意对象。\n而<code>dynamic</code>与<code>Object</code>相同之处在于,他们声明的变量可以在后期改变赋值类型。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">dynamic</span> t<span class=\"token punctuation\">;</span>\nObject x<span class=\"token punctuation\">;</span>\nt <span class=\"token operator\">=</span> <span class=\"token string\">&quot;hi world&quot;</span><span class=\"token punctuation\">;</span>\nx <span class=\"token operator\">=</span> <span class=\"token string\">'Hello Object'</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">//下面代码没有问题</span>\nt <span class=\"token operator\">=</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">;</span>\nx <span class=\"token operator\">=</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p><code>dynamic</code>与<code>Object</code>不同的是,<code>dynamic</code>声明的对象编译器会提供所有可能的组合,\n而<code>Object</code>声明的对象只能使用Object的属性与方法, 否则编译器会报错。如:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code> <span class=\"token keyword\">dynamic</span> a<span class=\"token punctuation\">;</span>\n Object b<span class=\"token punctuation\">;</span>\n <span class=\"token function\">main</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n     a <span class=\"token operator\">=</span> <span class=\"token string\">&quot;&quot;</span><span class=\"token punctuation\">;</span>\n     b <span class=\"token operator\">=</span> <span class=\"token string\">&quot;&quot;</span><span class=\"token punctuation\">;</span>\n     <span class=\"token function\">printLengths</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n <span class=\"token punctuation\">}</span>   \n\n <span class=\"token function\">printLengths</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n     <span class=\"token comment\">// no warning</span>\n     <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>a<span class=\"token punctuation\">.</span>length<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n     <span class=\"token comment\">// warning:</span>\n     <span class=\"token comment\">// The getter 'length' is not defined for the class 'Object'</span>\n     <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>b<span class=\"token punctuation\">.</span>length<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n <span class=\"token punctuation\">}</span>\n</code></pre></div><p>变量a不会报错, 变量b编译器会报错</p> <p><code>dynamic</code>的这个特性与<code>Objective-C</code>中的<code>id</code>作用很像.\n<code>dynamic</code>的这个特点使得我们在使用它时需要格外注意,这很容易引入一个运行时错误.</p></li> <li><p><strong>final</strong>和<strong>const</strong></p> <p>如果您从未打算更改一个变量，那么使用 <code>final</code> 或 <code>const</code>，不是<code>var</code>，也不是一个类型。 一个 <code>final</code> 变量只能被设置一次，两者区别在于：<code>const</code> 变量是一个编译时常量，<code>final</code>变量在第一次使用时被初始化。被<code>final</code>或者<code>const</code>修饰的变量，变量类型可以省略，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//可以省略String这个类型声明</span>\n<span class=\"token keyword\">final</span> str <span class=\"token operator\">=</span> <span class=\"token string\">&quot;hi world&quot;</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">//final String str = &quot;hi world&quot;; </span>\n<span class=\"token keyword\">const</span> str1 <span class=\"token operator\">=</span> <span class=\"token string\">&quot;hi world&quot;</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">//const String str1 = &quot;hi world&quot;;</span>\n</code></pre></div></li></ol> <h2 id=\"_1-4-2-函数\"><a href=\"#_1-4-2-函数\" class=\"header-anchor\">#</a> 1.4.2 函数</h2> <p>Dart是一种真正的面向对象的语言，所以即使是函数也是对象，并且有一个类型<strong>Function</strong>。这意味着函数可以赋值给变量或作为参数传递给其他函数，这是函数式编程的典型特征。</p> <ol><li><p>函数声明</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>bool <span class=\"token function\">isNoble</span><span class=\"token punctuation\">(</span>int atomicNumber<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> _nobleGases<span class=\"token punctuation\">[</span>atomicNumber<span class=\"token punctuation\">]</span> <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>Dart函数声明如果没有显式声明返回值类型时会默认当做<code>dynamic</code>处理，注意，函数返回值没有类型推断：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">typedef</span> bool <span class=\"token function\">CALLBACK</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">//不指定返回类型，此时默认为dynamic，不是bool</span>\n<span class=\"token function\">isNoble</span><span class=\"token punctuation\">(</span>int atomicNumber<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> _nobleGases<span class=\"token punctuation\">[</span>atomicNumber<span class=\"token punctuation\">]</span> <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">void</span> <span class=\"token function\">test</span><span class=\"token punctuation\">(</span>CALLBACK cb<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n   <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token function\">cb</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> \n<span class=\"token punctuation\">}</span>\n<span class=\"token comment\">//报错，isNoble不是bool类型</span>\n<span class=\"token function\">test</span><span class=\"token punctuation\">(</span>isNoble<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div></li> <li><p>对于只包含一个表达式的函数，可以使用简写语法</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>bool isNoble <span class=\"token punctuation\">(</span>int atomicNumber<span class=\"token punctuation\">)</span><span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _nobleGases <span class=\"token punctuation\">[</span> atomicNumber <span class=\"token punctuation\">]</span> ！<span class=\"token operator\">=</span> <span class=\"token keyword\">null</span> <span class=\"token punctuation\">;</span>   \n</code></pre></div></li> <li><p>函数作为变量</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">var</span> say <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>str<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n  <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>str<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n<span class=\"token function\">say</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;hi world&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div></li> <li><p>函数作为参数传递</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">execute</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">var</span> callback<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">callback</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n<span class=\"token function\">execute</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;xxx&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n</code></pre></div></li> <li><p>可选的位置参数</p> <p>包装一组函数参数，用[]标记为可选的位置参数，并放在参数列表的最后面：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>String <span class=\"token function\">say</span><span class=\"token punctuation\">(</span>String from<span class=\"token punctuation\">,</span> String msg<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>String device<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">var</span> result <span class=\"token operator\">=</span> <span class=\"token string\">'$from says $msg'</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>device <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    result <span class=\"token operator\">=</span> <span class=\"token string\">'$result with a $device'</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token keyword\">return</span> result<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>下面是一个不带可选参数调用这个函数的例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">say</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Bob'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Howdy'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//结果是： Bob says Howdy</span>\n</code></pre></div><p>下面是用第三个参数调用这个函数的例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">say</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Bob'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Howdy'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'smoke signal'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//结果是：Bob says Howdy with a smoke signal</span>\n</code></pre></div></li> <li><p>可选的命名参数</p> <p>定义函数时，使用{param1, param2, …}，放在参数列表的最后面，用于指定命名参数。例如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//设置[bold]和[hidden]标志</span>\n<span class=\"token keyword\">void</span> <span class=\"token function\">enableFlags</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>bool bold<span class=\"token punctuation\">,</span> bool hidden<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// ... </span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>调用函数时，可以使用指定命名参数。例如：<code>paramName: value</code></p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">enableFlags</span><span class=\"token punctuation\">(</span>bold<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span> hidden<span class=\"token punctuation\">:</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>可选命名参数在Flutter中使用非常多。</p> <p><strong>注意，不能同时使用可选的位置参数和可选的命名参数</strong></p></li></ol> <h2 id=\"_1-4-3-异步支持\"><a href=\"#_1-4-3-异步支持\" class=\"header-anchor\">#</a> 1.4.3 异步支持</h2> <p>Dart类库有非常多的返回<code>Future</code>或者<code>Stream</code>对象的函数。 这些函数被称为<strong>异步函数</strong>：它们只会在设置好一些耗时操作之后返回，比如像 IO操作。而不是等到这个操作完成。</p> <p><code>async</code>和<code>await</code>关键词支持了异步编程，允许您写出和同步代码很像的异步代码。</p> <h3 id=\"future\"><a href=\"#future\" class=\"header-anchor\">#</a> Future</h3> <p><code>Future</code>与JavaScript中的<code>Promise</code>非常相似，表示一个异步操作的最终完成（或失败）及其结果值的表示。简单来说，它就是用于处理异步操作的，异步处理成功了就执行成功的操作，异步处理失败了就捕获错误或者停止后续操作。一个Future只会对应一个结果，要么成功，要么失败。</p> <p>由于本身功能较多，这里我们只介绍其常用的API及特性。还有，请记住，<code>Future</code> 的所有API的返回值仍然是一个<code>Future</code>对象，所以可以很方便的进行链式调用。</p> <h4 id=\"future-then\"><a href=\"#future-then\" class=\"header-anchor\">#</a> Future.then</h4> <p>为了方便示例，在本例中我们使用<code>Future.delayed</code> 创建了一个延时任务（实际场景会是一个真正的耗时任务，比如一次网络请求），即2秒后返回结果字符串&quot;hi world!&quot;，然后我们在<code>then</code>中接收异步结果并打印结果，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Future<span class=\"token punctuation\">.</span><span class=\"token function\">delayed</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n   <span class=\"token keyword\">return</span> <span class=\"token string\">&quot;hi world!&quot;</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n   <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><h4 id=\"future-catcherror\"><a href=\"#future-catcherror\" class=\"header-anchor\">#</a> Future.catchError</h4> <p>如果异步任务发生错误，我们可以在<code>catchError</code>中捕获错误，我们将上面示例改为：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Future<span class=\"token punctuation\">.</span><span class=\"token function\">delayed</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n   <span class=\"token comment\">//return &quot;hi world!&quot;;</span>\n   <span class=\"token keyword\">throw</span> <span class=\"token function\">AssertionError</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Error&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>  \n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n   <span class=\"token comment\">//执行成功会走到这里  </span>\n   <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;success&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">catchError</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n   <span class=\"token comment\">//执行失败会走到这里  </span>\n   <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>在本示例中，我们在异步任务中抛出了一个异常，<code>then</code>的回调函数将不会被执行，取而代之的是 <code>catchError</code>回调函数将被调用；但是，并不是只有 <code>catchError</code>回调才能捕获错误，<code>then</code>方法还有一个可选参数<code>onError</code>，我们也可以它来捕获异常：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Future<span class=\"token punctuation\">.</span><span class=\"token function\">delayed</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n\t<span class=\"token comment\">//return &quot;hi world!&quot;;</span>\n\t<span class=\"token keyword\">throw</span> <span class=\"token function\">AssertionError</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Error&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n\t<span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;success&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> onError<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n\t<span class=\"token function\">print</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><h4 id=\"future-whencomplete\"><a href=\"#future-whencomplete\" class=\"header-anchor\">#</a> Future.whenComplete</h4> <p>有些时候，我们会遇到无论异步任务执行成功或失败都需要做一些事的场景，比如在网络请求前弹出加载对话框，在请求结束后关闭对话框。这种场景，有两种方法，第一种是分别在<code>then</code>或<code>catch</code>中关闭一下对话框，第二种就是使用<code>Future</code>的<code>whenComplete</code>回调，我们将上面示例改一下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Future<span class=\"token punctuation\">.</span><span class=\"token function\">delayed</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n   <span class=\"token comment\">//return &quot;hi world!&quot;;</span>\n   <span class=\"token keyword\">throw</span> <span class=\"token function\">AssertionError</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Error&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n   <span class=\"token comment\">//执行成功会走到这里 </span>\n   <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">catchError</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n   <span class=\"token comment\">//执行失败会走到这里   </span>\n   <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">whenComplete</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n   <span class=\"token comment\">//无论成功或失败都会走到这里</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><h4 id=\"future-wait\"><a href=\"#future-wait\" class=\"header-anchor\">#</a> Future.wait</h4> <p>有些时候，我们需要等待多个异步任务都执行结束后才进行一些操作，比如我们有一个界面，需要先分别从两个网络接口获取数据，获取成功后，我们需要将两个接口数据进行特定的处理后再显示到UI界面上，应该怎么做？答案是<code>Future.wait</code>，它接受一个<code>Future</code>数组参数，只有数组中所有<code>Future</code>都执行成功后，才会触发<code>then</code>的成功回调，只要有一个<code>Future</code>执行失败，就会触发错误回调。下面，我们通过模拟<code>Future.delayed</code> 来模拟两个数据获取的异步任务，等两个异步任务都执行成功时，将两个异步任务的结果拼接打印出来，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Future<span class=\"token punctuation\">.</span><span class=\"token function\">wait</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span>\n  <span class=\"token comment\">// 2秒后返回结果  </span>\n  Future<span class=\"token punctuation\">.</span><span class=\"token function\">delayed</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token string\">&quot;hello&quot;</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token comment\">// 4秒后返回结果  </span>\n  Future<span class=\"token punctuation\">.</span><span class=\"token function\">delayed</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">4</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token string\">&quot; world&quot;</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>results<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n  <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>results<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token operator\">+</span>results<span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">catchError</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n  <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>执行上面代码，4秒后你会在控制台中看到“hello world”。</p> <h3 id=\"async-await\"><a href=\"#async-await\" class=\"header-anchor\">#</a> Async/await</h3> <p>Dart中的<code>async/await</code> 和JavaScript中的<code>async/await</code>功能和用法是一模一样的，如果你已经了解JavaScript中的<code>async/await</code>的用法，可以直接跳过本节。</p> <h4 id=\"回调地狱-callback-hell\"><a href=\"#回调地狱-callback-hell\" class=\"header-anchor\">#</a> 回调地狱(Callback Hell)</h4> <p>如果代码中有大量异步逻辑，并且出现大量异步任务依赖其它异步任务的结果时，必然会出现<code>Future.then</code>回调中套回调情况。举个例子，比如现在有个需求场景是用户先登录，登录成功后会获得用户ID，然后通过用户ID，再去请求用户个人信息，获取到用户个人信息后，为了使用方便，我们需要将其缓存在本地文件系统，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//先分别定义各个异步任务</span>\nFuture<span class=\"token operator\">&lt;</span>String<span class=\"token operator\">&gt;</span> <span class=\"token function\">login</span><span class=\"token punctuation\">(</span>String userName<span class=\"token punctuation\">,</span> String pwd<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n\t<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n    <span class=\"token comment\">//用户登录</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\nFuture<span class=\"token operator\">&lt;</span>String<span class=\"token operator\">&gt;</span> <span class=\"token function\">getUserInfo</span><span class=\"token punctuation\">(</span>String id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n\t<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n    <span class=\"token comment\">//获取用户信息 </span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\nFuture <span class=\"token function\">saveUserInfo</span><span class=\"token punctuation\">(</span>String userInfo<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n\t<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n\t<span class=\"token comment\">// 保存用户信息 </span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span> \n</code></pre></div><p>接下来，执行整个任务流：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">login</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;alice&quot;</span><span class=\"token punctuation\">,</span><span class=\"token string\">&quot;******&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n <span class=\"token comment\">//登录成功后通过，id获取用户信息    </span>\n <span class=\"token function\">getUserInfo</span><span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>userInfo<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//获取用户信息后保存 </span>\n    <span class=\"token function\">saveUserInfo</span><span class=\"token punctuation\">(</span>userInfo<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n       <span class=\"token comment\">//保存用户信息，接下来执行其它操作</span>\n        <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>可以感受一下，如果业务逻辑中有大量异步依赖的情况，将会出现上面这种在回调里面套回调的情况，过多的嵌套会导致的代码可读性下降以及出错率提高，并且非常难维护，这个问题被形象的称为<strong>回调地狱（Callback Hell）</strong>。回调地狱问题在之前JavaScript中非常突出，也是JavaScript被吐槽最多的点，但随着ECMAScript6和ECMAScript7标准发布后，这个问题得到了非常好的解决，而解决回调地狱的两大神器正是ECMAScript6引入了<code>Promise</code>，以及ECMAScript7中引入的<code>async/await</code>。 而在Dart中几乎是完全平移了JavaScript中的这两者：<code>Future</code>相当于<code>Promise</code>，而<code>async/await</code>连名字都没改。接下来我们看看通过<code>Future</code>和<code>async/await</code>如何消除上面示例中的嵌套问题。</p> <h5 id=\"使用future消除callback-hell\"><a href=\"#使用future消除callback-hell\" class=\"header-anchor\">#</a> 使用Future消除Callback Hell</h5> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">login</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;alice&quot;</span><span class=\"token punctuation\">,</span><span class=\"token string\">&quot;******&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n  \t<span class=\"token keyword\">return</span> <span class=\"token function\">getUserInfo</span><span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>userInfo<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">saveUserInfo</span><span class=\"token punctuation\">(</span>userInfo<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n   <span class=\"token comment\">//执行接下来的操作 </span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">catchError</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">//错误处理  </span>\n  <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>正如上文所述， <em>“<code>Future</code> 的所有API的返回值仍然是一个<code>Future</code>对象，所以可以很方便的进行链式调用”</em> ，如果在then中返回的是一个<code>Future</code>的话，该<code>future</code>会执行，执行结束后会触发后面的<code>then</code>回调，这样依次向下，就避免了层层嵌套。</p> <h5 id=\"使用async-await消除callback-hell\"><a href=\"#使用async-await消除callback-hell\" class=\"header-anchor\">#</a> 使用async/await消除callback hell</h5> <p>通过<code>Future</code>回调中再返回<code>Future</code>的方式虽然能避免层层嵌套，但是还是有一层回调，有没有一种方式能够让我们可以像写同步代码那样来执行异步任务而不使用回调的方式？答案是肯定的，这就要使用<code>async/await</code>了，下面我们先直接看代码，然后再解释，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">task</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n   <span class=\"token keyword\">try</span><span class=\"token punctuation\">{</span>\n    String id <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">login</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;alice&quot;</span><span class=\"token punctuation\">,</span><span class=\"token string\">&quot;******&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    String userInfo <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">getUserInfo</span><span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">await</span> <span class=\"token function\">saveUserInfo</span><span class=\"token punctuation\">(</span>userInfo<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//执行接下来的操作   </span>\n   <span class=\"token punctuation\">}</span> <span class=\"token keyword\">catch</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//错误处理   </span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>   \n   <span class=\"token punctuation\">}</span>  \n<span class=\"token punctuation\">}</span>\n</code></pre></div><ul><li><code>async</code>用来表示函数是异步的，定义的函数会返回一个<code>Future</code>对象，可以使用then方法添加回调函数。</li> <li><code>await</code> 后面是一个<code>Future</code>，表示等待该异步任务完成，异步完成后才会往下走；<code>await</code>必须出现在 <code>async</code> 函数内部。</li></ul> <p>可以看到，我们通过<code>async/await</code>将一个异步流用同步的代码表示出来了。</p> <blockquote><p>其实，无论是在JavaScript还是Dart中，<code>async/await</code>都只是一个语法糖，编译器或解释器最终都会将其转化为一个Promise（Future）的调用链。</p></blockquote> <h2 id=\"_1-4-4-stream\"><a href=\"#_1-4-4-stream\" class=\"header-anchor\">#</a> 1.4.4 Stream</h2> <p><code>Stream</code> 也是用于接收异步事件数据，和<code>Future</code> 不同的是，它可以接收多个异步操作的结果（成功或失败）。 也就是说，在执行异步任务时，可以通过多次触发成功或失败事件来传递结果数据或错误异常。 <code>Stream</code> 常用于会多次读取数据的异步任务场景，如网络内容下载、文件读写等。举个例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Stream<span class=\"token punctuation\">.</span><span class=\"token function\">fromFutures</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span>\n  <span class=\"token comment\">// 1秒后返回结果</span>\n  Future<span class=\"token punctuation\">.</span><span class=\"token function\">delayed</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token string\">&quot;hello 1&quot;</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token comment\">// 抛出一个异常</span>\n  Future<span class=\"token punctuation\">.</span><span class=\"token function\">delayed</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">throw</span> <span class=\"token function\">AssertionError</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Error&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token comment\">// 3秒后返回结果</span>\n  Future<span class=\"token punctuation\">.</span><span class=\"token function\">delayed</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">3</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token string\">&quot;hello 3&quot;</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">listen</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n   <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> onError<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n   <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">.</span>message<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>onDone<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>上面的代码依次会输出：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>I/flutter (17666): hello 1\nI/flutter (17666): Error\nI/flutter (17666): hello 3\n</code></pre></div><p>代码很简单，就不赘述了。</p> <blockquote><p>思考题：既然Stream可以接收多次事件，那能不能用Stream来实现一个订阅者模式的事件总线？</p></blockquote> <h2 id=\"_1-4-5-dart和java及javascript对比\"><a href=\"#_1-4-5-dart和java及javascript对比\" class=\"header-anchor\">#</a> 1.4.5 Dart和Java及JavaScript对比</h2> <p>通过上面介绍，相信你对Dart应该有了一个初步的印象，由于笔者平时也使用Java和JavaScript，下面笔者根据自己的经验，结合Java和JavaScript，谈一下自己的看法。</p> <blockquote><p>之所以将Dart与Java和JavaScript对比，是因为，这两者分别是强类型语言和弱类型语言的典型代表，并且Dart 语法中很多地方也都借鉴了Java和JavaScript。</p></blockquote> <h3 id=\"dart-vs-java\"><a href=\"#dart-vs-java\" class=\"header-anchor\">#</a> Dart vs Java</h3> <p>客观的来讲，Dart在语法层面确实比Java更有表现力；在VM层面，Dart VM在内存回收和吞吐量都进行了反复的优化，但具体的性能对比，笔者没有找到相关测试数据，但在笔者看来，只要Dart语言能流行，VM的性能就不用担心，毕竟Google在Go（没用VM但有GC）、JavaScript（v8）、Dalvik（Android上的Java VM）上已经有了很多技术积淀。值得注意的是Dart在Flutter中已经可以将GC做到10ms以内，所以Dart和Java相比，决胜因素并不会是在性能方面。而在语法层面，Dart要比Java更有表现力，最重要的是Dart对函数式编程支持要远强于Java(目前只停留在Lambda表达式)，而Dart目前真正的不足是<strong>生态</strong>，但笔者相信，随着Flutter的逐渐火热，会回过头来反推Dart生态加速发展，对于Dart来说，现在需要的是时间。</p> <h3 id=\"dart-vs-javascript\"><a href=\"#dart-vs-javascript\" class=\"header-anchor\">#</a> Dart vs JavaScript</h3> <p>JavaScript的弱类型一直被抓短，所以TypeScript、CoffeeScript甚至是Facebook的flow（虽然并不能算JavaScript的一个超集，但也通过标注和打包工具提供了静态类型检查）才有市场。就笔者使用过的脚本语言中（笔者曾使用过Python、PHP），JavaScript无疑是<strong>动态化</strong>支持最好的脚本语言，比如在JavaScript中，可以给任何对象在任何时候动态扩展属性，对于精通JavaScript的高手来说，这无疑是一把利剑。但是，任何事物都有两面性，JavaScript的强大的动态化特性也是把双刃剑，你可经常听到另一个声音，认为JavaScript的这种动态性糟糕透了，太过灵活反而导致代码很难预期，无法限制不被期望的修改。毕竟有些人总是对自己或别人写的代码不放心，他们希望能够让代码变得可控，并期望有一套静态类型检查系统来帮助自己减少错误。正因如此，在Flutter中，Dart几乎放弃了脚本语言动态化的特性，如不支持反射、也不支持动态创建函数等。并且Dart在2.0强制开启了类型检查（Strong Mode），原先的检查模式（checked mode）和可选类型（optional type）将淡出，所以在类型安全这个层面来说，Dart和TypeScript、CoffeeScript是差不多的，所以单从这一点来看，Dart并不具备什么明显优势，但综合起来看，Dart既能进行服务端脚本、APP开发、web开发，这就有优势了！</p> <p>综上所述，笔者还是很看好Dart语言的将来，之所以表这个态，是因为在新技术发展初期，很多人可能还有所摇摆，有所犹豫，所以有必要给大家打一剂强心针，当然，这是一个见仁见智的问题，大家可以各抒己见。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/180.d048122a.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter1/flutter_intro.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>1.2 初识Flutter | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/105.b6044eeb.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter1/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_1-2-初识flutter\"><a href=\"#_1-2-初识flutter\" class=\"header-anchor\">#</a> 1.2 初识Flutter</h1> <h2 id=\"_1-2-1-flutter简介\"><a href=\"#_1-2-1-flutter简介\" class=\"header-anchor\">#</a> 1.2.1 Flutter简介</h2> <p>Flutter 是 Google推出并开源的移动应用开发框架，主打跨平台、高保真、高性能。开发者可以通过 Dart语言开发 App，一套代码同时运行在 iOS 和 Android平台。 Flutter提供了丰富的组件、接口，开发者可以很快地为 Flutter添加 native扩展。同时 Flutter还使用 Native引擎渲染视图，这无疑能为用户提供良好的体验。</p> <h4 id=\"跨平台自绘引擎\"><a href=\"#跨平台自绘引擎\" class=\"header-anchor\">#</a> 跨平台自绘引擎</h4> <p>Flutter与用于构建移动应用程序的其它大多数框架不同，因为Flutter既不使用WebView，也不使用操作系统的原生控件。 相反，Flutter使用自己的高性能渲染引擎来绘制widget。这样不仅可以保证在Android和iOS上UI的一致性，而且也可以避免对原生控件依赖而带来的限制及高昂的维护成本。</p> <p>Flutter使用Skia作为其2D渲染引擎，Skia是Google的一个2D图形处理函数库，包含字型、坐标转换，以及点阵图都有高效能且简洁的表现，Skia是跨平台的，并提供了非常友好的API，目前Google Chrome浏览器和Android均采用Skia作为其绘图引擎。</p> <p>目前Flutter默认支持iOS、Android、Fuchsia（Google新的自研操作系统）三个移动平台。但Flutter亦可支持Web开发（Flutter for web）和PC开发，本书的示例和介绍主要是基于iOS和Android平台的，其它平台读者可以自行了解。</p> <h4 id=\"高性能\"><a href=\"#高性能\" class=\"header-anchor\">#</a> 高性能</h4> <p>Flutter高性能主要靠两点来保证，首先，Flutter APP采用Dart语言开发。Dart在 JIT（即时编译）模式下，速度与 JavaScript基本持平。但是 Dart支持 AOT，当以 AOT模式运行时，JavaScript便远远追不上了。速度的提升对高帧率下的视图数据计算很有帮助。其次，Flutter使用自己的渲染引擎来绘制UI，布局数据等由Dart语言直接控制，所以在布局过程中不需要像RN那样要在JavaScript和Native之间通信，这在一些滑动和拖动的场景下具有明显优势，因为在滑动和拖动过程往往都会引起布局发生变化，所以JavaScript需要和Native之间不停的同步布局信息，这和在浏览器中要JavaScript频繁操作DOM所带来的问题是相同的，都会带来比较可观的性能开销。</p> <h4 id=\"采用dart语言开发\"><a href=\"#采用dart语言开发\" class=\"header-anchor\">#</a> 采用Dart语言开发</h4> <p>这是一个很有意思，但也很有争议的问题，在了解Flutter为什么选择了 Dart而不是 JavaScript之前我们先来介绍两个概念：JIT和AOT。</p> <p>目前，程序主要有两种运行方式：静态编译与动态解释。静态编译的程序在执行前全部被翻译为机器码，通常将这种类型称为<strong>AOT</strong> （Ahead of time）即 “提前编译”；而解释执行的则是一句一句边翻译边运行，通常将这种类型称为<strong>JIT</strong>（Just-in-time）即“即时编译”。AOT程序的典型代表是用C/C++开发的应用，它们必须在执行前编译成机器码，而JIT的代表则非常多，如JavaScript、python等，事实上，所有脚本语言都支持JIT模式。但需要注意的是JIT和AOT指的是程序运行方式，和编程语言并非强关联的，有些语言既可以以JIT方式运行也可以以AOT方式运行，如Java、Python，它们可以在第一次执行时编译成中间字节码、然后在之后执行时可以直接执行字节码，也许有人会说，中间字节码并非机器码，在程序执行时仍然需要动态将字节码转为机器码，是的，这没有错，不过通常我们区分是否为AOT的标准就是看代码在执行之前是否需要编译，只要需要编译，无论其编译产物是字节码还是机器码，都属于AOT。在此，读者不必纠结于概念，概念就是为了传达精神而发明的，只要读者能够理解其原理即可，得其神忘其形。</p> <p>现在我们看看Flutter为什么选择Dart语言？笔者根据官方解释以及自己对Flutter的理解总结了以下几条（由于其它跨平台框架都将JavaScript作为其开发语言，所以主要将Dart和JavaScript做一个对比）：</p> <ol><li><p><strong>开发效率高</strong></p> <p>Dart运行时和编译器支持Flutter的两个关键特性的组合：</p> <p><strong>基于JIT的快速开发周期</strong>：Flutter在开发阶段采用，采用JIT模式，这样就避免了每次改动都要进行编译，极大的节省了开发时间；</p> <p><strong>基于AOT的发布包</strong>:  Flutter在发布时可以通过AOT生成高效的ARM代码以保证应用性能。而JavaScript则不具有这个能力。</p></li> <li><p><strong>高性能</strong></p> <p>Flutter旨在提供流畅、高保真的的UI体验。为了实现这一点，Flutter中需要能够在每个动画帧中运行大量的代码。这意味着需要一种既能提供高性能的语言，而不会出现会丢帧的周期性暂停，而Dart支持AOT，在这一点上可以做的比JavaScript更好。</p></li> <li><p><strong>快速内存分配</strong></p> <p>Flutter框架使用函数式流，这使得它在很大程度上依赖于底层的内存分配器。因此，拥有一个能够有效地处理琐碎任务的内存分配器将显得十分重要，在缺乏此功能的语言中，Flutter将无法有效地工作。当然Chrome V8的JavaScript引擎在内存分配上也已经做的很好，事实上Dart开发团队的很多成员都是来自Chrome团队的，所以在内存分配上Dart并不能作为超越JavaScript的优势，而对于Flutter来说，它需要这样的特性，而Dart也正好满足而已。</p></li> <li><p><strong>类型安全</strong></p> <p>由于Dart是类型安全的语言，支持静态类型检测，所以可以在编译前发现一些类型的错误，并排除潜在问题，这一点对于前端开发者来说可能会更具有吸引力。与之不同的，JavaScript是一个弱类型语言，也因此前端社区出现了很多给JavaScript代码添加静态类型检测的扩展语言和工具，如：微软的TypeScript以及Facebook的Flow。相比之下，Dart本身就支持静态类型，这是它的一个重要优势。</p></li> <li><p><strong>Dart团队就在你身边</strong></p> <p>看似不起眼，实则举足轻重。由于有Dart团队的积极投入，Flutter团队可以获得更多、更方便的支持，正如Flutter官网所述“我们正与Dart社区进行密切合作，以改进Dart在Flutter中的使用。例如，当我们最初采用Dart时，该语言并没有提供生成原生二进制文件的工具链（这对于实现可预测的高性能具有很大的帮助），但是现在它实现了，因为Dart团队专门为Flutter构建了它。同样，Dart VM之前已经针对吞吐量进行了优化，但团队现在正在优化VM的延迟时间，这对于Flutter的工作负载更为重要。”</p></li></ol> <h4 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h4> <p>本节主要介绍了一下Flutter的特点，如果你感到有些点还不是很好理解，不用着急，随着日后对Flutter细节的了解，再回过头来看，相信你会有更深的体会。</p> <h2 id=\"_1-2-2-flutter框架结构\"><a href=\"#_1-2-2-flutter框架结构\" class=\"header-anchor\">#</a> 1.2.2 Flutter框架结构</h2> <p>本节我们先对Flutter的框架做一个整体介绍，旨在让读者心中有一个整体的印象，这对初学者来说非常重要。如果一下子便深入到Flutter中，就会像是一个在沙漠中没有地图的人，即使可以找到一个绿洲，但是他也不会知道下一个绿洲在哪。因此，无论学什么技术，都要先有一张清晰的“地图”，而我们的学习过程就是“按图索骥”，这样我们才不会陷于细节而“目无全牛”。言归正传，我们看一下Flutter官方提供的Flutter框架图，如图1-1所示：</p> <p><img src=\"/assets/img/1-1.41c572c4.png\" alt=\"图1-1\"></p> <h3 id=\"flutter-framework\"><a href=\"#flutter-framework\" class=\"header-anchor\">#</a> Flutter Framework</h3> <p>这是一个纯 Dart实现的 SDK，它实现了一套基础库，自底向上，我们来简单介绍一下：</p> <ul><li><p>底下两层（Foundation和Animation、Painting、Gestures）在Google的一些视频中被合并为一个dart UI层，对应的是Flutter中的<code>dart:ui</code>包，它是Flutter引擎暴露的底层UI库，提供动画、手势及绘制能力。</p></li> <li><p>Rendering层，这一层是一个抽象的布局层，它依赖于dart UI层，Rendering层会构建一个UI树，当UI树有变化时，会计算出有变化的部分，然后更新UI树，最终将UI树绘制到屏幕上，这个过程类似于React中的虚拟DOM。Rendering层可以说是Flutter UI框架最核心的部分，它除了确定每个UI元素的位置、大小之外还要进行坐标变换、绘制(调用底层dart:ui)。</p></li> <li><p>Widgets层是Flutter提供的的一套基础组件库，在基础组件库之上，Flutter还提供了 Material 和Cupertino两种视觉风格的组件库。而<strong>我们Flutter开发的大多数场景，只是和这两层打交道</strong>。</p></li></ul> <h3 id=\"flutter-engine\"><a href=\"#flutter-engine\" class=\"header-anchor\">#</a> Flutter Engine</h3> <p>这是一个纯 C++实现的 SDK，其中包括了 Skia引擎、Dart运行时、文字排版引擎等。在代码调用 <code>dart:ui</code>库时，调用最终会走到Engine层，然后实现真正的绘制逻辑。</p> <h3 id=\"总结-2\"><a href=\"#总结-2\" class=\"header-anchor\">#</a> 总结</h3> <p>Flutter框架本身有着良好的分层设计，本节旨在让读者对Flutter整体框架有个大概的印象，相信到现在为止，读者已经对Flutter有一个初始印象，在我们正式动手之前，我们还需要了解一下Flutter的开发语言Dart。</p> <h2 id=\"_1-2-3-如何学习flutter\"><a href=\"#_1-2-3-如何学习flutter\" class=\"header-anchor\">#</a> 1.2.3 如何学习Flutter</h2> <p>本节给大家一些学习建议，分享一下笔者在学习Flutter中的一些心得，希望可以帮助你提高学习效率，避免不必要的坑。</p> <h3 id=\"资源\"><a href=\"#资源\" class=\"header-anchor\">#</a> 资源</h3> <ul><li><p><strong>官网</strong>：阅读Flutter官网的资源是快速入门的最佳方式，同时官网也是了解最新Flutter发展动态的地方，由于目前Flutter仍然处于快速发展阶段，所以建议读者还是时不时的去官网看看有没有新的动态。</p></li> <li><p><strong>源码及注释</strong>：源码注释应作为学习Flutter的第一文档，Flutter SDK的源码是开源的，并且注释非常详细，也有很多示例，实际上，Flutter官方的SDK文档就是通过注释生成的。源码结合注释可以帮你解决大多数问题。</p></li> <li><p><strong>Github</strong>：如果遇到的问题在StackOverflow上也没有找到答案，可以去github flutter 项目下提issue。</p></li> <li><p><strong>Gallery源码</strong>：Gallery是Flutter官方示例APP，里面有丰富的示例，读者可以在网上下载安装。Gallery的源码在Flutter源码“examples”目录下。</p></li></ul> <h3 id=\"社区\"><a href=\"#社区\" class=\"header-anchor\">#</a> 社区</h3> <ul><li><strong>StackOverflow</strong>：如果你还没听过StackOverflow，这是目前全球最大的程序员问答社区，现在也是活跃度最高的Flutter问答社区。StackOverflow上面除了世界各地的Flutter使用者会在上面交流之外，Flutter开发团队的成员也经常会在上面回答问题。</li> <li><strong>Flutter中文网社区</strong>：Flutter中文网(https://flutterchina.club)是笔者维护中文网站，目前也是最大的中文资源社区，上面提供了Flutter官网的文档翻译、开源项目、及案例，还有申请加入组织的入口哦。</li> <li><strong>博客</strong>：随着Flutter技术的推广，相信很快网上将会有很多Flutter相关的文章、博客，读者可以多去浏览、阅读。</li></ul> <h3 id=\"总结-3\"><a href=\"#总结-3\" class=\"header-anchor\">#</a> 总结</h3> <p>有了资料和社区后，对于我们学习者自身来说，最重要的还是要多动手、多实践，在本书后面的章节中，希望读者能够亲自动手写一下示例。准备好了吗，下一章中，我们将正式进入Flutter的世界！</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/105.b6044eeb.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter1/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>本章目录 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/181.e3633acb.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter1/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h2 id=\"本章目录\"><a href=\"#本章目录\" class=\"header-anchor\">#</a> 本章目录</h2> <ul><li><a href=\"/v2/chapter1/mobile_development_intro.html\">移动开发技术简介</a></li> <li><a href=\"/v2/chapter1/flutter_intro.html\">Flutter简介</a></li> <li><a href=\"/v2/chapter1/install_flutter.html\">搭建Flutter开发环境</a></li> <li><a href=\"/v2/chapter1/dart.html\">Dart语言简介</a></li></ul></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/181.e3633acb.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter1/install_flutter.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>1.3 搭建Flutter开发环境 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/6.15c5e328.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter1/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_1-3-搭建flutter开发环境\"><a href=\"#_1-3-搭建flutter开发环境\" class=\"header-anchor\">#</a> 1.3 搭建Flutter开发环境</h1> <p>工欲善其事必先利其器，本节首先会分别介绍一下在Windows和macOS下Flutter SDK的安装，然后再介绍一下配IDE和模拟器的使用。</p> <h2 id=\"_1-3-1-安装flutter\"><a href=\"#_1-3-1-安装flutter\" class=\"header-anchor\">#</a> 1.3.1 安装Flutter</h2> <p>由于Flutter会同时构建Android和IOS两个平台的发布包，所以Flutter同时依赖Android SDK和iOS SDK，在安装Flutter时也需要安装相应平台的构建工具和SDK。下面我们分别介绍一下Windows和macOS下的环境搭建。</p> <blockquote><p>注意：本节介绍的安装方式随着Flutter的升级可能会发生变化，如果下面介绍的内容在您安装Flutter时已经失效，请访问Flutter官网，按照官网最新的安装教程安装。</p></blockquote> <h3 id=\"使用镜像\"><a href=\"#使用镜像\" class=\"header-anchor\">#</a> 使用镜像</h3> <p>由于在国内访问Flutter有时可能会受到限制，Flutter官方为中国开发者搭建了临时镜像，大家可以将如下环境变量加入到用户环境变量中：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>export PUB_HOSTED_URL=https://pub.flutter-io.cn\nexport FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn\n</code></pre></div><p><strong>注意：</strong> 此镜像为临时镜像，并不能保证一直可用，读者可以参考https://flutter.io/community/china 以获得有关镜像服务器的最新动态。</p> <h3 id=\"在windows上搭建flutter开发环境\"><a href=\"#在windows上搭建flutter开发环境\" class=\"header-anchor\">#</a> 在Windows上搭建Flutter开发环境</h3> <h4 id=\"系统要求\"><a href=\"#系统要求\" class=\"header-anchor\">#</a> 系统要求</h4> <p>要安装并运行Flutter，您的开发环境必须满足以下最低要求:</p> <ul><li><p>操作系统: Windows 7 或更高版本 (64-bit)</p></li> <li><p>磁盘空间: 400 MB (不包括Android Studio的磁盘空间).</p></li> <li><p>工具: Flutter 依赖下面这些命令行工具.</p> <ul><li><p><a href=\"https://docs.microsoft.com/en-us/powershell/scripting/setup/installing-windows-powershell#upgrading-existing-windows-powershell\" target=\"_blank\" rel=\"noopener noreferrer\">PowerShell 5.0<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 或更新的版本</p></li> <li><p><a href=\"https://git-scm.com/download/win\" target=\"_blank\" rel=\"noopener noreferrer\">Git for Windows<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>  (Git命令行工具)；</p></li></ul> <p>如果已安装Git for Windows，请确保可以在命令提示符或PowerShell中运行 git 命令</p></li></ul> <h4 id=\"获取flutter-sdk\"><a href=\"#获取flutter-sdk\" class=\"header-anchor\">#</a> 获取Flutter SDK</h4> <ol><li><p>去flutter官网下载其最新可用的安装包，下载地址：https://flutter.dev/docs/development/tools/sdk/releases ，打开后如图1-2所示：</p> <p><img src=\"/assets/img/1-2.c3960e42.png\" alt=\"图1-2\"></p> <p>注意，Flutter的渠道版本会不停变动，请以Flutter官网为准。另外，在中国大陆地区，要想正常获取安装包列表或下载安装包，可能需要翻墙，读者也可以去Flutter github项目下去下载安装包，地址：https://github.com/flutter/flutter/releases 。</p></li> <li><p>将安装包zip解压到你想安装Flutter SDK的路径（如：<code>C:\\src\\flutter</code>；注意，<strong>不要</strong>将flutter安装到需要一些高权限的路径如<code>C:\\Program Files\\</code>）。</p></li> <li><p>在Flutter安装目录的<code>flutter</code>文件下找到<code>flutter_console.bat</code>，双击运行并启动<strong>flutter命令行</strong>，接下来，你就可以在Flutter命令行运行flutter命令了。</p></li></ol> <h5 id=\"更新环境变量\"><a href=\"#更新环境变量\" class=\"header-anchor\">#</a> 更新环境变量</h5> <p>如果你想在Windows系统自带命令行运行flutter命令，需要添加以下环境变量到用户PATH：</p> <ul><li>转到 “控制面板&gt;用户帐户&gt;用户帐户&gt;更改我的环境变量”</li> <li>在“用户变量”下检查是否有名为“Path”的条目:\n<ul><li>如果该条目存在， 追加 flutter\\bin的全路径，使用 ; 作为分隔符.</li> <li>如果该条目不存在，创建一个新用户变量 Path ，然后将 <code>flutter\\bin</code> 的全路径作为它的值.</li></ul></li></ul> <p>重启Windows以应用此更改.</p> <h5 id=\"运行-flutter-doctor命令\"><a href=\"#运行-flutter-doctor命令\" class=\"header-anchor\">#</a> 运行 flutter doctor命令</h5> <p>在Flutter命令行运行如下命令来查看是否还需要安装其它依赖，如果需要，安装它们：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>flutter doctor\n</code></pre></div><p>该命令检查你的环境并在命令行窗口中显示报告。Dart SDK已经在打包在Flutter SDK里了，没有必要单独安装Dart。 仔细检查命令行输出以获取可能需要安装的其他软件或进一步需要执行的任务。</p> <p>例如：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>[-] Android toolchain - develop for Android devices\n    • Android SDK at D:\\Android\\sdk\n    ✗ Android SDK is missing command line tools; download from https://goo.gl/XxQghQ\n    • Try re-installing or updating your Android SDK,\n      visit https://flutter.io/setup/#android-setup for detailed instructions.\n</code></pre></div><p>第一次运行flutter命令（如<code>flutter doctor</code>）时，它会下载它自己的依赖项并自行编译。以后再运行就会快得多。缺失的依赖需要安装一下，安装完成后再运行<code>flutter doctor</code>命令来验证是否安装成功。</p> <h4 id=\"android设置\"><a href=\"#android设置\" class=\"header-anchor\">#</a> Android设置</h4> <p>Flutter依赖于Android Studio的全量安装。Android Studio不仅可以管理Android 平台依赖、SDK版本等，而且它也是Flutter开发推荐的IDE之一（当然，你也可以使用其它编辑器或IDE，我们将会在后面讨论）。</p> <h5 id=\"安装android-studio\"><a href=\"#安装android-studio\" class=\"header-anchor\">#</a> 安装Android Studio</h5> <ol><li>下载并安装 Android Studio，下载地址：https://developer.android.com/studio/index.html 。</li> <li>启动Android Studio，然后执行“Android Studio安装向导”。这将安装最新的Android SDK、Android SDK平台工具和Android SDK构建工具，这些是用Flutter进行Android开发所需要的。</li></ol> <h4 id=\"安装遇到问题\"><a href=\"#安装遇到问题\" class=\"header-anchor\">#</a> 安装遇到问题？</h4> <p>如果在安装过程中遇到问题，可以先去flutter官网查看一下安装方式是否发生变化，或者在网上搜索一下解决方案。</p> <h3 id=\"在macos上搭建flutter开发环境\"><a href=\"#在macos上搭建flutter开发环境\" class=\"header-anchor\">#</a> 在macOS上搭建Flutter开发环境</h3> <p>在masOS下可以同时进行Android和iOS设备的测试。</p> <h4 id=\"系统要求-2\"><a href=\"#系统要求-2\" class=\"header-anchor\">#</a> 系统要求</h4> <p>要安装并运行Flutter，您的开发环境必须满足以下最低要求:</p> <ul><li>操作系统: macOS (64-bit)</li> <li>磁盘空间: 700 MB (不包括Xcode或Android Studio的磁盘空间）.</li> <li>工具: Flutter 依赖下面这些命令行工具.\n<ul><li><code>bash、mkdir、rm、git、curl、unzip、which</code></li></ul></li></ul> <h4 id=\"获取flutter-sdk-2\"><a href=\"#获取flutter-sdk-2\" class=\"header-anchor\">#</a> 获取Flutter SDK</h4> <ol><li><p>去flutter官网下载其最新可用的安装包，官网地址：https://flutter.io/sdk-archive/#macos</p> <p>注意，Flutter的渠道版本会不停变动，请以Flutter官网为准。另外，在中国大陆地区，要想正常获取安装包列表或下载安装包，可能需要翻墙，读者也可以去Flutter github项目下去下载安装包，地址：https://github.com/flutter/flutter/releases 。</p></li> <li><p>解压安装包到你想安装的目录，如：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code><span class=\"token builtin class-name\">cd</span> ~/development\n<span class=\"token function\">unzip</span> ~/Downloads/flutter_macos_v0.5.1-beta.zip\n</code></pre></div></li> <li><p>添加<code>flutter</code>相关工具到path中：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code><span class=\"token builtin class-name\">export</span> <span class=\"token assign-left variable\"><span class=\"token environment constant\">PATH</span></span><span class=\"token operator\">=</span><span class=\"token variable\"><span class=\"token variable\">`</span><span class=\"token builtin class-name\">pwd</span><span class=\"token variable\">`</span></span>/flutter/bin:<span class=\"token environment constant\">$PATH</span>\n</code></pre></div><p>此代码只能暂时针对当前命令行窗口设置PATH环境变量，要想永久将Flutter添加到PATH中请参考下面<strong>更新环境变量</strong> 部分。</p></li></ol> <h5 id=\"运行-flutter-doctor命令-2\"><a href=\"#运行-flutter-doctor命令-2\" class=\"header-anchor\">#</a> 运行 flutter doctor命令</h5> <p>这一步和Windows下步骤一致，不再赘述。</p> <h5 id=\"更新环境变量-2\"><a href=\"#更新环境变量-2\" class=\"header-anchor\">#</a> 更新环境变量</h5> <p>将Flutter添加到PATH中，可以在任何终端会话中运行<code>flutter</code>命令。</p> <p>对于所有终端会话永久修改此变量的步骤是和特定计算机系统相关的。通常，您会在打开新窗口时将设置环境变量的命令添加到执行的文件中。例如</p> <ol><li><p>确定您Flutter SDK的目录记为“FLUTTER_INSTALL_PATH”，您将在步骤3中用到。</p></li> <li><p>打开(或创建) <code>$HOME/.bash_profile</code>。文件路径和文件名可能在你的电脑上不同.</p></li> <li><p>添加以下路径:</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code><span class=\"token builtin class-name\">export</span> <span class=\"token assign-left variable\"><span class=\"token environment constant\">PATH</span></span><span class=\"token operator\">=</span><span class=\"token punctuation\">[</span>FLUTTER_INSTALL_PATH<span class=\"token punctuation\">]</span>/flutter/bin:<span class=\"token environment constant\">$PATH</span>\n</code></pre></div><p>例如笔者Flutter 安装目录是“~/code/flutter_dir”，那么代码为：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>export PATH=~/code/flutter_dir/flutter/bin:$PATH\n</code></pre></div></li> <li><p>运行 <code>source $HOME/.bash_profile</code> 刷新当前终端窗口。</p> <blockquote><p><strong>注意:</strong> 如果你使用终端是zsh，终端启动时 <code>~/.bash_profile</code> 将不会被加载，解决办法就是修改 <code>～/.zshrc</code> ，在其中添加：source ～/.bash_profile</p></blockquote></li> <li><p>验证“flutter/bin”是否已在PATH中：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>echo $PATH\n</code></pre></div></li></ol> <h4 id=\"安装-xcode\"><a href=\"#安装-xcode\" class=\"header-anchor\">#</a> 安装 Xcode</h4> <p>要为iOS开发Flutter应用程序，您需要Xcode 9.0或更高版本:</p> <ol><li>安装Xcode 9.0或更新版本(通过<a href=\"https://developer.apple.com/xcode/\" target=\"_blank\" rel=\"noopener noreferrer\">链接下载<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>或<a href=\"https://itunes.apple.com/us/app/xcode/id497799835\" target=\"_blank\" rel=\"noopener noreferrer\">苹果应用商店<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>).</li> <li>配置Xcode命令行工具以使用新安装的Xcode版本 <code>sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer</code> 对于大多数情况，当您想要使用最新版本的Xcode时，这是正确的路径。如果您需要使用不同的版本，请指定相应路径。</li> <li>确保Xcode许可协议是通过打开一次Xcode或通过命令<code>sudo xcodebuild -license</code>同意过了.</li></ol> <p>使用Xcode，您可以在iOS设备或模拟器上运行Flutter应用程序。</p> <h4 id=\"安装android-studio-2\"><a href=\"#安装android-studio-2\" class=\"header-anchor\">#</a> 安装Android Studio</h4> <p>和Window一样，要在Android设备上构建并运行Flutter程序都需要先安装Android Studio，读者可以先自行下载并安装Android Studio，在此不再赘述。</p> <h3 id=\"升级-flutter\"><a href=\"#升级-flutter\" class=\"header-anchor\">#</a> 升级 Flutter</h3> <h4 id=\"flutter-sdk分支\"><a href=\"#flutter-sdk分支\" class=\"header-anchor\">#</a> Flutter SDK分支</h4> <p>Flutter SDK有多个分支，如beta、dev、master、stable，其中stable分支为稳定分支（日后有新的稳定版本发布后可能也会有新的稳定分支，如1.0.0），dev和master为开发分支，安装flutter后，你可以运行<code>flutter channel</code>查看所有分支，如笔者本地运行后，结果如下：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>Flutter channels:\n  beta\n  dev\n* master\n</code></pre></div><p>带&quot;*&quot;号的分支即你本地的Flutter SDK 跟踪的分支，要切换分支，可以使用<code>flutter channel beta</code> 或 <code>flutter channel master</code>，Flutter官方建议跟踪稳定分支，但你也可以跟踪<code>master</code>分支，这样可以查看最新的变化，但这样稳定性要低的多。</p> <h4 id=\"升级flutter-sdk和依赖包\"><a href=\"#升级flutter-sdk和依赖包\" class=\"header-anchor\">#</a> 升级Flutter SDK和依赖包</h4> <p>要升级flutter sdk，只需一句命令：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>flutter upgrade\n</code></pre></div><p>该命令会同时更新Flutter SDK和你的flutter项目依赖包。如果你只想更新项目依赖包（不包括Flutter SDK），可以使用如下命令：</p> <ul><li><code>flutter packages get</code>获取项目所有的依赖包。</li> <li><code>flutter packages upgrade</code> 获取项目所有依赖包的最新版本。</li></ul> <h4 id=\"\"><a href=\"#\" class=\"header-anchor\">#</a></h4> <h2 id=\"_1-3-2-ide配置与使用\"><a href=\"#_1-3-2-ide配置与使用\" class=\"header-anchor\">#</a> 1.3.2 IDE配置与使用</h2> <p>理论上可以使用任何文本编辑器与命令行工具来构建Flutter应用程序。 不过，Flutter官方建议使用Android Studio和VS Code之一以获得更好的开发体验。Flutter官方提供了这两款编辑器插件，通过IDE和插件可获得代码补全、语法高亮、widget编辑辅助、运行和调试支持等功能，可以帮助我们极大的提高开发效率。下面我们分别介绍一下Android Studio和VS Code的配置及使用（Android Studio和VS Code读者可以在其官网获得最新的安装，由于安装比较简单，故不再赘述）。</p> <h3 id=\"android-studio-配置与使用\"><a href=\"#android-studio-配置与使用\" class=\"header-anchor\">#</a> Android Studio 配置与使用</h3> <p>由于Android Studio是基于IntelliJ IDEA开发的，所以读者也可以使用IntelliJ IDEA。</p> <h4 id=\"安装flutter和dart插件\"><a href=\"#安装flutter和dart插件\" class=\"header-anchor\">#</a> 安装Flutter和Dart插件</h4> <p>需要安装两个插件:</p> <ul><li><code>Flutter</code>插件： 支持Flutter开发工作流 (运行、调试、热重载等)。</li> <li><code>Dart</code>插件： 提供代码分析 (输入代码时进行验证、代码补全等)。</li></ul> <p>安装步骤：</p> <ol><li>启动Android Studio。</li> <li>打开插件首选项 (macOS：<strong>Preferences&gt;Plugins</strong>, Windows：<strong>File&gt;Settings&gt;Plugins</strong>)。</li> <li>选择 <strong>Browse repositories…</strong>，选择 flutter 插件并点击 <code>install</code>。</li> <li>重启Android Studio后插件生效。</li></ol> <p>接下来，让我们用Android Studio创建一个Flutter项目，然后运行它，并体验“热重载”。</p> <h4 id=\"创建flutter应用\"><a href=\"#创建flutter应用\" class=\"header-anchor\">#</a> 创建Flutter应用</h4> <ol><li>选择 <strong>File&gt;New Flutter Project</strong> 。</li> <li>选择 <strong>Flutter application</strong> 作为 project 类型, 然后点击 Next。</li> <li>输入项目名称 (如 <code>myapp</code>)，然后点击 Next。</li> <li>点击 <strong>Finish</strong>。</li> <li>等待Android Studio安装SDK并创建项目。</li></ol> <p>上述命令创建一个Flutter项目，项目名为myapp，其中包含一个使用<a href=\"https://material.io/guidelines/\" target=\"_blank\" rel=\"noopener noreferrer\">Material 组件<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>的简单演示应用程序。</p> <p>在项目目录中，您应用程序的代码位于 <code>lib/main.dart</code>。</p> <h4 id=\"运行应用程序\"><a href=\"#运行应用程序\" class=\"header-anchor\">#</a> 运行应用程序</h4> <ol><li><p>定位到Android Studio工具栏，如图1-3所示：</p> <p><img src=\"/assets/img/1-3.656e852b.png\" alt=\"图1-3\"></p></li> <li><p>在 <strong>target selector</strong> 中, 选择一个运行该应用的Android设备。如果没有列出可用，请选择 <strong>Tools&gt;Android&gt;AVD Manager</strong> 并在那里创建一个。</p></li> <li><p>在工具栏中点击 <strong>Run图标</strong>。</p></li> <li><p>如果一切正常, 您应该在您的设备或模拟器上会看到启动的应用程序：</p> <p><img src=\"/assets/img/1-4.801e91b2.png\" alt=\"图1-4\"></p></li></ol> <h4 id=\"体验热重载\"><a href=\"#体验热重载\" class=\"header-anchor\">#</a> 体验热重载</h4> <p>Flutter 可以通过 <em>热重载（hot reload）</em> 实现快速的开发周期，热重载就是无需重启应用程序就能实时加载修改后的代码，并且不会丢失状态。简单的对代码进行更改，然后告诉IDE或命令行工具你需要重新加载（点击reload按钮），你就会在你的设备或模拟器上看到更改。</p> <ol><li><p>打开<code>lib/main.dart</code>文件</p></li> <li><p>将字符串\n<code>'You have pushed the button this many times:'</code> 更改为\n<code>'You have clicked the button this many times:'</code></p></li> <li><p>不要按“停止”按钮; 让您的应用继续运行.</p></li> <li><p>要查更改，请调用 <strong>Save</strong> (<code>cmd-s</code> / <code>ctrl-s</code>)，或者点击 <strong>热重载按钮</strong> (带有闪电⚡️图标的按钮)。</p> <p>你会立即在运行的应用程序中看到更新的字符串。</p></li></ol> <h3 id=\"vs-code的配置与使用\"><a href=\"#vs-code的配置与使用\" class=\"header-anchor\">#</a> VS Code的配置与使用</h3> <p>VS Code是一个轻量级编辑器，支持Flutter运行和调试。</p> <h4 id=\"安装flutter插件\"><a href=\"#安装flutter插件\" class=\"header-anchor\">#</a> 安装flutter插件</h4> <ol><li>启动 VS Code。</li> <li>调用 <strong>View&gt;Command Palette…</strong>。</li> <li>输入 ‘install’, 然后选择 <strong>Extensions: Install Extension</strong> action。</li> <li>在搜索框输入 <code>flutter</code> ，在搜索结果列表中选择 ‘Flutter’, 然后点击 <strong>Install</strong>。</li> <li>选择 ‘OK’ 重新启动 VS Code。</li> <li>验证配置\n<ul><li>调用 <strong>View&gt;Command Palette…</strong></li> <li>输入 ‘doctor’, 然后选择 <strong>‘Flutter: Run Flutter Doctor’</strong> action。</li> <li>查看“OUTPUT”窗口中的输出是否有问题</li></ul></li></ol> <h4 id=\"创建flutter应用-2\"><a href=\"#创建flutter应用-2\" class=\"header-anchor\">#</a> 创建Flutter应用</h4> <ol><li>启动 VS Code</li> <li>调用 <strong>View&gt;Command Palette…</strong></li> <li>输入 ‘flutter’, 然后选择 <strong>‘Flutter: New Project’</strong> action</li> <li>输入 Project 名称 (如<code>myapp</code>), 然后按回车键</li> <li>指定放置项目的位置，然后按蓝色的确定按钮</li> <li>等待项目创建继续，并显示main.dart文件</li></ol> <h4 id=\"体验热重载-2\"><a href=\"#体验热重载-2\" class=\"header-anchor\">#</a> 体验热重载</h4> <ol><li>打开<code>lib/main.dart</code>文件。</li> <li>将字符串\n<code>'You have pushed the button this many times:'</code> 更改为\n<code>'You have clicked the button this many times:'</code>。</li> <li>不要按“停止”按钮; 让您的应用继续运行。</li> <li>要查看您的更改，请调用 <strong>Save</strong> (<code>cmd-s</code> / <code>ctrl-s</code>), 或者点击 <strong>热重载按钮</strong> (绿色圆形箭头按钮)。</li></ol> <p>你会立即在运行的应用程序中看到更新的字符串。</p> <h2 id=\"_1-3-3-连接设备运行flutter应用\"><a href=\"#_1-3-3-连接设备运行flutter应用\" class=\"header-anchor\">#</a> 1.3.3 连接设备运行Flutter应用</h2> <p>Window下只支持为Android设备构建并运行Flutter应用，而macOS同时支持iOS和Android设备。下面分别介绍如何连接Android和iOS设备来运行flutter应用。</p> <h3 id=\"连接android模拟器\"><a href=\"#连接android模拟器\" class=\"header-anchor\">#</a> 连接Android模拟器</h3> <p>要准备在Android模拟器上运行并测试Flutter应用，请按照以下步骤操作：</p> <ol><li><p>启动 <strong>Android Studio&gt;Tools&gt;Android&gt;AVD Manager</strong> 并选择 <strong>Create Virtual Device</strong>.</p></li> <li><p>选择一个设备并选择 <strong>Next</strong>。</p></li> <li><p>为要模拟的Android版本选择一个或多个系统印象，然后选择 <strong>Next</strong>. 建议使用 <em>x86</em> 或 <em>x86_64</em> image .</p></li> <li><p>在 “Emulated Performance”下, 选择 <strong>Hardware - GLES 2.0</strong> 以启用 <a href=\"https://developer.android.com/studio/run/emulator-acceleration.html\" target=\"_blank\" rel=\"noopener noreferrer\">硬件加速<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>.</p></li> <li><p>验证AVD配置是否正确，然后选择 <strong>Finish</strong>。</p> <p>有关上述步骤的详细信息，请参阅 <a href=\"https://developer.android.com/studio/run/managing-avds.html\" target=\"_blank\" rel=\"noopener noreferrer\">Managing AVDs<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>.</p></li> <li><p>在“Android Virtual Device Manager”中，点击工具栏的 <strong>Run</strong>。模拟器启动并显示所选操作系统版本或设备的启动画面。</p></li> <li><p>运行 <code>flutter run</code> 启动您的设备。 连接的设备名是 <code>Android SDK built for &lt;platform&gt;</code>，其中 <em>platform</em> 是芯片系列，如 x86。</p></li></ol> <h3 id=\"连接android真机设备\"><a href=\"#连接android真机设备\" class=\"header-anchor\">#</a> 连接Android真机设备</h3> <p>要准备在Android设备上运行并测试Flutter应用，需要Android 4.1（API level 16）或更高版本的Android设备.</p> <ol><li>在Android设备上启用 <strong>开发人员选项</strong> 和 <strong>USB调试</strong> 。详细说明可在<a href=\"https://developer.android.com/studio/debug/dev-options.html\" target=\"_blank\" rel=\"noopener noreferrer\">Android文档<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>中找到。</li> <li>使用USB将手机插入电脑。如果设备出现调试授权提示，请授权你的电脑可以访问该设备。</li> <li>在命令行运行 <code>flutter devices</code> 命令以验证Flutter识别您连接的Android设备。</li> <li>运行启动你应用程序 <code>flutter run</code>。</li></ol> <p>默认情况下，Flutter使用的Android SDK版本是基于你的 <code>adb</code> 工具版本。 如果想让Flutter使用不同版本的Android SDK，则必须将该 <code>ANDROID_HOME</code> 环境变量设置为相应的SDK安装目录。</p> <h3 id=\"连接ios模拟器\"><a href=\"#连接ios模拟器\" class=\"header-anchor\">#</a> 连接iOS模拟器</h3> <p>要准备在iOS模拟器上运行并测试Flutter应用，请按以下步骤操作：</p> <ol><li><p>在你的MAC上，通过 Spotlight 或以下命令找到模拟器：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code><span class=\"token function\">open</span> -a Simulator\n</code></pre></div></li> <li><p>通过检查模拟器 <strong>Hardware &gt; Device</strong> 菜单中的设置，确保模拟器正在使用64位设备（iPhone 5s或更高版本）。</p></li> <li><p>根据你电脑屏幕大小，模拟高清屏iOS设备可能会溢出屏幕。可以在模拟器的 <strong>Window&gt; Scale</strong> 菜单下设置设备比例。</p></li> <li><p>运行 <code>flutter run</code>启动flutter应用程序。</p></li></ol> <h3 id=\"连接ios真机设备\"><a href=\"#连接ios真机设备\" class=\"header-anchor\">#</a> 连接iOS真机设备</h3> <p>要将Flutter应用安装到iOS真机设备，需要一些额外的工具和一个Apple帐户，还需要在Xcode中进行一些设置。</p> <ol><li><p>安装 <a href=\"http://brew.sh/\" target=\"_blank\" rel=\"noopener noreferrer\">homebrew<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> （如果已经安装了brew,跳过此步骤）。</p></li> <li><p>打开终端并运行如下这些命令:</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>brew update\nbrew <span class=\"token function\">install</span> --HEAD libimobiledevice\nbrew <span class=\"token function\">install</span> ideviceinstaller ios-deploy cocoapods\npod setup\n</code></pre></div><p>如果这些命令中的任何一个失败并出现错误，请运行brew doctor并按照说明解决问题.</p></li> <li><p>遵循Xcode签名流程来配置您的项目:</p> <ul><li><p>在你Flutter项目目录中通过 <code>open ios/Runner.xcworkspace</code> 打开默认的Xcode workspace.</p></li> <li><p>在Xcode中，选择导航面板左侧中的<code>Runner</code>项目。</p></li> <li><p>在<code>Runner</code> target设置页面中，确保在 <strong>General &gt; Signing &gt; Team</strong> 下选择了你的开发团队。当你选择一个团队时，Xcode会创建并下载开发证书，向你的设备注册你的帐户，并创建和下载配置文件（如果需要）。</p></li> <li><p>要开始您的第一个iOS开发项目，您可能需要使用您的Apple ID登录Xcode，如图1-5：</p> <p><img src=\"/assets/img/1-5.cc75b912.png\" alt=\"图1-5\"></p> <p>任何Apple ID都支持开发和测试，但若想将应用分发到App Store，就必须注册Apple开发者计划，有关详情读者可以自行了解。</p></li></ul> <ol start=\"4\"><li><p>当您第一次attach真机设备进行iOS开发时，需要同时信任你的Mac和该设备上的开发证书。首次将iOS设备连接到Mac时，请在对话框中选择 <code>Trust</code>。</p> <p><img src=\"/assets/img/1-6.739453e7.png\" alt=\"添加信任\"></p> <p>然后，转到iOS设备上的<strong>设置</strong>菜单，选择 <strong>常规&gt;设备管理</strong> 并信任您的证书。</p></li> <li><p>如果Xcode中的自动签名失败，请验证项目的 <strong>General &gt; Identity &gt; Bundle Identifier</strong> 值是否唯一，如图1-7所示：</p> <p><img src=\"/assets/img/1-7.4b555c37.png\" alt=\"验证bundle id是否唯一\"></p></li> <li><p>运行 <code>flutter run</code>启动flutter应用程序。</p></li></ol></li></ol> <h2 id=\"_1-3-4-常见配置问题\"><a href=\"#_1-3-4-常见配置问题\" class=\"header-anchor\">#</a> 1.3.4 常见配置问题</h2> <h3 id=\"android-studio问题\"><a href=\"#android-studio问题\" class=\"header-anchor\">#</a> Android Studio问题</h3> <h4 id=\"缺少依赖库问题\"><a href=\"#缺少依赖库问题\" class=\"header-anchor\">#</a> 缺少依赖库问题</h4> <p>上手安卓最常遇见的问题之一，错误如图1-8所示，此时点击超链接即可自动跳转到安装页面</p> <p><img src=\"data:image/png;base64,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\" alt=\"图1-8\"></p> <p>安装之后重新运行即可，如图1-9：</p> <p><img src=\"/assets/img/1-9.69ea9a5c.png\" alt=\"install_request_components.png\"></p> <h4 id=\"连接不上android-repository\"><a href=\"#连接不上android-repository\" class=\"header-anchor\">#</a> 连接不上Android Repository</h4> <p>这也是最常见的问题之一，当你发现自己无法下载部分依赖的时候，请优先考虑这种情况。进入 <code>File</code> -&gt; <code>Settings</code> -&gt; <code>Appearance &amp; Behavior</code> -&gt; <code>System Settings</code> -&gt; <code>Android SDK</code> -&gt; <code>SDK Update Sites</code> 列表，可以看到此时的 <code>Android Repository</code> 无法连接，如图1-10所示：</p> <p><img src=\"/assets/img/1-10.a2561ece.png\" alt=\"下载依赖失败\"></p> <p>这是由于要去Google下载Android SDK，但在国内目前无法访问Google所致，因此，我们可以配置代理或使用vpn。</p> <h4 id=\"安卓包配置问题\"><a href=\"#安卓包配置问题\" class=\"header-anchor\">#</a> 安卓包配置问题</h4> <p>一般格式为</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>Could not HEAD **\nCould not Get **\n</code></pre></div><p>如：<code>Android Studio Could not GET gradle-3.2.0.pom</code></p> <p>这一类问题是由于无法连接到 Maven 库造成的，解决方法如下：</p> <ol><li><p>进入<code>当前所在项目名/android</code></p></li> <li><p>打开 <code>build.gradle</code></p></li> <li><p>找到下面这一部分，并加上 <code>maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }</code></p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>allprojects {\n    repositories {\n      google()\n      jcenter()\n      maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' } //添加这一句\n\t}\n}\n</code></pre></div></li> <li><p>进入 File/ Settings/ Build, Execution, Deployment/ BuildTools/ Gradle/ Android Studio 中，勾选上 Enable embedded Maven repository ，重启 Android Studio 即可解决。</p> <blockquote><p>**注意：**存在这样的一种情况，当你根据上述步骤设置了之后，依旧无法解决这个问题，并有类似于 <code>Could not HEAD maven.aliyun.com</code> 的报错信息，请检查 <code>C:\\Users\\{user_name}\\.gradle\\gradle.properties</code> 是否有设置代理。删除后问题即可解决。</p></blockquote></li></ol> <h4 id=\"hot-reload-热重载失效问题\"><a href=\"#hot-reload-热重载失效问题\" class=\"header-anchor\">#</a> Hot Reload 热重载失效问题</h4> <p>在给 <code>Terminal</code> 之类的终端模拟器设置代理之后，会导致“Hot Reload”重载失效，此时调用 <strong>Save</strong> (<code>cmd-s</code> / <code>ctrl-s</code>)将不会进行热重载，<strong>热重载按钮</strong> (带有闪电⚡️图标的按钮)也不会显示，将代理移除即可解决。</p> <p>另外，有些情况下热重载是不生效的，比如修改了<code>main</code>函数、修改了全局静态方法等，读者可以认为“Hot Reload”只会重新构建整个widget树，如果变动不在构建widget树的过程中，“Hot Reload”就不会起作用。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/6.15c5e328.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter1/mobile_development_intro.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>1.1 移动开发技术简介 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/182.d30b9f69.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter1/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_1-1-移动开发技术简介\"><a href=\"#_1-1-移动开发技术简介\" class=\"header-anchor\">#</a> 1.1 移动开发技术简介</h1> <p>本节将主要介绍一下移动开发技术的进化历程，主要是想让读者知道Flutter技术出现的背景。笔者认为，了解一门新技术出现的背景是非常重要的，因为只有了解之前是什么样的，才能理解为什么会是现在这样。</p> <h2 id=\"_1-1-1-原生开发与跨平台技术\"><a href=\"#_1-1-1-原生开发与跨平台技术\" class=\"header-anchor\">#</a> 1.1.1 原生开发与跨平台技术</h2> <h3 id=\"原生开发\"><a href=\"#原生开发\" class=\"header-anchor\">#</a> 原生开发</h3> <p>原生应用程序是指某一个移动平台（比如iOS或安卓）所特有的应用，使用相应平台支持的开发工具和语言，并直接调用系统提供的SDK API。比如Android原生应用就是指使用Java或Kotlin语言直接调用Android SDK开发的应用程序；而iOS原生应用就是指通过Objective-C或Swift语言直接调用iOS SDK开发的应用程序。原生开发有以下主要优势：</p> <ul><li>可访问平台全部功能（GPS、摄像头）；</li> <li>速度快、性能高、可以实现复杂动画及绘制，整体用户体验好；</li></ul> <p>主要缺点：</p> <ul><li>平台特定，开发成本高；不同平台必须维护不同代码，人力成本随之变大；</li> <li>内容固定，动态化弱，大多数情况下，有新功能更新时只能发版；</li></ul> <p>在移动互联网发展初期，业务场景并不复杂，原生开发还可以应对产品需求迭代。 但近几年，随着物联网时代到来、移动互联网高歌猛进，日新月异，在很多业务场景中，传统的纯原生开发已经不能满足日益增长的业务需求。主要表现在：</p> <ul><li>动态化内容需求增大；当需求发生变化时，纯原生应用需要通过版本升级来更新内容，但应用上架、审核是需要周期的，这对高速变化的互联网时代来说是很难接受的，所以，对应用动态化(不发版也可以更新应用内容)的需求就变的迫在眉睫。</li> <li>业务需求变化快，开发成本变大；由于原生开发一般都要维护Android、iOS两个开发团队，版本迭代时，无论人力成本，还是测试成本都会变大。</li></ul> <p>总结一下，纯原生开发主要面临动态化和开发成本两个问题，而针对这两个问题，诞生了一些跨平台的动态化框架。</p> <h3 id=\"跨平台技术简介\"><a href=\"#跨平台技术简介\" class=\"header-anchor\">#</a> 跨平台技术简介</h3> <p>针对原生开发面临问题，人们一直都在努力寻找好的解决方案，而时至今日，已经有很多跨平台框架(注意，本书中所指的“跨平台”若无特殊说明，即特指Android和iOS两个平台)，根据其原理，主要分为三类：</p> <ul><li>H5+原生（Cordova、Ionic、微信小程序）</li> <li>JavaScript开发+原生渲染 （React Native、Weex、快应用）</li> <li>自绘UI+原生(QT for mobile、Flutter)</li></ul> <p>在接下来的章节中我们逐个来看看这三类框架的原理及优缺点。</p> <h2 id=\"_1-1-2-hybrid技术简介\"><a href=\"#_1-1-2-hybrid技术简介\" class=\"header-anchor\">#</a> 1.1.2 Hybrid技术简介</h2> <h3 id=\"h5-原生混合开发\"><a href=\"#h5-原生混合开发\" class=\"header-anchor\">#</a> H5+原生混合开发</h3> <p>这类框架主要原理就是将APP的一部分需要动态变动的内容通过H5来实现，通过原生的网页加载控件WebView (Android)或WKWebView（iOS）来加载（以后若无特殊说明，我们用WebView来统一指代android和iOS中的网页加载控件）。这样以来，H5部分是可以随时改变而不用发版，动态化需求能满足；同时，由于h5代码只需要一次开发，就能同时在Android和iOS两个平台运行，这也可以减小开发成本，也就是说，H5部分功能越多，开发成本就越小。我们称这种h5+原生的开发模式为<strong>混合开发 ** ，采用混合模式开发的APP我们称之为</strong>混合应用<strong>或</strong>Hybrid APP**  ，如果一个应用的大多数功能都是H5实现的话，我们称其为<strong>Web APP</strong> 。</p> <p>目前混合开发框架的典型代表有：Cordova、Ionic 和微信小程序，值得一提的是微信小程序目前是在webview中渲染的，并非原生渲染，但将来有可能会采用原生渲染。</p> <h3 id=\"混合开发技术点\"><a href=\"#混合开发技术点\" class=\"header-anchor\">#</a> 混合开发技术点</h3> <p>如之前所述，原生开发可以访问平台所有功能，而混合开发中，H5代码是运行在WebView中，而WebView实质上就是一个浏览器内核，其JavaScript依然运行在一个权限受限的沙箱中，所以对于大多数系统能力都没有访问权限，如无法访问文件系统、不能使用蓝牙等。所以，对于H5不能实现的功能，都需要原生去做。而混合框架一般都会在原生代码中预先实现一些访问系统能力的API， 然后暴露给WebView以供JavaScript调用，这样一来，WebView就成为了JavaScript与原生API之间通信的桥梁，主要负责JavaScript与原生之间传递调用消息，而消息的传递必须遵守一个标准的协议，它规定了消息的格式与含义，我们把依赖于WebView的用于在JavaScript与原生之间通信并实现了某种消息传输协议的工具称之为<strong>WebView JavaScript Bridge</strong>, 简称 <strong>JsBridge</strong>，它也是混合开发框架的核心。</p> <h4 id=\"示例-javascript调用原生api获取手机型号\"><a href=\"#示例-javascript调用原生api获取手机型号\" class=\"header-anchor\">#</a> 示例：JavaScript调用原生API获取手机型号</h4> <p>下面我们以Android为例，实现一个获取手机型号的原生API供JavaScript调用。在这个示例中将展示JavaScript调用原生API的流程，读者可以直观的感受一下调用流程。我们选用笔者在Github上开源的dsBridge作为JsBridge来进行通信。dsBridge是一个支持同步调用的跨平台的JsBridge，此示例中只使用其同步调用功能。</p> <ol><li><p>首先在原生中实现获取手机型号的API <code>getPhoneModel</code></p> <div class=\"language-java extra-class\"><pre class=\"language-java\"><code><span class=\"token keyword\">class</span> JSAPI <span class=\"token punctuation\">{</span>\n <span class=\"token annotation punctuation\">@JavascriptInterface</span>\n <span class=\"token keyword\">public</span> <span class=\"token class-name\">Object</span> <span class=\"token function\">getPhoneModel</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">Object</span> msg<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n   <span class=\"token keyword\">return</span> <span class=\"token class-name\">Build</span><span class=\"token punctuation\">.</span>MODEL<span class=\"token punctuation\">;</span>\n <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>    \n</code></pre></div></li> <li><p>将原生API通过WebView注册到JsBridge中</p> <div class=\"language-java extra-class\"><pre class=\"language-java\"><code><span class=\"token keyword\">import</span> <span class=\"token namespace\">wendu<span class=\"token punctuation\">.</span>dsbridge<span class=\"token punctuation\">.</span></span><span class=\"token class-name\">DWebView</span>\n<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token comment\">//DWebView继承自WebView，由dsBridge提供  </span>\n<span class=\"token class-name\">DWebView</span> dwebView <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token class-name\">DWebView</span><span class=\"token punctuation\">)</span> <span class=\"token function\">findViewById</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">R</span><span class=\"token punctuation\">.</span>id<span class=\"token punctuation\">.</span>dwebview<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">//注册原生API到JsBridge</span>\ndwebView<span class=\"token punctuation\">.</span><span class=\"token function\">addJavascriptObject</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">JsAPI</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div></li> <li><p>在JavaScript中调用原生API</p> <div class=\"language-javascript extra-class\"><pre class=\"language-javascript\"><code><span class=\"token keyword\">var</span> dsBridge <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;dsbridge&quot;</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//直接调用原生API `getPhoneModel`</span>\n<span class=\"token keyword\">var</span> model <span class=\"token operator\">=</span> <span class=\"token function\">dsBridge</span><span class=\"token punctuation\">.</span><span class=\"token function\">call</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;getPhoneModel&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">//打印机型</span>\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>model<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div></li></ol> <p>上面示例演示了JavaScript调用原生API的过程，同样的，一般来说优秀的JsBridge也支持原生调用JavaScript，dsBridge也是支持的，如果您感兴趣，可以去github dsBridge项目主页查看。</p> <p>现在，我们回头来看一下，混合应用无非就是在第一步中预先实现一系列API供JavaScript调用，让JavaScript有访问系统的能力，看到这里，我相信你也可以自己实现一个混合开发框架了。</p> <h3 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h3> <p>混合应用的优点是动态内容是H5，web技术栈，社区及资源丰富，缺点是性能不好，对于复杂用户界面或动画，WebView不堪重任。</p> <h2 id=\"_1-1-3-react-native、weex及快应用\"><a href=\"#_1-1-3-react-native、weex及快应用\" class=\"header-anchor\">#</a> 1.1.3 React Native、Weex及快应用</h2> <p>本篇主要介绍一下 <strong>JavaScript开发+原生渲染</strong>的跨平台框架原理。</p> <p>React Native (简称RN)是Facebook于2015年4月开源的跨平台移动应用开发框架，是Facebook早先开源的JS框架 React 在原生移动应用平台的衍生产物，目前支持iOS和Android两个平台。RN使用Javascript语言，类似于HTML的JSX，以及CSS来开发移动应用，因此熟悉Web前端开发的技术人员只需很少的学习就可以进入移动应用开发领域。</p> <p>由于RN和React原理相通，并且Flutter也是受React启发，很多思想也都是相通的，万丈高楼平地起，我们有必要深入了解一下React原理。React是一个响应式的Web框架，我们先了解一下两个重要的概念：DOM树与响应式编程。</p> <h3 id=\"dom树与控件树\"><a href=\"#dom树与控件树\" class=\"header-anchor\">#</a> DOM树与控件树</h3> <p>文档对象模型（Document Object Model，简称DOM），是W3C组织推荐的处理可扩展标志语言的标准编程接口，一种独立于平台和语言的方式访问和修改一个文档的内容和结构。换句话说，这是表示和处理一个HTML或XML文档的标准接口。简单来说，DOM就是文档树，与用户界面控件树对应，在前端开发中通常指HTML对应的渲染树，但广义的DOM也可以指Android中的XML布局文件对应的控件树，而术语<strong>DOM操作</strong>就是指直接来操作渲染树（或控件树）， 因此，可以看到其实DOM树和控件树是等价的概念，只不过前者常用于Web开发中，而后者常用于原生开发中。</p> <h3 id=\"响应式编程\"><a href=\"#响应式编程\" class=\"header-anchor\">#</a> 响应式编程</h3> <p>React中提出一个重要思想：状态改变则UI随之自动改变，而React框架本身就是响应用户状态改变的事件而执行重新构建用户界面的工作，这就是典型的<strong>响应式</strong>编程范式，下面我们总结一下React中响应式原理：</p> <ul><li>开发者只需关注状态转移（数据），当状态发生变化，React框架会自动根据新的状态重新构建UI。</li> <li>React框架在接收到用户状态改变通知后，会根据当前渲染树，结合最新的状态改变，通过Diff算法，计算出树中变化的部分，然后只更新变化的部分（DOM操作），从而避免整棵树重构，提高性能。</li></ul> <p>值得注意的是，在第二步中，状态变化后React框架并不会立即去计算并渲染DOM树的变化部分，相反，React会在DOM的基础上建立一个抽象层，即<strong>虚拟DOM</strong>树，对数据和状态所做的任何改动，都会被自动且高效的同步到虚拟DOM，最后再批量同步到真实DOM中，而不是每次改变都去操作一下DOM。为什么不能每次改变都直接去操作DOM树？这是因为在浏览器中每一次DOM操作都有可能引起浏览器的重绘或回流：</p> <ol><li>如果DOM只是外观风格发生变化，如颜色变化，会导致浏览器重绘界面。</li> <li>如果DOM树的结构发生变化，如尺寸、布局、节点隐藏等导致，浏览器就需要回流（及重新排版布局）。</li></ol> <p>而浏览器的重绘和回流都是比较昂贵的操作，如果每一次改变都直接对DOM进行操作，这会带来性能问题，而批量操作只会触发一次DOM更新。</p> <blockquote><p>思考题：Diff操作和DOM批量更新难道不应该是浏览器的职责吗？第三方框架中去做合不合适？</p></blockquote> <blockquote><p>此处需要有一张插图</p></blockquote> <h3 id=\"react-native\"><a href=\"#react-native\" class=\"header-anchor\">#</a> React Native</h3> <p>上文已经提到React Native 是React 在原生移动应用平台的衍生产物，那两者主要的区别是什么呢？其实，主要的区别在于虚拟DOM映射的对象是什么？React中虚拟DOM最终会映射为浏览器DOM树，而RN中虚拟DOM会通过 JavaScriptCore 映射为原生控件树。</p> <p>JavaScriptCore 是一个JavaScript解释器，它在React Native中主要有两个作用：</p> <ol><li>为JavaScript提供运行环境。</li> <li>是JavaScript与原生应用之间通信的桥梁，作用和JsBridge一样，事实上，在iOS中，很多JsBridge的实现都是基于 JavaScriptCore 。</li></ol> <p>而RN中将虚拟DOM映射为原生控件的过程中分两步：</p> <ol><li>布局消息传递； 将虚拟DOM布局信息传递给原生；</li> <li>原生根据布局信息通过对应的原生控件渲染控件树；</li></ol> <p>至此，React Native 便实现了跨平台。 相对于混合应用，由于React Native是原生控件渲染，所以性能会比混合应用中H5好很多，同时React Native使用了Web开发技术栈，也只需维护一份代码，同样是跨平台框架。</p> <h3 id=\"weex\"><a href=\"#weex\" class=\"header-anchor\">#</a> Weex</h3> <p>Weex是阿里巴巴于2016年发布的跨平台移动端开发框架，思想及原理和React Native类似，最大的不同是语法层面，Weex支持Vue语法和Rax语法，Rax 的 DSL(Domain Specific Language) 语法是基于 React JSX 语法而创造。与 React 不同，在 Rax 中 JSX 是必选的，它不支持通过其它方式创建组件，所以学习 JSX 是使用 Rax 的必要基础。而React Native只支持JSX语法。</p> <h3 id=\"快应用\"><a href=\"#快应用\" class=\"header-anchor\">#</a> 快应用</h3> <p>快应用是华为、小米、OPPO、魅族等国内9大主流手机厂商共同制定的轻量级应用标准，目标直指微信小程序。它也是采用JavaScript语言开发，原生控件渲染，与React Native和Weex相比主要有两点不同：</p> <ol><li>快应用自身不支持Vue或React语法，其采用原生JavaScript开发，其开发框架和微信小程序很像，值得一提的是小程序目前已经可以使用Vue语法开发（mpvue），从原理上来讲，Vue的语法也可以移植到快应用上。</li> <li>React Native和Weex的渲染/排版引擎是集成到框架中的，每一个APP都需要打包一份，安装包体积较大；而快应用渲染/排版引擎是集成到ROM中的，应用中无需打包，安装包体积小，正因如此，快应用才能在保证性能的同时做到快速分发。</li></ol> <h3 id=\"总结-2\"><a href=\"#总结-2\" class=\"header-anchor\">#</a> 总结</h3> <p>JavaScript开发+原生渲染的方式主要优点如下：</p> <ol><li>采用Web开发技术栈，社区庞大、上手快、开发成本相对较低。</li> <li>原生渲染，性能相比H5提高很多。</li> <li>动态化较好，支持热更新。</li></ol> <p>不足：</p> <ol><li>渲染时需要JavaScript和原生之间通信，在有些场景如拖动可能会因为通信频繁导致卡顿。</li> <li>JavaScript为脚本语言，执行时需要JIT(Just In Time)，执行效率和AOT(Ahead Of Time)代码仍有差距。</li> <li>由于渲染依赖原生控件，不同平台的控件需要单独维护，并且当系统更新时，社区控件可能会滞后；除此之外，其控件系统也会受到原生UI系统限制，例如，在Android中，手势冲突消歧规则是固定的，这在使用不同人写的控件嵌套时，手势冲突问题将会变得非常棘手。</li></ol> <h2 id=\"_1-1-4-qt-mobile\"><a href=\"#_1-1-4-qt-mobile\" class=\"header-anchor\">#</a> 1.1.4 QT Mobile</h2> <p>在本篇中，我们看看最后一种跨平台技术：自绘UI+原生。这种技术的思路是，通过在不同平台实现一个统一接口的渲染引擎来绘制UI，而不依赖系统原生控件，所以可以做到不同平台UI的一致性。注意，自绘引擎解决的是UI的跨平台问题，如果涉及其它系统能力调用，依然要涉及原生开发。这种平台技术的优点如下：</p> <ol><li><p>性能高；由于自绘引擎是直接调用系统API来绘制UI，所以性能和原生控件接近。</p></li> <li><p>灵活、组件库易维护、UI外观保真度和一致性高；由于UI渲染不依赖原生控件，也就不需要根据不同平台的控件单独维护一套组件库，所以代码容易维护。由于组件库是同一套代码、同一个渲染引擎，所以在不同平台，组件显示外观可以做到高保真和高一致性；另外，由于不依赖原生控件，也就不会受原生布局系统的限制，这样布局系统会非常灵活。</p></li></ol> <p>不足：</p> <ol><li>动态性不足；为了保证UI绘制性能，自绘UI系统一般都会采用AOT模式编译其发布包，所以应用发布后，不能像Hybrid和RN那些使用JavaScript（JIT）作为开发语言的框架那样动态下发代码。</li> <li>开发效率低：QT使用C++作为其开发语言，而编程效率是直接会影响APP开发效率的，C++作为一门静态语言，在UI开发方面灵活性不及JavaScript这样的动态语言，另外，C++需要开发者手动去管理内存分配，没有JavaScript及Java中垃圾回收（GC）的机制。</li></ol> <p>也许你已经猜到Flutter就属于这一类跨平台技术，没错，Flutter正是实现一套自绘引擎，并拥有一套自己的UI布局系统。不过，自绘制引擎的思路并不是什么新概念，Flutter并不是第一个尝试这么做的，在它之前有一个典型的代表，即大名鼎鼎的QT。</p> <h3 id=\"qt简介\"><a href=\"#qt简介\" class=\"header-anchor\">#</a> QT简介</h3> <p>Qt是一个1991年由Qt Company开发的跨平台C++图形用户界面应用程序开发框架。2008年，Qt Company科技被诺基亚公司收购，Qt也因此成为诺基亚旗下的编程语言工具。2012年，Qt被Digia收购。2014年4月，跨平台集成开发环境Qt Creator 3.1.0正式发布，实现了对于iOS的完全支持，新增WinRT、Beautifier等插件，废弃了无Python接口的GDB调试支持，集成了基于Clang的C/C++代码模块，并对Android支持做出了调整，至此实现了全面支持iOS、Android、WP，它提供给应用程序开发者构建图形用户界面所需的所有功能。但是，QT虽然在PC端获得了巨大成功，备受社区追捧，然而其在移动端却表现不佳，在近几年，虽然偶尔能听到QT的声音，但一直很弱，无论QT本身技术如何、设计思想如何，但事实上终究是败了，究其原因，笔者认为主要有四：</p> <p>第一：QT移动开发社区太小，学习资料不足，生态不好。</p> <p>第二：官方推广不利，支持不够。</p> <p>第三：移动端发力较晚，市场已被其它动态化框架占领（Hybrid和RN)。</p> <p>第四：在移动开发中，C++开发和Web开发栈相比有着先天的劣势，直接结果就是QT开发效率太低。</p> <p>基于此四点，尽管QT是移动端开发跨平台自绘引擎的先驱，但却成为了烈士。</p> <h2 id=\"_1-1-5-flutter出世\"><a href=\"#_1-1-5-flutter出世\" class=\"header-anchor\">#</a> 1.1.5 Flutter出世</h2> <p>“千呼万唤始出来”，铺垫这么久，现在终于等到本书的主角出场了！</p> <p>Flutter是Google发布的一个用于创建跨平台、高性能移动应用的框架。Flutter和QT mobile一样，都没有使用原生控件，相反都实现了一个自绘引擎，使用自身的布局、绘制系统。那么，我们会担心，QT mobile面对的问题Flutter是否也一样，Flutter会不会步入QT mobile后尘，成为另一个烈士？要回到这个问题，我们先来看看Flutter诞生过程：</p> <ul><li>2017 年 Google I/O 大会上，Google 首次推出了一款新的用于创建跨平台、高性能的移动应用框架——Flutter。</li> <li>2018年2月，Flutter发布了第一个Beta版本，同年五月， 在2018年Google I/O 大会上，Flutter 更新到了 beta 3 版本。</li> <li>2018年6月，Flutter发布了首个预览版本，这意味着 Flutter 进入了正式版（1.0）发布前的最后阶段。</li></ul> <p>观其发展，在2018年5月份，Flutter 进入了 GitHub stars 排行榜前 100 名，已有 27k  star。而今天(2019年5月29日)，已经有65K的Star。经历了短短2年多的时间，Flutter 生态系统得以快速增长，由此可见，Flutter在开发者中受到了热烈的欢迎，其未来发展值得期待！</p> <p>现在，我们来和QT mobile做一个对比：</p> <ol><li>生态：从Github上来看，目前Flutter活跃用户正在高速增长。从Stackoverflow上提问来看，Flutter社区现在已经很庞大。Flutter的文档、资源也越来越丰富，开发过程中遇到的很多问题都可以在Stackoverflow或其github issue中找到答案。</li> <li>技术支持：现在Google正在大力推广Flutter，Flutter的作者中很多人都是来自Chromium团队，并且github上活跃度很高。另一个角度，从今年上半年Flutter频繁的版本发布也可以看出Google对Flutter的投入的资源不小，所以在官方技术支持这方面，大可不必担心。</li> <li>开发效率：Flutter的热重载可帮助开发者快速地进行测试、构建UI、添加功能并更快地修复错误。在iOS和Android模拟器或真机上可以实现毫秒级热重载，并且不会丢失状态。这真的很棒，相信我，如果你是一名原生开发者，体验了Flutter开发流后，很可能就不想重新回去做原生了，毕竟很少有人不吐槽原生开发的编译速度。</li></ol> <p>基于以上三点，相信读者和笔者一样，Flutter未来如何，心中自有定论。到现在为止，我们已经对移动端开发技术有了一个全面的了解，接下来我们便要进入本书的主题，你准备好了吗！</p> <h2 id=\"_1-1-6-小结\"><a href=\"#_1-1-6-小结\" class=\"header-anchor\">#</a> 1.1.6 小结</h2> <p>本章主要介绍了目前移动开发中三种跨平台技术，现在我们从框架角度对比一下它们，如表1-1所示：</p> <table><thead><tr><th>技术类型</th> <th>UI渲染方式</th> <th>性能</th> <th>开发效率</th> <th>动态化</th> <th>框架代表</th></tr></thead> <tbody><tr><td>H5+原生</td> <td>WebView渲染</td> <td>一般</td> <td>高</td> <td>支持</td> <td>Cordova、Ionic</td></tr> <tr><td>JavaScript+原生渲染</td> <td>原生控件渲染</td> <td>好</td> <td>中</td> <td>支持</td> <td>RN、Weex</td></tr> <tr><td>自绘UI+原生</td> <td>调用系统API渲染</td> <td>好</td> <td>Flutter高, QT低</td> <td>默认不支持</td> <td>QT、Flutter</td></tr></tbody></table> <center>表1-1: 跨平台技术对比</center>\n上表中开发语言主要指UI的开发语言。而开发效率，是指整个开发周期的效率，包括编码时间、调试时间、以及排错、兼容时间。动态化主要指是否支持动态下发代码和是否支持热更新。值得注意的是Flutter的Release包默认是使用Dart AOT模式编译的，所以不支持动态化，但Dart还有JIT或snapshot运行方式，这些模式都是支持动态化的。\n</div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/182.d30b9f69.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter10/combine.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>10.2 组合现有组件 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/106.9b857fc8.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter10/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_10-2-组合现有组件\"><a href=\"#_10-2-组合现有组件\" class=\"header-anchor\">#</a> 10.2 组合现有组件</h1> <p>在Flutter中页面UI通常都是由一些低阶别的组件组合而成，当我们需要封装一些通用组件时，应该首先考虑是否可以通过组合其它组件来实现，如果可以，则应优先使用组合，因为直接通过现有组件拼装会非常简单、灵活、高效。</p> <h3 id=\"示例-自定义渐变按钮\"><a href=\"#示例-自定义渐变按钮\" class=\"header-anchor\">#</a> 示例：自定义渐变按钮</h3> <p>Flutter Material组件库中的按钮默认不支持渐变背景，为了实现渐变背景按钮，我们自定义一个<code>GradientButton</code>组件，它需要支持一下功能：</p> <ol><li>背景支持渐变色</li> <li>手指按下时有涟漪效果</li> <li>可以支持圆角</li></ol> <p>我们先来看看最终要实现的效果（图10-1）：</p> <p><img src=\"/assets/img/10-1.f2135ea6.png\" alt=\"gradient-button\"></p> <p>我们<code>DecoratedBox</code>可以支持背景色渐变和圆角，<code>InkWell</code>在手指按下有涟漪效果，所以我们可以通过组合<code>DecoratedBox</code>和<code>InkWell</code>来实现<code>GradientButton</code>，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">GradientButton</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">GradientButton</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>colors<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>width<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>height<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>onPressed<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>borderRadius<span class=\"token punctuation\">,</span>\n    <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// 渐变色数组</span>\n  <span class=\"token keyword\">final</span> List<span class=\"token operator\">&lt;</span>Color<span class=\"token operator\">&gt;</span> colors<span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// 按钮宽高</span>\n  <span class=\"token keyword\">final</span> double width<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> double height<span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> Widget child<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> BorderRadius borderRadius<span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">//点击回调</span>\n  <span class=\"token keyword\">final</span> GestureTapCallback onPressed<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    ThemeData theme <span class=\"token operator\">=</span> Theme<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token comment\">//确保colors数组不空</span>\n    List<span class=\"token operator\">&lt;</span>Color<span class=\"token operator\">&gt;</span> _colors <span class=\"token operator\">=</span> colors <span class=\"token operator\">?</span><span class=\"token operator\">?</span>\n        <span class=\"token punctuation\">[</span>theme<span class=\"token punctuation\">.</span>primaryColor<span class=\"token punctuation\">,</span> theme<span class=\"token punctuation\">.</span>primaryColorDark <span class=\"token operator\">?</span><span class=\"token operator\">?</span> theme<span class=\"token punctuation\">.</span>primaryColor<span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">return</span> <span class=\"token function\">DecoratedBox</span><span class=\"token punctuation\">(</span>\n      decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>\n        gradient<span class=\"token punctuation\">:</span> <span class=\"token function\">LinearGradient</span><span class=\"token punctuation\">(</span>colors<span class=\"token punctuation\">:</span> _colors<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        borderRadius<span class=\"token punctuation\">:</span> borderRadius<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Material</span><span class=\"token punctuation\">(</span>\n        type<span class=\"token punctuation\">:</span> MaterialType<span class=\"token punctuation\">.</span>transparency<span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">InkWell</span><span class=\"token punctuation\">(</span>\n          splashColor<span class=\"token punctuation\">:</span> _colors<span class=\"token punctuation\">.</span>last<span class=\"token punctuation\">,</span>\n          highlightColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>transparent<span class=\"token punctuation\">,</span>\n          borderRadius<span class=\"token punctuation\">:</span> borderRadius<span class=\"token punctuation\">,</span>\n          onTap<span class=\"token punctuation\">:</span> onPressed<span class=\"token punctuation\">,</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">ConstrainedBox</span><span class=\"token punctuation\">(</span>\n            constraints<span class=\"token punctuation\">:</span> BoxConstraints<span class=\"token punctuation\">.</span><span class=\"token function\">tightFor</span><span class=\"token punctuation\">(</span>height<span class=\"token punctuation\">:</span> height<span class=\"token punctuation\">,</span> width<span class=\"token punctuation\">:</span> width<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n                padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">8.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                child<span class=\"token punctuation\">:</span> <span class=\"token function\">DefaultTextStyle</span><span class=\"token punctuation\">(</span>\n                  style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>fontWeight<span class=\"token punctuation\">:</span> FontWeight<span class=\"token punctuation\">.</span>bold<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  child<span class=\"token punctuation\">:</span> child<span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以看到<code>GradientButton</code>是由<code>DecoratedBox</code>、<code>Padding</code>、<code>Center</code>、<code>InkWell</code>等组件组合而成。当然上面的代码只是一个示例，作为一个按钮它还并不完整，比如没有禁用状态，读者可以根据实际需要来完善。</p> <h4 id=\"使用gradientbutton\"><a href=\"#使用gradientbutton\" class=\"header-anchor\">#</a> 使用GradientButton</h4> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'../widgets/index.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">GradientButtonRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _GradientButtonRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">_GradientButtonRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_GradientButtonRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>GradientButtonRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n        children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n          <span class=\"token function\">GradientButton</span><span class=\"token punctuation\">(</span>\n            colors<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>Colors<span class=\"token punctuation\">.</span>orange<span class=\"token punctuation\">,</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n            height<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Submit&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            onPressed<span class=\"token punctuation\">:</span> onTap<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">GradientButton</span><span class=\"token punctuation\">(</span>\n            height<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n            colors<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>Colors<span class=\"token punctuation\">.</span>lightGreen<span class=\"token punctuation\">,</span> Colors<span class=\"token punctuation\">.</span>green<span class=\"token punctuation\">[</span><span class=\"token number\">700</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Submit&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            onPressed<span class=\"token punctuation\">:</span> onTap<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">GradientButton</span><span class=\"token punctuation\">(</span>\n            height<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n            colors<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>Colors<span class=\"token punctuation\">.</span>lightBlue<span class=\"token punctuation\">[</span><span class=\"token number\">300</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> Colors<span class=\"token punctuation\">.</span>blueAccent<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Submit&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            onPressed<span class=\"token punctuation\">:</span> onTap<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token function\">onTap</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;button click&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h3 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h3> <p>通过组合的方式定义组件和我们之前写界面并无差异，不过在抽离出单独的组件时我们要考虑代码规范性，如必要参数要用<code>@required</code> 标注，对于可选参数在特定场景需要判空或设置默认值等。这是由于使用者大多时候可能不了解组件的内部细节，所以为了保证代码健壮性，我们需要在用户错误地使用组件时能够兼容或报错提示（使用<code>assert</code>断言函数）。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/106.9b857fc8.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter10/custom_paint.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>10.4 自绘组件 （CustomPaint与Canvas） | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/107.188f42e4.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter10/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_10-4-自绘组件-custompaint与canvas\"><a href=\"#_10-4-自绘组件-custompaint与canvas\" class=\"header-anchor\">#</a> 10.4 自绘组件 （CustomPaint与Canvas）</h1> <p>对于一些复杂或不规则的UI，我们可能无法通过组合其它组件的方式来实现，比如我们需要一个正六边形、一个渐变的圆形进度条、一个棋盘等。当然，有时候我们可以使用图片来实现，但在一些需要动态交互的场景静态图片也是实现不了的，比如要实现一个手写输入面板，这时，我们就需要来自己绘制UI外观。</p> <p>几乎所有的UI系统都会提供一个自绘UI的接口，这个接口通常会提供一块2D画布<code>Canvas</code>，<code>Canvas</code>内部封装了一些基本绘制的API，开发者可以通过<code>Canvas</code>绘制各种自定义图形。在Flutter中，提供了一个<code>CustomPaint</code> 组件，它可以结合画笔<code>CustomPainter</code>来实现自定义图形绘制。</p> <h3 id=\"custompaint\"><a href=\"#custompaint\" class=\"header-anchor\">#</a> CustomPaint</h3> <p>我们看看<code>CustomPaint</code>构造函数：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">CustomPaint</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  Key key<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>painter<span class=\"token punctuation\">,</span> \n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>foregroundPainter<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>size <span class=\"token operator\">=</span> Size<span class=\"token punctuation\">.</span>zero<span class=\"token punctuation\">,</span> \n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>isComplex <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span> \n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>willChange <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span> \n  Widget child<span class=\"token punctuation\">,</span> <span class=\"token comment\">//子节点，可以为空</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><ul><li><code>painter</code>: 背景画笔，会显示在子节点后面;</li> <li><code>foregroundPainter</code>: 前景画笔，会显示在子节点前面</li> <li><code>size</code>：当child为null时，代表默认绘制区域大小，如果有child则忽略此参数，画布尺寸则为child尺寸。如果有child但是想指定画布为特定大小，可以使用SizeBox包裹CustomPaint实现。</li> <li><code>isComplex</code>：是否复杂的绘制，如果是，Flutter会应用一些缓存策略来减少重复渲染的开销。</li> <li><code>willChange</code>：和<code>isComplex</code>配合使用，当启用缓存时，该属性代表在下一帧中绘制是否会改变。</li></ul> <p>可以看到，绘制时我们需要提供前景或背景画笔，两者也可以同时提供。我们的画笔需要继承<code>CustomPainter</code>类，我们在画笔类中实现真正的绘制逻辑。</p> <h4 id=\"注意\"><a href=\"#注意\" class=\"header-anchor\">#</a> 注意</h4> <p>如果<code>CustomPaint</code>有子节点，为了避免子节点不必要的重绘并提高性能，通常情况下都会将子节点包裹在<code>RepaintBoundary</code>组件中，这样会在绘制时就会创建一个新的绘制层（Layer），其子组件将在新的Layer上绘制，而父组件将在原来Layer上绘制，也就是说<code>RepaintBoundary</code> 子组件的绘制将独立于父组件的绘制，<code>RepaintBoundary</code>会隔离其子节点和<code>CustomPaint</code>本身的绘制边界。示例如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">CustomPaint</span><span class=\"token punctuation\">(</span>\n  size<span class=\"token punctuation\">:</span> <span class=\"token function\">Size</span><span class=\"token punctuation\">(</span><span class=\"token number\">300</span><span class=\"token punctuation\">,</span> <span class=\"token number\">300</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//指定画布大小</span>\n  painter<span class=\"token punctuation\">:</span> <span class=\"token function\">MyPainter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">RepaintBoundary</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">:</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> \n<span class=\"token punctuation\">)</span>\n</code></pre></div><h3 id=\"custompainter\"><a href=\"#custompainter\" class=\"header-anchor\">#</a> CustomPainter</h3> <p><code>CustomPainter</code>中提定义了一个虚函数<code>paint</code>：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>void paint(Canvas canvas, Size size);\n</code></pre></div><p><code>paint</code>有两个参数:</p> <ul><li><p><code>Canvas</code>：一个画布，包括各种绘制方法，我们列出一下常用的方法：</p> <table><thead><tr><th>API名称</th> <th>功能</th></tr></thead> <tbody><tr><td>drawLine</td> <td>画线</td></tr> <tr><td>drawPoint</td> <td>画点</td></tr> <tr><td>drawPath</td> <td>画路径</td></tr> <tr><td>drawImage</td> <td>画图像</td></tr> <tr><td>drawRect</td> <td>画矩形</td></tr> <tr><td>drawCircle</td> <td>画圆</td></tr> <tr><td>drawOval</td> <td>画椭圆</td></tr> <tr><td>drawArc</td> <td>画圆弧</td></tr></tbody></table></li> <li><p><code>Size</code>：当前绘制区域大小。</p></li></ul> <h3 id=\"画笔paint\"><a href=\"#画笔paint\" class=\"header-anchor\">#</a> 画笔Paint</h3> <p>现在画布有了，我们最后还缺一个画笔，Flutter提供了<code>Paint</code>类来实现画笔。在<code>Paint</code>中，我们可以配置画笔的各种属性如粗细、颜色、样式等。如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">var</span> paint <span class=\"token operator\">=</span> <span class=\"token function\">Paint</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//创建一个画笔并配置其属性</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>isAntiAlias <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span> <span class=\"token comment\">//是否抗锯齿</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>style <span class=\"token operator\">=</span> PaintingStyle<span class=\"token punctuation\">.</span>fill <span class=\"token comment\">//画笔样式：填充</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>color<span class=\"token operator\">=</span><span class=\"token function\">Color</span><span class=\"token punctuation\">(</span><span class=\"token number\">0x77cdb175</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span><span class=\"token comment\">//画笔颜色</span>\n</code></pre></div><p>更多的配置属性读者可以参考Paint类定义。</p> <h3 id=\"示例-五子棋-盘\"><a href=\"#示例-五子棋-盘\" class=\"header-anchor\">#</a> 示例：五子棋/盘</h3> <p>下面我们通过一个五子棋游戏中棋盘和棋子的绘制来演示自绘UI的过程，首先我们看一下我们的目标效果，如图10-3所示：</p> <p><img src=\"/assets/img/10-3.3989fea9.png\" alt=\"图10-3\"></p> <p>代码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'dart:math'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">CustomPaintRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">CustomPaint</span><span class=\"token punctuation\">(</span>\n        size<span class=\"token punctuation\">:</span> <span class=\"token function\">Size</span><span class=\"token punctuation\">(</span><span class=\"token number\">300</span><span class=\"token punctuation\">,</span> <span class=\"token number\">300</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//指定画布大小</span>\n        painter<span class=\"token punctuation\">:</span> <span class=\"token function\">MyPainter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">MyPainter</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">CustomPainter</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">paint</span><span class=\"token punctuation\">(</span>Canvas canvas<span class=\"token punctuation\">,</span> Size size<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    double eWidth <span class=\"token operator\">=</span> size<span class=\"token punctuation\">.</span>width <span class=\"token operator\">/</span> <span class=\"token number\">15</span><span class=\"token punctuation\">;</span>\n    double eHeight <span class=\"token operator\">=</span> size<span class=\"token punctuation\">.</span>height <span class=\"token operator\">/</span> <span class=\"token number\">15</span><span class=\"token punctuation\">;</span>\n      \n    <span class=\"token comment\">//画棋盘背景</span>\n    <span class=\"token keyword\">var</span> paint <span class=\"token operator\">=</span> <span class=\"token function\">Paint</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>isAntiAlias <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span>\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>style <span class=\"token operator\">=</span> PaintingStyle<span class=\"token punctuation\">.</span>fill <span class=\"token comment\">//填充</span>\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>color <span class=\"token operator\">=</span> <span class=\"token function\">Color</span><span class=\"token punctuation\">(</span><span class=\"token number\">0x77cdb175</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//背景为纸黄色</span>\n    canvas<span class=\"token punctuation\">.</span><span class=\"token function\">drawRect</span><span class=\"token punctuation\">(</span>Offset<span class=\"token punctuation\">.</span>zero <span class=\"token operator\">&amp;</span> size<span class=\"token punctuation\">,</span> paint<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token comment\">//画棋盘网格</span>\n    paint\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>style <span class=\"token operator\">=</span> PaintingStyle<span class=\"token punctuation\">.</span>stroke <span class=\"token comment\">//线</span>\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>color <span class=\"token operator\">=</span> Colors<span class=\"token punctuation\">.</span>black87\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>strokeWidth <span class=\"token operator\">=</span> <span class=\"token number\">1.0</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span>int i <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span> i <span class=\"token operator\">&lt;=</span> <span class=\"token number\">15</span><span class=\"token punctuation\">;</span> <span class=\"token operator\">++</span>i<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      double dy <span class=\"token operator\">=</span> eHeight <span class=\"token operator\">*</span> i<span class=\"token punctuation\">;</span>\n      canvas<span class=\"token punctuation\">.</span><span class=\"token function\">drawLine</span><span class=\"token punctuation\">(</span><span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">,</span> dy<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span>size<span class=\"token punctuation\">.</span>width<span class=\"token punctuation\">,</span> dy<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> paint<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span>int i <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span> i <span class=\"token operator\">&lt;=</span> <span class=\"token number\">15</span><span class=\"token punctuation\">;</span> <span class=\"token operator\">++</span>i<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      double dx <span class=\"token operator\">=</span> eWidth <span class=\"token operator\">*</span> i<span class=\"token punctuation\">;</span>\n      canvas<span class=\"token punctuation\">.</span><span class=\"token function\">drawLine</span><span class=\"token punctuation\">(</span><span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span>dx<span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span>dx<span class=\"token punctuation\">,</span> size<span class=\"token punctuation\">.</span>height<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> paint<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token comment\">//画一个黑子</span>\n    paint\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>style <span class=\"token operator\">=</span> PaintingStyle<span class=\"token punctuation\">.</span>fill\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>color <span class=\"token operator\">=</span> Colors<span class=\"token punctuation\">.</span>black<span class=\"token punctuation\">;</span>\n    canvas<span class=\"token punctuation\">.</span><span class=\"token function\">drawCircle</span><span class=\"token punctuation\">(</span>\n      <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span>size<span class=\"token punctuation\">.</span>width <span class=\"token operator\">/</span> <span class=\"token number\">2</span> <span class=\"token operator\">-</span> eWidth <span class=\"token operator\">/</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> size<span class=\"token punctuation\">.</span>height <span class=\"token operator\">/</span> <span class=\"token number\">2</span> <span class=\"token operator\">-</span> eHeight <span class=\"token operator\">/</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token function\">min</span><span class=\"token punctuation\">(</span>eWidth <span class=\"token operator\">/</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> eHeight <span class=\"token operator\">/</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span>\n      paint<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      \n    <span class=\"token comment\">//画一个白子</span>\n    paint<span class=\"token punctuation\">.</span>color <span class=\"token operator\">=</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">;</span>\n    canvas<span class=\"token punctuation\">.</span><span class=\"token function\">drawCircle</span><span class=\"token punctuation\">(</span>\n      <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span>size<span class=\"token punctuation\">.</span>width <span class=\"token operator\">/</span> <span class=\"token number\">2</span> <span class=\"token operator\">+</span> eWidth <span class=\"token operator\">/</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> size<span class=\"token punctuation\">.</span>height <span class=\"token operator\">/</span> <span class=\"token number\">2</span> <span class=\"token operator\">-</span> eHeight <span class=\"token operator\">/</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token function\">min</span><span class=\"token punctuation\">(</span>eWidth <span class=\"token operator\">/</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> eHeight <span class=\"token operator\">/</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span>\n      paint<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">//在实际场景中正确利用此回调可以避免重绘开销，本示例我们简单的返回true</span>\n  <span class=\"token metadata symbol\">@override</span>\n  bool <span class=\"token function\">shouldRepaint</span><span class=\"token punctuation\">(</span>CustomPainter oldDelegate<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h3 id=\"性能\"><a href=\"#性能\" class=\"header-anchor\">#</a> 性能</h3> <p>绘制是比较昂贵的操作，所以我们在实现自绘控件时应该考虑到性能开销，下面是两条关于性能优化的建议：</p> <ul><li><p>尽可能的利用好<code>shouldRepaint</code>返回值；在UI树重新build时，控件在绘制前都会先调用该方法以确定是否有必要重绘；假如我们绘制的UI不依赖外部状态，那么就应该始终返回<code>false</code>，因为外部状态改变导致重新build时不会影响我们的UI外观；如果绘制依赖外部状态，那么我们就应该在<code>shouldRepaint</code>中判断依赖的状态是否改变，如果已改变则应返回<code>true</code>来重绘，反之则应返回<code>false</code>不需要重绘。</p></li> <li><p>绘制尽可能多的分层；在上面五子棋的示例中，我们将棋盘和棋子的绘制放在了一起，这样会有一个问题：由于棋盘始终是不变的，用户每次落子时变的只是棋子，但是如果按照上面的代码来实现，每次绘制棋子时都要重新绘制一次棋盘，这是没必要的。优化的方法就是将棋盘单独抽为一个组件，并设置其<code>shouldRepaint</code>回调值为<code>false</code>，然后将棋盘组件作为背景。然后将棋子的绘制放到另一个组件中，这样每次落子时只需要绘制棋子。</p></li></ul> <h3 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h3> <p>自绘控件非常强大，理论上可以实现任何2D图形外观，实际上Flutter提供的所有组件最终都是通过调用Canvas绘制出来的，只不过绘制的逻辑被封装起来了，读者有兴趣可以查看具有外观样式的组件源码，找到其对应的<code>RenderObject</code>对象，如<code>Text</code>对应的<code>RenderParagraph</code>对象最终会通过<code>Canvas</code>实现文本绘制逻辑。下一节我们会再通过一个自绘的圆形背景渐变进度条的实例来帮助读者加深印象。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/107.188f42e4.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter10/gradient_circular_progress_demo.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>10.5 自绘实例：圆形背景渐变进度条 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/108.47fd6022.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter10/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_10-5-自绘实例-圆形背景渐变进度条\"><a href=\"#_10-5-自绘实例-圆形背景渐变进度条\" class=\"header-anchor\">#</a> 10.5 自绘实例：圆形背景渐变进度条</h1> <p>本节我们实现一个圆形背景渐变进度条，它支持：</p> <ol><li>支持多种背景渐变色。</li> <li>任意弧度；进度条可以不是整圆。</li> <li>可以自定义粗细、两端是否圆角等样式。</li></ol> <p>可以发现要实现这样的一个进度条是无法通过现有组件组合而成的，所以我们通过自绘方式实现，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'dart:math'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">GradientCircularProgressIndicator</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">GradientCircularProgressIndicator</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>strokeWidth <span class=\"token operator\">=</span> <span class=\"token number\">2.0</span><span class=\"token punctuation\">,</span>\n    <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>radius<span class=\"token punctuation\">,</span>\n    <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>colors<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>stops<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>strokeCapRound <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>backgroundColor <span class=\"token operator\">=</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Color</span><span class=\"token punctuation\">(</span><span class=\"token number\">0xFFEEEEEE</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>totalAngle <span class=\"token operator\">=</span> <span class=\"token number\">2</span> <span class=\"token operator\">*</span> pi<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>value\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">///粗细</span>\n  <span class=\"token keyword\">final</span> double strokeWidth<span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">/// 圆的半径</span>\n  <span class=\"token keyword\">final</span> double radius<span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">///两端是否为圆角</span>\n  <span class=\"token keyword\">final</span> bool strokeCapRound<span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">/// 当前进度，取值范围 [0.0-1.0]</span>\n  <span class=\"token keyword\">final</span> double value<span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">/// 进度条背景色</span>\n  <span class=\"token keyword\">final</span> Color backgroundColor<span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">/// 进度条的总弧度，2*PI为整圆，小于2*PI则不是整圆</span>\n  <span class=\"token keyword\">final</span> double totalAngle<span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">/// 渐变色数组</span>\n  <span class=\"token keyword\">final</span> List<span class=\"token operator\">&lt;</span>Color<span class=\"token operator\">&gt;</span> colors<span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">/// 渐变色的终止点，对应colors属性</span>\n  <span class=\"token keyword\">final</span> List<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> stops<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    double _offset <span class=\"token operator\">=</span> <span class=\"token number\">.0</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">// 如果两端为圆角，则需要对起始位置进行调整，否则圆角部分会偏离起始位置</span>\n    <span class=\"token comment\">// 下面调整的角度的计算公式是通过数学几何知识得出，读者有兴趣可以研究一下为什么是这样  </span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>strokeCapRound<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _offset <span class=\"token operator\">=</span> <span class=\"token function\">asin</span><span class=\"token punctuation\">(</span>strokeWidth <span class=\"token operator\">/</span> <span class=\"token punctuation\">(</span>radius <span class=\"token operator\">*</span> <span class=\"token number\">2</span> <span class=\"token operator\">-</span> strokeWidth<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">var</span> _colors <span class=\"token operator\">=</span> colors<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>_colors <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      Color color <span class=\"token operator\">=</span> Theme\n          <span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span>\n          <span class=\"token punctuation\">.</span>accentColor<span class=\"token punctuation\">;</span>\n      _colors <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span>color<span class=\"token punctuation\">,</span> color<span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">return</span> Transform<span class=\"token punctuation\">.</span><span class=\"token function\">rotate</span><span class=\"token punctuation\">(</span>\n      angle<span class=\"token punctuation\">:</span> <span class=\"token operator\">-</span>pi <span class=\"token operator\">/</span> <span class=\"token number\">2.0</span> <span class=\"token operator\">-</span> _offset<span class=\"token punctuation\">,</span> \n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">CustomPaint</span><span class=\"token punctuation\">(</span>\n          size<span class=\"token punctuation\">:</span> Size<span class=\"token punctuation\">.</span><span class=\"token function\">fromRadius</span><span class=\"token punctuation\">(</span>radius<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          painter<span class=\"token punctuation\">:</span> <span class=\"token function\">_GradientCircularProgressPainter</span><span class=\"token punctuation\">(</span>\n            strokeWidth<span class=\"token punctuation\">:</span> strokeWidth<span class=\"token punctuation\">,</span>\n            strokeCapRound<span class=\"token punctuation\">:</span> strokeCapRound<span class=\"token punctuation\">,</span>\n            backgroundColor<span class=\"token punctuation\">:</span> backgroundColor<span class=\"token punctuation\">,</span>\n            value<span class=\"token punctuation\">:</span> value<span class=\"token punctuation\">,</span>\n            total<span class=\"token punctuation\">:</span> totalAngle<span class=\"token punctuation\">,</span>\n            radius<span class=\"token punctuation\">:</span> radius<span class=\"token punctuation\">,</span>\n            colors<span class=\"token punctuation\">:</span> _colors<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">//实现画笔</span>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_GradientCircularProgressPainter</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">CustomPainter</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">_GradientCircularProgressPainter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>strokeWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">10.0</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>strokeCapRound<span class=\"token punctuation\">:</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>backgroundColor <span class=\"token operator\">=</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Color</span><span class=\"token punctuation\">(</span><span class=\"token number\">0xFFEEEEEE</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>radius<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>total <span class=\"token operator\">=</span> <span class=\"token number\">2</span> <span class=\"token operator\">*</span> pi<span class=\"token punctuation\">,</span>\n    <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>colors<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>stops<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>value\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> double strokeWidth<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> bool strokeCapRound<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> double value<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> Color backgroundColor<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> List<span class=\"token operator\">&lt;</span>Color<span class=\"token operator\">&gt;</span> colors<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> double total<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> double radius<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> List<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> stops<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">paint</span><span class=\"token punctuation\">(</span>Canvas canvas<span class=\"token punctuation\">,</span> Size size<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>radius <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      size <span class=\"token operator\">=</span> Size<span class=\"token punctuation\">.</span><span class=\"token function\">fromRadius</span><span class=\"token punctuation\">(</span>radius<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    double _offset <span class=\"token operator\">=</span> strokeWidth <span class=\"token operator\">/</span> <span class=\"token number\">2.0</span><span class=\"token punctuation\">;</span>\n    double _value <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>value <span class=\"token operator\">?</span><span class=\"token operator\">?</span> <span class=\"token number\">.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    _value <span class=\"token operator\">=</span> _value<span class=\"token punctuation\">.</span><span class=\"token function\">clamp</span><span class=\"token punctuation\">(</span><span class=\"token number\">.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">1.0</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">*</span> total<span class=\"token punctuation\">;</span>\n    double _start <span class=\"token operator\">=</span> <span class=\"token number\">.0</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>strokeCapRound<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _start <span class=\"token operator\">=</span> <span class=\"token function\">asin</span><span class=\"token punctuation\">(</span>strokeWidth<span class=\"token operator\">/</span> <span class=\"token punctuation\">(</span>size<span class=\"token punctuation\">.</span>width <span class=\"token operator\">-</span> strokeWidth<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    Rect rect <span class=\"token operator\">=</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span>_offset<span class=\"token punctuation\">,</span> _offset<span class=\"token punctuation\">)</span> <span class=\"token operator\">&amp;</span> <span class=\"token function\">Size</span><span class=\"token punctuation\">(</span>\n        size<span class=\"token punctuation\">.</span>width <span class=\"token operator\">-</span> strokeWidth<span class=\"token punctuation\">,</span>\n        size<span class=\"token punctuation\">.</span>height <span class=\"token operator\">-</span> strokeWidth\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">var</span> paint <span class=\"token operator\">=</span> <span class=\"token function\">Paint</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>strokeCap <span class=\"token operator\">=</span> strokeCapRound <span class=\"token operator\">?</span> StrokeCap<span class=\"token punctuation\">.</span>round <span class=\"token punctuation\">:</span> StrokeCap<span class=\"token punctuation\">.</span>butt\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>style <span class=\"token operator\">=</span> PaintingStyle<span class=\"token punctuation\">.</span>stroke\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>isAntiAlias <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span>\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>strokeWidth <span class=\"token operator\">=</span> strokeWidth<span class=\"token punctuation\">;</span>\n\n    <span class=\"token comment\">// 先画背景</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>backgroundColor <span class=\"token operator\">!=</span> Colors<span class=\"token punctuation\">.</span>transparent<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      paint<span class=\"token punctuation\">.</span>color <span class=\"token operator\">=</span> backgroundColor<span class=\"token punctuation\">;</span>\n      canvas<span class=\"token punctuation\">.</span><span class=\"token function\">drawArc</span><span class=\"token punctuation\">(</span>\n          rect<span class=\"token punctuation\">,</span>\n          _start<span class=\"token punctuation\">,</span>\n          total<span class=\"token punctuation\">,</span>\n          <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n          paint\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token comment\">// 再画前景，应用渐变</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>_value <span class=\"token operator\">&gt;</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      paint<span class=\"token punctuation\">.</span>shader <span class=\"token operator\">=</span> <span class=\"token function\">SweepGradient</span><span class=\"token punctuation\">(</span>\n        startAngle<span class=\"token punctuation\">:</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span>\n        endAngle<span class=\"token punctuation\">:</span> _value<span class=\"token punctuation\">,</span>\n        colors<span class=\"token punctuation\">:</span> colors<span class=\"token punctuation\">,</span>\n        stops<span class=\"token punctuation\">:</span> stops<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">createShader</span><span class=\"token punctuation\">(</span>rect<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n      canvas<span class=\"token punctuation\">.</span><span class=\"token function\">drawArc</span><span class=\"token punctuation\">(</span>\n          rect<span class=\"token punctuation\">,</span>\n          _start<span class=\"token punctuation\">,</span>\n          _value<span class=\"token punctuation\">,</span>\n          <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n          paint\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  bool <span class=\"token function\">shouldRepaint</span><span class=\"token punctuation\">(</span>CustomPainter oldDelegate<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>下面我们来测试一下，为了尽可能多的展示<code>GradientCircularProgressIndicator</code>的不同外观和用途，这个示例代码会比较长，并且添加了动画，建议读者将此示例运行起来观看实际效果，我们先看看其中的一帧动画的截图：</p> <p><img src=\"/assets/img/gradient_circular_progress.4cea87cb.png\" alt=\"gradient_circular_progress\"></p> <p>示例代码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'dart:math'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'../widgets/index.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">GradientCircularProgressRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  GradientCircularProgressRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">GradientCircularProgressRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">GradientCircularProgressRouteState</span>\n    <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>GradientCircularProgressRoute<span class=\"token operator\">&gt;</span> <span class=\"token keyword\">with</span> TickerProviderStateMixin <span class=\"token punctuation\">{</span>\n  AnimationController _animationController<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    _animationController <span class=\"token operator\">=</span>\n        <span class=\"token keyword\">new</span> <span class=\"token class-name\">AnimationController</span><span class=\"token punctuation\">(</span>vsync<span class=\"token punctuation\">:</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">,</span> duration<span class=\"token punctuation\">:</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">3</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    bool isForward <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n    _animationController<span class=\"token punctuation\">.</span><span class=\"token function\">addStatusListener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>status<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>status <span class=\"token operator\">==</span> AnimationStatus<span class=\"token punctuation\">.</span>forward<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        isForward <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>status <span class=\"token operator\">==</span> AnimationStatus<span class=\"token punctuation\">.</span>completed <span class=\"token operator\">||</span>\n          status <span class=\"token operator\">==</span> AnimationStatus<span class=\"token punctuation\">.</span>dismissed<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>isForward<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          _animationController<span class=\"token punctuation\">.</span><span class=\"token function\">reverse</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n          _animationController<span class=\"token punctuation\">.</span><span class=\"token function\">forward</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n      <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>status <span class=\"token operator\">==</span> AnimationStatus<span class=\"token punctuation\">.</span>reverse<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        isForward <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    _animationController<span class=\"token punctuation\">.</span><span class=\"token function\">forward</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    _animationController<span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">SingleChildScrollView</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n          crossAxisAlignment<span class=\"token punctuation\">:</span> CrossAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            <span class=\"token function\">AnimatedBuilder</span><span class=\"token punctuation\">(</span>\n              animation<span class=\"token punctuation\">:</span> _animationController<span class=\"token punctuation\">,</span>\n              builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> Widget child<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token keyword\">return</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n                  padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">symmetric</span><span class=\"token punctuation\">(</span>vertical<span class=\"token punctuation\">:</span> <span class=\"token number\">16.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n                    children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                      <span class=\"token function\">Wrap</span><span class=\"token punctuation\">(</span>\n                        spacing<span class=\"token punctuation\">:</span> <span class=\"token number\">10.0</span><span class=\"token punctuation\">,</span>\n                        runSpacing<span class=\"token punctuation\">:</span> <span class=\"token number\">16.0</span><span class=\"token punctuation\">,</span>\n                        children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                          <span class=\"token function\">GradientCircularProgressIndicator</span><span class=\"token punctuation\">(</span>\n                            <span class=\"token comment\">// No gradient</span>\n                            colors<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                            radius<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n                            strokeWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">3.0</span><span class=\"token punctuation\">,</span>\n                            value<span class=\"token punctuation\">:</span> _animationController<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span>\n                          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                          <span class=\"token function\">GradientCircularProgressIndicator</span><span class=\"token punctuation\">(</span>\n                            colors<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span> Colors<span class=\"token punctuation\">.</span>orange<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                            radius<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n                            strokeWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">3.0</span><span class=\"token punctuation\">,</span>\n                            value<span class=\"token punctuation\">:</span> _animationController<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span>\n                          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                          <span class=\"token function\">GradientCircularProgressIndicator</span><span class=\"token punctuation\">(</span>\n                            colors<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span> Colors<span class=\"token punctuation\">.</span>orange<span class=\"token punctuation\">,</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                            radius<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n                            strokeWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">5.0</span><span class=\"token punctuation\">,</span>\n                            value<span class=\"token punctuation\">:</span> _animationController<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span>\n                          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                          <span class=\"token function\">GradientCircularProgressIndicator</span><span class=\"token punctuation\">(</span>\n                            colors<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>Colors<span class=\"token punctuation\">.</span>teal<span class=\"token punctuation\">,</span> Colors<span class=\"token punctuation\">.</span>cyan<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                            radius<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n                            strokeWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">5.0</span><span class=\"token punctuation\">,</span>\n                            strokeCapRound<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n                            value<span class=\"token punctuation\">:</span> <span class=\"token function\">CurvedAnimation</span><span class=\"token punctuation\">(</span>\n                                    parent<span class=\"token punctuation\">:</span> _animationController<span class=\"token punctuation\">,</span>\n                                    curve<span class=\"token punctuation\">:</span> Curves<span class=\"token punctuation\">.</span>decelerate<span class=\"token punctuation\">)</span>\n                                <span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span>\n                          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                          <span class=\"token function\">TurnBox</span><span class=\"token punctuation\">(</span>\n                            turns<span class=\"token punctuation\">:</span> <span class=\"token number\">1</span> <span class=\"token operator\">/</span> <span class=\"token number\">8</span><span class=\"token punctuation\">,</span>\n                            child<span class=\"token punctuation\">:</span> <span class=\"token function\">GradientCircularProgressIndicator</span><span class=\"token punctuation\">(</span>\n                                colors<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span> Colors<span class=\"token punctuation\">.</span>orange<span class=\"token punctuation\">,</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                                radius<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n                                strokeWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">5.0</span><span class=\"token punctuation\">,</span>\n                                strokeCapRound<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n                                backgroundColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">[</span><span class=\"token number\">50</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                                totalAngle<span class=\"token punctuation\">:</span> <span class=\"token number\">1.5</span> <span class=\"token operator\">*</span> pi<span class=\"token punctuation\">,</span>\n                                value<span class=\"token punctuation\">:</span> <span class=\"token function\">CurvedAnimation</span><span class=\"token punctuation\">(</span>\n                                        parent<span class=\"token punctuation\">:</span> _animationController<span class=\"token punctuation\">,</span>\n                                        curve<span class=\"token punctuation\">:</span> Curves<span class=\"token punctuation\">.</span>ease<span class=\"token punctuation\">)</span>\n                                    <span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                          <span class=\"token function\">RotatedBox</span><span class=\"token punctuation\">(</span>\n                            quarterTurns<span class=\"token punctuation\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n                            child<span class=\"token punctuation\">:</span> <span class=\"token function\">GradientCircularProgressIndicator</span><span class=\"token punctuation\">(</span>\n                                colors<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">[</span><span class=\"token number\">700</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">[</span><span class=\"token number\">200</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                                radius<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n                                strokeWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">3.0</span><span class=\"token punctuation\">,</span>\n                                strokeCapRound<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n                                backgroundColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>transparent<span class=\"token punctuation\">,</span>\n                                value<span class=\"token punctuation\">:</span> _animationController<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                          <span class=\"token function\">GradientCircularProgressIndicator</span><span class=\"token punctuation\">(</span>\n                            colors<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>\n                              Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span>\n                              Colors<span class=\"token punctuation\">.</span>amber<span class=\"token punctuation\">,</span>\n                              Colors<span class=\"token punctuation\">.</span>cyan<span class=\"token punctuation\">,</span>\n                              Colors<span class=\"token punctuation\">.</span>green<span class=\"token punctuation\">[</span><span class=\"token number\">200</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                              Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span>\n                              Colors<span class=\"token punctuation\">.</span>red\n                            <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                            radius<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n                            strokeWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">5.0</span><span class=\"token punctuation\">,</span>\n                            strokeCapRound<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n                            value<span class=\"token punctuation\">:</span> _animationController<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span>\n                          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                      <span class=\"token function\">GradientCircularProgressIndicator</span><span class=\"token punctuation\">(</span>\n                        colors<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">[</span><span class=\"token number\">700</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">[</span><span class=\"token number\">200</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                        radius<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n                        strokeWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">20.0</span><span class=\"token punctuation\">,</span>\n                        value<span class=\"token punctuation\">:</span> _animationController<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span>\n                      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n\n                      <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n                        padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">symmetric</span><span class=\"token punctuation\">(</span>vertical<span class=\"token punctuation\">:</span> <span class=\"token number\">16.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                        child<span class=\"token punctuation\">:</span> <span class=\"token function\">GradientCircularProgressIndicator</span><span class=\"token punctuation\">(</span>\n                          colors<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">[</span><span class=\"token number\">700</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">[</span><span class=\"token number\">300</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                          radius<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n                          strokeWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">20.0</span><span class=\"token punctuation\">,</span>\n                          value<span class=\"token punctuation\">:</span> _animationController<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span>\n                          strokeCapRound<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n                        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                      <span class=\"token comment\">//剪裁半圆</span>\n                      <span class=\"token function\">ClipRect</span><span class=\"token punctuation\">(</span>\n                        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Align</span><span class=\"token punctuation\">(</span>\n                          alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>topCenter<span class=\"token punctuation\">,</span>\n                          heightFactor<span class=\"token punctuation\">:</span> <span class=\"token number\">.5</span><span class=\"token punctuation\">,</span>\n                          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n                            padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">only</span><span class=\"token punctuation\">(</span>bottom<span class=\"token punctuation\">:</span> <span class=\"token number\">8.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                            child<span class=\"token punctuation\">:</span> <span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span>\n                              <span class=\"token comment\">//width: 100.0,</span>\n                              child<span class=\"token punctuation\">:</span> <span class=\"token function\">TurnBox</span><span class=\"token punctuation\">(</span>\n                                turns<span class=\"token punctuation\">:</span> <span class=\"token number\">.75</span><span class=\"token punctuation\">,</span>\n                                child<span class=\"token punctuation\">:</span> <span class=\"token function\">GradientCircularProgressIndicator</span><span class=\"token punctuation\">(</span>\n                                  colors<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>Colors<span class=\"token punctuation\">.</span>teal<span class=\"token punctuation\">,</span> Colors<span class=\"token punctuation\">.</span>cyan<span class=\"token punctuation\">[</span><span class=\"token number\">500</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                                  radius<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n                                  strokeWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">8.0</span><span class=\"token punctuation\">,</span>\n                                  value<span class=\"token punctuation\">:</span> _animationController<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span>\n                                  totalAngle<span class=\"token punctuation\">:</span> pi<span class=\"token punctuation\">,</span>\n                                  strokeCapRound<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n                                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                      <span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span>\n                        height<span class=\"token punctuation\">:</span> <span class=\"token number\">104.0</span><span class=\"token punctuation\">,</span>\n                        width<span class=\"token punctuation\">:</span> <span class=\"token number\">200.0</span><span class=\"token punctuation\">,</span>\n                        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Stack</span><span class=\"token punctuation\">(</span>\n                          alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n                          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                            <span class=\"token function\">Positioned</span><span class=\"token punctuation\">(</span>\n                              height<span class=\"token punctuation\">:</span> <span class=\"token number\">200.0</span><span class=\"token punctuation\">,</span>\n                              top<span class=\"token punctuation\">:</span> <span class=\"token number\">.0</span><span class=\"token punctuation\">,</span>\n                              child<span class=\"token punctuation\">:</span> <span class=\"token function\">TurnBox</span><span class=\"token punctuation\">(</span>\n                                turns<span class=\"token punctuation\">:</span> <span class=\"token number\">.75</span><span class=\"token punctuation\">,</span>\n                                child<span class=\"token punctuation\">:</span> <span class=\"token function\">GradientCircularProgressIndicator</span><span class=\"token punctuation\">(</span>\n                                  colors<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>Colors<span class=\"token punctuation\">.</span>teal<span class=\"token punctuation\">,</span> Colors<span class=\"token punctuation\">.</span>cyan<span class=\"token punctuation\">[</span><span class=\"token number\">500</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                                  radius<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n                                  strokeWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">8.0</span><span class=\"token punctuation\">,</span>\n                                  value<span class=\"token punctuation\">:</span> _animationController<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span>\n                                  totalAngle<span class=\"token punctuation\">:</span> pi<span class=\"token punctuation\">,</span>\n                                  strokeCapRound<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n                                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                            <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n                              padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">only</span><span class=\"token punctuation\">(</span>top<span class=\"token punctuation\">:</span> <span class=\"token number\">10.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>\n                                <span class=\"token string\">&quot;${(_animationController.value * 100).toInt()}%&quot;</span><span class=\"token punctuation\">,</span>\n                                style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>\n                                  fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">25.0</span><span class=\"token punctuation\">,</span>\n                                  color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blueGrey<span class=\"token punctuation\">,</span>\n                                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                            <span class=\"token punctuation\">)</span>\n                          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>怎么样，很炫酷吧！<code>GradientCircularProgressIndicator</code>已经被添加进了笔者维护的flukit组件库中了，读者如果有需要，可以直接依赖flukit包。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/108.47fd6022.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter10/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>本章目录 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/183.802b20e7.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter10/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"本章目录\"><a href=\"#本章目录\" class=\"header-anchor\">#</a> 本章目录</h1> <ul><li><a href=\"/v2/chapter10/intro.html\">10.1：自定义组件方法简介</a></li> <li><a href=\"/v2/chapter10/combine.html\">10.2：组合现有组件</a></li> <li><a href=\"/v2/chapter10/turn_box.html\">10.3：组合实例：TurnBox</a></li> <li><a href=\"/v2/chapter10/custom_paint.html\">10.4：自绘组件（CustomPaint与Canvas）</a></li> <li><a href=\"/v2/chapter10/gradient_circular_progress_demo.html\">10.5：自绘实例：圆形渐变进度条(自绘)</a></li></ul></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/183.802b20e7.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter10/intro.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>10.1 自定义组件方法简介 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/184.90e5f242.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter10/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_10-1-自定义组件方法简介\"><a href=\"#_10-1-自定义组件方法简介\" class=\"header-anchor\">#</a> 10.1 自定义组件方法简介</h1> <p>当Flutter提供的现有组件无法满足我们的需求，或者我们为了共享代码需要封装一些通用组件，这时我们就需要自定义组件。在Flutter中自定义组件有三种方式：通过组合其它组件、自绘和实现RenderObject。本节我们先分别介绍一下这三种方式的特点，后面章节中则详细介绍它们的细节。</p> <h3 id=\"组合其它widget\"><a href=\"#组合其它widget\" class=\"header-anchor\">#</a> 组合其它Widget</h3> <p>这种方式是通过拼装其它组件来组合成一个新的组件。例如我们之前介绍的<code>Container</code>就是一个组合组件，它是由<code>DecoratedBox</code>、<code>ConstrainedBox</code>、<code>Transform</code>、<code>Padding</code>、<code>Align</code>等组件组成。</p> <p>在Flutter中，组合的思想非常重要，Flutter提供了非常多的基础组件，而我们的界面开发其实就是按照需要组合这些组件来实现各种不同的布局而已。</p> <h3 id=\"自绘\"><a href=\"#自绘\" class=\"header-anchor\">#</a> 自绘</h3> <p>如果遇到无法通过现有的组件来实现需要的UI时，我们可以通过自绘组件的方式来实现，例如我们需要一个颜色渐变的圆形进度条，而Flutter提供的<code>CircularProgressIndicator</code>并不支持在显示精确进度时对进度条应用渐变色（其<code>valueColor</code> 属性只支持执行旋转动画时变化Indicator的颜色），这时最好的方法就是通过自定义组件来绘制出我们期望的外观。我们可以通过Flutter中提供的<code>CustomPaint</code>和<code>Canvas</code>来实现UI自绘。</p> <h3 id=\"实现renderobject\"><a href=\"#实现renderobject\" class=\"header-anchor\">#</a> 实现RenderObject</h3> <p>Flutter提供的自身具有UI外观的组件，如文本<code>Text</code>、<code>Image</code>都是通过相应的<code>RenderObject</code>（我们将在“Flutter核心原理”一章中详细介绍<code>RenderObject</code>）渲染出来的，如Text是由<code>RenderParagraph</code>渲染；而<code>Image</code>是由<code>RenderImage</code>渲染。<code>RenderObject</code>是一个抽象类，它定义了一个抽象方法<code>paint(...)</code>：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">paint</span><span class=\"token punctuation\">(</span>PaintingContext context<span class=\"token punctuation\">,</span> Offset offset<span class=\"token punctuation\">)</span>\n</code></pre></div><p><code>PaintingContext</code>代表组件的绘制上下文，通过<code>PaintingContext.canvas</code>可以获得<code>Canvas</code>，而绘制逻辑主要是通过<code>Canvas</code> API来实现。子类需要重写此方法以实现自身的绘制逻辑，如<code>RenderParagraph</code>需要实现文本绘制逻辑，而<code>RenderImage</code>需要实现图片绘制逻辑。</p> <p>可以发现，<code>RenderObject</code>中最终也是通过<code>Canvas</code> API来绘制的，那么通过实现<code>RenderObject</code>的方式和上面介绍的通过<code>CustomPaint</code>和<code>Canvas</code>自绘的方式有什么区别？其实答案很简单，<code>CustomPaint</code>只是为了方便开发者封装的一个代理类，它直接继承自<code>SingleChildRenderObjectWidget</code>，通过<code>RenderCustomPaint</code>的<code>paint</code>方法将<code>Canvas</code>和画笔<code>Painter</code>(需要开发者实现，后面章节介绍)连接起来实现了最终的绘制（绘制逻辑在<code>Painter</code>中）。</p> <h3 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h3> <p>“组合”是自定义组件最简单的方法，在任何需要自定义组件的场景下，我们都应该优先考虑是否能够通过组合来实现。而自绘和通过实现<code>RenderObject</code>的方法本质上是一样的，都需要开发者调用<code>Canvas</code> API手动去绘制UI，优点是强大灵活，理论上可以实现任何外观的UI，而缺点是必须了解<code>Canvas</code> API细节，并且得自己去实现绘制逻辑。</p> <p>在本章接下来的小节中，我们将通过一些实例来详细介绍自定义UI的过程，由于后两种方法本质是相同的，并且Flutter中很多基础组件都是通过<code>RenderObject</code>的形式来实现的，所以后续我们只介绍<code>CustomPaint</code>和<code>Canvas</code>的方式，读者如果对自定义<code>RenderObject</code>的方法好奇，可以查看Flutter中相关基础组件对应的<code>RenderObject</code>的实现源码，如<code>RenderParagraph</code>或<code>RenderImage</code>。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/184.90e5f242.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter10/turn_box.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>10.3 组合实例：TurnBox | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/109.9119c4a8.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter10/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_10-3-组合实例-turnbox\"><a href=\"#_10-3-组合实例-turnbox\" class=\"header-anchor\">#</a> 10.3 组合实例：TurnBox</h1> <p>我们之前已经介绍过<code>RotatedBox</code>，它可以旋转子组件，但是它有两个缺点：一是只能将其子节点以90度的倍数旋转；二是当旋转的角度发生变化时，旋转角度更新过程没有动画。</p> <p>本节我们将实现一个<code>TurnBox</code>组件，它不仅可以以任意角度来旋转其子节点，而且可以在角度发生变化时执行一个动画以过渡到新状态，同时，我们可以手动指定动画速度。</p> <p><code>TurnBox</code>的完整代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/widgets.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">TurnBox</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function\">TurnBox</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>turns <span class=\"token operator\">=</span> <span class=\"token number\">.0</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//旋转的“圈”数,一圈为360度，如0.25圈即90度</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>speed <span class=\"token operator\">=</span> <span class=\"token number\">200</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//过渡动画执行的总时长</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>child\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span><span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> double turns<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> int speed<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> Widget child<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  _TurnBoxState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_TurnBoxState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_TurnBoxState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>TurnBox<span class=\"token operator\">&gt;</span>\n    <span class=\"token keyword\">with</span> SingleTickerProviderStateMixin <span class=\"token punctuation\">{</span>\n  AnimationController _controller<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    _controller <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AnimationController</span><span class=\"token punctuation\">(</span>\n        vsync<span class=\"token punctuation\">:</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">,</span>\n        lowerBound<span class=\"token punctuation\">:</span> <span class=\"token operator\">-</span>double<span class=\"token punctuation\">.</span>infinity<span class=\"token punctuation\">,</span>\n        upperBound<span class=\"token punctuation\">:</span> double<span class=\"token punctuation\">.</span>infinity\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    _controller<span class=\"token punctuation\">.</span>value <span class=\"token operator\">=</span> widget<span class=\"token punctuation\">.</span>turns<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    _controller<span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">RotationTransition</span><span class=\"token punctuation\">(</span>\n      turns<span class=\"token punctuation\">:</span> _controller<span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> widget<span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">didUpdateWidget</span><span class=\"token punctuation\">(</span>TurnBox oldWidget<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">didUpdateWidget</span><span class=\"token punctuation\">(</span>oldWidget<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//旋转角度发生变化时执行过渡动画  </span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>oldWidget<span class=\"token punctuation\">.</span>turns <span class=\"token operator\">!=</span> widget<span class=\"token punctuation\">.</span>turns<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _controller<span class=\"token punctuation\">.</span><span class=\"token function\">animateTo</span><span class=\"token punctuation\">(</span>\n        widget<span class=\"token punctuation\">.</span>turns<span class=\"token punctuation\">,</span>\n        duration<span class=\"token punctuation\">:</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>milliseconds<span class=\"token punctuation\">:</span> widget<span class=\"token punctuation\">.</span>speed<span class=\"token operator\">?</span><span class=\"token operator\">?</span><span class=\"token number\">200</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        curve<span class=\"token punctuation\">:</span> Curves<span class=\"token punctuation\">.</span>easeOut<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面代码中：</p> <ol><li>我们是通过组合<code>RotationTransition</code>和child来实现的旋转效果。</li> <li>在<code>didUpdateWidget</code>中，我们判断要旋转的角度是否发生了变化，如果变了，则执行一个过渡动画。</li></ol> <p>下面我们测试一下<code>TurnBox</code>的功能，测试代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'../widgets/index.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">TurnBoxRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _TurnBoxRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_TurnBoxRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_TurnBoxRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>TurnBoxRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  double _turns <span class=\"token operator\">=</span> <span class=\"token number\">.0</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n        children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n          <span class=\"token function\">TurnBox</span><span class=\"token punctuation\">(</span>\n            turns<span class=\"token punctuation\">:</span> _turns<span class=\"token punctuation\">,</span>\n            speed<span class=\"token punctuation\">:</span> <span class=\"token number\">500</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>refresh<span class=\"token punctuation\">,</span> size<span class=\"token punctuation\">:</span> <span class=\"token number\">50</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">TurnBox</span><span class=\"token punctuation\">(</span>\n            turns<span class=\"token punctuation\">:</span> _turns<span class=\"token punctuation\">,</span>\n            speed<span class=\"token punctuation\">:</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>refresh<span class=\"token punctuation\">,</span> size<span class=\"token punctuation\">:</span> <span class=\"token number\">150.0</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;顺时针旋转1/5圈&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                _turns <span class=\"token operator\">+=</span> <span class=\"token number\">.2</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;逆时针旋转1/5圈&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                _turns <span class=\"token operator\">-=</span> <span class=\"token number\">.2</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>测试代码运行后效果如图10-2所示：</p> <p><img src=\"/assets/img/10-2.2a9ed156.png\" alt=\"图10-2\"></p> <p>当我们点击旋转按钮时，两个图标的旋转都会旋转1/5圈，但旋转的速度是不同的，读者可以自己运行一下示例看看效果。</p> <p>实际上本示例只组合了<code>RotationTransition</code>一个组件，它是一个最简的组合类组件示例。另外，如果我们封装的是<code>StatefulWidget</code>，那么一定要注意在组件更新时是否需要同步状态。比如我们要封装一个富文本展示组件<code>MyRichText</code> ，它可以自动处理url链接，定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">MyRichText</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">MyRichText</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>text<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 文本字符串</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>linkStyle<span class=\"token punctuation\">,</span> <span class=\"token comment\">// url链接样式</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> String text<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> TextStyle linkStyle<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  _MyRichTextState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">_MyRichTextState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>接下来我们在<code>_MyRichTextState</code>中要实现的功能有两个：</p> <ol><li>解析文本字符串“text”，生成<code>TextSpan</code>缓存起来；</li> <li>在<code>build</code>中返回最终的富文本样式；</li></ol> <p><code>_MyRichTextState</code> 实现的代码大致如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">_MyRichTextState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>MyRichText<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n\n  TextSpan _textSpan<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">RichText</span><span class=\"token punctuation\">(</span>\n      text<span class=\"token punctuation\">:</span> _textSpan<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  TextSpan <span class=\"token function\">parseText</span><span class=\"token punctuation\">(</span>String text<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 耗时操作：解析文本字符串，构建出TextSpan。</span>\n    <span class=\"token comment\">// 省略具体实现。</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    _textSpan <span class=\"token operator\">=</span> <span class=\"token function\">parseText</span><span class=\"token punctuation\">(</span>widget<span class=\"token punctuation\">.</span>text<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>由于解析文本字符串，构建出<code>TextSpan</code>是一个耗时操作，为了不在每次build的时候都解析一次，所以我们在<code>initState</code>中对解析的结果进行了缓存，然后再<code>build</code>中直接使用解析的结果<code>_textSpan</code>。这看起来很不错，但是上面的代码有一个严重的问题，就是父组件传入的<code>text</code>发生变化时（组件树结构不变），那么<code>MyRichText</code>显示的内容不会更新，原因就是<code>initState</code>只会在State创建时被调用，所以在<code>text</code>发生变化时，<code>parseText</code>没有重新执行，导致<code>_textSpan</code>任然是旧的解析值。要解决这个问题也很简单，我们只需添加一个<code>didUpdateWidget</code>回调，然后再里面重新调用<code>parseText</code>即可：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token metadata symbol\">@override</span>\n<span class=\"token keyword\">void</span> <span class=\"token function\">didUpdateWidget</span><span class=\"token punctuation\">(</span>MyRichText oldWidget<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>widget<span class=\"token punctuation\">.</span>text <span class=\"token operator\">!=</span> oldWidget<span class=\"token punctuation\">.</span>text<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    _textSpan <span class=\"token operator\">=</span> <span class=\"token function\">parseText</span><span class=\"token punctuation\">(</span>widget<span class=\"token punctuation\">.</span>text<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">didUpdateWidget</span><span class=\"token punctuation\">(</span>oldWidget<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>有些读者可能会觉得这个点也很简单，是的，的确很简单，之所以要在这里反复强调是因为这个点在实际开发中很容易被忽略，它虽然简单，但却很重要。总之，当我们在State中会缓存某些依赖Widget参数的数据时，一定要注意在组件更新时是否需要同步状态。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/109.9119c4a8.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter11/dio.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>11.3 Http请求-Dio http库 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/185.5cca4d69.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter11/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_11-3-http请求-dio-http库\"><a href=\"#_11-3-http请求-dio-http库\" class=\"header-anchor\">#</a> 11.3 Http请求-Dio http库</h1> <p>通过上一节介绍，我们可以发现直接使用HttpClient发起网络请求是比较麻烦的，很多事情得我们手动处理，如果再涉及到文件上传/下载、Cookie管理等就会非常繁琐。幸运的是，Dart社区有一些第三方http请求库，用它们来发起http请求将会简单的多，本节我们介绍一下目前人气较高的<a href=\"https://github.com/flutterchina/dio\" target=\"_blank\" rel=\"noopener noreferrer\">dio<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>库。</p> <blockquote><p>dio是一个强大的Dart Http请求库，支持Restful API、FormData、拦截器、请求取消、Cookie管理、文件上传/下载、超时等。dio的使用方式随着其版本升级可能会发生变化，如果本节所述内容和dio官方有差异，请以dio官方文档为准。</p></blockquote> <h3 id=\"引入\"><a href=\"#引入\" class=\"header-anchor\">#</a> 引入</h3> <p>引入dio:</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code><span class=\"token key atrule\">dependencies</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">dio</span><span class=\"token punctuation\">:</span> ^x.x.x <span class=\"token comment\">#请使用pub上的最新版本</span>\n</code></pre></div><p>导入并创建dio实例：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:dio/dio.dart'</span><span class=\"token punctuation\">;</span>\nDio dio <span class=\"token operator\">=</span>  <span class=\"token function\">Dio</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>接下来就可以通过 dio实例来发起网络请求了，注意，一个dio实例可以发起多个http请求，一般来说，APP只有一个http数据源时，dio应该使用单例模式。</p> <h3 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h3> <p>发起 <code>GET</code> 请求 :</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Response response<span class=\"token punctuation\">;</span>\nresponse<span class=\"token operator\">=</span><span class=\"token keyword\">await</span> dio<span class=\"token punctuation\">.</span><span class=\"token keyword\">get</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;/test?id=12&amp;name=wendu&quot;</span><span class=\"token punctuation\">)</span>\n<span class=\"token function\">print</span><span class=\"token punctuation\">(</span>response<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>对于<code>GET</code>请求我们可以将query参数通过对象来传递，上面的代码等同于：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>response<span class=\"token operator\">=</span><span class=\"token keyword\">await</span> dio<span class=\"token punctuation\">.</span><span class=\"token keyword\">get</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;/test&quot;</span><span class=\"token punctuation\">,</span>queryParameters<span class=\"token punctuation\">:</span><span class=\"token punctuation\">{</span><span class=\"token string\">&quot;id&quot;</span><span class=\"token punctuation\">:</span><span class=\"token number\">12</span><span class=\"token punctuation\">,</span><span class=\"token string\">&quot;name&quot;</span><span class=\"token punctuation\">:</span><span class=\"token string\">&quot;wendu&quot;</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token function\">print</span><span class=\"token punctuation\">(</span>response<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>发起一个 <code>POST</code> 请求:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>response<span class=\"token operator\">=</span><span class=\"token keyword\">await</span> dio<span class=\"token punctuation\">.</span><span class=\"token function\">post</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;/test&quot;</span><span class=\"token punctuation\">,</span>data<span class=\"token punctuation\">:</span><span class=\"token punctuation\">{</span><span class=\"token string\">&quot;id&quot;</span><span class=\"token punctuation\">:</span><span class=\"token number\">12</span><span class=\"token punctuation\">,</span><span class=\"token string\">&quot;name&quot;</span><span class=\"token punctuation\">:</span><span class=\"token string\">&quot;wendu&quot;</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>发起多个并发请求:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>response<span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> Future<span class=\"token punctuation\">.</span><span class=\"token function\">wait</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span>dio<span class=\"token punctuation\">.</span><span class=\"token function\">post</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;/info&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>dio<span class=\"token punctuation\">.</span><span class=\"token keyword\">get</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;/token&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>下载文件:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>response<span class=\"token operator\">=</span><span class=\"token keyword\">await</span> dio<span class=\"token punctuation\">.</span><span class=\"token function\">download</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;https://www.google.com/&quot;</span><span class=\"token punctuation\">,</span>_savePath<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>发送 FormData:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>FormData formData <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">FormData<span class=\"token punctuation\">.</span>from</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n   <span class=\"token string\">&quot;name&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;wendux&quot;</span><span class=\"token punctuation\">,</span>\n   <span class=\"token string\">&quot;age&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token number\">25</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\nresponse <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> dio<span class=\"token punctuation\">.</span><span class=\"token function\">post</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;/info&quot;</span><span class=\"token punctuation\">,</span> data<span class=\"token punctuation\">:</span> formData<span class=\"token punctuation\">)</span>\n</code></pre></div><p>如果发送的数据是FormData，则dio会将请求header的<code>contentType</code>设为“multipart/form-data”。</p> <p>通过FormData上传多个文件:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>FormData formData <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">FormData<span class=\"token punctuation\">.</span>from</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n   <span class=\"token string\">&quot;name&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;wendux&quot;</span><span class=\"token punctuation\">,</span>\n   <span class=\"token string\">&quot;age&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token number\">25</span><span class=\"token punctuation\">,</span>\n   <span class=\"token string\">&quot;file1&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">UploadFileInfo</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">File</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;./upload.txt&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;upload1.txt&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n   <span class=\"token string\">&quot;file2&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">UploadFileInfo</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">File</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;./upload.txt&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;upload2.txt&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n     <span class=\"token comment\">// 支持文件数组上传</span>\n   <span class=\"token string\">&quot;files&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>\n      <span class=\"token keyword\">new</span> <span class=\"token class-name\">UploadFileInfo</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">File</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;./example/upload.txt&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;upload.txt&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token keyword\">new</span> <span class=\"token class-name\">UploadFileInfo</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">File</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;./example/upload.txt&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;upload.txt&quot;</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\nresponse <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> dio<span class=\"token punctuation\">.</span><span class=\"token function\">post</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;/info&quot;</span><span class=\"token punctuation\">,</span> data<span class=\"token punctuation\">:</span> formData<span class=\"token punctuation\">)</span>\n</code></pre></div><p>值得一提的是，dio内部仍然使用HttpClient发起的请求，所以代理、请求认证、证书校验等和HttpClient是相同的，我们可以在<code>onHttpClientCreate</code>回调中设置，例如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token punctuation\">(</span>dio<span class=\"token punctuation\">.</span>httpClientAdapter <span class=\"token operator\">as</span> DefaultHttpClientAdapter<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>onHttpClientCreate <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>client<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//设置代理 </span>\n    client<span class=\"token punctuation\">.</span>findProxy <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>uri<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token string\">&quot;PROXY 192.168.1.2:8888&quot;</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//校验证书</span>\n    httpClient<span class=\"token punctuation\">.</span>badCertificateCallback<span class=\"token operator\">=</span><span class=\"token punctuation\">(</span>X509Certificate cert<span class=\"token punctuation\">,</span> String host<span class=\"token punctuation\">,</span> int port<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>cert<span class=\"token punctuation\">.</span>pem<span class=\"token operator\">==</span>PEM<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//证书一致，则允许发送数据</span>\n     <span class=\"token punctuation\">}</span>\n     <span class=\"token keyword\">return</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>   \n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>注意，<code>onHttpClientCreate</code>会在当前dio实例内部需要创建HttpClient时调用，所以通过此回调配置HttpClient会对整个dio实例生效，如果你想针对某个应用请求单独的代理或证书校验策略，可以创建一个新的dio实例即可。</p> <p>怎么样，是不是很简单，除了这些基本的用法，dio还支持请求配置、拦截器等，官方资料比较详细，故本书不再赘述，详情可以参考dio主页：https://github.com/flutterchina/dio 。 下一节我们将使用dio实现一个分块下载器。</p> <h3 id=\"实例\"><a href=\"#实例\" class=\"header-anchor\">#</a> 实例</h3> <p>我们通过Github开放的API来请求flutterchina组织下的所有公开的开源项目，实现：</p> <ol><li>在请求阶段弹出loading</li> <li>请求结束后，如果请求失败，则展示错误信息；如果成功，则将项目名称列表展示出来。</li></ol> <p>代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">_FutureBuilderRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>FutureBuilderRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  Dio _dio <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Dio</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Container</span><span class=\"token punctuation\">(</span>\n      alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">FutureBuilder</span><span class=\"token punctuation\">(</span>\n          future<span class=\"token punctuation\">:</span> _dio<span class=\"token punctuation\">.</span><span class=\"token keyword\">get</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;https://api.github.com/orgs/flutterchina/repos&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> AsyncSnapshot snapshot<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">//请求完成</span>\n            <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>snapshot<span class=\"token punctuation\">.</span>connectionState <span class=\"token operator\">==</span> ConnectionState<span class=\"token punctuation\">.</span>done<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              Response response <span class=\"token operator\">=</span> snapshot<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">;</span>\n              <span class=\"token comment\">//发生错误</span>\n              <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>snapshot<span class=\"token punctuation\">.</span>hasError<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token keyword\">return</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>snapshot<span class=\"token punctuation\">.</span>error<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span>\n              <span class=\"token comment\">//请求成功，通过项目信息构建用于显示项目名称的ListView</span>\n              <span class=\"token keyword\">return</span> <span class=\"token function\">ListView</span><span class=\"token punctuation\">(</span>\n                children<span class=\"token punctuation\">:</span> response<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">.</span>map<span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span>\n                    <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">[</span><span class=\"token string\">&quot;full_name&quot;</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toList</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span>\n            <span class=\"token comment\">//请求未完成时弹出loading</span>\n            <span class=\"token keyword\">return</span> <span class=\"token function\">CircularProgressIndicator</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/185.5cca4d69.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter11/download_with_chunks.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>11.4 实例：Http分块下载 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/186.56dca3cc.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter11/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_11-4-实例-http分块下载\"><a href=\"#_11-4-实例-http分块下载\" class=\"header-anchor\">#</a> 11.4 实例：Http分块下载</h1> <p>本节将通过一个“Http分块下载”的示例演示一下dio的具体用法。</p> <h3 id=\"原理\"><a href=\"#原理\" class=\"header-anchor\">#</a> 原理</h3> <p>Http协议定义了分块传输的响应header字段，但具体是否支持取决于Server的实现，我们可以指定请求头的&quot;range&quot;字段来验证服务器是否支持分块传输。例如，我们可以利用curl命令来验证：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>bogon:~ duwen$ <span class=\"token function\">curl</span> -H <span class=\"token string\">&quot;Range: bytes=0-10&quot;</span> http://download.dcloud.net.cn/HBuilder.9.0.2.macosx_64.dmg -v\n<span class=\"token comment\"># 请求头</span>\n<span class=\"token operator\">&gt;</span> GET /HBuilder.9.0.2.macosx_64.dmg HTTP/1.1\n<span class=\"token operator\">&gt;</span> Host: download.dcloud.net.cn\n<span class=\"token operator\">&gt;</span> User-Agent: curl/7.54.0\n<span class=\"token operator\">&gt;</span> Accept: */*\n<span class=\"token operator\">&gt;</span> Range: <span class=\"token assign-left variable\">bytes</span><span class=\"token operator\">=</span><span class=\"token number\">0</span>-10\n<span class=\"token comment\"># 响应头</span>\n<span class=\"token operator\">&lt;</span> HTTP/1.1 <span class=\"token number\">206</span> Partial Content\n<span class=\"token operator\">&lt;</span> Content-Type: application/octet-stream\n<span class=\"token operator\">&lt;</span> Content-Length: <span class=\"token number\">11</span>\n<span class=\"token operator\">&lt;</span> Connection: keep-alive\n<span class=\"token operator\">&lt;</span> Date: Thu, <span class=\"token number\">21</span> Feb <span class=\"token number\">2019</span> 06:25:15 GMT\n<span class=\"token operator\">&lt;</span> Content-Range: bytes <span class=\"token number\">0</span>-10/233295878\n\n</code></pre></div><p>我们在请求头中添加&quot;Range: bytes=0-10&quot;的作用是，告诉服务器本次请求我们只想获取文件0-10(包括10，共11字节)这块内容。如果服务器支持分块传输，则响应状态码为206，表示“部分内容”，并且同时响应头中包含“Content-Range”字段，如果不支持则不会包含。我们看看上面“Content-Range”的内容：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>Content-Range: bytes 0-10/233295878\n</code></pre></div><p>0-10表示本次返回的区块，233295878代表文件的总长度，单位都是byte,  也就是该文件大概233M多一点。</p> <p>基于此，我们可以设计一个简单的多线程的文件分块下载器，实现的思路是：</p> <ol><li>先检测是否支持分块传输，如果不支持，则直接下载；若支持，则将剩余内容分块下载。</li> <li>各个分块下载时保存到各自临时文件，等到所有分块下载完后合并临时文件。</li> <li>删除临时文件。</li></ol> <h3 id=\"实现\"><a href=\"#实现\" class=\"header-anchor\">#</a> 实现</h3> <p>下面是整体的流程：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">// 通过第一个分块请求检测服务器是否支持分块传输  </span>\nResponse response <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">downloadChunk</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">,</span> firstChunkSize<span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>response<span class=\"token punctuation\">.</span>statusCode <span class=\"token operator\">==</span> <span class=\"token number\">206</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>    <span class=\"token comment\">//如果支持</span>\n    <span class=\"token comment\">//解析文件总长度，进而算出剩余长度</span>\n    total <span class=\"token operator\">=</span> int<span class=\"token punctuation\">.</span><span class=\"token function\">parse</span><span class=\"token punctuation\">(</span>\n        response<span class=\"token punctuation\">.</span>headers<span class=\"token punctuation\">.</span><span class=\"token function\">value</span><span class=\"token punctuation\">(</span>HttpHeaders<span class=\"token punctuation\">.</span>contentRangeHeader<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">split</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;/&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>last<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    int reserved <span class=\"token operator\">=</span> total <span class=\"token operator\">-</span>\n        int<span class=\"token punctuation\">.</span><span class=\"token function\">parse</span><span class=\"token punctuation\">(</span>response<span class=\"token punctuation\">.</span>headers<span class=\"token punctuation\">.</span><span class=\"token function\">value</span><span class=\"token punctuation\">(</span>HttpHeaders<span class=\"token punctuation\">.</span>contentLengthHeader<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//文件的总块数(包括第一块)</span>\n    int chunk <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>reserved <span class=\"token operator\">/</span> firstChunkSize<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">ceil</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>chunk <span class=\"token operator\">&gt;</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        int chunkSize <span class=\"token operator\">=</span> firstChunkSize<span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>chunk <span class=\"token operator\">&gt;</span> maxChunk <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            chunk <span class=\"token operator\">=</span> maxChunk <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">;</span>\n            chunkSize <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>reserved <span class=\"token operator\">/</span> maxChunk<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">ceil</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n        <span class=\"token keyword\">var</span> futures <span class=\"token operator\">=</span> <span class=\"token operator\">&lt;</span>Future<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span>int i <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span> i <span class=\"token operator\">&lt;</span> maxChunk<span class=\"token punctuation\">;</span> <span class=\"token operator\">++</span>i<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            int start <span class=\"token operator\">=</span> firstChunkSize <span class=\"token operator\">+</span> i <span class=\"token operator\">*</span> chunkSize<span class=\"token punctuation\">;</span>\n            <span class=\"token comment\">//分块下载剩余文件  </span>\n            futures<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token function\">downloadChunk</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">,</span> start<span class=\"token punctuation\">,</span> start <span class=\"token operator\">+</span> chunkSize<span class=\"token punctuation\">,</span> i <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n        <span class=\"token comment\">//等待所有分块全部下载完成</span>\n        <span class=\"token keyword\">await</span> Future<span class=\"token punctuation\">.</span><span class=\"token function\">wait</span><span class=\"token punctuation\">(</span>futures<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token comment\">//合并文件文件  </span>\n    <span class=\"token keyword\">await</span> <span class=\"token function\">mergeTempFiles</span><span class=\"token punctuation\">(</span>chunk<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>下面我们使用dio的<code>download</code> API 实现<code>downloadChunk</code>：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//start 代表当前块的起始位置，end代表结束位置</span>\n<span class=\"token comment\">//no 代表当前是第几块</span>\nFuture<span class=\"token operator\">&lt;</span>Response<span class=\"token operator\">&gt;</span> <span class=\"token function\">downloadChunk</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">,</span> start<span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">,</span> no<span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n  progress<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//progress记录每一块已接收数据的长度</span>\n  <span class=\"token operator\">--</span>end<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">return</span> dio<span class=\"token punctuation\">.</span><span class=\"token function\">download</span><span class=\"token punctuation\">(</span>\n    url<span class=\"token punctuation\">,</span>\n    savePath <span class=\"token operator\">+</span> <span class=\"token string\">&quot;temp$no&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//临时文件按照块的序号命名，方便最后合并</span>\n    onReceiveProgress<span class=\"token punctuation\">:</span> <span class=\"token function\">createCallback</span><span class=\"token punctuation\">(</span>no<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 创建进度回调，后面实现</span>\n    options<span class=\"token punctuation\">:</span> <span class=\"token function\">Options</span><span class=\"token punctuation\">(</span>\n      headers<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span><span class=\"token string\">&quot;range&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;bytes=$start-$end&quot;</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//指定请求的内容区间</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>接下来实现<code>mergeTempFiles</code>:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Future <span class=\"token function\">mergeTempFiles</span><span class=\"token punctuation\">(</span>chunk<span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n  File f <span class=\"token operator\">=</span> <span class=\"token function\">File</span><span class=\"token punctuation\">(</span>savePath <span class=\"token operator\">+</span> <span class=\"token string\">&quot;temp0&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  IOSink ioSink<span class=\"token operator\">=</span> f<span class=\"token punctuation\">.</span><span class=\"token function\">openWrite</span><span class=\"token punctuation\">(</span>mode<span class=\"token punctuation\">:</span> FileMode<span class=\"token punctuation\">.</span>writeOnlyAppend<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">//合并临时文件  </span>\n  <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span>int i <span class=\"token operator\">=</span> <span class=\"token number\">1</span><span class=\"token punctuation\">;</span> i <span class=\"token operator\">&lt;</span> chunk<span class=\"token punctuation\">;</span> <span class=\"token operator\">++</span>i<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    File _f <span class=\"token operator\">=</span> <span class=\"token function\">File</span><span class=\"token punctuation\">(</span>savePath <span class=\"token operator\">+</span> <span class=\"token string\">&quot;temp$i&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">await</span> ioSink<span class=\"token punctuation\">.</span><span class=\"token function\">addStream</span><span class=\"token punctuation\">(</span>_f<span class=\"token punctuation\">.</span><span class=\"token function\">openRead</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">await</span> _f<span class=\"token punctuation\">.</span><span class=\"token function\">delete</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//删除临时文件</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token keyword\">await</span> ioSink<span class=\"token punctuation\">.</span><span class=\"token function\">close</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">await</span> f<span class=\"token punctuation\">.</span><span class=\"token function\">rename</span><span class=\"token punctuation\">(</span>savePath<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//合并后的文件重命名为真正的名称</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>下面我们看一下完整实现：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">/// Downloading by spiting as file in chunks</span>\nFuture <span class=\"token function\">downloadWithChunks</span><span class=\"token punctuation\">(</span>\n  url<span class=\"token punctuation\">,</span>\n  savePath<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n  ProgressCallback onReceiveProgress<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> firstChunkSize <span class=\"token operator\">=</span> <span class=\"token number\">102</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> maxChunk <span class=\"token operator\">=</span> <span class=\"token number\">3</span><span class=\"token punctuation\">;</span>\n\n  int total <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">var</span> dio <span class=\"token operator\">=</span> <span class=\"token function\">Dio</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">var</span> progress <span class=\"token operator\">=</span> <span class=\"token operator\">&lt;</span>int<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token function\">createCallback</span><span class=\"token punctuation\">(</span>no<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>int received<span class=\"token punctuation\">,</span> _<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      progress<span class=\"token punctuation\">[</span>no<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> received<span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>onReceiveProgress <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">&amp;&amp;</span> total <span class=\"token operator\">!=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">onReceiveProgress</span><span class=\"token punctuation\">(</span>progress<span class=\"token punctuation\">.</span><span class=\"token function\">reduce</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>a<span class=\"token punctuation\">,</span> b<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> a <span class=\"token operator\">+</span> b<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> total<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  Future<span class=\"token operator\">&lt;</span>Response<span class=\"token operator\">&gt;</span> <span class=\"token function\">downloadChunk</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">,</span> start<span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">,</span> no<span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    progress<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token operator\">--</span>end<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> dio<span class=\"token punctuation\">.</span><span class=\"token function\">download</span><span class=\"token punctuation\">(</span>\n      url<span class=\"token punctuation\">,</span>\n      savePath <span class=\"token operator\">+</span> <span class=\"token string\">&quot;temp$no&quot;</span><span class=\"token punctuation\">,</span>\n      onReceiveProgress<span class=\"token punctuation\">:</span> <span class=\"token function\">createCallback</span><span class=\"token punctuation\">(</span>no<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      options<span class=\"token punctuation\">:</span> <span class=\"token function\">Options</span><span class=\"token punctuation\">(</span>\n        headers<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span><span class=\"token string\">&quot;range&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;bytes=$start-$end&quot;</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  Future <span class=\"token function\">mergeTempFiles</span><span class=\"token punctuation\">(</span>chunk<span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    File f <span class=\"token operator\">=</span> <span class=\"token function\">File</span><span class=\"token punctuation\">(</span>savePath <span class=\"token operator\">+</span> <span class=\"token string\">&quot;temp0&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    IOSink ioSink<span class=\"token operator\">=</span> f<span class=\"token punctuation\">.</span><span class=\"token function\">openWrite</span><span class=\"token punctuation\">(</span>mode<span class=\"token punctuation\">:</span> FileMode<span class=\"token punctuation\">.</span>writeOnlyAppend<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span>int i <span class=\"token operator\">=</span> <span class=\"token number\">1</span><span class=\"token punctuation\">;</span> i <span class=\"token operator\">&lt;</span> chunk<span class=\"token punctuation\">;</span> <span class=\"token operator\">++</span>i<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      File _f <span class=\"token operator\">=</span> <span class=\"token function\">File</span><span class=\"token punctuation\">(</span>savePath <span class=\"token operator\">+</span> <span class=\"token string\">&quot;temp$i&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">await</span> ioSink<span class=\"token punctuation\">.</span><span class=\"token function\">addStream</span><span class=\"token punctuation\">(</span>_f<span class=\"token punctuation\">.</span><span class=\"token function\">openRead</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">await</span> _f<span class=\"token punctuation\">.</span><span class=\"token function\">delete</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">await</span> ioSink<span class=\"token punctuation\">.</span><span class=\"token function\">close</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">await</span> f<span class=\"token punctuation\">.</span><span class=\"token function\">rename</span><span class=\"token punctuation\">(</span>savePath<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  Response response <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">downloadChunk</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">,</span> firstChunkSize<span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>response<span class=\"token punctuation\">.</span>statusCode <span class=\"token operator\">==</span> <span class=\"token number\">206</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    total <span class=\"token operator\">=</span> int<span class=\"token punctuation\">.</span><span class=\"token function\">parse</span><span class=\"token punctuation\">(</span>\n        response<span class=\"token punctuation\">.</span>headers<span class=\"token punctuation\">.</span><span class=\"token function\">value</span><span class=\"token punctuation\">(</span>HttpHeaders<span class=\"token punctuation\">.</span>contentRangeHeader<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">split</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;/&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>last<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    int reserved <span class=\"token operator\">=</span> total <span class=\"token operator\">-</span>\n        int<span class=\"token punctuation\">.</span><span class=\"token function\">parse</span><span class=\"token punctuation\">(</span>response<span class=\"token punctuation\">.</span>headers<span class=\"token punctuation\">.</span><span class=\"token function\">value</span><span class=\"token punctuation\">(</span>HttpHeaders<span class=\"token punctuation\">.</span>contentLengthHeader<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    int chunk <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>reserved <span class=\"token operator\">/</span> firstChunkSize<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">ceil</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>chunk <span class=\"token operator\">&gt;</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      int chunkSize <span class=\"token operator\">=</span> firstChunkSize<span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>chunk <span class=\"token operator\">&gt;</span> maxChunk <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        chunk <span class=\"token operator\">=</span> maxChunk <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">;</span>\n        chunkSize <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>reserved <span class=\"token operator\">/</span> maxChunk<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">ceil</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n      <span class=\"token keyword\">var</span> futures <span class=\"token operator\">=</span> <span class=\"token operator\">&lt;</span>Future<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span>int i <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span> i <span class=\"token operator\">&lt;</span> maxChunk<span class=\"token punctuation\">;</span> <span class=\"token operator\">++</span>i<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        int start <span class=\"token operator\">=</span> firstChunkSize <span class=\"token operator\">+</span> i <span class=\"token operator\">*</span> chunkSize<span class=\"token punctuation\">;</span>\n        futures<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token function\">downloadChunk</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">,</span> start<span class=\"token punctuation\">,</span> start <span class=\"token operator\">+</span> chunkSize<span class=\"token punctuation\">,</span> i <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n      <span class=\"token keyword\">await</span> Future<span class=\"token punctuation\">.</span><span class=\"token function\">wait</span><span class=\"token punctuation\">(</span>futures<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">await</span> <span class=\"token function\">mergeTempFiles</span><span class=\"token punctuation\">(</span>chunk<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>现在可以进行分块下载了：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">main</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">var</span> url <span class=\"token operator\">=</span> <span class=\"token string\">&quot;http://download.dcloud.net.cn/HBuilder.9.0.2.macosx_64.dmg&quot;</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">var</span> savePath <span class=\"token operator\">=</span> <span class=\"token string\">&quot;./example/HBuilder.9.0.2.macosx_64.dmg&quot;</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">await</span> <span class=\"token function\">downloadWithChunks</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">,</span> savePath<span class=\"token punctuation\">,</span> onReceiveProgress<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>received<span class=\"token punctuation\">,</span> total<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>total <span class=\"token operator\">!=</span> <span class=\"token operator\">-</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;${(received / total * 100).floor()}%&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h3 id=\"思考\"><a href=\"#思考\" class=\"header-anchor\">#</a> 思考</h3> <ol><li><p>分块下载真的能提高下载速度吗？</p> <p>其实下载速度的主要瓶颈是取决于网络速度和服务器的出口速度，如果是同一个数据源，分块下载的意义并不大，因为服务器是同一个，出口速度确定的，主要取决于网速，而上面的例子正式同源分块下载，读者可以自己对比一下分块和不分块的的下载速度。如果有多个下载源，并且每个下载源的出口带宽都是有限制的，这时分块下载可能会更快一下，之所以说“可能”，是由于这并不是一定的，比如有三个源，三个源的出口带宽都为1G/s，而我们设备所连网络的峰值假设只有800M/s，那么瓶颈就在我们的网络。即使我们设备的带宽大于任意一个源，下载速度依然不一定就比单源单线下载快，试想一下，假设有两个源A和B，速度A源是B源的3倍，如果采用分块下载，两个源各下载一半的话，读者可以算一下所需的下载时间，然后再算一下只从A源下载所需的时间，看看哪个更快。</p> <p>分块下载的最终速度受设备所在网络带宽、源出口速度、每个块大小、以及分块的数量等诸多因素影响，实际过程中很难保证速度最优。在实际开发中，读者可可以先测试对比后再决定是否使用。</p></li> <li><p>分块下载有什么实际的用处吗？</p> <p>分块下载还有一个比较使用的场景是断点续传，可以将文件分为若干个块，然后维护一个下载状态文件用以记录每一个块的状态，这样即使在网络中断后，也可以恢复中断前的状态，具体实现读者可以自己尝试一下，还是有一些细节需要特别注意的，比如分块大小多少合适？下载到一半的块如何处理？要不要维护一个任务队列？</p></li></ol></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/186.56dca3cc.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter11/file_operation.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>11.1 文件操作 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/187.405078e0.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter11/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_11-1-文件操作\"><a href=\"#_11-1-文件操作\" class=\"header-anchor\">#</a> 11.1 文件操作</h1> <p>Dart的IO库包含了文件读写的相关类，它属于Dart语法标准的一部分，所以通过Dart IO库，无论是Dart VM下的脚本还是Flutter，都是通过Dart IO库来操作文件的，不过和Dart VM相比，Flutter有一个重要差异是文件系统路径不同，这是因为Dart VM是运行在PC或服务器操作系统下，而Flutter是运行在移动操作系统中，他们的文件系统会有一些差异。</p> <h4 id=\"app目录\"><a href=\"#app目录\" class=\"header-anchor\">#</a> APP目录</h4> <p>Android和iOS的应用存储目录不同，<a href=\"https://pub.dartlang.org/packages/path_provider\" target=\"_blank\" rel=\"noopener noreferrer\"><code>PathProvider</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 插件提供了一种平台透明的方式来访问设备文件系统上的常用位置。该类当前支持访问两个文件系统位置：</p> <ul><li><strong>临时目录:</strong>  可以使用 <code>getTemporaryDirectory()</code> 来获取临时目录； 系统可随时清除的临时目录（缓存）。在iOS上，这对应于<a href=\"https://developer.apple.com/reference/foundation/1409211-nstemporarydirectory\" target=\"_blank\" rel=\"noopener noreferrer\"><code>NSTemporaryDirectory()</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 返回的值。在Android上，这是<a href=\"https://developer.android.com/reference/android/content/Context.html#getCacheDir()\" target=\"_blank\" rel=\"noopener noreferrer\"><code>getCacheDir()</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>返回的值。</li> <li><strong>文档目录:</strong> 可以使用<code>getApplicationDocumentsDirectory()</code>来获取应用程序的文档目录，该目录用于存储只有自己可以访问的文件。只有当应用程序被卸载时，系统才会清除该目录。在iOS上，这对应于<code>NSDocumentDirectory</code>。在Android上，这是<code>AppData</code>目录。</li> <li><strong>外部存储目录</strong>：可以使用<code>getExternalStorageDirectory()</code>来获取外部存储目录，如SD卡；由于iOS不支持外部目录，所以在iOS下调用该方法会抛出<code>UnsupportedError</code>异常，而在Android下结果是android SDK中<code>getExternalStorageDirectory</code>的返回值。</li></ul> <p>一旦你的Flutter应用程序有一个文件位置的引用，你可以使用<a href=\"https://api.dartlang.org/stable/dart-io/dart-io-library.html\" target=\"_blank\" rel=\"noopener noreferrer\">dart:io<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>API来执行对文件系统的读/写操作。有关使用Dart处理文件和目录的详细内容可以参考Dart语言文档，下面我们看一个简单的例子。</p> <h4 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h4> <p>我们还是以计数器为例，实现在应用退出重启后可以恢复点击次数。 这里，我们使用文件来保存数据：</p> <ol><li><p>引入PathProvider插件；在<code>pubspec.yaml</code>文件中添加如下声明：</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code><span class=\"token key atrule\">path_provider</span><span class=\"token punctuation\">:</span> ^0.4.1\n</code></pre></div><p>添加后，执行<code>flutter packages get</code> 获取一下, 版本号可能随着时间推移会发生变化，读者可以使用最新版。</p></li> <li><p>实现：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'dart:io'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'dart:async'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:path_provider/path_provider.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">FileOperationRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">FileOperationRoute</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>Key key<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  _FileOperationRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_FileOperationRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_FileOperationRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>FileOperationRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  int _counter<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//从文件读取点击次数</span>\n    <span class=\"token function\">_readCounter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>int value<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        _counter <span class=\"token operator\">=</span> value<span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  Future<span class=\"token operator\">&lt;</span>File<span class=\"token operator\">&gt;</span> <span class=\"token function\">_getLocalFile</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 获取应用目录</span>\n    String dir <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">await</span> <span class=\"token function\">getApplicationDocumentsDirectory</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>path<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">File</span><span class=\"token punctuation\">(</span><span class=\"token string\">'$dir/counter.txt'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  Future<span class=\"token operator\">&lt;</span>int<span class=\"token operator\">&gt;</span> <span class=\"token function\">_readCounter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n      File file <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">_getLocalFile</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token comment\">// 读取点击次数（以字符串）</span>\n      String contents <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> file<span class=\"token punctuation\">.</span><span class=\"token function\">readAsString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">return</span> int<span class=\"token punctuation\">.</span><span class=\"token function\">parse</span><span class=\"token punctuation\">(</span>contents<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">on</span> FileSystemException <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  Future<span class=\"token operator\">&lt;</span>Null<span class=\"token operator\">&gt;</span> <span class=\"token function\">_incrementCounter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _counter<span class=\"token operator\">++</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">// 将点击次数以字符串类型写到文件中</span>\n    <span class=\"token keyword\">await</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">await</span> <span class=\"token function\">_getLocalFile</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">writeAsString</span><span class=\"token punctuation\">(</span><span class=\"token string\">'$_counter'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Scaffold</span><span class=\"token punctuation\">(</span>\n      appBar<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AppBar</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'文件操作'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      body<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Center</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'点击了 $_counter 次'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      floatingActionButton<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">FloatingActionButton</span><span class=\"token punctuation\">(</span>\n        onPressed<span class=\"token punctuation\">:</span> _incrementCounter<span class=\"token punctuation\">,</span>\n        tooltip<span class=\"token punctuation\">:</span> <span class=\"token string\">'Increment'</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>add<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面代码比较简单，不再赘述，需要说明的是，本示例只是为了演示文件读写，而在实际开发中，如果要存储一些简单的数据，使用shared_preferences插件会比较简单。</p> <blockquote><p>注意，Dart IO库操作文件的API非常丰富，但本书不是介绍Dart语言的，故不详细说明，读者需要的话可以自行学习。</p></blockquote></li></ol></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/187.405078e0.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter11/http.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>11.2 通过HttpClient发起HTTP请求 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/110.fec0c84f.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter11/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_11-2-通过httpclient发起http请求\"><a href=\"#_11-2-通过httpclient发起http请求\" class=\"header-anchor\">#</a> 11.2 通过HttpClient发起HTTP请求</h1> <p>Dart IO库中提供了用于发起Http请求的一些类，我们可以直接使用<code>HttpClient</code>来发起请求。使用<code>HttpClient</code>发起请求分为五步：</p> <ol><li><p>创建一个<code>HttpClient</code>：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code> HttpClient httpClient <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">HttpClient</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div></li> <li><p>打开Http连接，设置请求头：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>HttpClientRequest request <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> httpClient<span class=\"token punctuation\">.</span><span class=\"token function\">getUrl</span><span class=\"token punctuation\">(</span>uri<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>这一步可以使用任意Http Method，如<code>httpClient.post(...)</code>、<code>httpClient.delete(...)</code>等。如果包含Query参数，可以在构建uri时添加，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Uri uri<span class=\"token operator\">=</span><span class=\"token function\">Uri</span><span class=\"token punctuation\">(</span>scheme<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;https&quot;</span><span class=\"token punctuation\">,</span> host<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;flutterchina.club&quot;</span><span class=\"token punctuation\">,</span> queryParameters<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token string\">&quot;xx&quot;</span><span class=\"token punctuation\">:</span><span class=\"token string\">&quot;xx&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token string\">&quot;yy&quot;</span><span class=\"token punctuation\">:</span><span class=\"token string\">&quot;dd&quot;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>通过<code>HttpClientRequest</code>可以设置请求header，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>request<span class=\"token punctuation\">.</span>headers<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;user-agent&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;test&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>如果是post或put等可以携带请求体方法，可以通过HttpClientRequest对象发送request body，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>String payload<span class=\"token operator\">=</span><span class=\"token string\">&quot;...&quot;</span><span class=\"token punctuation\">;</span>\nrequest<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span>utf8<span class=\"token punctuation\">.</span><span class=\"token function\">encode</span><span class=\"token punctuation\">(</span>payload<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> \n<span class=\"token comment\">//request.addStream(_inputStream); //可以直接添加输入流</span>\n</code></pre></div></li> <li><p>等待连接服务器：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>HttpClientResponse response <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> request<span class=\"token punctuation\">.</span><span class=\"token function\">close</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>这一步完成后，请求信息就已经发送给服务器了，返回一个<code>HttpClientResponse</code>对象，它包含响应头（header）和响应流(响应体的Stream)，接下来就可以通过读取响应流来获取响应内容。</p></li> <li><p>读取响应内容：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>String responseBody <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> response<span class=\"token punctuation\">.</span><span class=\"token function\">transform</span><span class=\"token punctuation\">(</span>utf8<span class=\"token punctuation\">.</span>decoder<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">join</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>我们通过读取响应流来获取服务器返回的数据，在读取时我们可以设置编码格式，这里是utf8。</p></li> <li><p>请求结束，关闭<code>HttpClient</code>：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>httpClient<span class=\"token punctuation\">.</span><span class=\"token function\">close</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>关闭client后，通过该client发起的所有请求都会中止。</p></li></ol> <h4 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h4> <p>我们实现一个获取百度首页html的例子，示例效果如图11-1所示：</p> <p>​    <img src=\"/assets/img/11-1.dd10418c.png\" alt=\"图11-1\"></p> <p>点击“获取百度首页”按钮后，会请求百度首页，请求成功后，我们将返回内容显示出来并在控制台打印响应header，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'dart:convert'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'dart:io'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">HttpTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _HttpTestRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_HttpTestRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_HttpTestRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>HttpTestRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  bool _loading <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n  String _text <span class=\"token operator\">=</span> <span class=\"token string\">&quot;&quot;</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">ConstrainedBox</span><span class=\"token punctuation\">(</span>\n      constraints<span class=\"token punctuation\">:</span> BoxConstraints<span class=\"token punctuation\">.</span><span class=\"token function\">expand</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">SingleChildScrollView</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n                child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;获取百度首页&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                onPressed<span class=\"token punctuation\">:</span> _loading <span class=\"token operator\">?</span> <span class=\"token keyword\">null</span> <span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n                  <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                    _loading <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n                    _text <span class=\"token operator\">=</span> <span class=\"token string\">&quot;正在请求...&quot;</span><span class=\"token punctuation\">;</span>\n                  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                  <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n                    <span class=\"token comment\">//创建一个HttpClient</span>\n                    HttpClient httpClient <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">HttpClient</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                    <span class=\"token comment\">//打开Http连接</span>\n                    HttpClientRequest request <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> httpClient<span class=\"token punctuation\">.</span><span class=\"token function\">getUrl</span><span class=\"token punctuation\">(</span>\n                        Uri<span class=\"token punctuation\">.</span><span class=\"token function\">parse</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;https://www.baidu.com&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                    <span class=\"token comment\">//使用iPhone的UA</span>\n                    request<span class=\"token punctuation\">.</span>headers<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;user-agent&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                    <span class=\"token comment\">//等待连接服务器（会将请求信息发送给服务器）</span>\n                    HttpClientResponse response <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> request<span class=\"token punctuation\">.</span><span class=\"token function\">close</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                    <span class=\"token comment\">//读取响应内容</span>\n                    _text <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> response<span class=\"token punctuation\">.</span><span class=\"token function\">transform</span><span class=\"token punctuation\">(</span>utf8<span class=\"token punctuation\">.</span>decoder<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">join</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                    <span class=\"token comment\">//输出响应头</span>\n                    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>response<span class=\"token punctuation\">.</span>headers<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n                    <span class=\"token comment\">//关闭client后，通过该client发起的所有请求都会中止。</span>\n                    httpClient<span class=\"token punctuation\">.</span><span class=\"token function\">close</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n                  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                    _text <span class=\"token operator\">=</span> <span class=\"token string\">&quot;请求失败：$e&quot;</span><span class=\"token punctuation\">;</span>\n                  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">finally</span> <span class=\"token punctuation\">{</span>\n                    <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                      _loading <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n                    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                  <span class=\"token punctuation\">}</span>\n                <span class=\"token punctuation\">}</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n                width<span class=\"token punctuation\">:</span> MediaQuery<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>size<span class=\"token punctuation\">.</span>width<span class=\"token operator\">-</span><span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n                child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>_text<span class=\"token punctuation\">.</span><span class=\"token function\">replaceAll</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">RegExp</span><span class=\"token punctuation\">(</span><span class=\"token string\">r&quot;\\s&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">)</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>控制台输出：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>I/flutter (18545): connection: Keep-Alive\nI/flutter (18545): cache-control: no-cache\nI/flutter (18545): set-cookie: ....  //有多个，省略...\nI/flutter (18545): transfer-encoding: chunked\nI/flutter (18545): date: Tue, 30 Oct 2018 10:00:52 GMT\nI/flutter (18545): content-encoding: gzip\nI/flutter (18545): vary: Accept-Encoding\nI/flutter (18545): strict-transport-security: max-age=172800\nI/flutter (18545): content-type: text/html;charset=utf-8\nI/flutter (18545): tracecode: 00525262401065761290103018, 00522983\n</code></pre></div><h4 id=\"httpclient配置\"><a href=\"#httpclient配置\" class=\"header-anchor\">#</a> HttpClient配置</h4> <p><code>HttpClient</code>有很多属性可以配置，常用的属性列表如下：</p> <table><thead><tr><th>属性</th> <th>含义</th></tr></thead> <tbody><tr><td>idleTimeout</td> <td>对应请求头中的keep-alive字段值，为了避免频繁建立连接，httpClient在请求结束后会保持连接一段时间，超过这个阈值后才会关闭连接。</td></tr> <tr><td>connectionTimeout</td> <td>和服务器建立连接的超时，如果超过这个值则会抛出SocketException异常。</td></tr> <tr><td>maxConnectionsPerHost</td> <td>同一个host，同时允许建立连接的最大数量。</td></tr> <tr><td>autoUncompress</td> <td>对应请求头中的Content-Encoding，如果设置为true，则请求头中Content-Encoding的值为当前HttpClient支持的压缩算法列表，目前只有&quot;gzip&quot;</td></tr> <tr><td>userAgent</td> <td>对应请求头中的User-Agent字段。</td></tr></tbody></table> <p>可以发现，有些属性只是为了更方便的设置请求头，对于这些属性，你完全可以通过<code>HttpClientRequest</code>直接设置header，不同的是通过<code>HttpClient</code>设置的对整个<code>httpClient</code>都生效，而通过<code>HttpClientRequest</code>设置的只对当前请求生效。</p> <h4 id=\"http请求认证\"><a href=\"#http请求认证\" class=\"header-anchor\">#</a> HTTP请求认证</h4> <p>Http协议的认证（Authentication）机制可以用于保护非公开资源。如果Http服务器开启了认证，那么用户在发起请求时就需要携带用户凭据，如果你在浏览器中访问了启用Basic认证的资源时，浏览就会弹出一个登录框，如：</p> <p><img src=\"https://cdn.jsdelivr.net/gh/flutterchina/flutter-in-action@1.0/docs/imgs/image-20181031114207514.png\" alt=\"image-20181031114207514\"></p> <p>我们先看看Basic认证的基本过程：</p> <ol><li><p>客户端发送http请求给服务器，服务器验证该用户是否已经登录验证过了，如果没有的话，  服务器会返回一个401 Unauthozied给客户端，并且在响应header中添加一个 “WWW-Authenticate” 字段，例如：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>WWW-Authenticate: Basic realm=&quot;admin&quot;\n</code></pre></div><p>其中&quot;Basic&quot;为认证方式，realm为用户角色的分组，可以在后台添加分组。</p></li> <li><p>客户端得到响应码后，将用户名和密码进行base64编码（格式为用户名:密码），设置请求头Authorization，继续访问 :</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>Authorization: Basic YXXFISDJFISJFGIJIJG\n</code></pre></div><p>服务器验证用户凭据，如果通过就返回资源内容。</p></li></ol> <p>注意，Http的方式除了Basic认证之外还有：Digest认证、Client认证、Form Based认证等，目前Flutter的HttpClient只支持Basic和Digest两种认证方式，这两种认证方式最大的区别是发送用户凭据时，对于用户凭据的内容，前者只是简单的通过Base64编码（可逆），而后者会进行哈希运算，相对来说安全一点点，但是为了安全起见，<strong>无论是采用Basic认证还是Digest认证，都应该在Https协议下</strong>，这样可以防止抓包和中间人攻击。</p> <p><code>HttpClient</code>关于Http认证的方法和属性：</p> <ol><li><p><code>addCredentials(Uri url, String realm, HttpClientCredentials credentials)</code></p> <p>该方法用于添加用户凭据,如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>httpClient<span class=\"token punctuation\">.</span><span class=\"token function\">addCredentials</span><span class=\"token punctuation\">(</span>_uri<span class=\"token punctuation\">,</span>\n <span class=\"token string\">&quot;admin&quot;</span><span class=\"token punctuation\">,</span> \n  <span class=\"token keyword\">new</span> <span class=\"token class-name\">HttpClientBasicCredentials</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;username&quot;</span><span class=\"token punctuation\">,</span><span class=\"token string\">&quot;password&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//Basic认证凭据</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>如果是Digest认证，可以创建Digest认证凭据：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">HttpClientDigestCredentials</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;username&quot;</span><span class=\"token punctuation\">,</span><span class=\"token string\">&quot;password&quot;</span><span class=\"token punctuation\">)</span>\n</code></pre></div></li> <li><p><code>authenticate(Future&lt;bool&gt; f(Uri url, String scheme, String realm))</code></p> <p>这是一个setter，类型是一个回调，当服务器需要用户凭据且该用户凭据未被添加时，httpClient会调用此回调，在这个回调当中，一般会调用<code>addCredential()</code>来动态添加用户凭证，例如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>httpClient<span class=\"token punctuation\">.</span>authenticate<span class=\"token operator\">=</span><span class=\"token punctuation\">(</span>Uri url<span class=\"token punctuation\">,</span> String scheme<span class=\"token punctuation\">,</span> String realm<span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span><span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">.</span>host<span class=\"token operator\">==</span><span class=\"token string\">&quot;xx.com&quot;</span> <span class=\"token operator\">&amp;&amp;</span> realm<span class=\"token operator\">==</span><span class=\"token string\">&quot;admin&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    httpClient<span class=\"token punctuation\">.</span><span class=\"token function\">addCredentials</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">,</span>\n      <span class=\"token string\">&quot;admin&quot;</span><span class=\"token punctuation\">,</span>\n      <span class=\"token keyword\">new</span> <span class=\"token class-name\">HttpClientBasicCredentials</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;username&quot;</span><span class=\"token punctuation\">,</span><span class=\"token string\">&quot;pwd&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> \n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token keyword\">return</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>一个建议是，如果所有请求都需要认证，那么应该在HttpClient初始化时就调用<code>addCredentials()</code>来添加全局凭证，而不是去动态添加。</p></li></ol> <h4 id=\"代理\"><a href=\"#代理\" class=\"header-anchor\">#</a> 代理</h4> <p>可以通过<code>findProxy</code>来设置代理策略，例如，我们要将所有请求通过代理服务器（192.168.1.2:8888）发送出去：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>  client<span class=\"token punctuation\">.</span>findProxy <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>uri<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 如果需要过滤uri，可以手动判断</span>\n    <span class=\"token keyword\">return</span> <span class=\"token string\">&quot;PROXY 192.168.1.2:8888&quot;</span><span class=\"token punctuation\">;</span>\n <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p><code>findProxy</code> 回调返回值是一个遵循浏览器PAC脚本格式的字符串，详情可以查看API文档，如果不需要代理，返回&quot;DIRECT&quot;即可。</p> <p>在APP开发中，很多时候我们需要抓包来调试，而抓包软件(如charles)就是一个代理，这时我们就可以将请求发送到我们的抓包软件，我们就可以在抓包软件中看到请求的数据了。</p> <p>有时代理服务器也启用了身份验证，这和http协议的认证是相似的，HttpClient提供了对应的Proxy认证方法和属性：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">set</span> <span class=\"token function\">authenticateProxy</span><span class=\"token punctuation\">(</span>\n    Future<span class=\"token operator\">&lt;</span>bool<span class=\"token operator\">&gt;</span> <span class=\"token function\">f</span><span class=\"token punctuation\">(</span>String host<span class=\"token punctuation\">,</span> int port<span class=\"token punctuation\">,</span> String scheme<span class=\"token punctuation\">,</span> String realm<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">void</span> <span class=\"token function\">addProxyCredentials</span><span class=\"token punctuation\">(</span>\n    String host<span class=\"token punctuation\">,</span> int port<span class=\"token punctuation\">,</span> String realm<span class=\"token punctuation\">,</span> HttpClientCredentials credentials<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>他们的使用方法和上面“HTTP请求认证”一节中介绍的<code>addCredentials</code>和<code>authenticate</code> 相同，故不再赘述。</p> <h4 id=\"证书校验\"><a href=\"#证书校验\" class=\"header-anchor\">#</a> 证书校验</h4> <p>Https中为了防止通过伪造证书而发起的中间人攻击，客户端应该对自签名或非CA颁发的证书进行校验。<code>HttpClient</code>对证书校验的逻辑如下：</p> <ol><li>如果请求的Https证书是可信CA颁发的，并且访问host包含在证书的domain列表中(或者符合通配规则)并且证书未过期，则验证通过。</li> <li>如果第一步验证失败，但在创建HttpClient时，已经通过SecurityContext将证书添加到证书信任链中，那么当服务器返回的证书在信任链中的话，则验证通过。</li> <li>如果1、2验证都失败了，如果用户提供了<code>badCertificateCallback</code>回调，则会调用它，如果回调返回<code>true</code>，则允许继续链接，如果返回<code>false</code>，则终止链接。</li></ol> <p>综上所述，我们的证书校验其实就是提供一个<code>badCertificateCallback</code>回调，下面通过一个示例来说明。</p> <h5 id=\"示例-2\"><a href=\"#示例-2\" class=\"header-anchor\">#</a> 示例</h5> <p>假设我们的后台服务使用的是自签名证书，证书格式是PEM格式，我们将证书的内容保存在本地字符串中，那么我们的校验逻辑如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>String PEM<span class=\"token operator\">=</span><span class=\"token string\">&quot;XXXXX&quot;</span><span class=\"token punctuation\">;</span><span class=\"token comment\">//可以从文件读取</span>\n<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\nhttpClient<span class=\"token punctuation\">.</span>badCertificateCallback<span class=\"token operator\">=</span><span class=\"token punctuation\">(</span>X509Certificate cert<span class=\"token punctuation\">,</span> String host<span class=\"token punctuation\">,</span> int port<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>cert<span class=\"token punctuation\">.</span>pem<span class=\"token operator\">==</span>PEM<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//证书一致，则允许发送数据</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token keyword\">return</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p><code>X509Certificate</code>是证书的标准格式，包含了证书除私钥外所有信息，读者可以自行查阅文档。另外，上面的示例没有校验host，是因为只要服务器返回的证书内容和本地的保存一致就已经能证明是我们的服务器了（而不是中间人），host验证通常是为了防止证书和域名不匹配。</p> <p>对于自签名的证书，我们也可以将其添加到本地证书信任链中，这样证书验证时就会自动通过，而不会再走到<code>badCertificateCallback</code>回调中：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>SecurityContext sc<span class=\"token operator\">=</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">SecurityContext</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">//file为证书路径</span>\nsc<span class=\"token punctuation\">.</span><span class=\"token function\">setTrustedCertificates</span><span class=\"token punctuation\">(</span>file<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">//创建一个HttpClient</span>\nHttpClient httpClient <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">HttpClient</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">:</span> sc<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>注意，通过<code>setTrustedCertificates()</code>设置的证书格式必须为PEM或PKCS12，如果证书格式为PKCS12，则需将证书密码传入，这样则会在代码中暴露证书密码，所以客户端证书校验不建议使用PKCS12格式的证书。</p> <h4 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h4> <p>值得注意的是，<code>HttpClient</code>提供的这些属性和方法最终都会作用在请求header里，我们完全可以通过手动去设置header来实现，之所以提供这些方法，只是为了方便开发者而已。另外，Http协议是一个非常重要的、使用最多的网络协议，每一个开发者都应该对http协议非常熟悉。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/110.fec0c84f.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter11/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>本章目录 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/188.766e2a5e.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter11/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"本章目录\"><a href=\"#本章目录\" class=\"header-anchor\">#</a> 本章目录</h1> <ul><li><a href=\"/v2/chapter11/file_operation.html\">11.1：文件操作</a></li> <li><a href=\"/v2/chapter11/http.html\">11.2：Http请求-HttpClient</a></li> <li><a href=\"/v2/chapter11/dio.html\">11.3：Http请求-Dio package</a></li> <li><a href=\"/v2/chapter11/download_with_chunks.html\">11.4：实例：Http分块下载</a></li> <li><a href=\"/v2/chapter11/websocket.html\">11.5：WebSocket</a></li> <li><a href=\"/v2/chapter11/socket.html\">11.6：使用Socket API</a></li> <li><a href=\"/v2/chapter11/json_model.html\">11.7：Json转Dart Model类</a></li></ul></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/188.766e2a5e.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter11/json_model.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>11.7 Json转Dart Model类 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/111.95623244.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter11/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_11-7-json转dart-model类\"><a href=\"#_11-7-json转dart-model类\" class=\"header-anchor\">#</a> 11.7 Json转Dart Model类</h1> <p>在实战中，后台接口往往会返回一些结构化数据，如JSON、XML等，如之前我们请求Github API的示例，它返回的数据就是JSON格式的字符串，为了方便我们在代码中操作JSON，我们先将JSON格式的字符串转为Dart对象，这个可以通过<code>dart:convert</code>中内置的JSON解码器json.decode() 来实现，该方法可以根据JSON字符串具体内容将其转为List或Map，这样我们就可以通过他们来查找所需的值，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//一个JSON格式的用户列表字符串</span>\nString jsonStr<span class=\"token operator\">=</span><span class=\"token string\">'[{&quot;name&quot;:&quot;Jack&quot;},{&quot;name&quot;:&quot;Rose&quot;}]'</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">//将JSON字符串转为Dart对象(此处是List)</span>\nList items<span class=\"token operator\">=</span>json<span class=\"token punctuation\">.</span><span class=\"token function\">decode</span><span class=\"token punctuation\">(</span>jsonStr<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">//输出第一个用户的姓名</span>\n<span class=\"token function\">print</span><span class=\"token punctuation\">(</span>items<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">[</span><span class=\"token string\">&quot;name&quot;</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>通过json.decode() 将JSON字符串转为List/Map的方法比较简单，它没有外部依赖或其它的设置，对于小项目很方便。但当项目变大时，这种手动编写序列化逻辑可能变得难以管理且容易出错，例如有如下JSON:</p> <div class=\"language-json extra-class\"><pre class=\"language-json\"><code><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;name&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;John Smith&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;email&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;john@example.com&quot;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们可以通过调用<code>json.decode</code>方法来解码JSON ，使用JSON字符串作为参数:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Map<span class=\"token operator\">&lt;</span>String<span class=\"token punctuation\">,</span> <span class=\"token keyword\">dynamic</span><span class=\"token operator\">&gt;</span> user <span class=\"token operator\">=</span> json<span class=\"token punctuation\">.</span><span class=\"token function\">decode</span><span class=\"token punctuation\">(</span>json<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Howdy, ${user['</span>name<span class=\"token string\">']}!'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">'We sent the verification link to ${user['</span>email<span class=\"token string\">']}.'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>由于<code>json.decode()</code>仅返回一个<code>Map&lt;String, dynamic&gt;</code>，这意味着直到运行时我们才知道值的类型。 通过这种方法，我们失去了大部分静态类型语言特性：类型安全、自动补全和最重要的编译时异常。这样一来，我们的代码可能会变得非常容易出错。例如，当我们访问<code>name</code>或<code>email</code>字段时，我们输入的很快，导致字段名打错了。但由于这个JSON在map结构中，所以编译器不知道这个错误的字段名，所以编译时不会报错。</p> <p>其实，这个问题在很多平台上都会遇到，而也早就有了好的解决方法即“Json Model化”，具体做法就是，通过预定义一些与Json结构对应的Model类，然后在请求到数据后再动态根据数据创建出Model类的实例。这样一来，在开发阶段我们使用的是Model类的实例，而不再是Map/List，这样访问内部属性时就不会发生拼写错误。例如，我们可以通过引入一个简单的模型类(Model class)来解决前面提到的问题，我们称之为<code>User</code>。在User类内部，我们有：</p> <ul><li>一个<code>User.fromJson</code> 构造函数, 用于从一个map构造出一个 <code>User</code>实例 map structure</li> <li>一个<code>toJson</code> 方法, 将 <code>User</code> 实例转化为一个map.</li></ul> <p>这样，调用代码现在可以具有类型安全、自动补全字段（name和email）以及编译时异常。如果我们将拼写错误字段视为<code>int</code>类型而不是<code>String</code>， 那么我们的代码就不会通过编译，而不是在运行时崩溃。</p> <p><strong>user.dart</strong></p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">User</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">final</span> String name<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> String email<span class=\"token punctuation\">;</span>\n\n  <span class=\"token function\">User</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>name<span class=\"token punctuation\">,</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>email<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  User<span class=\"token punctuation\">.</span><span class=\"token function\">fromJson</span><span class=\"token punctuation\">(</span>Map<span class=\"token operator\">&lt;</span>String<span class=\"token punctuation\">,</span> <span class=\"token keyword\">dynamic</span><span class=\"token operator\">&gt;</span> json<span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">:</span> name <span class=\"token operator\">=</span> json<span class=\"token punctuation\">[</span><span class=\"token string\">'name'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        email <span class=\"token operator\">=</span> json<span class=\"token punctuation\">[</span><span class=\"token string\">'email'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n  Map<span class=\"token operator\">&lt;</span>String<span class=\"token punctuation\">,</span> <span class=\"token keyword\">dynamic</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">toJson</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span>\n    <span class=\"token operator\">&lt;</span>String<span class=\"token punctuation\">,</span> <span class=\"token keyword\">dynamic</span><span class=\"token operator\">&gt;</span><span class=\"token punctuation\">{</span>\n      <span class=\"token string\">'name'</span><span class=\"token punctuation\">:</span> name<span class=\"token punctuation\">,</span>\n      <span class=\"token string\">'email'</span><span class=\"token punctuation\">:</span> email<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>现在，序列化逻辑移到了模型本身内部。采用这种新方法，我们可以非常容易地反序列化user.</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Map userMap <span class=\"token operator\">=</span> json<span class=\"token punctuation\">.</span><span class=\"token function\">decode</span><span class=\"token punctuation\">(</span>json<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">var</span> user <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">User<span class=\"token punctuation\">.</span>fromJson</span><span class=\"token punctuation\">(</span>userMap<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Howdy, ${user.name}!'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">'We sent the verification link to ${user.email}.'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>要序列化一个user，我们只是将该<code>User</code>对象传递给该<code>json.encode</code>方法。我们不需要手动调用<code>toJson</code>这个方法，因为`JSON.encode内部会自动调用。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>String json <span class=\"token operator\">=</span> json<span class=\"token punctuation\">.</span><span class=\"token function\">encode</span><span class=\"token punctuation\">(</span>user<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>这样，调用代码就不用担心JSON序列化了，但是，Model类还是必须的。在实践中，<code>User.fromJson</code>和<code>User.toJson</code>方法都需要单元测试到位，以验证正确的行为。</p> <p>另外，实际场景中，JSON对象很少会这么简单，嵌套的JSON对象并不罕见，如果有什么能为我们自动处理JSON序列化，那将会非常好。幸运的是，有！</p> <h3 id=\"自动生成model\"><a href=\"#自动生成model\" class=\"header-anchor\">#</a> 自动生成Model</h3> <p>尽管还有其他库可用，但在本书中，我们介绍一下官方推荐的<a href=\"https://pub.dartlang.org/packages/json_serializable\" target=\"_blank\" rel=\"noopener noreferrer\">json_serializable package<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>包。 它是一个自动化的源代码生成器，可以在开发阶段为我们生成JSON序列化模板，这样一来，由于序列化代码不再由我们手写和维护，我们将运行时产生JSON序列化异常的风险降至最低。</p> <h3 id=\"在项目中设置json-serializable\"><a href=\"#在项目中设置json-serializable\" class=\"header-anchor\">#</a> 在项目中设置json_serializable</h3> <p>要包含<code>json_serializable</code>到我们的项目中，我们需要一个常规和两个<strong>开发依赖</strong>项。简而言之，<strong>开发依赖项</strong>是不包含在我们的应用程序源代码中的依赖项，它是开发过程中的一些辅助工具、脚本，和node中的开发依赖项相似。</p> <p><strong>pubspec.yaml</strong></p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code><span class=\"token key atrule\">dependencies</span><span class=\"token punctuation\">:</span>\n  <span class=\"token comment\"># Your other regular dependencies here</span>\n  <span class=\"token key atrule\">json_annotation</span><span class=\"token punctuation\">:</span> ^2.0.0\n\n<span class=\"token key atrule\">dev_dependencies</span><span class=\"token punctuation\">:</span>\n  <span class=\"token comment\"># Your other dev_dependencies here</span>\n  <span class=\"token key atrule\">build_runner</span><span class=\"token punctuation\">:</span> ^1.0.0\n  <span class=\"token key atrule\">json_serializable</span><span class=\"token punctuation\">:</span> ^2.0.0\n</code></pre></div><p>在您的项目根文件夹中运行 <code>flutter packages get</code> (或者在编辑器中点击 “Packages Get”) 以在项目中使用这些新的依赖项.</p> <h3 id=\"以json-serializable的方式创建model类\"><a href=\"#以json-serializable的方式创建model类\" class=\"header-anchor\">#</a> 以json_serializable的方式创建model类</h3> <p>让我们看看如何将我们的<code>User</code>类转换为一个<code>json_serializable</code>。为了简单起见，我们使用前面示例中的简化JSON model。</p> <p><strong>user.dart</strong></p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:json_annotation/json_annotation.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// user.g.dart 将在我们运行生成命令后自动生成</span>\n<span class=\"token keyword\">part</span> <span class=\"token string\">'user.g.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">///这个标注是告诉生成器，这个类是需要生成Model类的</span>\n<span class=\"token metadata symbol\">@JsonSerializable</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">User</span><span class=\"token punctuation\">{</span>\n  <span class=\"token function\">User</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>name<span class=\"token punctuation\">,</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>email<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  String name<span class=\"token punctuation\">;</span>\n  String email<span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">//不同的类使用不同的mixin即可</span>\n  <span class=\"token keyword\">factory</span> User<span class=\"token punctuation\">.</span><span class=\"token function\">fromJson</span><span class=\"token punctuation\">(</span>Map<span class=\"token operator\">&lt;</span>String<span class=\"token punctuation\">,</span> <span class=\"token keyword\">dynamic</span><span class=\"token operator\">&gt;</span> json<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _$<span class=\"token function\">UserFromJson</span><span class=\"token punctuation\">(</span>json<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  Map<span class=\"token operator\">&lt;</span>String<span class=\"token punctuation\">,</span> <span class=\"token keyword\">dynamic</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">toJson</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _$<span class=\"token function\">UserToJson</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>  \n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>有了上面的设置，源码生成器将生成用于序列化<code>name</code>和<code>email</code>字段的JSON代码。</p> <p>如果需要，自定义命名策略也很容易。例如，如果我们正在使用的API返回带有_snake_case_的对象，但我们想在我们的模型中使用_lowerCamelCase_， 那么我们可以使用@JsonKey标注：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//显式关联JSON字段名与Model属性的对应关系 </span>\n<span class=\"token metadata symbol\">@JsonKey</span><span class=\"token punctuation\">(</span>name<span class=\"token punctuation\">:</span> <span class=\"token string\">'registration_date_millis'</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">final</span> int registrationDateMillis<span class=\"token punctuation\">;</span>\n</code></pre></div><h3 id=\"运行代码生成程序\"><a href=\"#运行代码生成程序\" class=\"header-anchor\">#</a> 运行代码生成程序</h3> <p><code>json_serializable</code>第一次创建类时，您会看到与图11-4类似的错误。</p> <p><img src=\"/assets/img/11-4.b1cfbd03.png\" alt=\"ide_warning\"></p> <p>这些错误是完全正常的，这是因为Model类的生成代码还不存在。为了解决这个问题，我们必须运行代码生成器来为我们生成序列化模板。有两种运行代码生成器的方法：</p> <h4 id=\"一次性生成\"><a href=\"#一次性生成\" class=\"header-anchor\">#</a> 一次性生成</h4> <p>通过在我们的项目根目录下运行:</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>flutter packages pub run build_runner build\n</code></pre></div><p>这触发了一次性构建，我们可以在需要时为我们的Model生成json序列化代码，它通过我们的源文件，找出需要生成Model类的源文件（包含@JsonSerializable标注的）来生成对应的.g.dart文件。一个好的建议是将所有Model类放在一个单独的目录下，然后在该目录下执行命令。</p> <p>虽然这非常方便，但如果我们不需要每次在Model类中进行更改时都要手动运行构建命令的话会更好。</p> <h4 id=\"持续生成\"><a href=\"#持续生成\" class=\"header-anchor\">#</a> 持续生成</h4> <p>使用_watcher_可以使我们的源代码生成的过程更加方便。它会监视我们项目中文件的变化，并在需要时自动构建必要的文件，我们可以通过<code>flutter packages pub run build_runner watch</code>在项目根目录下运行来启动_watcher_。只需启动一次观察器，然后它就会在后台运行，这是安全的。</p> <h3 id=\"自动化生成模板\"><a href=\"#自动化生成模板\" class=\"header-anchor\">#</a> 自动化生成模板</h3> <p>上面的方法有一个最大的问题就是要为每一个json写模板，这是比较枯燥的。如果有一个工具可以直接根据JSON文本生成模板，那我们就能彻底解放双手了。笔者自己用dart实现了一个脚本，它可以自动生成模板，并直接将JSON转为Model类，下面我们看看怎么做：</p> <ol><li><p>定义一个&quot;模板的模板&quot;，名为&quot;template.dart&quot;：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:json_annotation/json_annotation.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token operator\">%</span>t\n<span class=\"token keyword\">part</span> <span class=\"token string\">'%s.g.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token metadata symbol\">@JsonSerializable</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">class</span> <span class=\"token operator\">%</span>s <span class=\"token punctuation\">{</span>\n    <span class=\"token operator\">%</span><span class=\"token function\">s</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token operator\">%</span>s\n    <span class=\"token keyword\">factory</span> <span class=\"token operator\">%</span>s<span class=\"token punctuation\">.</span><span class=\"token function\">fromJson</span><span class=\"token punctuation\">(</span>Map<span class=\"token operator\">&lt;</span>String<span class=\"token punctuation\">,</span><span class=\"token keyword\">dynamic</span><span class=\"token operator\">&gt;</span> json<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _$<span class=\"token operator\">%</span><span class=\"token function\">sFromJson</span><span class=\"token punctuation\">(</span>json<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    Map<span class=\"token operator\">&lt;</span>String<span class=\"token punctuation\">,</span> <span class=\"token keyword\">dynamic</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">toJson</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _$<span class=\"token operator\">%</span><span class=\"token function\">sToJson</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>模板中的“%t”、“%s”为占位符，将在脚本运行时动态被替换为合适的导入头和类名。</p></li> <li><p>写一个自动生成模板的脚本(mo.dart)，它可以根据指定的JSON目录，遍历生成模板，在生成时我们定义一些规则：</p> <ul><li>如果JSON文件名以下划线“_”开始，则忽略此JSON文件。</li> <li>复杂的JSON对象往往会出现嵌套，我们可以通过一个特殊标志来手动指定嵌套的对象（后面举例）。</li></ul> <p>脚本我们通过Dart来写，源码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'dart:convert'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'dart:io'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:path/path.dart'</span> <span class=\"token operator\">as</span> path<span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">const</span> TAG<span class=\"token operator\">=</span><span class=\"token string\">&quot;\\$&quot;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">const</span> SRC<span class=\"token operator\">=</span><span class=\"token string\">&quot;./json&quot;</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//JSON 目录</span>\n<span class=\"token keyword\">const</span> DIST<span class=\"token operator\">=</span><span class=\"token string\">&quot;lib/models/&quot;</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//输出model目录</span>\n\n<span class=\"token keyword\">void</span> <span class=\"token function\">walk</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span> <span class=\"token comment\">//遍历JSON目录生成模板</span>\n  <span class=\"token keyword\">var</span> src <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Directory</span><span class=\"token punctuation\">(</span>SRC<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">var</span> list <span class=\"token operator\">=</span> src<span class=\"token punctuation\">.</span><span class=\"token function\">listSync</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">var</span> template<span class=\"token operator\">=</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">File</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;./template.dart&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">readAsStringSync</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  File file<span class=\"token punctuation\">;</span>\n  list<span class=\"token punctuation\">.</span><span class=\"token function\">forEach</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>f<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>FileSystemEntity<span class=\"token punctuation\">.</span><span class=\"token function\">isFileSync</span><span class=\"token punctuation\">(</span>f<span class=\"token punctuation\">.</span>path<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      file <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">File</span><span class=\"token punctuation\">(</span>f<span class=\"token punctuation\">.</span>path<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">var</span> paths<span class=\"token operator\">=</span>path<span class=\"token punctuation\">.</span><span class=\"token function\">basename</span><span class=\"token punctuation\">(</span>f<span class=\"token punctuation\">.</span>path<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">split</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;.&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      String name<span class=\"token operator\">=</span>paths<span class=\"token punctuation\">.</span>first<span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>paths<span class=\"token punctuation\">.</span>last<span class=\"token punctuation\">.</span><span class=\"token function\">toLowerCase</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">!=</span><span class=\"token string\">&quot;json&quot;</span><span class=\"token operator\">||</span>name<span class=\"token punctuation\">.</span><span class=\"token function\">startsWith</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;_&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> <span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>name<span class=\"token punctuation\">.</span><span class=\"token function\">startsWith</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;_&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span><span class=\"token punctuation\">;</span>\n      <span class=\"token comment\">//下面生成模板</span>\n      <span class=\"token keyword\">var</span> map <span class=\"token operator\">=</span> json<span class=\"token punctuation\">.</span><span class=\"token function\">decode</span><span class=\"token punctuation\">(</span>file<span class=\"token punctuation\">.</span><span class=\"token function\">readAsStringSync</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token comment\">//为了避免重复导入相同的包，我们用Set来保存生成的import语句。</span>\n      <span class=\"token keyword\">var</span> <span class=\"token keyword\">set</span><span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Set</span><span class=\"token operator\">&lt;</span>String<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      StringBuffer attrs<span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">StringBuffer</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">(</span>map <span class=\"token operator\">as</span> Map<span class=\"token operator\">&lt;</span>String<span class=\"token punctuation\">,</span> <span class=\"token keyword\">dynamic</span><span class=\"token operator\">&gt;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">forEach</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">,</span> v<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">.</span><span class=\"token function\">startsWith</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;_&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> <span class=\"token punctuation\">;</span>\n          attrs<span class=\"token punctuation\">.</span><span class=\"token function\">write</span><span class=\"token punctuation\">(</span><span class=\"token function\">getType</span><span class=\"token punctuation\">(</span>v<span class=\"token punctuation\">,</span><span class=\"token keyword\">set</span><span class=\"token punctuation\">,</span>name<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          attrs<span class=\"token punctuation\">.</span><span class=\"token function\">write</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot; &quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          attrs<span class=\"token punctuation\">.</span><span class=\"token function\">write</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          attrs<span class=\"token punctuation\">.</span><span class=\"token function\">writeln</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;;&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          attrs<span class=\"token punctuation\">.</span><span class=\"token function\">write</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;    &quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      String  className<span class=\"token operator\">=</span>name<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span><span class=\"token function\">toUpperCase</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">+</span>name<span class=\"token punctuation\">.</span><span class=\"token function\">substring</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">var</span> dist<span class=\"token operator\">=</span><span class=\"token function\">format</span><span class=\"token punctuation\">(</span>template<span class=\"token punctuation\">,</span><span class=\"token punctuation\">[</span>name<span class=\"token punctuation\">,</span>className<span class=\"token punctuation\">,</span>className<span class=\"token punctuation\">,</span>attrs<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                                className<span class=\"token punctuation\">,</span>className<span class=\"token punctuation\">,</span>className<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">var</span> _import<span class=\"token operator\">=</span><span class=\"token keyword\">set</span><span class=\"token punctuation\">.</span><span class=\"token function\">join</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;;\\r\\n&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      _import<span class=\"token operator\">+=</span>_import<span class=\"token punctuation\">.</span>isEmpty<span class=\"token operator\">?</span><span class=\"token string\">&quot;&quot;</span><span class=\"token punctuation\">:</span><span class=\"token string\">&quot;;&quot;</span><span class=\"token punctuation\">;</span>\n      dist<span class=\"token operator\">=</span>dist<span class=\"token punctuation\">.</span><span class=\"token function\">replaceFirst</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;%t&quot;</span><span class=\"token punctuation\">,</span>_import <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token comment\">//将生成的模板输出</span>\n      <span class=\"token keyword\">new</span> <span class=\"token class-name\">File</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;$DIST$name.dart&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">writeAsStringSync</span><span class=\"token punctuation\">(</span>dist<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\nString <span class=\"token function\">changeFirstChar</span><span class=\"token punctuation\">(</span>String str<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>bool upper<span class=\"token operator\">=</span><span class=\"token boolean\">true</span><span class=\"token punctuation\">]</span> <span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>upper<span class=\"token operator\">?</span>str<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span><span class=\"token function\">toUpperCase</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>str<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span><span class=\"token function\">toLowerCase</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token operator\">+</span>str<span class=\"token punctuation\">.</span><span class=\"token function\">substring</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">//将JSON类型转为对应的dart类型</span>\n String <span class=\"token function\">getType</span><span class=\"token punctuation\">(</span>v<span class=\"token punctuation\">,</span>Set<span class=\"token operator\">&lt;</span>String<span class=\"token operator\">&gt;</span> <span class=\"token keyword\">set</span><span class=\"token punctuation\">,</span>String current<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n  current<span class=\"token operator\">=</span>current<span class=\"token punctuation\">.</span><span class=\"token function\">toLowerCase</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>v <span class=\"token operator\">is</span> bool<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token string\">&quot;bool&quot;</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token keyword\">else</span> <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>v <span class=\"token operator\">is</span> num<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token string\">&quot;num&quot;</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token keyword\">else</span> <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>v <span class=\"token operator\">is</span> Map<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token string\">&quot;Map&lt;String,dynamic&gt;&quot;</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token keyword\">else</span> <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>v <span class=\"token operator\">is</span> List<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token string\">&quot;List&quot;</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token keyword\">else</span> <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>v <span class=\"token operator\">is</span> String<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span> <span class=\"token comment\">//处理特殊标志</span>\n    <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>v<span class=\"token punctuation\">.</span><span class=\"token function\">startsWith</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;$TAG[]&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">var</span> className<span class=\"token operator\">=</span><span class=\"token function\">changeFirstChar</span><span class=\"token punctuation\">(</span>v<span class=\"token punctuation\">.</span><span class=\"token function\">substring</span><span class=\"token punctuation\">(</span><span class=\"token number\">3</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>className<span class=\"token punctuation\">.</span><span class=\"token function\">toLowerCase</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">!=</span>current<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">set</span><span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token string\">'import &quot;$className.dart&quot;'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n      <span class=\"token keyword\">return</span> <span class=\"token string\">&quot;List&lt;${changeFirstChar(className)}&gt;&quot;</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token punctuation\">}</span><span class=\"token keyword\">else</span> <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>v<span class=\"token punctuation\">.</span><span class=\"token function\">startsWith</span><span class=\"token punctuation\">(</span>TAG<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">var</span> fileName<span class=\"token operator\">=</span><span class=\"token function\">changeFirstChar</span><span class=\"token punctuation\">(</span>v<span class=\"token punctuation\">.</span><span class=\"token function\">substring</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>fileName<span class=\"token punctuation\">.</span><span class=\"token function\">toLowerCase</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">!=</span>current<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">set</span><span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token string\">'import &quot;$fileName.dart&quot;'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n      <span class=\"token keyword\">return</span> <span class=\"token function\">changeFirstChar</span><span class=\"token punctuation\">(</span>fileName<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">return</span> <span class=\"token string\">&quot;String&quot;</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token keyword\">else</span><span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token string\">&quot;String&quot;</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n <span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">//替换模板占位符</span>\nString <span class=\"token function\">format</span><span class=\"token punctuation\">(</span>String fmt<span class=\"token punctuation\">,</span> List<span class=\"token operator\">&lt;</span>Object<span class=\"token operator\">&gt;</span> params<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  int matchIndex <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span>\n  String <span class=\"token function\">replace</span><span class=\"token punctuation\">(</span>Match m<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>matchIndex <span class=\"token operator\">&lt;</span> params<span class=\"token punctuation\">.</span>length<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">switch</span> <span class=\"token punctuation\">(</span>m<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">case</span> <span class=\"token string\">&quot;%s&quot;</span><span class=\"token punctuation\">:</span>\n          <span class=\"token keyword\">return</span> params<span class=\"token punctuation\">[</span>matchIndex<span class=\"token operator\">++</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Exception</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Missing parameter for string format&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Exception</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Invalid format string: &quot;</span> <span class=\"token operator\">+</span> m<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token keyword\">return</span> fmt<span class=\"token punctuation\">.</span><span class=\"token function\">replaceAllMapped</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;%s&quot;</span><span class=\"token punctuation\">,</span> replace<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">void</span> <span class=\"token function\">main</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n  <span class=\"token function\">walk</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div></li> <li><p>写一个shell(mo.sh)，将生成模板和生成model串起来：</p> <div class=\"language-sh extra-class\"><pre class=\"language-sh\"><code>dart mo.dart\nflutter packages pub run build_runner build --delete-conflicting-outputs\n</code></pre></div></li></ol> <p>至此，我们的脚本写好了，我们在根目录下新建一个json目录，然后把user.json移进去，然后在lib目录下创建一个models目录，用于保存最终生成的Model类。现在我们只需要一句命令即可生成Model类了:</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>./mo.sh  \n</code></pre></div><p>运行后，一切都将自动执行，现在好多了，不是吗？</p> <h4 id=\"嵌套json\"><a href=\"#嵌套json\" class=\"header-anchor\">#</a> 嵌套JSON</h4> <p>我们定义一个person.json内容修改为：</p> <div class=\"language-json extra-class\"><pre class=\"language-json\"><code><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;name&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;John Smith&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;email&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;john@example.com&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;mother&quot;</span><span class=\"token operator\">:</span><span class=\"token punctuation\">{</span>\n    <span class=\"token property\">&quot;name&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;Alice&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;email&quot;</span><span class=\"token operator\">:</span><span class=\"token string\">&quot;alice@example.com&quot;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;friends&quot;</span><span class=\"token operator\">:</span><span class=\"token punctuation\">[</span>\n    <span class=\"token punctuation\">{</span>\n      <span class=\"token property\">&quot;name&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;Jack&quot;</span><span class=\"token punctuation\">,</span>\n      <span class=\"token property\">&quot;email&quot;</span><span class=\"token operator\">:</span><span class=\"token string\">&quot;Jack@example.com&quot;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">{</span>\n      <span class=\"token property\">&quot;name&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;Nancy&quot;</span><span class=\"token punctuation\">,</span>\n      <span class=\"token property\">&quot;email&quot;</span><span class=\"token operator\">:</span><span class=\"token string\">&quot;Nancy@example.com&quot;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>每个Person都有<code>name</code> 、<code>email</code> 、 <code>mother</code>和<code>friends</code>四个字段，由于<code>mother</code>也是一个Person，朋友是多个Person(数组)，所以我们期望生成的Model是下面这样：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:json_annotation/json_annotation.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">part</span> <span class=\"token string\">'person.g.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token metadata symbol\">@JsonSerializable</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">Person</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">Person</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    \n    String name<span class=\"token punctuation\">;</span>\n    String email<span class=\"token punctuation\">;</span>\n    Person mother<span class=\"token punctuation\">;</span>\n    List<span class=\"token operator\">&lt;</span>Person<span class=\"token operator\">&gt;</span> friends<span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">factory</span> Person<span class=\"token punctuation\">.</span><span class=\"token function\">fromJson</span><span class=\"token punctuation\">(</span>Map<span class=\"token operator\">&lt;</span>String<span class=\"token punctuation\">,</span><span class=\"token keyword\">dynamic</span><span class=\"token operator\">&gt;</span> json<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _$<span class=\"token function\">PersonFromJson</span><span class=\"token punctuation\">(</span>json<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    Map<span class=\"token operator\">&lt;</span>String<span class=\"token punctuation\">,</span> <span class=\"token keyword\">dynamic</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">toJson</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _$<span class=\"token function\">PersonToJson</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n</code></pre></div><p>这时，我们只需要简单修改一下JSON，添加一些特殊标志，重新运行mo.sh即可：</p> <div class=\"language-json extra-class\"><pre class=\"language-json\"><code><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;name&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;John Smith&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;email&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;john@example.com&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;mother&quot;</span><span class=\"token operator\">:</span><span class=\"token string\">&quot;$person&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;friends&quot;</span><span class=\"token operator\">:</span><span class=\"token string\">&quot;$[]person&quot;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们使用美元符“$”作为特殊标志符(如果与内容冲突，可以修改mo.dart中的<code>TAG</code>常量，自定义标志符)，脚本在遇到特殊标志符后会先把相应字段转为相应的对象或对象数组，对象数组需要在标志符后面添加数组符“[]”，符号后面接具体的类型名，此例中是person。其它类型同理，加入我们给User添加一个Person类型的 <code>boss</code>字段：</p> <div class=\"language-json extra-class\"><pre class=\"language-json\"><code><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;name&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;John Smith&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;email&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;john@example.com&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;boss&quot;</span><span class=\"token operator\">:</span><span class=\"token string\">&quot;$person&quot;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>重新运行mo.sh，生成的user.dart如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:json_annotation/json_annotation.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">&quot;person.dart&quot;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">part</span> <span class=\"token string\">'user.g.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token metadata symbol\">@JsonSerializable</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">User</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">User</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    String name<span class=\"token punctuation\">;</span>\n    String email<span class=\"token punctuation\">;</span>\n    Person boss<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token keyword\">factory</span> User<span class=\"token punctuation\">.</span><span class=\"token function\">fromJson</span><span class=\"token punctuation\">(</span>Map<span class=\"token operator\">&lt;</span>String<span class=\"token punctuation\">,</span><span class=\"token keyword\">dynamic</span><span class=\"token operator\">&gt;</span> json<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _$<span class=\"token function\">UserFromJson</span><span class=\"token punctuation\">(</span>json<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    Map<span class=\"token operator\">&lt;</span>String<span class=\"token punctuation\">,</span> <span class=\"token keyword\">dynamic</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">toJson</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _$<span class=\"token function\">UserToJson</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以看到，<code>boss</code>字段已自动添加，并自动导入了“person.dart”。</p> <h3 id=\"json-model-包\"><a href=\"#json-model-包\" class=\"header-anchor\">#</a> Json_model 包</h3> <p>如果每个项目都要构建一个上面这样的脚本显然很麻烦，为此，我们将上面脚本和生成模板封装了一个包,已经发布到了Pub上，包名为<a href=\"https://github.com/flutterchina/json_model\" target=\"_blank\" rel=\"noopener noreferrer\">Json_model<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>，开发者把该包加入开发依赖后，便可以用一条命令，根据Json文件生成Dart类。另外<a href=\"https://github.com/flutterchina/json_model\" target=\"_blank\" rel=\"noopener noreferrer\">Json_model<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 处于迭代中，功能会逐渐完善，所以建议读者直接使用该包（而不是手动复制上面的代码）。</p> <h3 id=\"使用ide插件生成model\"><a href=\"#使用ide插件生成model\" class=\"header-anchor\">#</a> 使用IDE插件生成model</h3> <p>目前Android Studio(或IntelliJ)有几个插件，可以将json文件转成Model类，但插件质量参差不齐，甚至还有一些沾染上了抄袭风波，故笔者在此不做优先推荐，读者有兴趣可以自行了解。但是，我们还是要了解一下IDE插件和<a href=\"https://link.juejin.im?target=https%3A%2F%2Fgithub.com%2Fflutterchina%2Fjson_model\" target=\"_blank\" rel=\"noopener noreferrer\">Json_model<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>的优劣：</p> <ol><li><a href=\"https://link.juejin.im?target=https%3A%2F%2Fgithub.com%2Fflutterchina%2Fjson_model\" target=\"_blank\" rel=\"noopener noreferrer\">Json_model<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>需要单独维护一个存放Json文件的文件夹，如果有改动，只需修改Json文件便可重新生成Model类；而IDE插件一般需要用户手动将Json内容拷贝复制到一个输入框中，这样生成之后Json文件没有存档的化，之后要改动就需要手动。</li> <li><a href=\"https://link.juejin.im?target=https%3A%2F%2Fgithub.com%2Fflutterchina%2Fjson_model\" target=\"_blank\" rel=\"noopener noreferrer\">Json_model<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>可以手动指定某个字段引用的其它Model类，可以避免生成重复的类；而IDE插件一般会为每一个Json文件中所有嵌套对象都单独生成一个Model类，即使这些嵌套对象可能在其它Model类中已经生成过。</li> <li><a href=\"https://link.juejin.im?target=https%3A%2F%2Fgithub.com%2Fflutterchina%2Fjson_model\" target=\"_blank\" rel=\"noopener noreferrer\">Json_model<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 提供了命令行转化方式，可以方便集成到CI等非UI环境的场景。</li></ol> <h3 id=\"faq\"><a href=\"#faq\" class=\"header-anchor\">#</a> FAQ</h3> <p>很多人可能会问Flutter中有没有像Java开发中的Gson/Jackson一样的Json序列化类库？答案是没有！因为这样的库需要使用运行时反射，这在Flutter中是禁用的。运行时反射会干扰Dart的_tree shaking_，使用_tree shaking_，可以在release版中“去除”未使用的代码，这可以显著优化应用程序的大小。由于反射会默认应用到所有代码，因此_tree shaking_会很难工作，因为在启用反射时很难知道哪些代码未被使用，因此冗余代码很难剥离，所以Flutter中禁用了Dart的反射功能，而正因如此也就无法实现动态转化Model的功能。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/111.95623244.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter11/socket.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>11.6 使用Socket API | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/112.e8e91632.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter11/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_11-6-使用socket-api\"><a href=\"#_11-6-使用socket-api\" class=\"header-anchor\">#</a> 11.6 使用Socket API</h1> <p>我们之前介绍的Http协议和WebSocket协议都属于应用层协议，除了它们，应用层协议还有很多如：SMTP、FTP等，这些应用层协议的实现都是通过Socket API来实现的。其实，操作系统中提供的原生网络请求API是标准的，在C语言的Socket库中，它主要提供了端到端建立链接和发送数据的基础API，而高级编程语言中的Socket库其实都是对操作系统的socket API的一个封装。所以，如果我们需要自定义协议或者想直接来控制管理网络链接、又或者我们觉得自带的HttpClient不好用想重新实现一个，这时我们就需要使用Socket。Flutter的Socket API在dart：io包中，下面我们看一个使用Socket实现简单http请求的示例，以请求百度首页为例：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">_request</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span><span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">//建立连接</span>\n  <span class=\"token keyword\">var</span> socket<span class=\"token operator\">=</span><span class=\"token keyword\">await</span> Socket<span class=\"token punctuation\">.</span><span class=\"token function\">connect</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;baidu.com&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token number\">80</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">//根据http协议，发送请求头</span>\n  socket<span class=\"token punctuation\">.</span><span class=\"token function\">writeln</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;GET / HTTP/1.1&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  socket<span class=\"token punctuation\">.</span><span class=\"token function\">writeln</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Host:baidu.com&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  socket<span class=\"token punctuation\">.</span><span class=\"token function\">writeln</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Connection:close&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  socket<span class=\"token punctuation\">.</span><span class=\"token function\">writeln</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">await</span> socket<span class=\"token punctuation\">.</span><span class=\"token function\">flush</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//发送</span>\n  <span class=\"token comment\">//读取返回内容</span>\n  _response <span class=\"token operator\">=</span><span class=\"token keyword\">await</span> socket<span class=\"token punctuation\">.</span><span class=\"token function\">transform</span><span class=\"token punctuation\">(</span>utf8<span class=\"token punctuation\">.</span>decoder<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">join</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">await</span> socket<span class=\"token punctuation\">.</span><span class=\"token function\">close</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以看到，使用Socket需要我们自己实现Http协议（需要自己实现和服务器的通信过程），本例只是一个简单示例，没有处理重定向、cookie等。本示例完整代码参考示例demo，运行后效果如图11-2所示：</p> <p><img src=\"/assets/img/11-2.f480cac6.png\" alt=\"图11-2\"></p> <p>可以看到响应内容分两个部分，第一部分是响应头，第二部分是响应体，服务端可以根据请求信息动态来输出响应体。由于本示例请求头比较简单，所以响应体和浏览器中访问的会有差别，读者可以补充一些请求头(如user-agent)来看看输出的变化。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/112.e8e91632.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter11/websocket.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>使用WebSockets | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/189.c927c79a.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter11/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"使用websockets\"><a href=\"#使用websockets\" class=\"header-anchor\">#</a> 使用WebSockets</h1> <p>Http协议是无状态的，只能由客户端主动发起，服务端再被动响应，服务端无法向客户端主动推送内容，并且一旦服务器响应结束，链接就会断开(见注解部分)，所以无法进行实时通信。WebSocket协议正是为解决客户端与服务端实时通信而产生的技术，现在已经被主流浏览器支持，所以对于Web开发者来说应该比较熟悉了，Flutter也提供了专门的包来支持WebSocket协议。</p> <blockquote><p>注意：Http协议中虽然可以通过keep-alive机制使服务器在响应结束后链接会保持一段时间，但最终还是会断开，keep-alive机制主要是用于避免在同一台服务器请求多个资源时频繁创建链接，它本质上是支持链接复用的技术，而并非用于实时通信，读者需要知道这两者的区别。</p></blockquote> <p>WebSocket协议本质上是一个基于tcp的协议，它是先通过HTTP协议发起一条特殊的http请求进行握手后，如果服务端支持WebSocket协议，则会进行协议升级。WebSocket会使用http协议握手后创建的tcp链接，和http协议不同的是，WebSocket的tcp链接是个长链接（不会断开），所以服务端与客户端就可以通过此TCP连接进行实时通信。有关WebSocket协议细节，读者可以看RFC文档，下面我们重点看看Flutter中如何使用WebSocket。</p> <p>在接下来例子中，我们将连接到由<a href=\"http://www.websocket.org/echo.html\" target=\"_blank\" rel=\"noopener noreferrer\">websocket.org提供的测试服务器<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>。服务器将简单地返回我们发送给它的相同消息！</p> <h3 id=\"步骤\"><a href=\"#步骤\" class=\"header-anchor\">#</a> 步骤</h3> <ol><li>连接到WebSocket服务器。</li> <li>监听来自服务器的消息。</li> <li>将数据发送到服务器。</li> <li>关闭WebSocket连接。</li></ol> <h3 id=\"_1-连接到websocket服务器\"><a href=\"#_1-连接到websocket服务器\" class=\"header-anchor\">#</a> 1. 连接到WebSocket服务器</h3> <p><a href=\"https://pub.dartlang.org/packages/web_socket_channel\" target=\"_blank\" rel=\"noopener noreferrer\">web_socket_channel<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> package 提供了我们需要连接到WebSocket服务器的工具。该package提供了一个<code>WebSocketChannel</code>允许我们既可以监听来自服务器的消息，又可以将消息发送到服务器的方法。</p> <p>在Flutter中，我们可以创建一个<code>WebSocketChannel</code>连接到一台服务器：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">final</span> channel <span class=\"token operator\">=</span> IOWebSocketChannel<span class=\"token punctuation\">.</span><span class=\"token function\">connect</span><span class=\"token punctuation\">(</span><span class=\"token string\">'ws://echo.websocket.org'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><h3 id=\"_2-监听来自服务器的消息\"><a href=\"#_2-监听来自服务器的消息\" class=\"header-anchor\">#</a> 2. 监听来自服务器的消息</h3> <p>现在我们建立了连接，我们可以监听来自服务器的消息，在我们发送消息给测试服务器之后，它会返回相同的消息。</p> <p>我们如何收取消息并显示它们？在这个例子中，我们将使用一个<a href=\"https://docs.flutter.io/flutter/widgets/StreamBuilder-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>StreamBuilder</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 来监听新消息， 并用一个Text来显示它们。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">new</span> <span class=\"token class-name\">StreamBuilder</span><span class=\"token punctuation\">(</span>\n  stream<span class=\"token punctuation\">:</span> widget<span class=\"token punctuation\">.</span>channel<span class=\"token punctuation\">.</span>stream<span class=\"token punctuation\">,</span>\n  builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> snapshot<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span>snapshot<span class=\"token punctuation\">.</span>hasData <span class=\"token operator\">?</span> <span class=\"token string\">'${snapshot.data}'</span> <span class=\"token punctuation\">:</span> <span class=\"token string\">''</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><h4 id=\"工作原理\"><a href=\"#工作原理\" class=\"header-anchor\">#</a> 工作原理</h4> <p><code>WebSocketChannel</code>提供了一个来自服务器的消息<code>Stream</code> 。该<code>Stream</code>类是<code>dart:async</code>包中的一个基础类。它提供了一种方法来监听来自数据源的异步事件。与<code>Future</code>返回单个异步响应不同，<code>Stream</code>类可以随着时间推移传递很多事件。该<a href=\"https://docs.flutter.io/flutter/widgets/StreamBuilder-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>StreamBuilder</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 组件将连接到一个<code>Stream</code>， 并在每次收到消息时通知Flutter重新构建界面。</p> <h3 id=\"_3-将数据发送到服务器\"><a href=\"#_3-将数据发送到服务器\" class=\"header-anchor\">#</a> 3. 将数据发送到服务器</h3> <p>为了将数据发送到服务器，我们会<code>add</code>消息给<code>WebSocketChannel</code>提供的sink。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>channel<span class=\"token punctuation\">.</span>sink<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Hello!'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><h4 id=\"工作原理-2\"><a href=\"#工作原理-2\" class=\"header-anchor\">#</a> 工作原理</h4> <p><code>WebSocketChannel</code>提供了一个<a href=\"https://docs.flutter.io/flutter/dart-async/StreamSink-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>StreamSink</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>，它将消息发给服务器。</p> <p><code>StreamSink</code>类提供了给数据源同步或异步添加事件的一般方法。</p> <h3 id=\"_4-关闭websocket连接\"><a href=\"#_4-关闭websocket连接\" class=\"header-anchor\">#</a> 4. 关闭WebSocket连接</h3> <p>在我们使用<code>WebSocket</code>后，要关闭连接：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>channel<span class=\"token punctuation\">.</span>sink<span class=\"token punctuation\">.</span><span class=\"token function\">close</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><h3 id=\"完整的例子\"><a href=\"#完整的例子\" class=\"header-anchor\">#</a> 完整的例子</h3> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:web_socket_channel/io.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">WebSocketRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _WebSocketRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_WebSocketRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_WebSocketRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>WebSocketRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  TextEditingController _controller <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">TextEditingController</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  IOWebSocketChannel channel<span class=\"token punctuation\">;</span>\n  String _text <span class=\"token operator\">=</span> <span class=\"token string\">&quot;&quot;</span><span class=\"token punctuation\">;</span>\n\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//创建websocket连接</span>\n    channel <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">IOWebSocketChannel<span class=\"token punctuation\">.</span>connect</span><span class=\"token punctuation\">(</span><span class=\"token string\">'ws://echo.websocket.org'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Scaffold</span><span class=\"token punctuation\">(</span>\n      appBar<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AppBar</span><span class=\"token punctuation\">(</span>\n        title<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;WebSocket(内容回显)&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      body<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Padding</span><span class=\"token punctuation\">(</span>\n        padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">20.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Column</span><span class=\"token punctuation\">(</span>\n          crossAxisAlignment<span class=\"token punctuation\">:</span> CrossAxisAlignment<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            <span class=\"token keyword\">new</span> <span class=\"token class-name\">Form</span><span class=\"token punctuation\">(</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">TextFormField</span><span class=\"token punctuation\">(</span>\n                controller<span class=\"token punctuation\">:</span> _controller<span class=\"token punctuation\">,</span>\n                decoration<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">InputDecoration</span><span class=\"token punctuation\">(</span>labelText<span class=\"token punctuation\">:</span> <span class=\"token string\">'Send a message'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token keyword\">new</span> <span class=\"token class-name\">StreamBuilder</span><span class=\"token punctuation\">(</span>\n              stream<span class=\"token punctuation\">:</span> channel<span class=\"token punctuation\">.</span>stream<span class=\"token punctuation\">,</span>\n              builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> snapshot<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token comment\">//网络不通会走到这</span>\n                <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>snapshot<span class=\"token punctuation\">.</span>hasError<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                  _text <span class=\"token operator\">=</span> <span class=\"token string\">&quot;网络不通...&quot;</span><span class=\"token punctuation\">;</span>\n                <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>snapshot<span class=\"token punctuation\">.</span>hasData<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                  _text <span class=\"token operator\">=</span> <span class=\"token string\">&quot;echo: &quot;</span><span class=\"token operator\">+</span>snapshot<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">;</span>\n                <span class=\"token punctuation\">}</span>\n                <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Padding</span><span class=\"token punctuation\">(</span>\n                  padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">symmetric</span><span class=\"token punctuation\">(</span>vertical<span class=\"token punctuation\">:</span> <span class=\"token number\">24.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span>_text<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      floatingActionButton<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">FloatingActionButton</span><span class=\"token punctuation\">(</span>\n        onPressed<span class=\"token punctuation\">:</span> _sendMessage<span class=\"token punctuation\">,</span>\n        tooltip<span class=\"token punctuation\">:</span> <span class=\"token string\">'Send message'</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>send<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_sendMessage</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>_controller<span class=\"token punctuation\">.</span>text<span class=\"token punctuation\">.</span>isNotEmpty<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      channel<span class=\"token punctuation\">.</span>sink<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span>_controller<span class=\"token punctuation\">.</span>text<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    channel<span class=\"token punctuation\">.</span>sink<span class=\"token punctuation\">.</span><span class=\"token function\">close</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面的例子比较简单，不再赘述。我们现在思考一个问题，假如我们想通过WebSocket传输二进制数据应该怎么做（比如要从服务器接收一张图片）？我们发现<code>StreamBuilder</code>和<code>Stream</code>都没有指定接收类型的参数，并且在创建WebSocket链接时也没有相应的配置，貌似没有什么办法……其实很简单，要接收二进制数据仍然使用<code>StreamBuilder</code>，因为WebSocket中所有发送的数据使用帧的形式发送，而帧是有固定格式，每一个帧的数据类型都可以通过Opcode字段指定，它可以指定当前帧是文本类型还是二进制类型（还有其它类型），所以客户端在收到帧时就已经知道了其数据类型，所以flutter完全可以在收到数据后解析出正确的类型，所以就无需开发者去关心，当服务器传输的数据是指定为二进制时，<code>StreamBuilder</code>的<code>snapshot.data</code>的类型就是<code>List&lt;int&gt;</code>，是文本时，则为<code>String</code>。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/189.c927c79a.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter12/android_implement.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>12.4 插件开发：Android端API实现 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/190.92378aeb.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter12/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_12-4-插件开发-android端api实现\"><a href=\"#_12-4-插件开发-android端api实现\" class=\"header-anchor\">#</a> 12.4 插件开发：Android端API实现</h1> <p>本节我们接着上一节&quot;获取电池电量&quot;插件的示例，来完成Android端API的实现。以下步骤是使用Java的示例，如果您更喜欢Kotlin，可以直接跳到后面Kotlin部分。</p> <p>首先在Android Studio中打开您的Flutter应用的Android部分：</p> <ol><li>启动 Android Studio</li> <li>选择 File &gt; Open…</li> <li>定位到您 Flutter app目录, 然后选择里面的 <code>android</code>文件夹，点击 OK</li> <li>在<code>java</code>目录下打开 <code>MainActivity.java</code></li></ol> <p>接下来，在<code>onCreate</code>里创建MethodChannel并设置一个<code>MethodCallHandler</code>。确保使用和Flutter客户端中使用的通道名称相同的名称。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> io<span class=\"token punctuation\">.</span>flutter<span class=\"token punctuation\">.</span>app<span class=\"token punctuation\">.</span>FlutterActivity<span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> io<span class=\"token punctuation\">.</span>flutter<span class=\"token punctuation\">.</span>plugin<span class=\"token punctuation\">.</span>common<span class=\"token punctuation\">.</span>MethodCall<span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> io<span class=\"token punctuation\">.</span>flutter<span class=\"token punctuation\">.</span>plugin<span class=\"token punctuation\">.</span>common<span class=\"token punctuation\">.</span>MethodChannel<span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> io<span class=\"token punctuation\">.</span>flutter<span class=\"token punctuation\">.</span>plugin<span class=\"token punctuation\">.</span>common<span class=\"token punctuation\">.</span>MethodChannel<span class=\"token punctuation\">.</span>MethodCallHandler<span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> io<span class=\"token punctuation\">.</span>flutter<span class=\"token punctuation\">.</span>plugin<span class=\"token punctuation\">.</span>common<span class=\"token punctuation\">.</span>MethodChannel<span class=\"token punctuation\">.</span>Result<span class=\"token punctuation\">;</span>\n\npublic <span class=\"token keyword\">class</span> <span class=\"token class-name\">MainActivity</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">FlutterActivity</span> <span class=\"token punctuation\">{</span>\n    private <span class=\"token keyword\">static</span> <span class=\"token keyword\">final</span> String CHANNEL <span class=\"token operator\">=</span> <span class=\"token string\">&quot;samples.flutter.io/battery&quot;</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token metadata symbol\">@Override</span>\n    public <span class=\"token keyword\">void</span> <span class=\"token function\">onCreate</span><span class=\"token punctuation\">(</span>Bundle savedInstanceState<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">onCreate</span><span class=\"token punctuation\">(</span>savedInstanceState<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">new</span> <span class=\"token class-name\">MethodChannel</span><span class=\"token punctuation\">(</span><span class=\"token function\">getFlutterView</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> CHANNEL<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">setMethodCallHandler</span><span class=\"token punctuation\">(</span>\n          <span class=\"token keyword\">new</span> <span class=\"token class-name\">MethodCallHandler</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n             <span class=\"token metadata symbol\">@Override</span>\n             public <span class=\"token keyword\">void</span> <span class=\"token function\">onMethodCall</span><span class=\"token punctuation\">(</span>MethodCall call<span class=\"token punctuation\">,</span> Result result<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                 <span class=\"token comment\">// TODO</span>\n             <span class=\"token punctuation\">}</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>接下来，我们添加Java代码，使用Android电池API来获取电池电量。此代码和在原生Android应用中编写的代码完全相同。</p> <p>首先，添加需要导入的依赖。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> android<span class=\"token punctuation\">.</span>content<span class=\"token punctuation\">.</span>ContextWrapper<span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> android<span class=\"token punctuation\">.</span>content<span class=\"token punctuation\">.</span>Intent<span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> android<span class=\"token punctuation\">.</span>content<span class=\"token punctuation\">.</span>IntentFilter<span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> android<span class=\"token punctuation\">.</span>os<span class=\"token punctuation\">.</span>BatteryManager<span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> android<span class=\"token punctuation\">.</span>os<span class=\"token punctuation\">.</span>Build<span class=\"token punctuation\">.</span>VERSION<span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> android<span class=\"token punctuation\">.</span>os<span class=\"token punctuation\">.</span>Build<span class=\"token punctuation\">.</span>VERSION_CODES<span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> android<span class=\"token punctuation\">.</span>os<span class=\"token punctuation\">.</span>Bundle<span class=\"token punctuation\">;</span>\n</code></pre></div><p>然后，将下面的新方法添加到activity类中的，位于onCreate 方法下方：</p> <div class=\"language-java extra-class\"><pre class=\"language-java\"><code><span class=\"token keyword\">private</span> <span class=\"token keyword\">int</span> <span class=\"token function\">getBatteryLevel</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">int</span> batteryLevel <span class=\"token operator\">=</span> <span class=\"token operator\">-</span><span class=\"token number\">1</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>VERSION<span class=\"token punctuation\">.</span>SDK_INT <span class=\"token operator\">&gt;=</span> VERSION_CODES<span class=\"token punctuation\">.</span>LOLLIPOP<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token class-name\">BatteryManager</span> batteryManager <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token class-name\">BatteryManager</span><span class=\"token punctuation\">)</span> <span class=\"token function\">getSystemService</span><span class=\"token punctuation\">(</span>BATTERY_SERVICE<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    batteryLevel <span class=\"token operator\">=</span> batteryManager<span class=\"token punctuation\">.</span><span class=\"token function\">getIntProperty</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">BatteryManager</span><span class=\"token punctuation\">.</span>BATTERY_PROPERTY_CAPACITY<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token class-name\">Intent</span> intent <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">ContextWrapper</span><span class=\"token punctuation\">(</span><span class=\"token function\">getApplicationContext</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>\n        <span class=\"token function\">registerReceiver</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">IntentFilter</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">Intent</span><span class=\"token punctuation\">.</span>ACTION_BATTERY_CHANGED<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    batteryLevel <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>intent<span class=\"token punctuation\">.</span><span class=\"token function\">getIntExtra</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">BatteryManager</span><span class=\"token punctuation\">.</span>EXTRA_LEVEL<span class=\"token punctuation\">,</span> <span class=\"token operator\">-</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">*</span> <span class=\"token number\">100</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">/</span>\n        intent<span class=\"token punctuation\">.</span><span class=\"token function\">getIntExtra</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">BatteryManager</span><span class=\"token punctuation\">.</span>EXTRA_SCALE<span class=\"token punctuation\">,</span> <span class=\"token operator\">-</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">return</span> batteryLevel<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>最后，我们完成之前添加的<code>onMethodCall</code>方法。我们需要处理平台方法名为<code>getBatteryLevel</code>的调用消息，所以我们需要先在call参数判断调用的方法是否为<code>getBatteryLevel</code>。 这个平台方法的实现只需调用我们在前一步中编写的Android代码，并通过result参数返回成功或错误情况的响应信息。如果调用了未定义的API，我们也会通知返回：</p> <div class=\"language-java extra-class\"><pre class=\"language-java\"><code><span class=\"token annotation punctuation\">@Override</span>\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">void</span> <span class=\"token function\">onMethodCall</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">MethodCall</span> call<span class=\"token punctuation\">,</span> <span class=\"token class-name\">Result</span> result<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>call<span class=\"token punctuation\">.</span>method<span class=\"token punctuation\">.</span><span class=\"token function\">equals</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;getBatteryLevel&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">int</span> batteryLevel <span class=\"token operator\">=</span> <span class=\"token function\">getBatteryLevel</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>batteryLevel <span class=\"token operator\">!=</span> <span class=\"token operator\">-</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            result<span class=\"token punctuation\">.</span><span class=\"token function\">success</span><span class=\"token punctuation\">(</span>batteryLevel<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n            result<span class=\"token punctuation\">.</span><span class=\"token function\">error</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;UNAVAILABLE&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;Battery level not available.&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n        result<span class=\"token punctuation\">.</span><span class=\"token function\">notImplemented</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>  \n</code></pre></div><p>现在就可以在Android上运行该应用程序了，如果使用的是Android模拟器，则可以通过工具栏中的&quot;...&quot;按钮访问Extended Controls面板中的电池电量。</p> <h3 id=\"使用kotlin添加android平台特定的实现\"><a href=\"#使用kotlin添加android平台特定的实现\" class=\"header-anchor\">#</a> 使用Kotlin添加Android平台特定的实现</h3> <p>使用Kotlin和使用Java的步骤类似，首先在Android Studio中打开您的Flutter应用的Android部分：</p> <ol><li>启动 Android Studio。</li> <li>选择 the menu item &quot;File &gt; Open…&quot;。</li> <li>定位到 Flutter app目录, 然后选择里面的 <code>android</code>文件夹，点击 OK。</li> <li>在<code>kotlin</code>目录中打开<code>MainActivity.kt</code>。</li></ol> <p>接下来，在<code>onCreate</code>里创建MethodChannel并设置一个<code>MethodCallHandler</code>。确保使用与在Flutter客户端使用的通道名称相同。</p> <div class=\"language-kotlin extra-class\"><pre class=\"language-kotlin\"><code><span class=\"token keyword\">import</span> android<span class=\"token punctuation\">.</span>os<span class=\"token punctuation\">.</span>Bundle\n<span class=\"token keyword\">import</span> io<span class=\"token punctuation\">.</span>flutter<span class=\"token punctuation\">.</span>app<span class=\"token punctuation\">.</span>FlutterActivity\n<span class=\"token keyword\">import</span> io<span class=\"token punctuation\">.</span>flutter<span class=\"token punctuation\">.</span>plugin<span class=\"token punctuation\">.</span>common<span class=\"token punctuation\">.</span>MethodChannel\n<span class=\"token keyword\">import</span> io<span class=\"token punctuation\">.</span>flutter<span class=\"token punctuation\">.</span>plugins<span class=\"token punctuation\">.</span>GeneratedPluginRegistrant\n\n<span class=\"token keyword\">class</span> <span class=\"token function\">MainActivity</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">:</span> <span class=\"token function\">FlutterActivity</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">private</span> <span class=\"token keyword\">val</span> CHANNEL <span class=\"token operator\">=</span> <span class=\"token string\">&quot;samples.flutter.io/battery&quot;</span>\n\n  <span class=\"token keyword\">override</span> <span class=\"token keyword\">fun</span> <span class=\"token function\">onCreate</span><span class=\"token punctuation\">(</span>savedInstanceState<span class=\"token operator\">:</span> Bundle<span class=\"token operator\">?</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">onCreate</span><span class=\"token punctuation\">(</span>savedInstanceState<span class=\"token punctuation\">)</span>\n    GeneratedPluginRegistrant<span class=\"token punctuation\">.</span><span class=\"token function\">registerWith</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span>\n\n    <span class=\"token function\">MethodChannel</span><span class=\"token punctuation\">(</span>flutterView<span class=\"token punctuation\">,</span> CHANNEL<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">setMethodCallHandler</span> <span class=\"token punctuation\">{</span> call<span class=\"token punctuation\">,</span> result <span class=\"token operator\">-&gt;</span>\n      <span class=\"token comment\">// TODO</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>接下来，我们添加Kotlin代码，使用Android电池API来获取电池电量，这和原生开发是一样的。</p> <p>首先，添加需要导入的依赖。</p> <div class=\"language-kotlin extra-class\"><pre class=\"language-kotlin\"><code><span class=\"token keyword\">import</span> android<span class=\"token punctuation\">.</span>content<span class=\"token punctuation\">.</span>Context\n<span class=\"token keyword\">import</span> android<span class=\"token punctuation\">.</span>content<span class=\"token punctuation\">.</span>ContextWrapper\n<span class=\"token keyword\">import</span> android<span class=\"token punctuation\">.</span>content<span class=\"token punctuation\">.</span>Intent\n<span class=\"token keyword\">import</span> android<span class=\"token punctuation\">.</span>content<span class=\"token punctuation\">.</span>IntentFilter\n<span class=\"token keyword\">import</span> android<span class=\"token punctuation\">.</span>os<span class=\"token punctuation\">.</span>BatteryManager\n<span class=\"token keyword\">import</span> android<span class=\"token punctuation\">.</span>os<span class=\"token punctuation\">.</span>Build<span class=\"token punctuation\">.</span>VERSION\n<span class=\"token keyword\">import</span> android<span class=\"token punctuation\">.</span>os<span class=\"token punctuation\">.</span>Build<span class=\"token punctuation\">.</span>VERSION_CODES\n</code></pre></div><p>然后，将下面的新方法添加到activity类中的，位于onCreate 方法下方：</p> <div class=\"language-kotlin extra-class\"><pre class=\"language-kotlin\"><code>  <span class=\"token keyword\">private</span> <span class=\"token keyword\">fun</span> <span class=\"token function\">getBatteryLevel</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> Int <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">val</span> batteryLevel<span class=\"token operator\">:</span> Int\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>VERSION<span class=\"token punctuation\">.</span>SDK_INT <span class=\"token operator\">&gt;=</span> VERSION_CODES<span class=\"token punctuation\">.</span>LOLLIPOP<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">val</span> batteryManager <span class=\"token operator\">=</span> <span class=\"token function\">getSystemService</span><span class=\"token punctuation\">(</span>Context<span class=\"token punctuation\">.</span>BATTERY_SERVICE<span class=\"token punctuation\">)</span> <span class=\"token keyword\">as</span> BatteryManager\n      batteryLevel <span class=\"token operator\">=</span> batteryManager<span class=\"token punctuation\">.</span><span class=\"token function\">getIntProperty</span><span class=\"token punctuation\">(</span>BatteryManager<span class=\"token punctuation\">.</span>BATTERY_PROPERTY_CAPACITY<span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">val</span> intent <span class=\"token operator\">=</span> <span class=\"token function\">ContextWrapper</span><span class=\"token punctuation\">(</span>applicationContext<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">registerReceiver</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token function\">IntentFilter</span><span class=\"token punctuation\">(</span>Intent<span class=\"token punctuation\">.</span>ACTION_BATTERY_CHANGED<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n      batteryLevel <span class=\"token operator\">=</span> intent<span class=\"token operator\">!!</span><span class=\"token punctuation\">.</span><span class=\"token function\">getIntExtra</span><span class=\"token punctuation\">(</span>BatteryManager<span class=\"token punctuation\">.</span>EXTRA_LEVEL<span class=\"token punctuation\">,</span> <span class=\"token operator\">-</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">*</span> <span class=\"token number\">100</span> <span class=\"token operator\">/</span> intent<span class=\"token punctuation\">.</span><span class=\"token function\">getIntExtra</span><span class=\"token punctuation\">(</span>BatteryManager<span class=\"token punctuation\">.</span>EXTRA_SCALE<span class=\"token punctuation\">,</span> <span class=\"token operator\">-</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">return</span> batteryLevel\n  <span class=\"token punctuation\">}</span>\n</code></pre></div><p>最后，我们完成之前添加的<code>onMethodCall</code>方法。我们需要处理平台方法名为<code>getBatteryLevel</code>的调用消息，所以我们需要先在call参数判断调用的方法是否为<code>getBatteryLevel</code>。 这个平台方法的实现只需调用我们在前一步中编写的Android代码，并通过result参数返回成功或错误情况的响应信息。如果调用了未定义的API，我们也会通知返回：\n​</p> <div class=\"language-kotlin extra-class\"><pre class=\"language-kotlin\"><code><span class=\"token function\">MethodChannel</span><span class=\"token punctuation\">(</span>flutterView<span class=\"token punctuation\">,</span> CHANNEL<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">setMethodCallHandler</span> <span class=\"token punctuation\">{</span> call<span class=\"token punctuation\">,</span> result <span class=\"token operator\">-&gt;</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>call<span class=\"token punctuation\">.</span>method <span class=\"token operator\">==</span> <span class=\"token string\">&quot;getBatteryLevel&quot;</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n     <span class=\"token keyword\">val</span> batteryLevel <span class=\"token operator\">=</span> <span class=\"token function\">getBatteryLevel</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n     <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>batteryLevel <span class=\"token operator\">!=</span> <span class=\"token operator\">-</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n       result<span class=\"token punctuation\">.</span><span class=\"token function\">success</span><span class=\"token punctuation\">(</span>batteryLevel<span class=\"token punctuation\">)</span>\n     <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n       result<span class=\"token punctuation\">.</span><span class=\"token function\">error</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;UNAVAILABLE&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;Battery level not available.&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span>\n     <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n      result<span class=\"token punctuation\">.</span><span class=\"token function\">notImplemented</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>您现在就可以在Android上运行该应用程序。如果您使用的是Android模拟器，则可以通过工具栏中的&quot;...&quot;按钮访问Extended Controls面板中的电池电量。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/190.92378aeb.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter12/develop_package.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>12.1 开发Package | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/71.f6151eb7.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter12/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_12-1-开发package\"><a href=\"#_12-1-开发package\" class=\"header-anchor\">#</a> 12.1 开发Package</h1> <p>第二章中已经讲过如何使用Package（包），我们知道通过package可以创建共享的模块化代码，本节将重点讲一下如何开发并发布我们自己的Package。一个最小的Package包括：</p> <ul><li>一个<code>pubspec.yaml</code>文件：声明了Package的名称、版本、作者等的元数据文件。</li> <li>一个 <code>lib</code> 文件夹：包括包中公开的(public)代码，最少应有一个<code>&lt;package-name&gt;.dart</code>文件</li></ul> <p>Flutter Packages分为两类：</p> <ul><li>Dart包：其中一些可能包含Flutter的特定功能，因此对Flutter框架具有依赖性，这种包仅用于Flutter，例如<a href=\"https://pub.dartlang.org/packages/fluro\" target=\"_blank\" rel=\"noopener noreferrer\"><code>fluro</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>包。</li> <li>插件包：一种专用的Dart包，其中包含用Dart代码编写的API，以及针对Android（使用Java或Kotlin）和针对iOS（使用OC或Swift）平台的特定实现，也就是说插件包括原生代码，一个具体的例子是<a href=\"https://pub.dartlang.org/packages/battery\" target=\"_blank\" rel=\"noopener noreferrer\"><code>battery</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>插件包。</li></ul> <p>注意，虽然Flutter的Dart运行时和Dart VM运行时不是完全相同，但是如果Package中没有涉及这些存在差异的部分，那么这样的包可以同时支持Flutter和Dart VM，如Dart http网络库<a href=\"https://github.com/flutterchina/dio\" target=\"_blank\" rel=\"noopener noreferrer\">dio<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>。</p> <p>下面我将带领读者一步步来开发一个Dart Package。</p> <h3 id=\"第一步-创建dart包\"><a href=\"#第一步-创建dart包\" class=\"header-anchor\">#</a> 第一步：创建Dart包</h3> <p>您可以通过Android Studio：File&gt;New&gt;New Flutter Project 来创建一个Package工程，如图12-1所示：</p> <p><img src=\"/assets/img/12-1.456f4488.png\" alt=\"图12-1\"></p> <p>您也可以通过使用<code>--template=package</code> 来执行 <code>flutter create</code> 命令来创建：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>flutter create --template<span class=\"token operator\">=</span>package hello\n</code></pre></div><p>这将在<code>hello/</code>文件夹下创建一个具有以下专用内容的package工程：</p> <ul><li><p><code>lib/hello.dart</code>：Package的Dart代码</p></li> <li><p><code>test/hello_test.dart</code>：Package的单元测试代码。</p></li></ul> <h3 id=\"实现package\"><a href=\"#实现package\" class=\"header-anchor\">#</a> 实现package</h3> <p>对于纯Dart包，只需在主<code>lib/&lt;package name&gt;.dart</code>文件内或<code>lib</code>目录中的文件中添加功能即可 。要测试软件包，请在<code>test</code>目录中添加<a href=\"https://flutter.io/testing/#unit-testing\" target=\"_blank\" rel=\"noopener noreferrer\">unit tests<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>。下面我们看看如何组织Package包的代码，我们以shelf Package为例，它的目录结构如图12-2所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图12-2\"></p> <p>在lib根目录下的“shelf.dart”中，导出了多个“lib/src”目录下的dart文件：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">export</span> <span class=\"token string\">'src/cascade.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">export</span> <span class=\"token string\">'src/handler.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">export</span> <span class=\"token string\">'src/handlers/logger.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">export</span> <span class=\"token string\">'src/hijack_exception.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">export</span> <span class=\"token string\">'src/middleware.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">export</span> <span class=\"token string\">'src/pipeline.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">export</span> <span class=\"token string\">'src/request.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">export</span> <span class=\"token string\">'src/response.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">export</span> <span class=\"token string\">'src/server.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">export</span> <span class=\"token string\">'src/server_handler.dart'</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>而Package中主要的功能的源码都在src目录下。shelf Package也导出了一个迷你库: shelf_io，它主要是处理HttpRequest的。</p> <h3 id=\"导入包\"><a href=\"#导入包\" class=\"header-anchor\">#</a> <strong>导入包</strong></h3> <p>当需要使用这个Package时，我们可以通过&quot;package:&quot;指令来指定包的入口文件：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:utilities/utilities.dart'</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>同一个包中的源码文件之间也可以使用相对路径来导入。</p> <h3 id=\"生成文档\"><a href=\"#生成文档\" class=\"header-anchor\">#</a> 生成文档</h3> <p>可以使用 <a href=\"https://github.com/dart-lang/dartdoc#dartdoc\" target=\"_blank\" rel=\"noopener noreferrer\">dartdoc<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 工具来为Package生成文档，开发者需要做的就是遵守文档注释语法在代码中添加文档注释，最后使用dartdoc可以直接生成API文档（一个静态网站）。文档注释是使用三斜线&quot;///&quot;开始，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">/// The event handler responsible for updating the badge in the UI.</span>\n<span class=\"token keyword\">void</span> <span class=\"token function\">updateBadge</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>详细的文档语法请参考<a href=\"https://github.com/dart-lang/dartdoc#dartdoc\" target=\"_blank\" rel=\"noopener noreferrer\">dartdoc<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 。</p> <h3 id=\"处理包的相互依赖\"><a href=\"#处理包的相互依赖\" class=\"header-anchor\">#</a> 处理包的相互依赖</h3> <p>如果我们正在开发一个<code>hello</code>包，它依赖于另一个包，则需要将该依赖包添加到<code>pubspec.yaml</code>文件的<code>dependencies</code>部分。 下面的代码使<code>url_launcher</code>插件的API在<code>hello</code>包中是可用的：</p> <p>在 <code>hello/pubspec.yaml</code>中:</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code><span class=\"token key atrule\">dependencies</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">url_launcher</span><span class=\"token punctuation\">:</span> ^0.4.2\n</code></pre></div><p>现在可以在<code>hello</code>中<code>import 'package:url_launcher/url_launcher.dart'</code> 然后调用 <code>launch()</code>方法了。</p> <p>这与在Flutter应用程序或任何其他Dart项目中引用软件包没有什么不同。</p> <p>但是，如果<code>hello</code>碰巧是一个插件包，其平台特定的代码需要访问<code>url_launcher</code>公开的特定于平台的API，那么我们还需要为特定于平台的构建文件添加合适的依赖声明，如下所示。</p> <p><strong>Android</strong></p> <p>在 <code>hello/android/build.gradle</code>:</p> <div class=\"language-groovy extra-class\"><pre class=\"language-groovy\"><code>android <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// lines skipped</span>\n    dependencies <span class=\"token punctuation\">{</span>\n        provided rootProject<span class=\"token punctuation\">.</span><span class=\"token function\">findProject</span><span class=\"token punctuation\">(</span><span class=\"token string gstring\">&quot;:url_launcher&quot;</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>您现在可以在<code>hello/android/src</code>源码中<code>import io.flutter.plugins.urllauncher.UrlLauncherPlugin</code>访问<code>UrlLauncherPlugin</code>类。</p> <p><strong>iOS</strong></p> <p>在<code>hello/ios/hello.podspec</code>:</p> <div class=\"language-ruby extra-class\"><pre class=\"language-ruby\"><code><span class=\"token constant\">Pod</span><span class=\"token punctuation\">:</span><span class=\"token punctuation\">:</span><span class=\"token constant\">Spec</span><span class=\"token punctuation\">.</span><span class=\"token keyword\">new</span> <span class=\"token keyword\">do</span> <span class=\"token operator\">|</span>s<span class=\"token operator\">|</span>\n  <span class=\"token comment\"># lines skipped</span>\n  s<span class=\"token punctuation\">.</span>dependency <span class=\"token string\">'url_launcher'</span>\n</code></pre></div><p>您现在可以在<code>hello/ios/Classes</code>源码中 <code>#import &quot;UrlLauncherPlugin.h&quot;</code> 然后访问 <code>UrlLauncherPlugin</code>类。</p> <h3 id=\"解决依赖冲突\"><a href=\"#解决依赖冲突\" class=\"header-anchor\">#</a> 解决依赖冲突</h3> <p>假设我们想在我们的<code>hello</code>包中使用<code>some_package</code>和<code>other_package</code>，并且这两个包都依赖<code>url_launcher</code>，但是依赖的是<code>url_launcher</code>的不同的版本。 那我们就有潜在的冲突。避免这种情况的最好方法是在指定依赖关系时，程序包作者使用<a href=\"https://www.dartlang.org/tools/pub/dependencies#version-constraints\" target=\"_blank\" rel=\"noopener noreferrer\">版本范围<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>而不是特定版本。</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code><span class=\"token key atrule\">dependencies</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">url_launcher</span><span class=\"token punctuation\">:</span> ^0.4.2    <span class=\"token comment\"># 这样会较好, 任何0.4.x(x &gt;= 2)都可.</span>\n  <span class=\"token key atrule\">image_picker</span><span class=\"token punctuation\">:</span> <span class=\"token string\">'0.1.1'</span>   <span class=\"token comment\"># 不是很好，只有0.1.1版本.</span>\n</code></pre></div><p>如果<code>some_package</code>声明了上面的依赖关系,<code>other_package</code>声明了<code>url_launcher</code>版本像’0.4.5’或’^0.4.0’，pub将能够自动解决问题。</p> <p>即使<code>some_package</code>和<code>other_package</code>声明了不兼容的<code>url_launcher</code>版本，它仍然可能会和<code>url_launcher</code>以兼容的方式正常工作。 你可以通过向<code>hello</code>包的<code>pubspec.yaml</code>文件中添加依赖性覆盖声明来处理冲突，从而强制使用特定版本：</p> <p>强制使用 <code>0.4.3</code>版本的<code>url_launcher</code>，在 <code>hello/pubspec.yaml</code>中:</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code><span class=\"token key atrule\">dependencies</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">some_package</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">other_package</span><span class=\"token punctuation\">:</span>\n<span class=\"token key atrule\">dependency_overrides</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">url_launcher</span><span class=\"token punctuation\">:</span> <span class=\"token string\">'0.4.3'</span>\n</code></pre></div><p>如果冲突的依赖不是一个包，而是一个特定于Android的库，比如<code>guava</code>，那么必须将依赖重写声明添加到Gradle构建逻辑中。</p> <p>强制使用<code>23.0</code>版本的<code>guava</code>库，在<code>hello/android/build.gradle</code>中：</p> <div class=\"language-groovy extra-class\"><pre class=\"language-groovy\"><code>configurations<span class=\"token punctuation\">.</span>all <span class=\"token punctuation\">{</span>\n    resolutionStrategy <span class=\"token punctuation\">{</span>\n        force <span class=\"token string\">'com.google.guava:guava:23.0-android'</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>Cocoapods目前不提供依赖覆盖功能。</p> <h3 id=\"发布package\"><a href=\"#发布package\" class=\"header-anchor\">#</a> 发布Package</h3> <p>一旦实现了一个包，我们可以在<a href=\"https://pub.dartlang.org/\" target=\"_blank\" rel=\"noopener noreferrer\">Pub<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>上发布它 ，这样其他开发者就可以轻松使用它。</p> <p>在发布之前，检查<code>pubspec.yaml</code>、<code>README.md</code>以及<code>CHANGELOG.md</code>文件，以确保其内容的完整性和正确性。然后，运行 dry-run 命令以查看是否都准备OK了:</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>flutter packages pub publish --dry-run\n</code></pre></div><p>验证无误后，我们就可以运行发布命令了：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>flutter packages pub publish\n</code></pre></div><blockquote><p>如果你遇到包发布失败的情况，先检查是否因为众所周知的网络原因，如果是网络问题，可以使用VPN，这里需要注意的是一些代理只会代理部分APP的网络请求，如浏览器的，它们可能并不能代理dart的网络请求，所以在这种情况下，即使开了代理也依然无法连接到Pub，因此，在发布Pub包时使用全局代理或全局VPN会保险些。如果网络没有问题，以管理员权限(sudo)运行发布命令重试。<br>\n很多时候开启全局代理也不会让terminal中的流量打代理服务器走，以socks5为例，应该在终端下输入以下指令：</p></blockquote> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code><span class=\"token builtin class-name\">export</span> <span class=\"token assign-left variable\">all_proxy</span><span class=\"token operator\">=</span>socks5://127.0.0.1:1080\n</code></pre></div><blockquote><p>此时终端中的http和https流量会打代理服务器走，可以通过<code>curl -i https://ip.cn</code>指令查看代理设置是否成功。</p></blockquote></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/71.f6151eb7.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter12/develop_plugin.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>12.3 开发Flutter插件 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/191.167ff3f3.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter12/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_12-3-开发flutter插件\"><a href=\"#_12-3-开发flutter插件\" class=\"header-anchor\">#</a> 12.3 开发Flutter插件</h1> <p>下面我们通过一个获取电池电量的插件来介绍一下Flutter插件的开发流程。该插件中我们在Dart中通过<code>getBatteryLevel</code> 调用Android <code>BatteryManager</code> API和iOS <code>device.batteryLevel</code> API。</p> <h3 id=\"创建一个新的应用程序项目\"><a href=\"#创建一个新的应用程序项目\" class=\"header-anchor\">#</a> 创建一个新的应用程序项目</h3> <p>首先创建一个新的应用程序:</p> <ul><li>在终端中运行：<code>flutter create batterylevel</code></li></ul> <p>默认情况下，模板支持使用Java编写Android代码，或使用Objective-C编写iOS代码。要使用Kotlin或Swift，请使用-i和/或-a标志:</p> <ul><li>在终端中运行: <code>flutter create -i swift -a kotlin batterylevel</code></li></ul> <h3 id=\"创建flutter平台客户端\"><a href=\"#创建flutter平台客户端\" class=\"header-anchor\">#</a> 创建Flutter平台客户端</h3> <p>该应用的<code>State</code>类拥有当前的应用状态。我们需要延长这一点以保持当前的电量</p> <p>首先，我们构建通道。我们使用<code>MethodChannel</code>调用一个方法来返回电池电量。</p> <p>通道的客户端和宿主通过通道构造函数中传递的通道名称进行连接。单个应用中使用的所有通道名称必须是唯一的; 我们建议在通道名称前加一个唯一的“域名前缀”，例如<code>samples.flutter.io/battery</code>。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'dart:async'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/services.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_MyHomePageState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>MyHomePage<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">static</span> <span class=\"token keyword\">const</span> platform <span class=\"token operator\">=</span> <span class=\"token keyword\">const</span> <span class=\"token function\">MethodChannel</span><span class=\"token punctuation\">(</span><span class=\"token string\">'samples.flutter.io/battery'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// Get battery level.</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>接下来，我们调用通道上的方法，指定通过字符串标识符调用方法<code>getBatteryLevel</code>。 该调用可能失败(平台不支持平台API，例如在模拟器中运行时)，所以我们将invokeMethod调用包装在try-catch语句中。</p> <p>我们使用返回的结果，在<code>setState</code>中来更新用户界面状态<code>batteryLevel</code>。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>  <span class=\"token comment\">// Get battery level.</span>\n  String _batteryLevel <span class=\"token operator\">=</span> <span class=\"token string\">'Unknown battery level.'</span><span class=\"token punctuation\">;</span>\n\n  Future<span class=\"token operator\">&lt;</span>Null<span class=\"token operator\">&gt;</span> <span class=\"token function\">_getBatteryLevel</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    String batteryLevel<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">final</span> int result <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> platform<span class=\"token punctuation\">.</span><span class=\"token function\">invokeMethod</span><span class=\"token punctuation\">(</span><span class=\"token string\">'getBatteryLevel'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      batteryLevel <span class=\"token operator\">=</span> <span class=\"token string\">'Battery level at $result % .'</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">on</span> PlatformException <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      batteryLevel <span class=\"token operator\">=</span> <span class=\"token string\">&quot;Failed to get battery level: '${e.message}'.&quot;</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _batteryLevel <span class=\"token operator\">=</span> batteryLevel<span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n</code></pre></div><p>最后，我们在build创建包含一个小字体显示电池状态和一个用于刷新值的按钮的用户界面。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token metadata symbol\">@override</span>\nWidget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Material</span><span class=\"token punctuation\">(</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Center</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Column</span><span class=\"token punctuation\">(</span>\n        mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>spaceEvenly<span class=\"token punctuation\">,</span>\n        children<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>\n          <span class=\"token keyword\">new</span> <span class=\"token class-name\">RaisedButton</span><span class=\"token punctuation\">(</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Get Battery Level'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            onPressed<span class=\"token punctuation\">:</span> _getBatteryLevel<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span>_batteryLevel<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>至此Flutter部分的测试代码写好了，接下来我们需要实现Android和iOS平台下的API，由于平台API实现部分篇幅较大，我们将在接下来的两节中，分别介绍Android和iOS端API的实现。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/191.167ff3f3.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter12/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>包与插件 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/192.53c8ab5d.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter12/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"包与插件\"><a href=\"#包与插件\" class=\"header-anchor\">#</a> 包与插件</h1> <ul><li><a href=\"/v2/chapter12/develop_package.html\">12.1：开发package</a></li> <li><a href=\"/v2/chapter12/platform-channel.html\">12.2：平台通道简介</a></li> <li><a href=\"/v2/chapter12/develop_plugin.html\">12.3：开发Flutter插件</a></li> <li><a href=\"/v2/chapter12/android_implement.html\">12.4：插件开发：实现Android端API</a></li> <li><a href=\"/v2/chapter12/ios_implement.html\">12.5：插件开发：实现IOS端API</a></li> <li><a href=\"/v2/chapter12/texture_platformview.html\">12.6：Texture和PlatformView</a></li></ul></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/192.53c8ab5d.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter12/ios_implement.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>12.5 插件开发：iOS端API实现 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/193.05fa90e3.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter12/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_12-5-插件开发-ios端api实现\"><a href=\"#_12-5-插件开发-ios端api实现\" class=\"header-anchor\">#</a> 12.5 插件开发：iOS端API实现</h1> <p>本节我们接着之前&quot;获取电池电量&quot;插件的示例，来完成iOS端API的实现。以下步骤使用Objective-C，如果您更喜欢Swift，可以直接跳到后面Swift部分。</p> <p>首先打开Xcode中Flutter应用程序的iOS部分:</p> <ol><li>启动 Xcode</li> <li>选择 File &gt; Open…</li> <li>定位到您 Flutter app目录, 然后选择里面的 <code>iOS</code>文件夹，点击 OK</li> <li>确保Xcode项目的构建没有错误。</li> <li>选择 Runner &gt; Runner ，打开<code>AppDelegate.m</code></li></ol> <p>接下来，在<code>application didFinishLaunchingWithOptions:</code>方法内部创建一个<code>FlutterMethodChannel</code>，并添加一个处理方法。 确保与在Flutter客户端使用的通道名称相同。</p> <div class=\"language-objectivec extra-class\"><pre class=\"language-objectivec\"><code><span class=\"token macro property\"><span class=\"token directive-hash\">#</span><span class=\"token directive keyword\">import</span> <span class=\"token expression\"><span class=\"token operator\">&lt;</span>Flutter<span class=\"token operator\">/</span>Flutter<span class=\"token punctuation\">.</span>h<span class=\"token operator\">&gt;</span></span></span>\n\n<span class=\"token keyword\">@implementation</span> AppDelegate\n<span class=\"token operator\">-</span> <span class=\"token punctuation\">(</span>BOOL<span class=\"token punctuation\">)</span>application<span class=\"token punctuation\">:</span><span class=\"token punctuation\">(</span>UIApplication<span class=\"token operator\">*</span><span class=\"token punctuation\">)</span>application didFinishLaunchingWithOptions<span class=\"token punctuation\">:</span><span class=\"token punctuation\">(</span>NSDictionary<span class=\"token operator\">*</span><span class=\"token punctuation\">)</span>launchOptions <span class=\"token punctuation\">{</span>\n  FlutterViewController<span class=\"token operator\">*</span> controller <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>FlutterViewController<span class=\"token operator\">*</span><span class=\"token punctuation\">)</span><span class=\"token keyword\">self</span><span class=\"token punctuation\">.</span>window<span class=\"token punctuation\">.</span>rootViewController<span class=\"token punctuation\">;</span>\n\n  FlutterMethodChannel<span class=\"token operator\">*</span> batteryChannel <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span>FlutterMethodChannel\n                                          methodChannelWithName<span class=\"token punctuation\">:</span><span class=\"token string\">@&quot;samples.flutter.io/battery&quot;</span>\n                                          binaryMessenger<span class=\"token punctuation\">:</span>controller<span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token punctuation\">[</span>batteryChannel setMethodCallHandler<span class=\"token punctuation\">:</span><span class=\"token operator\">^</span><span class=\"token punctuation\">(</span>FlutterMethodCall<span class=\"token operator\">*</span> call<span class=\"token punctuation\">,</span> FlutterResult result<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// TODO</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">[</span><span class=\"token keyword\">super</span> application<span class=\"token punctuation\">:</span>application didFinishLaunchingWithOptions<span class=\"token punctuation\">:</span>launchOptions<span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>接下来，我们添加Objective-C代码，使用iOS电池API来获取电池电量，这和原生是相同的。</p> <p>在<code>AppDelegate</code>类中添加以下新的方法：</p> <div class=\"language-objectivec extra-class\"><pre class=\"language-objectivec\"><code><span class=\"token operator\">-</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">int</span><span class=\"token punctuation\">)</span>getBatteryLevel <span class=\"token punctuation\">{</span>\n  UIDevice<span class=\"token operator\">*</span> device <span class=\"token operator\">=</span> UIDevice<span class=\"token punctuation\">.</span>currentDevice<span class=\"token punctuation\">;</span>\n  device<span class=\"token punctuation\">.</span>batteryMonitoringEnabled <span class=\"token operator\">=</span> YES<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>device<span class=\"token punctuation\">.</span>batteryState <span class=\"token operator\">==</span> UIDeviceBatteryStateUnknown<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token operator\">-</span><span class=\"token number\">1</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">int</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span>device<span class=\"token punctuation\">.</span>batteryLevel <span class=\"token operator\">*</span> <span class=\"token number\">100</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>最后，我们完成之前添加的<code>setMethodCallHandler</code>方法。我们需要处理的平台方法名为<code>getBatteryLevel</code>，所以我们在call参数中需要先判断是否为<code>getBatteryLevel</code>。 这个平台方法的实现只需调用我们在前一步中编写的iOS代码，并使用result参数返回成功或错误的响应。如果调用了未定义的API，我们也会通知返回：</p> <div class=\"language-objectivec extra-class\"><pre class=\"language-objectivec\"><code><span class=\"token punctuation\">[</span>batteryChannel setMethodCallHandler<span class=\"token punctuation\">:</span><span class=\"token operator\">^</span><span class=\"token punctuation\">(</span>FlutterMethodCall<span class=\"token operator\">*</span> call<span class=\"token punctuation\">,</span> FlutterResult result<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token string\">@&quot;getBatteryLevel&quot;</span> isEqualToString<span class=\"token punctuation\">:</span>call<span class=\"token punctuation\">.</span>method<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">int</span> batteryLevel <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token keyword\">self</span> getBatteryLevel<span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>batteryLevel <span class=\"token operator\">==</span> <span class=\"token operator\">-</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">result</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span>FlutterError errorWithCode<span class=\"token punctuation\">:</span><span class=\"token string\">@&quot;UNAVAILABLE&quot;</span>\n                                 message<span class=\"token punctuation\">:</span><span class=\"token string\">@&quot;电池信息不可用&quot;</span>\n                                 details<span class=\"token punctuation\">:</span>nil<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">result</span><span class=\"token punctuation\">(</span><span class=\"token operator\">@</span><span class=\"token punctuation\">(</span>batteryLevel<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">result</span><span class=\"token punctuation\">(</span>FlutterMethodNotImplemented<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>现在可以在iOS上运行该应用程序了，如果使用的是iOS模拟器，请注意，它不支持电池API，因此应用程序将显示“电池信息不可用”。</p> <h3 id=\"使用swift实现ios-api\"><a href=\"#使用swift实现ios-api\" class=\"header-anchor\">#</a> 使用Swift实现iOS API</h3> <p>以下步骤与上面使用Objective-C相似，首先打开Xcode中Flutter应用程序的iOS部分:</p> <ol><li>启动 Xcode</li> <li>选择 File &gt; Open…</li> <li>定位到您 Flutter app目录, 然后选择里面的 <code>ios</code>文件夹，点击 OK</li> <li>确保Xcode项目的构建没有错误。</li> <li>选择 Runner &gt; Runner ，然后打开<code>AppDelegate.swift</code></li></ol> <p>接下来，覆盖application方法并创建一个<code>FlutterMethodChannel</code>绑定通道名称<code>samples.flutter.io/battery</code>：</p> <div class=\"language-swift extra-class\"><pre class=\"language-swift\"><code><span class=\"token atrule\">@UIApplicationMain</span>\n<span class=\"token atrule\">@objc</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">AppDelegate</span><span class=\"token punctuation\">:</span> <span class=\"token builtin\">FlutterAppDelegate</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">override</span> <span class=\"token keyword\">func</span> <span class=\"token function\">application</span><span class=\"token punctuation\">(</span>\n    <span class=\"token number\">_</span> application<span class=\"token punctuation\">:</span> <span class=\"token builtin\">UIApplication</span><span class=\"token punctuation\">,</span>\n    didFinishLaunchingWithOptions launchOptions<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span><span class=\"token builtin\">UIApplicationLaunchOptionsKey</span><span class=\"token punctuation\">:</span> <span class=\"token builtin\">Any</span><span class=\"token punctuation\">]</span><span class=\"token operator\">?</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span><span class=\"token operator\">&gt;</span> <span class=\"token builtin\">Bool</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token builtin\">GeneratedPluginRegistrant</span><span class=\"token punctuation\">.</span><span class=\"token function\">register</span><span class=\"token punctuation\">(</span>with<span class=\"token punctuation\">:</span> <span class=\"token keyword\">self</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">let</span> controller <span class=\"token punctuation\">:</span> <span class=\"token builtin\">FlutterViewController</span> <span class=\"token operator\">=</span> window<span class=\"token operator\">?</span><span class=\"token punctuation\">.</span>rootViewController <span class=\"token keyword\">as</span><span class=\"token operator\">!</span> <span class=\"token builtin\">FlutterViewController</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">let</span> batteryChannel <span class=\"token operator\">=</span> <span class=\"token builtin\">FlutterMethodChannel</span><span class=\"token punctuation\">.</span><span class=\"token keyword\">init</span><span class=\"token punctuation\">(</span>name<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;samples.flutter.io/battery&quot;</span><span class=\"token punctuation\">,</span>\n                                                   binaryMessenger<span class=\"token punctuation\">:</span> controller<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    batteryChannel<span class=\"token punctuation\">.</span><span class=\"token function\">setMethodCallHandler</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n      <span class=\"token punctuation\">(</span>call<span class=\"token punctuation\">:</span> <span class=\"token builtin\">FlutterMethodCall</span><span class=\"token punctuation\">,</span> result<span class=\"token punctuation\">:</span> <span class=\"token builtin\">FlutterResult</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span><span class=\"token operator\">&gt;</span> <span class=\"token builtin\">Void</span> <span class=\"token keyword\">in</span>\n      <span class=\"token comment\">// Handle battery messages.</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">application</span><span class=\"token punctuation\">(</span>application<span class=\"token punctuation\">,</span> didFinishLaunchingWithOptions<span class=\"token punctuation\">:</span> launchOptions<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>接下来，我们添加Swift代码，使用iOS电池API来获取电池电量，这和原生开发是相同的。</p> <p>将以下新方法添加到<code>AppDelegate.swift</code>底部:</p> <div class=\"language-swift extra-class\"><pre class=\"language-swift\"><code><span class=\"token keyword\">private</span> <span class=\"token keyword\">func</span> <span class=\"token function\">receiveBatteryLevel</span><span class=\"token punctuation\">(</span>result<span class=\"token punctuation\">:</span> <span class=\"token builtin\">FlutterResult</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">let</span> device <span class=\"token operator\">=</span> <span class=\"token builtin\">UIDevice</span><span class=\"token punctuation\">.</span>current<span class=\"token punctuation\">;</span>\n  device<span class=\"token punctuation\">.</span>isBatteryMonitoringEnabled <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>device<span class=\"token punctuation\">.</span>batteryState <span class=\"token operator\">==</span> <span class=\"token builtin\">UIDeviceBatteryState</span><span class=\"token punctuation\">.</span>unknown<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">result</span><span class=\"token punctuation\">(</span><span class=\"token builtin\">FlutterError</span><span class=\"token punctuation\">.</span><span class=\"token keyword\">init</span><span class=\"token punctuation\">(</span>code<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;UNAVAILABLE&quot;</span><span class=\"token punctuation\">,</span>\n                             message<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;电池信息不可用&quot;</span><span class=\"token punctuation\">,</span>\n                             details<span class=\"token punctuation\">:</span> <span class=\"token constant\">nil</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">result</span><span class=\"token punctuation\">(</span><span class=\"token function\">Int</span><span class=\"token punctuation\">(</span>device<span class=\"token punctuation\">.</span>batteryLevel <span class=\"token operator\">*</span> <span class=\"token number\">100</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>最后，我们完成之前添加的<code>setMethodCallHandler</code>方法。我们需要处理的平台方法名为<code>getBatteryLevel</code>，所以我们在call参数中需要先判断是否为<code>getBatteryLevel</code>。 这个平台方法的实现只需调用我们在前一步中编写的iOS代码，并使用result参数返回成功或错误的响应。如果调用了未定义的API，我们也会通知返回：</p> <div class=\"language-swift extra-class\"><pre class=\"language-swift\"><code>batteryChannel<span class=\"token punctuation\">.</span><span class=\"token function\">setMethodCallHandler</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">(</span>call<span class=\"token punctuation\">:</span> <span class=\"token builtin\">FlutterMethodCall</span><span class=\"token punctuation\">,</span> result<span class=\"token punctuation\">:</span> <span class=\"token builtin\">FlutterResult</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span><span class=\"token operator\">&gt;</span> <span class=\"token builtin\">Void</span> <span class=\"token keyword\">in</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token string\">&quot;getBatteryLevel&quot;</span> <span class=\"token operator\">==</span> call<span class=\"token punctuation\">.</span>method<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">receiveBatteryLevel</span><span class=\"token punctuation\">(</span>result<span class=\"token punctuation\">:</span> result<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">result</span><span class=\"token punctuation\">(</span><span class=\"token builtin\">FlutterMethodNotImplemented</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>现在可以在iOS上运行应用程序，如果使用的是iOS模拟器，请注意，它不支持电池API，因此应用程序将显示“电池信息不可用”。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/193.05fa90e3.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter12/platform-channel.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>12.2 插件开发：平台通道简介 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/113.d0f44add.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter12/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_12-2-插件开发-平台通道简介\"><a href=\"#_12-2-插件开发-平台通道简介\" class=\"header-anchor\">#</a> 12.2 插件开发：平台通道简介</h1> <p>“平台特定”或“特定平台”中的平台指的就是Flutter应用程序运行的平台，如Android或IOS。我们知道一个完整的Flutter应用程序实际上包括原生代码和Flutter代码两部分。由于Flutter本身只是一个UI系统，它本身是无法提供一些系统能力，比如使用蓝牙、相机、GPS等，因此要在Flutter APP中调用这些能力就必须和原生平台进行通信。为此，Flutter中提供了一个平台通道（platform channel），用于Flutter和原生平台的通信。平台通道正是Flutter和原生之间通信的桥梁，它也是Flutter插件的底层基础设施。</p> <p>Flutter使用了一个灵活的系统，允许您调用特定平台的API，无论在Android上的Java或Kotlin代码中，还是iOS上的ObjectiveC或Swift代码中均可用。</p> <p>Flutter与原生之间的通信依赖灵活的消息传递方式：</p> <ul><li>应用的Flutter部分通过平台通道（platform channel）将消息发送到其应用程序的所在的宿主（iOS或Android）应用（原生应用）。</li> <li>宿主监听平台通道，并接收该消息。然后它会调用该平台的API，并将响应发送回客户端，即应用程序的Flutter部分。</li></ul> <h3 id=\"平台通道\"><a href=\"#平台通道\" class=\"header-anchor\">#</a> 平台通道</h3> <p>使用平台通道在Flutter(client)和原生(host)之间传递消息，如下图所示：</p> <p><img src=\"/assets/img/12-3.2326714a.png\" alt=\"平台通道\"></p> <p>当在Flutter中调用原生方法时，调用信息通过平台通道传递到原生，原生收到调用信息后方可执行指定的操作，如需返回数据，则原生会将数据再通过平台通道传递给Flutter。值得注意的是消息传递是异步的，这确保了用户界面在消息传递时不会被挂起。</p> <p>在客户端，<a href=\"https://docs.flutter.io/flutter/services/MethodChannel-class.html\" target=\"_blank\" rel=\"noopener noreferrer\">MethodChannel  API<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 可以发送与方法调用相对应的消息。 在宿主平台上，<code>MethodChannel</code> 在<a href=\"https://docs.flutter.io/javadoc/io/flutter/plugin/common/MethodChannel.html\" target=\"_blank\" rel=\"noopener noreferrer\">Android API<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 和 <a href=\"https://docs.flutter.io/objcdoc/Classes/FlutterMethodChannel.html\" target=\"_blank\" rel=\"noopener noreferrer\">FlutterMethodChannel iOS API<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>可以接收方法调用并返回结果。这些类可以帮助我们用很少的代码就能开发平台插件。</p> <blockquote><p><strong>注意</strong>: 如果需要，方法调用(消息传递)可以是反向的，即宿主作为客户端调用Dart中实现的API。 <a href=\"https://pub.dartlang.org/packages/quick_actions\" target=\"_blank\" rel=\"noopener noreferrer\"><code>quick_actions</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>插件就是一个具体的例子。</p></blockquote> <h3 id=\"平台通道数据类型支持\"><a href=\"#平台通道数据类型支持\" class=\"header-anchor\">#</a> 平台通道数据类型支持</h3> <p>平台通道使用标准消息编/解码器对消息进行编解码，它可以高效的对消息进行二进制序列化与反序列化。由于Dart与原生平台之间数据类型有所差异，下面我们列出数据类型之间的映射关系。</p> <table><thead><tr><th>Dart</th> <th>Android</th> <th>iOS</th></tr></thead> <tbody><tr><td>null</td> <td>null</td> <td>nil (NSNull when nested)</td></tr> <tr><td>bool</td> <td>java.lang.Boolean</td> <td>NSNumber numberWithBool:</td></tr> <tr><td>int</td> <td>java.lang.Integer</td> <td>NSNumber numberWithInt:</td></tr> <tr><td>int, 如果不足32位</td> <td>java.lang.Long</td> <td>NSNumber numberWithLong:</td></tr> <tr><td>int, 如果不足64位</td> <td>java.math.BigInteger</td> <td>FlutterStandardBigInteger</td></tr> <tr><td>double</td> <td>java.lang.Double</td> <td>NSNumber numberWithDouble:</td></tr> <tr><td>String</td> <td>java.lang.String</td> <td>NSString</td></tr> <tr><td>Uint8List</td> <td>byte[]</td> <td>FlutterStandardTypedData typedDataWithBytes:</td></tr> <tr><td>Int32List</td> <td>int[]</td> <td>FlutterStandardTypedData typedDataWithInt32:</td></tr> <tr><td>Int64List</td> <td>long[]</td> <td>FlutterStandardTypedData typedDataWithInt64:</td></tr> <tr><td>Float64List</td> <td>double[]</td> <td>FlutterStandardTypedData typedDataWithFloat64:</td></tr> <tr><td>List</td> <td>java.util.ArrayList</td> <td>NSArray</td></tr> <tr><td>Map</td> <td>java.util.HashMap</td> <td>NSDictionary</td></tr></tbody></table> <p>当在发送和接收值时，这些值在消息中的序列化和反序列化会自动进行。</p> <h3 id=\"自定义编解码器\"><a href=\"#自定义编解码器\" class=\"header-anchor\">#</a> 自定义编解码器</h3> <p>除了上面提到的<code>MethodChannel</code>，还可以使用<a href=\"https://docs.flutter.io/flutter/services/BasicMessageChannel-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>BasicMessageChannel</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>，它支持使用自定义消息编解码器进行基本的异步消息传递。 此外，可以使用专门的<a href=\"https://docs.flutter.io/flutter/services/BinaryCodec-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>BinaryCodec</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>、<a href=\"https://docs.flutter.io/flutter/services/StringCodec-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>StringCodec</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>和 <a href=\"https://docs.flutter.io/flutter/services/JSONMessageCodec-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>JSONMessageCodec</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>类，或创建自己的编解码器。</p> <h3 id=\"如何获取平台信息\"><a href=\"#如何获取平台信息\" class=\"header-anchor\">#</a> 如何获取平台信息</h3> <p>Flutter 中提供了一个全局变量<code>defaultTargetPlatform</code>来获取当前应用的平台信息，<code>defaultTargetPlatform</code>定义在&quot;platform.dart&quot;中，它的类型是<code>TargetPlatform</code>，这是一个枚举类，定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">enum</span> TargetPlatform <span class=\"token punctuation\">{</span>\n  android<span class=\"token punctuation\">,</span>\n  fuchsia<span class=\"token punctuation\">,</span>\n  iOS<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以看到目前Flutter只支持这三个平台。我们可以通过如下代码判断平台：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>defaultTargetPlatform<span class=\"token operator\">==</span>TargetPlatform<span class=\"token punctuation\">.</span>android<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// 是安卓系统，do something</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n</code></pre></div><p>由于不同平台有它们各自的交互规范，Flutter Material库中的一些组件都针对相应的平台做了一些适配，比如路由组件<code>MaterialPageRoute</code>，它在android和ios中会应用各自平台规范的切换动画。那如果我们想让我们的APP在所有平台都表现一致，比如希望在所有平台路由切换动画都按照ios平台一致的左右滑动切换风格该怎么做？Flutter中提供了一种覆盖默认平台的机制，我们可以通过显式指定<code>debugDefaultTargetPlatformOverride</code>全局变量的值来指定应用平台。比如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>debugDefaultTargetPlatformOverride<span class=\"token operator\">=</span>TargetPlatform<span class=\"token punctuation\">.</span>iOS<span class=\"token punctuation\">;</span>\n<span class=\"token function\">print</span><span class=\"token punctuation\">(</span>defaultTargetPlatform<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// 会输出TargetPlatform.iOS</span>\n</code></pre></div><p>上面代码即在Android中运行后，Flutter APP就会认为是当前系统是iOS，Material组件库中所有组件交互方式都会和iOS平台对齐，<code>defaultTargetPlatform</code>的值也会变为<code>TargetPlatform.iOS</code>。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/113.d0f44add.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter12/texture_platformview.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>12.6 Texture和PlatformView | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/72.2c5ab8cf.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter12/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_12-6-texture和platformview\"><a href=\"#_12-6-texture和platformview\" class=\"header-anchor\">#</a> 12.6 Texture和PlatformView</h1> <p>本节主要介绍原生和Flutter之间如何共享图像，以及如何在Flutter中嵌套原生组件。</p> <h2 id=\"_12-6-1-texture-示例-使用摄像头\"><a href=\"#_12-6-1-texture-示例-使用摄像头\" class=\"header-anchor\">#</a> 12.6.1 Texture（示例：使用摄像头）</h2> <p>前面说过Flutter本身只是一个UI系统，对于一些系统能力的调用我们可以通过消息传送机制与原生交互。但是这种消息传送机制并不能覆盖所有的应用场景，比如我们想调用摄像头来拍照或录视频，但在拍照和录视频的过程中我们需要将预览画面显示到我们的Flutter UI中，如果我们要用Flutter定义的消息通道机制来实现这个功能，就需要将摄像头采集的每一帧图片都要从原生传递到Flutter中，这样做代价将会非常大，因为将图像或视频数据通过消息通道实时传输必然会引起内存和CPU的巨大消耗！为此，Flutter提供了一种基于Texture的图片数据共享机制。</p> <p>Texture可以理解为GPU内保存将要绘制的图像数据的一个对象，Flutter engine会将Texture的数据在内存中直接进行映射（而无需在原生和Flutter之间再进行数据传递），Flutter会给每一个Texture分配一个id，同时Flutter中提供了一个<code>Texture</code>组件，<code>Texture</code>构造函数定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">const</span> <span class=\"token function\">Texture</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  Key key<span class=\"token punctuation\">,</span>\n  <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>textureId<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p><code>Texture</code> 组件正是通过<code>textureId</code>与Texture数据关联起来；在<code>Texture</code>组件绘制时，Flutter会自动从内存中找到相应id的Texture数据，然后进行绘制。可以总结一下整个流程：图像数据先在原生部分缓存，然后在Flutter部分再通过<code>textureId</code>和缓存关联起来，最后绘制由Flutter完成。</p> <p>如果我们作为一个插件开发者，我们在原生代码中分配了<code>textureId</code>，那么在Flutter侧使用<code>Texture</code>组件时要如何获取<code>textureId</code>呢？这又回到了之前的内容了，<code>textureId</code>完全可以通过MethodChannel来传递。</p> <p>另外，值得注意的是，当原生摄像头捕获的图像发生变化时，<code>Texture</code> 组件会自动重绘，这不需要我们写任何Dart 代码去控制。</p> <h3 id=\"texture用法\"><a href=\"#texture用法\" class=\"header-anchor\">#</a> Texture用法</h3> <p>如果我们要手动实现一个相机插件，和前面几节介绍的“获取剩余电量”插件的步骤一样，需要分别实现原生部分和Flutter部分。考虑到大多数读者可能并非同时既了解Android开发，又了解iOS开发，如果我们再花大量篇幅来介绍不同端的实现可能会没什么意义，另外，由于Flutter官方提供的相机（camera）插件和视频播放（video_player）插件都是使用Texture来实现的，它们本身就是Texture非常好的示例，所以在本书中将不会再介绍使用Texture的具体流程了，读者有兴趣查看camera和video_player的实现代码。下面我们重点介绍一下如何使用camera和video_player。</p> <h3 id=\"相机示例\"><a href=\"#相机示例\" class=\"header-anchor\">#</a> 相机示例</h3> <p>下面我们看一下camera包自带的一个示例，它包含如下功能：</p> <ol><li>可以拍照，也可以拍视频，拍摄完成后可以保存；排号的视频可以播放预览。</li> <li>可以切换摄像头（前置摄像头、后置摄像头、其它）</li> <li>可以显示已经拍摄内容的预览图。</li></ol> <p>下面我们看一下具体代码：</p> <ol><li><p>首先，依赖camera插件的最新版，并下载依赖。</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code><span class=\"token key atrule\">dependencies</span><span class=\"token punctuation\">:</span>\n  <span class=\"token punctuation\">...</span>  //省略无关代码\n  <span class=\"token key atrule\">camera</span><span class=\"token punctuation\">:</span> ^0.5.2+2\n</code></pre></div></li> <li><p>在<code>main</code>方法中获取可用摄像头列表。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">main</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// 获取可用摄像头列表，cameras为全局变量</span>\n  cameras <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">availableCameras</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token function\">runApp</span><span class=\"token punctuation\">(</span><span class=\"token function\">MyApp</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div></li> <li><p>构建UI。现在我们构建如图12-4的测试界面：</p> <p><img src=\"/assets/img/12-4.49c3c9bf.jpg\" alt=\"12-4\">\n线面是完整的代码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:camera/camera.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'../common.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'dart:async'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'dart:io'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:path_provider/path_provider.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:video_player/video_player.dart'</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//用于播放录制的视频</span>\n\n<span class=\"token comment\">/// 获取不同摄像头的图标（前置、后置、其它）</span>\nIconData <span class=\"token function\">getCameraLensIcon</span><span class=\"token punctuation\">(</span>CameraLensDirection direction<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">switch</span> <span class=\"token punctuation\">(</span>direction<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">case</span> CameraLensDirection<span class=\"token punctuation\">.</span>back<span class=\"token punctuation\">:</span>\n      <span class=\"token keyword\">return</span> Icons<span class=\"token punctuation\">.</span>camera_rear<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">case</span> CameraLensDirection<span class=\"token punctuation\">.</span>front<span class=\"token punctuation\">:</span>\n      <span class=\"token keyword\">return</span> Icons<span class=\"token punctuation\">.</span>camera_front<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">case</span> CameraLensDirection<span class=\"token punctuation\">.</span><span class=\"token keyword\">external</span><span class=\"token punctuation\">:</span>\n      <span class=\"token keyword\">return</span> Icons<span class=\"token punctuation\">.</span>camera<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token keyword\">throw</span> <span class=\"token function\">ArgumentError</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Unknown lens direction'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">void</span> <span class=\"token function\">logError</span><span class=\"token punctuation\">(</span>String code<span class=\"token punctuation\">,</span> String message<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Error: $code\\nError Message: $message'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// 示例页面路由</span>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">CameraExampleHome</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _CameraExampleHomeState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">_CameraExampleHomeState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_CameraExampleHomeState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>CameraExampleHome<span class=\"token operator\">&gt;</span>\n    <span class=\"token keyword\">with</span> WidgetsBindingObserver <span class=\"token punctuation\">{</span>\n  CameraController controller<span class=\"token punctuation\">;</span>\n  String imagePath<span class=\"token punctuation\">;</span> <span class=\"token comment\">// 图片保存路径</span>\n  String videoPath<span class=\"token punctuation\">;</span> <span class=\"token comment\">//视频保存路径</span>\n  VideoPlayerController videoController<span class=\"token punctuation\">;</span>\n  VoidCallback videoPlayerListener<span class=\"token punctuation\">;</span>\n  bool enableAudio <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">// 监听APP状态改变，是否在前台</span>\n    WidgetsBinding<span class=\"token punctuation\">.</span>instance<span class=\"token punctuation\">.</span><span class=\"token function\">addObserver</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    WidgetsBinding<span class=\"token punctuation\">.</span>instance<span class=\"token punctuation\">.</span><span class=\"token function\">removeObserver</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">didChangeAppLifecycleState</span><span class=\"token punctuation\">(</span>AppLifecycleState state<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 如果APP不在在前台</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>state <span class=\"token operator\">==</span> AppLifecycleState<span class=\"token punctuation\">.</span>inactive<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      controller<span class=\"token operator\">?</span><span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>state <span class=\"token operator\">==</span> AppLifecycleState<span class=\"token punctuation\">.</span>resumed<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">// 在前台</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>controller <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">onNewCameraSelected</span><span class=\"token punctuation\">(</span>controller<span class=\"token punctuation\">.</span>description<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">final</span> GlobalKey<span class=\"token operator\">&lt;</span>ScaffoldState<span class=\"token operator\">&gt;</span> _scaffoldKey <span class=\"token operator\">=</span> GlobalKey<span class=\"token operator\">&lt;</span>ScaffoldState<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n      key<span class=\"token punctuation\">:</span> _scaffoldKey<span class=\"token punctuation\">,</span>\n      appBar<span class=\"token punctuation\">:</span> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>\n        title<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'相机示例'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      body<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n        children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n          <span class=\"token function\">Expanded</span><span class=\"token punctuation\">(</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n                padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">1.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                child<span class=\"token punctuation\">:</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n                  child<span class=\"token punctuation\">:</span> <span class=\"token function\">_cameraPreviewWidget</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>\n                color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>black<span class=\"token punctuation\">,</span>\n                border<span class=\"token punctuation\">:</span> Border<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span>\n                  color<span class=\"token punctuation\">:</span> controller <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">&amp;&amp;</span> controller<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>isRecordingVideo\n                      <span class=\"token operator\">?</span> Colors<span class=\"token punctuation\">.</span>redAccent\n                      <span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">,</span>\n                  width<span class=\"token punctuation\">:</span> <span class=\"token number\">3.0</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">_captureControlRowWidget</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">_toggleAudioWidget</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n            padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">5.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n              mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n              children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                <span class=\"token function\">_cameraTogglesRowWidget</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token function\">_thumbnailWidget</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">/// 展示预览窗口</span>\n  Widget <span class=\"token function\">_cameraPreviewWidget</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>controller <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">||</span> <span class=\"token operator\">!</span>controller<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>isInitialized<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>\n        <span class=\"token string\">'选择一个摄像头'</span><span class=\"token punctuation\">,</span>\n        style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>\n          color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">,</span>\n          fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">24.0</span><span class=\"token punctuation\">,</span>\n          fontWeight<span class=\"token punctuation\">:</span> FontWeight<span class=\"token punctuation\">.</span>w900<span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token function\">AspectRatio</span><span class=\"token punctuation\">(</span>\n        aspectRatio<span class=\"token punctuation\">:</span> controller<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>aspectRatio<span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">CameraPreview</span><span class=\"token punctuation\">(</span>controller<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">/// 开启或关闭录音</span>\n  Widget <span class=\"token function\">_toggleAudioWidget</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n      padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">only</span><span class=\"token punctuation\">(</span>left<span class=\"token punctuation\">:</span> <span class=\"token number\">25</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n        children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n          <span class=\"token keyword\">const</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'开启录音:'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">Switch</span><span class=\"token punctuation\">(</span>\n            value<span class=\"token punctuation\">:</span> enableAudio<span class=\"token punctuation\">,</span>\n            onChanged<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>bool value<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              enableAudio <span class=\"token operator\">=</span> value<span class=\"token punctuation\">;</span>\n              <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>controller <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token function\">onNewCameraSelected</span><span class=\"token punctuation\">(</span>controller<span class=\"token punctuation\">.</span>description<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">/// 显示已拍摄的图片/视频缩略图。</span>\n  Widget <span class=\"token function\">_thumbnailWidget</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Expanded</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Align</span><span class=\"token punctuation\">(</span>\n        alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>centerRight<span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n          mainAxisSize<span class=\"token punctuation\">:</span> MainAxisSize<span class=\"token punctuation\">.</span>min<span class=\"token punctuation\">,</span>\n          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            videoController <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">&amp;&amp;</span> imagePath <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span>\n                <span class=\"token operator\">?</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n                <span class=\"token punctuation\">:</span> <span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>videoController <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span>\n                  <span class=\"token operator\">?</span> Image<span class=\"token punctuation\">.</span><span class=\"token function\">file</span><span class=\"token punctuation\">(</span><span class=\"token function\">File</span><span class=\"token punctuation\">(</span>imagePath<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n                  <span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n                child<span class=\"token punctuation\">:</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n                  child<span class=\"token punctuation\">:</span> <span class=\"token function\">AspectRatio</span><span class=\"token punctuation\">(</span>\n                      aspectRatio<span class=\"token punctuation\">:</span>\n                      videoController<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>size <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span>\n                          <span class=\"token operator\">?</span> videoController<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>aspectRatio\n                          <span class=\"token punctuation\">:</span> <span class=\"token number\">1.0</span><span class=\"token punctuation\">,</span>\n                      child<span class=\"token punctuation\">:</span> <span class=\"token function\">VideoPlayer</span><span class=\"token punctuation\">(</span>videoController<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>\n                    border<span class=\"token punctuation\">:</span> Border<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>pink<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              width<span class=\"token punctuation\">:</span> <span class=\"token number\">64.0</span><span class=\"token punctuation\">,</span>\n              height<span class=\"token punctuation\">:</span> <span class=\"token number\">64.0</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">/// 相机工具栏</span>\n  Widget <span class=\"token function\">_captureControlRowWidget</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n      mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>spaceEvenly<span class=\"token punctuation\">,</span>\n      mainAxisSize<span class=\"token punctuation\">:</span> MainAxisSize<span class=\"token punctuation\">.</span>max<span class=\"token punctuation\">,</span>\n      children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n        <span class=\"token function\">IconButton</span><span class=\"token punctuation\">(</span>\n          icon<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>camera_alt<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span>\n          onPressed<span class=\"token punctuation\">:</span> controller <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">&amp;&amp;</span>\n              controller<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>isInitialized <span class=\"token operator\">&amp;&amp;</span>\n              <span class=\"token operator\">!</span>controller<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>isRecordingVideo\n              <span class=\"token operator\">?</span> onTakePictureButtonPressed\n              <span class=\"token punctuation\">:</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token function\">IconButton</span><span class=\"token punctuation\">(</span>\n          icon<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>videocam<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span>\n          onPressed<span class=\"token punctuation\">:</span> controller <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">&amp;&amp;</span>\n              controller<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>isInitialized <span class=\"token operator\">&amp;&amp;</span>\n              <span class=\"token operator\">!</span>controller<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>isRecordingVideo\n              <span class=\"token operator\">?</span> onVideoRecordButtonPressed\n              <span class=\"token punctuation\">:</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token function\">IconButton</span><span class=\"token punctuation\">(</span>\n          icon<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>stop<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span>\n          onPressed<span class=\"token punctuation\">:</span> controller <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">&amp;&amp;</span>\n              controller<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>isInitialized <span class=\"token operator\">&amp;&amp;</span>\n              controller<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>isRecordingVideo\n              <span class=\"token operator\">?</span> onStopButtonPressed\n              <span class=\"token punctuation\">:</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">/// 展示所有摄像头</span>\n  Widget <span class=\"token function\">_cameraTogglesRowWidget</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">final</span> List<span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span> toggles <span class=\"token operator\">=</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>cameras<span class=\"token punctuation\">.</span>isEmpty<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'没有检测到摄像头'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span>CameraDescription cameraDescription <span class=\"token keyword\">in</span> cameras<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        toggles<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span>\n          <span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span>\n            width<span class=\"token punctuation\">:</span> <span class=\"token number\">90.0</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> RadioListTile<span class=\"token operator\">&lt;</span>CameraDescription<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n              title<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span><span class=\"token function\">getCameraLensIcon</span><span class=\"token punctuation\">(</span>cameraDescription<span class=\"token punctuation\">.</span>lensDirection<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              groupValue<span class=\"token punctuation\">:</span> controller<span class=\"token operator\">?</span><span class=\"token punctuation\">.</span>description<span class=\"token punctuation\">,</span>\n              value<span class=\"token punctuation\">:</span> cameraDescription<span class=\"token punctuation\">,</span>\n              onChanged<span class=\"token punctuation\">:</span> controller <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">&amp;&amp;</span> controller<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>isRecordingVideo\n                  <span class=\"token operator\">?</span> <span class=\"token keyword\">null</span>\n                  <span class=\"token punctuation\">:</span> onNewCameraSelected<span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>children<span class=\"token punctuation\">:</span> toggles<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  String <span class=\"token function\">timestamp</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> DateTime<span class=\"token punctuation\">.</span><span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>millisecondsSinceEpoch<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">showInSnackBar</span><span class=\"token punctuation\">(</span>String message<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    _scaffoldKey<span class=\"token punctuation\">.</span>currentState<span class=\"token punctuation\">.</span><span class=\"token function\">showSnackBar</span><span class=\"token punctuation\">(</span><span class=\"token function\">SnackBar</span><span class=\"token punctuation\">(</span>content<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>message<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">// 摄像头选中回调</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">onNewCameraSelected</span><span class=\"token punctuation\">(</span>CameraDescription cameraDescription<span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>controller <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">await</span> controller<span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    controller <span class=\"token operator\">=</span> <span class=\"token function\">CameraController</span><span class=\"token punctuation\">(</span>\n      cameraDescription<span class=\"token punctuation\">,</span>\n      ResolutionPreset<span class=\"token punctuation\">.</span>high<span class=\"token punctuation\">,</span>\n      enableAudio<span class=\"token punctuation\">:</span> enableAudio<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    controller<span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>mounted<span class=\"token punctuation\">)</span> <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>controller<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>hasError<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">showInSnackBar</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Camera error ${controller.value.errorDescription}'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">await</span> controller<span class=\"token punctuation\">.</span><span class=\"token function\">initialize</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">on</span> CameraException <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">_showCameraException</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>mounted<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">// 拍照按钮点击回调</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">onTakePictureButtonPressed</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">takePicture</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>String filePath<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>mounted<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          imagePath <span class=\"token operator\">=</span> filePath<span class=\"token punctuation\">;</span>\n          videoController<span class=\"token operator\">?</span><span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          videoController <span class=\"token operator\">=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>filePath <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token function\">showInSnackBar</span><span class=\"token punctuation\">(</span><span class=\"token string\">'图片保存在 $filePath'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">// 开始录制视频</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">onVideoRecordButtonPressed</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">startVideoRecording</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>String filePath<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>mounted<span class=\"token punctuation\">)</span> <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>filePath <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token function\">showInSnackBar</span><span class=\"token punctuation\">(</span><span class=\"token string\">'正在保存视频于 $filePath'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">// 终止视频录制</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">onStopButtonPressed</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">stopVideoRecording</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>_<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>mounted<span class=\"token punctuation\">)</span> <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token function\">showInSnackBar</span><span class=\"token punctuation\">(</span><span class=\"token string\">'视频保存在: $videoPath'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  Future<span class=\"token operator\">&lt;</span>String<span class=\"token operator\">&gt;</span> <span class=\"token function\">startVideoRecording</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>controller<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>isInitialized<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">showInSnackBar</span><span class=\"token punctuation\">(</span><span class=\"token string\">'请先选择一个摄像头'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">return</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token comment\">// 确定视频保存的路径</span>\n    <span class=\"token keyword\">final</span> Directory extDir <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">getApplicationDocumentsDirectory</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">final</span> String dirPath <span class=\"token operator\">=</span> <span class=\"token string\">'${extDir.path}/Movies/flutter_test'</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">await</span> <span class=\"token function\">Directory</span><span class=\"token punctuation\">(</span>dirPath<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">create</span><span class=\"token punctuation\">(</span>recursive<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">final</span> String filePath <span class=\"token operator\">=</span> <span class=\"token string\">'$dirPath/${timestamp()}.mp4'</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>controller<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>isRecordingVideo<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">// 如果正在录制，则直接返回</span>\n      <span class=\"token keyword\">return</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n      videoPath <span class=\"token operator\">=</span> filePath<span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">await</span> controller<span class=\"token punctuation\">.</span><span class=\"token function\">startVideoRecording</span><span class=\"token punctuation\">(</span>filePath<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">on</span> CameraException <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">_showCameraException</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">return</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">return</span> filePath<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  Future<span class=\"token operator\">&lt;</span><span class=\"token keyword\">void</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">stopVideoRecording</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>controller<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>isRecordingVideo<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">await</span> controller<span class=\"token punctuation\">.</span><span class=\"token function\">stopVideoRecording</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">on</span> CameraException <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">_showCameraException</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">return</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">await</span> <span class=\"token function\">_startVideoPlayer</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  Future<span class=\"token operator\">&lt;</span><span class=\"token keyword\">void</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">_startVideoPlayer</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">final</span> VideoPlayerController vcontroller <span class=\"token operator\">=</span>\n    VideoPlayerController<span class=\"token punctuation\">.</span><span class=\"token function\">file</span><span class=\"token punctuation\">(</span><span class=\"token function\">File</span><span class=\"token punctuation\">(</span>videoPath<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    videoPlayerListener <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>videoController <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">&amp;&amp;</span> videoController<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>size <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">// Refreshing the state to update video player with the correct ratio.</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>mounted<span class=\"token punctuation\">)</span> <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        videoController<span class=\"token punctuation\">.</span><span class=\"token function\">removeListener</span><span class=\"token punctuation\">(</span>videoPlayerListener<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n    vcontroller<span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span>videoPlayerListener<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">await</span> vcontroller<span class=\"token punctuation\">.</span><span class=\"token function\">setLooping</span><span class=\"token punctuation\">(</span><span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">await</span> vcontroller<span class=\"token punctuation\">.</span><span class=\"token function\">initialize</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">await</span> videoController<span class=\"token operator\">?</span><span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>mounted<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        imagePath <span class=\"token operator\">=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n        videoController <span class=\"token operator\">=</span> vcontroller<span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">await</span> vcontroller<span class=\"token punctuation\">.</span><span class=\"token function\">play</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  Future<span class=\"token operator\">&lt;</span>String<span class=\"token operator\">&gt;</span> <span class=\"token function\">takePicture</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>controller<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>isInitialized<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">showInSnackBar</span><span class=\"token punctuation\">(</span><span class=\"token string\">'错误: 请先选择一个相机'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">return</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">final</span> Directory extDir <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">getApplicationDocumentsDirectory</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">final</span> String dirPath <span class=\"token operator\">=</span> <span class=\"token string\">'${extDir.path}/Pictures/flutter_test'</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">await</span> <span class=\"token function\">Directory</span><span class=\"token punctuation\">(</span>dirPath<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">create</span><span class=\"token punctuation\">(</span>recursive<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">final</span> String filePath <span class=\"token operator\">=</span> <span class=\"token string\">'$dirPath/${timestamp()}.jpg'</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>controller<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span>isTakingPicture<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">// A capture is already pending, do nothing.</span>\n      <span class=\"token keyword\">return</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">await</span> controller<span class=\"token punctuation\">.</span><span class=\"token function\">takePicture</span><span class=\"token punctuation\">(</span>filePath<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">on</span> CameraException <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">_showCameraException</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">return</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">return</span> filePath<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_showCameraException</span><span class=\"token punctuation\">(</span>CameraException e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">logError</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">.</span>code<span class=\"token punctuation\">,</span> e<span class=\"token punctuation\">.</span>description<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token function\">showInSnackBar</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Error: ${e.code}\\n${e.description}'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div></li></ol> <blockquote><p>如果代码运行遇到困难，请直接查看<a href=\"https://pub.dev/packages/camera\" target=\"_blank\" rel=\"noopener noreferrer\">camera官方文档<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>。</p></blockquote> <h2 id=\"_12-6-2-platformview-示例-webview\"><a href=\"#_12-6-2-platformview-示例-webview\" class=\"header-anchor\">#</a> 12.6.2 PlatformView （示例：WebView）</h2> <p>如果我们在开发过程中需要使用一个原生组件，但这个原生组件在Flutter中很难实现时怎么办（如webview）？这时一个简单的方法就是将需要使用原生组件的页面全部用原生实现，在flutter中需要打开该页面时通过消息通道打开这个原生的页面。但是这种方法有一个最大的缺点，就是原生组件很难和Flutter组件进行组合。</p> <p>在 Flutter 1.0版本中，Flutter SDK中新增了<code>AndroidView</code>和<code>UIKitView</code> 两个组件，这两个组件的主要功能就是将原生的Android组件和iOS组件嵌入到Flutter的组件树中，这个功能是非常重要的，尤其是对一些实现非常复杂的组件，比如webview，这些组件原生已经有了，如果Flutter中要用，重新实现的话成本将非常高，所以如果有一种机制能让Flutter共享原生组件，这将会非常有用，也正因如此，Flutter才提供了这两个组件。</p> <p>由于<code>AndroidView</code>和<code>UIKitView</code> 是和具体平台相关的，所以称它们为PlatformView。需要说明的是将来Flutter支持的平台可能会增多，则相应的PlatformView也将会变多。那么如何使用Platform View呢？我们以Flutter官方提供的<a href=\"https://github.com/flutter/plugins/tree/master/packages/webview_flutter\" target=\"_blank\" rel=\"noopener noreferrer\">webview_flutter插件<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>为例：</p> <blockquote><p>注意，在本书写作之时，webview_flutter仍处于预览阶段，如您想在项目中使用它，请查看一下webview_flutter插件最新版本及动态。</p></blockquote> <ol><li><p>原生代码中注册要被Flutter嵌入的组件工厂，如webview_flutter插件中Android端注册webview插件代码：</p> <div class=\"language-java extra-class\"><pre class=\"language-java\"><code><span class=\"token keyword\">public</span> <span class=\"token keyword\">static</span> <span class=\"token keyword\">void</span> <span class=\"token function\">registerWith</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">Registrar</span> registrar<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n   registrar<span class=\"token punctuation\">.</span><span class=\"token function\">platformViewRegistry</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">registerViewFactory</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;webview&quot;</span><span class=\"token punctuation\">,</span> \n   <span class=\"token class-name\">WebViewFactory</span><span class=\"token punctuation\">(</span>registrar<span class=\"token punctuation\">.</span><span class=\"token function\">messenger</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p><code>WebViewFactory</code>的具体实现请参考webview_flutter插件的实现源码，在此不再赘述。</p></li> <li><p>在Flutter中使用；打开Flutter中文社区首页。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">PlatformViewRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">WebView</span><span class=\"token punctuation\">(</span>\n      initialUrl<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;https://flutterchina.club&quot;</span><span class=\"token punctuation\">,</span>\n      javascriptMode<span class=\"token punctuation\">:</span> JavascriptMode<span class=\"token punctuation\">.</span>unrestricted<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行效果如图12-5所示：</p> <p><img src=\"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QCMRXhpZgAATU0AKgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABAAAASgEbAAUAAAABAAAAUgEoAAMAAAABAAIAAIdpAAQAAAABAAAAWgAAAAAAAABIAAAAAQAAAEgAAAABAAOgAQADAAAAAQABAACgAgAEAAAAAQAAAKmgAwAEAAAAAQAAAUAAAAAA/+0AOFBob3Rvc2hvcCAzLjAAOEJJTQQEAAAAAAAAOEJJTQQlAAAAAAAQ1B2M2Y8AsgTpgAmY7PhCfv/AABEIAUAAqQMBIgACEQEDEQH/xAAfAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgv/xAC1EAACAQMDAgQDBQUEBAAAAX0BAgMABBEFEiExQQYTUWEHInEUMoGRoQgjQrHBFVLR8CQzYnKCCQoWFxgZGiUmJygpKjQ1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4eLj5OXm5+jp6vHy8/T19vf4+fr/xAAfAQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgv/xAC1EQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/3QAEAAv/2gAMAwEAAhEDEQA/AG0UVs29ppzaAZjclZ2k2zt9mMhgH8IABGAf73fpX29SoqaTfU+Ip03NtIxqKdIEWVljcyID8rldu4euO1NqyBCwBwSAfrS5B79K6LR57a10MzXDoi/2htcG1ExlTywSnP3c881cg0uz1WWyZoHW3+yRbUichk3yOMnHJwAOTx61yTxag3zLRdTqjhXNLlerOQJAGSQB70tdbBFZ6dGk8NmTJFp903nCT70iPtz0xux+WaZH4esDcxRyC4WLzEVZvN4ugYi5Kf3cEdqX16Gra0H9Tnok1c5WjI556Vviz0r+zVujaXBZrE3m0XRxw4XZ09D168VdjsrG3mutyq6aNO8pD43SxMm5VP8Aew/H0pvGRWyf9afnZErCy6tf1/wNTkwQehBorrbfRbS+iimuXkeQ2tvujhGCm4MS2FHIzxzx6moY9E0prKO7NxJsMJuiu/B8pVw/478Y9jS+vU9mmV9TqWumjli6A4LqD7mlLAdSB9TXS20ggtFjk82No1hDDzFKoXJGCdhxjAP41Lp0kFtYvuWSEg3HmgnPKsMndjqAAo9cmiWLaTfL+IlhbtLmOWorX1W0kuLuNrO3Z4Rbpt8peB94jH4A/lWVJHJDI0cqMjr1VhyOM/yNdFOopq/UwnTcHYbRRRWpmFFFFABRRRQB/9BtKCRnBIyMHB6j39a9U/4V9on/ADzm/wC/zf40f8K+0T/nnN/3+b/Gvpv7Xw/Z/cv8z5n+yMR3X3v/ACPKqK9V/wCFfaJ/zzm/7/N/jR/wr7Q/+ec3/f5qP7Xw/Z/cv8w/sjEeX3v/ACPKqOfU9McHtXpNx4T8K2svlTytHJx8pnbPNW1+H+hsoIjmwf8Aps1N5rQSu0/u/wCCSsrrSbScW15/8A8syfU/nSc8deOnPT6V6r/wr7RP+ec3/f5v8aP+FfaJ/wA85v8Av83+NL+18P2f3L/Mr+yMR5fe/wDI8r596Tn3r1X/AIV9on/POb/v83+NH/CvtE/55zf9/m/xo/tfD9n93/BD+yMR3X3v/I8r5HQkcY69qTFeq/8ACvtE/wCec3/f5v8AGj/hX2if885v+/zf40f2vh+z+5f5h/ZGI8vvf+R5Z5knleVvfys7tm47c+uOmaN7+X5e5tn93Jx+Vep/8K+0T/nnN/3+b/Gj/hX2if8APOb/AL/N/jS/tbDdn9y/zD+yMT3X3v8AyPLGkkdtzySM2MZZiTim8nrk16r/AMK+0T/nnN/3+b/Gj/hX2if885v+/wA3+NP+18P2f3L/ADD+yMR3X3v/ACPKqK9V/wCFfaJ/zzm/7/N/jR/wr7RP+ec3/f5v8aP7Xw/Z/cv8w/sjEeX3v/I8qor1X/hX2if885v+/wA3+NH/AAr7Q/8AnnN/3+b/ABo/tfD9n9y/zD+yMR5fe/8AI8qor1X/AIV9of8Azzm/7/N/jR/wr7RP+ec3/f5v8aP7Xw/Z/cv8w/sjEeX3v/I//9H3+sM+JIwSPJTj/p7h/wDiq3KzDoqnP+nXf5p/8TQBoRSebCkmMblDYyD19xWPrmuLp6eTCQ10w6ddgPc/0FbMabI1TJO0AZPU1Rv9PSU/aoYYmvY1PlNJ0z2zWlJwU7zWhhiVUdNqk7P+tvMxNJ0+C1m+2apPGty3zrHK4yuf4j7/AMq29U1IabZpOBGweVIw0kmxRuOMlvSvONI8RafF4ybTfEDOl5vCrJPgL5x6K35jaenb0r064tI7tYg5IEcqyjHcjpW+Mi4z1d/y+Ry5Z/C0jZfj8/Mp2usrLdLBKIgXVDG0UnmK5YMcA46YQ81XfxRarIVS3upVzAFdIiQwlbCkfTFW7nSI57n7Ss0sUwKFWTHy7Qw6EY5DkVAnh2COJI0uLgBEhUHIJzE25W6dfXsa5D0SR/EOnoXBlY7WCjbGx3kts+XA+b5uOKhk1yb7YkcNjK8ZMqli6AkpjOAT/OnQ+HLSCQmMsq+asqqFUbSH34yBkjPqamm0HTp7xLh7O3JBcvmJTvLAZJP4UAV7nXJUWJ4rMmJ4hcb5JFUGPALY5+8ARxTbjXJ4ngQ2nlNKitslyWBYkBflzzgE/gas3Wiw3hRZnfyISrQRR4QRsOjepI7Z49qR9EjuZBJeTSTuIvLB+53JDcfxDPB7daAJNI1JtSgeUqgUMNjISQykAg8/WtGqWnacmnpMsZG2STcFVdqoMAAAfhV2gAooooAKKKKACmmnUhFAFazvUu5LpFinQ28vlMZYigY4BypP3l56jjOfSrVZek2E1nc6pJLHEi3N2ZozHK7ll2KMsG4U5B4Xjv1JrUoA/9L3OO/81iFtp+PUD/GpPtL/APPtN+Q/xqCx/wBY/wBBV+gCv9pf/n2m/If40faH/wCfab8h/jViigDJu9O06/l8y80WO4fbt3Swoxx6ZNXVmKKFW1mCgYAAHH61Zop3YrIr/aX/AOfab8h/jR9pf/n2m/If41YopDK/2l/+fab8h/jR9pf/AJ9pvyH+NWKKAK/2l/8An2m/If40faX/AOfab8h/jViigCv9pf8A59pvyH+NH2l/+fab8h/jViigCv8AaX/59pvyH+NH2l/+fab8h/jViigCv9pf/n2m/If40faX/wCfab8h/jViigCv9pf/AJ9pvyH+NH2l/wDn2m/If41YooAr/aX/AOfab8h/jUf28/8APtP+Q/xq5VGgD//T9wsf9Y/0FX6oWP8ArH+gq/TYBRVA3tyt55BtlKqpd2STO1eccY5Jx0rNsvE321kC220lGZl3ZYYHYY5HT86QHQ0ViSa+Y2ud1sQkEQkL7sg5xj+Z/KoU8SlhOfIQeX0HmH1I5446HpmgDoaKwxr+QgNvsZ2wA74ABBIzgHBwOR71Y/tmJYJXdH3xkgooznG7GD77TVcrepDqRTs2alFZI121U4mEkJL7QHUc8Kc8Hp8wFRT+IFjH7qB33R70B4LfMR069qOSXYTrQ7m3RWNd66tu1qqW7yNcfcxjBGM9e3pk4HNST6x5V+LXyScsgySM/N1474yP19KOSQe1h3NWisgavJ/aAtfs4x5nlli/T07VrDpSaa3KjJS2FooopFBRRRQAUUUUAFUavVRoA//U9wsf9Y/0FX6oWP8ArH+gq/TYEH2VPNmkyxaVQpy3QDPA9Opqrb6JYWrh4bdUYBgCD0z1rRopAUE0exjDbLdV3KqEjqQPfrTE0OyQEBJcHt5zepPrx1rSooAqpYQJP5oUlwQVJP3cDaMfhUbaTZtL5hh+bkn5jg5znIzz94/nV6imm0JxT3RSGlWgZWEZ3Kcg72z0A9fYflTZNItZUCOhMYQJs3EAqDnH51foo5n3FyR7FP8As63ON8YcjqW5LcY59eDTG0m3aUvhxn7wDnBq/RRzMOSPYo/2XCbjz8yeZuyTv685xV4UUUXuNJLYKKKKQwooooAKKKKACqNXqo0Af//V9n5ByCQfUHFG5/8AnrJ/30aKKoQbn/56yf8AfRo3P/z1k/76NFFABuf/AJ6yf99Gjc//AD1k/wC+jRRQAbn/AOesn/fRo3P/AM9ZP++jRRQAbn/56yf99Gjc/wDz1k/76NFFABuf/nrJ/wB9Gjc//PWT/vo0UUCDc/8Az1k/76NG5/8AnrJ/30aKKADc/wDz1k/76NG5/wDnrJ/30aKKBhuf/nrJ/wB9Gjc//PWT/vo0UUAG5/8AnrJ/30aNz/8APWT/AL6NFFABl/8Ano//AH0aTaPelooEf//W9nooozVEhRRmjNABRRmjNABRRmjNABRRmjNABRRmjNABRRmjNABRRmjNABRRmjNABRRmjNABRRmigD//1/cLHBkfjsKvYHpVGx/1j/QVfpsBMD0owPSqr6rp8f3762XnHMyj+tWDNGsixl1DsCVXPJA6/wA6QDsD0owPSmNPEsixtIgdgSqk8kDrTY7q3lEZjnjcSDKbWB3fT1oAlwPSjA9KiF3bMWAnjyr+WfmHDf3fr7UpuoBI8ZmjDoAXUsAVB6E0ASYHpRgelMSeKQ4SVGOM4VgeKfkUAGB6UYHpS5FFACYHpRgelI0iIyqzqpc4UE9T1wKZPdW9su6eaOJcE5dgo45PWgCTA9KMD0qtFqVjPG8kN5byIhCsySAhSegOOmaE1KxkWRkvLdliXdIVlBCD1PPA4NAFnA9KMD0qGO9tZZfKjuYXkwDtWQE4PTinC4hYqBKhLEqAG6kdR+FAEmB6UYHpRuXdt3DdjOM84paAE2j0FUdq/wB0VfqjQB//0PcLH/WP9BV49Ko2P+sf6Cr9NgcHd2F0vmqbe6CSzS+YzRqdsT7gGwg55I+Xk8ZOKfqto5vbu6e2h853hEB/s95GWM7S4JXPzZByfTA7ZruaKQHF31m8moWc9vbO0a244ihMfIyBtB5XlhnPOBx0NNt7a6ttA0+GK3upZ4leKdRESplEf3wGIONwAGG7967ajFAHG2ttJa2NxGtldrci5ikmVY8+arsCRySDjLdDxgc5qm+mXEsN1E9hN9sufJZXeElAizKGBBYjoA2CckZrvsUYHpQBzE9neadeRy28LMqJEJGtoQoZfOJcBR/snnHJrOuLHUbuO4nnXUxJLbcKjlSAs7MFwDgNsIx613FGKAOQnXX5bi9CzXMYJdUEcf8AAXXy2Vj8oYLuzwcknPQV0c07Wf2SFY5JvMkERbOSowTuPr0/WrmBRQBi6zFJqckWnQiWIBlnkugMeVtOVCnuxYDgds56jKW0s95qNk11bPG8UUqyqynaJAU5B6EEZwfQn3rbooA5fUYZLm9uHt1YLHNbKxEJycMQwU9uG5YZ4rL0+yuFtJRLBMyvaxpHvgcZXeCRtYnBXPtnggYFd5RQBwuj6dd2mqRvNZSRSfvAiqB9wq2R3A5CY5FQ6XoWo22tRO9nKBbmFjJ5i89dx+p4zg9uc8V6BRgUAZjWqnxHb3RjclLSWMSbflG50OM+p2jj2rToooAKo1eqjQB//9H3Cx/1j/QVeJwM1Rsf9Y/0FW5/+PeT/cP8qfUXQym8V6ErFW1W0BBwf3lJ/wAJboH/AEFrX/vuvEQSFAHAwKNx9a+h/san/Mz5b+36v8i/E9u/4S3QP+gta/8AfdH/AAlugf8AQWtf++68RyfWjJ9aP7Gp/wAzD/WCr/IvxPbv+Et0D/oLWv8A33R/wlugf9Ba1/77rxHJ9aMt6mj+xqf8zD+36v8AIvxPbv8AhLdA/wCgta/990f8JboH/QWtf++68R3H1o3H1o/san/Mw/1gq/yL8T3BPFWhySLGmq2pdyFUeZ1JrXBzXz3CT5yc/wAS/wAxX0Gn3R9BXm4/Bxwzjyu9z1ssx8sWpOStYdRRRXnnqBRRRQAUUUUAFFFFABVGr1UaAP/S9wsf9Y/0FW5/+PeT/dP8qqWP+sf6Crc//HvJ/uH+VPqLofPXYfQUUdh9BRX3J+bvcu6VHHJqUKzBTHnL7k3DaOueRxjPPatZINOubiEFIwj2hc7U8rBLYBzk89gD3rnKKwqUXOV1Kx0Uq6hHlcbnVDTtPXUCht2+dCAjjCjDhc46+nP1NOFtpY1KfdbRpEzBIw8ZwSA5O32wF56Vyf4UVj9Uk95s2WMitoIvatFHFqUqRIEQBSABjGVBPH1qjSnJ60ldcI8sUn0OOcuaTkiSH/XJ/vL/ADFfQafdH0FfPkP+uT/eX+Yr6DT7o+grw863h8/0PpOHtqny/UdRRRXhn0YUUUUAFFFFABRRRQAVRq9VGgD/0/cLH/WP9BV51DoVPQjBqjY/6x/oKv02BxbfDbSix2z3KrngbuntSf8ACtdM/wCfm6/76rtaK6PrmI/nZyfUMN/IvuOK/wCFa6Z/z83X/fVH/CtdM/5+br/vqu1oo+uYj+dh9Qw3/PtfccV/wrXTP+fm6/76o/4Vrpn/AD83X/fVdrRR9cxH87D6hhv+fa+44r/hWumf8/N1/wB9Uf8ACtdM/wCfm6/76rtaKPrmI/nYfUMN/wA+19xxkfw40uOVHM9ywVgdpbg4OcV2QGBS0VlUrVKnxu5tSoUqV/ZxSuFFFFZmoUUUUAFFFFABRRRQAVRq9VGgD//U9utJEikbewUEDGat/a4P+eq/nWbRgelOwrml9rg/56r+dH2uD/nqv51m4HpRgelFguaX2uD/AJ6r+dH2uD/nqv51m4HpRgelFguaX2uD/nqv50fa4P8Anqv51m4HpRgelFguaX2uD/nqv50fa4P+eq/nWbgelGB6UWC5pfa4P+eq/nR9rg/56r+dZuB6UYHpRYLml9rg/wCeq/nR9rg/56r+dZuB6UYHpRYLml9rg/56r+dH2uD/AJ6r+dZuB6UYHpRYLml9rg/56r+dH2uD/nqv51m4HpRgelFguaX2uD/nqv50fa4P+eq/nWbgelGB6UWC5pfa4P8Anqv51S89PU/lUWB6UUWC5//V9noooqiQooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//W9noooqiQooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//Xu/8ACYa7/wBBF/8Av2n+FH/CYa7/ANBF/wDv2n+FYVFfbfVaH8i+5HwP1zEfzv72bv8AwmGu/wDQRf8A79p/hR/wmGu/9BF/+/af4VhU+KJ5pVjjUs7HAApfVaH8i+5B9cxH87+9m1/wmGvf9BF/+/af4Uf8Jhr3/QRf/v2n+FW7XQ7aOECdPMk6k5IH0FNu7LTLOAyywD/ZGTkmseTDXsqa+5F/WsR/O/vZW/4TDXf+gi//AH7T/Cj/AITDXf8AoIv/AN+0/wAKxJGDyMyoEUnhR2ptb/VaH8i+5EfXMR/O/vZu/wDCYa7/ANBF/wDv2n+FH/CYa7/0EX/79p/hWFRR9VofyL7kH1zEfzv72bv/AAmGu/8AQRf/AL9p/hR/wmGu/wDQRf8A79p/hWFRR9VofyL7kH1zEfzv72bv/CYa7/0EX/79p/hR/wAJhrv/AEEX/wC/af4VhUYo+q0P5F9yD65iP5397N3/AITDXf8AoIv/AN+0/wAKP+Ew13/oIv8A9+0/wrCoo+q0P5F9yD65iP5397N3/hMNd/6CL/8AftP8KP8AhMNd/wCgi/8A37T/AArCowaPqtD+Rfcg+uYj+d/ezd/4TDXf+gi//ftP8KP+Ew13/oIv/wB+0/wrCoo+q0P5F9yD65iP5397N3/hMNd/6CL/APftP8KP+Ew13/oIv/37T/CsKij6rQ/kX3IPrmI/nf3s/9DPoop8UTzSrHGpZ2OABX3p+cBFE80qxxqWdjgAV1enaalhFk4aZh8zf0HtRpunR2EeThpmHzN/Qe1T3d3FZwGWU8dlHUn0riq1XN8sdjVKwXd3FZwGWU/QDqx9BXJXl3LezmWU/wC6vZRT7ma61GYzGN2A4AVSQvtUP2W4/wCfeX/vg1tSpqGr3JbuRUUrKyMVZSrDqCMGkrcg1zb6culJIWzdgKzoGydueuM456Y69Knig0htVEUmI4FQbsyFcsRnGeaZLpVqmlG4WUmZYhIV3c5OO3pzUNppttcQRl7gpLIZABkAAKOCf/115/NFxcuZ9T0OWSaXKuhPHZabJbyFZoy4D4Pm8n+6eceh/Oq+k29nPHN9r2jhdmZdnPOR+PAq0vh5SoV7oLMTghkG0cgcnPoc/lTI9HtElaO4vQWEZcBQF6BvXPpzR7SHLJKb/Efs58ybgvwIZLSzXKrIgkEpYL5mQ0WQME+vUjvitC6stIWQtFsKqknAlGNwxgHnPTNVF0u1e1MqvcP+92KVVcMN2NwH93HO7t0qebR7CLDiYmNd+9g/C7RnsPXiplON177KjB2fuozfKtjqEKy7YomVDIFbABK5IzzjnH0zV5rLSNhJugrjkiOUccHgZ684FTQaLZSNdZkkCROVRg3BIGT25qtFpVsZLZXnkzLKq7QhzgpnPt/QVTqxk/iasTGnJfZTuRXNlp0dpJJBfmSRThY/lyeevH+RS2trp01pB50yRSsSHbzcEfMeNp4A2859cetQ6nZxWTxKruysCWLDHRsdK1P7BtPNtIzOymWRlb5weAM8D1pyqJQV5vXUmMG5tKKK32XSIpNrXTSgMoLbxggg9hz1Az9acLfSTc3AUAxD5ULTlctk/KufwwTx1qxbaFYSXk0UlxOqxthegLZUEfzqpe6Xa2+ix3scrs7AHBdSBnpx6cGoVSEnbneppySSvyLS5juMMRjb7Zzim1PdQiCcxqSQFU8+4zUFejF3V0edJWdj/9HPqzY3r2NwJVAYdGX1FVqK+8aTVmfnKdjsH1O2SyF1vyp6KOpPpWBmbVrzzJmKxA446KPQe9Z4xkZzjPOK3rdojAvk42dqwVNU9hyka8EUcEKxwqAgHGKz9U1UWoMMJBnI5P8Ad/8Ar1mX/iKPTn+yo26RvvHr5fv/APWrNLbzuLbi3Oc5zUU6alJ3ZbUlFNrcGYsxZiSTySe9JRRXWZGxDoU0yKVnUbgpbKnaMgEc9+tH/CPXJnRVeNo2IG8Z44BORjtmswXM6qqiaQKv3QHOB9OeKUXdwucTyjPXEjc/rXNyV/5l9x1e0odYv7xbq1a0mMT7TwCCvIIPQ1AOOnFOeR5XLuzMx6sxyT+JptdEU7a7nPJpvTYXcc574xRuOMZOPTNJRTsK7FyfU/nSZOMUUUWC7FLM3Uk/U5pB8pyOD6iiiiwXYu5s5yc+uaNzbduTj0pKKLBdis7O25mLHpk0lFFAj//Sz6KKK+9PzgKkjnliVxE5TcMZxnB9frUdFDVxp2OduYZIJmEuSx53dd3vVixv/IIilyYj0P8Ad/8ArVtZpcn1rnVBxd0zsli1KPLKImQeQcg9DRR1oroONmvDohntYJluFzKVAUp0z75oXRCVlJuo18kDzCyEKpI9T29x+VZ4vLkKqieQBemGxj/OBTXuJpYxHJLIyAABWYkYHTiufkrfzHT7Sjb4TWm8PPCLQtcJ/pDBeV4RiMgdefwqKHQriS1indljEqkqGU59s/Xr7VSkv7uREV7mVghBUFvunpxUSXE0aFEldVIIIDEDB6/nSUK9tZa+gOdC+kS+ujyPFLMJFMEYjbzAjHIfHQdeARmnHR8T+SblBIZTGAYzgkLuyDnkYx+JrOW4mRtyyyBsAZDEHjpQ1xMzhzLIWBJDFjkZ61XJW/m/Anno/wApffR2Gmw3UcodpQpCDA6++e1Vby0Nq0Y3h1kjEgIUjjOMYPeoRI4gMIY+WW3FexNEs0kzbpZHdsYyzEnFVGNRP3noTOVNr3UMooorUxCiiigAooooA//Z\" alt=\"12-5\"></p></li></ol> <p>注意，使用PlatformView的开销是非常大的，因此，如果一个原生组件用Flutter实现的难度不大时，我们应该首选Flutter实现。</p> <p>另外，PlatformView的相关功能在作者写作时还处于预览阶段，可能还会发生变化，因此，读者如果需要在项目中使用的话，应查看一下最新的文档。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/72.2c5ab8cf.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter13/faq.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>13.4 国际化常见问题 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/194.df254afb.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter13/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_13-4-国际化常见问题\"><a href=\"#_13-4-国际化常见问题\" class=\"header-anchor\">#</a> 13.4 国际化常见问题</h1> <p>本节主要解答一下在国际化中常见的问题。</p> <h3 id=\"默认语言区域不对\"><a href=\"#默认语言区域不对\" class=\"header-anchor\">#</a> 默认语言区域不对</h3> <p>在一些非大陆行货渠道买的一些Android和iOS设备，会出现默认的Locale不是中文简体的情况。这属于正常现象，但是为了防止设备获取的Locale与实际的地区不一致，所有的支持多语言的APP都必须提供一个手动选择语言的入口。</p> <h3 id=\"如何对应用标题进行国际化\"><a href=\"#如何对应用标题进行国际化\" class=\"header-anchor\">#</a> 如何对应用标题进行国际化</h3> <p><code>MaterialApp</code>有一个<code>title</code>属性，用于指定APP的标题。在Android系统中，APP的标题会出现在任务管理器中。所以也需要对<code>title</code>进行国际化。但是问题是很多国际化的配置都是在<code>MaterialApp</code>上设置的，我们无法在构建<code>MaterialApp</code>时通过<code>Localizations.of</code>来获取本地化资源，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">MaterialApp</span><span class=\"token punctuation\">(</span>\n  title<span class=\"token punctuation\">:</span> DemoLocalizations<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>title<span class=\"token punctuation\">,</span> <span class=\"token comment\">//不能正常工作！</span>\n  localizationsDelegates<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>\n    <span class=\"token comment\">// 本地化的代理类</span>\n    GlobalMaterialLocalizations<span class=\"token punctuation\">.</span>delegate<span class=\"token punctuation\">,</span>\n    GlobalWidgetsLocalizations<span class=\"token punctuation\">.</span>delegate<span class=\"token punctuation\">,</span>\n    <span class=\"token function\">DemoLocalizationsDelegate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// 设置Delegate</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>上面代码运行后，<code>DemoLocalizations.of(context).title</code> 是会报错的，原因是<code>Localizations.of</code>会从当前的context沿着widget树向顶部查找<code>DemoLocalizations</code>，但是我们在<code>MaterialApp</code>中设置完<code>DemoLocalizationsDelegate</code>后，实际上<code>DemoLocalizations</code>是在当前context的子树中的，所以<code>DemoLocalizations.of(context)</code>会返回null，报错。那么我们该如何处理这种情况呢？其实很简单，我们只需要设置一个<code>onGenerateTitle</code>回调即可：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">MaterialApp</span><span class=\"token punctuation\">(</span>\n  onGenerateTitle<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 此时context在Localizations的子树中</span>\n    <span class=\"token keyword\">return</span> DemoLocalizations<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>title<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  localizationsDelegates<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>\n    <span class=\"token function\">DemoLocalizationsDelegate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><h3 id=\"如何为英语系的国家指定同一个locale\"><a href=\"#如何为英语系的国家指定同一个locale\" class=\"header-anchor\">#</a> 如何为英语系的国家指定同一个locale</h3> <p>英语系的国家非常多，如美国、英国、澳大利亚等，这些英语系国家虽然说的都是英语，但也会有一些区别。如果我们的APP只想提供一种英语（如美国英语）供所有英语系国家使用，我们可以在前面介绍的<code>localeListResolutionCallback</code>中来做兼容：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>localeListResolutionCallback<span class=\"token punctuation\">:</span>\n    <span class=\"token punctuation\">(</span>List<span class=\"token operator\">&lt;</span>Locale<span class=\"token operator\">&gt;</span> locales<span class=\"token punctuation\">,</span> Iterable<span class=\"token operator\">&lt;</span>Locale<span class=\"token operator\">&gt;</span> supportedLocales<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// 判断当前locale是否为英语系国家，如果是直接返回Locale('en', 'US')     </span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/194.df254afb.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter13/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>本章目录 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/195.1c40c74b.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter13/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h2 id=\"本章目录\"><a href=\"#本章目录\" class=\"header-anchor\">#</a> 本章目录</h2> <ul><li><a href=\"/v2/chapter13/multi_languages_support.html\">13.1：让App支持多语言</a></li> <li><a href=\"/v2/chapter13/locallization_implement.html\">13.2：实现Localizations</a></li> <li><a href=\"/v2/chapter13/intl.html\">13.3：使用Intl包</a></li> <li><a href=\"/v2/chapter13/faq.html\">13.4：国际化常见问题</a></li></ul></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/195.1c40c74b.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter13/intl.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>使用Intl包 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/196.3dbd36a7.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter13/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"使用intl包\"><a href=\"#使用intl包\" class=\"header-anchor\">#</a> 使用Intl包</h1> <p>使用<a href=\"https://pub.dartlang.org/packages/intl\" target=\"_blank\" rel=\"noopener noreferrer\">Intl<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>包我们不仅可以非常轻松的实现国际化，而且也可以将字符串文本分离成单独的文件，方便开发人员和翻译人员分工协作。为了使用<a href=\"https://pub.dartlang.org/packages/intl\" target=\"_blank\" rel=\"noopener noreferrer\">Intl<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>包我们需要添加两个依赖：</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code><span class=\"token key atrule\">dependencies</span><span class=\"token punctuation\">:</span>\n  <span class=\"token comment\">#...省略无关项</span>\n  <span class=\"token key atrule\">intl</span><span class=\"token punctuation\">:</span> ^0.15.7 \n<span class=\"token key atrule\">dev_dependencies</span><span class=\"token punctuation\">:</span>\n   <span class=\"token comment\">#...省略无关项</span>\n  <span class=\"token key atrule\">intl_translation</span><span class=\"token punctuation\">:</span> ^0.17.2  \n</code></pre></div><p><a href=\"https://pub.dartlang.org/packages/intl_translation\" target=\"_blank\" rel=\"noopener noreferrer\">intl_translation<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 包主要包含了一些工具，它在开发阶段主要主要的作用是从代码中提取要国际化的字符串到单独的arb文件和根据arb文件生成对应语言的dart代码，而intl包主要是引用和加载intl_translation生成后的dart代码。下面我们将一步步来说明如何使用：</p> <h3 id=\"第一步-创建必要目录\"><a href=\"#第一步-创建必要目录\" class=\"header-anchor\">#</a> 第一步：创建必要目录</h3> <p>首先，在项目根目录下创建一个l10n-arb目录，该目录保存我们接下来通过intl_translation命令生成的arb文件。一个简单的arb文件内容如下：</p> <div class=\"language-json extra-class\"><pre class=\"language-json\"><code><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;@@last_modified&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;2018-12-10T15:46:20.897228&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;@@locale&quot;</span><span class=\"token operator\">:</span><span class=\"token string\">&quot;zh_CH&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;title&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;Flutter应用&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;@title&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">&quot;description&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;Title for the Demo application&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;type&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;text&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;placeholders&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们根据&quot;@@locale&quot;字段可以看出这个arb对应的是中文简体的翻译，里面的<code>title</code>字段对应的正是我们应用标题的中文简体翻译。<code>@title</code>字段是对<code>title</code>的一些描述信息。</p> <p>接下来，我们在lib目录下创建一个l10n的目录，该目录用于保存从arb文件生成的dart代码文件。</p> <h3 id=\"第二步-实现localizations和delegate类\"><a href=\"#第二步-实现localizations和delegate类\" class=\"header-anchor\">#</a> 第二步：实现Localizations和Delegate类</h3> <p>和上一节中的步骤类似，我们仍然要实现<code>Localizations</code>和Delegate类，不同的是，现在我们在实现时要使用intl包的一些方法（有些是动态生成的）。</p> <p>下面我们在<code>lib/l10n</code>目录下新建一个“localization_intl.dart”的文件，文件内容如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:intl/intl.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'messages_all.dart'</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//1</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">DemoLocalizations</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">static</span> Future<span class=\"token operator\">&lt;</span>DemoLocalizations<span class=\"token operator\">&gt;</span> <span class=\"token function\">load</span><span class=\"token punctuation\">(</span>Locale locale<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">final</span> String name <span class=\"token operator\">=</span> locale<span class=\"token punctuation\">.</span>countryCode<span class=\"token punctuation\">.</span>isEmpty <span class=\"token operator\">?</span> locale<span class=\"token punctuation\">.</span>languageCode <span class=\"token punctuation\">:</span> locale<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">final</span> String localeName <span class=\"token operator\">=</span> Intl<span class=\"token punctuation\">.</span><span class=\"token function\">canonicalizedLocale</span><span class=\"token punctuation\">(</span>name<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//2</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">initializeMessages</span><span class=\"token punctuation\">(</span>localeName<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>b<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      Intl<span class=\"token punctuation\">.</span>defaultLocale <span class=\"token operator\">=</span> localeName<span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">DemoLocalizations</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">static</span> DemoLocalizations <span class=\"token function\">of</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> Localizations<span class=\"token punctuation\">.</span>of<span class=\"token operator\">&lt;</span>DemoLocalizations<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> DemoLocalizations<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  String <span class=\"token keyword\">get</span> title <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> Intl<span class=\"token punctuation\">.</span><span class=\"token function\">message</span><span class=\"token punctuation\">(</span>\n      <span class=\"token string\">'Flutter APP'</span><span class=\"token punctuation\">,</span>\n      name<span class=\"token punctuation\">:</span> <span class=\"token string\">'title'</span><span class=\"token punctuation\">,</span>\n      desc<span class=\"token punctuation\">:</span> <span class=\"token string\">'Title for the Demo application'</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">//Locale代理类</span>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">DemoLocalizationsDelegate</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">LocalizationsDelegate</span><span class=\"token operator\">&lt;</span>DemoLocalizations<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function\">DemoLocalizationsDelegate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">//是否支持某个Local</span>\n  <span class=\"token metadata symbol\">@override</span>\n  bool <span class=\"token function\">isSupported</span><span class=\"token punctuation\">(</span>Locale locale<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">[</span><span class=\"token string\">'en'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'zh'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span><span class=\"token function\">contains</span><span class=\"token punctuation\">(</span>locale<span class=\"token punctuation\">.</span>languageCode<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// Flutter会调用此类加载相应的Locale资源类</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Future<span class=\"token operator\">&lt;</span>DemoLocalizations<span class=\"token operator\">&gt;</span> <span class=\"token function\">load</span><span class=\"token punctuation\">(</span>Locale locale<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//3</span>\n    <span class=\"token keyword\">return</span>  DemoLocalizations<span class=\"token punctuation\">.</span><span class=\"token function\">load</span><span class=\"token punctuation\">(</span>locale<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">// 当Localizations Widget重新build时，是否调用load重新加载Locale资源.</span>\n  <span class=\"token metadata symbol\">@override</span>\n  bool <span class=\"token function\">shouldReload</span><span class=\"token punctuation\">(</span>DemoLocalizationsDelegate old<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>注意：</p> <ul><li>注释1的&quot;messages_all.dart&quot;文件是通过<a href=\"https://pub.dartlang.org/packages/intl_translation\" target=\"_blank\" rel=\"noopener noreferrer\">intl_translation<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>工具从arb文件生成的代码，所以在第一次运行生成命令之前，此文件不存在。注释2处的<code>initializeMessages()</code>方法和&quot;messages_all.dart&quot;文件一样，是同时生成的。</li> <li>注释3处和上一节示例代码不同，这里我们直接调用<code>DemoLocalizations.load()</code>即可。</li></ul> <h3 id=\"第三步-添加需要国际化的属性\"><a href=\"#第三步-添加需要国际化的属性\" class=\"header-anchor\">#</a> 第三步：添加需要国际化的属性</h3> <p>现在我们可以在DemoLocalizations类中添加需要国际化的属性或方法，如上面示例代码中的<code>title</code>属性，这时我们就要用到Intl库提供的一些方法，这些方法可以帮我们轻松实现不同语言的一些语法特性，如复数语境，举个例子，比如我们有一个电子邮件列表页，我们需要在顶部显示未读邮件的数量，在未读数量不同事，我们展示的文本可能会不同：</p> <table><thead><tr><th>未读邮件数</th> <th>提示语</th></tr></thead> <tbody><tr><td>0</td> <td>There are no emails left</td></tr> <tr><td>1</td> <td>There is 1 email left</td></tr> <tr><td>n(n&gt;1)</td> <td>There are n emails left</td></tr></tbody></table> <p>我们可以通过<code>Intl.plural(...)</code>来实现：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">remainingEmailsMessage</span><span class=\"token punctuation\">(</span>int howMany<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Intl<span class=\"token punctuation\">.</span><span class=\"token function\">plural</span><span class=\"token punctuation\">(</span>howMany<span class=\"token punctuation\">,</span>\n    zero<span class=\"token punctuation\">:</span> <span class=\"token string\">'There are no emails left'</span><span class=\"token punctuation\">,</span>\n    one<span class=\"token punctuation\">:</span> <span class=\"token string\">'There is $howMany email left'</span><span class=\"token punctuation\">,</span>\n    other<span class=\"token punctuation\">:</span> <span class=\"token string\">'There are $howMany emails left'</span><span class=\"token punctuation\">,</span>\n    name<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;remainingEmailsMessage&quot;</span><span class=\"token punctuation\">,</span>\n    args<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>howMany<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    desc<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;How many emails remain after archiving.&quot;</span><span class=\"token punctuation\">,</span>\n    examples<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span><span class=\"token string\">'howMany'</span><span class=\"token punctuation\">:</span> <span class=\"token number\">42</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'userName'</span><span class=\"token punctuation\">:</span> <span class=\"token string\">'Fred'</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>可以看到通过<code>Intl.plural</code>方法可以在<code>howMany</code>值不同时输出不同的提示信息。</p> <p><a href=\"https://pub.dartlang.org/packages/intl\" target=\"_blank\" rel=\"noopener noreferrer\">Intl<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>包还有一些其他的方法，读者可以自行查看其文档，本书不在赘述。</p> <h3 id=\"第四步-生成arb文件\"><a href=\"#第四步-生成arb文件\" class=\"header-anchor\">#</a> 第四步：生成arb文件</h3> <p>现在我们可以通<a href=\"https://pub.dartlang.org/packages/intl_translation\" target=\"_blank\" rel=\"noopener noreferrer\">intl_translation<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>包的工具来提取代码中的字符串到一个arb文件，运行如下命名：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>flutter pub pub run intl_translation:extract_to_arb --output-dir<span class=\"token operator\">=</span>l10n-arb <span class=\"token punctuation\">\\</span> lib/l10n/localization_intl.dart\n</code></pre></div><p>运行此命令后，会将我们之前通过Intl API标识的属性和字符串提取到“l10n-arb/intl_messages.arb”文件中，我们看看其内容：</p> <div class=\"language-json extra-class\"><pre class=\"language-json\"><code><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;@@last_modified&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;2018-12-10T17:37:28.505088&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;title&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;Flutter APP&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;@title&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">&quot;description&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;Title for the Demo application&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;type&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;text&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;placeholders&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;remainingEmailsMessage&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;{howMany,plural, =0{There are no emails left}=1{There is {howMany} email left}other{There are {howMany} emails left}}&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;@remainingEmailsMessage&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">&quot;description&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;How many emails remain after archiving.&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;type&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;text&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;placeholders&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token property\">&quot;howMany&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token property\">&quot;example&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">42</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>这个是默认的Locale资源文件，如果我们现在要支持中文简体，只需要在该文件同级目录创建一个&quot;intl_zh_CN.arb&quot;文件，然后将&quot;intl_messages.arb&quot;的内容拷贝到&quot;intl_zh_CN.arb&quot;文件，接下来将英文翻译为中文即可，翻译后的&quot;intl_zh_CN.arb&quot;文件内容如下：</p> <div class=\"language-json extra-class\"><pre class=\"language-json\"><code><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;@@last_modified&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;2018-12-10T15:46:20.897228&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;@@locale&quot;</span><span class=\"token operator\">:</span><span class=\"token string\">&quot;zh_CN&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;title&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;Flutter应用&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;@title&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">&quot;description&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;Title for the Demo application&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;type&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;text&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;placeholders&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;remainingEmailsMessage&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;{howMany,plural, =0{没有未读邮件}=1{有{howMany}封未读邮件}other{有{howMany}封未读邮件}}&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;@remainingEmailsMessage&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">&quot;description&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;How many emails remain after archiving.&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;type&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;text&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;placeholders&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token property\">&quot;howMany&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token property\">&quot;example&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">42</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们必须要翻译<code>title</code>和<code>remainingEmailsMessage</code>字段，<code>description</code>是该字段的说明，通常给翻译人员看，代码中不会用到。</p> <p>有两点需要说明：</p> <ol><li>如果某个特定的arb中缺失某个属性，那么应用将会加载默认的arb文件(intl_messages.arb)中的相应属性，这是Intl的托底策略。</li> <li>每次运行提取命令时，intl_messages.arb都会根据代码重新生成，但其他arb文件不会，所以当要添加新的字段或方法时，其他arb文件是增量的，不用担心会覆盖。</li> <li>arb文件是标准的，其格式规范可以自行了解。通常会将arb文件交给翻译人员，当他们完成翻译后，我们再通过下面的步骤根据arb文件生成最终的dart代码。</li></ol> <h3 id=\"第五步-生成dart代码\"><a href=\"#第五步-生成dart代码\" class=\"header-anchor\">#</a> 第五步：生成dart代码</h3> <p>最后一步就是根据arb生成dart文件：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>flutter pub pub run intl_translation:generate_from_arb --output-dir<span class=\"token operator\">=</span>lib/l10n --no-use-deferred-loading lib/l10n/localization_intl.dart l10n-arb/intl_*.arb\n</code></pre></div><p>这句命令在首次运行时会在&quot;lib/l10n&quot;目录下生成多个文件，对应多种Locale，这些代码便是最终要使用的dart代码。</p> <h3 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h3> <p>至此，我们将使用<a href=\"https://pub.dartlang.org/packages/intl\" target=\"_blank\" rel=\"noopener noreferrer\">Intl<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>包对APP进行国际化的流程介绍完了，我们可以发现，其中第一步和第二步只在第一次需要，而我们开发时的主要的工作都是在第三步。由于最后两步在第三步完成后每次也都需要，所以我们可以将最后两步放在一个shell脚本里，当我们完成第三步或完成arb文件翻译后只需要分别执行该脚本即可。我们在根目录下创建一个intl.sh的脚本，内容为：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>flutter pub pub run intl_translation:extract_to_arb --output-dir<span class=\"token operator\">=</span>l10n-arb lib/l10n/localization_intl.dart\nflutter pub pub run intl_translation:generate_from_arb --output-dir<span class=\"token operator\">=</span>lib/l10n --no-use-deferred-loading lib/l10n/localization_intl.dart l10n-arb/intl_*.arb\n</code></pre></div><p>然后授予执行权限：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code><span class=\"token function\">chmod</span> +x intl.sh\n</code></pre></div><p>执行intl.sh</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>./intl.sh\n</code></pre></div></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/196.3dbd36a7.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter13/locallization_implement.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>13.2 实现Localizations | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/197.7fd8856f.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter13/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_13-2-实现localizations\"><a href=\"#_13-2-实现localizations\" class=\"header-anchor\">#</a> 13.2 实现Localizations</h1> <p>前面讲了Material组件库如何支持国际化，本节我们将介绍一下我们自己的UI中如何支持多语言。根据上节所述，我们需要实现两个类：一个<code>Delegate</code>类一个<code>Localizations</code>类，下面我们通过一个实例说明。</p> <h3 id=\"实现localizations类\"><a href=\"#实现localizations类\" class=\"header-anchor\">#</a> 实现Localizations类</h3> <p>我们已经知道<code>Localizations</code>类中主要实现提供了本地化值，如文本：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//Locale资源类</span>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">DemoLocalizations</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">DemoLocalizations</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>isZh<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">//是否为中文</span>\n  bool isZh <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">//为了使用方便，我们定义一个静态方法</span>\n  <span class=\"token keyword\">static</span> DemoLocalizations <span class=\"token function\">of</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> Localizations<span class=\"token punctuation\">.</span>of<span class=\"token operator\">&lt;</span>DemoLocalizations<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> DemoLocalizations<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token comment\">//Locale相关值，title为应用标题</span>\n  String <span class=\"token keyword\">get</span> title <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> isZh <span class=\"token operator\">?</span> <span class=\"token string\">&quot;Flutter应用&quot;</span> <span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;Flutter APP&quot;</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token comment\">//... 其它的值  </span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p><code>DemoLocalizations</code>中会根据当前的语言来返回不同的文本，如<code>title</code>，我们可以将所有需要支持多语言的文本都在此类中定义。<code>DemoLocalizations</code>的实例将会在Delegate类的<code>load</code>方法中创建。</p> <h3 id=\"实现delegate类\"><a href=\"#实现delegate类\" class=\"header-anchor\">#</a> 实现Delegate类</h3> <p>Delegate类的职责是在Locale改变时加载新的Locale资源，所以它有一个<code>load</code>方法，Delegate类需要继承自<code>LocalizationsDelegate</code>类，实现相应的接口，示例如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//Locale代理类</span>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">DemoLocalizationsDelegate</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">LocalizationsDelegate</span><span class=\"token operator\">&lt;</span>DemoLocalizations<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function\">DemoLocalizationsDelegate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">//是否支持某个Local</span>\n  <span class=\"token metadata symbol\">@override</span>\n  bool <span class=\"token function\">isSupported</span><span class=\"token punctuation\">(</span>Locale locale<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">[</span><span class=\"token string\">'en'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'zh'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span><span class=\"token function\">contains</span><span class=\"token punctuation\">(</span>locale<span class=\"token punctuation\">.</span>languageCode<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// Flutter会调用此类加载相应的Locale资源类</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Future<span class=\"token operator\">&lt;</span>DemoLocalizations<span class=\"token operator\">&gt;</span> <span class=\"token function\">load</span><span class=\"token punctuation\">(</span>Locale locale<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;$locale&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> SynchronousFuture<span class=\"token operator\">&lt;</span>DemoLocalizations<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n        <span class=\"token function\">DemoLocalizations</span><span class=\"token punctuation\">(</span>locale<span class=\"token punctuation\">.</span>languageCode <span class=\"token operator\">==</span> <span class=\"token string\">&quot;zh&quot;</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  bool <span class=\"token function\">shouldReload</span><span class=\"token punctuation\">(</span>DemoLocalizationsDelegate old<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p><code>shouldReload</code>的返回值决定当Localizations组件重新build时，是否调用<code>load</code>方法重新加载Locale资源。一般情况下，Locale资源只应该在Locale切换时加载一次，不需要每次在<code>Localizations</code>重新build时都加载，所以返回<code>false</code>即可。可能有些人会担心返回<code>false</code>的话在APP启动后用户再改变系统语言时<code>load</code>方法将不会被调用，所以Locale资源将不会被加载。事实上，每当Locale改变时Flutter都会再调用<code>load</code>方法加载新的Locale，无论<code>shouldReload</code>返回<code>true</code>还是<code>false</code>。</p> <h3 id=\"最后一步-添加多语言支持\"><a href=\"#最后一步-添加多语言支持\" class=\"header-anchor\">#</a> 最后一步：添加多语言支持</h3> <p>和上一节中介绍的相同，我们现在需要先注册<code>DemoLocalizationsDelegate</code>类，然后再通过<code>DemoLocalizations.of(context)</code>来动态获取当前Locale文本。</p> <p>只需要在MaterialApp或WidgetsApp的<code>localizationsDelegates</code>列表中添加我们的Delegate实例即可完成注册：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>localizationsDelegates<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>\n <span class=\"token comment\">// 本地化的代理类</span>\n GlobalMaterialLocalizations<span class=\"token punctuation\">.</span>delegate<span class=\"token punctuation\">,</span>\n GlobalWidgetsLocalizations<span class=\"token punctuation\">.</span>delegate<span class=\"token punctuation\">,</span>\n <span class=\"token comment\">// 注册我们的Delegate</span>\n <span class=\"token function\">DemoLocalizationsDelegate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n</code></pre></div><p>接下来我们可以在Widget中使用Locale值：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">return</span> <span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n  appBar<span class=\"token punctuation\">:</span> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>\n    <span class=\"token comment\">//使用Locale title  </span>\n    title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>DemoLocalizations<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>title<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n ） \n</code></pre></div><p>这样，当在美国英语和中文简体之间切换系统语言时，APP的标题将会分别为“Flutter APP”和“Flutter应用”。</p> <h3 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h3> <p>本节我们通过一个简单的示例说明了Flutter应用国际化的基本过程及原理。但是上面的实例还有一个严重的不足就是我们需要在DemoLocalizations类中获取<code>title</code>时手动的判断当前语言Locale，然后返回合适的文本。试想一下，当我们要支持的语言不是两种而是8种甚至20几种时，如果为每个文本属性都要分别去判断到底是哪种Locale从而获取相应语言的文本将会是一件非常复杂的事。还有，通常情况下翻译人员并不是开发人员，能不能像i18n或l10n标准那样可以将翻译单独保存为一个arb文件交由翻译人员去翻译，翻译好之后开发人员再通过工具将arb文件转为代码。答案是肯定的！我们将在下一节介绍如何通过Dart intl包来实现这些。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/197.7fd8856f.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter13/multi_languages_support.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>13.1 让App支持多语言 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/114.df1f86a1.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter13/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_13-1-让app支持多语言\"><a href=\"#_13-1-让app支持多语言\" class=\"header-anchor\">#</a> 13.1 让App支持多语言</h1> <p>如果我们的应用要支持多种语言，那么我们需要“国际化”它。这意味着我们在开发时需要为应用程序支持的每种语言环境设置“本地化”的一些值，如文本和布局。Flutter SDK已经提供了一些组件和类来帮助我们实现国际化，下面我们来介绍一下Flutter中实现国际化的步骤。</p> <p>接下来我们以<code>MaterialApp</code>类为入口的应用来说明如何支持国际化。</p> <blockquote><p>大多数应用程序都是通过<code>MaterialApp</code>为入口，但根据低级别的<code>WidgetsApp</code>类为入口编写的应用程序也可以使用相同的类和逻辑进行国际化。<code>MaterialApp</code>实际上也是<code>WidgetsApp</code>的一个包装。</p></blockquote> <p>注意，”本地化的值和资源“是指我们针对不同语言准备的不同资源，这些资源一般是指文案（字符串），当然也会有一些其他的资源会根据不同语言地区而不同，比如我们需要显示一个APP上架地的国旗图片，那么不同Locale区域我们就需要提供不同的的国旗图片。</p> <h3 id=\"支持国际化\"><a href=\"#支持国际化\" class=\"header-anchor\">#</a> 支持国际化</h3> <p>默认情况下，Flutter SDK中的组件仅提供美国英语本地化资源（主要是文本）。要添加对其他语言的支持，应用程序须添加一个名为“flutter_localizations”的包依赖，然后还需要在<code>MaterialApp</code>中进行一些配置。 要使用<code>flutter_localizations</code>包，首先需要添加依赖到<code>pubspec.yaml</code>文件中：</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code><span class=\"token key atrule\">dependencies</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">flutter</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">sdk</span><span class=\"token punctuation\">:</span> flutter\n  <span class=\"token key atrule\">flutter_localizations</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">sdk</span><span class=\"token punctuation\">:</span> flutter\n</code></pre></div><p>接下来，下载<code>flutter_localizations</code>库，然后指定<code>MaterialApp</code>的<code>localizationsDelegates</code>和<code>supportedLocales</code>：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter_localizations/flutter_localizations.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">new</span> <span class=\"token class-name\">MaterialApp</span><span class=\"token punctuation\">(</span>\n localizationsDelegates<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>\n   <span class=\"token comment\">// 本地化的代理类</span>\n   GlobalMaterialLocalizations<span class=\"token punctuation\">.</span>delegate<span class=\"token punctuation\">,</span>\n   GlobalWidgetsLocalizations<span class=\"token punctuation\">.</span>delegate<span class=\"token punctuation\">,</span>\n <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n supportedLocales<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>\n    <span class=\"token keyword\">const</span> <span class=\"token function\">Locale</span><span class=\"token punctuation\">(</span><span class=\"token string\">'en'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'US'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 美国英语</span>\n    <span class=\"token keyword\">const</span> <span class=\"token function\">Locale</span><span class=\"token punctuation\">(</span><span class=\"token string\">'zh'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'CN'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 中文简体</span>\n    <span class=\"token comment\">//其它Locales</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token comment\">// ...</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><blockquote><p>与<code>MaterialApp</code>类为入口的应用不同, 对基于<code>WidgetsApp</code>类为入口的应用程序进行国际化时,不需要<code>GlobalMaterialLocalizations.delegate</code>。</p></blockquote> <p><code>localizationsDelegates</code>列表中的元素是生成本地化值集合的工厂。<code>GlobalMaterialLocalizations.delegate</code> 为Material 组件库提供的本地化的字符串和其他值，它可以使Material 组件支持多语言。 <code>GlobalWidgetsLocalizations.delegate</code>定义组件默认的文本方向，从左到右或从右到左，这是因为有些语言的阅读习惯并不是从左到右，比如如阿拉伯语就是从右向左的。</p> <p><code>supportedLocales</code>也接收一个Locale数组，表示我们的应用支持的语言列表，在本例中我们的应用只支持美国英语和中文简体两种语言。</p> <h3 id=\"获取当前区域locale\"><a href=\"#获取当前区域locale\" class=\"header-anchor\">#</a> 获取当前区域Locale</h3> <p><a href=\"https://docs.flutter.io/flutter/dart-ui/Locale-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>Locale</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>类是用来标识用户的语言环境的，它包括语言和国家两个标志如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">const</span> <span class=\"token function\">Locale</span><span class=\"token punctuation\">(</span><span class=\"token string\">'zh'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'CN'</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// 中文简体</span>\n</code></pre></div><p>我们始终可以通过以下方式来获取应用的当前区域Locale：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Locale myLocale <span class=\"token operator\">=</span> Localizations<span class=\"token punctuation\">.</span><span class=\"token function\">localeOf</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p><a href=\"https://docs.flutter.io/flutter/widgets/Localizations-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>Localizations</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 组件一般位于widget树中其它业务组件的顶部，它的作用是定义区域Locale以及设置子树依赖的本地化资源。 如果系统的语言环境发生变化，<a href=\"https://docs.flutter.io/flutter/widgets/WidgetsApp-class.html\" target=\"_blank\" rel=\"noopener noreferrer\">WidgetsApp<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>将创建一个新的Localizations 组件并重建它，这样子树中通过<code>Localizations.localeOf(context)</code> 获取的Locale就会更新。</p> <h3 id=\"监听系统语言切换\"><a href=\"#监听系统语言切换\" class=\"header-anchor\">#</a> 监听系统语言切换</h3> <p>当我们更改系统语言设置时，APP中的Localizations组件会重新构建，<code>Localizations.localeOf(context)</code> 获取的Locale就会更新，最终界面会重新build达到切换语言的效果。但是这个过程是隐式完成的，我们并没有主动去监听系统语言切换，但是有时我们需要在系统语言发生改变时做一些事，比如系统语言切换为一种我们APP不支持的语言时，我们需要设置一个默认的语言，这时我们就需要监听locale改变事件。</p> <p>我们可以通过<code>localeResolutionCallback</code>或<code>localeListResolutionCallback</code>回调来监听locale改变的事件，我们先看看<code>localeResolutionCallback</code>的回调函数签名：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Locale <span class=\"token keyword\">Function</span><span class=\"token punctuation\">(</span>Locale locale<span class=\"token punctuation\">,</span> Iterable<span class=\"token operator\">&lt;</span>Locale<span class=\"token operator\">&gt;</span> supportedLocales<span class=\"token punctuation\">)</span>\n</code></pre></div><ul><li><p>参数<code>locale</code>的值为当前的当前的系统语言设置，当应用启动时或用户动态改变系统语言设置时此locale即为系统的当前locale。当开发者手动指定APP的locale时，那么此locale参数代表开发者指定的locale，此时将忽略系统locale如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">MaterialApp</span><span class=\"token punctuation\">(</span>\n <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n locale<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Locale</span><span class=\"token punctuation\">(</span><span class=\"token string\">'en'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'US'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//手动指定locale</span>\n <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>上面的例子中手动指定了应用locale为美国英语，指定后即使设备当前语言是中文简体，应用中的locale也依然是美国英语。如果<code>locale</code>为<code>null</code>，则表示Flutter未能获取到设备的Locale信息，所以我们在使用<code>locale</code>之前一定要先判空。</p></li> <li><p><code>supportedLocales</code> 为当前应用支持的locale列表，是开发者在MaterialApp中通过<code>supportedLocales</code>属性注册的。</p></li> <li><p>返回值是一个<code>Locale</code>，此<code>Locale</code>为Flutter APP最终使用的<code>Locale</code>。通常在不支持的语言区域时返回一个默认的<code>Locale</code>。</p></li></ul> <p><code>localeListResolutionCallback</code>和<code>localeResolutionCallback</code>唯一的不同就在第一个参数类型，前者接收的是一个<code>Locale</code>列表，而后者接收的是单个<code>Locale</code>。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Locale <span class=\"token keyword\">Function</span><span class=\"token punctuation\">(</span>List<span class=\"token operator\">&lt;</span>Locale<span class=\"token operator\">&gt;</span> locales<span class=\"token punctuation\">,</span> Iterable<span class=\"token operator\">&lt;</span>Locale<span class=\"token operator\">&gt;</span> supportedLocales<span class=\"token punctuation\">)</span>\n</code></pre></div><p>在较新的Android系统中，用户可以设置一个语言列表，这样一来，支持多语言的应用就会得到这个列表，应用通常的处理方式就是按照列表的顺序依次尝试加载相应的Locale，如果某一种语言加载成功则会停止。图13-1是Android系统中设置语言列表的截图：</p> <p><img src=\"/assets/img/13-1.fcda4f48.jpeg\" alt=\"设置语言列表\"></p> <p>在Flutter中，应该优先使用<code>localeListResolutionCallback</code>，当然你不必担心Android系统的差异性，如果在低版本的Android系统中，Flutter会自动处理这种情况，这时Locale列表只会包含一项。</p> <h3 id=\"localization-组件\"><a href=\"#localization-组件\" class=\"header-anchor\">#</a> Localization 组件</h3> <p>Localizations组件用于加载和查找应用当前语言下的本地化值或资源。应用程序通过<a href=\"https://docs.flutter.io/flutter/widgets/Localizations/of.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>Localizations.of(context,type)</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>来引用这些对象。 如果设备的Locale区域设置发生更改，则Localizations 组件会自动加载新区域的Locale值，然后重新build使用（依赖）了它们的组件，之所以会这样，是因为<code>Localizations</code>内部使用了<a href=\"https://book.flutterchina.club/chapter7/inherited_widget.html\" target=\"_blank\" rel=\"noopener noreferrer\">InheritedWidget<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> ，我们在介绍该组件时讲过：当子组件的<code>build</code>函数引用了<code>InheritedWidget</code>时，会创建对<code>InheritedWidget</code>的隐式依赖关系。因此，当<code>InheritedWidget</code>发生更改时，即<code>Localizations</code>的Locale设置发生更改时，将重建所有依赖它的子组件。</p> <p>本地化值由<code>Localizations</code>的 <a href=\"https://docs.flutter.io/flutter/widgets/LocalizationsDelegate-class.html\" target=\"_blank\" rel=\"noopener noreferrer\">LocalizationsDelegates<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 列表加载 。 <strong>每个委托必须定义一个异步load() 方法</strong>，以生成封装了一系列本地化值的对象。通常这些对象为每个本地化值定义一个方法。</p> <p>在大型应用程序中，不同模块或Package可能会与自己的本地化值捆绑在一起。 这就是为什么要用<code>Localizations</code> 管理对象表的原因。 要使用由<code>LocalizationsDelegate</code>的<code>load</code>方法之一产生的对象，可以指定一个<code>BuildContext</code>和对象的类型来找到它。例如，Material 组件库的本地化字符串由<a href=\"https://docs.flutter.io/flutter/material/MaterialLocalizations-class.html\" target=\"_blank\" rel=\"noopener noreferrer\">MaterialLocalizations<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>类定义，此类的实例由<a href=\"https://docs.flutter.io/flutter/material/MaterialApp-class.html\" target=\"_blank\" rel=\"noopener noreferrer\">MaterialApp<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>类提供的<code>LocalizationDelegate</code>创建， 它们可以如下方式获取到：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Localizations<span class=\"token punctuation\">.</span>of<span class=\"token operator\">&lt;</span>MaterialLocalizations<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> MaterialLocalizations<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>这个特殊的<code>Localizations.of()</code>表达式会经常使用，所以MaterialLocalizations类提供了一个便捷方法：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">static</span> MaterialLocalizations <span class=\"token function\">of</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> Localizations<span class=\"token punctuation\">.</span>of<span class=\"token operator\">&lt;</span>MaterialLocalizations<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> MaterialLocalizations<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">// 可以直接调用便捷方法</span>\ntooltip<span class=\"token punctuation\">:</span> MaterialLocalizations<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>backButtonTooltip<span class=\"token punctuation\">,</span>\n</code></pre></div><h3 id=\"使用打包好的localizationsdelegates\"><a href=\"#使用打包好的localizationsdelegates\" class=\"header-anchor\">#</a> 使用打包好的LocalizationsDelegates</h3> <p>为了尽可能小而且简单，flutter软件包中仅提供美国英语值的<code>MaterialLocalizations</code>和<code>WidgetsLocalizations</code>接口的实现。 这些实现类分别称为<code>DefaultMaterialLocalizations</code>和<code>DefaultWidgetsLocalizations</code>。flutter_localizations 包包含<code>GlobalMaterialLocalizations</code>和<code>GlobalWidgetsLocalizations</code>的本地化接口的多语言实现， 国际化的应用程序必须按照本节开头说明的那样为这些类指定本地化Delegate。</p> <p>上述的<code>GlobalMaterialLocalizations</code>和<code>GlobalWidgetsLocalizations</code>只是Material组件库的本地化实现，如果我们要让自己的布局支持多语言，那么就需要实现在即的<code>Localizations</code>，我们将在下一节介绍其具体的实现方式。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/114.df1f86a1.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter14/element_buildcontext.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>14.2 Element与BuildContext | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/49.e636869b.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter14/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_14-2-element与buildcontext\"><a href=\"#_14-2-element与buildcontext\" class=\"header-anchor\">#</a> 14.2 Element与BuildContext</h1> <h2 id=\"_14-2-1-element\"><a href=\"#_14-2-1-element\" class=\"header-anchor\">#</a> 14.2.1 Element</h2> <p>在“Widget简介”一节，我们介绍了Widget和Element的关系，我们知道最终的UI树其实是由一个个独立的Element节点构成。我们也说过组件最终的Layout、渲染都是通过<code>RenderObject</code>来完成的，从创建到渲染的大体流程是：根据Widget生成Element，然后创建相应的<code>RenderObject</code>并关联到<code>Element.renderObject</code>属性上，最后再通过<code>RenderObject</code>来完成布局排列和绘制。</p> <p>Element就是Widget在UI树具体位置的一个实例化对象，大多数Element只有唯一的<code>renderObject</code>，但还有一些Element会有多个子节点，如继承自<code>RenderObjectElement</code>的一些类，比如<code>MultiChildRenderObjectElement</code>。最终所有Element的RenderObject构成一棵树，我们称之为”Render Tree“即”渲染树“。总结一下，我们可以认为Flutter的UI系统包含三棵树：Widget树、Element树、渲染树。他们的依赖关系是：Element树根据Widget树生成，而渲染树又依赖于Element树，如图14-0所示。</p> <p><img src=\"/assets/img/14-0.bd5c2d9d.png\" alt=\"图14-0\"></p> <p>现在我们重点看一下Element，Element的生命周期如下：</p> <ol><li>Framework 调用<code>Widget.createElement</code> 创建一个Element实例，记为<code>element</code></li> <li>Framework 调用 <code>element.mount(parentElement,newSlot)</code> ，mount方法中首先调用<code>element</code>所对应Widget的<code>createRenderObject</code>方法创建与<code>element</code>相关联的RenderObject对象，然后调用<code>element.attachRenderObject</code>方法将<code>element.renderObject</code>添加到渲染树中插槽指定的位置（这一步不是必须的，一般发生在Element树结构发生变化时才需要重新attach）。插入到渲染树后的<code>element</code>就处于“active”状态，处于“active”状态后就可以显示在屏幕上了（可以隐藏）。</li> <li>当有父Widget的配置数据改变时，同时其<code>State.build</code>返回的Widget结构与之前不同，此时就需要重新构建对应的Element树。为了进行Element复用，在Element重新构建前会先尝试是否可以复用旧树上相同位置的element，element节点在更新前都会调用其对应Widget的<code>canUpdate</code>方法，如果返回<code>true</code>，则复用旧Element，旧的Element会使用新Widget配置数据更新，反之则会创建一个新的Element。<code>Widget.canUpdate</code>主要是判断<code>newWidget</code>与<code>oldWidget</code>的<code>runtimeType</code>和<code>key</code>是否同时相等，如果同时相等就返回<code>true</code>，否则就会返回<code>false</code>。根据这个原理，当我们需要强制更新一个Widget时，可以通过指定不同的Key来避免复用。</li> <li>当有祖先Element决定要移除<code>element</code> 时（如Widget树结构发生了变化，导致<code>element</code>对应的Widget被移除），这时该祖先Element就会调用<code>deactivateChild</code> 方法来移除它，移除后<code>element.renderObject</code>也会被从渲染树中移除，然后Framework会调用<code>element.deactivate</code> 方法，这时<code>element</code>状态变为“inactive”状态。</li> <li>“inactive”态的element将不会再显示到屏幕。为了避免在一次动画执行过程中反复创建、移除某个特定element，“inactive”态的element在当前动画最后一帧结束前都会保留，如果在动画执行结束后它还未能重新变成“active”状态，Framework就会调用其<code>unmount</code>方法将其彻底移除，这时element的状态为<code>defunct</code>,它将永远不会再被插入到树中。</li> <li>如果<code>element</code>要重新插入到Element树的其它位置，如<code>element</code>或<code>element</code>的祖先拥有一个GlobalKey（用于全局复用元素），那么Framework会先将element从现有位置移除，然后再调用其<code>activate</code>方法，并将其<code>renderObject</code>重新attach到渲染树。</li></ol> <p>看完Element的生命周期，可能有些读者会有疑问，开发者会直接操作Element树吗？其实对于开发者来说，大多数情况下只需要关注Widget树就行，Flutter框架已经将对Widget树的操作映射到了Element树上，这可以极大的降低复杂度，提高开发效率。但是了解Element对理解整个Flutter UI框架是至关重要的，Flutter正是通过Element这个纽带将Widget和RenderObject关联起来，了解Element层不仅会帮助读者对Flutter UI框架有个清晰的认识，而且也会提高自己的抽象能力和设计能力。另外在有些时候，我们必须得直接使用Element对象来完成一些操作，比如获取主题Theme数据，具体细节将在下文介绍。</p> <h2 id=\"_14-2-2-buildcontext\"><a href=\"#_14-2-2-buildcontext\" class=\"header-anchor\">#</a> 14.2.2 BuildContext</h2> <p>我们已经知道，<code>StatelessWidget</code>和<code>StatefulWidget</code>的<code>build</code>方法都会传一个<code>BuildContext</code>对象：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们也知道，在很多时候我们都需要使用这个<code>context</code> 做一些事，比如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Theme<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token comment\">//获取主题</span>\nNavigator<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> route<span class=\"token punctuation\">)</span> <span class=\"token comment\">//入栈新路由</span>\nLocalizations<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> type<span class=\"token punctuation\">)</span> <span class=\"token comment\">//获取Local</span>\ncontext<span class=\"token punctuation\">.</span>size <span class=\"token comment\">//获取上下文大小</span>\ncontext<span class=\"token punctuation\">.</span><span class=\"token function\">findRenderObject</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//查找当前或最近的一个祖先RenderObject</span>\n</code></pre></div><p>那么<code>BuildContext</code>到底是什么呢，查看其定义，发现其是一个抽象接口类：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">abstract</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">BuildContext</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>那这个<code>context</code>对象对应的实现类到底是谁呢？我们顺藤摸瓜，发现<code>build</code>调用是发生在<code>StatelessWidget</code>和<code>StatefulWidget</code>对应的<code>StatelessElement</code>和<code>StatefulElement</code>的<code>build</code>方法中，以<code>StatelessElement</code>为例：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">StatelessElement</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">ComponentElement</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> widget<span class=\"token punctuation\">.</span><span class=\"token function\">build</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>发现<code>build</code>传递的参数是<code>this</code>，很明显！这个<code>BuildContext</code>就是<code>StatelessElement</code>。同样，我们同样发现<code>StatefulWidget</code>的<code>context</code>是<code>StatefulElement</code>。但<code>StatelessElement</code>和<code>StatefulElement</code>本身并没有实现<code>BuildContext</code>接口，继续跟踪代码，发现它们间接继承自<code>Element</code>类，然后查看<code>Element</code>类定义，发现<code>Element</code>类果然实现了<code>BuildContext</code>接口:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">Element</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">DiagnosticableTree</span> <span class=\"token keyword\">implements</span> <span class=\"token class-name\">BuildContext</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>至此真相大白，<code>BuildContext</code>就是widget对应的<code>Element</code>，所以我们可以通过<code>context</code>在<code>StatelessWidget</code>和<code>StatefulWidget</code>的<code>build</code>方法中直接访问<code>Element</code>对象。我们获取主题数据的代码<code>Theme.of(context)</code>内部正是调用了Element的<code>dependOnInheritedWidgetOfExactType()</code>方法。</p> <blockquote><p>思考题：为什么build方法的参数不定义成Element对象，而要定义成BuildContext ?</p></blockquote> <h3 id=\"进阶\"><a href=\"#进阶\" class=\"header-anchor\">#</a> 进阶</h3> <p>我们可以看到Element是Flutter UI框架内部连接widget和<code>RenderObject</code>的纽带，大多数时候开发者只需要关注widget层即可，但是widget层有时候并不能完全屏蔽<code>Element</code>细节，所以Framework在<code>StatelessWidget</code>和<code>StatefulWidget</code>中通过<code>build</code>方法参数又将<code>Element</code>对象也传递给了开发者，这样一来，开发者便可以在需要时直接操作<code>Element</code>对象。那么现在笔者提两个问题，请读者先自己思考一下：</p> <ol><li>如果没有widget层，单靠<code>Element</code>层是否可以搭建起一个可用的UI框架？如果可以应该是什么样子？</li> <li>Flutter UI框架能不做成响应式吗？</li></ol> <p>对于问题1，答案当然是肯定的，因为我们之前说过widget树只是<code>Element</code>树的映射，我们完全可以直接通过Element来搭建一个UI框架。下面举一个例子：</p> <p>我们通过纯粹的Element来模拟一个<code>StatefulWidget</code>的功能，假设有一个页面，该页面有一个按钮，按钮的文本是一个9位数，点击一次按钮，则对9个数随机排一次序，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">HomeView</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">ComponentElement</span><span class=\"token punctuation\">{</span>\n  <span class=\"token function\">HomeView</span><span class=\"token punctuation\">(</span>Widget widget<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>widget<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  String text <span class=\"token operator\">=</span> <span class=\"token string\">&quot;123456789&quot;</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    Color primary<span class=\"token operator\">=</span>Theme<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>primaryColor<span class=\"token punctuation\">;</span> <span class=\"token comment\">//1</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">GestureDetector</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>text<span class=\"token punctuation\">,</span> style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> primary<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword\">var</span> t <span class=\"token operator\">=</span> text<span class=\"token punctuation\">.</span><span class=\"token function\">split</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token function\">shuffle</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            text <span class=\"token operator\">=</span> t<span class=\"token punctuation\">.</span><span class=\"token function\">join</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token function\">markNeedsBuild</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//点击后将该Element标记为dirty，Element将会rebuild</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><ul><li><p>上面<code>build</code>方法不接收参数，这一点和在<code>StatelessWidget</code>和<code>StatefulWidget</code>中<code>build(BuildContext)</code>方法不同。代码中需要用到<code>BuildContext</code>的地方直接用<code>this</code>代替即可，如代码注释1处<code>Theme.of(this)</code>参数直接传<code>this</code>即可，因为当前对象本身就是<code>Element</code>实例。</p></li> <li><p>当<code>text</code>发生改变时，我们调用<code>markNeedsBuild()</code>方法将当前Element标记为dirty即可，标记为dirty的Element会在下一帧中重建。实际上，<code>State.setState()</code>在内部也是调用的<code>markNeedsBuild()</code>方法。</p></li> <li><p>上面代码中build方法返回的仍然是一个widget，这是由于Flutter框架中已经有了widget这一层，并且组件库都已经是以widget的形式提供了，如果在Flutter框架中所有组件都像示例的<code>HomeView</code>一样以<code>Element</code>形式提供，那么就可以用纯<code>Element</code>来构建UI了<code>HomeView</code>的build方法返回值类型就可以是<code>Element</code>了。</p></li></ul> <p>如果我们需要将上面代码在现有Flutter框架中跑起来，那么还是得提供一个“适配器”widget将<code>HomeView</code>结合到现有框架中，下面<code>CustomHome</code>就相当于“适配器”：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">CustomHome</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">Widget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Element <span class=\"token function\">createElement</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">HomeView</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>现在就可以将<code>CustomHome</code>添加到widget树了，我们在一个新路由页创建它，最终效果如下如图14-1和14-2（点击后）所示：</p> <p><img src=\"/assets/img/14-1.075fa468.png\" alt=\"图14-1\"> <img src=\"/assets/img/14-2.7141498f.png\" alt=\"图14-2\"></p> <p>点击按钮则按钮文本会随机排序。</p> <p>对于问题2，答案当然也是肯定的，Flutter engine提供的dart API是原始且独立的，这个与操作系统提供的API类似，上层UI框架设计成什么样完全取决于设计者，完全可以将UI框架设计成Android风格或iOS风格，但这些事Google不会再去做，我们也没必要再去搞这一套，这是因为响应式的思想本身是很棒的，之所以提出这个问题，是因为笔者认为做与不做是一回事，但知道能不能做是另一回事，这能反映出我们对知识的理解程度。</p> <h3 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h3> <p>本节详细的介绍了<code>Element</code>的生命周期，以及它Widget、BuildContext的关系，也介绍了Element在Flutter UI系统中的角色和作用，我们将在下一节介绍Flutter UI系统中另一个重要的角色RenderObject。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/49.e636869b.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter14/flutter_app_startup.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>14.4 Flutter运行机制-从启动到显示 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/198.2a531f59.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter14/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_14-4-flutter运行机制-从启动到显示\"><a href=\"#_14-4-flutter运行机制-从启动到显示\" class=\"header-anchor\">#</a> 14.4 Flutter运行机制-从启动到显示</h1> <p>本节我们主要介绍一下Flutter从启动到显示的过程。</p> <h3 id=\"启动\"><a href=\"#启动\" class=\"header-anchor\">#</a> 启动</h3> <p>Flutter的入口在&quot;lib/main.dart&quot;的<code>main()</code>函数中，它是Dart应用程序的起点。在Flutter应用中，<code>main()</code>函数最简单的实现如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">main</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">runApp</span><span class=\"token punctuation\">(</span><span class=\"token function\">MyApp</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以看<code>main()</code>函数只调用了一个<code>runApp()</code>方法，我们看看<code>runApp()</code>方法中都做了什么：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">runApp</span><span class=\"token punctuation\">(</span>Widget app<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  WidgetsFlutterBinding<span class=\"token punctuation\">.</span><span class=\"token function\">ensureInitialized</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token function\">attachRootWidget</span><span class=\"token punctuation\">(</span>app<span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token function\">scheduleWarmUpFrame</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>参数<code>app</code>是一个widget，它是Flutter应用启动后要展示的第一个Widget。而<code>WidgetsFlutterBinding</code>正是绑定widget 框架和Flutter engine的桥梁，定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">WidgetsFlutterBinding</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">BindingBase</span> <span class=\"token keyword\">with</span> GestureBinding<span class=\"token punctuation\">,</span> ServicesBinding<span class=\"token punctuation\">,</span> SchedulerBinding<span class=\"token punctuation\">,</span> PaintingBinding<span class=\"token punctuation\">,</span> SemanticsBinding<span class=\"token punctuation\">,</span> RendererBinding<span class=\"token punctuation\">,</span> WidgetsBinding <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">static</span> WidgetsBinding <span class=\"token function\">ensureInitialized</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>WidgetsBinding<span class=\"token punctuation\">.</span>instance <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span>\n      <span class=\"token function\">WidgetsFlutterBinding</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> WidgetsBinding<span class=\"token punctuation\">.</span>instance<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以看到<code>WidgetsFlutterBinding</code>继承自<code>BindingBase</code> 并混入了很多<code>Binding</code>，在介绍这些<code>Binding</code>之前我们先介绍一下<code>Window</code>，下面是<code>Window</code>的官方解释：</p> <blockquote><p>The most basic interface to the host operating system's user interface.</p></blockquote> <p>很明显，<code>Window</code>正是Flutter Framework连接宿主操作系统的接口。我们看一下<code>Window</code>类的部分定义：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">Window</span> <span class=\"token punctuation\">{</span>\n    \n  <span class=\"token comment\">// 当前设备的DPI，即一个逻辑像素显示多少物理像素，数字越大，显示效果就越精细保真。</span>\n  <span class=\"token comment\">// DPI是设备屏幕的固件属性，如Nexus 6的屏幕DPI为3.5 </span>\n  double <span class=\"token keyword\">get</span> devicePixelRatio <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _devicePixelRatio<span class=\"token punctuation\">;</span>\n  \n  <span class=\"token comment\">// Flutter UI绘制区域的大小</span>\n  Size <span class=\"token keyword\">get</span> physicalSize <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _physicalSize<span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// 当前系统默认的语言Locale</span>\n  Locale <span class=\"token keyword\">get</span> locale<span class=\"token punctuation\">;</span>\n    \n  <span class=\"token comment\">// 当前系统字体缩放比例。  </span>\n  double <span class=\"token keyword\">get</span> textScaleFactor <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _textScaleFactor<span class=\"token punctuation\">;</span>  \n    \n  <span class=\"token comment\">// 当绘制区域大小改变回调</span>\n  VoidCallback <span class=\"token keyword\">get</span> onMetricsChanged <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _onMetricsChanged<span class=\"token punctuation\">;</span>  \n  <span class=\"token comment\">// Locale发生变化回调</span>\n  VoidCallback <span class=\"token keyword\">get</span> onLocaleChanged <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _onLocaleChanged<span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">// 系统字体缩放变化回调</span>\n  VoidCallback <span class=\"token keyword\">get</span> onTextScaleFactorChanged <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _onTextScaleFactorChanged<span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">// 绘制前回调，一般会受显示器的垂直同步信号VSync驱动，当屏幕刷新时就会被调用</span>\n  FrameCallback <span class=\"token keyword\">get</span> onBeginFrame <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _onBeginFrame<span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">// 绘制回调  </span>\n  VoidCallback <span class=\"token keyword\">get</span> onDrawFrame <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _onDrawFrame<span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">// 点击或指针事件回调</span>\n  PointerDataPacketCallback <span class=\"token keyword\">get</span> onPointerDataPacket <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _onPointerDataPacket<span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">// 调度Frame，该方法执行后，onBeginFrame和onDrawFrame将紧接着会在合适时机被调用，</span>\n  <span class=\"token comment\">// 此方法会直接调用Flutter engine的Window_scheduleFrame方法</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">scheduleFrame</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> native <span class=\"token string\">'Window_scheduleFrame'</span><span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">// 更新应用在GPU上的渲染,此方法会直接调用Flutter engine的Window_render方法</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">render</span><span class=\"token punctuation\">(</span>Scene scene<span class=\"token punctuation\">)</span> native <span class=\"token string\">'Window_render'</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// 发送平台消息</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">sendPlatformMessage</span><span class=\"token punctuation\">(</span>String name<span class=\"token punctuation\">,</span>\n                           ByteData data<span class=\"token punctuation\">,</span>\n                           PlatformMessageResponseCallback callback<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">// 平台通道消息处理回调  </span>\n  PlatformMessageCallback <span class=\"token keyword\">get</span> onPlatformMessage <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _onPlatformMessage<span class=\"token punctuation\">;</span>\n  \n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//其它属性及回调</span>\n   \n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以看到<code>Window</code>类包含了当前设备和系统的一些信息以及Flutter Engine的一些回调。现在我们再回来看看<code>WidgetsFlutterBinding</code>混入的各种Binding。通过查看这些 Binding的源码，我们可以发现这些Binding中基本都是监听并处理<code>Window</code>对象的一些事件，然后将这些事件按照Framework的模型包装、抽象然后分发。可以看到<code>WidgetsFlutterBinding</code>正是粘连Flutter engine与上层Framework的“胶水”。</p> <ul><li><code>GestureBinding</code>：提供了<code>window.onPointerDataPacket</code> 回调，绑定Framework手势子系统，是Framework事件模型与底层事件的绑定入口。</li> <li><code>ServicesBinding</code>：提供了<code>window.onPlatformMessage</code> 回调， 用于绑定平台消息通道（message channel），主要处理原生和Flutter通信。</li> <li><code>SchedulerBinding</code>：提供了<code>window.onBeginFrame</code>和<code>window.onDrawFrame</code>回调，监听刷新事件，绑定Framework绘制调度子系统。</li> <li><code>PaintingBinding</code>：绑定绘制库，主要用于处理图片缓存。</li> <li><code>SemanticsBinding</code>：语义化层与Flutter engine的桥梁，主要是辅助功能的底层支持。</li> <li><code>RendererBinding</code>: 提供了<code>window.onMetricsChanged</code> 、<code>window.onTextScaleFactorChanged</code> 等回调。它是渲染树与Flutter engine的桥梁。</li> <li><code>WidgetsBinding</code>：提供了<code>window.onLocaleChanged</code>、<code>onBuildScheduled</code> 等回调。它是Flutter widget层与engine的桥梁。</li></ul> <p><code>WidgetsFlutterBinding.ensureInitialized()</code>负责初始化一个<code>WidgetsBinding</code>的全局单例，紧接着会调用<code>WidgetsBinding</code>的<code>attachRootWidget</code>方法，该方法负责将根Widget添加到<code>RenderView</code>上，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">attachRootWidget</span><span class=\"token punctuation\">(</span>Widget rootWidget<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  _renderViewElement <span class=\"token operator\">=</span> RenderObjectToWidgetAdapter<span class=\"token operator\">&lt;</span>RenderBox<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n    container<span class=\"token punctuation\">:</span> renderView<span class=\"token punctuation\">,</span> \n    debugShortDescription<span class=\"token punctuation\">:</span> <span class=\"token string\">'[root]'</span><span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> rootWidget\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">attachToRenderTree</span><span class=\"token punctuation\">(</span>buildOwner<span class=\"token punctuation\">,</span> renderViewElement<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>注意，代码中的有<code>renderView</code>和<code>renderViewElement</code>两个变量，<code>renderView</code>是一个<code>RenderObject</code>，它是渲染树的根，而<code>renderViewElement</code>是<code>renderView</code>对应的<code>Element</code>对象，可见该方法主要完成了根widget到根 <code>RenderObject</code>再到根<code>Element</code>的整个关联过程。我们看看<code>attachToRenderTree</code>的源码实现：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>RenderObjectToWidgetElement<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span> <span class=\"token function\">attachToRenderTree</span><span class=\"token punctuation\">(</span>BuildOwner owner<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>RenderObjectToWidgetElement<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span> element<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>element <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    owner<span class=\"token punctuation\">.</span><span class=\"token function\">lockState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      element <span class=\"token operator\">=</span> <span class=\"token function\">createElement</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span>element <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      element<span class=\"token punctuation\">.</span><span class=\"token function\">assignOwner</span><span class=\"token punctuation\">(</span>owner<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    owner<span class=\"token punctuation\">.</span><span class=\"token function\">buildScope</span><span class=\"token punctuation\">(</span>element<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      element<span class=\"token punctuation\">.</span><span class=\"token function\">mount</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n    element<span class=\"token punctuation\">.</span>_newWidget <span class=\"token operator\">=</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">;</span>\n    element<span class=\"token punctuation\">.</span><span class=\"token function\">markNeedsBuild</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token keyword\">return</span> element<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>该方法负责创建根element，即<code>RenderObjectToWidgetElement</code>，并且将element与widget 进行关联，即创建出 widget树对应的element树。如果element 已经创建过了，则将根element 中关联的widget 设为新的，由此可以看出element 只会创建一次，后面会进行复用。那么<code>BuildOwner</code>是什么呢？其实他就是widget framework的管理类，它跟踪哪些widget需要重新构建。</p> <h3 id=\"渲染\"><a href=\"#渲染\" class=\"header-anchor\">#</a> 渲染</h3> <p>回到<code>runApp</code>的实现中，当调用完<code>attachRootWidget</code>后，最后一行会调用 <code>WidgetsFlutterBinding</code> 实例的 <code>scheduleWarmUpFrame()</code> 方法，该方法的实现在<code>SchedulerBinding</code> 中，它被调用后会立即进行一次绘制（而不是等待&quot;vsync&quot; 信号），在此次绘制结束前，该方法会锁定事件分发，也就是说在本次绘制结束完成之前Flutter将不会响应各种事件，这可以保证在绘制过程中不会再触发新的重绘。下面是<code>scheduleWarmUpFrame()</code> 方法的部分实现(省略了无关代码)：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">scheduleWarmUpFrame</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  Timer<span class=\"token punctuation\">.</span><span class=\"token function\">run</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">handleBeginFrame</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> \n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  Timer<span class=\"token punctuation\">.</span><span class=\"token function\">run</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">handleDrawFrame</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>  \n    <span class=\"token function\">resetEpoch</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">// 锁定事件</span>\n  <span class=\"token function\">lockEvents</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">await</span> endOfFrame<span class=\"token punctuation\">;</span>\n    Timeline<span class=\"token punctuation\">.</span><span class=\"token function\">finishSync</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以看到该方法中主要调用了<code>handleBeginFrame()</code> 和 <code>handleDrawFrame()</code> 两个方法，在看这两个方法之前我们首先了解一下Frame 和 FrameCallback 的概念：</p> <ul><li><p>Frame: 一次绘制过程，我们称其为一帧。Flutter engine受显示器垂直同步信号&quot;VSync&quot;的驱使不断的触发绘制。我们之前说的Flutter可以实现60fps（Frame Per-Second），就是指一秒钟可以触发60次重绘，FPS值越大，界面就越流畅。</p></li> <li><p>FrameCallback：<code>SchedulerBinding</code> 类中有三个FrameCallback回调队列， 在一次绘制过程中，这三个回调队列会放在不同时机被执行：</p> <ol><li><code>transientCallbacks</code>：用于存放一些临时回调，一般存放动画回调。可以通过<code>SchedulerBinding.instance.scheduleFrameCallback</code> 添加回调。</li> <li><code>persistentCallbacks</code>：用于存放一些持久的回调，不能在此类回调中再请求新的绘制帧，持久回调一经注册则不能移除。<code>SchedulerBinding.instance.addPersitentFrameCallback()</code>，这个回调中处理了布局与绘制工作。</li> <li><code>postFrameCallbacks</code>：在Frame结束时只会被调用一次，调用后会被系统移除，可由 <code>SchedulerBinding.instance.addPostFrameCallback()</code> 注册，注意，不要在此类回调中再触发新的Frame，这可以会导致循环刷新。</li></ol></li></ul> <p>现在请读者自行查看<code>handleBeginFrame()</code> 和 <code>handleDrawFrame()</code> 两个方法的源码，可以发现前者主要是执行了<code>transientCallbacks</code>队列，而后者执行了 <code>persistentCallbacks</code> 和 <code>postFrameCallbacks</code> 队列。</p> <h3 id=\"绘制\"><a href=\"#绘制\" class=\"header-anchor\">#</a> 绘制</h3> <p>渲染和绘制逻辑在<code>RendererBinding</code>中实现，查看其源码，发现在其<code>initInstances()</code>方法中有如下代码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">initInstances</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n      \n  <span class=\"token comment\">//监听Window对象的事件  </span>\n  ui<span class=\"token punctuation\">.</span>window\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>onMetricsChanged <span class=\"token operator\">=</span> handleMetricsChanged\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>onTextScaleFactorChanged <span class=\"token operator\">=</span> handleTextScaleFactorChanged\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>onSemanticsEnabledChanged <span class=\"token operator\">=</span> _handleSemanticsEnabledChanged\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>onSemanticsAction <span class=\"token operator\">=</span> _handleSemanticsAction<span class=\"token punctuation\">;</span>\n   \n  <span class=\"token comment\">//添加PersistentFrameCallback    </span>\n  <span class=\"token function\">addPersistentFrameCallback</span><span class=\"token punctuation\">(</span>_handlePersistentFrameCallback<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们看最后一行，通过<code>addPersistentFrameCallback</code> 向<code>persistentCallbacks</code>队列添加了一个回调 <code>_handlePersistentFrameCallback</code>:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">_handlePersistentFrameCallback</span><span class=\"token punctuation\">(</span>Duration timeStamp<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">drawFrame</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>该方法直接调用了<code>RendererBinding</code>的<code>drawFrame()</code>方法：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">drawFrame</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span>renderView <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  pipelineOwner<span class=\"token punctuation\">.</span><span class=\"token function\">flushLayout</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//布局</span>\n  pipelineOwner<span class=\"token punctuation\">.</span><span class=\"token function\">flushCompositingBits</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//重绘之前的预处理操作，检查RenderObject是否需要重绘</span>\n  pipelineOwner<span class=\"token punctuation\">.</span><span class=\"token function\">flushPaint</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// 重绘</span>\n  renderView<span class=\"token punctuation\">.</span><span class=\"token function\">compositeFrame</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// 将需要绘制的比特数据发给GPU</span>\n  pipelineOwner<span class=\"token punctuation\">.</span><span class=\"token function\">flushSemantics</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// this also sends the semantics to the OS.</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们看看这些方法分别做了什么：</p> <h4 id=\"flushlayout\"><a href=\"#flushlayout\" class=\"header-anchor\">#</a> flushLayout()</h4> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">flushLayout</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n   <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n    <span class=\"token keyword\">while</span> <span class=\"token punctuation\">(</span>_nodesNeedingLayout<span class=\"token punctuation\">.</span>isNotEmpty<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">final</span> List<span class=\"token operator\">&lt;</span>RenderObject<span class=\"token operator\">&gt;</span> dirtyNodes <span class=\"token operator\">=</span> _nodesNeedingLayout<span class=\"token punctuation\">;</span>\n      _nodesNeedingLayout <span class=\"token operator\">=</span> <span class=\"token operator\">&lt;</span>RenderObject<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span>RenderObject node <span class=\"token keyword\">in</span> \n           dirtyNodes<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token function\">sort</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>RenderObject a<span class=\"token punctuation\">,</span> RenderObject b<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> a<span class=\"token punctuation\">.</span>depth <span class=\"token operator\">-</span> b<span class=\"token punctuation\">.</span>depth<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>node<span class=\"token punctuation\">.</span>_needsLayout <span class=\"token operator\">&amp;&amp;</span> node<span class=\"token punctuation\">.</span>owner <span class=\"token operator\">==</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span>\n          node<span class=\"token punctuation\">.</span><span class=\"token function\">_layoutWithoutResize</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span> \n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>源码很简单，该方法主要任务是更新了所有被标记为“dirty”的<code>RenderObject</code>的布局信息。主要的动作发生在<code>node._layoutWithoutResize()</code>方法中，该方法中会调用<code>performLayout()</code>进行重新布局。</p> <h4 id=\"flushcompositingbits\"><a href=\"#flushcompositingbits\" class=\"header-anchor\">#</a> flushCompositingBits()</h4> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">flushCompositingBits</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  _nodesNeedingCompositingBitsUpdate<span class=\"token punctuation\">.</span><span class=\"token function\">sort</span><span class=\"token punctuation\">(</span>\n      <span class=\"token punctuation\">(</span>RenderObject a<span class=\"token punctuation\">,</span> RenderObject b<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> a<span class=\"token punctuation\">.</span>depth <span class=\"token operator\">-</span> b<span class=\"token punctuation\">.</span>depth\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span>RenderObject node <span class=\"token keyword\">in</span> _nodesNeedingCompositingBitsUpdate<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>node<span class=\"token punctuation\">.</span>_needsCompositingBitsUpdate <span class=\"token operator\">&amp;&amp;</span> node<span class=\"token punctuation\">.</span>owner <span class=\"token operator\">==</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span>\n      node<span class=\"token punctuation\">.</span><span class=\"token function\">_updateCompositingBits</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//更新RenderObject.needsCompositing属性值</span>\n  <span class=\"token punctuation\">}</span>\n  _nodesNeedingCompositingBitsUpdate<span class=\"token punctuation\">.</span><span class=\"token function\">clear</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>检查<code>RenderObject</code>是否需要重绘，然后更新<code>RenderObject.needsCompositing</code>属性，如果该属性值被标记为<code>true</code>则需要重绘。</p> <h4 id=\"flushpaint\"><a href=\"#flushpaint\" class=\"header-anchor\">#</a> flushPaint()</h4> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">flushPaint</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">final</span> List<span class=\"token operator\">&lt;</span>RenderObject<span class=\"token operator\">&gt;</span> dirtyNodes <span class=\"token operator\">=</span> _nodesNeedingPaint<span class=\"token punctuation\">;</span> \n    _nodesNeedingPaint <span class=\"token operator\">=</span> <span class=\"token operator\">&lt;</span>RenderObject<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">// 反向遍历需要重绘的RenderObject</span>\n    <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span>RenderObject node <span class=\"token keyword\">in</span> \n         dirtyNodes<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token function\">sort</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>RenderObject a<span class=\"token punctuation\">,</span> RenderObject b<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> b<span class=\"token punctuation\">.</span>depth <span class=\"token operator\">-</span> a<span class=\"token punctuation\">.</span>depth<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>node<span class=\"token punctuation\">.</span>_needsPaint <span class=\"token operator\">&amp;&amp;</span> node<span class=\"token punctuation\">.</span>owner <span class=\"token operator\">==</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>node<span class=\"token punctuation\">.</span>_layer<span class=\"token punctuation\">.</span>attached<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token comment\">// 真正的绘制逻辑  </span>\n          PaintingContext<span class=\"token punctuation\">.</span><span class=\"token function\">repaintCompositedChild</span><span class=\"token punctuation\">(</span>node<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n          node<span class=\"token punctuation\">.</span><span class=\"token function\">_skippedPaintingOnLayer</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span> \n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>该方法进行了最终的绘制，可以看出它不是重绘了所有 <code>RenderObject</code>，而是只重绘了需要重绘的 <code>RenderObject</code>。真正的绘制是通过<code>PaintingContext.repaintCompositedChild()</code>来绘制的，该方法最终会调用Flutter engine提供的Canvas API来完成绘制。</p> <h4 id=\"compositeframe\"><a href=\"#compositeframe\" class=\"header-anchor\">#</a> compositeFrame()</h4> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">compositeFrame</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">final</span> ui<span class=\"token punctuation\">.</span>SceneBuilder builder <span class=\"token operator\">=</span> ui<span class=\"token punctuation\">.</span><span class=\"token function\">SceneBuilder</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">final</span> ui<span class=\"token punctuation\">.</span>Scene scene <span class=\"token operator\">=</span> layer<span class=\"token punctuation\">.</span><span class=\"token function\">buildScene</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>automaticSystemUiAdjustment<span class=\"token punctuation\">)</span>\n      <span class=\"token function\">_updateSystemChrome</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    ui<span class=\"token punctuation\">.</span>window<span class=\"token punctuation\">.</span><span class=\"token function\">render</span><span class=\"token punctuation\">(</span>scene<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//调用Flutter engine的渲染API</span>\n    scene<span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> \n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">finally</span> <span class=\"token punctuation\">{</span>\n    Timeline<span class=\"token punctuation\">.</span><span class=\"token function\">finishSync</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>这个方法中有一个<code>Scene</code>对象，Scene对象是一个数据结构，保存最终渲染后的像素信息。这个方法将Canvas画好的<code>Scene</code>传给<code>window.render()</code>方法，该方法会直接将scene信息发送给Flutter engine，最终由engine将图像画在设备屏幕上。</p> <h4 id=\"最后\"><a href=\"#最后\" class=\"header-anchor\">#</a> 最后</h4> <p>需要注意的是：由于<code>RendererBinding</code>只是一个mixin，而with它的是<code>WidgetsBinding</code>，所以我们需要看看<code>WidgetsBinding</code>中是否重写该方法，查看<code>WidgetsBinding</code>的<code>drawFrame()</code>方法源码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token metadata symbol\">@override</span>\n<span class=\"token keyword\">void</span> <span class=\"token function\">drawFrame</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token comment\">//省略无关代码</span>\n  <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>renderViewElement <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span>\n      buildOwner<span class=\"token punctuation\">.</span><span class=\"token function\">buildScope</span><span class=\"token punctuation\">(</span>renderViewElement<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> \n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">drawFrame</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//调用RendererBinding的drawFrame()方法</span>\n    buildOwner<span class=\"token punctuation\">.</span><span class=\"token function\">finalizeTree</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span> \n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们发现在调用<code>RendererBinding.drawFrame()</code>方法前会调用 <code>buildOwner.buildScope()</code> （非首次绘制），该方法会将被标记为“dirty” 的 element 进行 <code>rebuild()</code> 。</p> <h3 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h3> <p>本节介绍了Flutter APP从启动到显示到屏幕上的主流程，读者可以结合前面章节对Widget、Element以及RenderObject的介绍来加强细节理解。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/198.2a531f59.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter14/flutter_ui_system.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>14.1 Flutter UI系统 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/199.7dc1f153.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter14/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_14-1-flutter-ui系统\"><a href=\"#_14-1-flutter-ui系统\" class=\"header-anchor\">#</a> 14.1 Flutter UI系统</h1> <p>在本书的前面章节中，我们多次提到&quot;UI系统&quot;这个概念，本书中所指的UI系统特指：基于一个平台，在此平台上实现GUI的一个系统，这里的平台特指操作系统，如Android、iOS或者Windows、macOS。我们说过各个平台UI系统的原理是相通的，也就是说无论是Android还是iOS，他们将一个用户界面展示到屏幕的流程是相似的，所以，在介绍Flutter UI系统之前，我们先看看UI系统的基本原理，这样可以帮助读者对操作系统和系统底层UI逻辑有一个清晰的认识。</p> <h3 id=\"硬件绘图基本原理\"><a href=\"#硬件绘图基本原理\" class=\"header-anchor\">#</a> 硬件绘图基本原理</h3> <p>提到原理，我们要从屏幕显示图像的基本原理谈起。我们知道显示器（屏幕）是由一个个物理显示单元组成，每一个单元我们可以称之为一个物理像素点，而每一个像素点可以发出多种颜色，显示器成相的原理就是在不同的物理像素点上显示不同的颜色，最终构成完整的图像。</p> <p>一个像素点能发出的所有颜色总数是显示器的一个重要指标，比如我们所说的1600万色的屏幕就是指一个像素点可以显示出1600万种颜色，而显示器颜色是有RGB三基色组成，所以1600万即2的24次方，即每个基本色（R、G、B）深度扩展至8 bit(位)，颜色深度越深，所能显示的色彩更加丰富靓丽。</p> <p>为了更新显示画面，显示器是以固定的频率刷新（从GPU取数据），比如有一部手机屏幕的刷新频率是 60Hz。当一帧图像绘制完毕后准备绘制下一帧时，显示器会发出一个垂直同步信号（如VSync）， 60Hz的屏幕就会一秒内发出 60次这样的信号。而这个信号主要是用于同步CPU、GPU和显示器的。一般地来说，计算机系统中，CPU、GPU和显示器以一种特定的方式协作：CPU将计算好的显示内容提交给 GPU，GPU渲染后放入帧缓冲区，然后视频控制器按照同步信号从帧缓冲区取帧数据传递给显示器显示。</p> <p>CPU和GPU的任务是各有偏重的，CPU主要用于基本数学和逻辑计算，而GPU主要执行和图形处理相关的复杂的数学，如矩阵变化和几何计算，GPU的主要作用就是确定最终输送给显示器的各个像素点的色值。</p> <h3 id=\"操作系统绘制api的封装\"><a href=\"#操作系统绘制api的封装\" class=\"header-anchor\">#</a> 操作系统绘制API的封装</h3> <p>由于最终的图形计算和绘制都是由相应的硬件来完成，而直接操作硬件的指令通常都会有操作系统屏蔽，应用开发者通常不会直接面对硬件，操作系统屏蔽了这些底层硬件操作后会提供一些封装后的API供操作系统之上的应用调用，但是对于应用开发者来说，直接调用这些操作系统提供的API是比较复杂和低效的，因为操作系统提供的API往往比较基础，直接调用需要了解API的很多细节。正是因为这个原因，几乎所有用于开发GUI程序的编程语言都会在操作系统之上再封装一层，将操作系统原生API封装在一个编程框架和模型中，然后定义一种简单的开发规则来开发GUI应用程序，而这一层抽象，正是我们所说的“UI”系统，如Android SDK正是封装了Android操作系统API，提供了一个“UI描述文件XML+Java操作DOM”的UI系统，而iOS的UIKit 对View的抽象也是一样的，他们都将操作系统API抽象成一个基础对象（如用于2D图形绘制的Canvas），然后再定义一套规则来描述UI，如UI树结构，UI操作的单线程原则等。</p> <h3 id=\"flutter-ui系统\"><a href=\"#flutter-ui系统\" class=\"header-anchor\">#</a> Flutter UI系统</h3> <p>我们可以看到，无论是Android SDK还是iOS的UIKit 的职责都是相同的，它们只是语言载体和底层的系统不同而已。那么可不可以实现这么一个UI系统：可以使用同一种编程语言开发，然后针对不同操作系统API抽象一个对上接口一致，对下适配不同操作系统的的中间层，然后在打包编译时再使用相应的中间层代码？如果可以做到，那么我们就可以使用同一套代码编写跨平台的应用了。而Flutter的原理正是如此，它提供了一套Dart API，然后在底层通过OpenGL这种跨平台的绘制库（内部会调用操作系统API）实现了一套代码跨多端。由于Dart API也是调用操作系统API，所以它的性能接近原生。</p> <blockquote><p>注意，虽然Dart是先调用了OpenGL，OpenGL才会调用操作系统API，但是这仍然是原生渲染，因为OpenGL只是操作系统API的一个封装库，它并不像WebView渲染那样需要JavaScript运行环境和CSS渲染器，所以不会有性能损失。</p></blockquote> <p>至此，我们已经介绍了Flutter UI系统和操作系统交互的这一部分原理，现在需要说一些它对应用开发者定义的开发标准。其实在前面的章节中，我们已经对这个标准非常熟悉了, 简单概括就是：组合和响应式。我们要开发一个UI界面，需要通过组合其它Widget来实现，Flutter中，一切都是Widget，当UI要发生变化时，我们不去直接修改DOM，而是通过更新状态，让Flutter UI系统来根据新的状态来重新构建UI。</p> <p>讲到这里，读者可能发现Flutter UI系统和Flutter Framework的概念是差不多的，的确如此，之所以用“UI系统”，是因为其他平台中可能不这么叫，我们只是为了概念统一，便于描述，读者不必纠结于概念本身。</p> <p>在接下来的小节中，我们先详细介绍一下<code>Element</code>、<code>RenderObject</code>，它们是组成Flutter UI系统的基石。最后我们再分析一下Flutter应用启动和运行的整体过程。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/199.7dc1f153.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter14/image_and_cache.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>14.5 图片加载原理与缓存 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/115.374ee72b.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter14/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_14-5-图片加载原理与缓存\"><a href=\"#_14-5-图片加载原理与缓存\" class=\"header-anchor\">#</a> 14.5 图片加载原理与缓存</h1> <p>在本书前面章节已经介绍过<code>Image</code> 组件，并提到Flutter框架对加载过的图片是有缓存的（内存），默认最大缓存数量是1000，最大缓存空间为100M。本节便详细介绍Image的原理及图片缓存机制，下面我们先看看<code>ImageProvider</code> 类。</p> <h2 id=\"_14-5-1-imageprovider\"><a href=\"#_14-5-1-imageprovider\" class=\"header-anchor\">#</a> 14.5.1 ImageProvider</h2> <p>我们已经知道<code>Image</code> 组件的<code>image</code> 参数是一个必选参数，它是<code>ImageProvider</code>类型。下面我们便详细介绍一下<code>ImageProvider</code>，<code>ImageProvider</code>是一个抽象类，定义了图片数据获取和加载的相关接口。它的主要职责有两个：</p> <ol><li>提供图片数据源</li> <li>缓存图片</li></ol> <p>我们看看<code>ImageProvider</code>抽象类的详细定义：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">abstract</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">ImageProvider</span><span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n\n  ImageStream <span class=\"token function\">resolve</span><span class=\"token punctuation\">(</span>ImageConfiguration configuration<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 实现代码省略</span>\n  <span class=\"token punctuation\">}</span>\n  Future<span class=\"token operator\">&lt;</span>bool<span class=\"token operator\">&gt;</span> <span class=\"token function\">evict</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> ImageCache cache<span class=\"token punctuation\">,</span>\n                      ImageConfiguration configuration <span class=\"token operator\">=</span> ImageConfiguration<span class=\"token punctuation\">.</span>empty <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 实现代码省略</span>\n  <span class=\"token punctuation\">}</span>\n\n  Future<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span> <span class=\"token function\">obtainKey</span><span class=\"token punctuation\">(</span>ImageConfiguration configuration<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> \n  <span class=\"token metadata symbol\">@protected</span>\n  ImageStreamCompleter <span class=\"token function\">load</span><span class=\"token punctuation\">(</span>T key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// 需子类实现</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h4 id=\"load-t-key-方法\"><a href=\"#load-t-key-方法\" class=\"header-anchor\">#</a> <code>load(T key)</code>方法</h4> <p>加载图片数据源的接口，不同的数据源的加载方法不同，每个<code>ImageProvider</code>的子类必须实现它。比如<code>NetworkImage</code>类和<code>AssetImage</code>类，它们都是<code>ImageProvider</code>的子类，但它们需要从不同的数据源来加载图片数据：<code>NetworkImage</code>是从网络来加载图片数据，而<code>AssetImage</code>则是从最终的应用包里来加载（加载打到应用安装包里的资源图片）。 我们以<code>NetworkImage</code>为例，看看其load方法的实现：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>\n<span class=\"token metadata symbol\">@override</span>\nImageStreamCompleter <span class=\"token function\">load</span><span class=\"token punctuation\">(</span>image_provider<span class=\"token punctuation\">.</span>NetworkImage key<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n\n  <span class=\"token keyword\">final</span> StreamController<span class=\"token operator\">&lt;</span>ImageChunkEvent<span class=\"token operator\">&gt;</span> chunkEvents <span class=\"token operator\">=</span> StreamController<span class=\"token operator\">&lt;</span>ImageChunkEvent<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  \n  <span class=\"token keyword\">return</span> <span class=\"token function\">MultiFrameImageStreamCompleter</span><span class=\"token punctuation\">(</span>\n    codec<span class=\"token punctuation\">:</span> <span class=\"token function\">_loadAsync</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">,</span> chunkEvents<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//调用</span>\n    chunkEvents<span class=\"token punctuation\">:</span> chunkEvents<span class=\"token punctuation\">.</span>stream<span class=\"token punctuation\">,</span>\n    scale<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">.</span>scale<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们看到，<code>load</code>方法的返回值类型是<code>ImageStreamCompleter</code> ，它是一个抽象类，定义了管理图片加载过程的一些接口，<code>Image</code> Widget中正是通过它来监听图片加载状态的（我们将在下面介绍<code>Image</code> 原理时详细介绍）。</p> <p><code>MultiFrameImageStreamCompleter</code> 是 <code>ImageStreamCompleter</code>的一个子类，是flutter sdk预置的类，通过该类，我们以方便、轻松地创建出一个<code>ImageStreamCompleter</code>实例来做为<code>load</code>方法的返回值。</p> <p>我们可以看到，<code>MultiFrameImageStreamCompleter</code> 需要一个<code>codec</code>参数，该参数类型为<code>Future&lt;ui.Codec&gt;</code>。<code>Codec</code> 是处理图片编解码的类的一个handler，实际上，它只是一个flutter engine API 的包装类，也就是说图片的编解码逻辑不是在Dart 代码部分实现，而是在flutter engine中实现的。<code>Codec</code>类部分定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token metadata symbol\">@pragma</span><span class=\"token punctuation\">(</span><span class=\"token string\">'vm:entry-point'</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">Codec</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">NativeFieldWrapperClass2</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// 此类由flutter engine创建，不应该手动实例化此类或直接继承此类。</span>\n  <span class=\"token metadata symbol\">@pragma</span><span class=\"token punctuation\">(</span><span class=\"token string\">'vm:entry-point'</span><span class=\"token punctuation\">)</span>\n  Codec<span class=\"token punctuation\">.</span><span class=\"token function\">_</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">/// 图片中的帧数(动态图会有多帧)</span>\n  int <span class=\"token keyword\">get</span> frameCount native <span class=\"token string\">'Codec_frameCount'</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">/// 动画重复的次数</span>\n  <span class=\"token comment\">/// * 0 表示只执行一次</span>\n  <span class=\"token comment\">/// * -1 表示循环执行</span>\n  int <span class=\"token keyword\">get</span> repetitionCount native <span class=\"token string\">'Codec_repetitionCount'</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">/// 获取下一个动画帧</span>\n  Future<span class=\"token operator\">&lt;</span>FrameInfo<span class=\"token operator\">&gt;</span> <span class=\"token function\">getNextFrame</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">_futurize</span><span class=\"token punctuation\">(</span>_getNextFrame<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  String <span class=\"token function\">_getNextFrame</span><span class=\"token punctuation\">(</span>_Callback<span class=\"token operator\">&lt;</span>FrameInfo<span class=\"token operator\">&gt;</span> callback<span class=\"token punctuation\">)</span> native <span class=\"token string\">'Codec_getNextFrame'</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>我们可以看到<code>Codec</code>最终的结果是一个或多个（动图）帧，而这些帧最终会绘制到屏幕上。</p> <p><code>MultiFrameImageStreamCompleter 的</code> <code>codec</code>参数值为<code>_loadAsync</code>方法的返回值，我们继续看<code>_loadAsync</code>方法的实现：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>\n Future<span class=\"token operator\">&lt;</span>ui<span class=\"token punctuation\">.</span>Codec<span class=\"token operator\">&gt;</span> <span class=\"token function\">_loadAsync</span><span class=\"token punctuation\">(</span>\n    NetworkImage key<span class=\"token punctuation\">,</span>\n    StreamController<span class=\"token operator\">&lt;</span>ImageChunkEvent<span class=\"token operator\">&gt;</span> chunkEvents<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">//下载图片</span>\n      <span class=\"token keyword\">final</span> Uri resolved <span class=\"token operator\">=</span> Uri<span class=\"token punctuation\">.</span>base<span class=\"token punctuation\">.</span><span class=\"token function\">resolve</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">.</span>url<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">final</span> HttpClientRequest request <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> _httpClient<span class=\"token punctuation\">.</span><span class=\"token function\">getUrl</span><span class=\"token punctuation\">(</span>resolved<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      headers<span class=\"token operator\">?</span><span class=\"token punctuation\">.</span><span class=\"token function\">forEach</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>String name<span class=\"token punctuation\">,</span> String value<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        request<span class=\"token punctuation\">.</span>headers<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span>name<span class=\"token punctuation\">,</span> value<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">final</span> HttpClientResponse response <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> request<span class=\"token punctuation\">.</span><span class=\"token function\">close</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>response<span class=\"token punctuation\">.</span>statusCode <span class=\"token operator\">!=</span> HttpStatus<span class=\"token punctuation\">.</span>ok<span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">throw</span> <span class=\"token function\">Exception</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token comment\">// 接收图片数据 </span>\n      <span class=\"token keyword\">final</span> Uint8List bytes <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">consolidateHttpClientResponseBytes</span><span class=\"token punctuation\">(</span>\n        response<span class=\"token punctuation\">,</span>\n        onBytesReceived<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>int cumulative<span class=\"token punctuation\">,</span> int total<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          chunkEvents<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token function\">ImageChunkEvent</span><span class=\"token punctuation\">(</span>\n            cumulativeBytesLoaded<span class=\"token punctuation\">:</span> cumulative<span class=\"token punctuation\">,</span>\n            expectedTotalBytes<span class=\"token punctuation\">:</span> total<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>bytes<span class=\"token punctuation\">.</span>lengthInBytes <span class=\"token operator\">==</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">throw</span> <span class=\"token function\">Exception</span><span class=\"token punctuation\">(</span><span class=\"token string\">'NetworkImage is an empty file: $resolved'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token comment\">// 对图片数据进行解码</span>\n      <span class=\"token keyword\">return</span> PaintingBinding<span class=\"token punctuation\">.</span>instance<span class=\"token punctuation\">.</span><span class=\"token function\">instantiateImageCodec</span><span class=\"token punctuation\">(</span>bytes<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">finally</span> <span class=\"token punctuation\">{</span>\n      chunkEvents<span class=\"token punctuation\">.</span><span class=\"token function\">close</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以看到<code>_loadAsync</code>方法主要做了两件事：</p> <ol><li>下载图片。</li> <li>对下载的图片数据进行解码。</li></ol> <p>下载逻辑比较简单：通过<code>HttpClient</code>从网上下载图片，另外下载请求会设置一些自定义的header，开发者可以通过<code>NetworkImage</code>的<code>headers</code>命名参数来传递。</p> <p>在图片下载完成后调用了<code>PaintingBinding.instance.instantiateImageCodec(bytes)</code>对图片进行解码，值得注意的是<code>instantiateImageCodec(...)</code>也是一个Native API的包装，实际上会调用Flutter engine的<code>instantiateImageCodec</code>方法，源码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>String <span class=\"token function\">_instantiateImageCodec</span><span class=\"token punctuation\">(</span>Uint8List list<span class=\"token punctuation\">,</span> _Callback<span class=\"token operator\">&lt;</span>Codec<span class=\"token operator\">&gt;</span> callback<span class=\"token punctuation\">,</span> _ImageInfo imageInfo<span class=\"token punctuation\">,</span> int targetWidth<span class=\"token punctuation\">,</span> int targetHeight<span class=\"token punctuation\">)</span>\n  native <span class=\"token string\">'instantiateImageCodec'</span><span class=\"token punctuation\">;</span>\n</code></pre></div><h4 id=\"obtainkey-imageconfiguration-方法\"><a href=\"#obtainkey-imageconfiguration-方法\" class=\"header-anchor\">#</a> <code>obtainKey(ImageConfiguration)</code>方法</h4> <p>该接口主要是为了配合实现图片缓存，<code>ImageProvider</code>从数据源加载完数据后，会在全局的<code>ImageCache</code>中缓存图片数据，而图片数据缓存是一个Map，而Map的key便是调用此方法的返回值，不同的key代表不同的图片数据缓存。</p> <h4 id=\"resolve-imageconfiguration-方法\"><a href=\"#resolve-imageconfiguration-方法\" class=\"header-anchor\">#</a> <code>resolve(ImageConfiguration)</code> 方法</h4> <p><code>resolve</code>方法是<code>ImageProvider</code>的暴露的给<code>Image</code>的主入口方法，它接受一个<code>ImageConfiguration</code>参数，返回<code>ImageStream</code>，即图片数据流。我们重点看一下<code>resolve</code>执行流程：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>ImageStream <span class=\"token function\">resolve</span><span class=\"token punctuation\">(</span>ImageConfiguration configuration<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n  <span class=\"token keyword\">final</span> ImageStream stream <span class=\"token operator\">=</span> <span class=\"token function\">ImageStream</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  T obtainedKey<span class=\"token punctuation\">;</span> <span class=\"token comment\">//</span>\n  <span class=\"token comment\">//定义错误处理函数</span>\n  Future<span class=\"token operator\">&lt;</span><span class=\"token keyword\">void</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">handleError</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">dynamic</span> exception<span class=\"token punctuation\">,</span> StackTrace stack<span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n    stream<span class=\"token punctuation\">.</span><span class=\"token function\">setCompleter</span><span class=\"token punctuation\">(</span>imageCompleter<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    imageCompleter<span class=\"token punctuation\">.</span><span class=\"token function\">setError</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">// 创建一个新Zone，主要是为了当发生错误时不会干扰MainZone</span>\n  <span class=\"token keyword\">final</span> Zone dangerZone <span class=\"token operator\">=</span> Zone<span class=\"token punctuation\">.</span>current<span class=\"token punctuation\">.</span><span class=\"token function\">fork</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  \n  dangerZone<span class=\"token punctuation\">.</span><span class=\"token function\">runGuarded</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    Future<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span> key<span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">// 先验证是否已经有缓存</span>\n    <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">// 生成缓存key，后面会根据此key来检测是否有缓存</span>\n      key <span class=\"token operator\">=</span> <span class=\"token function\">obtainKey</span><span class=\"token punctuation\">(</span>configuration<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span>error<span class=\"token punctuation\">,</span> stackTrace<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">handleError</span><span class=\"token punctuation\">(</span>error<span class=\"token punctuation\">,</span> stackTrace<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">return</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    key<span class=\"token punctuation\">.</span>then<span class=\"token operator\">&lt;</span><span class=\"token keyword\">void</span><span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>T key<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      obtainedKey <span class=\"token operator\">=</span> key<span class=\"token punctuation\">;</span>\n      <span class=\"token comment\">// 缓存的处理逻辑在这里，记为A，下面详细介绍</span>\n      <span class=\"token keyword\">final</span> ImageStreamCompleter completer <span class=\"token operator\">=</span> PaintingBinding<span class=\"token punctuation\">.</span>instance\n          <span class=\"token punctuation\">.</span>imageCache<span class=\"token punctuation\">.</span><span class=\"token function\">putIfAbsent</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">load</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> onError<span class=\"token punctuation\">:</span> handleError<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>completer <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        stream<span class=\"token punctuation\">.</span><span class=\"token function\">setCompleter</span><span class=\"token punctuation\">(</span>completer<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">catchError</span><span class=\"token punctuation\">(</span>handleError<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">return</span> stream<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p><code>ImageConfiguration</code>  包含图片和设备的相关信息，如图片的大小、所在的<code>AssetBundle</code>(只有打到安装包的图片存在)以及当前的设备平台、devicePixelRatio（设备像素比等）。Flutter SDK提供了一个便捷函数<code>createLocalImageConfiguration</code>来创建<code>ImageConfiguration</code>  对象：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>ImageConfiguration <span class=\"token function\">createLocalImageConfiguration</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> Size size <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">ImageConfiguration</span><span class=\"token punctuation\">(</span>\n    bundle<span class=\"token punctuation\">:</span> DefaultAssetBundle<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    devicePixelRatio<span class=\"token punctuation\">:</span> MediaQuery<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> nullOk<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token operator\">?</span><span class=\"token punctuation\">.</span>devicePixelRatio <span class=\"token operator\">?</span><span class=\"token operator\">?</span> <span class=\"token number\">1.0</span><span class=\"token punctuation\">,</span>\n    locale<span class=\"token punctuation\">:</span> Localizations<span class=\"token punctuation\">.</span><span class=\"token function\">localeOf</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> nullOk<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    textDirection<span class=\"token punctuation\">:</span> Directionality<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    size<span class=\"token punctuation\">:</span> size<span class=\"token punctuation\">,</span>\n    platform<span class=\"token punctuation\">:</span> defaultTargetPlatform<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们可以发现这些信息基本都是通过<code>Context</code>来获取。</p> <p>上面代码A处就是处理缓存的主要代码，这里的<code>PaintingBinding.instance.imageCache</code> 是 <code>ImageCache</code>的一个实例，它是<code>PaintingBinding</code>的一个属性，而Flutter框架中的<code>PaintingBinding.instance</code>是一个单例，<code>imageCache</code>事实上也是一个单例，也就是说图片缓存是全局的，统一由<code>PaintingBinding.instance.imageCache</code> 来管理。</p> <p>下面我们看看<code>ImageCache</code>类定义：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">const</span> int _kDefaultSize <span class=\"token operator\">=</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">const</span> int _kDefaultSizeBytes <span class=\"token operator\">=</span> <span class=\"token number\">100</span> <span class=\"token operator\">&lt;&lt;</span> <span class=\"token number\">20</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// 100 MiB</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">ImageCache</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// 正在加载中的图片队列</span>\n  <span class=\"token keyword\">final</span> Map<span class=\"token operator\">&lt;</span>Object<span class=\"token punctuation\">,</span> _PendingImage<span class=\"token operator\">&gt;</span> _pendingImages <span class=\"token operator\">=</span> <span class=\"token operator\">&lt;</span>Object<span class=\"token punctuation\">,</span> _PendingImage<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">// 缓存队列</span>\n  <span class=\"token keyword\">final</span> Map<span class=\"token operator\">&lt;</span>Object<span class=\"token punctuation\">,</span> _CachedImage<span class=\"token operator\">&gt;</span> _cache <span class=\"token operator\">=</span> <span class=\"token operator\">&lt;</span>Object<span class=\"token punctuation\">,</span> _CachedImage<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// 缓存数量上限(1000)</span>\n  int _maximumSize <span class=\"token operator\">=</span> _kDefaultSize<span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">// 缓存容量上限 (100 MB)</span>\n  int _maximumSizeBytes <span class=\"token operator\">=</span> _kDefaultSizeBytes<span class=\"token punctuation\">;</span>\n  \n  <span class=\"token comment\">// 缓存上限设置的setter</span>\n  <span class=\"token keyword\">set</span> <span class=\"token function\">maximumSize</span><span class=\"token punctuation\">(</span>int value<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">}</span>\n  <span class=\"token keyword\">set</span> <span class=\"token function\">maximumSizeBytes</span><span class=\"token punctuation\">(</span>int value<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">}</span>\n \n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">// 省略部分定义</span>\n\n  <span class=\"token comment\">// 清除所有缓存</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">clear</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// ...省略具体实现代码</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">// 清除指定key对应的图片缓存</span>\n  bool <span class=\"token function\">evict</span><span class=\"token punctuation\">(</span>Object key<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n   <span class=\"token comment\">// ...省略具体实现代码</span>\n  <span class=\"token punctuation\">}</span>\n\n \n  ImageStreamCompleter <span class=\"token function\">putIfAbsent</span><span class=\"token punctuation\">(</span>Object key<span class=\"token punctuation\">,</span> ImageStreamCompleter <span class=\"token function\">loader</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> ImageErrorListener onError <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span>key <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span>loader <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    ImageStreamCompleter result <span class=\"token operator\">=</span> _pendingImages<span class=\"token punctuation\">[</span>key<span class=\"token punctuation\">]</span><span class=\"token operator\">?</span><span class=\"token punctuation\">.</span>completer<span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">// 图片还未加载成功，直接返回</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>result <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span>\n      <span class=\"token keyword\">return</span> result<span class=\"token punctuation\">;</span>\n \n    <span class=\"token comment\">// 有缓存，继续往下走</span>\n    <span class=\"token comment\">// 先移除缓存，后再添加，可以让最新使用过的缓存在_map中的位置更近一些，清理时会LRU来清除</span>\n    <span class=\"token keyword\">final</span> _CachedImage image <span class=\"token operator\">=</span> _cache<span class=\"token punctuation\">.</span><span class=\"token function\">remove</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>image <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _cache<span class=\"token punctuation\">[</span>key<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> image<span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">return</span> image<span class=\"token punctuation\">.</span>completer<span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n      result <span class=\"token operator\">=</span> <span class=\"token function\">loader</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span>error<span class=\"token punctuation\">,</span> stackTrace<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>onError <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">onError</span><span class=\"token punctuation\">(</span>error<span class=\"token punctuation\">,</span> stackTrace<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">return</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">rethrow</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">void</span> <span class=\"token function\">listener</span><span class=\"token punctuation\">(</span>ImageInfo info<span class=\"token punctuation\">,</span> bool syncCall<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">final</span> int imageSize <span class=\"token operator\">=</span> info<span class=\"token operator\">?</span><span class=\"token punctuation\">.</span>image <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">?</span> <span class=\"token number\">0</span> <span class=\"token punctuation\">:</span> info<span class=\"token punctuation\">.</span>image<span class=\"token punctuation\">.</span>height <span class=\"token operator\">*</span> info<span class=\"token punctuation\">.</span>image<span class=\"token punctuation\">.</span>width <span class=\"token operator\">*</span> <span class=\"token number\">4</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">final</span> _CachedImage image <span class=\"token operator\">=</span> <span class=\"token function\">_CachedImage</span><span class=\"token punctuation\">(</span>result<span class=\"token punctuation\">,</span> imageSize<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token comment\">// 下面是缓存处理的逻辑</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>maximumSizeBytes <span class=\"token operator\">&gt;</span> <span class=\"token number\">0</span> <span class=\"token operator\">&amp;&amp;</span> imageSize <span class=\"token operator\">&gt;</span> maximumSizeBytes<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        _maximumSizeBytes <span class=\"token operator\">=</span> imageSize <span class=\"token operator\">+</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n      _currentSizeBytes <span class=\"token operator\">+=</span> imageSize<span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">final</span> _PendingImage pendingImage <span class=\"token operator\">=</span> _pendingImages<span class=\"token punctuation\">.</span><span class=\"token function\">remove</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>pendingImage <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        pendingImage<span class=\"token punctuation\">.</span><span class=\"token function\">removeListener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n\n      _cache<span class=\"token punctuation\">[</span>key<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> image<span class=\"token punctuation\">;</span>\n      <span class=\"token function\">_checkCacheSize</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>maximumSize <span class=\"token operator\">&gt;</span> <span class=\"token number\">0</span> <span class=\"token operator\">&amp;&amp;</span> maximumSizeBytes <span class=\"token operator\">&gt;</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">final</span> ImageStreamListener streamListener <span class=\"token operator\">=</span> <span class=\"token function\">ImageStreamListener</span><span class=\"token punctuation\">(</span>listener<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      _pendingImages<span class=\"token punctuation\">[</span>key<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">_PendingImage</span><span class=\"token punctuation\">(</span>result<span class=\"token punctuation\">,</span> streamListener<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token comment\">// Listener is removed in [_PendingImage.removeListener].</span>\n      result<span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span>streamListener<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">return</span> result<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">// 当缓存数量超过最大值或缓存的大小超过最大缓存容量，会调用此方法清理到缓存上限以内</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_checkCacheSize</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n   <span class=\"token keyword\">while</span> <span class=\"token punctuation\">(</span>_currentSizeBytes <span class=\"token operator\">&gt;</span> _maximumSizeBytes <span class=\"token operator\">||</span> _cache<span class=\"token punctuation\">.</span>length <span class=\"token operator\">&gt;</span> _maximumSize<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">final</span> Object key <span class=\"token operator\">=</span> _cache<span class=\"token punctuation\">.</span>keys<span class=\"token punctuation\">.</span>first<span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">final</span> _CachedImage image <span class=\"token operator\">=</span> _cache<span class=\"token punctuation\">[</span>key<span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n      _currentSizeBytes <span class=\"token operator\">-=</span> image<span class=\"token punctuation\">.</span>sizeBytes<span class=\"token punctuation\">;</span>\n      _cache<span class=\"token punctuation\">.</span><span class=\"token function\">remove</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>有缓存则使用缓存，没有缓存则调用load方法加载图片，加载成功后:</p> <ol><li>先判断图片数据有没有缓存，如果有，则直接返回<code>ImageStream</code>。</li> <li>如果没有缓存，则调用<code>load(T key)</code>方法从数据源加载图片数据，加载成功后先缓存，然后返回ImageStream。</li></ol> <p>另外，我们可以看到<code>ImageCache</code>类中有设置缓存上限的setter，所以，如果我们可以自定义缓存上限：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code> PaintingBinding<span class=\"token punctuation\">.</span>instance<span class=\"token punctuation\">.</span>imageCache<span class=\"token punctuation\">.</span>maximumSize<span class=\"token operator\">=</span><span class=\"token number\">2000</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//最多2000张</span>\n PaintingBinding<span class=\"token punctuation\">.</span>instance<span class=\"token punctuation\">.</span>imageCache<span class=\"token punctuation\">.</span>maximumSizeBytes <span class=\"token operator\">=</span> <span class=\"token number\">200</span> <span class=\"token operator\">&lt;&lt;</span> <span class=\"token number\">20</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//最大200M</span>\n</code></pre></div><p>现在我们看一下缓存的key，因为Map中相同key的值会被覆盖，也就是说key是图片缓存的一个唯一标识，只要是不同key，那么图片数据就会分别缓存（即使事实上是同一张图片）。那么图片的唯一标识是什么呢？跟踪源码，很容易发现key正是<code>ImageProvider.obtainKey()</code>方法的返回值，而此方法需要<code>ImageProvider</code>子类去重写，这也就意味着不同的<code>ImageProvider</code>对key的定义逻辑会不同。其实也很好理解，比如对于<code>NetworkImage</code>，将图片的url作为key会很合适，而对于<code>AssetImage</code>，则应该将“包名+路径”作为唯一的key。下面我们以<code>NetworkImage</code>为例，看一下它的<code>obtainKey()</code>实现：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token metadata symbol\">@override</span>\nFuture<span class=\"token operator\">&lt;</span>NetworkImage<span class=\"token operator\">&gt;</span> <span class=\"token function\">obtainKey</span><span class=\"token punctuation\">(</span>image_provider<span class=\"token punctuation\">.</span>ImageConfiguration configuration<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> SynchronousFuture<span class=\"token operator\">&lt;</span>NetworkImage<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>代码很简单，创建了一个同步的future，然后直接将自身做为key返回。因为Map中在判断key（此时是<code>NetworkImage</code>对象）是否相等时会使用“==”运算符，那么定义key的逻辑就是<code>NetworkImage</code>的“==”运算符：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token metadata symbol\">@override</span>\nbool <span class=\"token keyword\">operator</span> <span class=\"token operator\">==</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">dynamic</span> other<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n  <span class=\"token keyword\">final</span> NetworkImage typedOther <span class=\"token operator\">=</span> other<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">return</span> url <span class=\"token operator\">==</span> typedOther<span class=\"token punctuation\">.</span>url\n      <span class=\"token operator\">&amp;&amp;</span> scale <span class=\"token operator\">==</span> typedOther<span class=\"token punctuation\">.</span>scale<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>很清晰，对于网络图片来说，会将其“url+缩放比例”作为缓存的key。也就是说<strong>如果两张图片的url或scale只要有一个不同，便会重新下载并分别缓存</strong>。</p> <p>另外，我们需要注意的是，图片缓存是在内存中，并没有进行本地文件持久化存储，这也是为什么网络图片在应用重启后需要重新联网下载的原因。</p> <p>同时也意味着在应用生命周期内，如果缓存没有超过上限，相同的图片只会被下载一次。</p> <h3 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h3> <p>上面主要结合源码，探索了<code>ImageProvider</code>的主要功能和原理，如果要用一句话来总结<code>ImageProvider</code>功能，那么应该是：加载图片数据并进行缓存、解码。在此再次提醒读者，Flutter的源码是非常好的第一手资料，建议读者多多探索，另外，在阅读源码学习的同时一定要有总结，这样才不至于在源码中迷失。</p> <h2 id=\"_14-5-2-image组件原理\"><a href=\"#_14-5-2-image组件原理\" class=\"header-anchor\">#</a> 14.5.2 Image组件原理</h2> <p>前面章节中我们介绍过<code>Image</code>的基础用法，现在我们更深入一些，研究一下<code>Image</code>是如何和<code>ImageProvider</code>配合来获取最终解码后的数据，然后又如何将图片绘制到屏幕上的。</p> <p>本节换一个思路，我们先不去直接看<code>Image</code>的源码，而根据已经掌握的知识来实现一个简版的“<code>Image</code>组件” <code>MyImage</code>，代码大致如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">MyImage</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function\">MyImage</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n    <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>imageProvider<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">:</span> <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span>imageProvider <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> ImageProvider imageProvider<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  _MyImageState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">_MyImageState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_MyImageState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>MyImage<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  ImageStream _imageStream<span class=\"token punctuation\">;</span>\n  ImageInfo _imageInfo<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">didChangeDependencies</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">didChangeDependencies</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">// 依赖改变时，图片的配置信息可能会发生改变</span>\n    <span class=\"token function\">_getImage</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">didUpdateWidget</span><span class=\"token punctuation\">(</span>MyImage oldWidget<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">didUpdateWidget</span><span class=\"token punctuation\">(</span>oldWidget<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>widget<span class=\"token punctuation\">.</span>imageProvider <span class=\"token operator\">!=</span> oldWidget<span class=\"token punctuation\">.</span>imageProvider<span class=\"token punctuation\">)</span>\n      <span class=\"token function\">_getImage</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_getImage</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">final</span> ImageStream oldImageStream <span class=\"token operator\">=</span> _imageStream<span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">// 调用imageProvider.resolve方法，获得ImageStream。</span>\n    _imageStream <span class=\"token operator\">=</span>\n        widget<span class=\"token punctuation\">.</span>imageProvider<span class=\"token punctuation\">.</span><span class=\"token function\">resolve</span><span class=\"token punctuation\">(</span><span class=\"token function\">createLocalImageConfiguration</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//判断新旧ImageStream是否相同，如果不同，则需要调整流的监听器</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>_imageStream<span class=\"token punctuation\">.</span>key <span class=\"token operator\">!=</span> oldImageStream<span class=\"token operator\">?</span><span class=\"token punctuation\">.</span>key<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">final</span> ImageStreamListener listener <span class=\"token operator\">=</span> <span class=\"token function\">ImageStreamListener</span><span class=\"token punctuation\">(</span>_updateImage<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      oldImageStream<span class=\"token operator\">?</span><span class=\"token punctuation\">.</span><span class=\"token function\">removeListener</span><span class=\"token punctuation\">(</span>listener<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      _imageStream<span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span>listener<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_updateImage</span><span class=\"token punctuation\">(</span>ImageInfo imageInfo<span class=\"token punctuation\">,</span> bool synchronousCall<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">// Trigger a build whenever the image changes.</span>\n      _imageInfo <span class=\"token operator\">=</span> imageInfo<span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    _imageStream<span class=\"token punctuation\">.</span><span class=\"token function\">removeListener</span><span class=\"token punctuation\">(</span><span class=\"token function\">ImageStreamListener</span><span class=\"token punctuation\">(</span>_updateImage<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">RawImage</span><span class=\"token punctuation\">(</span>\n      image<span class=\"token punctuation\">:</span> _imageInfo<span class=\"token operator\">?</span><span class=\"token punctuation\">.</span>image<span class=\"token punctuation\">,</span> <span class=\"token comment\">// this is a dart:ui Image object</span>\n      scale<span class=\"token punctuation\">:</span> _imageInfo<span class=\"token operator\">?</span><span class=\"token punctuation\">.</span>scale <span class=\"token operator\">?</span><span class=\"token operator\">?</span> <span class=\"token number\">1.0</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面代码流程如下：</p> <ol><li>通过<code>imageProvider.resolve</code>方法可以得到一个<code>ImageStream</code>（图片数据流），然后监听<code>ImageStream</code>的变化。当图片数据源发生变化时，<code>ImageStream</code>会触发相应的事件，而本例中我们只设置了图片成功的监听器<code>_updateImage</code>，而<code>_updateImage</code>中只更新了<code>_imageInfo</code>。值得注意的是，如果是静态图，<code>ImageStream</code>只会触发一次时间，如果是动态图，则会触发多次事件，每一次都会有一个解码后的图片帧。</li> <li><code>_imageInfo</code> 更新后会rebuild，此时会创建一个<code>RawImage</code> Widget。<code>RawImage</code>最终会通过<code>RenderImage</code>来将图片绘制在屏幕上。如果继续跟进<code>RenderImage</code>类，我们会发现<code>RenderImage</code>的<code>paint</code> 方法中调用了<code>paintImage</code>方法，而<code>paintImage</code>方法中通过<code>Canvas</code>的<code>drawImageRect(…)</code>、<code>drawImageNine(...)</code>等方法来完成最终的绘制。</li> <li>最终的绘制由<code>RawImage</code>来完成。</li></ol> <p>下面测试一下<code>MyImage</code>：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">ImageInternalTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n      children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n        <span class=\"token function\">MyImage</span><span class=\"token punctuation\">(</span>\n          imageProvider<span class=\"token punctuation\">:</span> <span class=\"token function\">NetworkImage</span><span class=\"token punctuation\">(</span>\n            <span class=\"token string\">&quot;https://avatars2.githubusercontent.com/u/20411648?s=460&amp;v=4&quot;</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行效果如图14-4所示：</p> <p><img src=\"/assets/img/14-4.4a6d698c.png\" alt=\"图14-4\"></p> <p>成功了！ 现在，想必<code>Image</code> Widget的源码已经没必要在花费篇章去介绍了，读者有兴趣可以自行去阅读。</p> <h2 id=\"总结-2\"><a href=\"#总结-2\" class=\"header-anchor\">#</a> 总结</h2> <p>本节主要介绍了Flutter 图片的加载、缓存和绘制流程。其中<code>ImageProvider</code>主要负责图片数据的加载和缓存，而绘制部分逻辑主要是由<code>RawImage</code>来完成。 而<code>Image</code>正是连接起<code>ImageProvider</code>和<code>RawImage</code> 的桥梁。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/115.374ee72b.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter14/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>本章目录 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/200.92165c5b.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter14/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"本章目录\"><a href=\"#本章目录\" class=\"header-anchor\">#</a> 本章目录</h1> <ul><li><a href=\"/v2/chapter14/flutter_ui_system.html\">Flutter UI系统</a></li> <li><a href=\"/v2/chapter14/element_buildcontext.html\">Element和BuildContext</a></li> <li><a href=\"/v2/chapter14/render_object.html\">RenderObject和RenderBox</a></li> <li><a href=\"/v2/chapter14/flutter_app_startup.html\">Flutter从启动到显示</a></li></ul></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/200.92165c5b.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter14/render_object.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>14.3 RenderObject和RenderBox | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/116.5453b788.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter14/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_14-3-renderobject和renderbox\"><a href=\"#_14-3-renderobject和renderbox\" class=\"header-anchor\">#</a> 14.3 RenderObject和RenderBox</h1> <p>在上一节我们说过每个<code>Element</code>都对应一个<code>RenderObject</code>，我们可以通过<code>Element.renderObject</code> 来获取。并且我们也说过<code>RenderObject</code>的主要职责是Layout和绘制，所有的<code>RenderObject</code>会组成一棵渲染树Render Tree。本节我们将重点介绍一下<code>RenderObject</code>的作用。</p> <p><code>RenderObject</code>就是渲染树中的一个对象，它拥有一个<code>parent</code>和一个<code>parentData</code> 插槽（slot），所谓插槽，就是指预留的一个接口或位置，这个接口和位置是由其它对象来接入或占据的，这个接口或位置在软件中通常用预留变量来表示，而<code>parentData</code>正是一个预留变量，它正是由<code>parent</code> 来赋值的，<code>parent</code>通常会通过子<code>RenderObject</code>的<code>parentData</code>存储一些和子元素相关的数据，如在Stack布局中，<code>RenderStack</code>就会将子元素的偏移数据存储在子元素的<code>parentData</code>中（具体可以查看<code>Positioned</code>实现）。</p> <p><code>RenderObject</code>类本身实现了一套基础的layout和绘制协议，但是并没有定义子节点模型（如一个节点可以有几个子节点，没有子节点？一个？两个？或者更多？）。 它也没有定义坐标系统（如子节点定位是在笛卡尔坐标中还是极坐标？）和具体的布局协议（是通过宽高还是通过constraint和size?，或者是否由父节点在子节点布局之前或之后设置子节点的大小和位置等）。为此，Flutter提供了一个<code>RenderBox</code>类，它继承自``RenderObject<code>，布局坐标系统采用笛卡尔坐标系，这和Android和iOS原生坐标系是一致的，都是屏幕的top、left是原点，然后分宽高两个轴，大多数情况下，我们直接使用</code>RenderBox<code>就可以了，除非遇到要自定义布局模型或坐标系统的情况，下面我们重点介绍一下</code>RenderBox`。</p> <h2 id=\"_14-3-1-布局过程\"><a href=\"#_14-3-1-布局过程\" class=\"header-anchor\">#</a> 14.3.1 布局过程</h2> <h3 id=\"constraints\"><a href=\"#constraints\" class=\"header-anchor\">#</a> Constraints</h3> <p>在<code>RenderBox</code> 中，有个<code>size</code>属性用来保存控件的宽和高。<code>RenderBox</code>的layout是通过在组件树中从上往下传递<code>BoxConstraints</code>对象的实现的。<code>BoxConstraints</code>对象可以限制子节点的最大和最小宽高，子节点必须遵守父节点给定的限制条件。</p> <p>在布局阶段，父节点会调用子节点的<code>layout()</code>方法，下面我们看看<code>RenderObject</code>中<code>layout()</code>方法的大致实现（删掉了一些无关代码和异常捕获）:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">layout</span><span class=\"token punctuation\">(</span>Constraints constraints<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> bool parentUsesSize <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n   <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n   RenderObject relayoutBoundary<span class=\"token punctuation\">;</span> \n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>parentUsesSize <span class=\"token operator\">||</span> sizedByParent <span class=\"token operator\">||</span> constraints<span class=\"token punctuation\">.</span>isTight \n    \t<span class=\"token operator\">||</span> parent <span class=\"token operator\">is!</span> RenderObject<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      relayoutBoundary <span class=\"token operator\">=</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">final</span> RenderObject parent <span class=\"token operator\">=</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>parent<span class=\"token punctuation\">;</span>\n      relayoutBoundary <span class=\"token operator\">=</span> parent<span class=\"token punctuation\">.</span>_relayoutBoundary<span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>sizedByParent<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">performResize</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token function\">performLayout</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以看到<code>layout</code>方法需要传入两个参数，第一个为<code>constraints</code>，即 父节点对子节点大小的限制，该值根据父节点的布局逻辑确定。另外一个参数是 <code>parentUsesSize</code>，该值用于确定 <code>relayoutBoundary</code>，该参数表示子节点布局变化是否影响父节点，如果为<code>true</code>，当子节点布局发生变化时父节点都会标记为需要重新布局，如果为<code>false</code>，则子节点布局发生变化后不会影响父节点。</p> <h4 id=\"relayoutboundary\"><a href=\"#relayoutboundary\" class=\"header-anchor\">#</a> relayoutBoundary</h4> <p>上面<code>layout()</code>源码中定义了一个<code>relayoutBoundary</code>变量，什么是 <code>relayoutBoundary</code>？在前面介绍<code>Element</code>时，我们讲过当一个<code>Element</code>标记为 dirty 时便会重新build，这时<code>RenderObject</code>便会重新布局，我们是通过调用 <code>markNeedsBuild()</code> 来标记<code>Element</code>为dirty的。在<code>RenderObject</code>中有一个类似的<code>markNeedsLayout()</code>方法，它会将<code>RenderObject</code>的布局状态标记为 dirty，这样在下一个frame中便会重新layout，我们看看<code>RenderObject</code>的<code>markNeedsLayout()</code>的部分源码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">markNeedsLayout</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span>_relayoutBoundary <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>_relayoutBoundary <span class=\"token operator\">!=</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">markParentNeedsLayout</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n    _needsLayout <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>owner <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n      owner<span class=\"token punctuation\">.</span>_nodesNeedingLayout<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      owner<span class=\"token punctuation\">.</span><span class=\"token function\">requestVisualUpdate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>代码大致逻辑是先判断自身是不是<code>relayoutBoundary</code>，如果不是就继续向parent 查找，一直向上查找到是 <code>relayoutBoundary</code> 的 <code>RenderObject</code>为止，然后再将其标记为 dirty 的。这样来看它的作用就比较明显了，意思就是当一个控件的大小被改变时可能会影响到它的 parent，因此 parent 也需要被重新布局，那么到什么时候是个头呢？答案就是 <code>relayoutBoundary</code>，如果一个 <code>RenderObject</code> 是 <code>relayoutBoundary</code>，就表示它的大小变化不会再影响到 parent 的大小了，于是 parent 也就不用重新布局了。</p> <h4 id=\"performresize-和-performlayout\"><a href=\"#performresize-和-performlayout\" class=\"header-anchor\">#</a> performResize 和 performLayout</h4> <p><code>RenderBox</code>实际的测量和布局逻辑是在<code>performResize()</code> 和 <code>performLayout()</code>两个方法中，RenderBox子类需要实现这两个方法来定制自身的布局逻辑。根据<code>layout()</code> 源码可以看出只有 <code>sizedByParent</code> 为 <code>true</code> 时，<code>performResize()</code> 才会被调用，而 <code>performLayout()</code> 是每次布局都会被调用的。<code>sizedByParent</code> 意为该节点的大小是否仅通过 parent 传给它的 constraints 就可以确定了，即该节点的大小与它自身的属性和其子节点无关，比如如果一个控件永远充满 parent 的大小，那么 <code>sizedByParent</code>就应该返回<code>true</code>，此时其大小在 <code>performResize()</code> 中就确定了，在后面的 <code>performLayout()</code> 方法中将不会再被修改了，这种情况下 <code>performLayout()</code> 只负责布局子节点。</p> <p>在 <code>performLayout()</code> 方法中除了完成自身布局，也必须完成子节点的布局，这是因为只有父子节点全部完成后布局流程才算真正完成。所以最终的调用栈将会变成：<em>layout() &gt; performResize()/performLayout() &gt; child.layout() &gt; ...</em>  ，如此递归完成整个UI的布局。</p> <p><code>RenderBox</code>子类要定制布局算法不应该重写<code>layout()</code>方法，因为对于任何RenderBox的子类来说，它的layout流程基本是相同的，不同之处只在具体的布局算法，而具体的布局算法子类应该通过重写<code>performResize()</code> 和 <code>performLayout()</code>两个方法来实现，他们会在<code>layout()</code>中被调用。</p> <h4 id=\"parentdata\"><a href=\"#parentdata\" class=\"header-anchor\">#</a> ParentData</h4> <p>当layout结束后，每个节点的位置（相对于父节点的偏移）就已经确定了，<code>RenderObject</code>就可以根据位置信息来进行最终的绘制。但是在layout过程中，节点的位置信息怎么保存？对于大多数<code>RenderBox</code>子类来说如果子类只有一个子节点，那么子节点偏移一般都是<code>Offset.zero</code> ，如果有多个子节点，则每个子节点的偏移就可能不同。而子节点在父节点的偏移数据正是通过<code>RenderObject</code>的<code>parentData</code>属性来保存的。在<code>RenderBox</code>中，其<code>parentData</code>属性默认是一个<code>BoxParentData</code>对象，该属性只能通过父节点的<code>setupParentData()</code>方法来设置：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">abstract</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">RenderBox</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">RenderObject</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">setupParentData</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">covariant</span> RenderObject child<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">.</span>parentData <span class=\"token operator\">is!</span> BoxParentData<span class=\"token punctuation\">)</span>\n      child<span class=\"token punctuation\">.</span>parentData <span class=\"token operator\">=</span> <span class=\"token function\">BoxParentData</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p><code>BoxParentData</code>定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">/// Parentdata 会被RenderBox和它的子类使用.</span>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">BoxParentData</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">ParentData</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">/// offset表示在子节点在父节点坐标系中的绘制偏移  </span>\n  Offset offset <span class=\"token operator\">=</span> Offset<span class=\"token punctuation\">.</span>zero<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  String <span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token string\">'offset=$offset'</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><blockquote><p>一定要注意，<code>RenderObject</code>的<code>parentData</code> 只能通过父元素设置.</p></blockquote> <p>当然，<code>ParentData</code>并不仅仅可以用来存储偏移信息，通常所有和子节点特定的数据都可以存储到子节点的<code>ParentData</code>中，如<code>ContainerBox</code>的<code>ParentData</code>就保存了指向兄弟节点的<code>previousSibling</code>和<code>nextSibling</code>，<code>Element.visitChildren()</code>方法也正是通过它们来实现对子节点的遍历。再比如<code>KeepAlive</code> 组件，它使用<code>KeepAliveParentDataMixin</code>（继承自<code>ParentData</code>） 来保存子节的<code>keepAlive</code>状态。</p> <h2 id=\"_14-3-2-绘制过程\"><a href=\"#_14-3-2-绘制过程\" class=\"header-anchor\">#</a> 14.3.2 绘制过程</h2> <p><code>RenderObject</code>可以通过<code>paint()</code>方法来完成具体绘制逻辑，流程和布局流程相似，子类可以实现<code>paint()</code>方法来完成自身的绘制逻辑，<code>paint()</code>签名如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">paint</span><span class=\"token punctuation\">(</span>PaintingContext context<span class=\"token punctuation\">,</span> Offset offset<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span> <span class=\"token punctuation\">}</span>\n</code></pre></div><p>通过<code>context.canvas</code>可以取到<code>Canvas</code>对象，接下来就可以调用<code>Canvas</code> API来实现具体的绘制逻辑。</p> <p>如果节点有子节点，它除了完成自身绘制逻辑之外，还要调用子节点的绘制方法。我们以<code>RenderFlex</code>对象为例说明：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token metadata symbol\">@override</span>\n<span class=\"token keyword\">void</span> <span class=\"token function\">paint</span><span class=\"token punctuation\">(</span>PaintingContext context<span class=\"token punctuation\">,</span> Offset offset<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n\n  <span class=\"token comment\">// 如果子元素未超出当前边界，则绘制子元素  </span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>_overflow <span class=\"token operator\">&lt;=</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">defaultPaint</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> offset<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">// 如果size为空，则无需绘制</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>size<span class=\"token punctuation\">.</span>isEmpty<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// 剪裁掉溢出边界的部分</span>\n  context<span class=\"token punctuation\">.</span><span class=\"token function\">pushClipRect</span><span class=\"token punctuation\">(</span>needsCompositing<span class=\"token punctuation\">,</span> offset<span class=\"token punctuation\">,</span> Offset<span class=\"token punctuation\">.</span>zero <span class=\"token operator\">&amp;</span> size<span class=\"token punctuation\">,</span> defaultPaint<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">final</span> String debugOverflowHints <span class=\"token operator\">=</span> <span class=\"token string\">'...'</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//溢出提示内容，省略</span>\n    <span class=\"token comment\">// 绘制溢出部分的错误提示样式</span>\n    Rect overflowChildRect<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">switch</span> <span class=\"token punctuation\">(</span>_direction<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">case</span> Axis<span class=\"token punctuation\">.</span>horizontal<span class=\"token punctuation\">:</span>\n        overflowChildRect <span class=\"token operator\">=</span> Rect<span class=\"token punctuation\">.</span><span class=\"token function\">fromLTWH</span><span class=\"token punctuation\">(</span><span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span> size<span class=\"token punctuation\">.</span>width <span class=\"token operator\">+</span> _overflow<span class=\"token punctuation\">,</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">case</span> Axis<span class=\"token punctuation\">.</span>vertical<span class=\"token punctuation\">:</span>\n        overflowChildRect <span class=\"token operator\">=</span> Rect<span class=\"token punctuation\">.</span><span class=\"token function\">fromLTWH</span><span class=\"token punctuation\">(</span><span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span> size<span class=\"token punctuation\">.</span>height <span class=\"token operator\">+</span> _overflow<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>  \n    <span class=\"token function\">paintOverflowIndicator</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> offset<span class=\"token punctuation\">,</span> Offset<span class=\"token punctuation\">.</span>zero <span class=\"token operator\">&amp;</span> size<span class=\"token punctuation\">,</span>\n                           overflowChildRect<span class=\"token punctuation\">,</span> overflowHints<span class=\"token punctuation\">:</span> debugOverflowHints<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>代码很简单，首先判断有无溢出，如果没有则调用<code>defaultPaint(context, offset)</code>来完成绘制，该方法源码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">defaultPaint</span><span class=\"token punctuation\">(</span>PaintingContext context<span class=\"token punctuation\">,</span> Offset offset<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  ChildType child <span class=\"token operator\">=</span> firstChild<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">while</span> <span class=\"token punctuation\">(</span>child <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">final</span> ParentDataType childParentData <span class=\"token operator\">=</span> child<span class=\"token punctuation\">.</span>parentData<span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//绘制子节点， </span>\n    context<span class=\"token punctuation\">.</span><span class=\"token function\">paintChild</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">,</span> childParentData<span class=\"token punctuation\">.</span>offset <span class=\"token operator\">+</span> offset<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    child <span class=\"token operator\">=</span> childParentData<span class=\"token punctuation\">.</span>nextSibling<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>很明显，由于Flex本身没有需要绘制的东西，所以直接遍历其子节点，然后调用<code>paintChild()</code>来绘制子节点，同时将子节点<code>ParentData</code>中在layout阶段保存的offset加上自身偏移作为第二个参数传递给<code>paintChild()</code>。而如果子节点还有子节点时，<code>paintChild()</code>方法还会调用子节点的<code>paint()</code>方法，如此递归完成整个节点树的绘制，最终调用栈为： <em>paint() &gt; paintChild() &gt; paint() ...</em> 。</p> <p>当需要绘制的内容大小溢出当前空间时，将会执行<code>paintOverflowIndicator()</code> 来绘制溢出部分提示，这个就是我们经常看到的溢出提示，如图14-3所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"overflow\"></p> <h3 id=\"repaintboundary\"><a href=\"#repaintboundary\" class=\"header-anchor\">#</a> RepaintBoundary</h3> <p>我们已经在<code>CustomPaint</code>一节中介绍过<code>RepaintBoundary</code>，现在我们深入的了解一些。与 <code>RelayoutBoundary</code> 相似，<code>RepaintBoundary</code>是用于在确定重绘边界的，与<code>RelayoutBoundary</code>不同的是，这个绘制边界需要由开发者通过<code>RepaintBoundary</code> 组件自己指定，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">CustomPaint</span><span class=\"token punctuation\">(</span>\n  size<span class=\"token punctuation\">:</span> <span class=\"token function\">Size</span><span class=\"token punctuation\">(</span><span class=\"token number\">300</span><span class=\"token punctuation\">,</span> <span class=\"token number\">300</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//指定画布大小</span>\n  painter<span class=\"token punctuation\">:</span> <span class=\"token function\">MyPainter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">RepaintBoundary</span><span class=\"token punctuation\">(</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</code></pre></div><p>下面我们看看<code>RepaintBoundary</code>的原理，<code>RenderObject</code>有一个<code>isRepaintBoundary</code>属性，该属性决定这个<code>RenderObject</code>重绘时是否独立于其父元素，如果该属性值为<code>true</code> ，则独立绘制，反之则一起绘制。那独立绘制是怎么实现的呢？ 答案就在<code>paintChild()</code>源码中：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">paintChild</span><span class=\"token punctuation\">(</span>RenderObject child<span class=\"token punctuation\">,</span> Offset offset<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">.</span>isRepaintBoundary<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">stopRecordingIfNeeded</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token function\">_compositeChild</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">,</span> offset<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n    child<span class=\"token punctuation\">.</span><span class=\"token function\">_paintWithContext</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">,</span> offset<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们可以看到，在绘制子节点时，如果<code>child.isRepaintBoundary</code> 为 <code>true</code>则会调用<code>_compositeChild()</code>方法，<code>_compositeChild()</code>源码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">_compositeChild</span><span class=\"token punctuation\">(</span>RenderObject child<span class=\"token punctuation\">,</span> Offset offset<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// 给子节点创建一个layer ，然后再上面绘制子节点 </span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">.</span>_needsPaint<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">repaintCompositedChild</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">,</span> debugAlsoPaintedParent<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">.</span>_layer <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  child<span class=\"token punctuation\">.</span>_layer<span class=\"token punctuation\">.</span>offset <span class=\"token operator\">=</span> offset<span class=\"token punctuation\">;</span>\n  <span class=\"token function\">appendLayer</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">.</span>_layer<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>很明显了，独立绘制是通过在不同的layer（层）上绘制的。所以，很明显，正确使用<code>isRepaintBoundary</code>属性可以提高绘制效率，避免不必要的重绘。具体原理是：和触发重新build和layout类似，<code>RenderObject</code>也提供了一个<code>markNeedsPaint()</code>方法，其源码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">markNeedsPaint</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token comment\">//如果RenderObject.isRepaintBoundary 为true,则该RenderObject拥有layer，直接绘制  </span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>isRepaintBoundary<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>owner <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">//找到最近的layer，绘制  </span>\n      owner<span class=\"token punctuation\">.</span>_nodesNeedingPaint<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      owner<span class=\"token punctuation\">.</span><span class=\"token function\">requestVisualUpdate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>parent <span class=\"token operator\">is</span> RenderObject<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 没有自己的layer, 会和一个祖先节点共用一个layer  </span>\n    <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span>_layer <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">final</span> RenderObject parent <span class=\"token operator\">=</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>parent<span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">// 向父级递归查找  </span>\n    parent<span class=\"token punctuation\">.</span><span class=\"token function\">markNeedsPaint</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span>parent <span class=\"token operator\">==</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>parent<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 如果直到根节点也没找到一个Layer，那么便需要绘制自身，因为没有其它节点可以绘制根节点。  </span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>owner <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span>\n      owner<span class=\"token punctuation\">.</span><span class=\"token function\">requestVisualUpdate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以看出，当调用 <code>markNeedsPaint()</code> 方法时，会从当前 <code>RenderObject</code> 开始一直向父节点查找，直到找到 一个<code>isRepaintBoundary</code> 为 <code>true</code>的<code>RenderObject</code> 时，才会触发重绘，这样便可以实现局部重绘。当 有<code>RenderObject</code> 绘制的很频繁或很复杂时，可以通过RepaintBoundary Widget来指定<code>isRepaintBoundary</code> 为 <code>true</code>，这样在绘制时仅会重绘自身而无需重绘它的 parent，如此便可提高性能。</p> <p>还有一个问题，通过<code>RepaintBoundary</code> 如何设置<code>isRepaintBoundary</code>属性呢？其实，如果使用了<code>RepaintBoundary</code>，其对应的<code>RenderRepaintBoundary</code>会自动将<code>isRepaintBoundary</code>设为<code>true</code>的：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">RenderRepaintBoundary</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">RenderProxyBox</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">/// Creates a repaint boundary around [child].</span>\n  <span class=\"token function\">RenderRepaintBoundary</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> RenderBox child <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  bool <span class=\"token keyword\">get</span> isRepaintBoundary <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h2 id=\"_14-3-3-命中测试\"><a href=\"#_14-3-3-命中测试\" class=\"header-anchor\">#</a> 14.3.3 命中测试</h2> <p>我们在“事件处理与通知”一章中已经讲过Flutter事件机制和命中测试流程，本节我们看一下其内部实现原理。</p> <p>一个对象是否可以响应事件，取决于其对命中测试的返回，当发生用户事件时，会从根节点（<code>RenderView</code>）开始进行命中测试，下面是<code>RenderView</code>的<code>hitTest()</code>源码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>bool <span class=\"token function\">hitTest</span><span class=\"token punctuation\">(</span>HitTestResult result<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> Offset position <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>child <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span>\n    child<span class=\"token punctuation\">.</span><span class=\"token function\">hitTest</span><span class=\"token punctuation\">(</span>result<span class=\"token punctuation\">,</span> position<span class=\"token punctuation\">:</span> position<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//递归子RenderBox进行命中测试</span>\n  result<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token function\">HitTestEntry</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//将测试结果添加到result中</span>\n  <span class=\"token keyword\">return</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们再看看<code>RenderBox</code>默认的<code>hitTest()</code>实现：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>bool <span class=\"token function\">hitTest</span><span class=\"token punctuation\">(</span>HitTestResult result<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> <span class=\"token metadata symbol\">@required</span> Offset position <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>  \n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>_size<span class=\"token punctuation\">.</span><span class=\"token function\">contains</span><span class=\"token punctuation\">(</span>position<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token function\">hitTestChildren</span><span class=\"token punctuation\">(</span>result<span class=\"token punctuation\">,</span> position<span class=\"token punctuation\">:</span> position<span class=\"token punctuation\">)</span> <span class=\"token operator\">||</span> <span class=\"token function\">hitTestSelf</span><span class=\"token punctuation\">(</span>position<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      result<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token function\">BoxHitTestEntry</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">,</span> position<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">return</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token keyword\">return</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们看到默认的实现里调用了<code>hitTestSelf()</code>和<code>hitTestChildren()</code>两个方法，这两个方法默认实现如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code> \n<span class=\"token metadata symbol\">@protected</span>\nbool <span class=\"token function\">hitTestSelf</span><span class=\"token punctuation\">(</span>Offset position<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n \n<span class=\"token metadata symbol\">@protected</span>\nbool <span class=\"token function\">hitTestChildren</span><span class=\"token punctuation\">(</span>HitTestResult result<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> Offset position <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p><code>hitTest</code> 方法用来判断该<code>RenderObject</code> 是否在被点击的范围内，同时负责将被点击的 <code>RenderBox</code> 添加到 <code>HitTestResult</code> 列表中，参数 <code>position</code> 为事件触发的坐标（如果有的话），返回 true 则表示有<code>RenderBox</code> 通过了命中测试，需要响应事件，反之则认为当前<code>RenderBox</code>没有命中。在继承<code>RenderBox</code>时，可以直接重写<code>hitTest()</code>方法，也可以重写 <code>hitTestSelf()</code> 或 <code>hitTestChildren()</code>, 唯一不同的是 <code>hitTest()</code>中需要将通过命中测试的节点信息添加到命中测试结果列表中，而 <code>hitTestSelf()</code> 和 <code>hitTestChildren()</code>则只需要简单的返回<code>true</code>或<code>false</code>。</p> <h2 id=\"_14-3-4-语义化\"><a href=\"#_14-3-4-语义化\" class=\"header-anchor\">#</a> 14.3.4 语义化</h2> <p>语义化即Semantics，主要是提供给读屏软件的接口，也是实现辅助功能的基础，通过语义化接口可以让机器理解页面上的内容，对于有视力障碍用户可以使用读屏软件来理解UI内容。如果一个<code>RenderObject</code>要支持语义化接口，可以实现 <code>describeApproximatePaintClip</code>和 <code>visitChildrenForSemantics</code>方法和<code>semanticsAnnotator</code> getter。更多关于语义化的信息可以查看API文档。</p> <h2 id=\"_14-3-5-总结\"><a href=\"#_14-3-5-总结\" class=\"header-anchor\">#</a> 14.3.5 总结</h2> <p>本节我们介绍了<code>RenderObject</code>主要的功能和方法，理解这些内容可以帮助我们更好的理解Flutter UI底层原理。我们也可以看到，如果要从头到尾实现一个<code>RenderObject</code>是比较麻烦的，我们必须去实现layout、绘制和命中测试逻辑，但是值得庆幸的是，大多数时候我们可以直接在Widget层通过组合或者<code>CustomPaint</code>完成自定义UI。如果遇到只能定义一个新<code>RenderObject</code>的场景时（如要实现一个新的layout算法的布局容器），可以直接继承自<code>RenderBox</code>，这样可以帮我们减少一部分工作。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/116.5453b788.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter15/code_structure.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>15.2 Flutter APP代码结构 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/201.2953b2e7.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter15/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_15-2-flutter-app代码结构\"><a href=\"#_15-2-flutter-app代码结构\" class=\"header-anchor\">#</a> 15.2 Flutter APP代码结构</h1> <p>我们先来创建一个全新的Flutter工程，命名为&quot;github_client_app&quot;；创建新工程的步骤视读者使用的编辑器而定，都比较简单，在此不再赘述。创建完成后，工程结构如下：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>github_client_app\n├── android\n├── ios\n├── lib\n└── <span class=\"token builtin class-name\">test</span>\n</code></pre></div><p>由于我们需要使用外部图片和Icon资源，所以我们在项目根目录下分别创建“imgs”和“fonts”文件夹，前者用于保存图片，后者用于保存Icon文件。关于图片和Icon，读者可以参考第三章中相应的内容。</p> <p>由于在网络数据传输和持久化时，我们需要通过Json来传输、保存数据；但是在应用开发时我们又需要将Json转成Dart Model类，现在我们使用在第十一章中“Json转Model”小节中介绍的方案，所以，我们需要在根目录下再创建一个用于保存Json文件的“jsons”文件夹。</p> <p>多语言支持我们使用第十三章“国际化”中介绍的方案，所以还需要在根目录下创建一个“l10n”文件夹，用于保存各国语言对应的arb文件。</p> <p>现在工程目录变为：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>github_client_app\n├── android\n├── fonts\n├── l10n-arb\n├── imgs\n├── ios\n├── jsons\n├── lib\n└── <span class=\"token builtin class-name\">test</span>\n</code></pre></div><p>由于我们的Dart代码都在“lib”文件夹下，笔者根据技术选型和经验在lib文件下创建了如下目录：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>lib\n├── common\n├── l10n\n├── models\n├── states\n├── routes\n└── widgets \n</code></pre></div><table><thead><tr><th>文件夹</th> <th>作用</th></tr></thead> <tbody><tr><td>common</td> <td>一些工具类，如通用方法类、网络接口类、保存全局变量的静态类等</td></tr> <tr><td>l10n</td> <td>国际化相关的类都在此目录下</td></tr> <tr><td>models</td> <td>Json文件对应的Dart Model类会在此目录下</td></tr> <tr><td>states</td> <td>保存APP中需要跨组件共享的状态类</td></tr> <tr><td>routes</td> <td>存放所有路由页面类</td></tr> <tr><td>widgets</td> <td>APP内封装的一些Widget组件都在该目录下</td></tr></tbody></table> <p>注意，使用不同的框架或技术选型会对代码有不同的组织方式，因此，本节介绍的代码组织结构并不是固定或者“最佳”的，在实战中，读者可以自己根据情况调整源码结构。但是无论采取何种源码组织结构，清晰和解耦都是一个通用原则，我们应该让自己的代码结构清晰，以便交流和维护。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/201.2953b2e7.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter15/entry.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>15.6 APP入口及主页 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/32.c0efa5ac.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter15/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_15-6-app入口及主页\"><a href=\"#_15-6-app入口及主页\" class=\"header-anchor\">#</a> 15.6 APP入口及主页</h1> <p>本节来介绍一下APP入口及首页。</p> <h2 id=\"_15-6-1-app入口\"><a href=\"#_15-6-1-app入口\" class=\"header-anchor\">#</a> 15.6.1 APP入口</h2> <p><code>main</code>函数为APP入口函数，实现如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">main</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Global<span class=\"token punctuation\">.</span><span class=\"token function\">init</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">runApp</span><span class=\"token punctuation\">(</span><span class=\"token function\">MyApp</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>初始化完成后才会加载UI(<code>MyApp</code>)，<code>MyApp</code> 是应用的入口Widget，实现如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">MyApp</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// This widget is the root of your application.</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">MultiProvider</span><span class=\"token punctuation\">(</span>\n      providers<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>SingleChildCloneableWidget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n        ChangeNotifierProvider<span class=\"token punctuation\">.</span><span class=\"token function\">value</span><span class=\"token punctuation\">(</span>value<span class=\"token punctuation\">:</span> <span class=\"token function\">ThemeModel</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        ChangeNotifierProvider<span class=\"token punctuation\">.</span><span class=\"token function\">value</span><span class=\"token punctuation\">(</span>value<span class=\"token punctuation\">:</span> <span class=\"token function\">UserModel</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        ChangeNotifierProvider<span class=\"token punctuation\">.</span><span class=\"token function\">value</span><span class=\"token punctuation\">(</span>value<span class=\"token punctuation\">:</span> <span class=\"token function\">LocaleModel</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> Consumer2<span class=\"token operator\">&lt;</span>ThemeModel<span class=\"token punctuation\">,</span> LocaleModel<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n        builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> themeModel<span class=\"token punctuation\">,</span> localeModel<span class=\"token punctuation\">,</span> Widget child<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">return</span> <span class=\"token function\">MaterialApp</span><span class=\"token punctuation\">(</span>\n            theme<span class=\"token punctuation\">:</span> <span class=\"token function\">ThemeData</span><span class=\"token punctuation\">(</span>\n              primarySwatch<span class=\"token punctuation\">:</span> themeModel<span class=\"token punctuation\">.</span>theme<span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            onGenerateTitle<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n              <span class=\"token keyword\">return</span> GmLocalizations<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>title<span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n            home<span class=\"token punctuation\">:</span> <span class=\"token function\">HomeRoute</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//应用主页</span>\n            locale<span class=\"token punctuation\">:</span> localeModel<span class=\"token punctuation\">.</span><span class=\"token function\">getLocale</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token comment\">//我们只支持美国英语和中文简体</span>\n            supportedLocales<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>\n              <span class=\"token keyword\">const</span> <span class=\"token function\">Locale</span><span class=\"token punctuation\">(</span><span class=\"token string\">'en'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'US'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 美国英语</span>\n              <span class=\"token keyword\">const</span> <span class=\"token function\">Locale</span><span class=\"token punctuation\">(</span><span class=\"token string\">'zh'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'CN'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 中文简体</span>\n              <span class=\"token comment\">//其它Locales</span>\n            <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n            localizationsDelegates<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>\n              <span class=\"token comment\">// 本地化的代理类</span>\n              GlobalMaterialLocalizations<span class=\"token punctuation\">.</span>delegate<span class=\"token punctuation\">,</span>\n              GlobalWidgetsLocalizations<span class=\"token punctuation\">.</span>delegate<span class=\"token punctuation\">,</span>\n              <span class=\"token function\">GmLocalizationsDelegate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n            localeResolutionCallback<span class=\"token punctuation\">:</span>\n                <span class=\"token punctuation\">(</span>Locale _locale<span class=\"token punctuation\">,</span> Iterable<span class=\"token operator\">&lt;</span>Locale<span class=\"token operator\">&gt;</span> supportedLocales<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>localeModel<span class=\"token punctuation\">.</span><span class=\"token function\">getLocale</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token comment\">//如果已经选定语言，则不跟随系统</span>\n                <span class=\"token keyword\">return</span> localeModel<span class=\"token punctuation\">.</span><span class=\"token function\">getLocale</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n         \n                Locale locale<span class=\"token punctuation\">;</span>\n                <span class=\"token comment\">//APP语言跟随系统语言，如果系统语言不是中文简体或美国英语，</span>\n                <span class=\"token comment\">//则默认使用美国英语</span>\n                <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>supportedLocales<span class=\"token punctuation\">.</span><span class=\"token function\">contains</span><span class=\"token punctuation\">(</span>_locale<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                  locale<span class=\"token operator\">=</span> _locale<span class=\"token punctuation\">;</span>\n                <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n                  locale<span class=\"token operator\">=</span> <span class=\"token function\">Locale</span><span class=\"token punctuation\">(</span><span class=\"token string\">'en'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'US'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                <span class=\"token punctuation\">}</span>\n                <span class=\"token keyword\">return</span> locale<span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n            <span class=\"token comment\">// 注册命名路由表</span>\n            routes<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>String<span class=\"token punctuation\">,</span> WidgetBuilder<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">{</span>\n              <span class=\"token string\">&quot;login&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">LoginRoute</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token string\">&quot;themes&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">ThemeChangeRoute</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token string\">&quot;language&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">LanguageRoute</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>在上面的代码中：</p> <ol><li>我们的根widget是<code>MultiProvider</code>，它将主题、用户、语言三种状态绑定到了应用的根上，如此一来，任何路由中都可以通过<code>Provider.of()</code>来获取这些状态，也就是说这三种状态是全局共享的！</li> <li><code>HomeRoute</code>是应用的主页。</li> <li>在构建<code>MaterialApp</code>时，我们配置了APP支持的语言列表，以及监听了系统语言改变事件；另外<code>MaterialApp</code>消费（依赖）了<code>ThemeModel</code>和<code>LocaleModel</code>，所以当APP主题或语言改变时<code>MaterialApp</code>会重新构建</li> <li>我们注册了命名路由表，以便在APP中可以直接通过路由名跳转。</li> <li>为了支持多语言（本APP中我们支持美国英语和中文简体两种语言）我们实现了一个<code>GmLocalizationsDelegate</code>，子Widget中都可以通过<code>GmLocalizations</code>来动态获取APP当前语言对应的文案。关于<code>GmLocalizationsDelegate</code>和<code>GmLocalizations</code>的实现方式读者可以参考“国际化”一章中的介绍，此处不再赘述。</li></ol> <h2 id=\"_15-6-2-主页\"><a href=\"#_15-6-2-主页\" class=\"header-anchor\">#</a> 15.6.2 主页</h2> <p>为了简单起见，当APP启动后，如果之前已登录了APP，则显示该用户项目列表；如果之前未登录，则显示一个登录按钮，点击后跳转到登录页。另外，我们实现一个抽屉菜单，里面包含当前用户头像及APP的菜单。下面我们先看看要实现的效果，如图15-1、15-2所示：</p> <p><img src=\"/assets/img/15-1.c59fd39e.png\" alt=\"15-1\"><img src=\"/assets/img/15-2.cb77ca5a.png\" alt=\"15-2\"></p> <p>我们在“lib/routes”下创建一个“home_page.dart”文件，实现如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">HomeRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _HomeRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">_HomeRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_HomeRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>HomeRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n      appBar<span class=\"token punctuation\">:</span> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>\n        title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>GmLocalizations<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>home<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      body<span class=\"token punctuation\">:</span> <span class=\"token function\">_buildBody</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 构建主页面</span>\n      drawer<span class=\"token punctuation\">:</span> <span class=\"token function\">MyDrawer</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//抽屉菜单</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token comment\">// 省略</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面代码中，主页的标题（title）我们是通过<code>GmLocalizations.of(context).home</code>来获得，<code>GmLocalizations</code>是我们提供的一个<code>Localizations</code>类，用于支持多语言，因此当APP语言改变时，凡是使用<code>GmLocalizations</code>动态获取的文案都会是相应语言的文案，这在前面“国际化”一章中已经介绍过，读者可以前翻查阅。</p> <p>我们通过 <code>_buildBody()</code>方法来构建主页内容，<code>_buildBody()</code>方法实现代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>  Widget <span class=\"token function\">_buildBody</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    UserModel userModel <span class=\"token operator\">=</span> Provider<span class=\"token punctuation\">.</span>of<span class=\"token operator\">&lt;</span>UserModel<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>userModel<span class=\"token punctuation\">.</span>isLogin<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">//用户未登录，显示登录按钮</span>\n      <span class=\"token keyword\">return</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>GmLocalizations<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>login<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pushNamed</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;login&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">//已登录，则展示项目列表</span>\n      <span class=\"token keyword\">return</span> InfiniteListView<span class=\"token operator\">&lt;</span>Repo<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n        onRetrieveData<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>int page<span class=\"token punctuation\">,</span> List<span class=\"token operator\">&lt;</span>Repo<span class=\"token operator\">&gt;</span> items<span class=\"token punctuation\">,</span> bool refresh<span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">var</span> data <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">Git</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">getRepos</span><span class=\"token punctuation\">(</span>\n            refresh<span class=\"token punctuation\">:</span> refresh<span class=\"token punctuation\">,</span>\n            queryParameters<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token string\">'page'</span><span class=\"token punctuation\">:</span> page<span class=\"token punctuation\">,</span>\n              <span class=\"token string\">'page_size'</span><span class=\"token punctuation\">:</span> <span class=\"token number\">20</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token comment\">//把请求到的新数据添加到items中</span>\n          items<span class=\"token punctuation\">.</span><span class=\"token function\">addAll</span><span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> \n          <span class=\"token comment\">// 如果接口返回的数量等于'page_size'，则认为还有数据，反之则认为最后一页</span>\n          <span class=\"token keyword\">return</span> data<span class=\"token punctuation\">.</span>length<span class=\"token operator\">==</span><span class=\"token number\">20</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        itemBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>List list<span class=\"token punctuation\">,</span> int index<span class=\"token punctuation\">,</span> BuildContext ctx<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token comment\">// 项目信息列表项</span>\n          <span class=\"token keyword\">return</span> <span class=\"token function\">RepoItem</span><span class=\"token punctuation\">(</span>list<span class=\"token punctuation\">[</span>index<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面代码注释很清楚：如果用户未登录，显示登录按钮；如果用户已登录，则展示项目列表。这里项目列表使用了<code>InfiniteListView</code> Widget，它是flukit package中提供的。<code>InfiniteListView</code>同时支持了下拉刷新和上拉加载更多两种功能。<code>onRetrieveData</code> 为数据获取回调，该回调函数接收三个参数：</p> <table><thead><tr><th>参数名</th> <th>类型</th> <th>解释</th></tr></thead> <tbody><tr><td>page</td> <td>int</td> <td>当前页号</td></tr> <tr><td>items</td> <td>List<T></T></td> <td>保存当前列表数据的List</td></tr> <tr><td>refresh</td> <td>bool</td> <td>是否是下拉刷新触发</td></tr></tbody></table> <p>返回值类型为<code>bool</code>，为<code>true</code>时表示还有数据，为<code>false</code>时则表示后续没有数据了。<code>onRetrieveData</code> 回调中我们调用<code>Git(context).getRepos(...)</code>来获取用户项目列表，同时指定每次请求获取20条。当获取成功时，首先要将新获取的项目数据添加到<code>items</code>中，然后根据本次请求的项目条数是否等于期望的20条来判断还有没有更多的数据。在此需要注意，<code>Git(context).getRepos(…)</code>方法中需要<code>refresh</code>参数来判断是否使用缓存。</p> <p><code>itemBuilder</code>为列表项的builder，我们需要在该回调中构建每一个列表项Widget。由于列表项构建逻辑较复杂，我们单独封装一个<code>RepoItem</code> Widget 专门用于构建列表项UI。<code>RepoItem</code> 实现如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'../index.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">RepoItem</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// 将`repo.id`作为RepoItem的默认key</span>\n  <span class=\"token function\">RepoItem</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>repo<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> <span class=\"token function\">ValueKey</span><span class=\"token punctuation\">(</span>repo<span class=\"token punctuation\">.</span>id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> Repo repo<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  _RepoItemState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">_RepoItemState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_RepoItemState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>RepoItem<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">var</span> subtitle<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n      padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">only</span><span class=\"token punctuation\">(</span>top<span class=\"token punctuation\">:</span> <span class=\"token number\">8.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Material</span><span class=\"token punctuation\">(</span>\n        color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">,</span>\n        shape<span class=\"token punctuation\">:</span> <span class=\"token function\">BorderDirectional</span><span class=\"token punctuation\">(</span>\n          bottom<span class=\"token punctuation\">:</span> <span class=\"token function\">BorderSide</span><span class=\"token punctuation\">(</span>\n            color<span class=\"token punctuation\">:</span> Theme<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>dividerColor<span class=\"token punctuation\">,</span>\n            width<span class=\"token punctuation\">:</span> <span class=\"token number\">.5</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n          padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">only</span><span class=\"token punctuation\">(</span>top<span class=\"token punctuation\">:</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span> bottom<span class=\"token punctuation\">:</span> <span class=\"token number\">16</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n            crossAxisAlignment<span class=\"token punctuation\">:</span> CrossAxisAlignment<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n            children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n              <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>\n                dense<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n                leading<span class=\"token punctuation\">:</span> <span class=\"token function\">gmAvatar</span><span class=\"token punctuation\">(</span>\n                  <span class=\"token comment\">//项目owner头像</span>\n                  widget<span class=\"token punctuation\">.</span>repo<span class=\"token punctuation\">.</span>owner<span class=\"token punctuation\">.</span>avatar_url<span class=\"token punctuation\">,</span>\n                  width<span class=\"token punctuation\">:</span> <span class=\"token number\">24.0</span><span class=\"token punctuation\">,</span>\n                  borderRadius<span class=\"token punctuation\">:</span> BorderRadius<span class=\"token punctuation\">.</span><span class=\"token function\">circular</span><span class=\"token punctuation\">(</span><span class=\"token number\">12</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>\n                  widget<span class=\"token punctuation\">.</span>repo<span class=\"token punctuation\">.</span>owner<span class=\"token punctuation\">.</span>login<span class=\"token punctuation\">,</span>\n                  textScaleFactor<span class=\"token punctuation\">:</span> <span class=\"token number\">.9</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                subtitle<span class=\"token punctuation\">:</span> subtitle<span class=\"token punctuation\">,</span>\n                trailing<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>widget<span class=\"token punctuation\">.</span>repo<span class=\"token punctuation\">.</span>language <span class=\"token operator\">?</span><span class=\"token operator\">?</span> <span class=\"token string\">&quot;&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token comment\">// 构建项目标题和简介</span>\n              <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n                padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">symmetric</span><span class=\"token punctuation\">(</span>horizontal<span class=\"token punctuation\">:</span> <span class=\"token number\">16.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n                  crossAxisAlignment<span class=\"token punctuation\">:</span> CrossAxisAlignment<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n                  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                    <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>\n                      widget<span class=\"token punctuation\">.</span>repo<span class=\"token punctuation\">.</span>fork\n                          <span class=\"token operator\">?</span> widget<span class=\"token punctuation\">.</span>repo<span class=\"token punctuation\">.</span>full_name\n                          <span class=\"token punctuation\">:</span> widget<span class=\"token punctuation\">.</span>repo<span class=\"token punctuation\">.</span>name<span class=\"token punctuation\">,</span>\n                      style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>\n                        fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">15</span><span class=\"token punctuation\">,</span>\n                        fontWeight<span class=\"token punctuation\">:</span> FontWeight<span class=\"token punctuation\">.</span>bold<span class=\"token punctuation\">,</span>\n                        fontStyle<span class=\"token punctuation\">:</span> widget<span class=\"token punctuation\">.</span>repo<span class=\"token punctuation\">.</span>fork\n                            <span class=\"token operator\">?</span> FontStyle<span class=\"token punctuation\">.</span>italic\n                            <span class=\"token punctuation\">:</span> FontStyle<span class=\"token punctuation\">.</span>normal<span class=\"token punctuation\">,</span>\n                      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                    <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n                      padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">only</span><span class=\"token punctuation\">(</span>top<span class=\"token punctuation\">:</span> <span class=\"token number\">8</span><span class=\"token punctuation\">,</span> bottom<span class=\"token punctuation\">:</span> <span class=\"token number\">12</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                      child<span class=\"token punctuation\">:</span> widget<span class=\"token punctuation\">.</span>repo<span class=\"token punctuation\">.</span>description <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span>\n                          <span class=\"token operator\">?</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>\n                              GmLocalizations<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>noDescription<span class=\"token punctuation\">,</span>\n                              style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>\n                                  fontStyle<span class=\"token punctuation\">:</span> FontStyle<span class=\"token punctuation\">.</span>italic<span class=\"token punctuation\">,</span>\n                                  color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">[</span><span class=\"token number\">700</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                            <span class=\"token punctuation\">)</span>\n                          <span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>\n                              widget<span class=\"token punctuation\">.</span>repo<span class=\"token punctuation\">.</span>description<span class=\"token punctuation\">,</span>\n                              maxLines<span class=\"token punctuation\">:</span> <span class=\"token number\">3</span><span class=\"token punctuation\">,</span>\n                              style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>\n                                height<span class=\"token punctuation\">:</span> <span class=\"token number\">1.15</span><span class=\"token punctuation\">,</span>\n                                color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blueGrey<span class=\"token punctuation\">[</span><span class=\"token number\">700</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                                fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">13</span><span class=\"token punctuation\">,</span>\n                              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token comment\">// 构建卡片底部信息</span>\n              <span class=\"token function\">_buildBottom</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">// 构建卡片底部信息</span>\n  Widget <span class=\"token function\">_buildBottom</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> paddingWidth <span class=\"token operator\">=</span> <span class=\"token number\">10</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">IconTheme</span><span class=\"token punctuation\">(</span>\n      data<span class=\"token punctuation\">:</span> <span class=\"token function\">IconThemeData</span><span class=\"token punctuation\">(</span>\n        color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">,</span>\n        size<span class=\"token punctuation\">:</span> <span class=\"token number\">15</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">DefaultTextStyle</span><span class=\"token punctuation\">(</span>\n        style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">,</span> fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">12</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n          padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">symmetric</span><span class=\"token punctuation\">(</span>horizontal<span class=\"token punctuation\">:</span> <span class=\"token number\">16</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword\">var</span> children <span class=\"token operator\">=</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n              <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>star<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot; &quot;</span> <span class=\"token operator\">+</span>\n                  widget<span class=\"token punctuation\">.</span>repo<span class=\"token punctuation\">.</span>stargazers_count\n                      <span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n                      <span class=\"token punctuation\">.</span><span class=\"token function\">padRight</span><span class=\"token punctuation\">(</span>paddingWidth<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>info_outline<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot; &quot;</span> <span class=\"token operator\">+</span>\n                  widget<span class=\"token punctuation\">.</span>repo<span class=\"token punctuation\">.</span>open_issues_count\n                      <span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n                      <span class=\"token punctuation\">.</span><span class=\"token function\">padRight</span><span class=\"token punctuation\">(</span>paddingWidth<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n\n              <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>MyIcons<span class=\"token punctuation\">.</span>fork<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//我们的自定义图标</span>\n              <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>widget<span class=\"token punctuation\">.</span>repo<span class=\"token punctuation\">.</span>forks_count<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">padRight</span><span class=\"token punctuation\">(</span>paddingWidth<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n            <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>widget<span class=\"token punctuation\">.</span>repo<span class=\"token punctuation\">.</span>fork<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              children<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Forked&quot;</span><span class=\"token punctuation\">.</span><span class=\"token function\">padRight</span><span class=\"token punctuation\">(</span>paddingWidth<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span>\n\n            <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>widget<span class=\"token punctuation\">.</span>repo<span class=\"token punctuation\">.</span>private <span class=\"token operator\">==</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              children<span class=\"token punctuation\">.</span><span class=\"token function\">addAll</span><span class=\"token punctuation\">(</span><span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>lock<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot; private&quot;</span><span class=\"token punctuation\">.</span><span class=\"token function\">padRight</span><span class=\"token punctuation\">(</span>paddingWidth<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n              <span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span>\n            <span class=\"token keyword\">return</span> <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>children<span class=\"token punctuation\">:</span> children<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面代码有两点需要注意：</p> <ol><li><p>在构建项目拥有者头像时调用了<code>gmAvatar(…)</code>方法，该方法是是一个全局工具函数，专门用于获取头像图片，实现如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Widget <span class=\"token function\">gmAvatar</span><span class=\"token punctuation\">(</span>String url<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n  double width <span class=\"token operator\">=</span> <span class=\"token number\">30</span><span class=\"token punctuation\">,</span>\n  double height<span class=\"token punctuation\">,</span>\n  BoxFit fit<span class=\"token punctuation\">,</span>\n  BorderRadius borderRadius<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">var</span> placeholder <span class=\"token operator\">=</span> Image<span class=\"token punctuation\">.</span><span class=\"token function\">asset</span><span class=\"token punctuation\">(</span>\n      <span class=\"token string\">&quot;imgs/avatar-default.png&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//头像占位图，加载过程中显示</span>\n      width<span class=\"token punctuation\">:</span> width<span class=\"token punctuation\">,</span>\n      height<span class=\"token punctuation\">:</span> height\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">ClipRRect</span><span class=\"token punctuation\">(</span>\n    borderRadius<span class=\"token punctuation\">:</span> borderRadius <span class=\"token operator\">?</span><span class=\"token operator\">?</span> BorderRadius<span class=\"token punctuation\">.</span><span class=\"token function\">circular</span><span class=\"token punctuation\">(</span><span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">CachedNetworkImage</span><span class=\"token punctuation\">(</span> \n      imageUrl<span class=\"token punctuation\">:</span> url<span class=\"token punctuation\">,</span>\n      width<span class=\"token punctuation\">:</span> width<span class=\"token punctuation\">,</span>\n      height<span class=\"token punctuation\">:</span> height<span class=\"token punctuation\">,</span>\n      fit<span class=\"token punctuation\">:</span> fit<span class=\"token punctuation\">,</span>\n      placeholder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> url<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span>placeholder<span class=\"token punctuation\">,</span>\n      errorWidget<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> url<span class=\"token punctuation\">,</span> error<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span>placeholder<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>代码中调用了<code>CachedNetworkImage</code> 是cached_network_image包中提供的一个Widget，它不仅可以在图片加载过程中指定一个占位图，而且还可以对网络请求的图片进行缓存，更多详情读者可以自行查阅其文档。</p></li> <li><p>由于Flutter 的Material 图标库中没有fork图标，所以我们在iconfont.cn上找了一个fork图标，然后根据“图片和Icon”一节中介绍的使用自定义字体图标的方法集成到了我们的项目中。</p></li></ol> <h2 id=\"_15-6-3-抽屉菜单\"><a href=\"#_15-6-3-抽屉菜单\" class=\"header-anchor\">#</a> 15.6.3 抽屉菜单</h2> <p>抽屉菜单分为两部分：顶部头像和底部功能菜单项。当用户未登录，则抽屉菜单顶部会显示一个默认的灰色占位图，若用户已登录，则会显示用户的头像。抽屉菜单底部有“换肤”和“语言”两个固定菜单，若用户已登录，则会多一个“注销”菜单。用户点击“换肤”和“语言”两个菜单项，会进入相应的设置页面。我们的抽屉菜单效果如图15-3、15-4所示：</p> <p><img src=\"/assets/img/15-3.28296c90.png\" alt=\"15-3\"><img src=\"/assets/img/15-4.19779c8f.png\" alt=\"15-4\"></p> <p>实现代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">MyDrawer</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function\">MyDrawer</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Drawer</span><span class=\"token punctuation\">(</span>\n      <span class=\"token comment\">//移除顶部padding</span>\n      child<span class=\"token punctuation\">:</span> MediaQuery<span class=\"token punctuation\">.</span><span class=\"token function\">removePadding</span><span class=\"token punctuation\">(</span>\n        context<span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">,</span>\n        removeTop<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n          crossAxisAlignment<span class=\"token punctuation\">:</span> CrossAxisAlignment<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            <span class=\"token function\">_buildHeader</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//构建抽屉菜单头部</span>\n            <span class=\"token function\">Expanded</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">:</span> <span class=\"token function\">_buildMenus</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//构建功能菜单</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  Widget <span class=\"token function\">_buildHeader</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> Consumer<span class=\"token operator\">&lt;</span>UserModel<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n      builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> UserModel value<span class=\"token punctuation\">,</span> Widget child<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">return</span> <span class=\"token function\">GestureDetector</span><span class=\"token punctuation\">(</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n            color<span class=\"token punctuation\">:</span> Theme<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>primaryColor<span class=\"token punctuation\">,</span>\n            padding<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">only</span><span class=\"token punctuation\">(</span>top<span class=\"token punctuation\">:</span> <span class=\"token number\">40</span><span class=\"token punctuation\">,</span> bottom<span class=\"token punctuation\">:</span> <span class=\"token number\">20</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n              children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n                  padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">symmetric</span><span class=\"token punctuation\">(</span>horizontal<span class=\"token punctuation\">:</span> <span class=\"token number\">16.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  child<span class=\"token punctuation\">:</span> <span class=\"token function\">ClipOval</span><span class=\"token punctuation\">(</span>\n                    <span class=\"token comment\">// 如果已登录，则显示用户头像；若未登录，则显示默认头像</span>\n                    child<span class=\"token punctuation\">:</span> value<span class=\"token punctuation\">.</span>isLogin\n                        <span class=\"token operator\">?</span> <span class=\"token function\">gmAvatar</span><span class=\"token punctuation\">(</span>value<span class=\"token punctuation\">.</span>user<span class=\"token punctuation\">.</span>avatar_url<span class=\"token punctuation\">,</span> width<span class=\"token punctuation\">:</span> <span class=\"token number\">80</span><span class=\"token punctuation\">)</span>\n                        <span class=\"token punctuation\">:</span> Image<span class=\"token punctuation\">.</span><span class=\"token function\">asset</span><span class=\"token punctuation\">(</span>\n                            <span class=\"token string\">&quot;imgs/avatar-default.png&quot;</span><span class=\"token punctuation\">,</span>\n                            width<span class=\"token punctuation\">:</span> <span class=\"token number\">80</span><span class=\"token punctuation\">,</span>\n                          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>\n                  value<span class=\"token punctuation\">.</span>isLogin\n                      <span class=\"token operator\">?</span> value<span class=\"token punctuation\">.</span>user<span class=\"token punctuation\">.</span>login\n                      <span class=\"token punctuation\">:</span> GmLocalizations<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>login<span class=\"token punctuation\">,</span>\n                  style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>\n                    fontWeight<span class=\"token punctuation\">:</span> FontWeight<span class=\"token punctuation\">.</span>bold<span class=\"token punctuation\">,</span>\n                    color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span>\n              <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          onTap<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>value<span class=\"token punctuation\">.</span>isLogin<span class=\"token punctuation\">)</span> Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pushNamed</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;login&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">// 构建菜单项</span>\n  Widget <span class=\"token function\">_buildMenus</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> Consumer<span class=\"token operator\">&lt;</span>UserModel<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n      builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> UserModel userModel<span class=\"token punctuation\">,</span> Widget child<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">var</span> gm <span class=\"token operator\">=</span> GmLocalizations<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">return</span> <span class=\"token function\">ListView</span><span class=\"token punctuation\">(</span>\n          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>\n              leading<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>color_lens<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>gm<span class=\"token punctuation\">.</span>theme<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              onTap<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">pushNamed</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;themes&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>\n              leading<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>language<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>gm<span class=\"token punctuation\">.</span>language<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              onTap<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">pushNamed</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;language&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>userModel<span class=\"token punctuation\">.</span>isLogin<span class=\"token punctuation\">)</span> <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>\n              leading<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>power_settings_new<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>gm<span class=\"token punctuation\">.</span>logout<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              onTap<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token function\">showDialog</span><span class=\"token punctuation\">(</span>\n                  context<span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">,</span>\n                  builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>ctx<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                    <span class=\"token comment\">//退出账号前先弹二次确认窗</span>\n                    <span class=\"token keyword\">return</span> <span class=\"token function\">AlertDialog</span><span class=\"token punctuation\">(</span>\n                      content<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>gm<span class=\"token punctuation\">.</span>logoutTip<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                      actions<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                        <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n                          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>gm<span class=\"token punctuation\">.</span>cancel<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                          onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                        <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n                          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>gm<span class=\"token punctuation\">.</span>yes<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                          onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                            <span class=\"token comment\">//该赋值语句会触发MaterialApp rebuild</span>\n                            userModel<span class=\"token punctuation\">.</span>user <span class=\"token operator\">=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n                            Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n                        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>用户点击“注销”，<code>userModel.user</code> 会被置空，此时所有依赖<code>userModel</code>的组件都会被<code>rebuild</code>，如主页会恢复成未登录的状态。</p> <p>本小节我们介绍了APP入口<code>MaterialApp</code>的一些配置，然后实现了APP的首页。后面我们将展示登录页、换肤页、语言切换页。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/32.c0efa5ac.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter15/globals.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>15.4 全局变量及共享状态 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/202.de6ee6b8.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter15/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_15-4-全局变量及共享状态\"><a href=\"#_15-4-全局变量及共享状态\" class=\"header-anchor\">#</a> 15.4 全局变量及共享状态</h1> <p>应用程序中通常会包含一些贯穿APP生命周期的变量信息，这些信息在APP大多数地方可能都会被用到，比如当前用户信息、Local信息等。在Flutter中我们把需要全局共享的信息分为两类：全局变量和共享状态。全局变量就是单纯指会贯穿整个APP生命周期的变量，用于单纯的保存一些信息，或者封装一些全局工具和方法的对象。而共享状态则是指哪些需要跨组件或跨路由共享的信息，这些信息通常也是全局变量，而共享状态和全局变量的不同在于前者发生改变时需要通知所有使用该状态的组件，而后者不需要。为此，我们将全局变量和共享状态分开单独管理。</p> <h2 id=\"_15-4-1-全局变量-global类\"><a href=\"#_15-4-1-全局变量-global类\" class=\"header-anchor\">#</a> 15.4.1 全局变量-Global类</h2> <p>我们在“lib/common”目录下创建一个<code>Global</code>类，它主要管理APP的全局变量，定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">// 提供五套可选主题色</span>\n<span class=\"token keyword\">const</span> _themes <span class=\"token operator\">=</span> <span class=\"token operator\">&lt;</span>MaterialColor<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n  Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span>\n  Colors<span class=\"token punctuation\">.</span>cyan<span class=\"token punctuation\">,</span>\n  Colors<span class=\"token punctuation\">.</span>teal<span class=\"token punctuation\">,</span>\n  Colors<span class=\"token punctuation\">.</span>green<span class=\"token punctuation\">,</span>\n  Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">Global</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">static</span> SharedPreferences _prefs<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">static</span> Profile profile <span class=\"token operator\">=</span> <span class=\"token function\">Profile</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">// 网络缓存对象</span>\n  <span class=\"token keyword\">static</span> NetCache netCache <span class=\"token operator\">=</span> <span class=\"token function\">NetCache</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// 可选的主题列表</span>\n  <span class=\"token keyword\">static</span> List<span class=\"token operator\">&lt;</span>MaterialColor<span class=\"token operator\">&gt;</span> <span class=\"token keyword\">get</span> themes <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _themes<span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// 是否为release版</span>\n  <span class=\"token keyword\">static</span> bool <span class=\"token keyword\">get</span> isRelease <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> bool<span class=\"token punctuation\">.</span><span class=\"token function\">fromEnvironment</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;dart.vm.product&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">//初始化全局信息，会在APP启动时执行</span>\n  <span class=\"token keyword\">static</span> Future <span class=\"token function\">init</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    _prefs <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> SharedPreferences<span class=\"token punctuation\">.</span><span class=\"token function\">getInstance</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">var</span> _profile <span class=\"token operator\">=</span> _prefs<span class=\"token punctuation\">.</span><span class=\"token function\">getString</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;profile&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>_profile <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n        profile <span class=\"token operator\">=</span> Profile<span class=\"token punctuation\">.</span><span class=\"token function\">fromJson</span><span class=\"token punctuation\">(</span><span class=\"token function\">jsonDecode</span><span class=\"token punctuation\">(</span>_profile<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span> <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token comment\">// 如果没有缓存策略，设置默认缓存策略</span>\n    profile<span class=\"token punctuation\">.</span>cache <span class=\"token operator\">=</span> profile<span class=\"token punctuation\">.</span>cache <span class=\"token operator\">?</span><span class=\"token operator\">?</span> <span class=\"token function\">CacheConfig</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>enable <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span>\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>maxAge <span class=\"token operator\">=</span> <span class=\"token number\">3600</span>\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>maxCount <span class=\"token operator\">=</span> <span class=\"token number\">100</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token comment\">//初始化网络请求相关配置</span>\n    Git<span class=\"token punctuation\">.</span><span class=\"token function\">init</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">// 持久化Profile信息</span>\n  <span class=\"token keyword\">static</span> <span class=\"token function\">saveProfile</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span>\n      _prefs<span class=\"token punctuation\">.</span><span class=\"token function\">setString</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;profile&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token function\">jsonEncode</span><span class=\"token punctuation\">(</span>profile<span class=\"token punctuation\">.</span><span class=\"token function\">toJson</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>Global类的各个字段的意义都有注释，在此不再赘述，需要注意的是<code>init()</code>需要在App启动时就要执行，所以应用的<code>main</code>方法如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">main</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Global<span class=\"token punctuation\">.</span><span class=\"token function\">init</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">runApp</span><span class=\"token punctuation\">(</span><span class=\"token function\">MyApp</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>在此，一定要确保<code>Global.init()</code>方法不能抛出异常，否则 <code>runApp(MyApp())</code>根本执行不到。</p> <h2 id=\"_15-4-2-共享状态\"><a href=\"#_15-4-2-共享状态\" class=\"header-anchor\">#</a> 15.4.2 共享状态</h2> <p>有了全局变量，我们还需要考虑如何跨组件共享状态。当然，如果我们将要共享的状态全部用全局变量替代也是可以的，但是这在Flutter开发中并不是一个好主意，因为组件的状态是和UI相关，而在状态改变时我们会期望依赖该状态的UI组件会自动更新，如果使用全局变量，那么我们必须得去手动处理状态变动通知、接收机制以及变量和组件依赖关系。因此，本实例中，我们使用前面介绍过的Provider包来实现跨组件状态共享，因此我们需要定义相关的Provider。在本实例中，需要共享的状态有登录用户信息、APP主题信息、APP语言信息。由于这些信息改变后都要立即通知其它依赖的该信息的Widget更新，所以我们应该使用<code>ChangeNotifierProvider</code>，另外，这些信息改变后都是需要更新Profile信息并进行持久化的。综上所述，我们可以定义一个<code>ProfileChangeNotifier</code>基类，然后让需要共享的Model继承自该类即可，<code>ProfileChangeNotifier</code>定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">ProfileChangeNotifier</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">ChangeNotifier</span> <span class=\"token punctuation\">{</span>\n  Profile <span class=\"token keyword\">get</span> _profile <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Global<span class=\"token punctuation\">.</span>profile<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">notifyListeners</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    Global<span class=\"token punctuation\">.</span><span class=\"token function\">saveProfile</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//保存Profile变更</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">notifyListeners</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//通知依赖的Widget更新</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h3 id=\"用户状态\"><a href=\"#用户状态\" class=\"header-anchor\">#</a> 用户状态</h3> <p>用户状态在登录状态发生变化时更新、通知其依赖项，我们定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">UserModel</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">ProfileChangeNotifier</span> <span class=\"token punctuation\">{</span>\n  User <span class=\"token keyword\">get</span> user <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _profile<span class=\"token punctuation\">.</span>user<span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// APP是否登录(如果有用户信息，则证明登录过)</span>\n  bool <span class=\"token keyword\">get</span> isLogin <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> user <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">//用户信息发生变化，更新用户信息并通知依赖它的子孙Widgets更新</span>\n  <span class=\"token keyword\">set</span> <span class=\"token function\">user</span><span class=\"token punctuation\">(</span>User user<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>user<span class=\"token operator\">?</span><span class=\"token punctuation\">.</span>login <span class=\"token operator\">!=</span> _profile<span class=\"token punctuation\">.</span>user<span class=\"token operator\">?</span><span class=\"token punctuation\">.</span>login<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _profile<span class=\"token punctuation\">.</span>lastLogin <span class=\"token operator\">=</span> _profile<span class=\"token punctuation\">.</span>user<span class=\"token operator\">?</span><span class=\"token punctuation\">.</span>login<span class=\"token punctuation\">;</span>\n      _profile<span class=\"token punctuation\">.</span>user <span class=\"token operator\">=</span> user<span class=\"token punctuation\">;</span>\n      <span class=\"token function\">notifyListeners</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h3 id=\"app主题状态\"><a href=\"#app主题状态\" class=\"header-anchor\">#</a> APP主题状态</h3> <p>主题状态在用户更换APP主题时更新、通知其依赖项，定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">ThemeModel</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">ProfileChangeNotifier</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// 获取当前主题，如果为设置主题，则默认使用蓝色主题</span>\n  ColorSwatch <span class=\"token keyword\">get</span> theme <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Global<span class=\"token punctuation\">.</span>themes\n      <span class=\"token punctuation\">.</span><span class=\"token function\">firstWhere</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> e<span class=\"token punctuation\">.</span>value <span class=\"token operator\">==</span> _profile<span class=\"token punctuation\">.</span>theme<span class=\"token punctuation\">,</span> orElse<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// 主题改变后，通知其依赖项，新主题会立即生效</span>\n  <span class=\"token keyword\">set</span> <span class=\"token function\">theme</span><span class=\"token punctuation\">(</span>ColorSwatch color<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>color <span class=\"token operator\">!=</span> theme<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _profile<span class=\"token punctuation\">.</span>theme <span class=\"token operator\">=</span> color<span class=\"token punctuation\">[</span><span class=\"token number\">500</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">;</span>\n      <span class=\"token function\">notifyListeners</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h3 id=\"app语言状态\"><a href=\"#app语言状态\" class=\"header-anchor\">#</a> APP语言状态</h3> <p>当APP语言选为跟随系统（Auto）时，在系通语言改变时，APP语言会更新；当用户在APP中选定了具体语言时（美国英语或中文简体），则APP便会一直使用用户选定的语言，不会再随系统语言而变。语言状态类定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">LocaleModel</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">ProfileChangeNotifier</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// 获取当前用户的APP语言配置Locale类，如果为null，则语言跟随系统语言</span>\n  Locale <span class=\"token function\">getLocale</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>_profile<span class=\"token punctuation\">.</span>locale <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">var</span> t <span class=\"token operator\">=</span> _profile<span class=\"token punctuation\">.</span>locale<span class=\"token punctuation\">.</span><span class=\"token function\">split</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;_&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Locale</span><span class=\"token punctuation\">(</span>t<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> t<span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">// 获取当前Locale的字符串表示</span>\n  String <span class=\"token keyword\">get</span> locale <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _profile<span class=\"token punctuation\">.</span>locale<span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// 用户改变APP语言后，通知依赖项更新，新语言会立即生效</span>\n  <span class=\"token keyword\">set</span> <span class=\"token function\">locale</span><span class=\"token punctuation\">(</span>String locale<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>locale <span class=\"token operator\">!=</span> _profile<span class=\"token punctuation\">.</span>locale<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _profile<span class=\"token punctuation\">.</span>locale <span class=\"token operator\">=</span> locale<span class=\"token punctuation\">;</span>\n      <span class=\"token function\">notifyListeners</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/202.de6ee6b8.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter15/intro.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>15.1 Github客户端示例 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/203.ac627436.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter15/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_15-1-github客户端示例\"><a href=\"#_15-1-github客户端示例\" class=\"header-anchor\">#</a> 15.1 Github客户端示例</h1> <p>本章新建一个Flutter工程，实现一个简单的Github客户端。这个实例的主要目标有两个：</p> <ol><li>带领读者了解如何使用Flutter来开发一个完整APP，了解Flutter应用开发流程及工程结构等。</li> <li>对前面章节所学内容的一个应用及总结。</li></ol> <p>需要注意的是，由于Github本身功能非常多，我们的焦点并不是去实现Github的所有业务功能。因此，我们只需要实现一个APP的骨架，能达到上面这两点即可。下面对我们要实现的功能如下：</p> <ol><li>实现Github账号登录、退出登录功能</li> <li>登录后可以查看自己的项目主页</li> <li>支持换肤</li> <li>支持多语言</li> <li>登录状态可以持久化；</li></ol> <p>要实现上面这些功能会涉及到如下技术点：</p> <ol><li>网络请求；需要请求Github API。</li> <li>Json转Dart Model类；</li> <li>全局状态管理；语言、主题、登录态等都需要全局共享。</li> <li>持久化存储；保存登录信息，用户信息等。</li> <li>支持国际化、Intl包的使用</li></ol> <p>现在，目标已经确定，在接下来章节中，我们将分模块一步一步实现上述功能。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/203.ac627436.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter15/language_and_theme_setting.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>15.8 多语言和多主题 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/50.57b758f4.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter15/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_15-8-多语言和多主题\"><a href=\"#_15-8-多语言和多主题\" class=\"header-anchor\">#</a> 15.8 多语言和多主题</h1> <p>本实例APP中语言和主题都是可以设置的，而两者都是通过<code>ChangeNotifierProvider</code>来实现的：我们在<code>main</code>函数中使用了<code>Consumer2</code>，依赖了<code>ThemeModel</code>和<code>LocaleModel</code>，因此，当我们在语言和主题设置页更该当前的配置后，<code>Consumer2</code>的<code>builder</code>都会重新执行，构建一个新的<code>MaterialApp</code>，所以修改会立即生效。下面看一下语言和主题设置页的实现。</p> <h2 id=\"_15-8-1-语言选择页\"><a href=\"#_15-8-1-语言选择页\" class=\"header-anchor\">#</a> 15.8.1 语言选择页</h2> <p>APP语言选择页提供三个选项：中文简体、美国英语、跟随系统。我们将当前APP使用的语言高亮显示，并且在后面添加一个“对号”图标，实现如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">LanguageRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">var</span> color <span class=\"token operator\">=</span> Theme<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>primaryColor<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">var</span> localeModel <span class=\"token operator\">=</span> Provider<span class=\"token punctuation\">.</span>of<span class=\"token operator\">&lt;</span>LocaleModel<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">var</span> gm <span class=\"token operator\">=</span> GmLocalizations<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//构建语言选择项</span>\n    Widget <span class=\"token function\">_buildLanguageItem</span><span class=\"token punctuation\">(</span>String lan<span class=\"token punctuation\">,</span> value<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>\n        title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>\n          lan<span class=\"token punctuation\">,</span>\n          <span class=\"token comment\">// 对APP当前语言进行高亮显示</span>\n          style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> localeModel<span class=\"token punctuation\">.</span>locale <span class=\"token operator\">==</span> value <span class=\"token operator\">?</span> color <span class=\"token punctuation\">:</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        trailing<span class=\"token punctuation\">:</span>\n            localeModel<span class=\"token punctuation\">.</span>locale <span class=\"token operator\">==</span> value <span class=\"token operator\">?</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>done<span class=\"token punctuation\">,</span> color<span class=\"token punctuation\">:</span> color<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span>\n        onTap<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token comment\">// 更新locale后MaterialApp会重新build</span>\n          localeModel<span class=\"token punctuation\">.</span>locale <span class=\"token operator\">=</span> value<span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n      appBar<span class=\"token punctuation\">:</span> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>\n        title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>gm<span class=\"token punctuation\">.</span>language<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      body<span class=\"token punctuation\">:</span> <span class=\"token function\">ListView</span><span class=\"token punctuation\">(</span>\n        children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n          <span class=\"token function\">_buildLanguageItem</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;中文简体&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;zh_CN&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">_buildLanguageItem</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;English&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;en_US&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">_buildLanguageItem</span><span class=\"token punctuation\">(</span>gm<span class=\"token punctuation\">.</span>auto<span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面代码逻辑很简单，唯一需要注意的是我们在<code>build(…)</code>方法里面定义了<code>_buildLanguageItem(…)</code>方法，它和在<code>LanguageRoute</code>类中定义该方法的区别就在于：在<code>build(…)</code>内定义的方法可以共享<code>build(...)</code>方法上下文中的变量，本例中是共享了<code>localeModel</code>。当然，如果<code>_buildLanguageItem(…)</code>的实现复杂一些的话不建议这样做，此时最好是将其作为<code>LanguageRoute</code>类的方法。该页面运行效果如图15-6、15-7所示：</p> <p><img src=\"/assets/img/15-6.92dd6b15.png\" alt=\"15-6\"><img src=\"/assets/img/15-7.aa6d4238.png\" alt=\"15-7\"></p> <p>切换语言后立即生效。</p> <h2 id=\"_15-8-2-主题选择页\"><a href=\"#_15-8-2-主题选择页\" class=\"header-anchor\">#</a> 15.8.2 主题选择页</h2> <p>一个完整的主题<code>Theme</code>包括很多选项，这些选项在<code>ThemeData</code>中定义。本实例为了简单起见，我们只配置主题颜色。我们提供几种默认预定义的主题色供用户选择，用户点击一种色块后则更新主题。主题选择页的实现代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">ThemeChangeRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span><span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n      appBar<span class=\"token punctuation\">:</span> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>\n        title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>GmLocalizations<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>theme<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      body<span class=\"token punctuation\">:</span> <span class=\"token function\">ListView</span><span class=\"token punctuation\">(</span> <span class=\"token comment\">//显示主题色块</span>\n        children<span class=\"token punctuation\">:</span> Global<span class=\"token punctuation\">.</span>themes<span class=\"token punctuation\">.</span>map<span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">return</span> <span class=\"token function\">GestureDetector</span><span class=\"token punctuation\">(</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n              padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">symmetric</span><span class=\"token punctuation\">(</span>vertical<span class=\"token punctuation\">:</span> <span class=\"token number\">5</span><span class=\"token punctuation\">,</span> horizontal<span class=\"token punctuation\">:</span> <span class=\"token number\">16</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n                color<span class=\"token punctuation\">:</span> e<span class=\"token punctuation\">,</span>\n                height<span class=\"token punctuation\">:</span> <span class=\"token number\">40</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            onTap<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token comment\">//主题更新后，MaterialApp会重新build</span>\n              Provider<span class=\"token punctuation\">.</span>of<span class=\"token operator\">&lt;</span>ThemeModel<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>theme <span class=\"token operator\">=</span> e<span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toList</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行效果如图15-8所示：</p> <p><img src=\"/assets/img/15-8.1362ff85.png\" alt=\"15-8\"></p> <p>点击其它主题色块后，APP主题色立马切换生效。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/50.57b758f4.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter15/login_page.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>15.7 登录页 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/117.f7002db2.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter15/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_15-7-登录页\"><a href=\"#_15-7-登录页\" class=\"header-anchor\">#</a> 15.7 登录页</h1> <p>我们说过Github有多种登录方式，为了简单起见，我们只实现通过用户名和密码登录。在实现登录页时有四点需要注意：</p> <ol><li>可以自动填充上次登录的用户名（如果有）。</li> <li>为了防止密码输入错误，密码框应该有开关可以看明文。</li> <li>用户名或密码字段在调用登录接口前有本地合法性校验（比如不能为空）。</li> <li>登录成功后需更新用户信息。</li></ol> <p>实现代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'../index.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">LoginRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _LoginRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">_LoginRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_LoginRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>LoginRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  TextEditingController _unameController <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">TextEditingController</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  TextEditingController _pwdController <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">TextEditingController</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  bool pwdShow <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//密码是否显示明文</span>\n  GlobalKey _formKey <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">GlobalKey</span><span class=\"token operator\">&lt;</span>FormState<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  bool _nameAutoFocus <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 自动填充上次登录的用户名，填充后将焦点定位到密码输入框</span>\n    _unameController<span class=\"token punctuation\">.</span>text <span class=\"token operator\">=</span> Global<span class=\"token punctuation\">.</span>profile<span class=\"token punctuation\">.</span>lastLogin<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>_unameController<span class=\"token punctuation\">.</span>text <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _nameAutoFocus <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">var</span> gm <span class=\"token operator\">=</span> GmLocalizations<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n      appBar<span class=\"token punctuation\">:</span> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>gm<span class=\"token punctuation\">.</span>login<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      body<span class=\"token punctuation\">:</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n        padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">16.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Form</span><span class=\"token punctuation\">(</span>\n          key<span class=\"token punctuation\">:</span> _formKey<span class=\"token punctuation\">,</span>\n          autovalidate<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n            children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n              <span class=\"token function\">TextFormField</span><span class=\"token punctuation\">(</span>\n                  autofocus<span class=\"token punctuation\">:</span> _nameAutoFocus<span class=\"token punctuation\">,</span>\n                  controller<span class=\"token punctuation\">:</span> _unameController<span class=\"token punctuation\">,</span>\n                  decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">InputDecoration</span><span class=\"token punctuation\">(</span>\n                    labelText<span class=\"token punctuation\">:</span> gm<span class=\"token punctuation\">.</span>userName<span class=\"token punctuation\">,</span>\n                    hintText<span class=\"token punctuation\">:</span> gm<span class=\"token punctuation\">.</span>userNameOrEmail<span class=\"token punctuation\">,</span>\n                    prefixIcon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>person<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token comment\">// 校验用户名（不能为空）</span>\n                  validator<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>v<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                    <span class=\"token keyword\">return</span> v<span class=\"token punctuation\">.</span><span class=\"token function\">trim</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>isNotEmpty <span class=\"token operator\">?</span> <span class=\"token keyword\">null</span> <span class=\"token punctuation\">:</span> gm<span class=\"token punctuation\">.</span>userNameRequired<span class=\"token punctuation\">;</span>\n                  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token function\">TextFormField</span><span class=\"token punctuation\">(</span>\n                controller<span class=\"token punctuation\">:</span> _pwdController<span class=\"token punctuation\">,</span>\n                autofocus<span class=\"token punctuation\">:</span> <span class=\"token operator\">!</span>_nameAutoFocus<span class=\"token punctuation\">,</span>\n                decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">InputDecoration</span><span class=\"token punctuation\">(</span>\n                    labelText<span class=\"token punctuation\">:</span> gm<span class=\"token punctuation\">.</span>password<span class=\"token punctuation\">,</span>\n                    hintText<span class=\"token punctuation\">:</span> gm<span class=\"token punctuation\">.</span>password<span class=\"token punctuation\">,</span>\n                    prefixIcon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>lock<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                    suffixIcon<span class=\"token punctuation\">:</span> <span class=\"token function\">IconButton</span><span class=\"token punctuation\">(</span>\n                      icon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>\n                          pwdShow <span class=\"token operator\">?</span> Icons<span class=\"token punctuation\">.</span>visibility_off <span class=\"token punctuation\">:</span> Icons<span class=\"token punctuation\">.</span>visibility<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                      onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                        <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                          pwdShow <span class=\"token operator\">=</span> <span class=\"token operator\">!</span>pwdShow<span class=\"token punctuation\">;</span>\n                        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n                    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                obscureText<span class=\"token punctuation\">:</span> <span class=\"token operator\">!</span>pwdShow<span class=\"token punctuation\">,</span>\n                <span class=\"token comment\">//校验密码（不能为空）</span>\n                validator<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>v<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                  <span class=\"token keyword\">return</span> v<span class=\"token punctuation\">.</span><span class=\"token function\">trim</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>isNotEmpty <span class=\"token operator\">?</span> <span class=\"token keyword\">null</span> <span class=\"token punctuation\">:</span> gm<span class=\"token punctuation\">.</span>passwordRequired<span class=\"token punctuation\">;</span>\n                <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n                padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">only</span><span class=\"token punctuation\">(</span>top<span class=\"token punctuation\">:</span> <span class=\"token number\">25</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                child<span class=\"token punctuation\">:</span> <span class=\"token function\">ConstrainedBox</span><span class=\"token punctuation\">(</span>\n                  constraints<span class=\"token punctuation\">:</span> BoxConstraints<span class=\"token punctuation\">.</span><span class=\"token function\">expand</span><span class=\"token punctuation\">(</span>height<span class=\"token punctuation\">:</span> <span class=\"token number\">55.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  child<span class=\"token punctuation\">:</span> <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n                    color<span class=\"token punctuation\">:</span> Theme<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>primaryColor<span class=\"token punctuation\">,</span>\n                    onPressed<span class=\"token punctuation\">:</span> _onLogin<span class=\"token punctuation\">,</span>\n                    textColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">,</span>\n                    child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>gm<span class=\"token punctuation\">.</span>login<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_onLogin</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 提交前，先验证各个表单字段是否合法</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>_formKey<span class=\"token punctuation\">.</span>currentState <span class=\"token operator\">as</span> FormState<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">validate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">showLoading</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      User user<span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n        user <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">Git</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">login</span><span class=\"token punctuation\">(</span>_unameController<span class=\"token punctuation\">.</span>text<span class=\"token punctuation\">,</span> _pwdController<span class=\"token punctuation\">.</span>text<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token comment\">// 因为登录页返回后，首页会build，所以我们传false，更新user后不触发更新</span>\n        Provider<span class=\"token punctuation\">.</span>of<span class=\"token operator\">&lt;</span>UserModel<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> listen<span class=\"token punctuation\">:</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>user <span class=\"token operator\">=</span> user<span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span> <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">//登录失败则提示</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">.</span>response<span class=\"token operator\">?</span><span class=\"token punctuation\">.</span>statusCode <span class=\"token operator\">==</span> <span class=\"token number\">401</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token function\">showToast</span><span class=\"token punctuation\">(</span>GmLocalizations<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>userNameOrPasswordWrong<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token function\">showToast</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n      <span class=\"token punctuation\">}</span> <span class=\"token keyword\">finally</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">// 隐藏loading框</span>\n        Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>user <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">// 返回</span>\n        Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>代码很简单，关键地方都有注释，不再赘述，下面我们看一下运行效果，如图15-5所示。</p> <p><img src=\"/assets/img/15-5.bcaa565a.png\" alt=\"图15-5\"></p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/117.f7002db2.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter15/models.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>15.3 Model类定义 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/204.fb9289cb.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter15/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_15-3-model类定义\"><a href=\"#_15-3-model类定义\" class=\"header-anchor\">#</a> 15.3 Model类定义</h1> <p>本节我们先梳理一下APP中将用到的数据，然后生成相应的Dart Model类。Json文件转Dart Model的方案采用前面介绍过的 json_model 包方案</p> <h3 id=\"github账号信息\"><a href=\"#github账号信息\" class=\"header-anchor\">#</a> Github账号信息</h3> <p>登录Github后，我们需要获取当前登录者的Github账号信息，Github API接口返回Json结构如下：</p> <div class=\"language-json extra-class\"><pre class=\"language-json\"><code><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;login&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;octocat&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//用户登录名</span>\n  <span class=\"token property\">&quot;avatar_url&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;https://github.com/images/error/octocat_happy.gif&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//用户头像地址</span>\n  <span class=\"token property\">&quot;type&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;User&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//用户类型，可能是组织</span>\n  <span class=\"token property\">&quot;name&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;monalisa octocat&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//用户名字</span>\n  <span class=\"token property\">&quot;company&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;GitHub&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//公司</span>\n  <span class=\"token property\">&quot;blog&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;https://github.com/blog&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//博客地址</span>\n  <span class=\"token property\">&quot;location&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;San Francisco&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 用户所处地理位置</span>\n  <span class=\"token property\">&quot;email&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;octocat@github.com&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 邮箱</span>\n  <span class=\"token property\">&quot;hireable&quot;</span><span class=\"token operator\">:</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;bio&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;There once was...&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 用户简介</span>\n  <span class=\"token property\">&quot;public_repos&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 公开项目数</span>\n  <span class=\"token property\">&quot;followers&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">20</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//关注该用户的人数</span>\n  <span class=\"token property\">&quot;following&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 该用户关注的人数</span>\n  <span class=\"token property\">&quot;created_at&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;2008-01-14T04:33:35Z&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 账号创建时间</span>\n  <span class=\"token property\">&quot;updated_at&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;2008-01-14T04:33:35Z&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 账号信息更新时间</span>\n  <span class=\"token property\">&quot;total_private_repos&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">100</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//该用户总的私有项目数(包括参与的其它组织的私有项目)</span>\n  <span class=\"token property\">&quot;owned_private_repos&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">100</span> <span class=\"token comment\">//该用户自己的私有项目数</span>\n  ... <span class=\"token comment\">//省略其它字段</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们在“jsons”目录下创建一个“user.json”文件保存上述信息。</p> <h3 id=\"api缓存策略信息\"><a href=\"#api缓存策略信息\" class=\"header-anchor\">#</a> API缓存策略信息</h3> <p>由于Github服务器在国内访问速度较慢，我们对Github API应用一些简单的缓存策略。我们在“jsons”目录下创建一个“cacheConfig.json”文件缓存策略信息，定义如下：</p> <div class=\"language-json extra-class\"><pre class=\"language-json\"><code><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;enable&quot;</span><span class=\"token operator\">:</span><span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 是否启用缓存</span>\n  <span class=\"token property\">&quot;maxAge&quot;</span><span class=\"token operator\">:</span><span class=\"token number\">1000</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 缓存的最长时间，单位（秒）</span>\n  <span class=\"token property\">&quot;maxCount&quot;</span><span class=\"token operator\">:</span><span class=\"token number\">100</span> <span class=\"token comment\">// 最大缓存数</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h3 id=\"用户信息\"><a href=\"#用户信息\" class=\"header-anchor\">#</a> 用户信息</h3> <p>用户信息(Profile)应包括如下信息：</p> <ol><li>Github账号信息；由于我们的APP可以切换账号登录，且登录后再次打开则不需要登录，所以我们需要对用户账号信息和登录状态进行持久化。</li> <li>应用使用配置信息；每一个用户都应有自己的APP配置信息，如主题、语言、以及数据缓存策略等。</li> <li>用户注销登录后，为了便于用户在退出APP前再次登录，我们需要记住上次登录的用户名。</li></ol> <p>需要注意的是，目前Github有三种登录方式，分别是账号密码登录、oauth授权登录、二次认证登录；这三种登录方式的安全性依次加强，但是在本示例中，为了简单起见，我们使用账号密码登录，因此我们需要保存用户的密码。</p> <blockquote><p>注意：在这里需要提醒读者，在登录场景中，保护用户账号安全是一个非常重要且永恒的话题，在实际开发中应严格杜绝直接明文存储用户账密的行为。</p></blockquote> <p>我们在“jsons”目录下创建一个“profile.json”文件，结构如下：</p> <div class=\"language-json extra-class\"><pre class=\"language-json\"><code><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;user&quot;</span><span class=\"token operator\">:</span><span class=\"token string\">&quot;$user&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//Github账号信息，结构见&quot;user.json&quot;</span>\n  <span class=\"token property\">&quot;token&quot;</span><span class=\"token operator\">:</span><span class=\"token string\">&quot;&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 登录用户的token(oauth)或密码</span>\n  <span class=\"token property\">&quot;theme&quot;</span><span class=\"token operator\">:</span><span class=\"token number\">5678</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//主题色值</span>\n  <span class=\"token property\">&quot;cache&quot;</span><span class=\"token operator\">:</span><span class=\"token string\">&quot;$cacheConfig&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 缓存策略信息，结构见&quot;cacheConfig.json&quot;</span>\n  <span class=\"token property\">&quot;lastLogin&quot;</span><span class=\"token operator\">:</span><span class=\"token string\">&quot;&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//最近一次的注销登录的用户名</span>\n  <span class=\"token property\">&quot;locale&quot;</span><span class=\"token operator\">:</span><span class=\"token string\">&quot;&quot;</span> <span class=\"token comment\">// APP语言信息</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h3 id=\"项目信息\"><a href=\"#项目信息\" class=\"header-anchor\">#</a> 项目信息</h3> <p>由于APP主页要显示其所有项目信息，我们在“jsons”目录下创建一个“repo.json”文件保存项目信息。通过参考Github 获取项目信息的API文档，定义出最终的“repo.json”文件结构，如下：</p> <div class=\"language-json extra-class\"><pre class=\"language-json\"><code><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;id&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">1296269</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;name&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;Hello-World&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//项目名称</span>\n  <span class=\"token property\">&quot;full_name&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;octocat/Hello-World&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//项目完整名称</span>\n  <span class=\"token property\">&quot;owner&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;$user&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 项目拥有者，结构见&quot;user.json&quot;</span>\n  <span class=\"token property\">&quot;parent&quot;</span><span class=\"token operator\">:</span><span class=\"token string\">&quot;$repo&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 如果是fork的项目，则此字段表示fork的父项目信息</span>\n  <span class=\"token property\">&quot;private&quot;</span><span class=\"token operator\">:</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 是否私有项目</span>\n  <span class=\"token property\">&quot;description&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;This your first repo!&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//项目描述</span>\n  <span class=\"token property\">&quot;fork&quot;</span><span class=\"token operator\">:</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 该项目是否为fork的项目</span>\n  <span class=\"token property\">&quot;language&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;JavaScript&quot;</span><span class=\"token punctuation\">,</span><span class=\"token comment\">//该项目的主要编程语言</span>\n  <span class=\"token property\">&quot;forks_count&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">9</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// fork了该项目的数量</span>\n  <span class=\"token property\">&quot;stargazers_count&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">80</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//该项目的star数量</span>\n  <span class=\"token property\">&quot;size&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">108</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 项目占用的存储大小</span>\n  <span class=\"token property\">&quot;default_branch&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;master&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//项目的默认分支</span>\n  <span class=\"token property\">&quot;open_issues_count&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//该项目当前打开的issue数量</span>\n  <span class=\"token property\">&quot;pushed_at&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;2011-01-26T19:06:43Z&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;created_at&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;2011-01-26T19:01:12Z&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;updated_at&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;2011-01-26T19:14:43Z&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;subscribers_count&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">42</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//订阅（关注）该项目的人数</span>\n  <span class=\"token property\">&quot;license&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> <span class=\"token comment\">// 该项目的开源许可证</span>\n    <span class=\"token property\">&quot;key&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;mit&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;name&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;MIT License&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;spdx_id&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;MIT&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;url&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;https://api.github.com/licenses/mit&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;node_id&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;MDc6TGljZW5zZW1pdA==&quot;</span>\n  <span class=\"token punctuation\">}</span>\n  ...<span class=\"token comment\">//省略其它字段</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h3 id=\"生成dart-model类\"><a href=\"#生成dart-model类\" class=\"header-anchor\">#</a> 生成Dart Model类</h3> <p>现在，我们需要的Json数据已经定义完毕，现在只需要运行json_model package提供的命令来通过json文件生成相应的Dart类：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>flutter packages pub run json_model\n</code></pre></div><p>命令执行成功后，可以看到lib/models文件夹下会生成相应的Dart Model类：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>├── models\n│   ├── cacheConfig.dart\n│   ├── cacheConfig.g.dart\n│   ├── index.dart\n│   ├── profile.dart\n│   ├── profile.g.dart\n│   ├── repo.dart\n│   ├── repo.g.dart\n│   ├── user.dart\n│   └── user.g.dart\n\n</code></pre></div><h3 id=\"数据持久化\"><a href=\"#数据持久化\" class=\"header-anchor\">#</a> 数据持久化</h3> <p>我们使用shared_preferences包来对登录用户的Profile信息进行持久化。shared_preferences是一个Flutter插件，它通过Android和iOS平台提供的机制来实现数据持久化。由于shared_preferences的使用非常简单，读者可以自行查看其文档，在此不再赘述。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/204.fb9289cb.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter15/network.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>15.5 网络请求封装 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/205.90d0284c.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter15/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_15-5-网络请求封装\"><a href=\"#_15-5-网络请求封装\" class=\"header-anchor\">#</a> 15.5 网络请求封装</h1> <p>本节我们会基于前面介绍过的dio网络库封装APP中用到的网络请求接口，并同时应用一个简单的缓存策略。下面我们先介绍一下网络接口缓存原理，然后再封装APP的业务请求接口。</p> <h2 id=\"_15-5-1-网络接口缓存\"><a href=\"#_15-5-1-网络接口缓存\" class=\"header-anchor\">#</a> 15.5.1 网络接口缓存</h2> <p>由于在国内访问Github服务器速度较慢，所以我们应用一些简单的缓存策略：将请求的url作为key，对请求的返回值在一个指定时间段类进行缓存，另外设置一个最大缓存数，当超过最大缓存数后移除最早的一条缓存。但是也得提供一种针对特定接口或请求决定是否启用缓存的机制，这种机制可以指定哪些接口或那次请求不应用缓存，这种机制是很有必要的，比如登录接口就不应该缓存，又比如用户在下拉刷新时就不应该再应用缓存。在实现缓存之前我们先定义保存缓存信息的<code>CacheObject</code>类：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">CacheObject</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">CacheObject</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>response<span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">:</span> timeStamp <span class=\"token operator\">=</span> DateTime<span class=\"token punctuation\">.</span><span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>millisecondsSinceEpoch<span class=\"token punctuation\">;</span>\n  Response response<span class=\"token punctuation\">;</span>\n  int timeStamp<span class=\"token punctuation\">;</span> <span class=\"token comment\">// 缓存创建时间</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  bool <span class=\"token keyword\">operator</span> <span class=\"token operator\">==</span><span class=\"token punctuation\">(</span>other<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> response<span class=\"token punctuation\">.</span>hashCode <span class=\"token operator\">==</span> other<span class=\"token punctuation\">.</span>hashCode<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">//将请求uri作为缓存的key</span>\n  <span class=\"token metadata symbol\">@override</span>\n  int <span class=\"token keyword\">get</span> hashCode <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> response<span class=\"token punctuation\">.</span>realUri<span class=\"token punctuation\">.</span>hashCode<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>接下来我们需要实现具体的缓存策略，由于我们使用的是dio package，所以我们可以直接通过拦截器来实现缓存策略：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'dart:collection'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:dio/dio.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'../index.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">CacheObject</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">CacheObject</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>response<span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">:</span> timeStamp <span class=\"token operator\">=</span> DateTime<span class=\"token punctuation\">.</span><span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>millisecondsSinceEpoch<span class=\"token punctuation\">;</span>\n  Response response<span class=\"token punctuation\">;</span>\n  int timeStamp<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  bool <span class=\"token keyword\">operator</span> <span class=\"token operator\">==</span><span class=\"token punctuation\">(</span>other<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> response<span class=\"token punctuation\">.</span>hashCode <span class=\"token operator\">==</span> other<span class=\"token punctuation\">.</span>hashCode<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  int <span class=\"token keyword\">get</span> hashCode <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> response<span class=\"token punctuation\">.</span>realUri<span class=\"token punctuation\">.</span>hashCode<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">NetCache</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">Interceptor</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// 为确保迭代器顺序和对象插入时间一致顺序一致，我们使用LinkedHashMap</span>\n  <span class=\"token keyword\">var</span> cache <span class=\"token operator\">=</span> LinkedHashMap<span class=\"token operator\">&lt;</span>String<span class=\"token punctuation\">,</span> CacheObject<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token function\">onRequest</span><span class=\"token punctuation\">(</span>RequestOptions options<span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>Global<span class=\"token punctuation\">.</span>profile<span class=\"token punctuation\">.</span>cache<span class=\"token punctuation\">.</span>enable<span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> options<span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">// refresh标记是否是&quot;下拉刷新&quot;</span>\n    bool refresh <span class=\"token operator\">=</span> options<span class=\"token punctuation\">.</span>extra<span class=\"token punctuation\">[</span><span class=\"token string\">&quot;refresh&quot;</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">==</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//如果是下拉刷新，先删除相关缓存</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>refresh<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>options<span class=\"token punctuation\">.</span>extra<span class=\"token punctuation\">[</span><span class=\"token string\">&quot;list&quot;</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">==</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">//若是列表，则只要url中包含当前path的缓存全部删除（简单实现，并不精准）</span>\n        cache<span class=\"token punctuation\">.</span><span class=\"token function\">removeWhere</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">,</span> v<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> key<span class=\"token punctuation\">.</span><span class=\"token function\">contains</span><span class=\"token punctuation\">(</span>options<span class=\"token punctuation\">.</span>path<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">// 如果不是列表，则只删除uri相同的缓存</span>\n        <span class=\"token function\">delete</span><span class=\"token punctuation\">(</span>options<span class=\"token punctuation\">.</span>uri<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n      <span class=\"token keyword\">return</span> options<span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>options<span class=\"token punctuation\">.</span>extra<span class=\"token punctuation\">[</span><span class=\"token string\">&quot;noCache&quot;</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">!=</span> <span class=\"token boolean\">true</span> <span class=\"token operator\">&amp;&amp;</span>\n        options<span class=\"token punctuation\">.</span>method<span class=\"token punctuation\">.</span><span class=\"token function\">toLowerCase</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">==</span> <span class=\"token string\">'get'</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      String key <span class=\"token operator\">=</span> options<span class=\"token punctuation\">.</span>extra<span class=\"token punctuation\">[</span><span class=\"token string\">&quot;cacheKey&quot;</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">?</span><span class=\"token operator\">?</span> options<span class=\"token punctuation\">.</span>uri<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">var</span> ob <span class=\"token operator\">=</span> cache<span class=\"token punctuation\">[</span>key<span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>ob <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">//若缓存未过期，则返回缓存内容</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>DateTime<span class=\"token punctuation\">.</span><span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>millisecondsSinceEpoch <span class=\"token operator\">-</span> ob<span class=\"token punctuation\">.</span>timeStamp<span class=\"token punctuation\">)</span> <span class=\"token operator\">/</span> <span class=\"token number\">1000</span> <span class=\"token operator\">&lt;</span>\n            Global<span class=\"token punctuation\">.</span>profile<span class=\"token punctuation\">.</span>cache<span class=\"token punctuation\">.</span>maxAge<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">return</span> cache<span class=\"token punctuation\">[</span>key<span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span>response<span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token comment\">//若已过期则删除缓存，继续向服务器请求</span>\n          cache<span class=\"token punctuation\">.</span><span class=\"token function\">remove</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token function\">onError</span><span class=\"token punctuation\">(</span>DioError err<span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 错误状态不缓存</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token function\">onResponse</span><span class=\"token punctuation\">(</span>Response response<span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 如果启用缓存，将返回结果保存到缓存</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>Global<span class=\"token punctuation\">.</span>profile<span class=\"token punctuation\">.</span>cache<span class=\"token punctuation\">.</span>enable<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">_saveCache</span><span class=\"token punctuation\">(</span>response<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token function\">_saveCache</span><span class=\"token punctuation\">(</span>Response object<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    RequestOptions options <span class=\"token operator\">=</span> object<span class=\"token punctuation\">.</span>request<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>options<span class=\"token punctuation\">.</span>extra<span class=\"token punctuation\">[</span><span class=\"token string\">&quot;noCache&quot;</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">!=</span> <span class=\"token boolean\">true</span> <span class=\"token operator\">&amp;&amp;</span>\n        options<span class=\"token punctuation\">.</span>method<span class=\"token punctuation\">.</span><span class=\"token function\">toLowerCase</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">==</span> <span class=\"token string\">&quot;get&quot;</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">// 如果缓存数量超过最大数量限制，则先移除最早的一条记录</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>cache<span class=\"token punctuation\">.</span>length <span class=\"token operator\">==</span> Global<span class=\"token punctuation\">.</span>profile<span class=\"token punctuation\">.</span>cache<span class=\"token punctuation\">.</span>maxCount<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        cache<span class=\"token punctuation\">.</span><span class=\"token function\">remove</span><span class=\"token punctuation\">(</span>cache<span class=\"token punctuation\">[</span>cache<span class=\"token punctuation\">.</span>keys<span class=\"token punctuation\">.</span>first<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n      String key <span class=\"token operator\">=</span> options<span class=\"token punctuation\">.</span>extra<span class=\"token punctuation\">[</span><span class=\"token string\">&quot;cacheKey&quot;</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">?</span><span class=\"token operator\">?</span> options<span class=\"token punctuation\">.</span>uri<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      cache<span class=\"token punctuation\">[</span>key<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">CacheObject</span><span class=\"token punctuation\">(</span>object<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">delete</span><span class=\"token punctuation\">(</span>String key<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    cache<span class=\"token punctuation\">.</span><span class=\"token function\">remove</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>关于代码的解释都在注释中了，在此需要说明的是dio包的<code>option.extra</code>是专门用于扩展请求参数的，我们通过定义了“refresh”和“noCache”两个参数实现了“针对特定接口或请求决定是否启用缓存的机制”，这两个参数含义如下：</p> <table><thead><tr><th>参数名</th> <th>类型</th> <th>解释</th></tr></thead> <tbody><tr><td>refresh</td> <td>bool</td> <td>如果为true，则本次请求不使用缓存，但新的请求结果依然会被缓存</td></tr> <tr><td>noCache</td> <td>bool</td> <td>本次请求禁用缓存，请求结果也不会被缓存。</td></tr></tbody></table> <h2 id=\"_15-5-2-封装网络请求\"><a href=\"#_15-5-2-封装网络请求\" class=\"header-anchor\">#</a> 15.5.2 封装网络请求</h2> <p>一个完整的APP，可能会涉及很多网络请求，为了便于管理、收敛请求入口，工程上最好的作法就是将所有网络请求放到同一个源码文件中。由于我们的接口都是请求的Github 开发平台提供的API，所以我们定义一个Git类，专门用于Github API接口调用。另外，在调试过程中，我们通常需要一些工具来查看网络请求、响应报文，使用网络代理工具来调试网络数据问题是主流方式。配置代理需要在应用中指定代理服务器的地址和端口，另外Github API是HTTPS协议，所以在配置完代理后还应该禁用证书校验，这些配置我们在Git类初始化时执行（<code>init()方法</code>）。下面是Git类的源码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'dart:async'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'dart:convert'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'dart:io'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:dio/dio.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:dio/adapter.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'../index.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">Git</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// 在网络请求过程中可能会需要使用当前的context信息，比如在请求失败时</span>\n  <span class=\"token comment\">// 打开一个新路由，而打开新路由需要context信息。</span>\n  <span class=\"token function\">Git</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>context<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    _options <span class=\"token operator\">=</span> <span class=\"token function\">Options</span><span class=\"token punctuation\">(</span>extra<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span><span class=\"token string\">&quot;context&quot;</span><span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  BuildContext context<span class=\"token punctuation\">;</span>\n  Options _options<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">static</span> Dio dio <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Dio</span><span class=\"token punctuation\">(</span><span class=\"token function\">BaseOptions</span><span class=\"token punctuation\">(</span>\n    baseUrl<span class=\"token punctuation\">:</span> <span class=\"token string\">'https://api.github.com/'</span><span class=\"token punctuation\">,</span>\n    headers<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span>\n      HttpHeaders<span class=\"token punctuation\">.</span>acceptHeader<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;application/vnd.github.squirrel-girl-preview,&quot;</span>\n          <span class=\"token string\">&quot;application/vnd.github.symmetra-preview+json&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">static</span> <span class=\"token keyword\">void</span> <span class=\"token function\">init</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 添加缓存插件</span>\n    dio<span class=\"token punctuation\">.</span>interceptors<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span>Global<span class=\"token punctuation\">.</span>netCache<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">// 设置用户token（可能为null，代表未登录）</span>\n    dio<span class=\"token punctuation\">.</span>options<span class=\"token punctuation\">.</span>headers<span class=\"token punctuation\">[</span>HttpHeaders<span class=\"token punctuation\">.</span>authorizationHeader<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> Global<span class=\"token punctuation\">.</span>profile<span class=\"token punctuation\">.</span>token<span class=\"token punctuation\">;</span>\n\n    <span class=\"token comment\">// 在调试模式下需要抓包调试，所以我们使用代理，并禁用HTTPS证书校验</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>Global<span class=\"token punctuation\">.</span>isRelease<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token punctuation\">(</span>dio<span class=\"token punctuation\">.</span>httpClientAdapter <span class=\"token operator\">as</span> DefaultHttpClientAdapter<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>onHttpClientCreate <span class=\"token operator\">=</span>\n          <span class=\"token punctuation\">(</span>client<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        client<span class=\"token punctuation\">.</span>findProxy <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>uri<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">return</span> <span class=\"token string\">&quot;PROXY 10.1.10.250:8888&quot;</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n        <span class=\"token comment\">//代理工具会提供一个抓包的自签名证书，会通不过证书校验，所以我们禁用证书校验</span>\n        client<span class=\"token punctuation\">.</span>badCertificateCallback <span class=\"token operator\">=</span>\n            <span class=\"token punctuation\">(</span>X509Certificate cert<span class=\"token punctuation\">,</span> String host<span class=\"token punctuation\">,</span> int port<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">// 登录接口，登录成功后返回用户信息</span>\n  Future<span class=\"token operator\">&lt;</span>User<span class=\"token operator\">&gt;</span> <span class=\"token function\">login</span><span class=\"token punctuation\">(</span>String login<span class=\"token punctuation\">,</span> String pwd<span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    String basic <span class=\"token operator\">=</span> <span class=\"token string\">'Basic '</span> <span class=\"token operator\">+</span> base64<span class=\"token punctuation\">.</span><span class=\"token function\">encode</span><span class=\"token punctuation\">(</span>utf8<span class=\"token punctuation\">.</span><span class=\"token function\">encode</span><span class=\"token punctuation\">(</span><span class=\"token string\">'$login:$pwd'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">var</span> r <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> dio<span class=\"token punctuation\">.</span><span class=\"token keyword\">get</span><span class=\"token punctuation\">(</span>\n      <span class=\"token string\">&quot;/users/$login&quot;</span><span class=\"token punctuation\">,</span>\n      options<span class=\"token punctuation\">:</span> _options<span class=\"token punctuation\">.</span><span class=\"token function\">merge</span><span class=\"token punctuation\">(</span>headers<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span>\n        HttpHeaders<span class=\"token punctuation\">.</span>authorizationHeader<span class=\"token punctuation\">:</span> basic\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> extra<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token string\">&quot;noCache&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//本接口禁用缓存</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//登录成功后更新公共头（authorization），此后的所有请求都会带上用户身份信息</span>\n    dio<span class=\"token punctuation\">.</span>options<span class=\"token punctuation\">.</span>headers<span class=\"token punctuation\">[</span>HttpHeaders<span class=\"token punctuation\">.</span>authorizationHeader<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> basic<span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//清空所有缓存</span>\n    Global<span class=\"token punctuation\">.</span>netCache<span class=\"token punctuation\">.</span>cache<span class=\"token punctuation\">.</span><span class=\"token function\">clear</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//更新profile中的token信息</span>\n    Global<span class=\"token punctuation\">.</span>profile<span class=\"token punctuation\">.</span>token <span class=\"token operator\">=</span> basic<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> User<span class=\"token punctuation\">.</span><span class=\"token function\">fromJson</span><span class=\"token punctuation\">(</span>r<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">//获取用户项目列表</span>\n  Future<span class=\"token operator\">&lt;</span>List<span class=\"token operator\">&lt;</span>Repo<span class=\"token operator\">&gt;&gt;</span> <span class=\"token function\">getRepos</span><span class=\"token punctuation\">(</span>\n      <span class=\"token punctuation\">{</span>Map<span class=\"token operator\">&lt;</span>String<span class=\"token punctuation\">,</span> <span class=\"token keyword\">dynamic</span><span class=\"token operator\">&gt;</span> queryParameters<span class=\"token punctuation\">,</span> <span class=\"token comment\">//query参数，用于接收分页信息</span>\n      refresh <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>refresh<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">// 列表下拉刷新，需要删除缓存（拦截器中会读取这些信息）</span>\n      _options<span class=\"token punctuation\">.</span>extra<span class=\"token punctuation\">.</span><span class=\"token function\">addAll</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span><span class=\"token string\">&quot;refresh&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;list&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">var</span> r <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> dio<span class=\"token punctuation\">.</span><span class=\"token keyword\">get</span><span class=\"token operator\">&lt;</span>List<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n      <span class=\"token string\">&quot;user/repos&quot;</span><span class=\"token punctuation\">,</span>\n      queryParameters<span class=\"token punctuation\">:</span> queryParameters<span class=\"token punctuation\">,</span>\n      options<span class=\"token punctuation\">:</span> _options<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> r<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Repo<span class=\"token punctuation\">.</span><span class=\"token function\">fromJson</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toList</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以看到我们在<code>init()</code>方法中，我们判断了是否是调试环境，然后做了一些针对调试环境的网络配置（设置代理和禁用证书校验）。而<code>Git.init()</code>方法是应用启动时被调用的（<code>Global.init()</code>方法中会调用<code>Git.init()</code>）。</p> <p>另外需要注意，我们所有的网络请求是通过同一个<code>dio</code>实例（静态变量）发出的，在创建该<code>dio</code>实例时我们将Github API的基地址和API支持的Header进行了全局配置，这样所有通过该<code>dio</code>实例发出的请求都会默认使用者些配置。</p> <p>在本实例中，我们只用到了登录接口和获取用户项目的接口，所以在<code>Git</code>类中只定义了<code>login(…)</code>和<code>getRepos(…)</code>方法，如果读者要在本实例的基础上扩充功能，读者可以将其它的接口请求方法添加到<code>Git</code>类中，这样便实现了网络请求接口在代码层面的集中管理和维护。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/205.90d0284c.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter2/first_flutter_app.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>2.1 计数器应用示例 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/118.d4908451.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter2/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_2-1-计数器应用示例\"><a href=\"#_2-1-计数器应用示例\" class=\"header-anchor\">#</a> 2.1 计数器应用示例</h1> <p>用Android Studio和VS Code创建的Flutter应用模板默认是一个简单的计数器示例。本节先仔细讲解一下这个计数器Demo的源码，让读者对Flutter应用程序结构有个基本了解，然后在随后的小节中将会基于此示例，一步一步添加一些新的功能来介绍Flutter应用的其它概念与技术。</p> <p>对于接下来的示例，希望读者可以跟着笔者一起亲自动手来写一下，这样不仅可以加深印象，而且也会对介绍的概念与技术有一个真切的体会。如果你还不是很熟悉Dart语言或者没有移动开发经验，不用担心，只要你熟悉面向对象和基本编程概念（如变量、循环和条件控制），则可以完成本示例。</p> <h2 id=\"_2-1-1-创建flutter应用模板\"><a href=\"#_2-1-1-创建flutter应用模板\" class=\"header-anchor\">#</a> 2.1.1 创建Flutter应用模板</h2> <p>通过Android Studio或VS Code创建一个新的Flutter工程，命名为&quot;first_flutter_app&quot;。创建好后，就会得到一个计数器应用的Demo。</p> <blockquote><p>注意，默认Demo示例可能随着编辑器Flutter插件的版本变化而变化，本例中会介绍计数器示例的全部代码，所以不会对本示例产生影响。</p></blockquote> <p>我们先运行创建的工程，效果如图2-1所示：</p> <p><img src=\"/assets/img/2-1.801e91b2.png\" alt=\"图2-1\"></p> <p>该计数器示例中，每点击一次右下角带“+”号的悬浮按钮，屏幕中央的数字就会加1。</p> <p>在这个示例中，主要Dart代码是在 <strong>lib/main.dart</strong> 文件中，下面是它的源码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">void</span> <span class=\"token function\">main</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">runApp</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">MyApp</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">MyApp</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">MaterialApp</span><span class=\"token punctuation\">(</span>\n      title<span class=\"token punctuation\">:</span> <span class=\"token string\">'Flutter Demo'</span><span class=\"token punctuation\">,</span>\n      theme<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">ThemeData</span><span class=\"token punctuation\">(</span>\n        primarySwatch<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      home<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">MyHomePage</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token string\">'Flutter Demo Home Page'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">MyHomePage</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">MyHomePage</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>Key key<span class=\"token punctuation\">,</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>title<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> String title<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  _MyHomePageState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_MyHomePageState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_MyHomePageState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>MyHomePage<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  int _counter <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_incrementCounter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _counter<span class=\"token operator\">++</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Scaffold</span><span class=\"token punctuation\">(</span>\n      appBar<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AppBar</span><span class=\"token punctuation\">(</span>\n        title<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span>widget<span class=\"token punctuation\">.</span>title<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      body<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Center</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Column</span><span class=\"token punctuation\">(</span>\n          mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span>\n              <span class=\"token string\">'You have pushed the button this many times:'</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span>\n              <span class=\"token string\">'$_counter'</span><span class=\"token punctuation\">,</span>\n              style<span class=\"token punctuation\">:</span> Theme<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>textTheme<span class=\"token punctuation\">.</span>headline4<span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      floatingActionButton<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">FloatingActionButton</span><span class=\"token punctuation\">(</span>\n        onPressed<span class=\"token punctuation\">:</span> _incrementCounter<span class=\"token punctuation\">,</span>\n        tooltip<span class=\"token punctuation\">:</span> <span class=\"token string\">'Increment'</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>add<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// This trailing comma makes auto-formatting nicer for build methods.</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n</code></pre></div><h3 id=\"分析\"><a href=\"#分析\" class=\"header-anchor\">#</a> 分析</h3> <ol><li><p>导入包。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>此行代码作用是导入了Material UI组件库。<a href=\"https://material.io/guidelines/\" target=\"_blank\" rel=\"noopener noreferrer\">Material<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>是一种标准的移动端和web端的视觉设计语言， Flutter默认提供了一套丰富的Material风格的UI组件。</p></li> <li><p>应用入口。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">main</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">runApp</span><span class=\"token punctuation\">(</span><span class=\"token function\">MyApp</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><ul><li>与C/C++、Java类似，Flutter 应用中<code>main</code>函数为应用程序的入口。<code>main</code>函数中调用了<code>runApp</code> 方法，它的功能是启动Flutter应用。<code>runApp</code>它接受一个<code>Widget</code>参数，在本示例中它是一个<code>MyApp</code>对象，<code>MyApp()</code>是Flutter应用的根组件。</li> <li><code>main</code>函数使用了(<code>=&gt;</code>)符号，这是Dart中单行函数或方法的简写。</li></ul></li> <li><p>应用结构。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">MyApp</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">MaterialApp</span><span class=\"token punctuation\">(</span>\n      <span class=\"token comment\">//应用名称  </span>\n      title<span class=\"token punctuation\">:</span> <span class=\"token string\">'Flutter Demo'</span><span class=\"token punctuation\">,</span> \n      theme<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">ThemeData</span><span class=\"token punctuation\">(</span>\n        <span class=\"token comment\">//蓝色主题  </span>\n        primarySwatch<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token comment\">//应用首页路由  </span>\n      home<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">MyHomePage</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token string\">'Flutter Demo Home Page'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><ul><li><p><code>MyApp</code>类代表Flutter应用，它继承了 <code>StatelessWidget</code>类，这也就意味着应用本身也是一个widget。</p></li> <li><p>在Flutter中，大多数东西都是widget（后同“组件”或“部件”），包括对齐(alignment)、填充(padding)和布局(layout)等，它们都是以widget的形式提供。</p></li> <li><p>Flutter在构建页面时，会调用组件的<code>build</code>方法，widget的主要工作是提供一个build()方法来描述如何构建UI界面（通常是通过组合、拼装其它基础widget）。</p></li> <li><p><code>MaterialApp</code> 是Material库中提供的Flutter APP框架，通过它可以设置应用的名称、主题、语言、首页及路由列表等。<code>MaterialApp</code>也是一个widget。</p></li> <li><p><code>home</code> 为Flutter应用的首页，它也是一个widget。</p></li></ul></li></ol> <h2 id=\"_2-1-2-首页\"><a href=\"#_2-1-2-首页\" class=\"header-anchor\">#</a> 2.1.2 首页</h2> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">MyHomePage</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">MyHomePage</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>Key key<span class=\"token punctuation\">,</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>title<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> String title<span class=\"token punctuation\">;</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _MyHomePageState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_MyHomePageState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_MyHomePageState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>MyHomePage<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p><code>MyHomePage</code> 是Flutter应用的首页，它继承自<code>StatefulWidget</code>类，表示它是一个有状态的组件（Stateful widget）。关于Stateful widget我们将在第三章“Widget简介”一节仔细介绍，现在我们只需简单认为有状态的组件（Stateful widget） 和无状态的组件（Stateless widget）有两点不同：</p> <ol><li><p>Stateful widget可以拥有状态，这些状态在widget生命周期中是可以变的，而Stateless widget是不可变的。</p></li> <li><p>Stateful widget至少由两个类组成：</p> <ul><li>一个<code>StatefulWidget</code>类。</li> <li>一个 <code>State</code>类； <code>StatefulWidget</code>类本身是不变的，但是<code>State</code>类中持有的状态在widget生命周期中可能会发生变化。</li></ul> <p><code>_MyHomePageState</code>类是<code>MyHomePage</code>类对应的状态类。看到这里，读者可能已经发现：和<code>MyApp</code> 类不同， <code>MyHomePage</code>类中并没有<code>build</code>方法，取而代之的是，<code>build</code>方法被挪到了<code>_MyHomePageState</code>方法中，至于为什么这么做，先留个疑问，在分析完完整代码后再来解答。</p></li></ol> <h3 id=\"state类\"><a href=\"#state类\" class=\"header-anchor\">#</a> State类</h3> <p>接下来，我们看看<code>_MyHomePageState</code>中都包含哪些东西：</p> <ol><li><p>该组件的状态。由于我们只需要维护一个点击次数计数器，所以定义一个<code>_counter</code>状态：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>int _counter <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//用于记录按钮点击的总次数</span>\n</code></pre></div><p><code>_counter</code> 为保存屏幕右下角带“+”号按钮点击次数的状态。</p></li> <li><p>设置状态的自增函数。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">_incrementCounter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n     _counter<span class=\"token operator\">++</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>当按钮点击时，会调用此函数，该函数的作用是先自增<code>_counter</code>，然后调用<code>setState</code> 方法。<code>setState</code>方法的作用是通知Flutter框架，有状态发生了改变，Flutter框架收到通知后，会执行<code>build</code>方法来根据新的状态重新构建界面， Flutter 对此方法做了优化，使重新执行变的很快，所以你可以重新构建任何需要更新的东西，而无需分别去修改各个widget。</p></li> <li><p>构建UI界面</p> <p>构建UI界面的逻辑在<code>build</code>方法中，当<code>MyHomePage</code>第一次创建时，<code>_MyHomePageState</code>类会被创建，当初始化完成后，Flutter框架会调用Widget的<code>build</code>方法来构建widget树，最终将widget树渲染到设备屏幕上。所以，我们看看<code>_MyHomePageState</code>的<code>build</code>方法中都干了什么事：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Scaffold</span><span class=\"token punctuation\">(</span>\n      appBar<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AppBar</span><span class=\"token punctuation\">(</span>\n        title<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span>widget<span class=\"token punctuation\">.</span>title<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      body<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Center</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Column</span><span class=\"token punctuation\">(</span>\n          mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span>\n              <span class=\"token string\">'You have pushed the button this many times:'</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span>\n              <span class=\"token string\">'$_counter'</span><span class=\"token punctuation\">,</span>\n              style<span class=\"token punctuation\">:</span> Theme<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>textTheme<span class=\"token punctuation\">.</span>headline4<span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      floatingActionButton<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">FloatingActionButton</span><span class=\"token punctuation\">(</span>\n        onPressed<span class=\"token punctuation\">:</span> _incrementCounter<span class=\"token punctuation\">,</span>\n        tooltip<span class=\"token punctuation\">:</span> <span class=\"token string\">'Increment'</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>add<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n</code></pre></div><ul><li><code>Scaffold</code> 是 Material 库中提供的页面脚手架，它提供了默认的导航栏、标题和包含主屏幕widget树（后同“组件树”或“部件树”）的<code>body</code>属性，组件树可以很复杂。本书后面示例中，路由默认都是通过<code>Scaffold</code>创建。</li> <li><code>body</code>的组件树中包含了一个<code>Center</code> 组件，<code>Center</code> 可以将其子组件树对齐到屏幕中心。此例中， <code>Center</code> 子组件是一个<code>Column</code> 组件，<code>Column</code>的作用是将其所有子组件沿屏幕垂直方向依次排列； 此例中<code>Column</code>子组件是两个 <code>Text</code>，第一个<code>Text</code> 显示固定文本 “You have pushed the button this many times:”，第二个<code>Text</code> 显示<code>_counter</code>状态的数值。</li> <li><code>floatingActionButton</code>是页面右下角的带“+”的悬浮按钮，它的<code>onPressed</code>属性接受一个回调函数，代表它被点击后的处理器，本例中直接将<code>_incrementCounter</code>方法作为其处理函数。</li></ul></li></ol> <p>现在，我们将整个计数器执行流程串起来：当右下角的<code>floatingActionButton</code>按钮被点击之后，会调用<code>_incrementCounter</code>方法。在<code>_incrementCounter</code>方法中，首先会自增<code>_counter</code>计数器（状态），然后<code>setState</code>会通知Flutter框架状态发生变化，接着，Flutter框架会调用<code>build</code>方法以新的状态重新构建UI，最终显示在设备屏幕上。</p> <h4 id=\"为什么要将build方法放在state中-而不是放在statefulwidget中\"><a href=\"#为什么要将build方法放在state中-而不是放在statefulwidget中\" class=\"header-anchor\">#</a> 为什么要将build方法放在State中，而不是放在StatefulWidget中？</h4> <p>现在，我们回答之前提出的问题，为什么<code>build()</code>方法放在State（而不是<code>StatefulWidget</code>）中 ？这主要是为了提高开发的灵活性。如果将<code>build()</code>方法放在<code>StatefulWidget</code>中则会有两个问题：</p> <ul><li><p>状态访问不便</p> <p>试想一下，如果我们的<code>StatefulWidget</code>有很多状态，而每次状态改变都要调用<code>build</code>方法，由于状态是保存在State中的，如果<code>build</code>方法在<code>StatefulWidget</code>中，那么<code>build</code>方法和状态分别在两个类中，那么构建时读取状态将会很不方便！试想一下，如果真的将<code>build</code>方法放在StatefulWidget中的话，由于构建用户界面过程需要依赖State，所以<code>build</code>方法将必须加一个<code>State</code>参数，大概是下面这样：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> State state<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">//state.counter</span>\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token punctuation\">}</span>\n</code></pre></div><p>这样的话就只能将State的所有状态声明为公开的状态，这样才能在State类外部访问状态！但是，将状态设置为公开后，状态将不再具有私密性，这就会导致对状态的修改将会变的不可控。但如果将<code>build()</code>方法放在State中的话，构建过程不仅可以直接访问状态，而且也无需公开私有状态，这会非常方便。</p></li> <li><p>继承<code>StatefulWidget</code>不便</p> <p>例如，Flutter中有一个动画widget的基类<code>AnimatedWidget</code>，它继承自<code>StatefulWidget</code>类。<code>AnimatedWidget</code>中引入了一个抽象方法<code>build(BuildContext context)</code>，继承自<code>AnimatedWidget</code>的动画widget都要实现这个<code>build</code>方法。现在设想一下，如果<code>StatefulWidget</code> 类中已经有了一个<code>build</code>方法，正如上面所述，此时<code>build</code>方法需要接收一个state对象，这就意味着<code>AnimatedWidget</code>必须将自己的State对象(记为_animatedWidgetState)提供给其子类，因为子类需要在其<code>build</code>方法中调用父类的<code>build</code>方法，代码可能如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">MyAnimationWidget</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">AnimatedWidget</span><span class=\"token punctuation\">{</span>\n    <span class=\"token metadata symbol\">@override</span>\n    Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> State state<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">//由于子类要用到AnimatedWidget的状态对象_animatedWidgetState，</span>\n      <span class=\"token comment\">//所以AnimatedWidget必须通过某种方式将其状态对象_animatedWidgetState</span>\n      <span class=\"token comment\">//暴露给其子类   </span>\n      <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">build</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> _animatedWidgetState<span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>这样很显然是不合理的，因为</p> <ol><li><code>AnimatedWidget</code>的状态对象是<code>AnimatedWidget</code>内部实现细节，不应该暴露给外部。</li> <li>如果要将父类状态暴露给子类，那么必须得有一种传递机制，而做这一套传递机制是无意义的，因为父子类之间状态的传递和子类本身逻辑是无关的。</li></ol></li></ul> <p>综上所述，可以发现，对于<code>StatefulWidget</code>，将<code>build</code>方法放在State中，可以给开发带来很大的灵活性。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/118.d4908451.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter2/flutter_app_debug.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>2.5 调试Flutter应用 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/206.2dbac1ab.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter2/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_2-5-调试flutter应用\"><a href=\"#_2-5-调试flutter应用\" class=\"header-anchor\">#</a> 2.5 调试Flutter应用</h1> <p>有各种各样的工具和功能来帮助调试Flutter应用程序。</p> <h3 id=\"dart-分析器\"><a href=\"#dart-分析器\" class=\"header-anchor\">#</a> Dart 分析器</h3> <p>在运行应用程序前，请运行<code>flutter analyze</code>测试你的代码。这个工具是一个静态代码检查工具，它是<code>dartanalyzer</code>工具的一个包装，主要用于分析代码并帮助开发者发现可能的错误，比如，Dart分析器大量使用了代码中的类型注释来帮助追踪问题，避免<code>var</code>、无类型的参数、无类型的列表文字等。</p> <p>如果你使用IntelliJ的Flutter插件，那么分析器在打开IDE时就已经自动启用了，如果读者使用的是其它IDE，强烈建议读者启用Dart 分析器，因为在大多数时候，Dart 分析器可以在代码运行前发现大多数问题。</p> <h3 id=\"dart-observatory-语句级的单步调试和分析器\"><a href=\"#dart-observatory-语句级的单步调试和分析器\" class=\"header-anchor\">#</a> Dart Observatory (语句级的单步调试和分析器)</h3> <p>如果我们使用<code>flutter run</code>启动应用程序，那么当它运行时，我们可以打开Observatory工具的Web页面，例如Observatory默认监听<a href=\"http://127.0.0.1:8100/\" target=\"_blank\" rel=\"noopener noreferrer\">http://127.0.0.1:8100/<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>，可以在浏览器中直接打开该链接。直接使用语句级单步调试器连接到您的应用程序。如果您使用的是IntelliJ，则还可以使用其内置的调试器来调试您的应用程序。</p> <p>Observatory 同时支持分析、检查堆等。有关Observatory的更多信息请参考<a href=\"https://dart-lang.github.io/observatory/\" target=\"_blank\" rel=\"noopener noreferrer\">Observatory 文档<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>。</p> <p>如果您使用Observatory进行分析，请确保通过<code>--profile</code>选项来运行<code>flutter run</code>命令来运行应用程序。 否则，配置文件中将出现的主要问题将是调试断言，以验证框架的各种不变量（请参阅下面的“调试模式断言”）。</p> <h3 id=\"debugger-声明\"><a href=\"#debugger-声明\" class=\"header-anchor\">#</a> <code>debugger()</code> 声明</h3> <p>当使用Dart Observatory（或另一个Dart调试器，例如IntelliJ IDE中的调试器）时，可以使用该<code>debugger()</code>语句插入编程式断点。要使用这个，你必须添加<code>import 'dart:developer';</code>到相关文件顶部。</p> <p><code>debugger()</code>语句采用一个可选<code>when</code>参数，您可以指定该参数仅在特定条件为真时中断，如下所示：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">someFunction</span><span class=\"token punctuation\">(</span>double offset<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">debugger</span><span class=\"token punctuation\">(</span>when<span class=\"token punctuation\">:</span> offset <span class=\"token operator\">&gt;</span> <span class=\"token number\">30.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">// ...</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h3 id=\"print、debugprint、flutter-logs\"><a href=\"#print、debugprint、flutter-logs\" class=\"header-anchor\">#</a> <code>print</code>、<code>debugPrint</code>、<code>flutter logs</code></h3> <p>Dart <code>print()</code>功能将输出到系统控制台，您可以使用<code>flutter logs</code>来查看它（基本上是一个包装<code>adb logcat</code>）。</p> <p>如果你一次输出太多，那么Android有时会丢弃一些日志行。为了避免这种情况，您可以使用Flutter的<code>foundation</code>库中的<a href=\"https://docs.flutter.io/flutter/foundation/debugPrint.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>debugPrint()</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>。 这是一个封装print，它将输出限制在一个级别，避免被Android内核丢弃。</p> <p>Flutter框架中的许多类都有<code>toString</code>实现。按照惯例，这些输出通常包括对象的<code>runtimeType</code>单行输出，通常在表单中ClassName(more information about this instance…)。 树中使用的一些类也具有<code>toStringDeep</code>，从该点返回整个子树的多行描述。已一些具有详细信息<code>toString</code>的类会实现一个<code>toStringShort</code>，它只返回对象的类型或其他非常简短的（一个或两个单词）描述。</p> <h3 id=\"调试模式断言\"><a href=\"#调试模式断言\" class=\"header-anchor\">#</a> 调试模式断言</h3> <p>在Flutter应用调试过程中，Dart <code>assert</code>语句被启用，并且Flutter框架使用它来执行许多运行时检查来验证是否违反一些不可变的规则。</p> <p>当一个不可变的规则被违反时，它被报告给控制台，并带有一些上下文信息来帮助追踪问题的根源。</p> <p>要关闭调试模式并使用发布模式，请使用<code>flutter run --release</code>运行您的应用程序。 这也关闭了Observatory调试器。一个中间模式可以关闭除Observatory之外所有调试辅助工具的，称为“profile mode”，用<code>--profile</code>替代<code>--release</code>即可。</p> <h3 id=\"调试应用程序层\"><a href=\"#调试应用程序层\" class=\"header-anchor\">#</a> 调试应用程序层</h3> <p>Flutter框架的每一层都提供了将其当前状态或事件转储(dump)到控制台（使用<code>debugPrint</code>）的功能。</p> <h4 id=\"widget-树\"><a href=\"#widget-树\" class=\"header-anchor\">#</a> Widget 树</h4> <p>要转储Widgets树的状态，请调用<a href=\"https://docs.flutter.io/flutter/widgets/debugDumpApp.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>debugDumpApp()</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>。 只要应用程序已经构建了至少一次（即在调用<code>build()</code>之后的任何时间），您可以在应用程序未处于构建阶段（即，不在<code>build()</code>方法内调用 ）的任何时间调用此方法（在调用<code>runApp()</code>之后）。</p> <p>如, 这个应用程序:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">void</span> <span class=\"token function\">main</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">runApp</span><span class=\"token punctuation\">(</span>\n    <span class=\"token keyword\">new</span> <span class=\"token class-name\">MaterialApp</span><span class=\"token punctuation\">(</span>\n      home<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AppHome</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">AppHome</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Material</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Center</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">FlatButton</span><span class=\"token punctuation\">(</span>\n          onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token function\">debugDumpApp</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Dump App'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>…会输出这样的内容（精确的细节会根据框架的版本、设备的大小等等而变化）：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>I/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>: WidgetsFlutterBinding - CHECKED MODE\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>: RenderObjectToWidgetAdapter<span class=\"token operator\">&lt;</span>RenderBox<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span>GlobalObjectKey RenderView<span class=\"token punctuation\">(</span><span class=\"token number\">497039273</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span> renderObject: RenderView<span class=\"token punctuation\">)</span>\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>: └MaterialApp<span class=\"token punctuation\">(</span>state: _MaterialAppState<span class=\"token punctuation\">(</span><span class=\"token number\">1009803148</span><span class=\"token punctuation\">))</span>\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:  └ScrollConfiguration<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:   └AnimatedTheme<span class=\"token punctuation\">(</span>duration: 200ms<span class=\"token punctuation\">;</span> state: _AnimatedThemeState<span class=\"token punctuation\">(</span><span class=\"token number\">543295893</span><span class=\"token punctuation\">;</span> ticker inactive<span class=\"token punctuation\">;</span> ThemeDataTween<span class=\"token punctuation\">(</span>ThemeData<span class=\"token punctuation\">(</span>Brightness.light Color<span class=\"token punctuation\">(</span>0xff2196f3<span class=\"token punctuation\">)</span> etc<span class=\"token punctuation\">..</span>.<span class=\"token punctuation\">)</span> → null<span class=\"token punctuation\">))</span><span class=\"token punctuation\">)</span>\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:    └Theme<span class=\"token punctuation\">(</span>ThemeData<span class=\"token punctuation\">(</span>Brightness.light Color<span class=\"token punctuation\">(</span>0xff2196f3<span class=\"token punctuation\">)</span> etc<span class=\"token punctuation\">..</span>.<span class=\"token punctuation\">))</span>\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:     └WidgetsApp<span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span>GlobalObjectKey _MaterialAppState<span class=\"token punctuation\">(</span><span class=\"token number\">1009803148</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span> state: _WidgetsAppState<span class=\"token punctuation\">(</span><span class=\"token number\">552902158</span><span class=\"token punctuation\">))</span>\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:      └CheckedModeBanner<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:       └Banner<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:        └CustomPaint<span class=\"token punctuation\">(</span>renderObject: RenderCustomPaint<span class=\"token punctuation\">)</span>\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:         └DefaultTextStyle<span class=\"token punctuation\">(</span>inherit: <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span> color: Color<span class=\"token punctuation\">(</span>0xd0ff0000<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> family: <span class=\"token string\">&quot;monospace&quot;</span><span class=\"token punctuation\">;</span> size: <span class=\"token number\">48.0</span><span class=\"token punctuation\">;</span> weight: <span class=\"token number\">900</span><span class=\"token punctuation\">;</span> decoration: double Color<span class=\"token punctuation\">(</span>0xffffff00<span class=\"token punctuation\">)</span> TextDecoration.underline<span class=\"token punctuation\">)</span>\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:          └MediaQuery<span class=\"token punctuation\">(</span>MediaQueryData<span class=\"token punctuation\">(</span>size: Size<span class=\"token punctuation\">(</span><span class=\"token number\">411.4</span>, <span class=\"token number\">683.4</span><span class=\"token punctuation\">)</span>, devicePixelRatio: <span class=\"token number\">2.625</span>, textScaleFactor: <span class=\"token number\">1.0</span>, padding: EdgeInsets<span class=\"token punctuation\">(</span><span class=\"token number\">0.0</span>, <span class=\"token number\">24.0</span>, <span class=\"token number\">0.0</span>, <span class=\"token number\">0.0</span><span class=\"token punctuation\">))</span><span class=\"token punctuation\">)</span>\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:           └LocaleQuery<span class=\"token punctuation\">(</span>null<span class=\"token punctuation\">)</span>\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:            └Title<span class=\"token punctuation\">(</span>color: Color<span class=\"token punctuation\">(</span>0xff2196f3<span class=\"token punctuation\">))</span>\n<span class=\"token punctuation\">..</span>. <span class=\"token comment\">#省略剩余内容</span>\n</code></pre></div><p>这是一个“扁平化”的树，显示了通过各种构建函数投影的所有widget（如果你在widget树的根中调用<code>toStringDeepwidget</code>，这是你获得的树）。 你会看到很多在你的应用源代码中没有出现的widget，因为它们是被框架中widget的<code>build()</code>函数插入的。例如，<a href=\"https://docs.flutter.io/flutter/material/InkFeature-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>InkFeature</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>是Material widget的一个实现细节 。</p> <p>当按钮从被按下变为被释放时debugDumpApp()被调用，FlatButton对象同时调用<code>setState()</code>，并将自己标记为&quot;dirty&quot;。 这就是为什么如果你看转储，你会看到特定的对象标记为“dirty”。您还可以查看已注册了哪些手势监听器; 在这种情况下，一个单一的GestureDetector被列出，并且监听“tap”手势（“tap”是<code>TapGestureDetector</code>的<code>toStringShort</code>函数输出的）</p> <p>如果您编写自己的widget，则可以通过覆盖<a href=\"https://docs.flutter.io/flutter/widgets/Widget/debugFillProperties.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>debugFillProperties()</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>来添加信息。 将<a href=\"https://docs.flutter.io/flutter/foundation/DiagnosticsProperty-class.html\" target=\"_blank\" rel=\"noopener noreferrer\">DiagnosticsProperty<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>对象作为方法参数，并调用父类方法。 该函数是该<code>toString</code>方法用来填充小部件描述信息的。</p> <h4 id=\"渲染树\"><a href=\"#渲染树\" class=\"header-anchor\">#</a> 渲染树</h4> <p>如果您尝试调试布局问题，那么Widget树可能不够详细。在这种情况下，您可以通过调用<code>debugDumpRenderTree()</code>转储渲染树。 正如<code>debugDumpApp()</code>，除布局或绘制阶段外，您可以随时调用此函数。作为一般规则，从<a href=\"https://docs.flutter.io/flutter/scheduler/SchedulerBinding/addPersistentFrameCallback.html\" target=\"_blank\" rel=\"noopener noreferrer\">frame 回调<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 或事件处理器中调用它是最佳解决方案。</p> <p>要调用<code>debugDumpRenderTree()</code>，您需要添加<code>import'package:flutter/rendering.dart';</code>到您的源文件。</p> <p>上面这个小例子的输出结果如下所示：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>I/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>: RenderView\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:  │ debug mode enabled - android\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:  │ window size: Size<span class=\"token punctuation\">(</span><span class=\"token number\">1080.0</span>, <span class=\"token number\">1794.0</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">(</span>in physical pixels<span class=\"token punctuation\">)</span>\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:  │ device pixel ratio: <span class=\"token number\">2.625</span> <span class=\"token punctuation\">(</span>physical pixels per logical pixel<span class=\"token punctuation\">)</span>\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:  │ configuration: Size<span class=\"token punctuation\">(</span><span class=\"token number\">411.4</span>, <span class=\"token number\">683.4</span><span class=\"token punctuation\">)</span> at <span class=\"token number\">2</span>.625x <span class=\"token punctuation\">(</span>in logical pixels<span class=\"token punctuation\">)</span>\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:  │\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:  └─child: RenderCustomPaint\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:    │ creator: CustomPaint ← Banner ← CheckedModeBanner ←\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:    │   WidgetsApp-<span class=\"token punctuation\">[</span>GlobalObjectKey _MaterialAppState<span class=\"token punctuation\">(</span><span class=\"token number\">1009803148</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">]</span> ←\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:    │   Theme ← AnimatedTheme ← ScrollConfiguration ← MaterialApp ←\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:    │   <span class=\"token punctuation\">[</span>root<span class=\"token punctuation\">]</span>\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:    │ parentData: <span class=\"token operator\">&lt;</span>none<span class=\"token operator\">&gt;</span>\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:    │ constraints: BoxConstraints<span class=\"token punctuation\">(</span>w<span class=\"token operator\">=</span><span class=\"token number\">411.4</span>, <span class=\"token assign-left variable\">h</span><span class=\"token operator\">=</span><span class=\"token number\">683.4</span><span class=\"token punctuation\">)</span>\nI/flutter <span class=\"token punctuation\">(</span> <span class=\"token number\">6559</span><span class=\"token punctuation\">)</span>:    │ size: Size<span class=\"token punctuation\">(</span><span class=\"token number\">411.4</span>, <span class=\"token number\">683.4</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">..</span>. <span class=\"token comment\"># 省略</span>\n</code></pre></div><p>这是根<code>RenderObject</code>对象的<code>toStringDeep</code>函数的输出。</p> <p>当调试布局问题时，关键要看的是<code>size</code>和<code>constraints</code>字段。约束沿着树向下传递，尺寸向上传递。</p> <p>如果您编写自己的渲染对象，则可以通过覆盖<a href=\"https://docs.flutter.io/flutter/rendering/Layer/debugFillProperties.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>debugFillProperties()</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>将信息添加到转储。 将<a href=\"https://docs.flutter.io/flutter/foundation/DiagnosticsProperty-class.html\" target=\"_blank\" rel=\"noopener noreferrer\">DiagnosticsProperty<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>对象作为方法的参数，并调用父类方法。</p> <h4 id=\"layer树\"><a href=\"#layer树\" class=\"header-anchor\">#</a> Layer树</h4> <p>读者可以理解为渲染树是可以分层的，而最终绘制需要将不同的层合成起来，而Layer则是绘制时需要合成的层，如果您尝试调试合成问题，则可以使用<a href=\"https://docs.flutter.io/flutter/rendering/debugDumpLayerTree.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>debugDumpLayerTree()</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>。对于上面的例子，它会输出：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>I/flutter : TransformLayer\nI/flutter :  │ creator: [root]\nI/flutter :  │ offset: Offset(0.0, 0.0)\nI/flutter :  │ transform:\nI/flutter :  │   [0] 3.5,0.0,0.0,0.0\nI/flutter :  │   [1] 0.0,3.5,0.0,0.0\nI/flutter :  │   [2] 0.0,0.0,1.0,0.0\nI/flutter :  │   [3] 0.0,0.0,0.0,1.0\nI/flutter :  │\nI/flutter :  ├─child 1: OffsetLayer\nI/flutter :  │ │ creator: RepaintBoundary ← _FocusScope ← Semantics ← Focus-[GlobalObjectKey MaterialPageRoute(560156430)] ← _ModalScope-[GlobalKey 328026813] ← _OverlayEntry-[GlobalKey 388965355] ← Stack ← Overlay-[GlobalKey 625702218] ← Navigator-[GlobalObjectKey _MaterialAppState(859106034)] ← Title ← ⋯\nI/flutter :  │ │ offset: Offset(0.0, 0.0)\nI/flutter :  │ │\nI/flutter :  │ └─child 1: PictureLayer\nI/flutter :  │\nI/flutter :  └─child 2: PictureLayer\n</code></pre></div><p>这是根<code>Layer</code>的<code>toStringDeep</code>输出的。</p> <p>根部的变换是应用设备像素比的变换; 在这种情况下，每个逻辑像素代表3.5个设备像素。</p> <p><code>RepaintBoundary</code> widget在渲染树的层中创建了一个<code>RenderRepaintBoundary</code>。这用于减少需要重绘的需求量。</p> <h3 id=\"语义\"><a href=\"#语义\" class=\"header-anchor\">#</a> 语义</h3> <p>您还可以调用<a href=\"https://docs.flutter.io/flutter/rendering/debugDumpSemanticsTree.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>debugDumpSemanticsTree()</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>获取语义树（呈现给系统可访问性API的树）的转储。 要使用此功能，必须首先启用辅助功能，例如启用系统辅助工具或<code>SemanticsDebugger</code> （下面讨论）。</p> <p>对于上面的例子，它会输出:</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>I/flutter : SemanticsNode(0; Rect.fromLTRB(0.0, 0.0, 411.4, 683.4))\nI/flutter :  ├SemanticsNode(1; Rect.fromLTRB(0.0, 0.0, 411.4, 683.4))\nI/flutter :  │ └SemanticsNode(2; Rect.fromLTRB(0.0, 0.0, 411.4, 683.4); canBeTapped)\nI/flutter :  └SemanticsNode(3; Rect.fromLTRB(0.0, 0.0, 411.4, 683.4))\nI/flutter :    └SemanticsNode(4; Rect.fromLTRB(0.0, 0.0, 82.0, 36.0); canBeTapped; &quot;Dump App&quot;)\n</code></pre></div><h3 id=\"调度\"><a href=\"#调度\" class=\"header-anchor\">#</a> 调度</h3> <p>要找出相对于帧的开始/结束事件发生的位置，可以切换<a href=\"https://docs.flutter.io/flutter/scheduler/debugPrintBeginFrameBanner.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>debugPrintBeginFrameBanner</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>和<a href=\"https://docs.flutter.io/flutter/scheduler/debugPrintEndFrameBanner.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>debugPrintEndFrameBanner</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>布尔值以将帧的开始和结束打印到控制台。</p> <p>例如:</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>I/flutter : ▄▄▄▄▄▄▄▄ Frame 12         30s 437.086ms ▄▄▄▄▄▄▄▄\nI/flutter : Debug print: Am I performing this work more than once per frame?\nI/flutter : Debug print: Am I performing this work more than once per frame?\nI/flutter : ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀\n</code></pre></div><p><a href=\"https://docs.flutter.io/flutter/scheduler/debugPrintScheduleFrameStacks.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>debugPrintScheduleFrameStacks</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>还可以用来打印导致当前帧被调度的调用堆栈。</p> <h3 id=\"可视化调试\"><a href=\"#可视化调试\" class=\"header-anchor\">#</a> 可视化调试</h3> <p>您也可以通过设置<code>debugPaintSizeEnabled</code>为<code>true</code>以可视方式调试布局问题。 这是来自<code>rendering</code>库的布尔值。它可以在任何时候启用，并在为true时影响绘制。 设置它的最简单方法是在<code>void main()</code>的顶部设置。</p> <p>当它被启用时，所有的盒子都会得到一个明亮的深青色边框，padding（来自widget如Padding）显示为浅蓝色，子widget周围有一个深蓝色框， 对齐方式（来自widget如Center和Align）显示为黄色箭头. 空白（如没有任何子节点的Container）以灰色显示。</p> <p><a href=\"https://docs.flutter.io/flutter/rendering/debugPaintBaselinesEnabled.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>debugPaintBaselinesEnabled</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>做了类似的事情，但对于具有基线的对象，文字基线以绿色显示，表意(ideographic)基线以橙色显示。</p> <p><a href=\"https://docs.flutter.io/flutter/rendering/debugPaintPointersEnabled.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>debugPaintPointersEnabled</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>标志打开一个特殊模式，任何正在点击的对象都会以深青色突出显示。 这可以帮助您确定某个对象是否以某种不正确的方式进行hit测试（Flutter检测点击的位置是否有能响应用户操作的widget）,例如，如果它实际上超出了其父项的范围，首先不会考虑通过hit测试。</p> <p>如果您尝试调试合成图层，例如以确定是否以及在何处添加<code>RepaintBoundary</code> widget，则可以使用<a href=\"https://docs.flutter.io/flutter/rendering/debugPaintLayerBordersEnabled.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>debugPaintLayerBordersEnabled</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 标志， 该标志用橙色或轮廓线标出每个层的边界，或者使用<a href=\"https://docs.flutter.io/flutter/rendering/debugRepaintRainbowEnabled.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>debugRepaintRainbowEnabled</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>标志， 只要他们重绘时，这会使该层被一组旋转色所覆盖。</p> <p>所有这些标志只能在调试模式下工作。通常，Flutter框架中以“<code>debug...</code>” 开头的任何内容都只能在调试模式下工作。</p> <h3 id=\"调试动画\"><a href=\"#调试动画\" class=\"header-anchor\">#</a> 调试动画</h3> <p>调试动画最简单的方法是减慢它们的速度。为此，请将<a href=\"https://docs.flutter.io/flutter/scheduler/timeDilation.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>timeDilation</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>变量（在scheduler库中）设置为大于1.0的数字，例如50.0。 最好在应用程序启动时只设置一次。如果您在运行中更改它，尤其是在动画运行时将其值改小，则在观察时可能会出现倒退，这可能会导致断言命中，并且这通常会干扰我们的开发工作。</p> <h3 id=\"调试性能问题\"><a href=\"#调试性能问题\" class=\"header-anchor\">#</a> 调试性能问题</h3> <p>要了解您的应用程序导致重新布局或重新绘制的原因，您可以分别设置<a href=\"https://docs.flutter.io/flutter/rendering/debugPrintMarkNeedsLayoutStacks.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>debugPrintMarkNeedsLayoutStacks</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>和 <a href=\"https://docs.flutter.io/flutter/rendering/debugPrintMarkNeedsPaintStacks.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>debugPrintMarkNeedsPaintStacks</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>标志。 每当渲染盒被要求重新布局和重新绘制时，这些都会将堆栈跟踪记录到控制台。如果这种方法对您有用，您可以使用<code>services</code>库中的<code>debugPrintStack()</code>方法按需打印堆栈痕迹。</p> <h3 id=\"统计应用启动时间\"><a href=\"#统计应用启动时间\" class=\"header-anchor\">#</a> 统计应用启动时间</h3> <p>要收集有关Flutter应用程序启动所需时间的详细信息，可以在运行<code>flutter run</code>时使用<code>trace-startup</code>和<code>profile</code>选项。</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>$ flutter run --trace-startup --profile\n</code></pre></div><p>跟踪输出保存为<code>start_up_info.json</code>，在Flutter工程目录在build目录下。输出列出了从应用程序启动到这些跟踪事件（以微秒捕获）所用的时间：</p> <ul><li>进入Flutter引擎时.</li> <li>展示应用第一帧时.</li> <li>初始化Flutter框架时.</li> <li>完成Flutter框架初始化时.</li></ul> <p>如 :</p> <div class=\"language-json extra-class\"><pre class=\"language-json\"><code><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;engineEnterTimestampMicros&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">96025565262</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;timeToFirstFrameMicros&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">2171978</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;timeToFrameworkInitMicros&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">514585</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;timeAfterFrameworkInitMicros&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">1657393</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h3 id=\"跟踪dart代码性能\"><a href=\"#跟踪dart代码性能\" class=\"header-anchor\">#</a> 跟踪Dart代码性能</h3> <p>要执行自定义性能跟踪和测量Dart任意代码段的wall/CPU时间（类似于在Android上使用<a href=\"https://developer.android.com/studio/profile/systrace.html\" target=\"_blank\" rel=\"noopener noreferrer\">systrace<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>）。 使用<code>dart:developer</code>的<a href=\"https://api.dartlang.org/stable/dart-developer/Timeline-class.html\" target=\"_blank\" rel=\"noopener noreferrer\">Timeline<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>工具来包含你想测试的代码块，例如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Timeline<span class=\"token punctuation\">.</span><span class=\"token function\">startSync</span><span class=\"token punctuation\">(</span><span class=\"token string\">'interesting function'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">// iWonderHowLongThisTakes();</span>\nTimeline<span class=\"token punctuation\">.</span><span class=\"token function\">finishSync</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>然后打开你应用程序的Observatory timeline页面，在“Recorded Streams”中选择‘Dart’复选框，并执行你想测量的功能。</p> <p>刷新页面将在Chrome的<a href=\"https://www.chromium.org/developers/how-tos/trace-event-profiling-tool\" target=\"_blank\" rel=\"noopener noreferrer\">跟踪工具<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>中显示应用按时间顺序排列的timeline记录。</p> <p>请确保运行<code>flutter run</code>时带有<code>--profile</code>标志，以确保运行时性能特征与您的最终产品差异最小。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/206.2dbac1ab.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter2/flutter_assets_mgr.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>2.4 资源管理 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/33.8b948e2d.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter2/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_2-4-资源管理\"><a href=\"#_2-4-资源管理\" class=\"header-anchor\">#</a> 2.4 资源管理</h1> <p>Flutter APP安装包中会包含代码和 assets（资源）两部分。Assets是会打包到程序安装包中的，可在运行时访问。常见类型的assets包括静态数据（例如JSON文件）、配置文件、图标和图片（JPEG，WebP，GIF，动画WebP / GIF，PNG，BMP和WBMP）等。</p> <h2 id=\"指定-assets\"><a href=\"#指定-assets\" class=\"header-anchor\">#</a> 指定 assets</h2> <p>和包管理一样，Flutter也使用<a href=\"https://www.dartlang.org/tools/pub/pubspec\" target=\"_blank\" rel=\"noopener noreferrer\"><code>pubspec.yaml</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>文件来管理应用程序所需的资源，举个例子:</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code><span class=\"token key atrule\">flutter</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">assets</span><span class=\"token punctuation\">:</span>\n    <span class=\"token punctuation\">-</span> assets/my_icon.png\n    <span class=\"token punctuation\">-</span> assets/background.png\n</code></pre></div><p><code>assets</code>指定应包含在应用程序中的文件， 每个asset都通过相对于<code>pubspec.yaml</code>文件所在的文件系统路径来标识自身的路径。asset的声明顺序是无关紧要的，asset的实际目录可以是任意文件夹（在本示例中是assets文件夹）。</p> <p>在构建期间，Flutter将asset放置到称为 <em>asset bundle</em> 的特殊存档中，应用程序可以在运行时读取它们（但不能修改）。</p> <h2 id=\"asset-变体-variant\"><a href=\"#asset-变体-variant\" class=\"header-anchor\">#</a> Asset 变体（variant）</h2> <p>构建过程支持“asset变体”的概念：不同版本的asset可能会显示在不同的上下文中。 在<code>pubspec.yaml</code>的assets部分中指定asset路径时，构建过程中，会在相邻子目录中查找具有相同名称的任何文件。这些文件随后会与指定的asset一起被包含在asset bundle中。</p> <p>例如，如果应用程序目录中有以下文件:</p> <ul><li>…/pubspec.yaml</li> <li>…/graphics/my_icon.png</li> <li>…/graphics/background.png</li> <li>…/graphics/dark/background.png</li> <li>…etc.</li></ul> <p>然后<code>pubspec.yaml</code>文件中只需包含:</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>flutter:\n  assets:\n    - graphics/background.png\n</code></pre></div><p>那么这两个<code>graphics/background.png</code>和<code>graphics/dark/background.png</code> 都将包含在您的asset bundle中。前者被认为是_main asset_ （主资源），后者被认为是一种变体（variant）。</p> <p>在选择匹配当前设备分辨率的图片时，Flutter会使用到asset变体（见下文），将来，Flutter可能会将这种机制扩展到本地化、阅读提示等方面。</p> <h2 id=\"加载-assets\"><a href=\"#加载-assets\" class=\"header-anchor\">#</a> 加载 assets</h2> <p>您的应用可以通过<a href=\"https://docs.flutter.io/flutter/services/AssetBundle-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>AssetBundle</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>对象访问其asset 。有两种主要方法允许从Asset bundle中加载字符串或图片（二进制）文件。</p> <h3 id=\"加载文本assets\"><a href=\"#加载文本assets\" class=\"header-anchor\">#</a> 加载文本assets</h3> <ul><li>通过<a href=\"https://docs.flutter.io/flutter/services/rootBundle.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>rootBundle</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 对象加载：每个Flutter应用程序都有一个<a href=\"https://docs.flutter.io/flutter/services/rootBundle.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>rootBundle</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>对象， 通过它可以轻松访问主资源包，直接使用<code>package:flutter/services.dart</code>中全局静态的<code>rootBundle</code>对象来加载asset即可。</li> <li>通过 <a href=\"https://docs.flutter.io/flutter/widgets/DefaultAssetBundle-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>DefaultAssetBundle</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 加载：建议使用 <a href=\"https://docs.flutter.io/flutter/widgets/DefaultAssetBundle-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>DefaultAssetBundle</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 来获取当前BuildContext的AssetBundle。 这种方法不是使用应用程序构建的默认asset bundle，而是使父级widget在运行时动态替换的不同的AssetBundle，这对于本地化或测试场景很有用。</li></ul> <p>通常，可以使用<code>DefaultAssetBundle.of()</code>在应用运行时来间接加载asset（例如JSON文件），而在widget上下文之外，或其它<code>AssetBundle</code>句柄不可用时，可以使用<code>rootBundle</code>直接加载这些asset，例如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'dart:async'</span> <span class=\"token keyword\">show</span> Future<span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/services.dart'</span> <span class=\"token keyword\">show</span> rootBundle<span class=\"token punctuation\">;</span>\n\nFuture<span class=\"token operator\">&lt;</span>String<span class=\"token operator\">&gt;</span> <span class=\"token function\">loadAsset</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token keyword\">await</span> rootBundle<span class=\"token punctuation\">.</span><span class=\"token function\">loadString</span><span class=\"token punctuation\">(</span><span class=\"token string\">'assets/config.json'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h3 id=\"加载图片\"><a href=\"#加载图片\" class=\"header-anchor\">#</a> 加载图片</h3> <p>类似于原生开发，Flutter也可以为当前设备加载适合其分辨率的图像。</p> <h4 id=\"声明分辨率相关的图片-assets\"><a href=\"#声明分辨率相关的图片-assets\" class=\"header-anchor\">#</a> 声明分辨率相关的图片 assets</h4> <p><a href=\"https://docs.flutter.io/flutter/painting/AssetImage-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>AssetImage</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 可以将asset的请求逻辑映射到最接近当前设备像素比例（dpi）的asset。为了使这种映射起作用，必须根据特定的目录结构来保存asset：</p> <ul><li>…/image.png</li> <li>…/<strong>M</strong>x/image.png</li> <li>…/<strong>N</strong>x/image.png</li> <li>…etc.</li></ul> <p>其中M和N是数字标识符，对应于其中包含的图像的分辨率，也就是说，它们指定不同设备像素比例的图片。</p> <p>主资源默认对应于1.0倍的分辨率图片。看一个例子：</p> <ul><li>…/my_icon.png</li> <li>…/2.0x/my_icon.png</li> <li>…/3.0x/my_icon.png</li></ul> <p>在设备像素比率为1.8的设备上，<code>.../2.0x/my_icon.png</code> 将被选择。对于2.7的设备像素比率，<code>.../3.0x/my_icon.png</code>将被选择。</p> <p>如果未在<code>Image</code> widget上指定渲染图像的宽度和高度，那么<code>Image</code> widget将占用与主资源相同的屏幕空间大小。 也就是说，如果<code>.../my_icon.png</code>是72px乘72px，那么<code>.../3.0x/my_icon.png</code>应该是216px乘216px; 但如果未指定宽度和高度，它们都将渲染为72像素×72像素（以逻辑像素为单位）。</p> <p><code>pubspec.yaml</code>中asset部分中的每一项都应与实际文件相对应，但主资源项除外。当主资源缺少某个资源时，会按分辨率从低到高的顺序去选择 ，也就是说1x中没有的话会在2x中找，2x中还没有的话就在3x中找。</p> <h4 id=\"加载图片-2\"><a href=\"#加载图片-2\" class=\"header-anchor\">#</a> 加载图片</h4> <p>要加载图片，可以使用 <a href=\"https://docs.flutter.io/flutter/painting/AssetImage-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>AssetImage</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>类。例如，我们可以从上面的asset声明中加载背景图片：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">DecoratedBox</span><span class=\"token punctuation\">(</span>\n    decoration<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">BoxDecoration</span><span class=\"token punctuation\">(</span>\n      image<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">DecorationImage</span><span class=\"token punctuation\">(</span>\n        image<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AssetImage</span><span class=\"token punctuation\">(</span><span class=\"token string\">'graphics/background.png'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>注意，<code>AssetImage</code> 并非是一个widget， 它实际上是一个<code>ImageProvider</code>，有些时候你可能期望直接得到一个显示图片的widget，那么你可以使用<code>Image.asset()</code>方法，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> Image<span class=\"token punctuation\">.</span><span class=\"token function\">asset</span><span class=\"token punctuation\">(</span><span class=\"token string\">'graphics/background.png'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>使用默认的 asset bundle 加载资源时，内部会自动处理分辨率等，这些处理对开发者来说是无感知的。 (如果使用一些更低级别的类，如 <a href=\"https://docs.flutter.io/flutter/painting/ImageStream-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>ImageStream</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>或 <a href=\"https://docs.flutter.io/flutter/painting/ImageCache-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>ImageCache</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 时你会注意到有与缩放相关的参数)</p> <h4 id=\"依赖包中的资源图片\"><a href=\"#依赖包中的资源图片\" class=\"header-anchor\">#</a> 依赖包中的资源图片</h4> <p>要加载依赖包中的图像，必须给<code>AssetImage</code>提供<code>package</code>参数。</p> <p>例如，假设您的应用程序依赖于一个名为“my_icons”的包，它具有如下目录结构：</p> <ul><li>…/pubspec.yaml</li> <li>…/icons/heart.png</li> <li>…/icons/1.5x/heart.png</li> <li>…/icons/2.0x/heart.png</li> <li>…etc.</li></ul> <p>然后加载图像，使用:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AssetImage</span><span class=\"token punctuation\">(</span><span class=\"token string\">'icons/heart.png'</span><span class=\"token punctuation\">,</span> package<span class=\"token punctuation\">:</span> <span class=\"token string\">'my_icons'</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>或</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">new</span> <span class=\"token class-name\">Image<span class=\"token punctuation\">.</span>asset</span><span class=\"token punctuation\">(</span><span class=\"token string\">'icons/heart.png'</span><span class=\"token punctuation\">,</span> package<span class=\"token punctuation\">:</span> <span class=\"token string\">'my_icons'</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p><strong>注意：包在使用本身的资源时也应该加上<code>package</code>参数来获取</strong>。</p> <h5 id=\"打包包中的-assets\"><a href=\"#打包包中的-assets\" class=\"header-anchor\">#</a> 打包包中的 assets</h5> <p>如果在<code>pubspec.yaml</code>文件中声明了期望的资源，它将会打包到相应的package中。特别是，包本身使用的资源必须在<code>pubspec.yaml</code>中指定。</p> <p>包也可以选择在其<code>lib/</code>文件夹中包含未在其<code>pubspec.yaml</code>文件中声明的资源。在这种情况下，对于要打包的图片，应用程序必须在<code>pubspec.yaml</code>中指定包含哪些图像。 例如，一个名为“fancy_backgrounds”的包，可能包含以下文件：</p> <ul><li>…/lib/backgrounds/background1.png</li> <li>…/lib/backgrounds/background2.png</li> <li>…/lib/backgrounds/background3.png</li></ul> <p>要包含第一张图像，必须在<code>pubspec.yaml</code>的assets部分中声明它：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>flutter:\n  assets:\n    - packages/fancy_backgrounds/backgrounds/background1.png\n</code></pre></div><p><code>lib/</code>是隐含的，所以它不应该包含在资产路径中。</p> <h3 id=\"特定平台-assets\"><a href=\"#特定平台-assets\" class=\"header-anchor\">#</a> 特定平台 assets</h3> <p>上面的资源都是flutter应用中的，这些资源只有在Flutter框架运行之后才能使用，如果要给我们的应用设置APP图标或者添加启动图，那我们必须使用特定平台的assets。</p> <h4 id=\"设置app图标\"><a href=\"#设置app图标\" class=\"header-anchor\">#</a> 设置APP图标</h4> <p>更新Flutter应用程序启动图标的方式与在本机Android或iOS应用程序中更新启动图标的方式相同。</p> <ul><li><p>Android</p> <p>在Flutter项目的根目录中，导航到<code>.../android/app/src/main/res</code>目录，里面包含了各种资源文件夹（如<code>mipmap-hdpi</code>已包含占位符图像“ic_launcher.png”，见图2-8）。 只需按照<a href=\"https://developer.android.com/guide/practices/ui_guidelines/icon_design_launcher.html#size\" target=\"_blank\" rel=\"noopener noreferrer\">Android开发人员指南<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>中的说明， 将其替换为所需的资源，并遵守每种屏幕密度（dpi）的建议图标大小标准。</p> <p><img src=\"/assets/img/2-8.89d0af83.png\" alt=\"图2-8\"></p> <blockquote><p><strong>注意:</strong> 如果您重命名.png文件，则还必须在您<code>AndroidManifest.xml</code>的<code>&lt;application&gt;</code>标签的<code>android:icon</code>属性中更新名称。</p></blockquote></li> <li><p>iOS</p> <p>在Flutter项目的根目录中，导航到<code>.../ios/Runner</code>。该目录中<code>Assets.xcassets/AppIcon.appiconset</code>已经包含占位符图片（见图2-9）， 只需将它们替换为适当大小的图片，保留原始文件名称。</p> <p><img src=\"/assets/img/2-9.0a86cf44.png\" alt=\"图2-9\"></p></li></ul> <h4 id=\"更新启动页\"><a href=\"#更新启动页\" class=\"header-anchor\">#</a> 更新启动页</h4> <p><img src=\"/assets/img/2-10.e56b6689.png\" alt=\"图2-10\"></p> <p>在Flutter框架加载时，Flutter会使用本地平台机制绘制启动页。此启动页将持续到Flutter渲染应用程序的第一帧时。</p> <blockquote><p><strong>注意:</strong> 这意味着如果您不在应用程序的<code>main()</code>方法中调用<a href=\"https://docs.flutter.io/flutter/widgets/runApp.html\" target=\"_blank\" rel=\"noopener noreferrer\">runApp<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 函数 （或者更具体地说，如果您不调用<a href=\"https://docs.flutter.io/flutter/dart-ui/Window/render.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>window.render</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>去响应<a href=\"https://docs.flutter.io/flutter/dart-ui/Window/onDrawFrame.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>window.onDrawFrame</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>）的话， 启动屏幕将永远持续显示。</p></blockquote> <h5 id=\"android\"><a href=\"#android\" class=\"header-anchor\">#</a> Android</h5> <p>要将启动屏幕（splash screen）添加到您的Flutter应用程序， 请导航至<code>.../android/app/src/main</code>。在<code>res/drawable/launch_background.xml</code>，通过自定义drawable来实现自定义启动界面（你也可以直接换一张图片）。</p> <h5 id=\"ios\"><a href=\"#ios\" class=\"header-anchor\">#</a> iOS</h5> <p>要将图片添加到启动屏幕（splash screen）的中心，请导航至<code>.../ios/Runner</code>。在<code>Assets.xcassets/LaunchImage.imageset</code>， 拖入图片，并命名为<code>LaunchImage.png</code>、<code>LaunchImage@2x.png</code>、<code>LaunchImage@3x.png</code>。 如果你使用不同的文件名，那您还必须更新同一目录中的<code>Contents.json</code>文件，图片的具体尺寸可以查看苹果官方的标准。</p> <p>您也可以通过打开Xcode完全自定义storyboard。在Project Navigator中导航到<code>Runner/Runner</code>然后通过打开<code>Assets.xcassets</code>拖入图片，或者通过在LaunchScreen.storyboard中使用Interface Builder进行自定义，如图2-11所示。</p> <p><img src=\"/assets/img/2-11.9f54d13a.png\" alt=\"图2-11\"></p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/33.8b948e2d.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter2/flutter_package_mgr.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>2.3 包管理 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/51.4c0d2270.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter2/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_2-3-包管理\"><a href=\"#_2-3-包管理\" class=\"header-anchor\">#</a> 2.3 包管理</h1> <p>在软件开发中，很多时候有一些公共的库或SDK可能会被很多项目用到，因此，将这些代码单独抽到一个独立模块，然后哪个项目需要使用时再直接集成这个模块，便可大大提高开发效率。很多编程语言或开发工具都支持这种“模块共享”机制，如Java语言中这种独立模块会被打成一个jar包，Android中的aar包，Web开发中的npm包等。为了方便表述，我们将这种可共享的独立模块统一称为“包”（ Package）。</p> <p>一个APP在实际开发中往往会依赖很多包，而这些包通常都有交叉依赖关系、版本依赖等，如果由开发者手动来管理应用中的依赖包将会非常麻烦。因此，各种开发生态或编程语言官方通常都会提供一些包管理工具，比如在Android提供了Gradle来管理依赖，iOS用Cocoapods或Carthage来管理依赖，Node中通过npm等。而在Flutter开发中也有自己的包管理工具。本节我们主要介绍一下flutter如何使用配置文件<code>pubspec.yaml</code>（位于项目根目录）来管理第三方依赖包。</p> <p>YAML是一种直观、可读性高并且容易被人类阅读的文件格式，它和xml或Json相比，它语法简单并非常容易解析，所以YAML常用于配置文件，Flutter也是用yaml文件作为其配置文件。Flutter项目默认的配置文件是<code>pubspec.yaml</code>，我们看一个简单的示例：</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code><span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> flutter_in_action\n<span class=\"token key atrule\">description</span><span class=\"token punctuation\">:</span> First Flutter application.\n\n<span class=\"token key atrule\">version</span><span class=\"token punctuation\">:</span> 1.0.0+1\n\n<span class=\"token key atrule\">dependencies</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">flutter</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">sdk</span><span class=\"token punctuation\">:</span> flutter\n  <span class=\"token key atrule\">cupertino_icons</span><span class=\"token punctuation\">:</span> ^0.1.2\n\n<span class=\"token key atrule\">dev_dependencies</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">flutter_test</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">sdk</span><span class=\"token punctuation\">:</span> flutter\n    \n<span class=\"token key atrule\">flutter</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">uses-material-design</span><span class=\"token punctuation\">:</span> <span class=\"token boolean important\">true</span>\n</code></pre></div><p>下面，我们逐一解释一下各个字段的意义：</p> <ul><li><code>name</code>：应用或包名称。</li> <li><code>description</code>: 应用或包的描述、简介。</li> <li><code>version</code>：应用或包的版本号。</li> <li><code>dependencies</code>：应用或包依赖的其它包或插件。</li> <li><code>dev_dependencies</code>：开发环境依赖的工具包（而不是flutter应用本身依赖的包）。</li> <li><code>flutter</code>：flutter相关的配置选项。</li></ul> <p>如果我们的Flutter应用本身依赖某个包，我们需要将所依赖的包添加到<code>dependencies</code> 下，接下来我们通过一个例子来演示一下如何添加、下载并使用第三方包。</p> <h2 id=\"pub仓库\"><a href=\"#pub仓库\" class=\"header-anchor\">#</a> Pub仓库</h2> <p>Pub（https://pub.dev/ ）是Google官方的Dart Packages仓库，类似于node中的npm仓库，android中的jcenter。我们可以在Pub上面查找我们需要的包和插件，也可以向Pub发布我们的包和插件。我们将在后面的章节中介绍如何向Pub发布我们的包和插件。</p> <h2 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h2> <p>接下来，我们实现一个显示随机字符串的widget。有一个名为“english_words”的开源软件包，其中包含数千个常用的英文单词以及一些实用功能。我们首先在pub上找到english_words这个包（如图2-5所示），确定其最新的版本号和是否支持Flutter。</p> <p><img src=\"/assets/img/2-5.7456ef8f.png\" alt=\"图2-5\"></p> <p>我们看到“english_words”包最新的版本是3.1.3，并且支持flutter，接下来：</p> <ol><li><p>将“english_words”（3.1.3版本）添加到依赖项列表，如下：</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code><span class=\"token key atrule\">dependencies</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">flutter</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">sdk</span><span class=\"token punctuation\">:</span> flutter\n\n  <span class=\"token key atrule\">cupertino_icons</span><span class=\"token punctuation\">:</span> ^0.1.0\n  <span class=\"token comment\"># 新添加的依赖</span>\n  <span class=\"token key atrule\">english_words</span><span class=\"token punctuation\">:</span> ^3.1.3\n</code></pre></div></li> <li><p>下载包。在Android Studio的编辑器视图中查看pubspec.yaml时（图2-6），单击右上角的 <strong>Packages get</strong> 。</p> <p><img src=\"/assets/img/2-6.fb55e91b.png\" alt=\"图2-6\"></p> <p>这会将依赖包安装到您的项目。我们可以在控制台中看到以下内容：</p> <div class=\"language-shell extra-class\"><pre class=\"language-shell\"><code>flutter packages get\nRunning <span class=\"token string\">&quot;flutter packages get&quot;</span> <span class=\"token keyword\">in</span> flutter_in_action<span class=\"token punctuation\">..</span>.\nProcess finished with <span class=\"token builtin class-name\">exit</span> code <span class=\"token number\">0</span>\n</code></pre></div><p>我们也可以在控制台，定位到当前工程目录，然后手动运行<code>flutter packages get</code> 命令来下载依赖包。另外，需要注意<code>dependencies</code>和<code>dev_dependencies</code>的区别，前者的依赖包将作为APP的源码的一部分参与编译，生成最终的安装包。而后者的依赖包只是作为开发阶段的一些工具包，主要是用于帮助我们提高开发、测试效率，比如flutter的自动化测试包等。</p></li> <li><p>引入<code>english_words</code>包。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:english_words/english_words.dart'</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>在输入时，Android Studio会自动提供有关库导入的建议选项。导入后该行代码将会显示为灰色，表示导入的库尚未使用。</p></li> <li><p>使用<code>english_words</code>包来生成随机字符串。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">RandomWordsWidget</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n   <span class=\"token comment\">// 生成随机字符串</span>\n    <span class=\"token keyword\">final</span> wordPair <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">WordPair<span class=\"token punctuation\">.</span>random</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n      padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">8.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span>wordPair<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们将<code>RandomWordsWidget</code> 添加到 <code>_MyHomePageState.build</code> 的<code>Column</code>的子widget中。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n  mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n    <span class=\"token function\">RandomWordsWidget</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div></li> <li><p>如果应用程序正在运行，请使用热重载按钮（⚡️图标） 更新正在运行的应用程序。每次单击热重载或保存项目时，都会在正在运行的应用程序中随机选择不同的单词对。 这是因为单词对是在 <code>build</code> 方法内部生成的。每次热更新时，<code>build</code>方法都会被执行，运行效果如图2-7所示。</p> <p><img src=\"/assets/img/2-7.90b5e799.png\" alt=\"图2-7\"></p></li></ol> <h2 id=\"其它依赖方式\"><a href=\"#其它依赖方式\" class=\"header-anchor\">#</a> 其它依赖方式</h2> <p>上文所述的依赖方式是依赖Pub仓库的。但我们还可以依赖本地包和git仓库。</p> <ul><li><p>依赖本地包</p> <p>如果我们正在本地开发一个包，包名为pkg1，我们可以通过下面方式依赖：</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code><span class=\"token key atrule\">dependencies</span><span class=\"token punctuation\">:</span>\n\t<span class=\"token key atrule\">pkg1</span><span class=\"token punctuation\">:</span>\n        <span class=\"token key atrule\">path</span><span class=\"token punctuation\">:</span> ../../code/pkg1\n</code></pre></div><p>路径可以是相对的，也可以是绝对的。</p></li> <li><p>依赖Git：你也可以依赖存储在Git仓库中的包。如果软件包位于仓库的根目录中，请使用以下语法</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code><span class=\"token key atrule\">dependencies</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">pkg1</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">git</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">url</span><span class=\"token punctuation\">:</span> git<span class=\"token punctuation\">:</span>//github.com/xxx/pkg1.git\n</code></pre></div><p>上面假定包位于Git存储库的根目录中。如果不是这种情况，可以使用path参数指定相对位置，例如：</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code><span class=\"token key atrule\">dependencies</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">package1</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">git</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">url</span><span class=\"token punctuation\">:</span> git<span class=\"token punctuation\">:</span>//github.com/flutter/packages.git\n      <span class=\"token key atrule\">path</span><span class=\"token punctuation\">:</span> packages/package1        \n</code></pre></div></li></ul> <p>上面介绍的这些依赖方式是Flutter开发中常用的，但还有一些其它依赖方式，完整的内容读者可以自行查看：https://www.dartlang.org/tools/pub/dependencies 。</p> <h2 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h2> <p>本节介绍了Flutter中包管理、引用、下载的整体流程，我们将在后面的章节中介绍如何开发并发布我们自己的包。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/51.4c0d2270.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter2/flutter_router.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>2.2 路由管理 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/52.61d5b4f1.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter2/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_2-2-路由管理\"><a href=\"#_2-2-路由管理\" class=\"header-anchor\">#</a> 2.2 路由管理</h1> <p>路由(Route)在移动开发中通常指页面（Page），这跟web开发中单页应用的Route概念意义是相同的，Route在Android中通常指一个Activity，在iOS中指一个ViewController。所谓路由管理，就是管理页面之间如何跳转，通常也可被称为导航管理。Flutter中的路由管理和原生开发类似，无论是Android还是iOS，导航管理都会维护一个路由栈，路由入栈(push)操作对应打开一个新页面，路由出栈(pop)操作对应页面关闭操作，而路由管理主要是指如何来管理路由栈。</p> <h2 id=\"_2-2-1-一个简单示例\"><a href=\"#_2-2-1-一个简单示例\" class=\"header-anchor\">#</a> 2.2.1 一个简单示例</h2> <p>我们在上一节“计数器”示例的基础上，做如下修改：</p> <ol><li><p>创建一个新路由，命名“NewRoute”</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">NewRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n      appBar<span class=\"token punctuation\">:</span> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>\n        title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;New route&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      body<span class=\"token punctuation\">:</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;This is new route&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>新路由继承自<code>StatelessWidget</code>，界面很简单，在页面中间显示一句&quot;This is new route&quot;。</p></li> <li><p>在<code>_MyHomePageState.build</code>方法中的<code>Column</code>的子widget中添加一个按钮（<code>FlatButton</code>） :</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n      mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n      children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n      <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n         child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;open new route&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n         textColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span>\n         onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token comment\">//导航到新路由   </span>\n          Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span> context<span class=\"token punctuation\">,</span>\n           <span class=\"token function\">MaterialPageRoute</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token keyword\">return</span> <span class=\"token function\">NewRoute</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n           <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n         <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n       <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n <span class=\"token punctuation\">)</span>\n</code></pre></div><p>我们添加了一个打开新路由的按钮，并将按钮文字颜色设置为蓝色，点击该按钮后就会打开新的路由页面，效果如图2-2和2-3所示。</p> <p><img src=\"/assets/img/2-2.de6feb35.png\" alt=\"图2-2\"> <img src=\"/assets/img/2-3.c20b3236.png\" alt=\"图2-3\"></p></li></ol> <h2 id=\"_2-2-2-materialpageroute\"><a href=\"#_2-2-2-materialpageroute\" class=\"header-anchor\">#</a> 2.2.2 MaterialPageRoute</h2> <p><code>MaterialPageRoute</code>继承自<code>PageRoute</code>类，<code>PageRoute</code>类是一个抽象类，表示占有整个屏幕空间的一个模态路由页面，它还定义了路由构建及切换时过渡动画的相关接口及属性。<code>MaterialPageRoute</code> 是Material组件库提供的组件，它可以针对不同平台，实现与平台页面切换动画风格一致的路由切换动画：</p> <ul><li>对于Android，当打开新页面时，新的页面会从屏幕底部滑动到屏幕顶部；当关闭页面时，当前页面会从屏幕顶部滑动到屏幕底部后消失，同时上一个页面会显示到屏幕上。</li> <li>对于iOS，当打开页面时，新的页面会从屏幕右侧边缘一致滑动到屏幕左边，直到新页面全部显示到屏幕上，而上一个页面则会从当前屏幕滑动到屏幕左侧而消失；当关闭页面时，正好相反，当前页面会从屏幕右侧滑出，同时上一个页面会从屏幕左侧滑入。</li></ul> <p>下面我们介绍一下<code>MaterialPageRoute</code> 构造函数的各个参数的意义：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>  <span class=\"token function\">MaterialPageRoute</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    WidgetBuilder builder<span class=\"token punctuation\">,</span>\n    RouteSettings settings<span class=\"token punctuation\">,</span>\n    bool maintainState <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n    bool fullscreenDialog <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><ul><li><code>builder</code> 是一个WidgetBuilder类型的回调函数，它的作用是构建路由页面的具体内容，返回值是一个widget。我们通常要实现此回调，返回新路由的实例。</li> <li><code>settings</code> 包含路由的配置信息，如路由名称、是否初始路由（首页）。</li> <li><code>maintainState</code>：默认情况下，当入栈一个新路由时，原来的路由仍然会被保存在内存中，如果想在路由没用的时候释放其所占用的所有资源，可以设置<code>maintainState</code>为false。</li> <li><code>fullscreenDialog</code>表示新的路由页面是否是一个全屏的模态对话框，在iOS中，如果<code>fullscreenDialog</code>为<code>true</code>，新页面将会从屏幕底部滑入（而不是水平方向）。</li></ul> <blockquote><p>如果想自定义路由切换动画，可以自己继承PageRoute来实现，我们将在后面介绍动画时，实现一个自定义的路由组件。</p></blockquote> <h2 id=\"_2-2-3-navigator\"><a href=\"#_2-2-3-navigator\" class=\"header-anchor\">#</a> 2.2.3 Navigator</h2> <p><code>Navigator</code>是一个路由管理的组件，它提供了打开和退出路由页方法。<code>Navigator</code>通过一个栈来管理活动路由集合。通常当前屏幕显示的页面就是栈顶的路由。<code>Navigator</code>提供了一系列方法来管理路由栈，在此我们只介绍其最常用的两个方法：</p> <h3 id=\"future-push-buildcontext-context-route-route\"><a href=\"#future-push-buildcontext-context-route-route\" class=\"header-anchor\">#</a> Future  push(BuildContext context, Route route)</h3> <p>将给定的路由入栈（即打开新的页面），返回值是一个<code>Future</code>对象，用以接收新路由出栈（即关闭）时的返回数据。</p> <h3 id=\"bool-pop-buildcontext-context-result\"><a href=\"#bool-pop-buildcontext-context-result\" class=\"header-anchor\">#</a> bool  pop(BuildContext context, [ result ])</h3> <p>将栈顶路由出栈，<code>result</code>为页面关闭时返回给上一个页面的数据。</p> <p><code>Navigator</code> 还有很多其它方法，如<code>Navigator.replace</code>、<code>Navigator.popUntil</code>等，详情请参考API文档或SDK源码注释，在此不再赘述。下面我们还需要介绍一下路由相关的另一个概念“命名路由”。</p> <h3 id=\"实例方法\"><a href=\"#实例方法\" class=\"header-anchor\">#</a> 实例方法</h3> <p>Navigator类中第一个参数为context的<strong>静态方法</strong>都对应一个Navigator的<strong>实例方法</strong>， 比如<code>Navigator.push(BuildContext context, Route route)</code>等价于<code>Navigator.of(context).push(Route route)</code> ，下面命名路由相关的方法也是一样的。</p> <h2 id=\"_2-2-4-路由传值\"><a href=\"#_2-2-4-路由传值\" class=\"header-anchor\">#</a> 2.2.4 路由传值</h2> <p>很多时候，在路由跳转时我们需要带一些参数，比如打开商品详情页时，我们需要带一个商品id，这样商品详情页才知道展示哪个商品信息；又比如我们在填写订单时需要选择收货地址，打开地址选择页并选择地址后，可以将用户选择的地址返回到订单页等等。下面我们通过一个简单的示例来演示新旧路由如何传参。</p> <h3 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h3> <p>我们创建一个<code>TipRoute</code>路由，它接受一个提示文本参数，负责将传入它的文本显示在页面上，另外<code>TipRoute</code>中我们添加一个“返回”按钮，点击后在返回上一个路由的同时会带上一个返回参数，下面我们看一下实现代码。</p> <p><code>TipRoute</code>实现代码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">TipRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">TipRoute</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n    <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>text<span class=\"token punctuation\">,</span>  <span class=\"token comment\">// 接收一个text参数</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> String text<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n      appBar<span class=\"token punctuation\">:</span> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>\n        title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;提示&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      body<span class=\"token punctuation\">:</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n        padding<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">18</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n            children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n              <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>text<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n                onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;我是返回值&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;返回&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>下面是打开新路由<code>TipRoute</code>的代码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">RouterTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n        onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token comment\">// 打开`TipRoute`，并等待返回结果</span>\n          <span class=\"token keyword\">var</span> result <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span>\n            context<span class=\"token punctuation\">,</span>\n            <span class=\"token function\">MaterialPageRoute</span><span class=\"token punctuation\">(</span>\n              builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token keyword\">return</span> <span class=\"token function\">TipRoute</span><span class=\"token punctuation\">(</span>\n                  <span class=\"token comment\">// 路由参数</span>\n                  text<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;我是提示xxxx&quot;</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token comment\">//输出`TipRoute`路由返回结果</span>\n          <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;路由返回值: $result&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;打开提示页&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行上面代码，点击<code>RouterTestRoute</code>页的“打开提示页”按钮，会打开<code>TipRoute</code>页，运行效果如图2-4所示下：</p> <p><img src=\"/assets/img/2-4.1abb1cab.png\" alt=\"图2-4\"></p> <p>需要说明：</p> <ol><li><p>提示文案“我是提示xxxx”是通过<code>TipRoute</code>的<code>text</code>参数传递给新路由页的。我们可以通过等待<code>Navigator.push(…)</code>返回的<code>Future</code>来获取新路由的返回数据。</p></li> <li><p>在<code>TipRoute</code>页中有两种方式可以返回到上一页；第一种方式时直接点击导航栏返回箭头，第二种方式是点击页面中的“返回”按钮。这两种返回方式的区别是前者不会返回数据给上一个路由，而后者会。下面是分别点击页面中的返回按钮和导航栏返回箭头后，<code>RouterTestRoute</code>页中<code>print</code>方法在控制台输出的内容：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>I/flutter (27896): 路由返回值: 我是返回值\nI/flutter (27896): 路由返回值: null\n</code></pre></div></li></ol> <p>上面介绍的是非命名路由的传值方式，命名路由的传值方式会有所不同，我们会在下面介绍命名路由时介绍。</p> <h2 id=\"_2-2-5-命名路由\"><a href=\"#_2-2-5-命名路由\" class=\"header-anchor\">#</a> 2.2.5 命名路由</h2> <p>所谓“命名路由”（Named Route）即有名字的路由，我们可以先给路由起一个名字，然后就可以通过路由名字直接打开新的路由了，这为路由管理带来了一种直观、简单的方式。</p> <h3 id=\"路由表\"><a href=\"#路由表\" class=\"header-anchor\">#</a> 路由表</h3> <p>要想使用命名路由，我们必须先提供并注册一个路由表（routing table），这样应用程序才知道哪个名字与哪个路由组件相对应。其实注册路由表就是给路由起名字，路由表的定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Map<span class=\"token operator\">&lt;</span>String<span class=\"token punctuation\">,</span> WidgetBuilder<span class=\"token operator\">&gt;</span> routes<span class=\"token punctuation\">;</span>\n</code></pre></div><p>它是一个<code>Map</code>，key为路由的名字，是个字符串；value是个<code>builder</code>回调函数，用于生成相应的路由widget。我们在通过路由名字打开新路由时，应用会根据路由名字在路由表中查找到对应的<code>WidgetBuilder</code>回调函数，然后调用该回调函数生成路由widget并返回。</p> <h3 id=\"注册路由表\"><a href=\"#注册路由表\" class=\"header-anchor\">#</a> 注册路由表</h3> <p>路由表的注册方式很简单，我们回到之前“计数器”的示例，然后在<code>MyApp</code>类的<code>build</code>方法中找到<code>MaterialApp</code>，添加<code>routes</code>属性，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">MaterialApp</span><span class=\"token punctuation\">(</span>\n  title<span class=\"token punctuation\">:</span> <span class=\"token string\">'Flutter Demo'</span><span class=\"token punctuation\">,</span>\n  theme<span class=\"token punctuation\">:</span> <span class=\"token function\">ThemeData</span><span class=\"token punctuation\">(</span>\n    primarySwatch<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token comment\">//注册路由表</span>\n  routes<span class=\"token punctuation\">:</span><span class=\"token punctuation\">{</span>\n   <span class=\"token string\">&quot;new_page&quot;</span><span class=\"token punctuation\">:</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">NewRoute</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">// 省略其它路由注册信息</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token punctuation\">,</span>\n  home<span class=\"token punctuation\">:</span> <span class=\"token function\">MyHomePage</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token string\">'Flutter Demo Home Page'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>现在我们就完成了路由表的注册。上面的代码中<code>home</code>路由并没有使用命名路由，如果我们也想将<code>home</code>注册为命名路由应该怎么做呢？其实很简单，直接看代码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">MaterialApp</span><span class=\"token punctuation\">(</span>\n  title<span class=\"token punctuation\">:</span> <span class=\"token string\">'Flutter Demo'</span><span class=\"token punctuation\">,</span>\n  initialRoute<span class=\"token punctuation\">:</span><span class=\"token string\">&quot;/&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//名为&quot;/&quot;的路由作为应用的home(首页)</span>\n  theme<span class=\"token punctuation\">:</span> <span class=\"token function\">ThemeData</span><span class=\"token punctuation\">(</span>\n    primarySwatch<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token comment\">//注册路由表</span>\n  routes<span class=\"token punctuation\">:</span><span class=\"token punctuation\">{</span>\n   <span class=\"token string\">&quot;new_page&quot;</span><span class=\"token punctuation\">:</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">NewRoute</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n   <span class=\"token string\">&quot;/&quot;</span><span class=\"token punctuation\">:</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">MyHomePage</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token string\">'Flutter Demo Home Page'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//注册首页路由</span>\n  <span class=\"token punctuation\">}</span> \n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>可以看到，我们只需在路由表中注册一下<code>MyHomePage</code>路由，然后将其名字作为<code>MaterialApp</code>的<code>initialRoute</code>属性值即可，该属性决定应用的初始路由页是哪一个命名路由。</p> <h3 id=\"通过路由名打开新路由页\"><a href=\"#通过路由名打开新路由页\" class=\"header-anchor\">#</a> 通过路由名打开新路由页</h3> <p>要通过路由名称来打开新路由，可以使用<code>Navigator</code> 的<code>pushNamed</code>方法：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Future <span class=\"token function\">pushNamed</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> String routeName<span class=\"token punctuation\">,</span><span class=\"token punctuation\">{</span>Object arguments<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p><code>Navigator</code> 除了<code>pushNamed</code>方法，还有<code>pushReplacementNamed</code>等其他管理命名路由的方法，读者可以自行查看API文档。接下来我们通过路由名来打开新的路由页，修改<code>FlatButton</code>的<code>onPressed</code>回调代码，改为：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">pushNamed</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;new_page&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">//Navigator.push(context,</span>\n  <span class=\"token comment\">//  MaterialPageRoute(builder: (context) {</span>\n  <span class=\"token comment\">//  return NewRoute();</span>\n  <span class=\"token comment\">//}));  </span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n</code></pre></div><p>热重载应用，再次点击“open new route”按钮，依然可以打开新的路由页。</p> <h3 id=\"命名路由参数传递\"><a href=\"#命名路由参数传递\" class=\"header-anchor\">#</a> 命名路由参数传递</h3> <p>在Flutter最初的版本中，命名路由是不能传递参数的，后来才支持了参数；下面展示命名路由如何传递并获取路由参数：</p> <p>我们先注册一个路由：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code> routes<span class=\"token punctuation\">:</span><span class=\"token punctuation\">{</span>\n   <span class=\"token string\">&quot;new_page&quot;</span><span class=\"token punctuation\">:</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">EchoRoute</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token punctuation\">,</span>\n</code></pre></div><p>在路由页通过<code>RouteSetting</code>对象获取路由参数：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">EchoRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//获取路由参数  </span>\n    <span class=\"token keyword\">var</span> args<span class=\"token operator\">=</span>ModalRoute<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>settings<span class=\"token punctuation\">.</span>arguments<span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//...省略无关代码</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>在打开路由时传递参数</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pushNamed</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;new_page&quot;</span><span class=\"token punctuation\">,</span> arguments<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;hi&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><h3 id=\"适配\"><a href=\"#适配\" class=\"header-anchor\">#</a> 适配</h3> <p>假设我们也想将上面路由传参示例中的<code>TipRoute</code>路由页注册到路由表中，以便也可以通过路由名来打开它。但是，由于<code>TipRoute</code>接受一个<code>text</code> 参数，我们如何在不改变<code>TipRoute</code>源码的前提下适配这种情况？其实很简单：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">MaterialApp</span><span class=\"token punctuation\">(</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n  routes<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span>\n   <span class=\"token string\">&quot;tip2&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n     <span class=\"token keyword\">return</span> <span class=\"token function\">TipRoute</span><span class=\"token punctuation\">(</span>text<span class=\"token punctuation\">:</span> ModalRoute<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>settings<span class=\"token punctuation\">.</span>arguments<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n   <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> \n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><h2 id=\"_2-2-6-路由生成钩子\"><a href=\"#_2-2-6-路由生成钩子\" class=\"header-anchor\">#</a> 2.2.6 路由生成钩子</h2> <p>假设我们要开发一个电商APP，当用户没有登录时可以看店铺、商品等信息，但交易记录、购物车、用户个人信息等页面需要登录后才能看。为了实现上述功能，我们需要在打开每一个路由页前判断用户登录状态！如果每次打开路由前我们都需要去判断一下将会非常麻烦，那有什么更好的办法吗？答案是有！</p> <p><code>MaterialApp</code>有一个<code>onGenerateRoute</code>属性，它在打开命名路由时可能会被调用，之所以说可能，是因为当调用<code>Navigator.pushNamed(...)</code>打开命名路由时，如果指定的路由名在路由表中已注册，则会调用路由表中的<code>builder</code>函数来生成路由组件；如果路由表中没有注册，才会调用<code>onGenerateRoute</code>来生成路由。<code>onGenerateRoute</code>回调签名如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Route<span class=\"token operator\">&lt;</span><span class=\"token keyword\">dynamic</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">Function</span><span class=\"token punctuation\">(</span>RouteSettings settings<span class=\"token punctuation\">)</span>\n</code></pre></div><p>有了<code>onGenerateRoute</code>回调，要实现上面控制页面权限的功能就非常容易：我们放弃使用路由表，取而代之的是提供一个<code>onGenerateRoute</code>回调，然后在该回调中进行统一的权限控制，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">MaterialApp</span><span class=\"token punctuation\">(</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n  onGenerateRoute<span class=\"token punctuation\">:</span><span class=\"token punctuation\">(</span>RouteSettings settings<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n\t  <span class=\"token keyword\">return</span> <span class=\"token function\">MaterialPageRoute</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n\t\t   String routeName <span class=\"token operator\">=</span> settings<span class=\"token punctuation\">.</span>name<span class=\"token punctuation\">;</span>\n       <span class=\"token comment\">// 如果访问的路由页需要登录，但当前未登录，则直接返回登录页路由，</span>\n       <span class=\"token comment\">// 引导用户登录；其它情况则正常打开路由。</span>\n     <span class=\"token punctuation\">}</span>\n   <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><blockquote><p>注意，<code>onGenerateRoute</code>只会对命名路由生效。</p></blockquote> <h2 id=\"_2-2-7-总结\"><a href=\"#_2-2-7-总结\" class=\"header-anchor\">#</a> 2.2.7 总结</h2> <p>本章先介绍了Flutter中路由管理、传参的方式，然后又着重介绍了命名路由相关内容。在此需要说明一点，由于命名路由只是一种可选的路由管理方式，在实际开发中，读者可能心中会犹豫到底使用哪种路由管理方式。在此，根据笔者经验，建议读者最好统一使用命名路由的管理方式，这将会带来如下好处：</p> <ol><li>语义化更明确。</li> <li>代码更好维护；如果使用匿名路由，则必须在调用<code>Navigator.push</code>的地方创建新路由页，这样不仅需要import新路由页的dart文件，而且这样的代码将会非常分散。</li> <li>可以通过<code>onGenerateRoute</code>做一些全局的路由跳转前置处理逻辑。</li></ol> <p>综上所述，笔者比较建议使用命名路由，当然这并不是什么金科玉律，读者可以根据自己偏好或实际情况来决定。</p> <p>另外，还有一些关于路由管理的内容我们没有介绍，比如路由MaterialApp中还有<code>navigatorObservers</code>和<code>onUnknownRoute</code>两个回调属性，前者可以监听所有路由跳转动作，后者在打开一个不存在的命名路由时会被调用，由于这些功能并不常用，而且也比较简单，我们便不再花费篇幅来介绍了，读者可以自行查看API文档。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/52.61d5b4f1.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter2/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>简介 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/207.d052f90b.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter2/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h2 id=\"简介\"><a href=\"#简介\" class=\"header-anchor\">#</a> 简介</h2> <p>本章将通过一些简单的示例来一步步介绍Flutter的开发流程.</p> <h2 id=\"本章目录\"><a href=\"#本章目录\" class=\"header-anchor\">#</a> 本章目录</h2> <ul><li><a href=\"/v2/chapter2/first_flutter_app.html\">2.1：计数器示例</a></li> <li><a href=\"/v2/chapter2/flutter_router.html\">2.2：路由管理</a></li> <li><a href=\"/v2/chapter2/flutter_package_mgr.html\">2.3：包管理</a></li> <li><a href=\"/v2/chapter2/flutter_assets_mgr.html\">2.4：资源管理</a></li> <li><a href=\"/v2/chapter2/flutter_app_debug.html\">2.5：调试Flutter APP</a></li> <li><a href=\"/v2/chapter2/thread_model_and_error_report.html\">2.6：Dart线程模型及异常捕获</a></li></ul></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/207.d052f90b.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter2/thread_model_and_error_report.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>2.6 Flutter异常捕获 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/119.baead276.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter2/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_2-6-flutter异常捕获\"><a href=\"#_2-6-flutter异常捕获\" class=\"header-anchor\">#</a> 2.6 Flutter异常捕获</h1> <p>在介绍Flutter异常捕获之前必须先了解一下Dart单线程模型，只有了解了Dart的代码执行流程，我们才能知道该在什么地方去捕获异常。</p> <h2 id=\"_2-6-1-dart单线程模型\"><a href=\"#_2-6-1-dart单线程模型\" class=\"header-anchor\">#</a> 2.6.1 Dart单线程模型</h2> <p>在Java和Objective-C（以下简称“OC”）中，如果程序发生异常且没有被捕获，那么程序将会终止，但是这在Dart或JavaScript中则不会！究其原因，这和它们的运行机制有关系。Java和OC都是多线程模型的编程语言，任意一个线程触发异常且该异常未被捕获时，就会导致整个进程退出。但Dart和JavaScript不会，它们都是单线程模型，运行机制很相似(但有区别)，下面我们通过Dart官方提供的一张图来看看Dart大致运行原理：</p> <p><img src=\"/assets/img/2-12.eb7484c9.png\" alt=\"图2-12\"></p> <p>Dart 在单线程中是以消息循环机制来运行的，其中包含两个任务队列，一个是“微任务队列”  <strong>microtask queue</strong>，另一个叫做“事件队列” <strong>event queue</strong>。从图中可以发现，微任务队列的执行优先级高于事件队列。</p> <p>现在我们来介绍一下Dart线程运行过程，如上图中所示，入口函数 main() 执行完后，消息循环机制便启动了。首先会按照先进先出的顺序逐个执行微任务队列中的任务，事件任务执行完毕后程序便会退出，但是，在事件任务执行的过程中也可以插入新的微任务和事件任务，在这种情况下，整个线程的执行过程便是一直在循环，不会退出，而Flutter中，主线程的执行过程正是如此，永不终止。</p> <p>在Dart中，所有的外部事件任务都在事件队列中，如IO、计时器、点击、以及绘制事件等，而微任务通常来源于Dart内部，并且微任务非常少，之所以如此，是因为微任务队列优先级高，如果微任务太多，执行时间总和就越久，事件队列任务的延迟也就越久，对于GUI应用来说最直观的表现就是比较卡，所以必须得保证微任务队列不会太长。值得注意的是，我们可以通过<code>Future.microtask(…)</code>方法向微任务队列插入一个任务。</p> <p>在事件循环中，当某个任务发生异常并没有被捕获时，程序并不会退出，而直接导致的结果是<strong>当前任务</strong>的后续代码就不会被执行了，也就是说一个任务中的异常是不会影响其它任务执行的。</p> <h2 id=\"_2-6-2-flutter异常捕获\"><a href=\"#_2-6-2-flutter异常捕获\" class=\"header-anchor\">#</a> 2.6.2 Flutter异常捕获</h2> <p>Dart中可以通过<code>try/catch/finally</code>来捕获代码块异常，这个和其它编程语言类似，如果读者不清楚，可以查看Dart语言文档，不再赘述，下面我们看看Flutter中的异常捕获。</p> <h3 id=\"flutter框架异常捕获\"><a href=\"#flutter框架异常捕获\" class=\"header-anchor\">#</a> Flutter框架异常捕获</h3> <p>Flutter 框架为我们在很多关键的方法进行了异常捕获。这里举一个例子，当我们布局发生越界或不合规范时，Flutter就会自动弹出一个错误界面，这是因为Flutter已经在执行build方法时添加了异常捕获，最终的源码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token metadata symbol\">@override</span>\n<span class=\"token keyword\">void</span> <span class=\"token function\">performRebuild</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//执行build方法  </span>\n    built <span class=\"token operator\">=</span> <span class=\"token function\">build</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">,</span> stack<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 有异常时则弹出错误提示  </span>\n    built <span class=\"token operator\">=</span> ErrorWidget<span class=\"token punctuation\">.</span><span class=\"token function\">builder</span><span class=\"token punctuation\">(</span><span class=\"token function\">_debugReportException</span><span class=\"token punctuation\">(</span><span class=\"token string\">'building $this'</span><span class=\"token punctuation\">,</span> e<span class=\"token punctuation\">,</span> stack<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span> \n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span>      \n</code></pre></div><p>可以看到，在发生异常时，Flutter默认的处理方式是弹一个ErrorWidget，但如果我们想自己捕获异常并上报到报警平台的话应该怎么做？我们进入<code>_debugReportException()</code>方法看看：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>FlutterErrorDetails <span class=\"token function\">_debugReportException</span><span class=\"token punctuation\">(</span>\n  String context<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">dynamic</span> exception<span class=\"token punctuation\">,</span>\n  StackTrace stack<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n  InformationCollector informationCollector\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">//构建错误详情对象  </span>\n  <span class=\"token keyword\">final</span> FlutterErrorDetails details <span class=\"token operator\">=</span> <span class=\"token function\">FlutterErrorDetails</span><span class=\"token punctuation\">(</span>\n    exception<span class=\"token punctuation\">:</span> exception<span class=\"token punctuation\">,</span>\n    stack<span class=\"token punctuation\">:</span> stack<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">library</span><span class=\"token punctuation\">:</span> <span class=\"token string\">'widgets library'</span><span class=\"token punctuation\">,</span>\n    context<span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">,</span>\n    informationCollector<span class=\"token punctuation\">:</span> informationCollector<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">//报告错误 </span>\n  FlutterError<span class=\"token punctuation\">.</span><span class=\"token function\">reportError</span><span class=\"token punctuation\">(</span>details<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">return</span> details<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们发现，错误是通过<code>FlutterError.reportError</code>方法上报的，继续跟踪：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>\n<span class=\"token keyword\">static</span> <span class=\"token keyword\">void</span> <span class=\"token function\">reportError</span><span class=\"token punctuation\">(</span>FlutterErrorDetails details<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>onError <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span>\n    <span class=\"token function\">onError</span><span class=\"token punctuation\">(</span>details<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//调用了onError回调</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们发现<code>onError</code>是<code>FlutterError</code>的一个静态属性，它有一个默认的处理方法 <code>dumpErrorToConsole</code>，到这里就清晰了，如果我们想自己上报异常，只需要提供一个自定义的错误处理回调即可，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">main</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  FlutterError<span class=\"token punctuation\">.</span>onError <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>FlutterErrorDetails details<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">reportError</span><span class=\"token punctuation\">(</span>details<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>这样我们就可以处理那些Flutter为我们捕获的异常了，接下来我们看看如何捕获其它异常。</p> <h3 id=\"其它异常捕获与日志收集\"><a href=\"#其它异常捕获与日志收集\" class=\"header-anchor\">#</a> 其它异常捕获与日志收集</h3> <p>在Flutter中，还有一些Flutter没有为我们捕获的异常，如调用空对象方法异常、Future中的异常。在Dart中，异常分两类：同步异常和异步异常，同步异常可以通过<code>try/catch</code>捕获，而异步异常则比较麻烦，如下面的代码是捕获不了<code>Future</code>的异常的：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">try</span><span class=\"token punctuation\">{</span>\n    Future<span class=\"token punctuation\">.</span><span class=\"token function\">delayed</span><span class=\"token punctuation\">(</span><span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Future<span class=\"token punctuation\">.</span><span class=\"token function\">error</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;xxx&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>Dart中有一个<code>runZoned(...)</code> 方法，可以给执行对象指定一个Zone。Zone表示一个代码执行的环境范围，为了方便理解，读者可以将Zone类比为一个代码执行沙箱，不同沙箱的之间是隔离的，沙箱可以捕获、拦截或修改一些代码行为，如Zone中可以捕获日志输出、Timer创建、微任务调度的行为，同时Zone也可以捕获所有未处理的异常。下面我们看看<code>runZoned(...)</code>方法定义：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>R runZoned<span class=\"token operator\">&lt;</span>R<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>R <span class=\"token function\">body</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n    Map zoneValues<span class=\"token punctuation\">,</span> \n    ZoneSpecification zoneSpecification<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">Function</span> onError<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> \n</code></pre></div><ul><li><p><code>zoneValues</code>: Zone 的私有数据，可以通过实例<code>zone[key]</code>获取，可以理解为每个“沙箱”的私有数据。</p></li> <li><p><code>zoneSpecification</code>：Zone的一些配置，可以自定义一些代码行为，比如拦截日志输出行为等，举个例子：</p> <p>下面是拦截应用中所有调用<code>print</code>输出日志的行为。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">main</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">runZoned</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">runApp</span><span class=\"token punctuation\">(</span><span class=\"token function\">MyApp</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> zoneSpecification<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">ZoneSpecification</span><span class=\"token punctuation\">(</span>\n      print<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>Zone self<span class=\"token punctuation\">,</span> ZoneDelegate parent<span class=\"token punctuation\">,</span> Zone zone<span class=\"token punctuation\">,</span> String line<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        parent<span class=\"token punctuation\">.</span><span class=\"token function\">print</span><span class=\"token punctuation\">(</span>zone<span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;Intercepted: $line&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>这样一来，我们APP中所有调用<code>print</code>方法输出日志的行为都会被拦截，通过这种方式，我们也可以在应用中记录日志，等到应用触发未捕获的异常时，将异常信息和日志统一上报。ZoneSpecification还可以自定义一些其他行为，读者可以查看API文档。</p></li> <li><p><code>onError</code>：Zone中未捕获异常处理回调，如果开发者提供了onError回调或者通过<code>ZoneSpecification.handleUncaughtError</code>指定了错误处理回调，那么这个zone将会变成一个error-zone，该error-zone中发生未捕获异常(无论同步还是异步)时都会调用开发者提供的回调，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">runZoned</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">runApp</span><span class=\"token punctuation\">(</span><span class=\"token function\">MyApp</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> onError<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>Object obj<span class=\"token punctuation\">,</span> StackTrace stack<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">var</span> details<span class=\"token operator\">=</span><span class=\"token function\">makeDetails</span><span class=\"token punctuation\">(</span>obj<span class=\"token punctuation\">,</span>stack<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token function\">reportError</span><span class=\"token punctuation\">(</span>details<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>这样一来，结合上面的<code>FlutterError.onError</code>我们就可以捕获我们Flutter应用中全部错误了！需要注意的是，error-zone内部发生的错误是不会跨越当前error-zone的边界的，如果想跨越error-zone边界去捕获异常，可以通过共同的“源”zone来捕获，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">var</span> future <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Future<span class=\"token punctuation\">.</span>value</span><span class=\"token punctuation\">(</span><span class=\"token number\">499</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token function\">runZoned</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n\t<span class=\"token keyword\">var</span> future2 <span class=\"token operator\">=</span> future<span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>_<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span> <span class=\"token keyword\">throw</span> <span class=\"token string\">&quot;error in first error-zone&quot;</span><span class=\"token punctuation\">;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\t<span class=\"token function\">runZoned</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n\t\t<span class=\"token keyword\">var</span> future3 <span class=\"token operator\">=</span> future2<span class=\"token punctuation\">.</span><span class=\"token function\">catchError</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span> <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Never reached!&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\t<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> onError<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span> <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;unused error handler&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> onError<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span> <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;catches error of first error-zone.&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n</code></pre></div></li></ul> <h3 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h3> <p>我们最终的异常捕获和上报代码大致如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">collectLog</span><span class=\"token punctuation\">(</span>String line<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//收集日志</span>\n<span class=\"token punctuation\">}</span>\n<span class=\"token keyword\">void</span> <span class=\"token function\">reportErrorAndLog</span><span class=\"token punctuation\">(</span>FlutterErrorDetails details<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//上报错误和日志逻辑</span>\n<span class=\"token punctuation\">}</span>\n\nFlutterErrorDetails <span class=\"token function\">makeDetails</span><span class=\"token punctuation\">(</span>Object obj<span class=\"token punctuation\">,</span> StackTrace stack<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token comment\">// 构建错误信息</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">void</span> <span class=\"token function\">main</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  FlutterError<span class=\"token punctuation\">.</span>onError <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>FlutterErrorDetails details<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">reportErrorAndLog</span><span class=\"token punctuation\">(</span>details<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n  <span class=\"token function\">runZoned</span><span class=\"token punctuation\">(</span>\n    <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">runApp</span><span class=\"token punctuation\">(</span><span class=\"token function\">MyApp</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    zoneSpecification<span class=\"token punctuation\">:</span> <span class=\"token function\">ZoneSpecification</span><span class=\"token punctuation\">(</span>\n      print<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>Zone self<span class=\"token punctuation\">,</span> ZoneDelegate parent<span class=\"token punctuation\">,</span> Zone zone<span class=\"token punctuation\">,</span> String line<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">collectLog</span><span class=\"token punctuation\">(</span>line<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// 收集日志</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    onError<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>Object obj<span class=\"token punctuation\">,</span> StackTrace stack<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">var</span> details <span class=\"token operator\">=</span> <span class=\"token function\">makeDetails</span><span class=\"token punctuation\">(</span>obj<span class=\"token punctuation\">,</span> stack<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token function\">reportErrorAndLog</span><span class=\"token punctuation\">(</span>details<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/119.baead276.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter3/buttons.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>3.4 按钮 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/10.5f481ee1.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter3/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_3-4-按钮\"><a href=\"#_3-4-按钮\" class=\"header-anchor\">#</a> 3.4 按钮</h1> <h2 id=\"_3-4-1-material组件库中的按钮\"><a href=\"#_3-4-1-material组件库中的按钮\" class=\"header-anchor\">#</a> 3.4.1 Material组件库中的按钮</h2> <p>Material 组件库中提供了多种按钮组件如<code>RaisedButton</code>、<code>FlatButton</code>、<code>OutlineButton</code>等，它们都是直接或间接对<code>RawMaterialButton</code>组件的包装定制，所以他们大多数属性都和<code>RawMaterialButton</code>一样。在介绍各个按钮时我们先介绍其默认外观，而按钮的外观大都可以通过属性来自定义，我们在后面统一介绍这些属性。另外，所有Material 库中的按钮都有如下相同点：</p> <ol><li>按下时都会有“水波动画”（又称“涟漪动画”，就是点击时按钮上会出现水波荡漾的动画）。</li> <li>有一个<code>onPressed</code>属性来设置点击回调，当按钮按下时会执行该回调，如果不提供该回调则按钮会处于禁用状态，禁用状态不响应用户点击。</li></ol> <h3 id=\"raisedbutton\"><a href=\"#raisedbutton\" class=\"header-anchor\">#</a> RaisedButton</h3> <p><code>RaisedButton</code> 即&quot;漂浮&quot;按钮，它默认带有阴影和灰色背景。按下后，阴影会变大，如图3-10所示：</p> <p><img src=\"data:image/png;base64,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J598cvF5NIITjs6BoyOvvPKKGYzao+zJJ59MK6+8svVj5plrAy1da5R88cUX6eCDD07ff/990e8XXnghXX311QnnlBNiIwRynuISAj5833jjjfZxU/HM+a+//rrgGsfgHF9ORhZffHHLk+acfir77rvvpg8//NBsT5zC82mnnVZ8PPXtt99Od999dzrkkEMsENBGU6R2awB8yBJScfgbbrjBgMcQbr/9dsOUSHz44Ycb6eutt545JXW33Xbb9O233yaidVlwfp4KZ5xxRlphhRWGnN58882tjdtuuy0tsMAC9kWkY4891gIM7d58883m9BgbxEtOPPHEhPOfffbZ6b777rM25p133oQxvPzyy6pWKy2jrlWnKjpz/PHHm/PvvPPO6d5777UZ2hprrGFP8vPOO8+uwEl5YuP8zOjgigC96aabps8//zwdddRRabnllrMZxDLLLGPXnHrqqfYwqbilBQLugTz00ENFlcsuu8zS48ePt6DALOOUU04x57/ooovM8QlMBAcWFf3DpGikxonaBYClllrKouiCCy6Yll56aZuyg99TTz1lMAIwK+pzzDFHOv/889NCCy2UqIvTIhiEj/qUERxOOumktMUWW1iblEkgljaY/p9++ukqtkBCuzwZttlmGyt/8cUXi/O8XjCd3GSTTdJcc81lbfCqgNQ1ABSdr3ECx4YrsD3iiCPs89a8ox944IHW6zfeeMM0dsDskM+eM2ODK3gkMCPYCAGP1zYFPpy0/Bpnlf/3R68AOLaEmQei14pLL73UHhI77rhjWmedddJ8882Xll9++UQe4ZWlSVK7eaqmZAIRYhGe7ghTMqT8vXvIxcEfe+wxCxa777671eMPBHUjCy+8cFHN90Pvdrx6SFZccUXrE1NNHP7LL7+0g/M8JUKmDwGcFWx57+fpz5Se93N9rZoAgfzwww+mCb6amlPA6x/rAXJ6q9TlH2YMCG1zH3hkLYJXDq1B8TqK8O6voESemSuCLeyyyy6WrsMf4SBd7lPtAkC5g8qLeL1zewdVHQUL3vV6IeoDbTMDYNrJKwmGQ/CQUfbi3oPUJq9a7M/j9HBKANeakHBQQKiyA63TqG63GidZd911EzO99957z5ycazX9Jw3fiBYWLeP++HULV1zbZGMCgBDU05jIXBYt9LGT0EvhP9CQ89966622a8D9LrjgAivv5b1z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\" alt=\"图3-10\"></p> <p>使用<code>RaisedButton</code>非常简单，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;normal&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><h3 id=\"flatbutton\"><a href=\"#flatbutton\" class=\"header-anchor\">#</a> FlatButton</h3> <p><code>FlatButton</code>即扁平按钮，默认背景透明并不带阴影。按下后，会有背景色，如图3-11所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图3-11\"></p> <p>使用FlatButton也很简单，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;normal&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><h3 id=\"outlinebutton\"><a href=\"#outlinebutton\" class=\"header-anchor\">#</a> OutlineButton</h3> <p><code>OutlineButton</code>默认有一个边框，不带阴影且背景透明。按下后，边框颜色会变亮、同时出现背景和阴影(较弱)，如图3-12所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图3-12\"></p> <p>使用<code>OutlineButton</code>也很简单，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">OutlineButton</span><span class=\"token punctuation\">(</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;normal&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><h3 id=\"iconbutton\"><a href=\"#iconbutton\" class=\"header-anchor\">#</a> IconButton</h3> <p><code>IconButton</code>是一个可点击的Icon，不包括文字，默认没有背景，点击后会出现背景，如图3-13所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图3-13\"></p> <p>代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">IconButton</span><span class=\"token punctuation\">(</span>\n  icon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>thumb_up<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><h3 id=\"带图标的按钮\"><a href=\"#带图标的按钮\" class=\"header-anchor\">#</a> 带图标的按钮</h3> <p><code>RaisedButton</code>、<code>FlatButton</code>、<code>OutlineButton</code>都有一个<code>icon</code> 构造函数，通过它可以轻松创建带图标的按钮，如图3-14所示：</p> <p><img src=\"/assets/img/3-14.e8ad30dc.png\" alt=\"图3-14\"></p> <p>代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>RaisedButton<span class=\"token punctuation\">.</span><span class=\"token function\">icon</span><span class=\"token punctuation\">(</span>\n  icon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>send<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  label<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;发送&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  onPressed<span class=\"token punctuation\">:</span> _onPressed<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\nOutlineButton<span class=\"token punctuation\">.</span><span class=\"token function\">icon</span><span class=\"token punctuation\">(</span>\n  icon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>add<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  label<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;添加&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  onPressed<span class=\"token punctuation\">:</span> _onPressed<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\nFlatButton<span class=\"token punctuation\">.</span><span class=\"token function\">icon</span><span class=\"token punctuation\">(</span>\n  icon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>info<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  label<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;详情&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  onPressed<span class=\"token punctuation\">:</span> _onPressed<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</code></pre></div><h2 id=\"_3-4-2-自定义按钮外观\"><a href=\"#_3-4-2-自定义按钮外观\" class=\"header-anchor\">#</a> 3.4.2 自定义按钮外观</h2> <p>按钮外观可以通过其属性来定义，不同按钮属性大同小异，我们以FlatButton为例，介绍一下常见的按钮属性，详细的信息可以查看API文档。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">const</span> <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>  \n  <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>onPressed<span class=\"token punctuation\">,</span> <span class=\"token comment\">//按钮点击回调</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>textColor<span class=\"token punctuation\">,</span> <span class=\"token comment\">//按钮文字颜色</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>disabledTextColor<span class=\"token punctuation\">,</span> <span class=\"token comment\">//按钮禁用时的文字颜色</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>color<span class=\"token punctuation\">,</span> <span class=\"token comment\">//按钮背景颜色</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>disabledColor<span class=\"token punctuation\">,</span><span class=\"token comment\">//按钮禁用时的背景颜色</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>highlightColor<span class=\"token punctuation\">,</span> <span class=\"token comment\">//按钮按下时的背景颜色</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>splashColor<span class=\"token punctuation\">,</span> <span class=\"token comment\">//点击时，水波动画中水波的颜色</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>colorBrightness<span class=\"token punctuation\">,</span><span class=\"token comment\">//按钮主题，默认是浅色主题 </span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>padding<span class=\"token punctuation\">,</span> <span class=\"token comment\">//按钮的填充</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>shape<span class=\"token punctuation\">,</span> <span class=\"token comment\">//外形</span>\n  <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span> <span class=\"token comment\">//按钮的内容</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>其中大多数属性名都是自解释的，我们不赘述。下面我们通过一个示例来看看如何自定义按钮。</p> <h4 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h4> <p>定义一个背景蓝色，两边圆角的按钮。效果如图3-15所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图3-15\"></p> <p>代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n  color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span>\n  highlightColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">[</span><span class=\"token number\">700</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  colorBrightness<span class=\"token punctuation\">:</span> Brightness<span class=\"token punctuation\">.</span>dark<span class=\"token punctuation\">,</span>\n  splashColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Submit&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  shape<span class=\"token punctuation\">:</span><span class=\"token function\">RoundedRectangleBorder</span><span class=\"token punctuation\">(</span>borderRadius<span class=\"token punctuation\">:</span> BorderRadius<span class=\"token punctuation\">.</span><span class=\"token function\">circular</span><span class=\"token punctuation\">(</span><span class=\"token number\">20.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>很简单吧，在上面的代码中，我们主要通过<code>shape</code>来指定其外形为一个圆角矩形。因为按钮背景是蓝色(深色)，我们需要指定按钮主题<code>colorBrightness</code>为<code>Brightness.dark</code>，这是为了保证按钮文字颜色为浅色。</p> <p>Flutter 中没有提供去除背景的设置，假若我们需要去除背景，则可以通过将背景颜色设置为全透明来实现。对应上面的代码，便是将 <code>color: Colors.blue</code> 替换为 <code>color: Color(0x000000)</code>。</p> <p>细心的读者可能会发现这个按钮没有阴影(点击之后也没有)，这样会显得没有质感。其实这也很容易，将上面的<code>FlatButton</code>换成<code>RaisedButton</code>就行，其它代码不用改（这里 color 也不做更改），换了之后的效果如图3-16所示：</p> <p><img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKoAAABQCAYAAACJf+79AAAMJWlDQ1BJQ0MgUHJvZmlsZQAASImVVwdUk8kWnr8kISGhBUKREnoTpUiXGloEAamCjZAEEkoMgSBiBVlUYC2oWLCiqyKKrgWQxYZdWRTsdUFERVlFXWyovEkC6LqvnHfPmZnv3Ln3znennRkAVCM5YnEGqgZApihHEhXsz5yckMgkPQIo0AbKgATGcrjZYr/IyDAAZbj9u7y7CRBZe81OFuuf/f9V1Hn8bC4ASCTEybxsbibEhwHAXbhiSQ4AhF6oN52VI4aYCFkCTQkkCLGZDKcqsJsMJytwmNwmJooFcRIASlQOR5IKgIqMFzOXmwrjqJRBbC/iCUUQN0HszRVweBB/hnh0ZuZMiFWtILZK/i5O6t9iJo/E5HBSR7AiF7koBQizxRmc2f/ndPxvycyQDo9hCgtVIAmJkuUsm7f0maEyTIX4gig5PAJiDYivC3lyexl+IpCGxA7Zf+Bms+CcAQYAKJXHCQiFWB9iE2l6rN8Q9uZI5L7QHk3MF8TEK+KjIsnMqKH4aL4oIzxsKE6ZgM8exlX87MDoYZsUYRAbYriGaIMwhx0zFPNCrjAuHGIViO9np0eHDvk+zxewwkfGkkbJOMM1x0Bm9nAumFmKJChKYY+5CITs8CF9WI4gJkThi03ncuQcdCBO42dPDhvmw+MHBCr4YIV8UewQT6xcnOMfNWS/Q5wROWSPNfEzgmV6E4hbs3Ojh337cuBmU+SCgzTOhEjFuLimOCcyRsENZ4IwwAIBgAmksCSDmSANCFt763vBcE8Q4AAJSAV8YDekGfaIl/eIYB0N8sGfEPFB9oifv7yXD3Kh/suIVlHbgRR5b67cIx08gTgT18O9cU88DNa+sDjibrj7sB9TdXhUYiAxgBhCDCJazxAWSn6IywRcmEEGLBIQCls+zErGQTTM/VscwhNCG+ER4Qahg3AHxIHH0E74jwy/RROO6CaCDhg1aCi75O+zwy0ga2fcH/eC/CF3nIHrATt8HMzED/eBuTlD7bdZ+3fcpcOsyfZklKxN9iVb/WinYqPiPOIjy+17ngpeySOZsEZ6fhyN9V1uPNiG/miJLcEOYeexU9hFrAmrB0zsBNaAtWDHZHhkbzyW743h0aLkfNJhHOGwjX2NfY/95x/G5gyNL5GvP8jh5+XIDg5rpni2RJgqyGH6wduaz2SLuGNGMx3tHeAtKrv7FVfLW4b8TkcYl77pCuYAMP7Q4OBg0zddGLxZjhgAQHn1TWf1Hh5newAuFHClklyFDpdVBEABqvCk6AJDeHdZwYwcgQvwBL4gEEwAESAGJIDpcJ4FIBOyngXmggJQDErBCrAGbABbwHawG+wDB0E9aAKnwDlwGVwFN8A9uFe6wQvQB96BAQRBSAgNoSO6iBFijtgijogb4o0EImFIFJKAJCGpiAiRInORRUgpUo5sQLYh1civyFHkFHIRaUPuIJ1ID/IG+YRiKBXVRA1QC3Qs6ob6oaFoDDoNTUWz0Hy0CF2GrkOr0L1oHXoKvYzeQDvQF2g/BjBljIEZY3aYG8bCIrBELAWTYPOxEqwCq8JqsUa40tewDqwX+4gTcTrOxO3gfg3BY3EunoXPx8vwDfhuvA4/g1/DO/E+/CuBRtAn2BI8CGzCZEIqYRahmFBB2Ek4QjgLz1Q34R2RSGQQLYmu8KwmENOIc4hlxE3E/cSTxDZiF7GfRCLpkmxJXqQIEoeUQyomrSftJZ0gtZO6SR+UlJWMlByVgpQSlURKhUoVSnuUjiu1Kz1VGiCrkc3JHuQIMo88m7ycvIPcSL5C7iYPUNQplhQvSgwljVJAWUeppZyl3Ke8VVZWNlF2V56kLFReqLxO+YDyBeVO5Y9UDaoNlUWdSpVSl1F3UU9S71Df0mg0C5ovLZGWQ1tGq6adpj2kfVChq4xRYavwVBaoVKrUqbSrvFQlq5qr+qlOV81XrVA9pHpFtVeNrGahxlLjqM1Xq1Q7qnZLrV+dru6gHqGeqV6mvkf9ovozDZKGhUagBk+jSGO7xmmNLjpGN6Wz6Fz6IvoO+ll6tyZR01KTrZmmWaq5T7NVs09LQ2ucVpxWnlal1jGtDgbGsGCwGRmM5YyDjJuMT9oG2n7afO2l2rXa7drvdUbp+OrwdUp09uvc0Pmky9QN1E3XXalbr/tAD9ez0ZukN0tvs95Zvd5RmqM8R3FHlYw6OOquPqpvox+lP0d/u36Lfr+BoUGwgdhgvcFpg15DhqGvYZrhasPjhj1GdCNvI6HRaqMTRs+ZWkw/ZgZzHfMMs89Y3zjEWGq8zbjVeMDE0iTWpNBkv8kDU4qpm2mK6WrTZtM+MyOziWZzzWrM7pqTzd3MBeZrzc+bv7ewtIi3WGxRb/HMUseSbZlvWWN534pm5WOVZVVldd2aaO1mnW69yfqqDWrjbCOwqbS5YovautgKbTfZto0mjHYfLRpdNfqWHdXOzy7XrsaucwxjTNiYwjH1Y16ONRubOHbl2PNjv9o722fY77C/56DhMMGh0KHR4Y2jjSPXsdLxuhPNKchpgVOD0+txtuP44zaPu+1Md57ovNi52fmLi6uLxKXWpcfVzDXJdaPrLTdNt0i3MrcL7gR3f/cF7k3uHz1cPHI8Dnq88rTzTPfc4/lsvOV4/vgd47u8TLw4Xtu8OryZ3kneW707fIx9OD5VPo98TX15vjt9n/pZ+6X57fV76W/vL/E/4v+e5cGaxzoZgAUEB5QEtAZqBMYGbgh8GGQSlBpUE9QX7Bw8J/hkCCEkNGRlyC22AZvLrmb3TXCdMG/CmVBqaHTohtBHYTZhkrDGiejECRNXTbwfbh4uCq+PABHsiFURDyItI7Mif5tEnBQ5qXLSkyiHqLlR56Pp0TOi90S/i/GPWR5zL9YqVhrbHKcaNzWuOu59fEB8eXzH5LGT502+nKCXIExoSCQlxiXuTOyfEjhlzZTuqc5Ti6fenGY5LW/axel60zOmH5uhOoMz41ASISk+aU/SZ04Ep4rTn8xO3pjcx2Vx13Jf8Hx5q3k9fC9+Of9pildKecqzVK/UVak9Ah9BhaBXyBJuEL5OC0nbkvY+PSJ9V/pgRnzG/kylzKTMoyINUbrozEzDmXkz28S24mJxR5ZH1pqsPkmoZGc2kj0tuyFHEz6yW6RW0p+knbneuZW5H2bFzTqUp54nymuZbTN76eyn+UH5v8zB53DnNM81nlswt3Oe37xt85H5yfObF5guKFrQvTB44e4CSkF6we+F9oXlhX8til/UWGRQtLCo66fgn2qKVYolxbcWey7esgRfIlzSutRp6fqlX0t4JZdK7UsrSj+Xccsu/ezw87qfB5elLGtd7rJ88wriCtGKmyt9Vu4uVy/PL+9aNXFV3Wrm6pLVf62ZseZixbiKLWspa6VrO9aFrWtYb7Z+xfrPGwQbblT6V+7fqL9x6cb3m3ib2jf7bq7dYrCldMunrcKtt7cFb6ursqiq2E7cnrv9yY64Hed/cfuleqfeztKdX3aJdnXsjtp9ptq1unqP/p7lNWiNtKZn79S9V/cF7Guotavdtp+xv/QAOCA98PzXpF9vHgw92HzI7VDtYfPDG4/Qj5TUIXWz6/rqBfUdDQkNbUcnHG1u9Gw88tuY33Y1GTdVHtM6tvw45XjR8cET+Sf6T4pP9p5KPdXVPKP53unJp6+fmXSm9Wzo2Qvngs6dPu93/sQFrwtNFz0uHr3kdqn+ssvluhbnliO/O/9+pNWlte6K65WGq+5XG9vGtx1v92k/dS3g2rnr7OuXb4TfaLsZe/P2ram3Om7zbj+7k3Hn9d3cuwP3Ft4n3C95oPag4qH+w6o/rP/Y3+HScawzoLPlUfSje13crhePsx9/7i56QntS8dToafUzx2dNPUE9V59Ped79QvxioLf4T/U/N760enn4le+rlr7Jfd2vJa8H35S91X27669xfzX3R/Y/fJf5buB9yQfdD7s/un08/yn+09OBWZ9Jn9d9sf7S+DX06/3BzMFBMUfCkT8FMFjQlBQA3uwCgJYAAP0qfD9MUfzN5IIo/pNyBP4TVvzf5OICQC1sZM9w1kkADsBi6QtjwyJ7jsf4AtTJaaQMSXaKk6MiFhX+cAgfBgffwncMqRGAL5LBwYFNg4NfdkCydwA4maX4E8pE9gfdKo/RzshbCH6QfwEOk3CtyyHjHgAAAAlwSFlzAAAWJQAAFiUBSVIk8AAADMxJREFUeAHtXXmMVdUZ/96+zJuFdQZxoCwCFYuUKApYiliX2pYICS4ttQqNwf5DaNEmtJYmtTFNU6vGBtu0SbV1aSuFSq1libSA2LBYJEFZisQZEGQZmO3t791+v/vmg8s4I8PM3HPvfZwvOXPPu+++e873/X7nO+d859w7vmw2a5AWbQGXW8Dv8vrp6mkLmBbQRNVE8IQFNFE9AZOupCaq5oAnLKCJ6gmYdCU1UTUHPGEBTVRPwKQrqYmqOeAJC2iiegImXUlNVM0BT1hAE9UTMOlKaqJqDnjCApqonoBJV1ITVXPAExbQRPUETLqSQa+YwDAMKhaLVCgUCPn2nEFHWg1qzUIDn1fUUFRPgyrDRFdW+qgi5COfz0eBQID8fr+ZV1SJfi3G1UQFIUHMs6k8rXyXaM0hH6ULRAXe6m0YIKcmaNdsOG8X5igFfAZFA3m6a4xBD19LVBMLmsQFgb0iPjfu8Ac5kZ7bXaDf7fVThsmpH0PoH0qBmpEA0aKJRVo8OWASFt7W7eIqoqJrz+fz9LPtBXrlgJ/yRbebz9v1C/IM5d5xRfr+1AAFg0FzaOBWjVxDVBB0a0OOlm72UzLvVnOVZ73iPAD85cwi3TQiZBLWjVo6TlSMQ3O5HH13U5E2Nvp0F+8QSzAk+FK9QU/e7KdQKOS6SZejREVXz2NkumM10bF27wzsHeKSkmKHVRj0z7lE4XDYVUMBx+Ko8KQg6S2rNEmVMLCHhcBhABNgA4zcIo4QFQZIp9N0KxvkVEp7UreQQeoBTIANMHILWZUTVTzpnNd8dEKTVLjhuiOwAUZu8axKiQqSYnb/1K4CNbRqT+o6dnaqEDACVsDMac+qjKhQFJOnY80Zev599weYO2F22X4EVsAM2DlJVqVERRhqwfqguQR62SLvMcWxXA3MgF3ZE1W86RYO6J9I6i7fY1w1MQN2TnpVJR4VREWLXLE9pAP6XmMp1xdBKmDnpFe1najiTQ835agprb2pB3lqVhnYAUOnvKoSomIn1OM7g9qbepWlXG94VWAILJ0Yq9pKVPGmiMXtbbK1KCUUuHqQn9bdEzdTVeTy6x2AIbB0wqvaunEaREU6zduhMgXecq5YEry7/ddfjtLnhvgpznlIOm/QgSaDFq9L0Yn2S1sijAZ9dNXAUoML2MzTH90UofuvCdHmxjwtfD1t1v0bE0O0gM8dOF2kJRtL58wvFP3JFHwmlsOjJVxVbry21c2JR11zyK+82/czkdbdG6dpwwOUCPMqWNKgw2eLBLJNrvXTRv5uRJWt6veJPtgrCh2ilhYxOO6j8dxQRlY7U280a2BZVh5VvCnGNG8eDfUJtN78+DtTwjQs4aMiW/crf07S/qbSLuwBUR+tmhejzzDY37shTEs2qPdMPdHn8bcy9NvdOTqZdNfucWD50OSC+WQAMFblVW3v+kHUo0n1RL2CSQppbDHOkRSfz6QN+uv+PHehQRpVXbrmsRkRenBSiHYcK9A9a1K4zJTn7ojSbaOC9Oq+HD26KSOnzeOS68M0b1zQHFLgUZltR/O0+I30ucWMtfPjNHGwn17am6MvjAjSsAqf6SHPcvmP8L1q2TsuuzFMNTzWRWNqbC3St9amzAcWUcAPpkfom9zNbztSoB9vzdB67gFEJg310wcPJ+iZnVl6aof5dKN8ZfvxaNLPEyr1wX/b+hCrR8XqhmrZ1MDsYRnBZPz25AvHx8/uytKNzydpzqvnSXmp9cP4sSllUENLkfhZObplZJCemBX9xG3u43FlgtvpwTMlzzgw5qNnb4vST74YoQKfgqdHNz+KPfzPZ3/y97hhE5P7V1znrUxayLG20uft3LBUC7CUmT8wViW2ERUKQBGMZ+AxVMuGw3l67WDefE51+bQw7XigglbeHqUpdf2zz2ApT2ZmvZSk2ZxWbCl527vYw9ay57TKG4fyNO2FdnP4sYA9JgTEXs1efcYfSueXvVkaflzDk76uBA3iF9uztOt4iZgnebyNz/C2qgVYOjFG7doy/ai9ylbXudog0wN/T5lebxB7sttHczc+N0abF1TQ9cP6RtjNjedJ8vJ7OWrjf9cFzzil9sL77j1VoGzHpTs+4qdrOxrtem5I8vDiwY7xc4wnel4QJzC1jahQRpJT5gcnQKibX0zSLE7L/53hjdqG+WKGF+fEaGofySqkAuFwXwgma90JSCq9pXWKJHkvPGYP7QRXlYS1jahWsBBmUS2Y8Q+v9JvPsINCGEu+wp5v5h/bzUkTvN8jPJnpL5EokpDuYvd1wCQXq1KPvncCS1RMCVHxpg7VsmpenLYsiNPXeTJjlTQ/iv3O8RKd6jviqIUONxftZQykkuO0dYmSKY+39ZSq1lp5J+8ElrCO7UTF+47GV14Y2lEBy9tHSwNDxEoRYhIZXeOnuTzpgezjFR7IEQ5hQSYMChC+h1zHwwIsFnQnj7I3lq56Ba8ihfhnLTxO/Q+PQ+2S9o5/W1sVsauEi98XWAJT1XIewX4uGYFgJCg1uy5FO5ti/VzCp9/usc0ZJp2Prh0aIMRDMdnBGBHeD90XPOtPt5UaEOKqS6eGqZpjmv+4O04pXmaV67oqBTPf+RNC9NWxvNGG81j5gjzNMU3ctyfSm67/3ROlhoXFij2LKujl9/L0xNtqnQCw9PtjJraqgv2wp+1NA8rMrONW2BtkeoJ4N9ck+W1/d69O0UMchEcg38/1wJvtPuKu+Te7sxxWaieZbbfxtff9LWWGf/gycza+8r9Z+tP7uS7vjvAQQk0N7IkjPFP/sNkgRBh+v6fr67u8SS9OvsPhqSc5LIVGF2CDnu6YwPXiVr36CTAElioJKhW17QUUiLXhobBMJkPNzc30tQ2DKFmwvV2IXvpogwXigSKtvfU0VVdXUyQSUfq+KtuYg1aHhK4fr4iZNbTVBtPpW6q0ADAElsBU8FVVvm1EhQJQCAlvils4psV8T6cqxXQ5/WsBzPaBobz1D7iqFNtKkxYHhfD+zep4mEZVqB34qzRkuZcF7IAhsASmgq8qvW0jqiggHhVjmuWf/Vh7VTGMh47wpsBO9bjUaiJbiSqtDq0QY5srqkI0pbrNWr7Oe8ACwAzYAUNgKbiqrLrtRIVHFa8ajUZp2YSTFPGX9+qNSgDtLgtYATNgZx2fgqwqxVaiQhEohFYIJfHOzZpEjH44roHjmqXVIJXK6rIuzQLACFgBM2AHDMWjXtqd+n61EqLCo0r3j5b5eX5m6c4hp/pee30HWy0AjIAVMJNuH1iq9qZQ0naiohDxqlAWA/JYLEaLxrbQjJoz+FqLCy0AbIARsAJmQlQnSArz2LbWb7U9lBOvii4EjzJg5Wrp+JP8LIZBb50daL1c5x22wIyaJsbmFCUS1aY3BWboEZ3ypjCHEqKiICEqNtuihYKoICwMUnMoR6+fqsVlWhy2wJ2DP6aFY5qZpAmTpBKSEqI6VT1lRIWCICsG5LJDXJR+kA1zdaKVnv5wNGUNJaMRKVofOywQ9vFLLUZ+QDfUFk2SVlRUnBubymzfSWMpJaoMAaC4VXB+eiBJ9bF99MKROtrZqocCVvvYnb+usonuv/I41Q+IUDxeySl+rssXkjo1NhXdbds9JQV0dYRHRbeP3VV4lxF2WKVSKTMlk0na0xSgvxwfRgdTVV39XJ/rJwtcFWuh+XXHaNLAgklOTJxk8uR0OKqzio4QFZWwklW2A+K/cAhpkd/fHKR/nR5A21qGUE4PCTpj16vPIe7ip1edpFmDztD46rzpOYWcCEPJmBSeFONSpz2pKOkYUVEBkFX2rcLDwruKhwVhJZ9M5+hAW5QOtMfof6lKaszE6Ww+ovx9VmI0rxyxdlQTzFB9JEljY600riJF4xJpikdLYUJ4TRATCXmZ3bulu7fa2VGioiJCVokCyHAAbzcGUXGUhO+QcG2WXzOSzAeInxphwqpdzrMa0I15/KNOvCIgHixQOHB+CRsERDxUEoiJPI7iQTHhdTIM1Z09L5zVdHeVjefRtUjoA3kJY4kBQUwQFUd4XSQQFQkk19K9BcSeYlPYWciKoySxv5u6+s5aOe5RrRUC8aweVryslaCaqFaLfXq+M1GthBVy4hwSrkVyqzjuUa2GEWOJgUFaGNRKXsnjCJGj9T46X1q2hh3ElnIUUspRbO52m7mKqGIsq/FgUJBREq6x5uU3+ti1Bay2lLwcu/6FO8+6kqhWU3U2qtWDWvPW3+h8yQKwnYg1L+e8dHQ9UTsb02pwa77zdfpzeVlAL6yXF55lq40matlCW16KaaKWF55lq40matlCW16KaaKWF55lq40matlCW16KaaKWF55lq42yOKqs0ZetJS8DxbBKiCVtJ0QJUbF7H0TV4n0LgKjYaK1a/g8ZMDj/cr4kRgAAAABJRU5ErkJggg==\" alt=\"图3-16\"></p> <p>是不是有质感了！之所以会这样，是因为<code>RaisedButton</code>默认有配置阴影：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">const</span> <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>elevation <span class=\"token operator\">=</span> <span class=\"token number\">2.0</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//正常状态下的阴影</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>highlightElevation <span class=\"token operator\">=</span> <span class=\"token number\">8.0</span><span class=\"token punctuation\">,</span><span class=\"token comment\">//按下时的阴影</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>disabledElevation <span class=\"token operator\">=</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span><span class=\"token comment\">// 禁用时的阴影</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>值得注意的是，在Material 组件库中，我们会在很多组件中见到elevation相关的属性，它们都是用来控制阴影的，这是因为阴影在Material设计风格中是一种很重要的表现形式，以后在介绍其它组件时，便不再赘述。</p> <p>如果我们想实现一个背景渐变的圆角按钮，按钮有没有相应的属性呢？答案是否定的，但是，我们可以通过其它方式来实现，我们将在后面&quot;自定义组件&quot;一章中实现。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/10.5f481ee1.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter3/flutter_widget_intro.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>3.1 Widget简介 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/23.87c6db58.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter3/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_3-1-widget简介\"><a href=\"#_3-1-widget简介\" class=\"header-anchor\">#</a> 3.1 Widget简介</h1> <h2 id=\"_3-1-1-概念\"><a href=\"#_3-1-1-概念\" class=\"header-anchor\">#</a> 3.1.1 概念</h2> <p>在前面的介绍中，我们知道在Flutter中几乎所有的对象都是一个Widget。与原生开发中“控件”不同的是，Flutter中的Widget的概念更广泛，它不仅可以表示UI元素，也可以表示一些功能性的组件如：用于手势检测的 <code>GestureDetector</code> widget、用于APP主题数据传递的<code>Theme</code>等等，而原生开发中的控件通常只是指UI元素。在后面的内容中，我们在描述UI元素时可能会用到“控件”、“组件”这样的概念，读者心里需要知道他们就是widget，只是在不同场景的不同表述而已。由于Flutter主要就是用于构建用户界面的，所以，在大多数时候，读者可以认为widget就是一个控件，不必纠结于概念。</p> <h2 id=\"_3-1-2-widget与element\"><a href=\"#_3-1-2-widget与element\" class=\"header-anchor\">#</a> 3.1.2 Widget与Element</h2> <p>在Flutter中，Widget的功能是“描述一个UI元素的配置数据”，它就是说，Widget其实并不是表示最终绘制在设备屏幕上的显示元素，而它只是描述显示元素的一个配置数据。</p> <p>实际上，Flutter中真正代表屏幕上显示元素的类是<code>Element</code>，也就是说Widget只是描述<code>Element</code>的配置数据！有关<code>Element</code>的详细介绍我们将在本书后面的高级部分深入介绍，现在，读者只需要知道：<strong>Widget只是UI元素的一个配置数据，并且一个Widget可以对应多个<code>Element</code></strong>。这是因为同一个Widget对象可以被添加到UI树的不同部分，而真正渲染时，UI树的每一个<code>Element</code>节点都会对应一个Widget对象。总结一下：</p> <ul><li>Widget实际上就是<code>Element</code>的配置数据，Widget树实际上是一个配置树，而真正的UI渲染树是由<code>Element</code>构成；不过，由于<code>Element</code>是通过Widget生成的，所以它们之间有对应关系，在大多数场景，我们可以宽泛地认为Widget树就是指UI控件树或UI渲染树。</li> <li>一个Widget对象可以对应多个<code>Element</code>对象。这很好理解，根据同一份配置（Widget），可以创建多个实例（Element）。</li></ul> <p>读者应该将这两点牢记在心中。</p> <h2 id=\"_3-1-3-widget主要接口\"><a href=\"#_3-1-3-widget主要接口\" class=\"header-anchor\">#</a> 3.1.3 Widget主要接口</h2> <p>我们先来看一下Widget类的声明：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token metadata symbol\">@immutable</span>\n<span class=\"token keyword\">abstract</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">Widget</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">DiagnosticableTree</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function\">Widget</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>key <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> Key key<span class=\"token punctuation\">;</span>\n    \n  <span class=\"token metadata symbol\">@protected</span>\n  Element <span class=\"token function\">createElement</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  String <span class=\"token function\">toStringShort</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> key <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">?</span> <span class=\"token string\">'$runtimeType'</span> <span class=\"token punctuation\">:</span> <span class=\"token string\">'$runtimeType-$key'</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">debugFillProperties</span><span class=\"token punctuation\">(</span>DiagnosticPropertiesBuilder properties<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">debugFillProperties</span><span class=\"token punctuation\">(</span>properties<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    properties<span class=\"token punctuation\">.</span>defaultDiagnosticsTreeStyle <span class=\"token operator\">=</span> DiagnosticsTreeStyle<span class=\"token punctuation\">.</span>dense<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  \n  <span class=\"token keyword\">static</span> bool <span class=\"token function\">canUpdate</span><span class=\"token punctuation\">(</span>Widget oldWidget<span class=\"token punctuation\">,</span> Widget newWidget<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> oldWidget<span class=\"token punctuation\">.</span>runtimeType <span class=\"token operator\">==</span> newWidget<span class=\"token punctuation\">.</span>runtimeType\n        <span class=\"token operator\">&amp;&amp;</span> oldWidget<span class=\"token punctuation\">.</span>key <span class=\"token operator\">==</span> newWidget<span class=\"token punctuation\">.</span>key<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><ul><li><code>Widget</code>类继承自<code>DiagnosticableTree</code>，<code>DiagnosticableTree</code>即“诊断树”，主要作用是提供调试信息。</li> <li><code>Key</code>: 这个<code>key</code>属性类似于React/Vue中的<code>key</code>，主要的作用是决定是否在下一次<code>build</code>时复用旧的widget，决定的条件在<code>canUpdate()</code>方法中。</li> <li><code>createElement()</code>：正如前文所述“一个Widget可以对应多个<code>Element</code>”；Flutter Framework在构建UI树时，会先调用此方法生成对应节点的<code>Element</code>对象。此方法是Flutter Framework隐式调用的，在我们开发过程中基本不会调用到。</li> <li><code>debugFillProperties(...)</code> 复写父类的方法，主要是设置诊断树的一些特性。</li> <li><code>canUpdate(...)</code>是一个静态方法，它主要用于在Widget树重新<code>build</code>时复用旧的widget，其实具体来说，应该是：是否用新的Widget对象去更新旧UI树上所对应的<code>Element</code>对象的配置；通过其源码我们可以看到，只要<code>newWidget</code>与<code>oldWidget</code>的<code>runtimeType</code>和<code>key</code>同时相等时就会用<code>newWidget</code>去更新<code>Element</code>对象的配置，否则就会创建新的<code>Element</code>。</li></ul> <p>有关Key和Widget复用的细节将会在本书后面高级部分深入讨论，读者现在只需知道，为Widget显式添加key的话可能（但不一定）会使UI在重新构建时变的高效，读者目前可以先忽略此参数。本书后面的示例中，只会在构建列表项UI时会显式指定Key。</p> <p>另外<code>Widget</code>类本身是一个抽象类，其中最核心的就是定义了<code>createElement()</code>接口，在Flutter开发中，我们一般都不用直接继承<code>Widget</code>类来实现一个新组件，相反，我们通常会通过继承<code>StatelessWidget</code>或<code>StatefulWidget</code>来间接继承<code>Widget</code>类来实现。<code>StatelessWidget</code>和<code>StatefulWidget</code>都是直接继承自<code>Widget</code>类，而这两个类也正是Flutter中非常重要的两个抽象类，它们引入了两种Widget模型，接下来我们将重点介绍一下这两个类。</p> <h2 id=\"_3-1-4-statelesswidget\"><a href=\"#_3-1-4-statelesswidget\" class=\"header-anchor\">#</a> 3.1.4 StatelessWidget</h2> <p>在之前的章节中，我们已经简单介绍过<code>StatelessWidget</code>，<code>StatelessWidget</code>相对比较简单，它继承自<code>Widget</code>类，重写了<code>createElement()</code>方法：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token metadata symbol\">@override</span>\nStatelessElement <span class=\"token function\">createElement</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">StatelessElement</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p><code>StatelessElement</code> 间接继承自<code>Element</code>类，与<code>StatelessWidget</code>相对应（作为其配置数据）。</p> <p><code>StatelessWidget</code>用于不需要维护状态的场景，它通常在<code>build</code>方法中通过嵌套其它Widget来构建UI，在构建过程中会递归的构建其嵌套的Widget。我们看一个简单的例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">Echo</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function\">Echo</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>  \n    <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>text<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>backgroundColor<span class=\"token punctuation\">:</span>Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span><span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span>key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    \n  <span class=\"token keyword\">final</span> String text<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> Color backgroundColor<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n        color<span class=\"token punctuation\">:</span> backgroundColor<span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>text<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面的代码，实现了一个回显字符串的<code>Echo</code> widget。</p> <blockquote><p>按照惯例，<code>widget</code>的构造函数参数应使用命名参数，命名参数中的必要参数要添加<code>@required</code>标注，这样有利于静态代码分析器进行检查。另外，在继承<code>widget</code>时，第一个参数通常应该是<code>Key</code>，另外，如果Widget需要接收子Widget，那么<code>child</code>或<code>children</code>参数通常应被放在参数列表的最后。同样是按照惯例，Widget的属性应尽可能的被声明为<code>final</code>，防止被意外改变。</p></blockquote> <p>然后我们可以通过如下方式使用它：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">Echo</span><span class=\"token punctuation\">(</span>text<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;hello world&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行后效果如图3-1所示：</p> <p><img src=\"/assets/img/3-1.587e85ad.png\" alt=\"图3-1\"></p> <h3 id=\"context\"><a href=\"#context\" class=\"header-anchor\">#</a> Context</h3> <p><code>build</code>方法有一个<code>context</code>参数，它是<code>BuildContext</code>类的一个实例，表示当前widget在widget树中的上下文，每一个widget都会对应一个context对象（因为每一个widget都是widget树上的一个节点）。实际上，<code>context</code>是当前widget在widget树中位置中执行”相关操作“的一个句柄，比如它提供了从当前widget开始向上遍历widget树以及按照widget类型查找父级widget的方法。下面是在子树中获取父级widget的一个示例：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">ContextRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n      appBar<span class=\"token punctuation\">:</span> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>\n        title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Context测试&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      body<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token comment\">// 在Widget树中向上查找最近的父级`Scaffold` widget</span>\n          Scaffold scaffold <span class=\"token operator\">=</span> context<span class=\"token punctuation\">.</span>findAncestorWidgetOfExactType<span class=\"token operator\">&lt;</span>Scaffold<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token comment\">// 直接返回 AppBar的title， 此处实际上是Text(&quot;Context测试&quot;)</span>\n          <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>scaffold<span class=\"token punctuation\">.</span>appBar <span class=\"token operator\">as</span> AppBar<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>title<span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行后效果如图3-1-1所示：</p> <p><img src=\"/assets/img/3-1-1.63daca67.png\" alt=\"图3-1-1\"></p> <blockquote><p><strong>注意</strong>：对于<code>BuildContext</code>读者现在可以先作了解，随着本书后面内容的展开，也会用到Context的一些方法，读者可以通过具体的场景对其有个直观的认识。关于<code>BuildContext</code>更多的内容，我们也将在后面高级部分再深入介绍。</p></blockquote> <h2 id=\"_3-1-5-statefulwidget\"><a href=\"#_3-1-5-statefulwidget\" class=\"header-anchor\">#</a> 3.1.5 StatefulWidget</h2> <p>和<code>StatelessWidget</code>一样，<code>StatefulWidget</code>也是继承自<code>Widget</code>类，并重写了<code>createElement()</code>方法，不同的是返回的<code>Element</code> 对象并不相同；另外<code>StatefulWidget</code>类中添加了一个新的接口<code>createState()</code>。</p> <p>下面我们看看<code>StatefulWidget</code>的类定义：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">abstract</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">Widget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function\">StatefulWidget</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> Key key <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    \n  <span class=\"token metadata symbol\">@override</span>\n  StatefulElement <span class=\"token function\">createElement</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">StatefulElement</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    \n  <span class=\"token metadata symbol\">@protected</span>\n  State <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><ul><li><p><code>StatefulElement</code> 间接继承自<code>Element</code>类，与StatefulWidget相对应（作为其配置数据）。<code>StatefulElement</code>中可能会多次调用<code>createState()</code>来创建状态(State)对象。</p></li> <li><p><code>createState()</code> 用于创建和Stateful widget相关的状态，它在Stateful widget的生命周期中可能会被多次调用。例如，当一个Stateful widget同时插入到widget树的多个位置时，Flutter framework就会调用该方法为每一个位置生成一个独立的State实例，其实，本质上就是一个<code>StatefulElement</code>对应一个State实例。</p> <blockquote><p>在本书中经常会出现“树”的概念，在不同的场景可能指不同的意思，在说“widget树”时它可以指widget结构树，但由于widget与Element有对应关系（一可能对多），在有些场景（Flutter的SDK文档中）也代指“UI树”的意思。而在stateful widget中，State对象也和<code>StatefulElement</code>具有对应关系（一对一），所以在Flutter的SDK文档中，可以经常看到“从树中移除State对象”或“插入State对象到树中”这样的描述。其实，无论哪种描述，其意思都是在描述“一棵构成用户界面的节点元素的树”，读者不必纠结于这些概念，还是那句话“得其神，忘其形”，因此，本书中出现的各种“树”，如果没有特别说明，读者都可抽象的认为它是“一棵构成用户界面的节点元素的树”。</p></blockquote></li></ul> <h2 id=\"_3-1-6-state\"><a href=\"#_3-1-6-state\" class=\"header-anchor\">#</a> 3.1.6 State</h2> <p>一个StatefulWidget类会对应一个State类，State表示与其对应的StatefulWidget要维护的状态，State中的保存的状态信息可以：</p> <ol><li>在widget 构建时可以被同步读取。</li> <li>在widget生命周期中可以被改变，当State被改变时，可以手动调用其<code>setState()</code>方法通知Flutter framework状态发生改变，Flutter framework在收到消息后，会重新调用其<code>build</code>方法重新构建widget树，从而达到更新UI的目的。</li></ol> <p>State中有两个常用属性：</p> <ol><li><p><code>widget</code>，它表示与该State实例关联的widget实例，由Flutter framework动态设置。注意，这种关联并非永久的，因为在应用生命周期中，UI树上的某一个节点的widget实例在重新构建时可能会变化，但State实例只会在第一次插入到树中时被创建，当在重新构建时，如果widget被修改了，Flutter framework会动态设置State.widget为新的widget实例。</p></li> <li><p><code>context</code>。StatefulWidget对应的BuildContext，作用同StatelessWidget的BuildContext。</p></li></ol> <h4 id=\"state生命周期\"><a href=\"#state生命周期\" class=\"header-anchor\">#</a> State生命周期</h4> <p>理解State的生命周期对flutter开发非常重要，为了加深读者印象，本节我们通过一个实例来演示一下State的生命周期。在接下来的示例中，我们实现一个计数器widget，点击它可以使计数器加1，由于要保存计数器的数值状态，所以我们应继承StatefulWidget，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">CounterWidget</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function\">CounterWidget</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>initValue<span class=\"token punctuation\">:</span> <span class=\"token number\">0</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> int initValue<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  _CounterWidgetState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_CounterWidgetState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p><code>CounterWidget</code>接收一个<code>initValue</code>整型参数，它表示计数器的初始值。下面我们看一下State的代码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">_CounterWidgetState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>CounterWidget<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>  \n  int _counter<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//初始化状态  </span>\n    _counter<span class=\"token operator\">=</span>widget<span class=\"token punctuation\">.</span>initValue<span class=\"token punctuation\">;</span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;initState&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;build&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n      body<span class=\"token punctuation\">:</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'$_counter'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token comment\">//点击后计数器自增</span>\n          onPressed<span class=\"token punctuation\">:</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span><span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token operator\">++</span>_counter<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">didUpdateWidget</span><span class=\"token punctuation\">(</span>CounterWidget oldWidget<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">didUpdateWidget</span><span class=\"token punctuation\">(</span>oldWidget<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;didUpdateWidget&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">deactivate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">deactivate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;deactive&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;dispose&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">reassemble</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">reassemble</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;reassemble&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">didChangeDependencies</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">didChangeDependencies</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;didChangeDependencies&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>接下来，我们创建一个新路由，在新路由中，我们只显示一个<code>CounterWidget</code>：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">CounterWidget</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们运行应用并打开该路由页面，在新路由页打开后，屏幕中央就会出现一个数字0，然后控制台日志输出：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>I/flutter ( 5436): initState\nI/flutter ( 5436): didChangeDependencies\nI/flutter ( 5436): build\n</code></pre></div><p>可以看到，在StatefulWidget插入到Widget树时首先<code>initState</code>方法会被调用。</p> <p>然后我们点击⚡️按钮热重载，控制台输出日志如下：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>I/flutter ( 5436): reassemble\nI/flutter ( 5436): didUpdateWidget\nI/flutter ( 5436): build\n</code></pre></div><p>可以看到此时<code>initState</code> 和<code>didChangeDependencies</code>都没有被调用，而此时<code>didUpdateWidget</code>被调用。</p> <p>接下来，我们在widget树中移除<code>CounterWidget</code>，将路由<code>build</code>方法改为：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">//移除计数器 </span>\n  <span class=\"token comment\">//return CounterWidget();</span>\n  <span class=\"token comment\">//随便返回一个Text()</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;xxx&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>然后热重载，日志如下：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>I/flutter ( 5436): reassemble\nI/flutter ( 5436): deactive\nI/flutter ( 5436): dispose\n</code></pre></div><p>我们可以看到，在<code>CounterWidget</code>从widget树中移除时，<code>deactive</code>和<code>dispose</code>会依次被调用。</p> <p>下面我们来看看各个回调函数：</p> <ul><li><p><code>initState</code>：当Widget第一次插入到Widget树时会被调用，对于每一个State对象，Flutter framework只会调用一次该回调，所以，通常在该回调中做一些一次性的操作，如状态初始化、订阅子树的事件通知等。不能在该回调中调用<code>BuildContext.dependOnInheritedWidgetOfExactType</code>（该方法用于在Widget树上获取离当前widget最近的一个父级<code>InheritFromWidget</code>，关于<code>InheritedWidget</code>我们将在后面章节介绍），原因是在初始化完成后，Widget树中的<code>InheritFromWidget</code>也可能会发生变化，所以正确的做法应该在在<code>build（）</code>方法或<code>didChangeDependencies()</code>中调用它。</p></li> <li><p><code>didChangeDependencies()</code>：当State对象的依赖发生变化时会被调用；例如：在之前<code>build()</code> 中包含了一个<code>InheritedWidget</code>，然后在之后的<code>build()</code> 中<code>InheritedWidget</code>发生了变化，那么此时<code>InheritedWidget</code>的子widget的<code>didChangeDependencies()</code>回调都会被调用。典型的场景是当系统语言Locale或应用主题改变时，Flutter framework会通知widget调用此回调。</p></li> <li><p><code>build()</code>：此回调读者现在应该已经相当熟悉了，它主要是用于构建Widget子树的，会在如下场景被调用：</p> <ol><li>在调用<code>initState()</code>之后。</li> <li>在调用<code>didUpdateWidget()</code>之后。</li> <li>在调用<code>setState()</code>之后。</li> <li>在调用<code>didChangeDependencies()</code>之后。</li> <li>在State对象从树中一个位置移除后（会调用deactivate）又重新插入到树的其它位置之后。</li></ol></li> <li><p><code>reassemble()</code>：此回调是专门为了开发调试而提供的，在热重载(hot reload)时会被调用，此回调在Release模式下永远不会被调用。</p></li> <li><p><code>didUpdateWidget()</code>：在widget重新构建时，Flutter framework会调用<code>Widget.canUpdate</code>来检测Widget树中同一位置的新旧节点，然后决定是否需要更新，如果<code>Widget.canUpdate</code>返回<code>true</code>则会调用此回调。正如之前所述，<code>Widget.canUpdate</code>会在新旧widget的key和runtimeType同时相等时会返回true，也就是说在在新旧widget的key和runtimeType同时相等时<code>didUpdateWidget()</code>就会被调用。</p></li> <li><p><code>deactivate()</code>：当State对象从树中被移除时，会调用此回调。在一些场景下，Flutter framework会将State对象重新插到树中，如包含此State对象的子树在树的一个位置移动到另一个位置时（可以通过GlobalKey来实现）。如果移除后没有重新插入到树中则紧接着会调用<code>dispose()</code>方法。</p></li> <li><p><code>dispose()</code>：当State对象从树中被永久移除时调用；通常在此回调中释放资源。</p></li></ul> <p>StatefulWidget生命周期如图3-2所示：</p> <p><img src=\"/assets/img/3-2.a59bef97.jpg\" alt=\"图3-2\"></p> <blockquote><p><strong>注意</strong>：在继承<code>StatefulWidget</code>重写其方法时，对于包含<code>@mustCallSuper</code>标注的父类方法，都要在子类方法中先调用父类方法。</p></blockquote> <h3 id=\"为什么要将build方法放在state中-而不是放在statefulwidget中\"><a href=\"#为什么要将build方法放在state中-而不是放在statefulwidget中\" class=\"header-anchor\">#</a> 为什么要将build方法放在State中，而不是放在StatefulWidget中？</h3> <p>现在，我们回答之前提出的问题，为什么<code>build()</code>方法放在State（而不是<code>StatefulWidget</code>）中 ？这主要是为了提高开发的灵活性。如果将<code>build()</code>方法在<code>StatefulWidget</code>中则会有两个问题：</p> <ul><li><p>状态访问不便。</p> <p>试想一下，如果我们的<code>StatefulWidget</code>有很多状态，而每次状态改变都要调用<code>build</code>方法，由于状态是保存在State中的，如果<code>build</code>方法在<code>StatefulWidget</code>中，那么<code>build</code>方法和状态分别在两个类中，那么构建时读取状态将会很不方便！试想一下，如果真的将<code>build</code>方法放在StatefulWidget中的话，由于构建用户界面过程需要依赖State，所以<code>build</code>方法将必须加一个<code>State</code>参数，大概是下面这样：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> State state<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">//state.counter</span>\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token punctuation\">}</span>\n</code></pre></div><p>这样的话就只能将State的所有状态声明为公开的状态，这样才能在State类外部访问状态！但是，将状态设置为公开后，状态将不再具有私密性，这就会导致对状态的修改将会变的不可控。但如果将<code>build()</code>方法放在State中的话，构建过程不仅可以直接访问状态，而且也无需公开私有状态，这会非常方便。</p></li> <li><p>继承<code>StatefulWidget</code>不便。</p> <p>例如，Flutter中有一个动画widget的基类<code>AnimatedWidget</code>，它继承自<code>StatefulWidget</code>类。<code>AnimatedWidget</code>中引入了一个抽象方法<code>build(BuildContext context)</code>，继承自<code>AnimatedWidget</code>的动画widget都要实现这个<code>build</code>方法。现在设想一下，如果<code>StatefulWidget</code> 类中已经有了一个<code>build</code>方法，正如上面所述，此时<code>build</code>方法需要接收一个state对象，这就意味着<code>AnimatedWidget</code>必须将自己的State对象(记为_animatedWidgetState)提供给其子类，因为子类需要在其<code>build</code>方法中调用父类的<code>build</code>方法，代码可能如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">MyAnimationWidget</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">AnimatedWidget</span><span class=\"token punctuation\">{</span>\n    <span class=\"token metadata symbol\">@override</span>\n    Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> State state<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">//由于子类要用到AnimatedWidget的状态对象_animatedWidgetState，</span>\n      <span class=\"token comment\">//所以AnimatedWidget必须通过某种方式将其状态对象_animatedWidgetState</span>\n      <span class=\"token comment\">//暴露给其子类   </span>\n      <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">build</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> _animatedWidgetState<span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>这样很显然是不合理的，因为</p> <ol><li><code>AnimatedWidget</code>的状态对象是<code>AnimatedWidget</code>内部实现细节，不应该暴露给外部。</li> <li>如果要将父类状态暴露给子类，那么必须得有一种传递机制，而做这一套传递机制是无意义的，因为父子类之间状态的传递和子类本身逻辑是无关的。</li></ol></li></ul> <p>综上所述，可以发现，对于<code>StatefulWidget</code>，将<code>build</code>方法放在State中，可以给开发带来很大的灵活性。</p> <h2 id=\"_3-1-7-在widget树中获取state对象\"><a href=\"#_3-1-7-在widget树中获取state对象\" class=\"header-anchor\">#</a> 3.1.7 在Widget树中获取State对象</h2> <p>由于StatefulWidget的的具体逻辑都在其State中，所以很多时候，我们需要获取StatefulWidget对应的State对象来调用一些方法，比如<code>Scaffold</code>组件对应的状态类<code>ScaffoldState</code>中就定义了打开SnackBar(路由页底部提示条)的方法。我们有两种方法在子widget树中获取父级StatefulWidget的State对象。</p> <h3 id=\"通过context获取\"><a href=\"#通过context获取\" class=\"header-anchor\">#</a> 通过Context获取</h3> <p><code>context</code>对象有一个<code>findAncestorStateOfType()</code>方法，该方法可以从当前节点沿着widget树向上查找指定类型的StatefulWidget对应的State对象。下面是实现打开SnackBar的示例：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n  appBar<span class=\"token punctuation\">:</span> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>\n    title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;子树中获取State对象&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  body<span class=\"token punctuation\">:</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n        onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token comment\">// 查找父级最近的Scaffold对应的ScaffoldState对象</span>\n          ScaffoldState _state <span class=\"token operator\">=</span> context<span class=\"token punctuation\">.</span>findAncestorStateOfType<span class=\"token operator\">&lt;</span>ScaffoldState<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token comment\">//调用ScaffoldState的showSnackBar来弹出SnackBar</span>\n          _state<span class=\"token punctuation\">.</span><span class=\"token function\">showSnackBar</span><span class=\"token punctuation\">(</span>\n            <span class=\"token function\">SnackBar</span><span class=\"token punctuation\">(</span>\n              content<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;我是SnackBar&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;显示SnackBar&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>上面示例运行后，点击”显示SnackBar“，效果如图3-1-2所示：</p> <p><img src=\"/assets/img/3-1-2.70506d6d.png\" alt=\"图3-1-2\"></p> <p>一般来说，如果StatefulWidget的状态是私有的（不应该向外部暴露），那么我们代码中就不应该去直接获取其State对象；如果StatefulWidget的状态是希望暴露出的（通常还有一些组件的操作方法），我们则可以去直接获取其State对象。但是通过<code>context.findAncestorStateOfType</code>获取StatefulWidget的状态的方法是通用的，我们并不能在语法层面指定StatefulWidget的状态是否私有，所以在Flutter开发中便有了一个默认的约定：如果StatefulWidget的状态是希望暴露出的，应当在StatefulWidget中提供一个<code>of</code>静态方法来获取其State对象，开发者便可直接通过该方法来获取；如果State不希望暴露，则不提供<code>of</code>方法。这个约定在Flutter SDK里随处可见。所以，上面示例中的<code>Scaffold</code>也提供了一个<code>of</code>方法，我们其实是可以直接调用它的：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token comment\">//省略无关代码</span>\n<span class=\"token comment\">// 直接通过of静态方法来获取ScaffoldState </span>\nScaffoldState _state<span class=\"token operator\">=</span>Scaffold<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> \n_state<span class=\"token punctuation\">.</span><span class=\"token function\">showSnackBar</span><span class=\"token punctuation\">(</span>\n  <span class=\"token function\">SnackBar</span><span class=\"token punctuation\">(</span>\n    content<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;我是SnackBar&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><h3 id=\"通过globalkey\"><a href=\"#通过globalkey\" class=\"header-anchor\">#</a> 通过GlobalKey</h3> <p>Flutter还有一种通用的获取<code>State</code>对象的方法——通过GlobalKey来获取！ 步骤分两步：</p> <ol><li><p>给目标<code>StatefulWidget</code>添加<code>GlobalKey</code>。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//定义一个globalKey, 由于GlobalKey要保持全局唯一性，我们使用静态变量存储</span>\n<span class=\"token keyword\">static</span> GlobalKey<span class=\"token operator\">&lt;</span>ScaffoldState<span class=\"token operator\">&gt;</span> _globalKey<span class=\"token operator\">=</span> <span class=\"token function\">GlobalKey</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n    key<span class=\"token punctuation\">:</span> _globalKey <span class=\"token punctuation\">,</span> <span class=\"token comment\">//设置key</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>  \n<span class=\"token punctuation\">)</span>\n</code></pre></div></li> <li><p>通过<code>GlobalKey</code>来获取<code>State</code>对象</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>_globalKey<span class=\"token punctuation\">.</span>currentState<span class=\"token punctuation\">.</span><span class=\"token function\">openDrawer</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n</code></pre></div></li></ol> <p>GlobalKey是Flutter提供的一种在整个APP中引用element的机制。如果一个widget设置了<code>GlobalKey</code>，那么我们便可以通过<code>globalKey.currentWidget</code>获得该widget对象、<code>globalKey.currentElement</code>来获得widget对应的element对象，如果当前widget是<code>StatefulWidget</code>，则可以通过<code>globalKey.currentState</code>来获得该widget对应的state对象。</p> <blockquote><p>注意：使用GlobalKey开销较大，如果有其他可选方案，应尽量避免使用它。另外同一个GlobalKey在整个widget树中必须是唯一的，不能重复。</p></blockquote> <h2 id=\"_3-1-8-flutter-sdk内置组件库介绍\"><a href=\"#_3-1-8-flutter-sdk内置组件库介绍\" class=\"header-anchor\">#</a> 3.1.8 Flutter SDK内置组件库介绍</h2> <p>Flutter提供了一套丰富、强大的基础组件，在基础组件库之上Flutter又提供了一套Material风格（Android默认的视觉风格）和一套Cupertino风格（iOS视觉风格）的组件库。要使用基础组件库，需要先导入：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/widgets.dart'</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>下面我们介绍一下常用的组件。</p> <h4 id=\"基础组件\"><a href=\"#基础组件\" class=\"header-anchor\">#</a> 基础组件</h4> <ul><li><a href=\"https://docs.flutter.io/flutter/widgets/Text-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>Text</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>：该组件可让您创建一个带格式的文本。</li> <li><a href=\"https://docs.flutter.io/flutter/widgets/Row-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>Row</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>、 <a href=\"https://docs.flutter.io/flutter/widgets/Column-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>Column</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>： 这些具有弹性空间的布局类Widget可让您在水平（Row）和垂直（Column）方向上创建灵活的布局。其设计是基于Web开发中的Flexbox布局模型。</li> <li><a href=\"https://docs.flutter.io/flutter/widgets/Stack-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>Stack</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>： 取代线性布局 (译者语：和Android中的<code>FrameLayout</code>相似)，<a href=\"https://docs.flutter.io/flutter/widgets/Stack-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>Stack</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>允许子 widget 堆叠， 你可以使用 <a href=\"https://docs.flutter.io/flutter/widgets/Positioned-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>Positioned</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 来定位他们相对于<code>Stack</code>的上下左右四条边的位置。Stacks是基于Web开发中的绝对定位（absolute positioning )布局模型设计的。</li> <li><a href=\"https://docs.flutter.io/flutter/widgets/Container-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>Container</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>： <a href=\"https://docs.flutter.io/flutter/widgets/Container-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>Container</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 可让您创建矩形视觉元素。container 可以装饰一个<a href=\"https://docs.flutter.io/flutter/painting/BoxDecoration-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>BoxDecoration</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>, 如 background、一个边框、或者一个阴影。 <a href=\"https://docs.flutter.io/flutter/widgets/Container-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>Container</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 也可以具有边距（margins）、填充(padding)和应用于其大小的约束(constraints)。另外， <a href=\"https://docs.flutter.io/flutter/widgets/Container-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>Container</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>可以使用矩阵在三维空间中对其进行变换。</li></ul> <h4 id=\"material组件\"><a href=\"#material组件\" class=\"header-anchor\">#</a> Material组件</h4> <p>Flutter提供了一套丰富的Material组件，它可以帮助我们构建遵循Material Design设计规范的应用程序。Material应用程序以<a href=\"https://docs.flutter.io/flutter/material/MaterialApp-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>MaterialApp</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 组件开始， 该组件在应用程序的根部创建了一些必要的组件，比如<code>Theme</code>组件，它用于配置应用的主题。 是否使用<a href=\"https://docs.flutter.io/flutter/material/MaterialApp-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>MaterialApp</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>完全是可选的，但是使用它是一个很好的做法。在之前的示例中，我们已经使用过多个Material 组件了，如：<code>Scaffold</code>、<code>AppBar</code>、<code>FlatButton</code>等。要使用Material 组件，需要先引入它：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n</code></pre></div><h4 id=\"cupertino组件\"><a href=\"#cupertino组件\" class=\"header-anchor\">#</a> Cupertino组件</h4> <p>Flutter也提供了一套丰富的Cupertino风格的组件，尽管目前还没有Material 组件那么丰富，但是它仍在不断的完善中。值得一提的是在Material 组件库中有一些组件可以根据实际运行平台来切换表现风格，比如<code>MaterialPageRoute</code>，在路由切换时，如果是Android系统，它将会使用Android系统默认的页面切换动画(从底向上)；如果是iOS系统，它会使用iOS系统默认的页面切换动画（从右向左）。由于在前面的示例中还没有Cupertino组件的示例，下面我们实现一个简单的Cupertino组件风格的页面：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//导入cupertino widget库</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/cupertino.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">CupertinoTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">CupertinoPageScaffold</span><span class=\"token punctuation\">(</span>\n      navigationBar<span class=\"token punctuation\">:</span> <span class=\"token function\">CupertinoNavigationBar</span><span class=\"token punctuation\">(</span>\n        middle<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Cupertino Demo&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">CupertinoButton</span><span class=\"token punctuation\">(</span>\n            color<span class=\"token punctuation\">:</span> CupertinoColors<span class=\"token punctuation\">.</span>activeBlue<span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Press&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>下面（图3-3）是在iPhoneX上页面效果截图：</p> <p><img src=\"/assets/img/3-3.3b5b2dd3.png\" alt=\"图3-3\"></p> <h3 id=\"关于示例\"><a href=\"#关于示例\" class=\"header-anchor\">#</a> 关于示例</h3> <p>本章后面章节的示例中会使用一些布局类组件，如<code>Scaffold</code>、<code>Row</code>、<code>Column</code>等，这些组件将在后面“布局类组件”一章中详细介绍，读者可以先不用关注。</p> <h3 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h3> <p>Flutter提供了丰富的组件，在实际的开发中你可以根据需要随意使用它们，而不必担心引入过多组件库会让你的应用安装包变大，这不是web开发，dart在编译时只会编译你使用了的代码。由于Material和Cupertino都是在基础组件库之上的，所以如果我们的应用中引入了这两者之一，则不需要再引入<code>flutter/widgets.dart</code>了，因为它们内部已经引入过了。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/23.87c6db58.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter3/img_and_icon.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>3.5 图片及ICON | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/17.59a459c4.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter3/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h2 id=\"_3-5-图片及icon\"><a href=\"#_3-5-图片及icon\" class=\"header-anchor\">#</a> 3.5 图片及ICON</h2> <h2 id=\"_3-5-1-图片\"><a href=\"#_3-5-1-图片\" class=\"header-anchor\">#</a> 3.5.1 图片</h2> <p>Flutter中，我们可以通过<code>Image</code>组件来加载并显示图片，<code>Image</code>的数据源可以是asset、文件、内存以及网络。</p> <h3 id=\"imageprovider\"><a href=\"#imageprovider\" class=\"header-anchor\">#</a> ImageProvider</h3> <p><code>ImageProvider</code> 是一个抽象类，主要定义了图片数据获取的接口<code>load()</code>，从不同的数据源获取图片需要实现不同的<code>ImageProvider</code> ，如<code>AssetImage</code>是实现了从Asset中加载图片的ImageProvider，而<code>NetworkImage</code>实现了从网络加载图片的ImageProvider。</p> <h3 id=\"image\"><a href=\"#image\" class=\"header-anchor\">#</a> Image</h3> <p><code>Image</code> widget有一个必选的<code>image</code>参数，它对应一个ImageProvider。下面我们分别演示一下如何从asset和网络加载图片。</p> <h4 id=\"从asset中加载图片\"><a href=\"#从asset中加载图片\" class=\"header-anchor\">#</a> 从asset中加载图片</h4> <ol><li><p>在工程根目录下创建一个<code>images目录</code>，并将图片avatar.png拷贝到该目录。</p></li> <li><p>在<code>pubspec.yaml</code>中的<code>flutter</code>部分添加如下内容：</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code>  <span class=\"token key atrule\">assets</span><span class=\"token punctuation\">:</span>\n    <span class=\"token punctuation\">-</span> images/avatar.png\n</code></pre></div></li></ol> <blockquote><p>注意: 由于 yaml 文件对缩进严格，所以必须严格按照每一层两个空格的方式进行缩进，此处assets前面应有两个空格。</p></blockquote> <ol start=\"3\"><li><p>加载该图片</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Image</span><span class=\"token punctuation\">(</span>\n  image<span class=\"token punctuation\">:</span> <span class=\"token function\">AssetImage</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;images/avatar.png&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  width<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>Image也提供了一个快捷的构造函数<code>Image.asset</code>用于从asset中加载、显示图片：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Image<span class=\"token punctuation\">.</span><span class=\"token function\">asset</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;images/avatar.png&quot;</span><span class=\"token punctuation\">,</span>\n  width<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div></li></ol> <h4 id=\"从网络加载图片\"><a href=\"#从网络加载图片\" class=\"header-anchor\">#</a> 从网络加载图片</h4> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Image</span><span class=\"token punctuation\">(</span>\n  image<span class=\"token punctuation\">:</span> <span class=\"token function\">NetworkImage</span><span class=\"token punctuation\">(</span>\n      <span class=\"token string\">&quot;https://avatars2.githubusercontent.com/u/20411648?s=460&amp;v=4&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  width<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>Image也提供了一个快捷的构造函数<code>Image.network</code>用于从网络加载、显示图片：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Image<span class=\"token punctuation\">.</span><span class=\"token function\">network</span><span class=\"token punctuation\">(</span>\n  <span class=\"token string\">&quot;https://avatars2.githubusercontent.com/u/20411648?s=460&amp;v=4&quot;</span><span class=\"token punctuation\">,</span>\n  width<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行上面两个示例，图片加载成功后如图3-17所示：</p> <p><img src=\"/assets/img/3-17.a063365a.png\" alt=\"图3-17\"></p> <h4 id=\"参数\"><a href=\"#参数\" class=\"header-anchor\">#</a> 参数</h4> <p><code>Image</code>在显示图片时定义了一系列参数，通过这些参数我们可以控制图片的显示外观、大小、混合效果等。我们看一下Image的主要参数：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">const</span> <span class=\"token function\">Image</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>width<span class=\"token punctuation\">,</span> <span class=\"token comment\">//图片的宽</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>height<span class=\"token punctuation\">,</span> <span class=\"token comment\">//图片高度</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>color<span class=\"token punctuation\">,</span> <span class=\"token comment\">//图片的混合色值</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>colorBlendMode<span class=\"token punctuation\">,</span> <span class=\"token comment\">//混合模式</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>fit<span class=\"token punctuation\">,</span><span class=\"token comment\">//缩放模式</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>alignment <span class=\"token operator\">=</span> Alignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span> <span class=\"token comment\">//对齐方式</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>repeat <span class=\"token operator\">=</span> ImageRepeat<span class=\"token punctuation\">.</span>noRepeat<span class=\"token punctuation\">,</span> <span class=\"token comment\">//重复方式</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><ul><li><p><code>width</code>、<code>height</code>：用于设置图片的宽、高，当不指定宽高时，图片会根据当前父容器的限制，尽可能的显示其原始大小，如果只设置<code>width</code>、<code>height</code>的其中一个，那么另一个属性默认会按比例缩放，但可以通过下面介绍的<code>fit</code>属性来指定适应规则。</p></li> <li><p><code>fit</code>：该属性用于在图片的显示空间和图片本身大小不同时指定图片的适应模式。适应模式是在<code>BoxFit</code>中定义，它是一个枚举类型，有如下值：</p> <ul><li><code>fill</code>：会拉伸填充满显示空间，图片本身长宽比会发生变化，图片会变形。</li> <li><code>cover</code>：会按图片的长宽比放大后居中填满显示空间，图片不会变形，超出显示空间部分会被剪裁。</li> <li><code>contain</code>：这是图片的默认适应规则，图片会在保证图片本身长宽比不变的情况下缩放以适应当前显示空间，图片不会变形。</li> <li><code>fitWidth</code>：图片的宽度会缩放到显示空间的宽度，高度会按比例缩放，然后居中显示，图片不会变形，超出显示空间部分会被剪裁。</li> <li><code>fitHeight</code>：图片的高度会缩放到显示空间的高度，宽度会按比例缩放，然后居中显示，图片不会变形，超出显示空间部分会被剪裁。</li> <li><code>none</code>：图片没有适应策略，会在显示空间内显示图片，如果图片比显示空间大，则显示空间只会显示图片中间部分。</li></ul> <p>一图胜万言！ 我们对一个宽高相同的头像图片应用不同的<code>fit</code>值，效果如图3-18所示：</p> <p><img src=\"/assets/img/3-18.cb8a4b0f.png\" alt=\"图3-18\"></p></li> <li><p><code>color</code>和 <code>colorBlendMode</code>：在图片绘制时可以对每一个像素进行颜色混合处理，<code>color</code>指定混合色，而<code>colorBlendMode</code>指定混合模式，下面是一个简单的示例：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Image</span><span class=\"token punctuation\">(</span>\n  image<span class=\"token punctuation\">:</span> <span class=\"token function\">AssetImage</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;images/avatar.png&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  width<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n  color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span>\n  colorBlendMode<span class=\"token punctuation\">:</span> BlendMode<span class=\"token punctuation\">.</span>difference<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div></li></ul> <p>运行效果如图3-19所示（彩色）:</p> <p><img src=\"/assets/img/3-19.feb336de.png\" alt=\"图3-19\"></p> <ul><li><p><code>repeat</code>：当图片本身大小小于显示空间时，指定图片的重复规则。简单示例如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Image</span><span class=\"token punctuation\">(</span>\n  image<span class=\"token punctuation\">:</span> <span class=\"token function\">AssetImage</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;images/avatar.png&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  width<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n  height<span class=\"token punctuation\">:</span> <span class=\"token number\">200.0</span><span class=\"token punctuation\">,</span>\n  repeat<span class=\"token punctuation\">:</span> ImageRepeat<span class=\"token punctuation\">.</span>repeatY <span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行后效果如图3-20所示：</p> <p><img src=\"/assets/img/3-20.9c7569a6.png\" alt=\"图3-20\"></p></li></ul> <p>完整的示例代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">ImageAndIconRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">var</span> img<span class=\"token operator\">=</span><span class=\"token function\">AssetImage</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;imgs/avatar.png&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">SingleChildScrollView</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n        children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Image<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n          <span class=\"token function\">Image</span><span class=\"token punctuation\">(</span>\n            image<span class=\"token punctuation\">:</span> img<span class=\"token punctuation\">,</span>\n            height<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n            width<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n            fit<span class=\"token punctuation\">:</span> BoxFit<span class=\"token punctuation\">.</span>fill<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">Image</span><span class=\"token punctuation\">(</span>\n            image<span class=\"token punctuation\">:</span> img<span class=\"token punctuation\">,</span>\n            height<span class=\"token punctuation\">:</span> <span class=\"token number\">50</span><span class=\"token punctuation\">,</span>\n            width<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n            fit<span class=\"token punctuation\">:</span> BoxFit<span class=\"token punctuation\">.</span>contain<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">Image</span><span class=\"token punctuation\">(</span>\n            image<span class=\"token punctuation\">:</span> img<span class=\"token punctuation\">,</span>\n            width<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n            height<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n            fit<span class=\"token punctuation\">:</span> BoxFit<span class=\"token punctuation\">.</span>cover<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">Image</span><span class=\"token punctuation\">(</span>\n            image<span class=\"token punctuation\">:</span> img<span class=\"token punctuation\">,</span>\n            width<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n            height<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n            fit<span class=\"token punctuation\">:</span> BoxFit<span class=\"token punctuation\">.</span>fitWidth<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">Image</span><span class=\"token punctuation\">(</span>\n            image<span class=\"token punctuation\">:</span> img<span class=\"token punctuation\">,</span>\n            width<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n            height<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n            fit<span class=\"token punctuation\">:</span> BoxFit<span class=\"token punctuation\">.</span>fitHeight<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">Image</span><span class=\"token punctuation\">(</span>\n            image<span class=\"token punctuation\">:</span> img<span class=\"token punctuation\">,</span>\n            width<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n            height<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n            fit<span class=\"token punctuation\">:</span> BoxFit<span class=\"token punctuation\">.</span>scaleDown<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">Image</span><span class=\"token punctuation\">(</span>\n            image<span class=\"token punctuation\">:</span> img<span class=\"token punctuation\">,</span>\n            height<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n            width<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n            fit<span class=\"token punctuation\">:</span> BoxFit<span class=\"token punctuation\">.</span>none<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">Image</span><span class=\"token punctuation\">(</span>\n            image<span class=\"token punctuation\">:</span> img<span class=\"token punctuation\">,</span>\n            width<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n            color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span>\n            colorBlendMode<span class=\"token punctuation\">:</span> BlendMode<span class=\"token punctuation\">.</span>difference<span class=\"token punctuation\">,</span>\n            fit<span class=\"token punctuation\">:</span> BoxFit<span class=\"token punctuation\">.</span>fill<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">Image</span><span class=\"token punctuation\">(</span>\n            image<span class=\"token punctuation\">:</span> img<span class=\"token punctuation\">,</span>\n            width<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n            height<span class=\"token punctuation\">:</span> <span class=\"token number\">200.0</span><span class=\"token punctuation\">,</span>\n            repeat<span class=\"token punctuation\">:</span> ImageRepeat<span class=\"token punctuation\">.</span>repeatY <span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">return</span> <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n            children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n              <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n                padding<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">16.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                child<span class=\"token punctuation\">:</span> <span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span>\n                  width<span class=\"token punctuation\">:</span> <span class=\"token number\">100</span><span class=\"token punctuation\">,</span>\n                  child<span class=\"token punctuation\">:</span> e<span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">.</span>fit<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toList</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h3 id=\"image缓存\"><a href=\"#image缓存\" class=\"header-anchor\">#</a> Image缓存</h3> <p>Flutter框架对加载过的图片是有缓存的（内存），默认最大缓存数量是1000，最大缓存空间为100M。关于Image的详细内容及原理我们将会在后面进阶部分深入介绍。</p> <h2 id=\"_3-5-2-icon\"><a href=\"#_3-5-2-icon\" class=\"header-anchor\">#</a> 3.5.2 ICON</h2> <p>Flutter中，可以像Web开发一样使用iconfont，iconfont即“字体图标”，它是将图标做成字体文件，然后通过指定不同的字符而显示不同的图片。</p> <blockquote><p>在字体文件中，每一个字符都对应一个位码，而每一个位码对应一个显示字形，不同的字体就是指字形不同，即字符对应的字形是不同的。而在iconfont中，只是将位码对应的字形做成了图标，所以不同的字符最终就会渲染成不同的图标。</p></blockquote> <p>在Flutter开发中，iconfont和图片相比有如下优势：</p> <ol><li>体积小：可以减小安装包大小。</li> <li>矢量的：iconfont都是矢量图标，放大不会影响其清晰度。</li> <li>可以应用文本样式：可以像文本一样改变字体图标的颜色、大小对齐等。</li> <li>可以通过TextSpan和文本混用。</li></ol> <h5 id=\"使用material-design字体图标\"><a href=\"#使用material-design字体图标\" class=\"header-anchor\">#</a> 使用Material Design字体图标</h5> <p>Flutter默认包含了一套Material Design的字体图标，在<code>pubspec.yaml</code>文件中的配置如下</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code><span class=\"token key atrule\">flutter</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">uses-material-design</span><span class=\"token punctuation\">:</span> <span class=\"token boolean important\">true</span>\n</code></pre></div><p>Material Design所有图标可以在其官网查看：https://material.io/tools/icons/</p> <p>我们看一个简单的例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>String icons <span class=\"token operator\">=</span> <span class=\"token string\">&quot;&quot;</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">// accessible: &amp;#xE914; or 0xE914 or E914</span>\nicons <span class=\"token operator\">+=</span> <span class=\"token string\">&quot;\\uE914&quot;</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">// error: &amp;#xE000; or 0xE000 or E000</span>\nicons <span class=\"token operator\">+=</span> <span class=\"token string\">&quot; \\uE000&quot;</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">// fingerprint: &amp;#xE90D; or 0xE90D or E90D</span>\nicons <span class=\"token operator\">+=</span> <span class=\"token string\">&quot; \\uE90D&quot;</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>icons<span class=\"token punctuation\">,</span>\n  style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>\n      fontFamily<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;MaterialIcons&quot;</span><span class=\"token punctuation\">,</span>\n      fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">24.0</span><span class=\"token punctuation\">,</span>\n      color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>green\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>运行效果如图3-21所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图3-21\"></p> <p>通过这个示例可以看到，使用图标就像使用文本一样，但是这种方式需要我们提供每个图标的码点，这并对开发者不友好，所以，Flutter封装了<code>IconData</code>和<code>Icon</code>来专门显示字体图标，上面的例子也可以用如下方式实现：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n  mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>accessible<span class=\"token punctuation\">,</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>green<span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>error<span class=\"token punctuation\">,</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>green<span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>fingerprint<span class=\"token punctuation\">,</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>green<span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p><code>Icons</code>类中包含了所有Material Design图标的<code>IconData</code>静态变量定义。</p> <h4 id=\"使用自定义字体图标\"><a href=\"#使用自定义字体图标\" class=\"header-anchor\">#</a> 使用自定义字体图标</h4> <p>我们也可以使用自定义字体图标。iconfont.cn上有很多字体图标素材，我们可以选择自己需要的图标打包下载后，会生成一些不同格式的字体文件，在Flutter中，我们使用ttf格式即可。</p> <p>假设我们项目中需要使用一个书籍图标和微信图标，我们打包下载后导入：</p> <ol><li><p>导入字体图标文件；这一步和导入字体文件相同，假设我们的字体图标文件保存在项目根目录下，路径为&quot;fonts/iconfont.ttf&quot;：</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code><span class=\"token key atrule\">fonts</span><span class=\"token punctuation\">:</span>\n  <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">family</span><span class=\"token punctuation\">:</span> myIcon  <span class=\"token comment\">#指定一个字体名</span>\n    <span class=\"token key atrule\">fonts</span><span class=\"token punctuation\">:</span>\n      <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">asset</span><span class=\"token punctuation\">:</span> fonts/iconfont.ttf\n</code></pre></div></li> <li><p>为了使用方便，我们定义一个<code>MyIcons</code>类，功能和<code>Icons</code>类一样：将字体文件中的所有图标都定义成静态变量：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">MyIcons</span><span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// book 图标</span>\n  <span class=\"token keyword\">static</span> <span class=\"token keyword\">const</span> IconData book <span class=\"token operator\">=</span> <span class=\"token keyword\">const</span> <span class=\"token function\">IconData</span><span class=\"token punctuation\">(</span>\n      <span class=\"token number\">0xe614</span><span class=\"token punctuation\">,</span> \n      fontFamily<span class=\"token punctuation\">:</span> <span class=\"token string\">'myIcon'</span><span class=\"token punctuation\">,</span> \n      matchTextDirection<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">// 微信图标</span>\n  <span class=\"token keyword\">static</span> <span class=\"token keyword\">const</span> IconData wechat <span class=\"token operator\">=</span> <span class=\"token keyword\">const</span> <span class=\"token function\">IconData</span><span class=\"token punctuation\">(</span>\n      <span class=\"token number\">0xec7d</span><span class=\"token punctuation\">,</span>  \n      fontFamily<span class=\"token punctuation\">:</span> <span class=\"token string\">'myIcon'</span><span class=\"token punctuation\">,</span> \n      matchTextDirection<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div></li> <li><p>使用</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n  mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>MyIcons<span class=\"token punctuation\">.</span>book<span class=\"token punctuation\">,</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>purple<span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>MyIcons<span class=\"token punctuation\">.</span>wechat<span class=\"token punctuation\">,</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>green<span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行后效果如图3-22所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图3-22\"></p></li></ol></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/17.59a459c4.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter3/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>基础Widget | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/208.0818aeb7.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter3/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"基础widget\"><a href=\"#基础widget\" class=\"header-anchor\">#</a> 基础Widget</h1> <p>本节介绍一下Flutter中常用的一些基础widget，由于大多数widget的属性都比较多，我们在介绍widget时会着重介绍常用的属性，而不会像API文档一样所有属性都介绍，关于属性详细的信息请参考Flutter SDK文档。</p> <h2 id=\"本章目录\"><a href=\"#本章目录\" class=\"header-anchor\">#</a> 本章目录</h2> <ul><li><a href=\"/v2/chapter3/flutter_widget_intro.html\">3.1：Widget简介</a></li> <li><a href=\"/v2/chapter3/state_manage.html\">3.2：状态管理</a></li> <li><a href=\"/v2/chapter3/text.html\">3.3：文本、字体样式</a></li> <li><a href=\"/v2/chapter3/buttons.html\">3.4：按钮</a></li> <li><a href=\"/v2/chapter3/img_and_icon.html\">3.5：图片和Icon</a></li> <li><a href=\"/v2/chapter3/radio_and_checkbox.html\">3.6：单选框和复选框</a></li> <li><a href=\"/v2/chapter3/input_and_form.html\">3.7：输入框和表单</a></li> <li><a href=\"/v2/chapter3/progress.html\">3.8：进度指示器</a></li></ul></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/208.0818aeb7.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter3/input_and_form.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>3.7 输入框及表单 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/18.318a79e1.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter3/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_3-7-输入框及表单\"><a href=\"#_3-7-输入框及表单\" class=\"header-anchor\">#</a> 3.7 输入框及表单</h1> <p>Material组件库中提供了输入框组件<code>TextField</code>和表单组件<code>Form</code>。下面我们分别介绍一下。</p> <h2 id=\"_3-7-1-textfield\"><a href=\"#_3-7-1-textfield\" class=\"header-anchor\">#</a> 3.7.1 TextField</h2> <p><code>TextField</code>用于文本输入，它提供了很多属性，我们先简单介绍一下主要属性的作用，然后通过几个示例来演示一下关键属性的用法。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">const</span> <span class=\"token function\">TextField</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  TextEditingController controller<span class=\"token punctuation\">,</span> \n  FocusNode focusNode<span class=\"token punctuation\">,</span>\n  InputDecoration decoration <span class=\"token operator\">=</span> <span class=\"token keyword\">const</span> <span class=\"token function\">InputDecoration</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  TextInputType keyboardType<span class=\"token punctuation\">,</span>\n  TextInputAction textInputAction<span class=\"token punctuation\">,</span>\n  TextStyle style<span class=\"token punctuation\">,</span>\n  TextAlign textAlign <span class=\"token operator\">=</span> TextAlign<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n  bool autofocus <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n  bool obscureText <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n  int maxLines <span class=\"token operator\">=</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n  int maxLength<span class=\"token punctuation\">,</span>\n  bool maxLengthEnforced <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n  ValueChanged<span class=\"token operator\">&lt;</span>String<span class=\"token operator\">&gt;</span> onChanged<span class=\"token punctuation\">,</span>\n  VoidCallback onEditingComplete<span class=\"token punctuation\">,</span>\n  ValueChanged<span class=\"token operator\">&lt;</span>String<span class=\"token operator\">&gt;</span> onSubmitted<span class=\"token punctuation\">,</span>\n  List<span class=\"token operator\">&lt;</span>TextInputFormatter<span class=\"token operator\">&gt;</span> inputFormatters<span class=\"token punctuation\">,</span>\n  bool enabled<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>cursorWidth <span class=\"token operator\">=</span> <span class=\"token number\">2.0</span><span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>cursorRadius<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>cursorColor<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><ul><li><p><code>controller</code>：编辑框的控制器，通过它可以设置/获取编辑框的内容、选择编辑内容、监听编辑文本改变事件。大多数情况下我们都需要显式提供一个<code>controller</code>来与文本框交互。如果没有提供<code>controller</code>，则<code>TextField</code>内部会自动创建一个。</p></li> <li><p><code>focusNode</code>：用于控制<code>TextField</code>是否占有当前键盘的输入焦点。它是我们和键盘交互的一个句柄（handle）。</p></li> <li><p><code>InputDecoration</code>：用于控制<code>TextField</code>的外观显示，如提示文本、背景颜色、边框等。</p></li> <li><p><code>keyboardType</code>：用于设置该输入框默认的键盘输入类型，取值如下：</p> <table><thead><tr><th>TextInputType枚举值</th> <th>含义</th></tr></thead> <tbody><tr><td>text</td> <td>文本输入键盘</td></tr> <tr><td>multiline</td> <td>多行文本，需和maxLines配合使用(设为null或大于1)</td></tr> <tr><td>number</td> <td>数字；会弹出数字键盘</td></tr> <tr><td>phone</td> <td>优化后的电话号码输入键盘；会弹出数字键盘并显示“* #”</td></tr> <tr><td>datetime</td> <td>优化后的日期输入键盘；Android上会显示“: -”</td></tr> <tr><td>emailAddress</td> <td>优化后的电子邮件地址；会显示“@ .”</td></tr> <tr><td>url</td> <td>优化后的url输入键盘； 会显示“/ .”</td></tr></tbody></table></li> <li><p><code>textInputAction</code>：键盘动作按钮图标(即回车键位图标)，它是一个枚举值，有多个可选值，全部的取值列表读者可以查看API文档，下面是当值为<code>TextInputAction.search</code>时，原生Android系统下键盘样式如图3-24所示：</p> <p><img src=\"/assets/img/3-24.68d03561.png\" alt=\"图3-24\"></p></li> <li><p><code>style</code>：正在编辑的文本样式。</p></li> <li><p><code>textAlign</code>: 输入框内编辑文本在水平方向的对齐方式。</p></li> <li><p><code>autofocus</code>: 是否自动获取焦点。</p></li> <li><p><code>obscureText</code>：是否隐藏正在编辑的文本，如用于输入密码的场景等，文本内容会用“•”替换。</p></li> <li><p><code>maxLines</code>：输入框的最大行数，默认为1；如果为<code>null</code>，则无行数限制。</p></li> <li><p><code>maxLength</code>和<code>maxLengthEnforced</code> ：<code>maxLength</code>代表输入框文本的最大长度，设置后输入框右下角会显示输入的文本计数。<code>maxLengthEnforced</code>决定当输入文本长度超过<code>maxLength</code>时是否阻止输入，为<code>true</code>时会阻止输入，为<code>false</code>时不会阻止输入但输入框会变红。</p></li> <li><p><code>onChange</code>：输入框内容改变时的回调函数；注：内容改变事件也可以通过<code>controller</code>来监听。</p></li> <li><p><code>onEditingComplete</code>和<code>onSubmitted</code>：这两个回调都是在输入框输入完成时触发，比如按了键盘的完成键（对号图标）或搜索键（🔍图标）。不同的是两个回调签名不同，<code>onSubmitted</code>回调是<code>ValueChanged&lt;String&gt;</code>类型，它接收当前输入内容做为参数，而<code>onEditingComplete</code>不接收参数。</p></li> <li><p><code>inputFormatters</code>：用于指定输入格式；当用户输入内容改变时，会根据指定的格式来校验。</p></li> <li><p><code>enable</code>：如果为<code>false</code>，则输入框会被禁用，禁用状态不接收输入和事件，同时显示禁用态样式（在其<code>decoration</code>中定义）。</p></li> <li><p><code>cursorWidth</code>、<code>cursorRadius</code>和<code>cursorColor</code>：这三个属性是用于自定义输入框光标宽度、圆角和颜色的。</p></li></ul> <h4 id=\"示例-登录输入框\"><a href=\"#示例-登录输入框\" class=\"header-anchor\">#</a> 示例：登录输入框</h4> <h5 id=\"布局\"><a href=\"#布局\" class=\"header-anchor\">#</a> 布局</h5> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n        children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n          <span class=\"token function\">TextField</span><span class=\"token punctuation\">(</span>\n            autofocus<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n            decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">InputDecoration</span><span class=\"token punctuation\">(</span>\n                labelText<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;用户名&quot;</span><span class=\"token punctuation\">,</span>\n                hintText<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;用户名或邮箱&quot;</span><span class=\"token punctuation\">,</span>\n                prefixIcon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>person<span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">TextField</span><span class=\"token punctuation\">(</span>\n            decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">InputDecoration</span><span class=\"token punctuation\">(</span>\n                labelText<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;密码&quot;</span><span class=\"token punctuation\">,</span>\n                hintText<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;您的登录密码&quot;</span><span class=\"token punctuation\">,</span>\n                prefixIcon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>lock<span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            obscureText<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>运行后，效果如图3-25所示：</p> <p><img src=\"/assets/img/3-25.18d09233.png\" alt=\"图3-25\"></p> <h5 id=\"获取输入内容\"><a href=\"#获取输入内容\" class=\"header-anchor\">#</a> 获取输入内容</h5> <p>获取输入内容有两种方式：</p> <ol><li>定义两个变量，用于保存用户名和密码，然后在<code>onChange</code>触发时，各自保存一下输入内容。</li> <li>通过<code>controller</code>直接获取。</li></ol> <p>第一种方式比较简单，不在举例，我们来重点看一下第二种方式，我们以用户名输入框举例：</p> <p>定义一个<code>controller</code>：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//定义一个controller</span>\nTextEditingController _unameController <span class=\"token operator\">=</span> <span class=\"token function\">TextEditingController</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>然后设置输入框controller：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">TextField</span><span class=\"token punctuation\">(</span>\n    autofocus<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n    controller<span class=\"token punctuation\">:</span> _unameController<span class=\"token punctuation\">,</span> <span class=\"token comment\">//设置controller</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>通过controller获取输入框内容</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">print</span><span class=\"token punctuation\">(</span>_unameController<span class=\"token punctuation\">.</span>text<span class=\"token punctuation\">)</span>\n</code></pre></div><h5 id=\"监听文本变化\"><a href=\"#监听文本变化\" class=\"header-anchor\">#</a> 监听文本变化</h5> <p>监听文本变化也有两种方式：</p> <ol><li><p>设置<code>onChange</code>回调，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">TextField</span><span class=\"token punctuation\">(</span>\n    autofocus<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n    onChanged<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>v<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;onChange: $v&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div></li> <li><p>通过<code>controller</code>监听，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token metadata symbol\">@override</span>\n<span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">//监听输入改变  </span>\n  _unameController<span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>_unameController<span class=\"token punctuation\">.</span>text<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div></li></ol> <p>两种方式相比，<code>onChanged</code>是专门用于监听文本变化，而<code>controller</code>的功能却多一些，除了能监听文本变化外，它还可以设置默认值、选择文本，下面我们看一个例子：</p> <p>创建一个<code>controller</code>:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>TextEditingController _selectionController <span class=\"token operator\">=</span>  <span class=\"token function\">TextEditingController</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>设置默认值，并从第三个字符开始选中后面的字符</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>_selectionController<span class=\"token punctuation\">.</span>text<span class=\"token operator\">=</span><span class=\"token string\">&quot;hello world!&quot;</span><span class=\"token punctuation\">;</span>\n_selectionController<span class=\"token punctuation\">.</span>selection<span class=\"token operator\">=</span><span class=\"token function\">TextSelection</span><span class=\"token punctuation\">(</span>\n    baseOffset<span class=\"token punctuation\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span>\n    extentOffset<span class=\"token punctuation\">:</span> _selectionController<span class=\"token punctuation\">.</span>text<span class=\"token punctuation\">.</span>length\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>设置<code>controlle</code>r:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">TextField</span><span class=\"token punctuation\">(</span>\n  controller<span class=\"token punctuation\">:</span> _selectionController<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行效果如图3-26所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图3-26\"></p> <h5 id=\"控制焦点\"><a href=\"#控制焦点\" class=\"header-anchor\">#</a> 控制焦点</h5> <p>焦点可以通过<code>FocusNode</code>和<code>FocusScopeNode</code>来控制，默认情况下，焦点由<code>FocusScope</code>来管理，它代表焦点控制范围，可以在这个范围内可以通过<code>FocusScopeNode</code>在输入框之间移动焦点、设置默认焦点等。我们可以通过<code>FocusScope.of(context)</code> 来获取Widget树中默认的<code>FocusScopeNode</code>。下面看一个示例，在此示例中创建两个<code>TextField</code>，第一个自动获取焦点，然后创建两个按钮：</p> <ul><li>点击第一个按钮可以将焦点从第一个<code>TextField</code>挪到第二个<code>TextField</code>。</li> <li>点击第二个按钮可以关闭键盘。</li></ul> <p>我们要实现的效果如图3-27所示：</p> <p><img src=\"/assets/img/3-27.6ff2b58c.png\" alt=\"图3-27\"></p> <p>代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">FocusTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _FocusTestRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_FocusTestRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_FocusTestRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>FocusTestRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  FocusNode focusNode1 <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">FocusNode</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  FocusNode focusNode2 <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">FocusNode</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  FocusScopeNode focusScopeNode<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n      padding<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">16.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n        children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n          <span class=\"token function\">TextField</span><span class=\"token punctuation\">(</span>\n            autofocus<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span> \n            focusNode<span class=\"token punctuation\">:</span> focusNode1<span class=\"token punctuation\">,</span><span class=\"token comment\">//关联focusNode1</span>\n            decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">InputDecoration</span><span class=\"token punctuation\">(</span>\n                labelText<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;input1&quot;</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">TextField</span><span class=\"token punctuation\">(</span>\n            focusNode<span class=\"token punctuation\">:</span> focusNode2<span class=\"token punctuation\">,</span><span class=\"token comment\">//关联focusNode2</span>\n            decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">InputDecoration</span><span class=\"token punctuation\">(</span>\n                labelText<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;input2&quot;</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>ctx<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword\">return</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n              children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n                  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;移动焦点&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                    <span class=\"token comment\">//将焦点从第一个TextField移到第二个TextField</span>\n                    <span class=\"token comment\">// 这是一种写法 FocusScope.of(context).requestFocus(focusNode2);</span>\n                    <span class=\"token comment\">// 这是第二种写法</span>\n                    <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">null</span> <span class=\"token operator\">==</span> focusScopeNode<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n                      focusScopeNode <span class=\"token operator\">=</span> FocusScope<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                    <span class=\"token punctuation\">}</span>\n                    focusScopeNode<span class=\"token punctuation\">.</span><span class=\"token function\">requestFocus</span><span class=\"token punctuation\">(</span>focusNode2<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n                  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;隐藏键盘&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                    <span class=\"token comment\">// 当所有编辑框都失去焦点时键盘就会收起  </span>\n                    focusNode1<span class=\"token punctuation\">.</span><span class=\"token function\">unfocus</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                    focusNode2<span class=\"token punctuation\">.</span><span class=\"token function\">unfocus</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p><code>FocusNode</code>和<code>FocusScopeNode</code>还有一些其它的方法，详情可以查看API文档。</p> <h5 id=\"监听焦点状态改变事件\"><a href=\"#监听焦点状态改变事件\" class=\"header-anchor\">#</a> 监听焦点状态改变事件</h5> <p><code>FocusNode</code>继承自<code>ChangeNotifier</code>，通过<code>FocusNode</code>可以监听焦点的改变事件，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token comment\">// 创建 focusNode   </span>\nFocusNode focusNode <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">FocusNode</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token comment\">// focusNode绑定输入框   </span>\n<span class=\"token function\">TextField</span><span class=\"token punctuation\">(</span>focusNode<span class=\"token punctuation\">:</span> focusNode<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token comment\">// 监听焦点变化    </span>\nfocusNode<span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n   <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>focusNode<span class=\"token punctuation\">.</span>hasFocus<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>获得焦点时<code>focusNode.hasFocus</code>值为<code>true</code>，失去焦点时为<code>false</code>。</p> <h5 id=\"自定义样式\"><a href=\"#自定义样式\" class=\"header-anchor\">#</a> 自定义样式</h5> <p>虽然我们可以通过<code>decoration</code>属性来定义输入框样式，下面以自定义输入框下划线颜色为例来介绍一下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">TextField</span><span class=\"token punctuation\">(</span>\n  decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">InputDecoration</span><span class=\"token punctuation\">(</span>\n    labelText<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;请输入用户名&quot;</span><span class=\"token punctuation\">,</span>\n    prefixIcon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>person<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token comment\">// 未获得焦点下划线设为灰色</span>\n    enabledBorder<span class=\"token punctuation\">:</span> <span class=\"token function\">UnderlineInputBorder</span><span class=\"token punctuation\">(</span>\n      borderSide<span class=\"token punctuation\">:</span> <span class=\"token function\">BorderSide</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token comment\">//获得焦点下划线设为蓝色</span>\n    focusedBorder<span class=\"token punctuation\">:</span> <span class=\"token function\">UnderlineInputBorder</span><span class=\"token punctuation\">(</span>\n      borderSide<span class=\"token punctuation\">:</span> <span class=\"token function\">BorderSide</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</code></pre></div><p>上面代码我们直接通过InputDecoration的enabledBorder和focusedBorder来分别设置了输入框在未获取焦点和获得焦点后的下划线颜色。另外，我们也可以通过主题来自定义输入框的样式，下面我们探索一下如何在不使用enabledBorder和focusedBorder的情况下来自定义下滑线颜色。</p> <p>由于<code>TextField</code>在绘制下划线时使用的颜色是主题色里面的<code>hintColor</code>，但提示文本颜色也是用的<code>hintColor</code>， 如果我们直接修改<code>hintColor</code>，那么下划线和提示文本的颜色都会变。值得高兴的是<code>decoration</code>中可以设置<code>hintStyle</code>，它可以覆盖<code>hintColor</code>，并且主题中可以通过<code>inputDecorationTheme</code>来设置输入框默认的<code>decoration</code>。所以我们可以通过主题来自定义，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Theme</span><span class=\"token punctuation\">(</span>\n  data<span class=\"token punctuation\">:</span> Theme<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">copyWith</span><span class=\"token punctuation\">(</span>\n      hintColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">[</span><span class=\"token number\">200</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//定义下划线颜色</span>\n      inputDecorationTheme<span class=\"token punctuation\">:</span> <span class=\"token function\">InputDecorationTheme</span><span class=\"token punctuation\">(</span>\n          labelStyle<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token comment\">//定义label字体样式</span>\n          hintStyle<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">,</span> fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">14.0</span><span class=\"token punctuation\">)</span><span class=\"token comment\">//定义提示文本样式</span>\n      <span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n    children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n      <span class=\"token function\">TextField</span><span class=\"token punctuation\">(</span>\n        decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">InputDecoration</span><span class=\"token punctuation\">(</span>\n            labelText<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;用户名&quot;</span><span class=\"token punctuation\">,</span>\n            hintText<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;用户名或邮箱&quot;</span><span class=\"token punctuation\">,</span>\n            prefixIcon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>person<span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token function\">TextField</span><span class=\"token punctuation\">(</span>\n        decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">InputDecoration</span><span class=\"token punctuation\">(</span>\n            prefixIcon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>lock<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            labelText<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;密码&quot;</span><span class=\"token punctuation\">,</span>\n            hintText<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;您的登录密码&quot;</span><span class=\"token punctuation\">,</span>\n            hintStyle<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">,</span> fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">13.0</span><span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        obscureText<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行效果如图3-28所示：</p> <p><img src=\"/assets/img/3-28.b6b9c9b2.png\" alt=\"图3-28\"></p> <p>我们成功的自定义了下划线颜色和提问文字样式，细心的读者可能已经发现，通过这种方式自定义后，输入框在获取焦点时，<code>labelText</code>不会高亮显示了，正如上图中的&quot;用户名&quot;本应该显示蓝色，但现在却显示为灰色，并且我们还是无法定义下划线宽度。另一种灵活的方式是直接隐藏掉<code>TextField</code>本身的下划线，然后通过<code>Container</code>去嵌套定义样式，如:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">TextField</span><span class=\"token punctuation\">(</span>\n    keyboardType<span class=\"token punctuation\">:</span> TextInputType<span class=\"token punctuation\">.</span>emailAddress<span class=\"token punctuation\">,</span>\n    decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">InputDecoration</span><span class=\"token punctuation\">(</span>\n        labelText<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;Email&quot;</span><span class=\"token punctuation\">,</span>\n        hintText<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;电子邮件地址&quot;</span><span class=\"token punctuation\">,</span>\n        prefixIcon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>email<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        border<span class=\"token punctuation\">:</span> InputBorder<span class=\"token punctuation\">.</span>none <span class=\"token comment\">//隐藏下划线</span>\n    <span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>\n      <span class=\"token comment\">// 下滑线浅灰色，宽度1像素</span>\n      border<span class=\"token punctuation\">:</span> <span class=\"token function\">Border</span><span class=\"token punctuation\">(</span>bottom<span class=\"token punctuation\">:</span> <span class=\"token function\">BorderSide</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">[</span><span class=\"token number\">200</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> width<span class=\"token punctuation\">:</span> <span class=\"token number\">1.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行效果：</p> <p><img src=\"https://cdn.jsdelivr.net/gh/flutterchina/flutter-in-action@1.0/docs/imgs/image-20180904150511545.png\" alt=\"image-20180904150511545\"></p> <p>通过这种组件组合的方式，也可以定义背景圆角等。一般来说，优先通过<code>decoration</code>来自定义样式，如果<code>decoration</code>实现不了，再用widget组合的方式。</p> <blockquote><p>思考题：在这个示例中，下划线颜色是固定的，所以获得焦点后颜色仍然为灰色，如何实现点击后下滑线也变色呢？</p></blockquote> <h2 id=\"_3-7-2-表单form\"><a href=\"#_3-7-2-表单form\" class=\"header-anchor\">#</a> 3.7.2 表单Form</h2> <p>实际业务中，在正式向服务器提交数据前，都会对各个输入框数据进行合法性校验，但是对每一个<code>TextField</code>都分别进行校验将会是一件很麻烦的事。还有，如果用户想清除一组<code>TextField</code>的内容，除了一个一个清除有没有什么更好的办法呢？为此，Flutter提供了一个<code>Form</code> 组件，它可以对输入框进行分组，然后进行一些统一操作，如输入内容校验、输入框重置以及输入内容保存。</p> <h4 id=\"form\"><a href=\"#form\" class=\"header-anchor\">#</a> Form</h4> <p><code>Form</code>继承自<code>StatefulWidget</code>对象，它对应的状态类为<code>FormState</code>。我们先看看<code>Form</code>类的定义：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Form</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@required</span> Widget child<span class=\"token punctuation\">,</span>\n  bool autovalidate <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n  WillPopCallback onWillPop<span class=\"token punctuation\">,</span>\n  VoidCallback onChanged<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><ul><li><code>autovalidate</code>：是否自动校验输入内容；当为<code>true</code>时，每一个子FormField内容发生变化时都会自动校验合法性，并直接显示错误信息。否则，需要通过调用<code>FormState.validate()</code>来手动校验。</li> <li><code>onWillPop</code>：决定<code>Form</code>所在的路由是否可以直接返回（如点击返回按钮），该回调返回一个<code>Future</code>对象，如果Future的最终结果是<code>false</code>，则当前路由不会返回；如果为<code>true</code>，则会返回到上一个路由。此属性通常用于拦截返回按钮。</li> <li><code>onChanged</code>：<code>Form</code>的任意一个子<code>FormField</code>内容发生变化时会触发此回调。</li></ul> <h4 id=\"formfield\"><a href=\"#formfield\" class=\"header-anchor\">#</a> FormField</h4> <p><code>Form</code>的子孙元素必须是<code>FormField</code>类型，<code>FormField</code>是一个抽象类，定义几个属性，<code>FormState</code>内部通过它们来完成操作，<code>FormField</code>部分定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">const</span> <span class=\"token function\">FormField</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  FormFieldSetter<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span> onSaved<span class=\"token punctuation\">,</span> <span class=\"token comment\">//保存回调</span>\n  FormFieldValidator<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span>  validator<span class=\"token punctuation\">,</span> <span class=\"token comment\">//验证回调</span>\n  T initialValue<span class=\"token punctuation\">,</span> <span class=\"token comment\">//初始值</span>\n  bool autovalidate <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//是否自动校验。</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>为了方便使用，Flutter提供了一个<code>TextFormField</code>组件，它继承自<code>FormField</code>类，也是<code>TextField</code>的一个包装类，所以除了<code>FormField</code>定义的属性之外，它还包括<code>TextField</code>的属性。</p> <h4 id=\"formstate\"><a href=\"#formstate\" class=\"header-anchor\">#</a> FormState</h4> <p><code>FormState</code>为<code>Form</code>的<code>State</code>类，可以通过<code>Form.of()</code>或<code>GlobalKey</code>获得。我们可以通过它来对<code>Form</code>的子孙<code>FormField</code>进行统一操作。我们看看其常用的三个方法：</p> <ul><li><code>FormState.validate()</code>：调用此方法后，会调用<code>Form</code>子孙<code>FormField的validate</code>回调，如果有一个校验失败，则返回false，所有校验失败项都会返回用户返回的错误提示。</li> <li><code>FormState.save()</code>：调用此方法后，会调用<code>Form</code>子孙<code>FormField</code>的<code>save</code>回调，用于保存表单内容</li> <li><code>FormState.reset()</code>：调用此方法后，会将子孙<code>FormField</code>的内容清空。</li></ul> <h4 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h4> <p>我们修改一下上面用户登录的示例，在提交之前校验：</p> <ol><li>用户名不能为空，如果为空则提示“用户名不能为空”。</li> <li>密码不能小于6位，如果小于6为则提示“密码不能少于6位”。</li></ol> <p>完整代码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">FormTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _FormTestRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_FormTestRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_FormTestRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>FormTestRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  TextEditingController _unameController <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">TextEditingController</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  TextEditingController _pwdController <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">TextEditingController</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  GlobalKey _formKey<span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">GlobalKey</span><span class=\"token operator\">&lt;</span>FormState<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n      appBar<span class=\"token punctuation\">:</span> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>\n        title<span class=\"token punctuation\">:</span><span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Form Test&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      body<span class=\"token punctuation\">:</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n        padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">symmetric</span><span class=\"token punctuation\">(</span>vertical<span class=\"token punctuation\">:</span> <span class=\"token number\">16.0</span><span class=\"token punctuation\">,</span> horizontal<span class=\"token punctuation\">:</span> <span class=\"token number\">24.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Form</span><span class=\"token punctuation\">(</span>\n          key<span class=\"token punctuation\">:</span> _formKey<span class=\"token punctuation\">,</span> <span class=\"token comment\">//设置globalKey，用于后面获取FormState</span>\n          autovalidate<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//开启自动校验</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n            children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n              <span class=\"token function\">TextFormField</span><span class=\"token punctuation\">(</span>\n                  autofocus<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n                  controller<span class=\"token punctuation\">:</span> _unameController<span class=\"token punctuation\">,</span>\n                  decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">InputDecoration</span><span class=\"token punctuation\">(</span>\n                      labelText<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;用户名&quot;</span><span class=\"token punctuation\">,</span>\n                      hintText<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;用户名或邮箱&quot;</span><span class=\"token punctuation\">,</span>\n                      icon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>person<span class=\"token punctuation\">)</span>\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token comment\">// 校验用户名</span>\n                  validator<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>v<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                    <span class=\"token keyword\">return</span> v\n                        <span class=\"token punctuation\">.</span><span class=\"token function\">trim</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n                        <span class=\"token punctuation\">.</span>length <span class=\"token operator\">&gt;</span> <span class=\"token number\">0</span> <span class=\"token operator\">?</span> <span class=\"token keyword\">null</span> <span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;用户名不能为空&quot;</span><span class=\"token punctuation\">;</span>\n                  <span class=\"token punctuation\">}</span>\n\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token function\">TextFormField</span><span class=\"token punctuation\">(</span>\n                  controller<span class=\"token punctuation\">:</span> _pwdController<span class=\"token punctuation\">,</span>\n                  decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">InputDecoration</span><span class=\"token punctuation\">(</span>\n                      labelText<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;密码&quot;</span><span class=\"token punctuation\">,</span>\n                      hintText<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;您的登录密码&quot;</span><span class=\"token punctuation\">,</span>\n                      icon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>lock<span class=\"token punctuation\">)</span>\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  obscureText<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token comment\">//校验密码</span>\n                  validator<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>v<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                    <span class=\"token keyword\">return</span> v\n                        <span class=\"token punctuation\">.</span><span class=\"token function\">trim</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n                        <span class=\"token punctuation\">.</span>length <span class=\"token operator\">&gt;</span> <span class=\"token number\">5</span> <span class=\"token operator\">?</span> <span class=\"token keyword\">null</span> <span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;密码不能少于6位&quot;</span><span class=\"token punctuation\">;</span>\n                  <span class=\"token punctuation\">}</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token comment\">// 登录按钮</span>\n              <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n                padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">only</span><span class=\"token punctuation\">(</span>top<span class=\"token punctuation\">:</span> <span class=\"token number\">28.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                child<span class=\"token punctuation\">:</span> <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n                  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                    <span class=\"token function\">Expanded</span><span class=\"token punctuation\">(</span>\n                      child<span class=\"token punctuation\">:</span> <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n                        padding<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">15.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;登录&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                        color<span class=\"token punctuation\">:</span> Theme\n                            <span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span>\n                            <span class=\"token punctuation\">.</span>primaryColor<span class=\"token punctuation\">,</span>\n                        textColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">,</span>\n                        onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                          <span class=\"token comment\">//在这里不能通过此方式获取FormState，context不对</span>\n                          <span class=\"token comment\">//print(Form.of(context));</span>\n                            \n                          <span class=\"token comment\">// 通过_formKey.currentState 获取FormState后，</span>\n                          <span class=\"token comment\">// 调用validate()方法校验用户名密码是否合法，校验</span>\n                          <span class=\"token comment\">// 通过后再提交数据。 </span>\n                          <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>_formKey<span class=\"token punctuation\">.</span>currentState <span class=\"token operator\">as</span> FormState<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">validate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n                            <span class=\"token comment\">//验证通过提交数据</span>\n                          <span class=\"token punctuation\">}</span>\n                        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n                      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行后效果如图3-29所示：</p> <p><img src=\"/assets/img/3-29.ff4de036.png\" alt=\"图3-29\"></p> <p>注意，登录按钮的<code>onPressed</code>方法中不能通过<code>Form.of(context)</code>来获取，原因是，此处的<code>context</code>为<code>FormTestRoute</code>的context，而<code>Form.of(context)</code>是根据所指定<code>context</code>向根去查找，而<code>FormState</code>是在<code>FormTestRoute</code>的子树中，所以不行。正确的做法是通过<code>Builder</code>来构建登录按钮，<code>Builder</code>会将<code>widget</code>节点的<code>context</code>作为回调参数：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Expanded</span><span class=\"token punctuation\">(</span>\n <span class=\"token comment\">// 通过Builder来获取RaisedButton所在widget树的真正context(Element) </span>\n  child<span class=\"token punctuation\">:</span><span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n      onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">//由于本widget也是Form的子代widget，所以可以通过下面方式获取FormState  </span>\n        <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>Form<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">validate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n          <span class=\"token comment\">//验证通过提交数据</span>\n        <span class=\"token punctuation\">}</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>其实<code>context</code>正是操作Widget所对应的<code>Element</code>的一个接口，由于Widget树对应的<code>Element</code>都是不同的，所以<code>context</code>也都是不同的，有关<code>context</code>的更多内容会在后面高级部分详细讨论。Flutter中有很多“of(context)”这种方法，读者在使用时一定要注意<code>context</code>是否正确。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/18.318a79e1.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter3/progress.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>3.8 进度指示器 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/34.9dff7b9d.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter3/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_3-8-进度指示器\"><a href=\"#_3-8-进度指示器\" class=\"header-anchor\">#</a> 3.8 进度指示器</h1> <p>Material 组件库中提供了两种进度指示器：<code>LinearProgressIndicator</code>和<code>CircularProgressIndicator</code>，它们都可以同时用于精确的进度指示和模糊的进度指示。精确进度通常用于任务进度可以计算和预估的情况，比如文件下载；而模糊进度则用户任务进度无法准确获得的情况，如下拉刷新，数据提交等。</p> <h3 id=\"linearprogressindicator\"><a href=\"#linearprogressindicator\" class=\"header-anchor\">#</a> LinearProgressIndicator</h3> <p><code>LinearProgressIndicator</code>是一个线性、条状的进度条，定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">LinearProgressIndicator</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  double value<span class=\"token punctuation\">,</span>\n  Color backgroundColor<span class=\"token punctuation\">,</span>\n  Animation<span class=\"token operator\">&lt;</span>Color<span class=\"token operator\">&gt;</span> valueColor<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><ul><li><code>value</code>：<code>value</code>表示当前的进度，取值范围为[0,1]；如果<code>value</code>为<code>null</code>时则指示器会执行一个循环动画（模糊进度）；当<code>value</code>不为<code>null</code>时，指示器为一个具体进度的进度条。</li> <li><code>backgroundColor</code>：指示器的背景色。</li> <li><code>valueColor</code>: 指示器的进度条颜色；值得注意的是，该值类型是<code>Animation&lt;Color&gt;</code>，这允许我们对进度条的颜色也可以指定动画。如果我们不需要对进度条颜色执行动画，换言之，我们想对进度条应用一种固定的颜色，此时我们可以通过<code>AlwaysStoppedAnimation</code>来指定。</li></ul> <h3 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h3> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">// 模糊进度条(会执行一个动画)</span>\n<span class=\"token function\">LinearProgressIndicator</span><span class=\"token punctuation\">(</span>\n  backgroundColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">[</span><span class=\"token number\">200</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  valueColor<span class=\"token punctuation\">:</span> <span class=\"token function\">AlwaysStoppedAnimation</span><span class=\"token punctuation\">(</span>Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token comment\">//进度条显示50%</span>\n<span class=\"token function\">LinearProgressIndicator</span><span class=\"token punctuation\">(</span>\n  backgroundColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">[</span><span class=\"token number\">200</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  valueColor<span class=\"token punctuation\">:</span> <span class=\"token function\">AlwaysStoppedAnimation</span><span class=\"token punctuation\">(</span>Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  value<span class=\"token punctuation\">:</span> <span class=\"token number\">.5</span><span class=\"token punctuation\">,</span> \n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行效果如图3-30所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图3-30\"></p> <p>第一个进度条在执行循环动画：蓝色条一直在移动，而第二个进度条是静止的，停在50%的位置。</p> <h3 id=\"circularprogressindicator\"><a href=\"#circularprogressindicator\" class=\"header-anchor\">#</a> CircularProgressIndicator</h3> <p><code>CircularProgressIndicator</code>是一个圆形进度条，定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code> <span class=\"token function\">CircularProgressIndicator</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  double value<span class=\"token punctuation\">,</span>\n  Color backgroundColor<span class=\"token punctuation\">,</span>\n  Animation<span class=\"token operator\">&lt;</span>Color<span class=\"token operator\">&gt;</span> valueColor<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>strokeWidth <span class=\"token operator\">=</span> <span class=\"token number\">4.0</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>   \n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> \n</code></pre></div><p>前三个参数和<code>LinearProgressIndicator</code>相同，不再赘述。<code>strokeWidth</code> 表示圆形进度条的粗细。示例如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">// 模糊进度条(会执行一个旋转动画)</span>\n<span class=\"token function\">CircularProgressIndicator</span><span class=\"token punctuation\">(</span>\n  backgroundColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">[</span><span class=\"token number\">200</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  valueColor<span class=\"token punctuation\">:</span> <span class=\"token function\">AlwaysStoppedAnimation</span><span class=\"token punctuation\">(</span>Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token comment\">//进度条显示50%，会显示一个半圆</span>\n<span class=\"token function\">CircularProgressIndicator</span><span class=\"token punctuation\">(</span>\n  backgroundColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">[</span><span class=\"token number\">200</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  valueColor<span class=\"token punctuation\">:</span> <span class=\"token function\">AlwaysStoppedAnimation</span><span class=\"token punctuation\">(</span>Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  value<span class=\"token punctuation\">:</span> <span class=\"token number\">.5</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</code></pre></div><p>运行效果如图3-31所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图3-31\"></p> <p>第一个进度条会执行旋转动画，而第二个进度条是静止的，它停在50%的位置。</p> <h3 id=\"自定义尺寸\"><a href=\"#自定义尺寸\" class=\"header-anchor\">#</a> 自定义尺寸</h3> <p>我们可以发现<code>LinearProgressIndicator</code>和<code>CircularProgressIndicator</code>，并没有提供设置圆形进度条尺寸的参数；如果我们希望<code>LinearProgressIndicator</code>的线细一些，或者希望<code>CircularProgressIndicator</code>的圆大一些该怎么做？</p> <p>其实<code>LinearProgressIndicator</code>和<code>CircularProgressIndicator</code>都是取父容器的尺寸作为绘制的边界的。知道了这点，我们便可以通过尺寸限制类Widget，如<code>ConstrainedBox</code>、<code>SizedBox</code> （我们将在后面容器类组件一章中介绍）来指定尺寸，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">// 线性进度条高度指定为3</span>\n<span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span>\n  height<span class=\"token punctuation\">:</span> <span class=\"token number\">3</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">LinearProgressIndicator</span><span class=\"token punctuation\">(</span>\n    backgroundColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">[</span><span class=\"token number\">200</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    valueColor<span class=\"token punctuation\">:</span> <span class=\"token function\">AlwaysStoppedAnimation</span><span class=\"token punctuation\">(</span>Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    value<span class=\"token punctuation\">:</span> <span class=\"token number\">.5</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token comment\">// 圆形进度条直径指定为100</span>\n<span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span>\n  height<span class=\"token punctuation\">:</span> <span class=\"token number\">100</span><span class=\"token punctuation\">,</span>\n  width<span class=\"token punctuation\">:</span> <span class=\"token number\">100</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">CircularProgressIndicator</span><span class=\"token punctuation\">(</span>\n    backgroundColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">[</span><span class=\"token number\">200</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    valueColor<span class=\"token punctuation\">:</span> <span class=\"token function\">AlwaysStoppedAnimation</span><span class=\"token punctuation\">(</span>Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    value<span class=\"token punctuation\">:</span> <span class=\"token number\">.7</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</code></pre></div><p>运行效果如图3-32所示：</p> <p><img src=\"/assets/img/3-32.11e62a02.png\" alt=\"图3-32\"></p> <p>注意，如果<code>CircularProgressIndicator</code>显示空间的宽高不同，则会显示为椭圆。如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">// 宽高不等</span>\n<span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span>\n  height<span class=\"token punctuation\">:</span> <span class=\"token number\">100</span><span class=\"token punctuation\">,</span>\n  width<span class=\"token punctuation\">:</span> <span class=\"token number\">130</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">CircularProgressIndicator</span><span class=\"token punctuation\">(</span>\n    backgroundColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">[</span><span class=\"token number\">200</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    valueColor<span class=\"token punctuation\">:</span> <span class=\"token function\">AlwaysStoppedAnimation</span><span class=\"token punctuation\">(</span>Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    value<span class=\"token punctuation\">:</span> <span class=\"token number\">.7</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</code></pre></div><p>运行效果如图3-33所示：</p> <p><img src=\"/assets/img/progress_oval.4912d1a4.png\" alt=\"progress_oval\"></p> <h3 id=\"进度色动画\"><a href=\"#进度色动画\" class=\"header-anchor\">#</a> 进度色动画</h3> <p>前面说过可以通过<code>valueColor</code>对进度条颜色做动画，关于动画我们将在后面专门的章节详细介绍，这里先给出一个例子，读者在了解了Flutter动画一章后再回过头来看。</p> <p>我们实现一个进度条在3秒内从灰色变成蓝色的动画：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">ProgressRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _ProgressRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">_ProgressRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_ProgressRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>ProgressRoute<span class=\"token operator\">&gt;</span>\n    <span class=\"token keyword\">with</span> SingleTickerProviderStateMixin <span class=\"token punctuation\">{</span>\n  AnimationController _animationController<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//动画执行时间3秒  </span>\n    _animationController <span class=\"token operator\">=</span>\n        <span class=\"token keyword\">new</span> <span class=\"token class-name\">AnimationController</span><span class=\"token punctuation\">(</span>vsync<span class=\"token punctuation\">:</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">,</span> duration<span class=\"token punctuation\">:</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">3</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    _animationController<span class=\"token punctuation\">.</span><span class=\"token function\">forward</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    _animationController<span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    _animationController<span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">SingleChildScrollView</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n        children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n            padding<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">16</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">LinearProgressIndicator</span><span class=\"token punctuation\">(</span>\n              backgroundColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">[</span><span class=\"token number\">200</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n              valueColor<span class=\"token punctuation\">:</span> <span class=\"token function\">ColorTween</span><span class=\"token punctuation\">(</span>begin<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">)</span>\n                <span class=\"token punctuation\">.</span><span class=\"token function\">animate</span><span class=\"token punctuation\">(</span>_animationController<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 从灰色变成蓝色</span>\n              value<span class=\"token punctuation\">:</span> _animationController<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h3 id=\"自定义进度指示器样式\"><a href=\"#自定义进度指示器样式\" class=\"header-anchor\">#</a> 自定义进度指示器样式</h3> <p>定制进度指示器风格样式，可以通过<code>CustomPainter</code> Widget 来自定义绘制逻辑，实际上<code>LinearProgressIndicator</code>和<code>CircularProgressIndicator</code>也正是通过<code>CustomPainter</code>来实现外观绘制的。关于<code>CustomPainter</code>，我们将在后面“自定义Widget”一章中详细介绍。</p> <blockquote><p><a href=\"https://pub.flutter-io.cn/packages/flutter_spinkit\" target=\"_blank\" rel=\"noopener noreferrer\">flutter_spinkit<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 包提供了多种风格的模糊进度指示器，读者若是感兴趣，可以参考。</p></blockquote></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/34.9dff7b9d.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter3/radio_and_checkbox.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>3.6 单选开关和复选框 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/120.681a706e.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter3/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_3-6-单选开关和复选框\"><a href=\"#_3-6-单选开关和复选框\" class=\"header-anchor\">#</a> 3.6 单选开关和复选框</h1> <p>Material 组件库中提供了Material风格的单选开关<code>Switch</code>和复选框<code>Checkbox</code>，虽然它们都是继承自<code>StatefulWidget</code>，但它们本身不会保存当前选中状态，选中状态都是由父组件来管理的。当<code>Switch</code>或<code>Checkbox</code>被点击时，会触发它们的<code>onChanged</code>回调，我们可以在此回调中处理选中状态改变逻辑。下面看一个简单的例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">SwitchAndCheckBoxTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _SwitchAndCheckBoxTestRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_SwitchAndCheckBoxTestRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_SwitchAndCheckBoxTestRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>SwitchAndCheckBoxTestRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  bool _switchSelected<span class=\"token operator\">=</span><span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//维护单选开关状态</span>\n  bool _checkboxSelected<span class=\"token operator\">=</span><span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span><span class=\"token comment\">//维护复选框状态</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n      children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n        <span class=\"token function\">Switch</span><span class=\"token punctuation\">(</span>\n          value<span class=\"token punctuation\">:</span> _switchSelected<span class=\"token punctuation\">,</span><span class=\"token comment\">//当前状态</span>\n          onChanged<span class=\"token punctuation\">:</span><span class=\"token punctuation\">(</span>value<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">//重新构建页面  </span>\n            <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              _switchSelected<span class=\"token operator\">=</span>value<span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token function\">Checkbox</span><span class=\"token punctuation\">(</span>\n          value<span class=\"token punctuation\">:</span> _checkboxSelected<span class=\"token punctuation\">,</span>\n          activeColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span> <span class=\"token comment\">//选中时的颜色</span>\n          onChanged<span class=\"token punctuation\">:</span><span class=\"token punctuation\">(</span>value<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n            <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              _checkboxSelected<span class=\"token operator\">=</span>value<span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span> <span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面代码中，由于需要维护<code>Switch</code>和<code>Checkbox</code>的选中状态，所以<code>SwitchAndCheckBoxTestRoute</code>继承自<code>StatefulWidget</code> 。在其<code>build</code>方法中分别构建了一个<code>Switch</code>和<code>Checkbox</code>，初始状态都为选中状态，当用户点击时，会将状态置反，然后回调用<code>setState()</code>通知Flutter framework重新构建UI。</p> <p><img src=\"/assets/img/3-23.a249df4c.png\" alt=\"图3-23\"></p> <h3 id=\"属性及外观\"><a href=\"#属性及外观\" class=\"header-anchor\">#</a> 属性及外观</h3> <p><code>Switch</code>和<code>Checkbox</code>属性比较简单，读者可以查看API文档，它们都有一个<code>activeColor</code>属性，用于设置激活态的颜色。至于大小，到目前为止，<code>Checkbox</code>的大小是固定的，无法自定义，而<code>Switch</code>只能定义宽度，高度也是固定的。值得一提的是<code>Checkbox</code>有一个属性<code>tristate</code> ，表示是否为三态，其默认值为<code>false</code> ，这时Checkbox有两种状态即“选中”和“不选中”，对应的value值为<code>true</code>和<code>false</code> 。如果<code>tristate</code>值为<code>true</code>时，value的值会增加一个状态<code>null</code>，读者可以自行了解。</p> <h3 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h3> <p>通过<code>Switch</code>和<code>Checkbox</code>我们可以看到，虽然它们本身是与状态（是否选中）关联的，但它们却不是自己来维护状态，而是需要父组件来管理状态，然后当用户点击时，再通过事件通知给父组件，这样是合理的，因为<code>Switch</code>和<code>Checkbox</code>是否选中本就和用户数据关联，而这些用户数据也不可能是它们的私有状态。我们在自定义组件时也应该思考一下哪种状态的管理方式最为合理。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/120.681a706e.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter3/state_manage.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>3.2 状态管理 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/121.aef1325d.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter3/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_3-2-状态管理\"><a href=\"#_3-2-状态管理\" class=\"header-anchor\">#</a> 3.2 状态管理</h1> <p>响应式的编程框架中都会有一个永恒的主题——“状态(State)管理”，无论是在React/Vue（两者都是支持响应式编程的Web开发框架）还是Flutter中，他们讨论的问题和解决的思想都是一致的。所以，如果你对React/Vue的状态管理有了解，可以跳过本节。言归正传，我们想一个问题，<code>StatefulWidget</code>的状态应该被谁管理？Widget本身？父Widget？都会？还是另一个对象？答案是取决于实际情况！以下是管理状态的最常见的方法：</p> <ul><li>Widget管理自己的状态。</li> <li>Widget管理子Widget状态。</li> <li>混合管理（父Widget和子Widget都管理状态）。</li></ul> <p>如何决定使用哪种管理方法？下面是官方给出的一些原则可以帮助你做决定：</p> <ul><li>如果状态是用户数据，如复选框的选中状态、滑块的位置，则该状态最好由父Widget管理。</li> <li>如果状态是有关界面外观效果的，例如颜色、动画，那么状态最好由Widget本身来管理。</li> <li>如果某一个状态是不同Widget共享的则最好由它们共同的父Widget管理。</li></ul> <p>在Widget内部管理状态封装性会好一些，而在父Widget中管理会比较灵活。有些时候，如果不确定到底该怎么管理状态，那么推荐的首选是在父widget中管理（灵活会显得更重要一些）。</p> <p>接下来，我们将通过创建三个简单示例TapboxA、TapboxB和TapboxC来说明管理状态的不同方式。 这些例子功能是相似的 ——创建一个盒子，当点击它时，盒子背景会在绿色与灰色之间切换。状态 <code>_active</code>确定颜色：绿色为<code>true</code> ，灰色为<code>false</code>，如图3-4所示。<img src=\"/assets/img/3-4.8e70e140.png\" alt=\"a large grey box with the text, 'Inactive'\"></p> <p>下面的例子将使用<code>GestureDetector</code>来识别点击事件，关于该<code>GestureDetector</code>的详细内容我们将在后面“事件处理”一章中介绍。</p> <h3 id=\"_3-2-1-widget管理自身状态\"><a href=\"#_3-2-1-widget管理自身状态\" class=\"header-anchor\">#</a> 3.2.1 Widget管理自身状态</h3> <p>_TapboxAState 类:</p> <ul><li>管理TapboxA的状态。</li> <li>定义<code>_active</code>：确定盒子的当前颜色的布尔值。</li> <li>定义<code>_handleTap()</code>函数，该函数在点击该盒子时更新<code>_active</code>，并调用<code>setState()</code>更新UI。</li> <li>实现widget的所有交互式行为。</li></ul> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">// TapboxA 管理自身状态.</span>\n\n<span class=\"token comment\">//------------------------- TapboxA ----------------------------------</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">TapboxA</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">TapboxA</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>Key key<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  _TapboxAState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_TapboxAState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_TapboxAState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>TapboxA<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  bool _active <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_handleTap</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _active <span class=\"token operator\">=</span> <span class=\"token operator\">!</span>_active<span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">GestureDetector</span><span class=\"token punctuation\">(</span>\n      onTap<span class=\"token punctuation\">:</span> _handleTap<span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Container</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Center</span><span class=\"token punctuation\">(</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span>\n            _active <span class=\"token operator\">?</span> <span class=\"token string\">'Active'</span> <span class=\"token punctuation\">:</span> <span class=\"token string\">'Inactive'</span><span class=\"token punctuation\">,</span>\n            style<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">TextStyle</span><span class=\"token punctuation\">(</span>fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">32.0</span><span class=\"token punctuation\">,</span> color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        width<span class=\"token punctuation\">:</span> <span class=\"token number\">200.0</span><span class=\"token punctuation\">,</span>\n        height<span class=\"token punctuation\">:</span> <span class=\"token number\">200.0</span><span class=\"token punctuation\">,</span>\n        decoration<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">BoxDecoration</span><span class=\"token punctuation\">(</span>\n          color<span class=\"token punctuation\">:</span> _active <span class=\"token operator\">?</span> Colors<span class=\"token punctuation\">.</span>lightGreen<span class=\"token punctuation\">[</span><span class=\"token number\">700</span><span class=\"token punctuation\">]</span> <span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">[</span><span class=\"token number\">600</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n</code></pre></div><h3 id=\"_3-2-2-父widget管理子widget的状态\"><a href=\"#_3-2-2-父widget管理子widget的状态\" class=\"header-anchor\">#</a> 3.2.2 父Widget管理子Widget的状态</h3> <p>对于父Widget来说，管理状态并告诉其子Widget何时更新通常是比较好的方式。 例如，<code>IconButton</code>是一个图标按钮，但它是一个无状态的Widget，因为我们认为父Widget需要知道该按钮是否被点击来采取相应的处理。</p> <p>在以下示例中，TapboxB通过回调将其状态导出到其父组件，状态由父组件管理，因此它的父组件为<code>StatefulWidget</code>。但是由于TapboxB不管理任何状态，所以<code>TapboxB</code>为<code>StatelessWidget</code>。</p> <p><code>ParentWidgetState</code> 类:</p> <ul><li>为TapboxB 管理<code>_active</code>状态。</li> <li>实现<code>_handleTapboxChanged()</code>，当盒子被点击时调用的方法。</li> <li>当状态改变时，调用<code>setState()</code>更新UI。</li></ul> <p>TapboxB 类:</p> <ul><li>继承<code>StatelessWidget</code>类，因为所有状态都由其父组件处理。</li> <li>当检测到点击时，它会通知父组件。</li></ul> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">// ParentWidget 为 TapboxB 管理状态.</span>\n\n<span class=\"token comment\">//------------------------ ParentWidget --------------------------------</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">ParentWidget</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _ParentWidgetState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_ParentWidgetState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_ParentWidgetState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>ParentWidget<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  bool _active <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_handleTapboxChanged</span><span class=\"token punctuation\">(</span>bool newValue<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _active <span class=\"token operator\">=</span> newValue<span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Container</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">TapboxB</span><span class=\"token punctuation\">(</span>\n        active<span class=\"token punctuation\">:</span> _active<span class=\"token punctuation\">,</span>\n        onChanged<span class=\"token punctuation\">:</span> _handleTapboxChanged<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">//------------------------- TapboxB ----------------------------------</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">TapboxB</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">TapboxB</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>Key key<span class=\"token punctuation\">,</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>active<span class=\"token punctuation\">:</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span> <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>onChanged<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> bool active<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> ValueChanged<span class=\"token operator\">&lt;</span>bool<span class=\"token operator\">&gt;</span> onChanged<span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_handleTap</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">onChanged</span><span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>active<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">GestureDetector</span><span class=\"token punctuation\">(</span>\n      onTap<span class=\"token punctuation\">:</span> _handleTap<span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Container</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Center</span><span class=\"token punctuation\">(</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span>\n            active <span class=\"token operator\">?</span> <span class=\"token string\">'Active'</span> <span class=\"token punctuation\">:</span> <span class=\"token string\">'Inactive'</span><span class=\"token punctuation\">,</span>\n            style<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">TextStyle</span><span class=\"token punctuation\">(</span>fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">32.0</span><span class=\"token punctuation\">,</span> color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        width<span class=\"token punctuation\">:</span> <span class=\"token number\">200.0</span><span class=\"token punctuation\">,</span>\n        height<span class=\"token punctuation\">:</span> <span class=\"token number\">200.0</span><span class=\"token punctuation\">,</span>\n        decoration<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">BoxDecoration</span><span class=\"token punctuation\">(</span>\n          color<span class=\"token punctuation\">:</span> active <span class=\"token operator\">?</span> Colors<span class=\"token punctuation\">.</span>lightGreen<span class=\"token punctuation\">[</span><span class=\"token number\">700</span><span class=\"token punctuation\">]</span> <span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">[</span><span class=\"token number\">600</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h3 id=\"_3-2-3-混合状态管理\"><a href=\"#_3-2-3-混合状态管理\" class=\"header-anchor\">#</a> 3.2.3 混合状态管理</h3> <p>对于一些组件来说，混合管理的方式会非常有用。在这种情况下，组件自身管理一些内部状态，而父组件管理一些其他外部状态。</p> <p>在下面TapboxC示例中，手指按下时，盒子的周围会出现一个深绿色的边框，抬起时，边框消失。点击完成后，盒子的颜色改变。 TapboxC将其<code>_active</code>状态导出到其父组件中，但在内部管理其<code>_highlight</code>状态。这个例子有两个状态对象<code>_ParentWidgetState</code>和<code>_TapboxCState</code>。</p> <p><code>_ParentWidgetStateC</code>类:</p> <ul><li>管理<code>_active</code> 状态。</li> <li>实现 <code>_handleTapboxChanged()</code> ，当盒子被点击时调用。</li> <li>当点击盒子并且<code>_active</code>状态改变时调用<code>setState()</code>更新UI。</li></ul> <p><code>_TapboxCState</code> 对象:</p> <ul><li>管理<code>_highlight</code> 状态。</li> <li><code>GestureDetector</code>监听所有tap事件。当用户点下时，它添加高亮（深绿色边框）；当用户释放时，会移除高亮。</li> <li>当按下、抬起、或者取消点击时更新<code>_highlight</code>状态，调用<code>setState()</code>更新UI。</li> <li>当点击时，将状态的改变传递给父组件。</li></ul> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//---------------------------- ParentWidget ----------------------------</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">ParentWidgetC</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _ParentWidgetCState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_ParentWidgetCState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_ParentWidgetCState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>ParentWidgetC<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  bool _active <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_handleTapboxChanged</span><span class=\"token punctuation\">(</span>bool newValue<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _active <span class=\"token operator\">=</span> newValue<span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Container</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">TapboxC</span><span class=\"token punctuation\">(</span>\n        active<span class=\"token punctuation\">:</span> _active<span class=\"token punctuation\">,</span>\n        onChanged<span class=\"token punctuation\">:</span> _handleTapboxChanged<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">//----------------------------- TapboxC ------------------------------</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">TapboxC</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">TapboxC</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>Key key<span class=\"token punctuation\">,</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>active<span class=\"token punctuation\">:</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span> <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>onChanged<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> bool active<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> ValueChanged<span class=\"token operator\">&lt;</span>bool<span class=\"token operator\">&gt;</span> onChanged<span class=\"token punctuation\">;</span>\n  \n  <span class=\"token metadata symbol\">@override</span>\n  _TapboxCState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_TapboxCState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_TapboxCState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>TapboxC<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  bool _highlight <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_handleTapDown</span><span class=\"token punctuation\">(</span>TapDownDetails details<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _highlight <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_handleTapUp</span><span class=\"token punctuation\">(</span>TapUpDetails details<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _highlight <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_handleTapCancel</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _highlight <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_handleTap</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    widget<span class=\"token punctuation\">.</span><span class=\"token function\">onChanged</span><span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>widget<span class=\"token punctuation\">.</span>active<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 在按下时添加绿色边框，当抬起时，取消高亮  </span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">GestureDetector</span><span class=\"token punctuation\">(</span>\n      onTapDown<span class=\"token punctuation\">:</span> _handleTapDown<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 处理按下事件</span>\n      onTapUp<span class=\"token punctuation\">:</span> _handleTapUp<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 处理抬起事件</span>\n      onTap<span class=\"token punctuation\">:</span> _handleTap<span class=\"token punctuation\">,</span>\n      onTapCancel<span class=\"token punctuation\">:</span> _handleTapCancel<span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Container</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Center</span><span class=\"token punctuation\">(</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span>widget<span class=\"token punctuation\">.</span>active <span class=\"token operator\">?</span> <span class=\"token string\">'Active'</span> <span class=\"token punctuation\">:</span> <span class=\"token string\">'Inactive'</span><span class=\"token punctuation\">,</span>\n              style<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">TextStyle</span><span class=\"token punctuation\">(</span>fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">32.0</span><span class=\"token punctuation\">,</span> color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        width<span class=\"token punctuation\">:</span> <span class=\"token number\">200.0</span><span class=\"token punctuation\">,</span>\n        height<span class=\"token punctuation\">:</span> <span class=\"token number\">200.0</span><span class=\"token punctuation\">,</span>\n        decoration<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">BoxDecoration</span><span class=\"token punctuation\">(</span>\n          color<span class=\"token punctuation\">:</span> widget<span class=\"token punctuation\">.</span>active <span class=\"token operator\">?</span> Colors<span class=\"token punctuation\">.</span>lightGreen<span class=\"token punctuation\">[</span><span class=\"token number\">700</span><span class=\"token punctuation\">]</span> <span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">[</span><span class=\"token number\">600</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n          border<span class=\"token punctuation\">:</span> _highlight\n              <span class=\"token operator\">?</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Border<span class=\"token punctuation\">.</span>all</span><span class=\"token punctuation\">(</span>\n                  color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>teal<span class=\"token punctuation\">[</span><span class=\"token number\">700</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                  width<span class=\"token punctuation\">:</span> <span class=\"token number\">10.0</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span>\n              <span class=\"token punctuation\">:</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>另一种实现可能会将高亮状态导出到父组件，但同时保持<code>_active</code>状态为内部状态，但如果你要将该TapBox给其它人使用，可能没有什么意义。 开发人员只会关心该框是否处于Active状态，而不在乎高亮显示是如何管理的，所以应该让TapBox内部处理这些细节。</p> <h3 id=\"_3-2-4-全局状态管理\"><a href=\"#_3-2-4-全局状态管理\" class=\"header-anchor\">#</a> 3.2.4 全局状态管理</h3> <p>当应用中需要一些跨组件（包括跨路由）的状态需要同步时，上面介绍的方法便很难胜任了。比如，我们有一个设置页，里面可以设置应用的语言，我们为了让设置实时生效，我们期望在语言状态发生改变时，APP中依赖应用语言的组件能够重新build一下，但这些依赖应用语言的组件和设置页并不在一起，所以这种情况用上面的方法很难管理。这时，正确的做法是通过一个全局状态管理器来处理这种相距较远的组件之间的通信。目前主要有两种办法：</p> <ol><li>实现一个全局的事件总线，将语言状态改变对应为一个事件，然后在APP中依赖应用语言的组件的<code>initState</code> 方法中订阅语言改变的事件。当用户在设置页切换语言后，我们发布语言改变事件，而订阅了此事件的组件就会收到通知，收到通知后调用<code>setState(...)</code>方法重新<code>build</code>一下自身即可。</li> <li>使用一些专门用于状态管理的包，如Provider、Redux，读者可以在pub上查看其详细信息。</li></ol> <p>本书将在&quot;功能型组件&quot;一章中介绍Provider包的实现原理及用法，同时也将会在&quot;事件处理与通知&quot;一章中实现一个全局事件总线，读者有需要可以直接翻看。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/121.aef1325d.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter3/text.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>3.3 文本及样式 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/24.bf1834df.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter3/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h2 id=\"_3-3-文本及样式\"><a href=\"#_3-3-文本及样式\" class=\"header-anchor\">#</a> 3.3 文本及样式</h2> <h3 id=\"_3-3-1-text\"><a href=\"#_3-3-1-text\" class=\"header-anchor\">#</a> 3.3.1 Text</h3> <p><code>Text</code>用于显示简单样式文本，它包含一些控制文本显示样式的一些属性，一个简单的例子如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hello world&quot;</span><span class=\"token punctuation\">,</span>\n  textAlign<span class=\"token punctuation\">:</span> TextAlign<span class=\"token punctuation\">.</span>left<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hello world! I'm Jack. &quot;</span><span class=\"token operator\">*</span><span class=\"token number\">4</span><span class=\"token punctuation\">,</span>\n  maxLines<span class=\"token punctuation\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n  overflow<span class=\"token punctuation\">:</span> TextOverflow<span class=\"token punctuation\">.</span>ellipsis<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hello world&quot;</span><span class=\"token punctuation\">,</span>\n  textScaleFactor<span class=\"token punctuation\">:</span> <span class=\"token number\">1.5</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>运行效果如图3-5所示：</p> <p><img src=\"/assets/img/3-5.e95eb747.png\" alt=\"image-20180829103242552\"></p> <ul><li><p><code>textAlign</code>：文本的对齐方式；可以选择左对齐、右对齐还是居中。注意，对齐的参考系是Text widget本身。本例中虽然是指定了居中对齐，但因为Text文本内容宽度不足一行，Text的宽度和文本内容长度相等，那么这时指定对齐方式是没有意义的，只有Text宽度大于文本内容长度时指定此属性才有意义。下面我们指定一个较长的字符串：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hello world &quot;</span><span class=\"token operator\">*</span><span class=\"token number\">6</span><span class=\"token punctuation\">,</span>  <span class=\"token comment\">//字符串重复六次</span>\n  textAlign<span class=\"token punctuation\">:</span> TextAlign<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>；\n</code></pre></div><p>运行效果如图3-6所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"image-20180829104807535\"></p></li></ul> <p>​      字符串内容超过一行，Text宽度等于屏幕宽度，第二行文本便会居中显示。</p> <ul><li><code>maxLines</code>、<code>overflow</code>：指定文本显示的最大行数，默认情况下，文本是自动折行的，如果指定此参数，则文本最多不会超过指定的行。如果有多余的文本，可以通过<code>overflow</code>来指定截断方式，默认是直接截断，本例中指定的截断方式<code>TextOverflow.ellipsis</code>，它会将多余文本截断后以省略符“...”表示；TextOverflow的其它截断方式请参考SDK文档。</li> <li><code>textScaleFactor</code>：代表文本相对于当前字体大小的缩放因子，相对于去设置文本的样式<code>style</code>属性的<code>fontSize</code>，它是调整字体大小的一个快捷方式。该属性的默认值可以通过<code>MediaQueryData.textScaleFactor</code>获得，如果没有<code>MediaQuery</code>，那么会默认值将为1.0。</li></ul> <h3 id=\"_3-3-2-textstyle\"><a href=\"#_3-3-2-textstyle\" class=\"header-anchor\">#</a> 3.3.2 TextStyle</h3> <p><code>TextStyle</code>用于指定文本显示的样式如颜色、字体、粗细、背景等。我们看一个示例：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hello world&quot;</span><span class=\"token punctuation\">,</span>\n  style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>\n    color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span>\n    fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">18.0</span><span class=\"token punctuation\">,</span>\n    height<span class=\"token punctuation\">:</span> <span class=\"token number\">1.2</span><span class=\"token punctuation\">,</span>  \n    fontFamily<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;Courier&quot;</span><span class=\"token punctuation\">,</span>\n    background<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Paint</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>color<span class=\"token operator\">=</span>Colors<span class=\"token punctuation\">.</span>yellow<span class=\"token punctuation\">,</span>\n    decoration<span class=\"token punctuation\">:</span>TextDecoration<span class=\"token punctuation\">.</span>underline<span class=\"token punctuation\">,</span>\n    decorationStyle<span class=\"token punctuation\">:</span> TextDecorationStyle<span class=\"token punctuation\">.</span>dashed\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>效果如图3-7所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"3-7\"></p> <p>此示例只展示了TextStyle的部分属性，它还有一些其它属性，属性名基本都是自解释的，在此不再赘述，读者可以查阅SDK文档。值得注意的是：</p> <ul><li><p><code>height</code>：该属性用于指定行高，但它并不是一个绝对值，而是一个因子，具体的行高等于<code>fontSize</code>*<code>height</code>。</p></li> <li><p><code>fontFamily</code> ：由于不同平台默认支持的字体集不同，所以在手动指定字体时一定要先在不同平台测试一下。</p></li> <li><p><code>fontSize</code>：该属性和Text的<code>textScaleFactor</code>都用于控制字体大小。但是有两个主要区别：</p> <ul><li><code>fontSize</code>可以精确指定字体大小，而<code>textScaleFactor</code>只能通过缩放比例来控制。</li> <li><code>textScaleFactor</code>主要是用于系统字体大小设置改变时对Flutter应用字体进行全局调整，而<code>fontSize</code>通常用于单个文本，字体大小不会跟随系统字体大小变化。</li></ul></li></ul> <h3 id=\"_3-3-3-textspan\"><a href=\"#_3-3-3-textspan\" class=\"header-anchor\">#</a> 3.3.3 TextSpan</h3> <p>在上面的例子中，Text的所有文本内容只能按同一种样式，如果我们需要对一个Text内容的不同部分按照不同的样式显示，这时就可以使用<code>TextSpan</code>，它代表文本的一个“片段”。我们看看TextSpan的定义:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">const</span> <span class=\"token function\">TextSpan</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  TextStyle style<span class=\"token punctuation\">,</span> \n  Sting text<span class=\"token punctuation\">,</span>\n  List<span class=\"token operator\">&lt;</span>TextSpan<span class=\"token operator\">&gt;</span> children<span class=\"token punctuation\">,</span>\n  GestureRecognizer recognizer<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>其中<code>style</code> 和 <code>text</code>属性代表该文本片段的样式和内容。  <code>children</code>是一个<code>TextSpan</code>的数组，也就是说<code>TextSpan</code>可以包括其他<code>TextSpan</code>。而<code>recognizer</code>用于对该文本片段上用于手势进行识别处理。下面我们看一个效果（图3-8），然后用<code>TextSpan</code>实现它。</p> <p><img src=\"/assets/img/3-8.cc415da2.png\" alt=\"3-8\"></p> <p>源码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Text<span class=\"token punctuation\">.</span><span class=\"token function\">rich</span><span class=\"token punctuation\">(</span><span class=\"token function\">TextSpan</span><span class=\"token punctuation\">(</span>\n    children<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>\n     <span class=\"token function\">TextSpan</span><span class=\"token punctuation\">(</span>\n       text<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;Home: &quot;</span>\n     <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n     <span class=\"token function\">TextSpan</span><span class=\"token punctuation\">(</span>\n       text<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;https://flutterchina.club&quot;</span><span class=\"token punctuation\">,</span>\n       style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>\n         color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue\n       <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>  \n       recognizer<span class=\"token punctuation\">:</span> _tapRecognizer\n     <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n</code></pre></div><ul><li>上面代码中，我们通过TextSpan实现了一个基础文本片段和一个链接片段，然后通过<code>Text.rich</code> 方法将<code>TextSpan</code> 添加到Text中，之所以可以这样做，是因为Text其实就是RichText的一个包装，而RichText是可以显示多种样式(富文本)的widget。</li> <li><code>_tapRecognizer</code>，它是点击链接后的一个处理器（代码已省略），关于手势识别的更多内容我们将在后面单独介绍。</li></ul> <h3 id=\"_3-3-4-defaulttextstyle\"><a href=\"#_3-3-4-defaulttextstyle\" class=\"header-anchor\">#</a> 3.3.4 DefaultTextStyle</h3> <p>在Widget树中，文本的样式默认是可以被继承的（子类文本类组件未指定具体样式时可以使用Widget树中父级设置的默认样式），因此，如果在Widget树的某一个节点处设置一个默认的文本样式，那么该节点的子树中所有文本都会默认使用这个样式，而<code>DefaultTextStyle</code>正是用于设置默认文本样式的。下面我们看一个例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">DefaultTextStyle</span><span class=\"token punctuation\">(</span>\n  <span class=\"token comment\">//1.设置文本默认样式  </span>\n  style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>\n    color<span class=\"token punctuation\">:</span>Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span>\n    fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">20.0</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  textAlign<span class=\"token punctuation\">:</span> TextAlign<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n    crossAxisAlignment<span class=\"token punctuation\">:</span> CrossAxisAlignment<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n    children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n      <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;hello world&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;I am Jack&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;I am Jack&quot;</span><span class=\"token punctuation\">,</span>\n        style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>\n          inherit<span class=\"token punctuation\">:</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//2.不继承默认样式</span>\n          color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>上面代码中，我们首先设置了一个默认的文本样式，即字体为20像素(逻辑像素)、颜色为红色。然后通过<code>DefaultTextStyle</code> 设置给了子树Column节点处，这样一来Column的所有子孙Text默认都会继承该样式，除非Text显示指定不继承样式，如代码中注释2。示例运行效果如图3-9：</p> <p><img src=\"/assets/img/3-9.bb214e70.png\" alt=\"3-9\"></p> <h3 id=\"_3-3-5-字体\"><a href=\"#_3-3-5-字体\" class=\"header-anchor\">#</a> 3.3.5 字体</h3> <p>可以在Flutter应用程序中使用不同的字体。例如，我们可能会使用设计人员创建的自定义字体，或者其它第三方的字体，如<a href=\"https://fonts.google.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Google Fonts<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>中的字体。本节将介绍如何为Flutter应用配置字体，并在渲染文本时使用它们。</p> <p>在Flutter中使用字体分两步完成。首先在<code>pubspec.yaml</code>中声明它们，以确保它们会打包到应用程序中。然后通过<a href=\"https://docs.flutter.io/flutter/painting/TextStyle-class.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>TextStyle</code><span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>属性使用字体。</p> <h4 id=\"在asset中声明\"><a href=\"#在asset中声明\" class=\"header-anchor\">#</a> 在asset中声明</h4> <p>要将字体文件打包到应用中，和使用其它资源一样，要先在<code>pubspec.yaml</code>中声明它。然后将字体文件复制到在<code>pubspec.yaml</code>中指定的位置。如：</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code><span class=\"token key atrule\">flutter</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">fonts</span><span class=\"token punctuation\">:</span>\n    <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">family</span><span class=\"token punctuation\">:</span> Raleway\n      <span class=\"token key atrule\">fonts</span><span class=\"token punctuation\">:</span>\n        <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">asset</span><span class=\"token punctuation\">:</span> assets/fonts/Raleway<span class=\"token punctuation\">-</span>Regular.ttf\n        <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">asset</span><span class=\"token punctuation\">:</span> assets/fonts/Raleway<span class=\"token punctuation\">-</span>Medium.ttf\n          <span class=\"token key atrule\">weight</span><span class=\"token punctuation\">:</span> <span class=\"token number\">500</span>\n        <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">asset</span><span class=\"token punctuation\">:</span> assets/fonts/Raleway<span class=\"token punctuation\">-</span>SemiBold.ttf\n          <span class=\"token key atrule\">weight</span><span class=\"token punctuation\">:</span> <span class=\"token number\">600</span>\n    <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">family</span><span class=\"token punctuation\">:</span> AbrilFatface\n      <span class=\"token key atrule\">fonts</span><span class=\"token punctuation\">:</span>\n        <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">asset</span><span class=\"token punctuation\">:</span> assets/fonts/abrilfatface/AbrilFatface<span class=\"token punctuation\">-</span>Regular.ttf\n</code></pre></div><h4 id=\"使用字体\"><a href=\"#使用字体\" class=\"header-anchor\">#</a> 使用字体</h4> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">// 声明文本样式</span>\n<span class=\"token keyword\">const</span> textStyle <span class=\"token operator\">=</span> <span class=\"token keyword\">const</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>\n  fontFamily<span class=\"token punctuation\">:</span> <span class=\"token string\">'Raleway'</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// 使用文本样式</span>\n<span class=\"token keyword\">var</span> buttonText <span class=\"token operator\">=</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>\n  <span class=\"token string\">&quot;Use the font for this text&quot;</span><span class=\"token punctuation\">,</span>\n  style<span class=\"token punctuation\">:</span> textStyle<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><h4 id=\"package中的字体\"><a href=\"#package中的字体\" class=\"header-anchor\">#</a> Package中的字体</h4> <p>要使用Package中定义的字体，<strong>必须提供<code>package</code>参数</strong>。例如，假设上面的字体声明位于<code>my_package</code>包中。然后创建TextStyle的过程如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">const</span> textStyle <span class=\"token operator\">=</span> <span class=\"token keyword\">const</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>\n  fontFamily<span class=\"token punctuation\">:</span> <span class=\"token string\">'Raleway'</span><span class=\"token punctuation\">,</span>\n  package<span class=\"token punctuation\">:</span> <span class=\"token string\">'my_package'</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//指定包名</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>如果在package包内部使用它自己定义的字体，也应该在创建文本样式时指定<code>package</code>参数，如上例所示。</p> <p>一个包也可以只提供字体文件而不需要在pubspec.yaml中声明。 这些文件应该存放在包的<code>lib/</code>文件夹中。字体文件不会自动绑定到应用程序中，应用程序可以在声明字体时有选择地使用这些字体。假设一个名为my_package的包中有一个字体文件：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>lib/fonts/Raleway-Medium.ttf\n</code></pre></div><p>然后，应用程序可以声明一个字体，如下面的示例所示：</p> <div class=\"language-yaml extra-class\"><pre class=\"language-yaml\"><code> <span class=\"token key atrule\">flutter</span><span class=\"token punctuation\">:</span>\n   <span class=\"token key atrule\">fonts</span><span class=\"token punctuation\">:</span>\n     <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">family</span><span class=\"token punctuation\">:</span> Raleway\n       <span class=\"token key atrule\">fonts</span><span class=\"token punctuation\">:</span>\n         <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">asset</span><span class=\"token punctuation\">:</span> assets/fonts/Raleway<span class=\"token punctuation\">-</span>Regular.ttf\n         <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">asset</span><span class=\"token punctuation\">:</span> packages/my_package/fonts/Raleway<span class=\"token punctuation\">-</span>Medium.ttf\n           <span class=\"token key atrule\">weight</span><span class=\"token punctuation\">:</span> <span class=\"token number\">500</span>\n</code></pre></div><p><code>lib/</code>是隐含的，所以它不应该包含在asset路径中。</p> <p>在这种情况下，由于应用程序本地定义了字体，所以在创建TextStyle时可以不指定<code>package</code>参数：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">const</span> textStyle <span class=\"token operator\">=</span> <span class=\"token keyword\">const</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>\n  fontFamily<span class=\"token punctuation\">:</span> <span class=\"token string\">'Raleway'</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/24.bf1834df.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter4/alignment.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>4.6 对齐与相对定位（Align） | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/35.a9c7aa47.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter4/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_4-6-对齐与相对定位-align\"><a href=\"#_4-6-对齐与相对定位-align\" class=\"header-anchor\">#</a> 4.6 对齐与相对定位（Align）</h1> <p>在上一节中我们讲过通过<code>Stack</code>和<code>Positioned</code>，我们可以指定一个或多个子元素相对于父元素各个边的精确偏移，并且可以重叠。但如果我们只想简单的调整<strong>一个</strong>子元素在父元素中的位置的话，使用<code>Align</code>组件会更简单一些。</p> <h2 id=\"_4-6-1-align\"><a href=\"#_4-6-1-align\" class=\"header-anchor\">#</a> 4.6.1 Align</h2> <p><code>Align</code> 组件可以调整子组件的位置，并且可以根据子组件的宽高来确定自身的的宽高，定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Align</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  Key key<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>alignment <span class=\"token operator\">=</span> Alignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>widthFactor<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>heightFactor<span class=\"token punctuation\">,</span>\n  Widget child<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><ul><li><code>alignment</code> : 需要一个<code>AlignmentGeometry</code>类型的值，表示子组件在父组件中的起始位置。<code>AlignmentGeometry</code> 是一个抽象类，它有两个常用的子类：<code>Alignment</code>和 <code>FractionalOffset</code>，我们将在下面的示例中详细介绍。</li> <li><code>widthFactor</code>和<code>heightFactor</code>是用于确定<code>Align</code> 组件本身宽高的属性；它们是两个缩放因子，会分别乘以子元素的宽、高，最终的结果就是<code>Align</code> 组件的宽高。如果值为<code>null</code>，则组件的宽高将会占用尽可能多的空间。</li></ul> <h3 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h3> <p>我们先来看一个简单的例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n  height<span class=\"token punctuation\">:</span> <span class=\"token number\">120.0</span><span class=\"token punctuation\">,</span>\n  width<span class=\"token punctuation\">:</span> <span class=\"token number\">120.0</span><span class=\"token punctuation\">,</span>\n  color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">[</span><span class=\"token number\">50</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Align</span><span class=\"token punctuation\">(</span>\n    alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>topRight<span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">FlutterLogo</span><span class=\"token punctuation\">(</span>\n      size<span class=\"token punctuation\">:</span> <span class=\"token number\">60</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行效果如图4-11所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图4-11\"></p> <p><code>FlutterLogo</code> 是Flutter SDK提供的一个组件，内容就是Flutter的商标。在上面的例子中，我们显式指定了<code>Container</code>的宽、高都为120。如果我们不显式指定宽高，而通过同时指定<code>widthFactor</code>和<code>heightFactor</code> 为2也是可以达到同样的效果：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Align</span><span class=\"token punctuation\">(</span>\n  widthFactor<span class=\"token punctuation\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span>\n  heightFactor<span class=\"token punctuation\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span>\n  alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>topRight<span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">FlutterLogo</span><span class=\"token punctuation\">(</span>\n    size<span class=\"token punctuation\">:</span> <span class=\"token number\">60</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</code></pre></div><p>因为<code>FlutterLogo</code>的宽高为60，则<code>Align</code>的最终宽高都为<code>2*60=120</code>。</p> <p>另外，我们通过<code>Alignment.topRight</code>将<code>FlutterLogo</code>定位在<code>Container</code>的右上角。那<code>Alignment.topRight</code>是什么呢？通过源码我们可以看到其定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//右上角</span>\n<span class=\"token keyword\">static</span> <span class=\"token keyword\">const</span> Alignment topRight <span class=\"token operator\">=</span> <span class=\"token function\">Alignment</span><span class=\"token punctuation\">(</span><span class=\"token number\">1.0</span><span class=\"token punctuation\">,</span> <span class=\"token operator\">-</span><span class=\"token number\">1.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>可以看到它只是<code>Alignment</code>的一个实例，下面我们介绍一下<code>Alignment</code>。</p> <h3 id=\"alignment\"><a href=\"#alignment\" class=\"header-anchor\">#</a> Alignment</h3> <p><code>Alignment</code>继承自<code>AlignmentGeometry</code>，表示矩形内的一个点，他有两个属性<code>x</code>、<code>y</code>，分别表示在水平和垂直方向的偏移，<code>Alignment</code>定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Alignment</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>x<span class=\"token punctuation\">,</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>y<span class=\"token punctuation\">)</span>\n</code></pre></div><p><code>Alignment</code> Widget会以<strong>矩形的中心点作为坐标原点</strong>，即<code>Alignment(0.0, 0.0)</code> 。<code>x</code>、<code>y</code>的值从-1到1分别代表矩形左边到右边的距离和顶部到底边的距离，因此2个水平（或垂直）单位则等于矩形的宽（或高），如<code>Alignment(-1.0, -1.0)</code> 代表矩形的左侧顶点，而<code>Alignment(1.0, 1.0)</code>代表右侧底部终点，而<code>Alignment(1.0, -1.0)</code> 则正是右侧顶点，即<code>Alignment.topRight</code>。为了使用方便，矩形的原点、四个顶点，以及四条边的终点在<code>Alignment</code>类中都已经定义为了静态常量。</p> <p><code>Alignment</code>可以通过其<strong>坐标转换公式</strong>将其坐标转为子元素的具体偏移坐标：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>(Alignment.x*childWidth/2+childWidth/2, Alignment.y*childHeight/2+childHeight/2)\n</code></pre></div><p>其中<code>childWidth</code>为子元素的宽度，<code>childHeight</code>为子元素高度。</p> <p>现在我们再看看上面的示例，我们将<code>Alignment(1.0, -1.0)</code>带入上面公式，可得<code>FlutterLogo</code>的实际偏移坐标正是（60，0）。下面再看一个例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code> <span class=\"token function\">Align</span><span class=\"token punctuation\">(</span>\n  widthFactor<span class=\"token punctuation\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span>\n  heightFactor<span class=\"token punctuation\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span>\n  alignment<span class=\"token punctuation\">:</span> <span class=\"token function\">Alignment</span><span class=\"token punctuation\">(</span><span class=\"token number\">2</span><span class=\"token punctuation\">,</span><span class=\"token number\">0.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">FlutterLogo</span><span class=\"token punctuation\">(</span>\n    size<span class=\"token punctuation\">:</span> <span class=\"token number\">60</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>我们可以先想象一下运行效果：将<code>Alignment(2,0.0)</code>带入上述坐标转换公式，可以得到<code>FlutterLogo</code>的实际偏移坐标为（90，30）。实际运行如图4-12所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图4-12\"></p> <h3 id=\"fractionaloffset\"><a href=\"#fractionaloffset\" class=\"header-anchor\">#</a> FractionalOffset</h3> <p><code>FractionalOffset</code> 继承自 <code>Alignment</code>，它和 <code>Alignment</code>唯一的区别就是坐标原点不同！<code>FractionalOffset</code> 的坐标原点为矩形的左侧顶点，这和布局系统的一致，所以理解起来会比较容易。<code>FractionalOffset</code>的坐标转换公式为：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>实际偏移 = (FractionalOffse.x * childWidth, FractionalOffse.y * childHeight)\n</code></pre></div><p>下面看一个例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n  height<span class=\"token punctuation\">:</span> <span class=\"token number\">120.0</span><span class=\"token punctuation\">,</span>\n  width<span class=\"token punctuation\">:</span> <span class=\"token number\">120.0</span><span class=\"token punctuation\">,</span>\n  color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">[</span><span class=\"token number\">50</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Align</span><span class=\"token punctuation\">(</span>\n    alignment<span class=\"token punctuation\">:</span> <span class=\"token function\">FractionalOffset</span><span class=\"token punctuation\">(</span><span class=\"token number\">0.2</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0.6</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">FlutterLogo</span><span class=\"token punctuation\">(</span>\n      size<span class=\"token punctuation\">:</span> <span class=\"token number\">60</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>实际运行效果如图4-13所示下：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图4-13\"></p> <p>我们将<code>FractionalOffset(0.2, 0.6)</code>带入坐标转换公式得<code>FlutterLogo</code>实际偏移为（12，36），和实际运行效果吻合。</p> <h2 id=\"_4-6-2-align和stack对比\"><a href=\"#_4-6-2-align和stack对比\" class=\"header-anchor\">#</a> 4.6.2 Align和Stack对比</h2> <p>可以看到，<code>Align</code>和<code>Stack</code>/<code>Positioned</code>都可以用于指定子元素相对于父元素的偏移，但它们还是有两个主要区别：</p> <ol><li>定位参考系统不同；<code>Stack</code>/<code>Positioned</code>定位的的参考系可以是父容器矩形的四个顶点；而<code>Align</code>则需要先通过<code>alignment</code> 参数来确定坐标原点，不同的<code>alignment</code>会对应不同原点，最终的偏移是需要通过<code>alignment</code>的转换公式来计算出。</li> <li><code>Stack</code>可以有多个子元素，并且子元素可以堆叠，而<code>Align</code>只能有一个子元素，不存在堆叠。</li></ol> <h2 id=\"_4-6-3-center组件\"><a href=\"#_4-6-3-center组件\" class=\"header-anchor\">#</a> 4.6.3 Center组件</h2> <p>我们在前面章节的例子中已经使用过<code>Center</code>组件来居中子元素了，现在我们正式来介绍一下它。通过查找SDK源码，我们看到<code>Center</code>组件定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">Center</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">Align</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> Key key<span class=\"token punctuation\">,</span> double widthFactor<span class=\"token punctuation\">,</span> double heightFactor<span class=\"token punctuation\">,</span> Widget child <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">,</span> widthFactor<span class=\"token punctuation\">:</span> widthFactor<span class=\"token punctuation\">,</span> heightFactor<span class=\"token punctuation\">:</span> heightFactor<span class=\"token punctuation\">,</span> child<span class=\"token punctuation\">:</span> child<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以看到<code>Center</code>继承自<code>Align</code>，它比<code>Align</code>只少了一个<code>alignment</code> 参数；由于<code>Align</code>的构造函数中<code>alignment</code> 值为<code>Alignment.center</code>，所以，我们可以认为<code>Center</code>组件其实是对齐方式确定（<code>Alignment.center</code>）了的<code>Align</code>。</p> <p>上面我们讲过当<code>widthFactor</code>或<code>heightFactor</code>为<code>null</code>时组件的宽高将会占用尽可能多的空间，这一点需要特别注意，我们通过一个示例说明：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token comment\">//省略无关代码</span>\n<span class=\"token function\">DecoratedBox</span><span class=\"token punctuation\">(</span>\n  decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;xxx&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token function\">DecoratedBox</span><span class=\"token punctuation\">(</span>\n  decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n    widthFactor<span class=\"token punctuation\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n    heightFactor<span class=\"token punctuation\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;xxx&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行效果如图4-14所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图4-14\"></p> <h2 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h2> <p>本节重点介绍了<code>Align</code>组件及两种偏移类<code>Alignment</code> 和<code>FractionalOffset</code>，读者需要理解这两种偏移类的区别及各自的坐标转化公式。另外，在此建议读者在需要制定一些精确的偏移时应优先使用<code>FractionalOffset</code>，因为它的坐标原点和布局系统相同，能更容易算出实际偏移。</p> <p>在后面，我们又介绍了<code>Align</code>组件和<code>Stack</code>/<code>Positioned</code>、<code>Center</code>的关系，读者可以对比理解。</p> <p>还有，熟悉Web开发的同学可能会发现<code>Align</code>组件的特性和Web开发中相对定位（<code>position: relative</code>）非常像，是的！在大多数时候，我们可以直接使用<code>Align</code>组件来实现Web中相对定位的效果，读者可以类比记忆。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/35.a9c7aa47.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter4/flex.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>4.3 弹性布局（Flex） | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/122.59b7f284.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter4/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_4-3-弹性布局-flex\"><a href=\"#_4-3-弹性布局-flex\" class=\"header-anchor\">#</a> 4.3 弹性布局（Flex）</h1> <p>弹性布局允许子组件按照一定比例来分配父容器空间。弹性布局的概念在其它UI系统中也都存在，如H5中的弹性盒子布局，Android中的<code>FlexboxLayout</code>等。Flutter中的弹性布局主要通过<code>Flex</code>和<code>Expanded</code>来配合实现。</p> <h3 id=\"flex\"><a href=\"#flex\" class=\"header-anchor\">#</a> Flex</h3> <p><code>Flex</code>组件可以沿着水平或垂直方向排列子组件，如果你知道主轴方向，使用<code>Row</code>或<code>Column</code>会方便一些，因为<code>Row</code>和<code>Column</code>都继承自<code>Flex</code>，参数基本相同，所以能使用Flex的地方基本上都可以使用<code>Row</code>或<code>Column</code>。<code>Flex</code>本身功能是很强大的，它也可以和<code>Expanded</code>组件配合实现弹性布局。接下来我们只讨论<code>Flex</code>和弹性布局相关的属性(其它属性已经在介绍<code>Row</code>和<code>Column</code>时介绍过了)。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Flex</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>direction<span class=\"token punctuation\">,</span> <span class=\"token comment\">//弹性布局的方向, Row默认为水平方向，Column默认为垂直方向</span>\n  List<span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span> children <span class=\"token operator\">=</span> <span class=\"token keyword\">const</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p><code>Flex</code>继承自<code>MultiChildRenderObjectWidget</code>，对应的<code>RenderObject</code>为<code>RenderFlex</code>，<code>RenderFlex</code>中实现了其布局算法。</p> <h3 id=\"expanded\"><a href=\"#expanded\" class=\"header-anchor\">#</a> Expanded</h3> <p>可以按比例“扩伸” <code>Row</code>、<code>Column</code>和<code>Flex</code>子组件所占用的空间。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">const</span> <span class=\"token function\">Expanded</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  int flex <span class=\"token operator\">=</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span> \n  <span class=\"token metadata symbol\">@required</span> Widget child<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p><code>flex</code>参数为弹性系数，如果为0或<code>null</code>，则<code>child</code>是没有弹性的，即不会被扩伸占用的空间。如果大于0，所有的<code>Expanded</code>按照其flex的比例来分割主轴的全部空闲空间。下面我们看一个例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">FlexLayoutTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n      children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n        <span class=\"token comment\">//Flex的两个子widget按1：2来占据水平空间  </span>\n        <span class=\"token function\">Flex</span><span class=\"token punctuation\">(</span>\n          direction<span class=\"token punctuation\">:</span> Axis<span class=\"token punctuation\">.</span>horizontal<span class=\"token punctuation\">,</span>\n          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            <span class=\"token function\">Expanded</span><span class=\"token punctuation\">(</span>\n              flex<span class=\"token punctuation\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n                height<span class=\"token punctuation\">:</span> <span class=\"token number\">30.0</span><span class=\"token punctuation\">,</span>\n                color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token function\">Expanded</span><span class=\"token punctuation\">(</span>\n              flex<span class=\"token punctuation\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n                height<span class=\"token punctuation\">:</span> <span class=\"token number\">30.0</span><span class=\"token punctuation\">,</span>\n                color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>green<span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n          padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">only</span><span class=\"token punctuation\">(</span>top<span class=\"token punctuation\">:</span> <span class=\"token number\">20.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span>\n            height<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n            <span class=\"token comment\">//Flex的三个子widget，在垂直方向按2：1：1来占用100像素的空间  </span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Flex</span><span class=\"token punctuation\">(</span>\n              direction<span class=\"token punctuation\">:</span> Axis<span class=\"token punctuation\">.</span>vertical<span class=\"token punctuation\">,</span>\n              children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                <span class=\"token function\">Expanded</span><span class=\"token punctuation\">(</span>\n                  flex<span class=\"token punctuation\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span>\n                  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n                    height<span class=\"token punctuation\">:</span> <span class=\"token number\">30.0</span><span class=\"token punctuation\">,</span>\n                    color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token function\">Spacer</span><span class=\"token punctuation\">(</span>\n                  flex<span class=\"token punctuation\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token function\">Expanded</span><span class=\"token punctuation\">(</span>\n                  flex<span class=\"token punctuation\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n                  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n                    height<span class=\"token punctuation\">:</span> <span class=\"token number\">30.0</span><span class=\"token punctuation\">,</span>\n                    color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>green<span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行效果如图4-5所示：</p> <p><img src=\"/assets/img/4-5.67110f64.png\" alt=\"弹性布局\"></p> <p>示例中的<code>Spacer</code>的功能是占用指定比例的空间，实际上它只是<code>Expanded</code>的一个包装类，<code>Spacer</code>的源码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">Spacer</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function\">Spacer</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>Key key<span class=\"token punctuation\">,</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>flex <span class=\"token operator\">=</span> <span class=\"token number\">1</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">:</span> <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span>flex <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span>flex <span class=\"token operator\">&gt;</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  \n  <span class=\"token keyword\">final</span> int flex<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Expanded</span><span class=\"token punctuation\">(</span>\n      flex<span class=\"token punctuation\">:</span> flex<span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> SizedBox<span class=\"token punctuation\">.</span><span class=\"token function\">shrink</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h3 id=\"小结\"><a href=\"#小结\" class=\"header-anchor\">#</a> 小结</h3> <p>弹性布局比较简单，唯一需要注意的就是<code>Row</code>、<code>Column</code>以及<code>Flex</code>的关系。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/122.59b7f284.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter4/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>本章目录 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/209.95753ceb.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter4/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"本章目录\"><a href=\"#本章目录\" class=\"header-anchor\">#</a> 本章目录</h1> <ul><li><a href=\"/v2/chapter4/intro.html\">4.1：布局类组件简介</a></li> <li><a href=\"/v2/chapter4/row_and_column.html\">4.2：线性布局（Row、Column）</a></li> <li><a href=\"/v2/chapter4/flex.html\">4.3：弹性布局（Flex）</a></li> <li><a href=\"/v2/chapter4/wrap_and_flow.html\">4.4：流式布局（Wrap、Flow）</a></li> <li><a href=\"/v2/chapter4/stack.html\">4.5：层叠布局（Stack、Positioned）</a></li> <li><a href=\"/v2/chapter4/alignment.html\">4.6：对齐与相对定位（Align）</a></li></ul></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/209.95753ceb.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter4/intro.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>4.1 布局类组件简介 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/210.0c9a3892.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter4/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_4-1-布局类组件简介\"><a href=\"#_4-1-布局类组件简介\" class=\"header-anchor\">#</a> 4.1 布局类组件简介</h1> <p>布局类组件都会包含一个或多个子组件，不同的布局类组件对子组件排版(layout)方式不同。我们在前面说过<code>Element</code>树才是最终的绘制树，<code>Element</code>树是通过Widget树来创建的（通过<code>Widget.createElement()</code>），Widget其实就是Element的配置数据。在Flutter中，根据Widget是否需要包含子节点将Widget分为了三类，分别对应三种Element，如下表：</p> <table><thead><tr><th>Widget</th> <th>对应的Element</th> <th>用途</th></tr></thead> <tbody><tr><td>LeafRenderObjectWidget</td> <td>LeafRenderObjectElement</td> <td>Widget树的叶子节点，用于没有子节点的widget，通常基础组件都属于这一类，如Image。</td></tr> <tr><td>SingleChildRenderObjectWidget</td> <td>SingleChildRenderObjectElement</td> <td>包含一个子Widget，如：ConstrainedBox、DecoratedBox等</td></tr> <tr><td>MultiChildRenderObjectWidget</td> <td>MultiChildRenderObjectElement</td> <td>包含多个子Widget，一般都有一个children参数，接受一个Widget数组。如Row、Column、Stack等</td></tr></tbody></table> <blockquote><p>注意，Flutter中的很多Widget是直接继承自StatelessWidget或StatefulWidget，然后在<code>build()</code>方法中构建真正的RenderObjectWidget，如Text，它其实是继承自StatelessWidget，然后在<code>build()</code>方法中通过RichText来构建其子树，而RichText才是继承自MultiChildRenderObjectWidget。所以为了方便叙述，我们也可以直接说Text属于MultiChildRenderObjectWidget（其它widget也可以这么描述），这才是本质。读到这里我们也会发现，其实<strong>StatelessWidget和StatefulWidget就是两个用于组合Widget的基类，它们本身并不关联最终的渲染对象（RenderObjectWidget）</strong>。</p></blockquote> <p>布局类组件就是指直接或间接继承(包含)<code>MultiChildRenderObjectWidget</code>的Widget，它们一般都会有一个<code>children</code>属性用于接收子Widget。我们看一下继承关系 Widget &gt; RenderObjectWidget &gt; (Leaf/SingleChild/MultiChild)RenderObjectWidget 。</p> <p><code>RenderObjectWidget</code>类中定义了创建、更新<code>RenderObject</code>的方法，子类必须实现他们，关于<code>RenderObject</code>我们现在只需要知道它是最终布局、渲染UI界面的对象即可，也就是说，对于布局类组件来说，其布局算法都是通过对应的<code>RenderObject</code>对象来实现的，所以读者如果对接下来介绍的某个布局类组件的原理感兴趣，可以查看其对应的<code>RenderObject</code>的实现，比如<code>Stack</code>（层叠布局）对应的<code>RenderObject</code>对象就是<code>RenderStack</code>，而层叠布局的实现就在<code>RenderStack</code>中。</p> <p>在本章中，为了让读者对布局类Widget有个快速的认识，所以我们并不会深入到<code>RenderObject</code>的细节中去。在学习本章时，读者的重点是掌握不同布局组件的布局特点，具体原理和细节等我们对Flutter整体入门后，感兴趣的话再去研究。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/210.0c9a3892.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter4/row_and_column.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>4.2 线性布局（Row和Column） | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/36.d9eeb78f.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter4/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_4-2-线性布局-row和column\"><a href=\"#_4-2-线性布局-row和column\" class=\"header-anchor\">#</a> 4.2 线性布局（Row和Column）</h1> <p>所谓线性布局，即指沿水平或垂直方向排布子组件。Flutter中通过<code>Row</code>和<code>Column</code>来实现线性布局，类似于Android中的<code>LinearLayout</code>控件。<code>Row</code>和<code>Column</code>都继承自<code>Flex</code>，我们将在弹性布局一节中详细介绍<code>Flex</code>。</p> <h3 id=\"主轴和纵轴\"><a href=\"#主轴和纵轴\" class=\"header-anchor\">#</a> 主轴和纵轴</h3> <p>对于线性布局，有主轴和纵轴之分，如果布局是沿水平方向，那么主轴就是指水平方向，而纵轴即垂直方向；如果布局沿垂直方向，那么主轴就是指垂直方向，而纵轴就是水平方向。在线性布局中，有两个定义对齐方式的枚举类<code>MainAxisAlignment</code>和<code>CrossAxisAlignment</code>，分别代表主轴对齐和纵轴对齐。</p> <h3 id=\"row\"><a href=\"#row\" class=\"header-anchor\">#</a> Row</h3> <p>Row可以在水平方向排列其子widget。定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Row</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>  \n  TextDirection textDirection<span class=\"token punctuation\">,</span>    \n  MainAxisSize mainAxisSize <span class=\"token operator\">=</span> MainAxisSize<span class=\"token punctuation\">.</span>max<span class=\"token punctuation\">,</span>    \n  MainAxisAlignment mainAxisAlignment <span class=\"token operator\">=</span> MainAxisAlignment<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n  VerticalDirection verticalDirection <span class=\"token operator\">=</span> VerticalDirection<span class=\"token punctuation\">.</span>down<span class=\"token punctuation\">,</span>  \n  CrossAxisAlignment crossAxisAlignment <span class=\"token operator\">=</span> CrossAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n  List<span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span> children <span class=\"token operator\">=</span> <span class=\"token keyword\">const</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><ul><li><code>textDirection</code>：表示水平方向子组件的布局顺序(是从左往右还是从右往左)，默认为系统当前Locale环境的文本方向(如中文、英语都是从左往右，而阿拉伯语是从右往左)。</li> <li><code>mainAxisSize</code>：表示<code>Row</code>在主轴(水平)方向占用的空间，默认是<code>MainAxisSize.max</code>，表示尽可能多的占用水平方向的空间，此时无论子widgets实际占用多少水平空间，<code>Row</code>的宽度始终等于水平方向的最大宽度；而<code>MainAxisSize.min</code>表示尽可能少的占用水平空间，当子组件没有占满水平剩余空间，则<code>Row</code>的实际宽度等于所有子组件占用的的水平空间；</li> <li><code>mainAxisAlignment</code>：表示子组件在<code>Row</code>所占用的水平空间内对齐方式，如果<code>mainAxisSize</code>值为<code>MainAxisSize.min</code>，则此属性无意义，因为子组件的宽度等于<code>Row</code>的宽度。只有当<code>mainAxisSize</code>的值为<code>MainAxisSize.max</code>时，此属性才有意义，<code>MainAxisAlignment.start</code>表示沿<code>textDirection</code>的初始方向对齐，如<code>textDirection</code>取值为<code>TextDirection.ltr</code>时，则<code>MainAxisAlignment.start</code>表示左对齐，<code>textDirection</code>取值为<code>TextDirection.rtl</code>时表示从右对齐。而<code>MainAxisAlignment.end</code>和<code>MainAxisAlignment.start</code>正好相反；<code>MainAxisAlignment.center</code>表示居中对齐。读者可以这么理解：<code>textDirection</code>是<code>mainAxisAlignment</code>的参考系。</li> <li><code>verticalDirection</code>：表示<code>Row</code>纵轴（垂直）的对齐方向，默认是<code>VerticalDirection.down</code>，表示从上到下。</li> <li><code>crossAxisAlignment</code>：表示子组件在纵轴方向的对齐方式，<code>Row</code>的高度等于子组件中最高的子元素高度，它的取值和<code>MainAxisAlignment</code>一样(包含<code>start</code>、<code>end</code>、 <code>center</code>三个值)，不同的是<code>crossAxisAlignment</code>的参考系是<code>verticalDirection</code>，即<code>verticalDirection</code>值为<code>VerticalDirection.down</code>时<code>crossAxisAlignment.start</code>指顶部对齐，<code>verticalDirection</code>值为<code>VerticalDirection.up</code>时，<code>crossAxisAlignment.start</code>指底部对齐；而<code>crossAxisAlignment.end</code>和<code>crossAxisAlignment.start</code>正好相反；</li> <li><code>children</code> ：子组件数组。</li></ul> <h3 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h3> <p>请阅读下面代码，先想象一下运行的结果：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n  <span class=\"token comment\">//测试Row对齐方式，排除Column默认居中对齐的干扰</span>\n  crossAxisAlignment<span class=\"token punctuation\">:</span> CrossAxisAlignment<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n      mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n      children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n        <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot; hello world &quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot; I am Jack &quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n      mainAxisSize<span class=\"token punctuation\">:</span> MainAxisSize<span class=\"token punctuation\">.</span>min<span class=\"token punctuation\">,</span>\n      mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n      children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n        <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot; hello world &quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot; I am Jack &quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n      mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>end<span class=\"token punctuation\">,</span>\n      textDirection<span class=\"token punctuation\">:</span> TextDirection<span class=\"token punctuation\">.</span>rtl<span class=\"token punctuation\">,</span>\n      children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n        <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot; hello world &quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot; I am Jack &quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n      crossAxisAlignment<span class=\"token punctuation\">:</span> CrossAxisAlignment<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>  \n      verticalDirection<span class=\"token punctuation\">:</span> VerticalDirection<span class=\"token punctuation\">.</span>up<span class=\"token punctuation\">,</span>\n      children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n        <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot; hello world &quot;</span><span class=\"token punctuation\">,</span> style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">30.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot; I am Jack &quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>实际运行结果如图4-1所示：</p> <p><img src=\"/assets/img/4-1.86777353.png\" alt=\"图4-1\"></p> <p>解释：第一个<code>Row</code>很简单，默认为居中对齐；第二个<code>Row</code>，由于<code>mainAxisSize</code>值为<code>MainAxisSize.min</code>，<code>Row</code>的宽度等于两个<code>Text</code>的宽度和，所以对齐是无意义的，所以会从左往右显示；第三个<code>Row</code>设置<code>textDirection</code>值为<code>TextDirection.rtl</code>，所以子组件会从右向左的顺序排列，而此时<code>MainAxisAlignment.end</code>表示左对齐，所以最终显示结果就是图中第三行的样子；第四个Row测试的是纵轴的对齐方式，由于两个子Text字体不一样，所以其高度也不同，我们指定了<code>verticalDirection</code>值为<code>VerticalDirection.up</code>，即从低向顶排列，而此时<code>crossAxisAlignment</code>值为<code>CrossAxisAlignment.start</code>表示底对齐。</p> <h3 id=\"column\"><a href=\"#column\" class=\"header-anchor\">#</a> Column</h3> <p><code>Column</code>可以在垂直方向排列其子组件。参数和<code>Row</code>一样，不同的是布局方向为垂直，主轴纵轴正好相反，读者可类比<code>Row</code>来理解，下面看一个例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">CenterColumnRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n      crossAxisAlignment<span class=\"token punctuation\">:</span> CrossAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n      children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n        <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;hi&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;world&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行效果如图4-2所示：</p> <p><img src=\"/assets/img/4-2.c3de517d.png\" alt=\"图4-2示例\"></p> <p>解释：</p> <ul><li>由于我们没有指定<code>Column</code>的<code>mainAxisSize</code>，所以使用默认值<code>MainAxisSize.max</code>，则<code>Column</code>会在垂直方向占用尽可能多的空间，此例中为屏幕高度。</li> <li>由于我们指定了 <code>crossAxisAlignment</code> 属性为<code>CrossAxisAlignment.center</code>，那么子项在<code>Column</code>纵轴方向（此时为水平方向）会居中对齐。注意，在水平方向对齐是有边界的，总宽度为<code>Column</code>占用空间的实际宽度，而实际的宽度取决于子项中宽度最大的Widget。在本例中，<code>Column</code>有两个子Widget，而显示“world”的<code>Text</code>宽度最大，所以<code>Column</code>的实际宽度则为<code>Text(&quot;world&quot;)</code> 的宽度，所以居中对齐后<code>Text(&quot;hi&quot;)</code>会显示在<code>Text(&quot;world&quot;)</code>的中间部分。</li></ul> <p><strong>实际上，<code>Row</code>和<code>Column</code>都只会在主轴方向占用尽可能大的空间，而纵轴的长度则取决于他们最大子元素的长度</strong>。如果我们想让本例中的两个文本控件在整个手机屏幕中间对齐，我们有两种方法：</p> <ul><li><p>将<code>Column</code>的宽度指定为屏幕宽度；这很简单，我们可以通过<code>ConstrainedBox</code>或<code>SizedBox</code>（我们将在后面章节中专门介绍这两个Widget）来强制更改宽度限制，例如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">ConstrainedBox</span><span class=\"token punctuation\">(</span>\n  constraints<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxConstraints</span><span class=\"token punctuation\">(</span>minWidth<span class=\"token punctuation\">:</span> double<span class=\"token punctuation\">.</span>infinity<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> \n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n    crossAxisAlignment<span class=\"token punctuation\">:</span> CrossAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n    children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n      <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;hi&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;world&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>将<code>minWidth</code>设为<code>double.infinity</code>，可以使宽度占用尽可能多的空间。</p></li> <li><p>使用<code>Center</code> Widget；我们将在后面章节中介绍。</p></li></ul> <h3 id=\"特殊情况\"><a href=\"#特殊情况\" class=\"header-anchor\">#</a> 特殊情况</h3> <p>如果<code>Row</code>里面嵌套<code>Row</code>，或者<code>Column</code>里面再嵌套<code>Column</code>，那么只有最外面的<code>Row</code>或<code>Column</code>会占用尽可能大的空间，里面<code>Row</code>或<code>Column</code>所占用的空间为实际大小，下面以<code>Column</code>为例说明：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n  color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>green<span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n    padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">16.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n      crossAxisAlignment<span class=\"token punctuation\">:</span> CrossAxisAlignment<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n      mainAxisSize<span class=\"token punctuation\">:</span> MainAxisSize<span class=\"token punctuation\">.</span>max<span class=\"token punctuation\">,</span> <span class=\"token comment\">//有效，外层Colum高度为整个屏幕</span>\n      children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n        <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n          color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n            mainAxisSize<span class=\"token punctuation\">:</span> MainAxisSize<span class=\"token punctuation\">.</span>max<span class=\"token punctuation\">,</span><span class=\"token comment\">//无效，内层Colum高度为实际高度  </span>\n            children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n              <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;hello world &quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;I am Jack &quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>运行效果如图4-3所示：</p> <p><img src=\"/assets/img/4-3.cc8d50f6.png\" alt=\"图4-3\"></p> <p>如果要让里面的<code>Column</code>占满外部<code>Column</code>，可以使用<code>Expanded</code> 组件：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Expanded</span><span class=\"token punctuation\">(</span> \n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n    color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n      mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span> <span class=\"token comment\">//垂直方向居中对齐</span>\n      children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n        <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;hello world &quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;I am Jack &quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行效果如图4-4所示：</p> <p><img src=\"/assets/img/4-4.00885ea7.png\" alt=\"图4-4\"></p> <p>我们将在介绍弹性布局时详细介绍Expanded。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/36.d9eeb78f.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter4/stack.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>4.5 层叠布局 Stack、Positioned | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/73.dcf07497.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter4/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_4-5-层叠布局-stack、positioned\"><a href=\"#_4-5-层叠布局-stack、positioned\" class=\"header-anchor\">#</a> 4.5 层叠布局 Stack、Positioned</h1> <p>层叠布局和Web中的绝对定位、Android中的Frame布局是相似的，子组件可以根据距父容器四个角的位置来确定自身的位置。绝对定位允许子组件堆叠起来（按照代码中声明的顺序）。Flutter中使用<code>Stack</code>和<code>Positioned</code>这两个组件来配合实现绝对定位。<code>Stack</code>允许子组件堆叠，而<code>Positioned</code>用于根据<code>Stack</code>的四个角来确定子组件的位置。</p> <h3 id=\"stack\"><a href=\"#stack\" class=\"header-anchor\">#</a> Stack</h3> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Stack</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>alignment <span class=\"token operator\">=</span> AlignmentDirectional<span class=\"token punctuation\">.</span>topStart<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>textDirection<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>fit <span class=\"token operator\">=</span> StackFit<span class=\"token punctuation\">.</span>loose<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>overflow <span class=\"token operator\">=</span> Overflow<span class=\"token punctuation\">.</span>clip<span class=\"token punctuation\">,</span>\n  List<span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span> children <span class=\"token operator\">=</span> <span class=\"token keyword\">const</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><ul><li><code>alignment</code>：此参数决定如何去对齐没有定位（没有使用<code>Positioned</code>）或部分定位的子组件。所谓部分定位，在这里<strong>特指没有在某一个轴上定位：</strong><code>left</code>、<code>right</code>为横轴，<code>top</code>、<code>bottom</code>为纵轴，只要包含某个轴上的一个定位属性就算在该轴上有定位。</li> <li><code>textDirection</code>：和<code>Row</code>、<code>Wrap</code>的<code>textDirection</code>功能一样，都用于确定<code>alignment</code>对齐的参考系，即：<code>textDirection</code>的值为<code>TextDirection.ltr</code>，则<code>alignment</code>的<code>start</code>代表左，<code>end</code>代表右，即<code>从左往右</code>的顺序；<code>textDirection</code>的值为<code>TextDirection.rtl</code>，则alignment的<code>start</code>代表右，<code>end</code>代表左，即<code>从右往左</code>的顺序。</li> <li><code>fit</code>：此参数用于确定<strong>没有定位</strong>的子组件如何去适应<code>Stack</code>的大小。<code>StackFit.loose</code>表示使用子组件的大小，<code>StackFit.expand</code>表示扩伸到<code>Stack</code>的大小。</li> <li><code>overflow</code>：此属性决定如何显示超出<code>Stack</code>显示空间的子组件；值为<code>Overflow.clip</code>时，超出部分会被剪裁（隐藏），值为<code>Overflow.visible</code> 时则不会。</li></ul> <h3 id=\"positioned\"><a href=\"#positioned\" class=\"header-anchor\">#</a> Positioned</h3> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">const</span> <span class=\"token function\">Positioned</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  Key key<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>left<span class=\"token punctuation\">,</span> \n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>top<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>right<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>bottom<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>width<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>height<span class=\"token punctuation\">,</span>\n  <span class=\"token metadata symbol\">@required</span> Widget child<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p><code>left</code>、<code>top</code> 、<code>right</code>、 <code>bottom</code>分别代表离<code>Stack</code>左、上、右、底四边的距离。<code>width</code>和<code>height</code>用于指定需要定位元素的宽度和高度。注意，<code>Positioned</code>的<code>width</code>、<code>height</code> 和其它地方的意义稍微有点区别，此处用于配合<code>left</code>、<code>top</code> 、<code>right</code>、 <code>bottom</code>来定位组件，举个例子，在水平方向时，你只能指定<code>left</code>、<code>right</code>、<code>width</code>三个属性中的两个，如指定<code>left</code>和<code>width</code>后，<code>right</code>会自动算出(<code>left</code>+<code>width</code>)，如果同时指定三个属性则会报错，垂直方向同理。</p> <h3 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h3> <p>在下面的例子中，我们通过对几个<code>Text</code>组件的定位来演示<code>Stack</code>和<code>Positioned</code>的特性：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//通过ConstrainedBox来确保Stack占满屏幕</span>\n<span class=\"token function\">ConstrainedBox</span><span class=\"token punctuation\">(</span>\n  constraints<span class=\"token punctuation\">:</span> BoxConstraints<span class=\"token punctuation\">.</span><span class=\"token function\">expand</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Stack</span><span class=\"token punctuation\">(</span>\n    alignment<span class=\"token punctuation\">:</span>Alignment<span class=\"token punctuation\">.</span>center <span class=\"token punctuation\">,</span> <span class=\"token comment\">//指定未定位或部分定位widget的对齐方式</span>\n    children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n      <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hello world&quot;</span><span class=\"token punctuation\">,</span>style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token function\">Positioned</span><span class=\"token punctuation\">(</span>\n        left<span class=\"token punctuation\">:</span> <span class=\"token number\">18.0</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;I am Jack&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token function\">Positioned</span><span class=\"token punctuation\">(</span>\n        top<span class=\"token punctuation\">:</span> <span class=\"token number\">18.0</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Your friend&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span>        \n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>运行效果见图4-9：</p> <p><img src=\"/assets/img/4-9.7897e012.png\" alt=\"图4-9\"></p> <p>由于第一个子文本组件<code>Text(&quot;Hello world&quot;)</code>没有指定定位，并且<code>alignment</code>值为<code>Alignment.center</code>，所以它会居中显示。第二个子文本组件<code>Text(&quot;I am Jack&quot;)</code>只指定了水平方向的定位(<code>left</code>)，所以属于部分定位，即垂直方向上没有定位，那么它在垂直方向的对齐方式则会按照<code>alignment</code>指定的对齐方式对齐，即垂直方向居中。对于第三个子文本组件<code>Text(&quot;Your friend&quot;)</code>，和第二个<code>Text</code>原理一样，只不过是水平方向没有定位，则水平方向居中。</p> <p>我们给上例中的<code>Stack</code>指定一个<code>fit</code>属性，然后将三个子文本组件的顺序调整一下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Stack</span><span class=\"token punctuation\">(</span>\n  alignment<span class=\"token punctuation\">:</span>Alignment<span class=\"token punctuation\">.</span>center <span class=\"token punctuation\">,</span>\n  fit<span class=\"token punctuation\">:</span> StackFit<span class=\"token punctuation\">.</span>expand<span class=\"token punctuation\">,</span> <span class=\"token comment\">//未定位widget占满Stack整个空间</span>\n  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token function\">Positioned</span><span class=\"token punctuation\">(</span>\n      left<span class=\"token punctuation\">:</span> <span class=\"token number\">18.0</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;I am Jack&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hello world&quot;</span><span class=\"token punctuation\">,</span>style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Positioned</span><span class=\"token punctuation\">(</span>\n      top<span class=\"token punctuation\">:</span> <span class=\"token number\">18.0</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Your friend&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</code></pre></div><p>显示效果如图4-10所示：</p> <p><img src=\"/assets/img/4-10.9e758696.png\" alt=\"图4-10\"></p> <p>可以看到，由于第二个子文本组件没有定位，所以<code>fit</code>属性会对它起作用，就会占满<code>Stack</code>。由于<code>Stack</code>子元素是堆叠的，所以第一个子文本组件被第二个遮住了，而第三个在最上层，所以可以正常显示。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/73.dcf07497.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter4/wrap_and_flow.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>4.4 流式布局 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/53.56438d06.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter4/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_4-4-流式布局\"><a href=\"#_4-4-流式布局\" class=\"header-anchor\">#</a> 4.4 流式布局</h1> <p>在介绍Row和Colum时，如果子widget超出屏幕范围，则会报溢出错误，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;xxx&quot;</span><span class=\"token operator\">*</span><span class=\"token number\">100</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>运行效果如图4-6所示：</p> <p><img src=\"/assets/img/4-6.563f1017.png\" alt=\"图4-6\"></p> <p>可以看到，右边溢出部分报错。这是因为Row默认只有一行，如果超出屏幕不会折行。我们把超出屏幕显示范围会自动折行的布局称为流式布局。Flutter中通过<code>Wrap</code>和<code>Flow</code>来支持流式布局，将上例中的Row换成Wrap后溢出部分则会自动折行，下面我们分别介绍<code>Wrap</code>和<code>Flow</code>.</p> <h2 id=\"_4-4-1-wrap\"><a href=\"#_4-4-1-wrap\" class=\"header-anchor\">#</a> 4.4.1 Wrap</h2> <p>下面是Wrap的定义:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Wrap</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>direction <span class=\"token operator\">=</span> Axis<span class=\"token punctuation\">.</span>horizontal<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>alignment <span class=\"token operator\">=</span> WrapAlignment<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>spacing <span class=\"token operator\">=</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>runAlignment <span class=\"token operator\">=</span> WrapAlignment<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>runSpacing <span class=\"token operator\">=</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>crossAxisAlignment <span class=\"token operator\">=</span> WrapCrossAlignment<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>textDirection<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>verticalDirection <span class=\"token operator\">=</span> VerticalDirection<span class=\"token punctuation\">.</span>down<span class=\"token punctuation\">,</span>\n  List<span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span> children <span class=\"token operator\">=</span> <span class=\"token keyword\">const</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>我们可以看到Wrap的很多属性在<code>Row</code>（包括<code>Flex</code>和<code>Column</code>）中也有，如<code>direction</code>、<code>crossAxisAlignment</code>、<code>textDirection</code>、<code>verticalDirection</code>等，这些参数意义是相同的，我们不再重复介绍，读者可以查阅前面介绍<code>Row</code>的部分。读者可以认为<code>Wrap</code>和<code>Flex</code>（包括<code>Row</code>和<code>Column</code>）除了超出显示范围后<code>Wrap</code>会折行外，其它行为基本相同。下面我们看一下<code>Wrap</code>特有的几个属性：</p> <ul><li><code>spacing</code>：主轴方向子widget的间距</li> <li><code>runSpacing</code>：纵轴方向的间距</li> <li><code>runAlignment</code>：纵轴方向的对齐方式</li></ul> <p>下面看一个示例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Wrap</span><span class=\"token punctuation\">(</span>\n  spacing<span class=\"token punctuation\">:</span> <span class=\"token number\">8.0</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 主轴(水平)方向间距</span>\n  runSpacing<span class=\"token punctuation\">:</span> <span class=\"token number\">4.0</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 纵轴（垂直）方向间距</span>\n  alignment<span class=\"token punctuation\">:</span> WrapAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span> <span class=\"token comment\">//沿主轴方向居中</span>\n  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token keyword\">new</span> <span class=\"token class-name\">Chip</span><span class=\"token punctuation\">(</span>\n      avatar<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">CircleAvatar</span><span class=\"token punctuation\">(</span>backgroundColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span> child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'A'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      label<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Hamilton'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">new</span> <span class=\"token class-name\">Chip</span><span class=\"token punctuation\">(</span>\n      avatar<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">CircleAvatar</span><span class=\"token punctuation\">(</span>backgroundColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span> child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'M'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      label<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Lafayette'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">new</span> <span class=\"token class-name\">Chip</span><span class=\"token punctuation\">(</span>\n      avatar<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">CircleAvatar</span><span class=\"token punctuation\">(</span>backgroundColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span> child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'H'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      label<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Mulligan'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">new</span> <span class=\"token class-name\">Chip</span><span class=\"token punctuation\">(</span>\n      avatar<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">CircleAvatar</span><span class=\"token punctuation\">(</span>backgroundColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span> child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'J'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      label<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Laurens'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行效果如图4-7所示：</p> <p><img src=\"/assets/img/4-7.632eedaf.png\" alt=\"图4-7\"></p> <h2 id=\"_4-4-2-flow\"><a href=\"#_4-4-2-flow\" class=\"header-anchor\">#</a> 4.4.2 Flow</h2> <p>我们一般很少会使用<code>Flow</code>，因为其过于复杂，需要自己实现子widget的位置转换，在很多场景下首先要考虑的是<code>Wrap</code>是否满足需求。<code>Flow</code>主要用于一些需要自定义布局策略或性能要求较高(如动画中)的场景。<code>Flow</code>有如下优点：</p> <ul><li>性能好；<code>Flow</code>是一个对子组件尺寸以及位置调整非常高效的控件，<code>Flow</code>用转换矩阵在对子组件进行位置调整的时候进行了优化：在<code>Flow</code>定位过后，如果子组件的尺寸或者位置发生了变化，在<code>FlowDelegate</code>中的<code>paintChildren()</code>方法中调用<code>context.paintChild</code> 进行重绘，而<code>context.paintChild</code>在重绘时使用了转换矩阵，并没有实际调整组件位置。</li> <li>灵活；由于我们需要自己实现<code>FlowDelegate</code>的<code>paintChildren()</code>方法，所以我们需要自己计算每一个组件的位置，因此，可以自定义布局策略。</li></ul> <p>缺点：</p> <ul><li>使用复杂。</li> <li>不能自适应子组件大小，必须通过指定父容器大小或实现<code>TestFlowDelegate</code>的<code>getSize</code>返回固定大小。</li></ul> <p>示例：</p> <p>我们对六个色块进行自定义流式布局：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Flow</span><span class=\"token punctuation\">(</span>\n  delegate<span class=\"token punctuation\">:</span> <span class=\"token function\">TestFlowDelegate</span><span class=\"token punctuation\">(</span>margin<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">10.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token keyword\">new</span> <span class=\"token class-name\">Container</span><span class=\"token punctuation\">(</span>width<span class=\"token punctuation\">:</span> <span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span> height<span class=\"token punctuation\">:</span><span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span> color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">new</span> <span class=\"token class-name\">Container</span><span class=\"token punctuation\">(</span>width<span class=\"token punctuation\">:</span> <span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span> height<span class=\"token punctuation\">:</span><span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span> color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>green<span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">new</span> <span class=\"token class-name\">Container</span><span class=\"token punctuation\">(</span>width<span class=\"token punctuation\">:</span> <span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span> height<span class=\"token punctuation\">:</span><span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span> color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">new</span> <span class=\"token class-name\">Container</span><span class=\"token punctuation\">(</span>width<span class=\"token punctuation\">:</span> <span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span> height<span class=\"token punctuation\">:</span><span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span>  color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>yellow<span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">new</span> <span class=\"token class-name\">Container</span><span class=\"token punctuation\">(</span>width<span class=\"token punctuation\">:</span> <span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span> height<span class=\"token punctuation\">:</span><span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span> color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>brown<span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">new</span> <span class=\"token class-name\">Container</span><span class=\"token punctuation\">(</span>width<span class=\"token punctuation\">:</span> <span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span> height<span class=\"token punctuation\">:</span><span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span>  color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>purple<span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>实现TestFlowDelegate:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">TestFlowDelegate</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">FlowDelegate</span> <span class=\"token punctuation\">{</span>\n  EdgeInsets margin <span class=\"token operator\">=</span> EdgeInsets<span class=\"token punctuation\">.</span>zero<span class=\"token punctuation\">;</span>\n  <span class=\"token function\">TestFlowDelegate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>margin<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">paintChildren</span><span class=\"token punctuation\">(</span>FlowPaintingContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">var</span> x <span class=\"token operator\">=</span> margin<span class=\"token punctuation\">.</span>left<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">var</span> y <span class=\"token operator\">=</span> margin<span class=\"token punctuation\">.</span>top<span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//计算每一个子widget的位置  </span>\n    <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span>int i <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span> i <span class=\"token operator\">&lt;</span> context<span class=\"token punctuation\">.</span>childCount<span class=\"token punctuation\">;</span> i<span class=\"token operator\">++</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">var</span> w <span class=\"token operator\">=</span> context<span class=\"token punctuation\">.</span><span class=\"token function\">getChildSize</span><span class=\"token punctuation\">(</span>i<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>width <span class=\"token operator\">+</span> x <span class=\"token operator\">+</span> margin<span class=\"token punctuation\">.</span>right<span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>w <span class=\"token operator\">&lt;</span> context<span class=\"token punctuation\">.</span>size<span class=\"token punctuation\">.</span>width<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        context<span class=\"token punctuation\">.</span><span class=\"token function\">paintChild</span><span class=\"token punctuation\">(</span>i<span class=\"token punctuation\">,</span>\n            transform<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Matrix4<span class=\"token punctuation\">.</span>translationValues</span><span class=\"token punctuation\">(</span>\n                x<span class=\"token punctuation\">,</span> y<span class=\"token punctuation\">,</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        x <span class=\"token operator\">=</span> w <span class=\"token operator\">+</span> margin<span class=\"token punctuation\">.</span>left<span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n        x <span class=\"token operator\">=</span> margin<span class=\"token punctuation\">.</span>left<span class=\"token punctuation\">;</span>\n        y <span class=\"token operator\">+=</span> context<span class=\"token punctuation\">.</span><span class=\"token function\">getChildSize</span><span class=\"token punctuation\">(</span>i<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>height <span class=\"token operator\">+</span> margin<span class=\"token punctuation\">.</span>top <span class=\"token operator\">+</span> margin<span class=\"token punctuation\">.</span>bottom<span class=\"token punctuation\">;</span>\n        <span class=\"token comment\">//绘制子widget(有优化)  </span>\n        context<span class=\"token punctuation\">.</span><span class=\"token function\">paintChild</span><span class=\"token punctuation\">(</span>i<span class=\"token punctuation\">,</span>\n            transform<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Matrix4<span class=\"token punctuation\">.</span>translationValues</span><span class=\"token punctuation\">(</span>\n                x<span class=\"token punctuation\">,</span> y<span class=\"token punctuation\">,</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n         x <span class=\"token operator\">+=</span> context<span class=\"token punctuation\">.</span><span class=\"token function\">getChildSize</span><span class=\"token punctuation\">(</span>i<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>width <span class=\"token operator\">+</span> margin<span class=\"token punctuation\">.</span>left <span class=\"token operator\">+</span> margin<span class=\"token punctuation\">.</span>right<span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token function\">getSize</span><span class=\"token punctuation\">(</span>BoxConstraints constraints<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//指定Flow的大小  </span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Size</span><span class=\"token punctuation\">(</span>double<span class=\"token punctuation\">.</span>infinity<span class=\"token punctuation\">,</span><span class=\"token number\">200.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  bool <span class=\"token function\">shouldRepaint</span><span class=\"token punctuation\">(</span>FlowDelegate oldDelegate<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> oldDelegate <span class=\"token operator\">!=</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行效果见图4-8：</p> <p><img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAWgAAAC/CAYAAADTqUb2AAAMJWlDQ1BJQ0MgUHJvZmlsZQAASImVVwdUk8kWnr8kISGhBUKREnoTpUiXGloEAamCjZAEEkoMgSBiBVlUYC2oWLCiqyKKrgWQxYZdWRTsdUFERVlFXWyovEkC6LqvnHfPmZnv3Ln3znennRkAVCM5YnEGqgZApihHEhXsz5yckMgkPQIo0AbKgATGcrjZYr/IyDAAZbj9u7y7CRBZe81OFuuf/f9V1Hn8bC4ASCTEybxsbibEhwHAXbhiSQ4AhF6oN52VI4aYCFkCTQkkCLGZDKcqsJsMJytwmNwmJooFcRIASlQOR5IKgIqMFzOXmwrjqJRBbC/iCUUQN0HszRVweBB/hnh0ZuZMiFWtILZK/i5O6t9iJo/E5HBSR7AiF7koBQizxRmc2f/ndPxvycyQDo9hCgtVIAmJkuUsm7f0maEyTIX4gig5PAJiDYivC3lyexl+IpCGxA7Zf+Bms+CcAQYAKJXHCQiFWB9iE2l6rN8Q9uZI5L7QHk3MF8TEK+KjIsnMqKH4aL4oIzxsKE6ZgM8exlX87MDoYZsUYRAbYriGaIMwhx0zFPNCrjAuHGIViO9np0eHDvk+zxewwkfGkkbJOMM1x0Bm9nAumFmKJChKYY+5CITs8CF9WI4gJkThi03ncuQcdCBO42dPDhvmw+MHBCr4YIV8UewQT6xcnOMfNWS/Q5wROWSPNfEzgmV6E4hbs3Ojh337cuBmU+SCgzTOhEjFuLimOCcyRsENZ4IwwAIBgAmksCSDmSANCFt763vBcE8Q4AAJSAV8YDekGfaIl/eIYB0N8sGfEPFB9oifv7yXD3Kh/suIVlHbgRR5b67cIx08gTgT18O9cU88DNa+sDjibrj7sB9TdXhUYiAxgBhCDCJazxAWSn6IywRcmEEGLBIQCls+zErGQTTM/VscwhNCG+ER4Qahg3AHxIHH0E74jwy/RROO6CaCDhg1aCi75O+zwy0ga2fcH/eC/CF3nIHrATt8HMzED/eBuTlD7bdZ+3fcpcOsyfZklKxN9iVb/WinYqPiPOIjy+17ngpeySOZsEZ6fhyN9V1uPNiG/miJLcEOYeexU9hFrAmrB0zsBNaAtWDHZHhkbzyW743h0aLkfNJhHOGwjX2NfY/95x/G5gyNL5GvP8jh5+XIDg5rpni2RJgqyGH6wduaz2SLuGNGMx3tHeAtKrv7FVfLW4b8TkcYl77pCuYAMP7Q4OBg0zddGLxZjhgAQHn1TWf1Hh5newAuFHClklyFDpdVBEABqvCk6AJDeHdZwYwcgQvwBL4gEEwAESAGJIDpcJ4FIBOyngXmggJQDErBCrAGbABbwHawG+wDB0E9aAKnwDlwGVwFN8A9uFe6wQvQB96BAQRBSAgNoSO6iBFijtgijogb4o0EImFIFJKAJCGpiAiRInORRUgpUo5sQLYh1civyFHkFHIRaUPuIJ1ID/IG+YRiKBXVRA1QC3Qs6ob6oaFoDDoNTUWz0Hy0CF2GrkOr0L1oHXoKvYzeQDvQF2g/BjBljIEZY3aYG8bCIrBELAWTYPOxEqwCq8JqsUa40tewDqwX+4gTcTrOxO3gfg3BY3EunoXPx8vwDfhuvA4/g1/DO/E+/CuBRtAn2BI8CGzCZEIqYRahmFBB2Ek4QjgLz1Q34R2RSGQQLYmu8KwmENOIc4hlxE3E/cSTxDZiF7GfRCLpkmxJXqQIEoeUQyomrSftJZ0gtZO6SR+UlJWMlByVgpQSlURKhUoVSnuUjiu1Kz1VGiCrkc3JHuQIMo88m7ycvIPcSL5C7iYPUNQplhQvSgwljVJAWUeppZyl3Ke8VVZWNlF2V56kLFReqLxO+YDyBeVO5Y9UDaoNlUWdSpVSl1F3UU9S71Df0mg0C5ovLZGWQ1tGq6adpj2kfVChq4xRYavwVBaoVKrUqbSrvFQlq5qr+qlOV81XrVA9pHpFtVeNrGahxlLjqM1Xq1Q7qnZLrV+dru6gHqGeqV6mvkf9ovozDZKGhUagBk+jSGO7xmmNLjpGN6Wz6Fz6IvoO+ll6tyZR01KTrZmmWaq5T7NVs09LQ2ucVpxWnlal1jGtDgbGsGCwGRmM5YyDjJuMT9oG2n7afO2l2rXa7drvdUbp+OrwdUp09uvc0Pmky9QN1E3XXalbr/tAD9ez0ZukN0tvs95Zvd5RmqM8R3FHlYw6OOquPqpvox+lP0d/u36Lfr+BoUGwgdhgvcFpg15DhqGvYZrhasPjhj1GdCNvI6HRaqMTRs+ZWkw/ZgZzHfMMs89Y3zjEWGq8zbjVeMDE0iTWpNBkv8kDU4qpm2mK6WrTZtM+MyOziWZzzWrM7pqTzd3MBeZrzc+bv7ewtIi3WGxRb/HMUseSbZlvWWN534pm5WOVZVVldd2aaO1mnW69yfqqDWrjbCOwqbS5YovautgKbTfZto0mjHYfLRpdNfqWHdXOzy7XrsaucwxjTNiYwjH1Y16ONRubOHbl2PNjv9o722fY77C/56DhMMGh0KHR4Y2jjSPXsdLxuhPNKchpgVOD0+txtuP44zaPu+1Md57ovNi52fmLi6uLxKXWpcfVzDXJdaPrLTdNt0i3MrcL7gR3f/cF7k3uHz1cPHI8Dnq88rTzTPfc4/lsvOV4/vgd47u8TLw4Xtu8OryZ3kneW707fIx9OD5VPo98TX15vjt9n/pZ+6X57fV76W/vL/E/4v+e5cGaxzoZgAUEB5QEtAZqBMYGbgh8GGQSlBpUE9QX7Bw8J/hkCCEkNGRlyC22AZvLrmb3TXCdMG/CmVBqaHTohtBHYTZhkrDGiejECRNXTbwfbh4uCq+PABHsiFURDyItI7Mif5tEnBQ5qXLSkyiHqLlR56Pp0TOi90S/i/GPWR5zL9YqVhrbHKcaNzWuOu59fEB8eXzH5LGT502+nKCXIExoSCQlxiXuTOyfEjhlzZTuqc5Ti6fenGY5LW/axel60zOmH5uhOoMz41ASISk+aU/SZ04Ep4rTn8xO3pjcx2Vx13Jf8Hx5q3k9fC9+Of9pildKecqzVK/UVak9Ah9BhaBXyBJuEL5OC0nbkvY+PSJ9V/pgRnzG/kylzKTMoyINUbrozEzDmXkz28S24mJxR5ZH1pqsPkmoZGc2kj0tuyFHEz6yW6RW0p+knbneuZW5H2bFzTqUp54nymuZbTN76eyn+UH5v8zB53DnNM81nlswt3Oe37xt85H5yfObF5guKFrQvTB44e4CSkF6we+F9oXlhX8til/UWGRQtLCo66fgn2qKVYolxbcWey7esgRfIlzSutRp6fqlX0t4JZdK7UsrSj+Xccsu/ezw87qfB5elLGtd7rJ88wriCtGKmyt9Vu4uVy/PL+9aNXFV3Wrm6pLVf62ZseZixbiKLWspa6VrO9aFrWtYb7Z+xfrPGwQbblT6V+7fqL9x6cb3m3ib2jf7bq7dYrCldMunrcKtt7cFb6ursqiq2E7cnrv9yY64Hed/cfuleqfeztKdX3aJdnXsjtp9ptq1unqP/p7lNWiNtKZn79S9V/cF7Guotavdtp+xv/QAOCA98PzXpF9vHgw92HzI7VDtYfPDG4/Qj5TUIXWz6/rqBfUdDQkNbUcnHG1u9Gw88tuY33Y1GTdVHtM6tvw45XjR8cET+Sf6T4pP9p5KPdXVPKP53unJp6+fmXSm9Wzo2Qvngs6dPu93/sQFrwtNFz0uHr3kdqn+ssvluhbnliO/O/9+pNWlte6K65WGq+5XG9vGtx1v92k/dS3g2rnr7OuXb4TfaLsZe/P2ram3Om7zbj+7k3Hn9d3cuwP3Ft4n3C95oPag4qH+w6o/rP/Y3+HScawzoLPlUfSje13crhePsx9/7i56QntS8dToafUzx2dNPUE9V59Ped79QvxioLf4T/U/N760enn4le+rlr7Jfd2vJa8H35S91X27669xfzX3R/Y/fJf5buB9yQfdD7s/un08/yn+09OBWZ9Jn9d9sf7S+DX06/3BzMFBMUfCkT8FMFjQlBQA3uwCgJYAAP0qfD9MUfzN5IIo/pNyBP4TVvzf5OICQC1sZM9w1kkADsBi6QtjwyJ7jsf4AtTJaaQMSXaKk6MiFhX+cAgfBgffwncMqRGAL5LBwYFNg4NfdkCydwA4maX4E8pE9gfdKo/RzshbCH6QfwEOk3CtyyHjHgAAAAlwSFlzAAAWJQAAFiUBSVIk8AAAAVlpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDUuNC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KTMInWQAAFw5JREFUeAHtncuPJNlVh29kZlX1dE9Ptz0PzzRt0GAGYdAIIWyBZmGEF5YQSCxghwTiL2DrBX8De4SEWLBCyIKdhbGEkDcjEJJXaKwRD4+MxeDHoB6qp7IyMzgnM2/FraiIzLiPeOYXreq4cR/n3PudG7+4EVmVkS2Xy9zIdnl5adbrtcmyTA/ZIAABCECgZwKznv3jHgIQgAAEaggg0DVgyIYABCDQNwEEuu8I4B8CEIBADQEEugYM2RCAAAT6JoBA9x0B/EMAAhCoIYBA14AhGwIQgEDfBBDoviOAfwhAAAI1BBDoGjBkQwACEOibAALddwTwDwEIQKCGwKImvzo73/7RYXUZudUEEv5lZg7/asZ1ufJHsZn8S7GB3pPilr1nG6rfIdBcoHWGLhYmlx/DbL0DsjJDOGWrVTJeF4uLSjdkVhNYbVZmncvXF0SKtEr82SI3M0noEkWP7VJF07rZ491R/P9lH659W1beu17r+mXbaF037bYtp916dXbdNromuV5lZr0RH7aBW4F0YwLNBFqEJj8/N/PvvGdm3/i6MQ9fMmYj9NmqCeisFGHOX/60WX/5K8Y8eGDki06CZquumuezublaXZmvvfc35vnq0ixmcqHkIlnNXnK3Ypbl5p2n75iffvSmWa1XQd8xo4jn8hDw/+Qa+81/XZjv/ciYCzljNq5a1vYirkB1rc6NLbN7H09uGzd9yEbTempDL2IfL4353bc35rMv52Yp7BDpQ3QPlzUWaDOfm+wHPzDzr/6xyd8Uox8cNhxV6jMjohw1aNykL+U6j8TuD+UE+4PfMesv/brMUH3ULwIduOkKUFeCf/39r5mvX71rfmH+WbPMZeaz3SEwF9bL/NqsZ7n581d+zrz1+HMmhpSGdiVrkb/7Xmb+8n05ekEyWJvc4X6ToVP948x85a2NmUkM5B5yu1K/KSfhRaCZQKtJuZznZ2cmvyfpX5RV4eevvBydVGVZ8ZpnH5n8ldf3y4e6tVAzKnaaPz1/Yn7D/Kp5MH8gq7hwwW/mdZy1MjPbsslFKHZ3GnHj0MjpCvDVc0m8aMwX5SnTlWSqcLPdJXBPwLwrYqF3HrW3AHebkVNDoLlA64zUe75PZL8Ucb56XmOSbL3bMM+fyaI5Zu12l+O1rJo/ya/M2eZMFnEs4+4SUuHc3W3oVNULW+xmhXitpgT5x/LzXNI2P9b+1NqvFYww2j6BA1J0ePU6x9YKgfZmZ3uWWwHRudHUfLZi44witX3H9LSS8dfHafEIGA0CHQCNJqdFAEEOjDfgAsEVzRDoggUpCFQSYCFYieV4JuCOMzpSA4E+AohiCLAQZA70RQCB7os8fkdDgIVgYKi4sgWCK5oh0AWLwaaY576hSUuMP7Tw5U/9VAQQ6FQkW7TDCs4XbmJiic35jma09eEWHToEOhohBqZOAJ2ZeoSHOz4EerixoWcQgMCJE0CgT3wCMPzjBHgGfZzRnRr6MUDajwLuuDiFDAR6BFFmnvsGKfEX9PCMwzcAfA+HP7HKFgh0JZZhZaIPvvFI8S0cvj6pD4H0BBDo9EyxCAEIKAFWFtHzAIGORoiBqRNAZ6Ye4eGOD4Eebmxqe4Zg1KJppYDPAAKxAi4QXNEMgS5YjCbFvO82VFwQA3kDLhBc0QyBLliQggAEIDAoAgj0oMJR3RlWzNVcusrl96ADSOukZeIGgLvdBIG+zWOQR9wp9huW8htV+u3NSLzrpGXiRgcLgY5GiIGpE2AhGBhhwAWCK5oh0AULUhCAQEoCrKCjaSLQ0QgxAAEIQKAdAs0F2rkaOsl2eoXVgwTgfxBP0kJlDe+kSDHmQaC5QDvPk5ykhyuqpiIA/1Qkj9tR1vA+zoka7RBoLtDt+McqBCAAAQjUEECga8CQDQFLgEcclgT7rgkg0F0Tx9/oCPCIY3Qhm0yHEejJhJKBQAACUyOAQE8toowHAhCYDAEEejKhZCAQGBgBng1FBwSBjkaIAQhAoJIAn65WYvHJRKB9aFEXAhCAQIcEEOgOYeMKAhCAgA8BBNqHFnUhAAEIdEiguUDzPKnDsOAqhkDKqaq2UtqLGRdtT49Ac4HmE9nTmx0jHXHKqaq2UtobKVK63ROB5gLdUwdxi0D4z4HEkprYnP94RtiCK1uSoCHQSTC2a4RbbF++iYklNuc7mlHWh1mSsCHQSTBiZGgEki56kxobGin6M2QCCPSQo0PfIDBWAlzUkkQOgU6CESMQgMAtAjziuIUj9ACBDiVHu0ETSKoPSY0NGlvazsEtmicCHY2wfQPcLfoyTkwssTnf0YyyvjKDW3ToEOhohO0bYCHiyzgxscTmfEczyvowSxI2BDoJRoxMlYDqDFoTGF3ABYIrmiHQBQtSELhDYHunzq36HS6NMuDWCNOhSgj0ITqUQUAJsBJkHvREAIHuCTxuIQABCBwjgEAfI0Q5BCAQRoA7jzBuTisE2oFBEgIQSEiAZ9DRMBHoaIQYgAAEINAOAQS6Ha5YnRAB7tQDgwm4QHBFMwS6YEEKApUEMm7VK7kczYTbUUTHKiDQxwhRDgFWgmFzAG5h3JxWCLQDY6hJFiI9R4YA+AdAmcHNn1upBQJdAjLEQxYiQ4wKfTpIgEl7EE/TQgS6KSnqQQACEOiYAALdMXDcjZAAq8GwoMEtjJvTCoF2YJCEAAQSEuAZdDRMBDoaIQYgAIFKAqygK7H4ZCLQPrSoCwEIQKBDAgh0h7Bx1R0B7q67Y13riSDUomlagEA3JdVjPeZ5j/DVNQHoOQCn6x6BHkHseZTnH6SkzJIa8x/LaFvALTp0CHQ0QgxAAAIQaIcAAt0OV6xCAAIQiCaAQEcjxAAEIACBdggg0O1wxeqECPAoNTCYfLgaCK5ohkAXLEhBoJIAOlOJ5XgmV7bjjI7UQKCPAAorllO6pbO6JbNhwxxsq6wt/IMdMR2bJoFF42Hp1dCqw1yaLc4aNz25irO58LkwZpb2+jfP5maeLeRH7GswbEzKexe4XcVo7KrSbl1Nl+tpnm2nad0O1dEy3dw2VXm7WvX/l9tYn1V7a0V8ZvL6E/2XZ/l2b4ti9zP1Kz/numerJbDlI4x4C00tIq+C5gKt0NfrnfHv/5sxH38gaWbrXdqiEmePjHnvh8b8/NsiVKpUcZzsevDHq4/Mf1x/YF6dv2xW+SrS6t2eTyFnZmZbNmu5hq3z/XyNHJhG8NlK/rsy5ttqSzPYqgnomuQTYzYb2cdN+2r7J5bbTKB1VbJam83rbxjzF39mzAv39xE4MVpNh6vLB7mY5Z/6tDEX9+SE1tkauImpjfyby6r89z/3e+ZqvZQV9AyNqMGpmpDLP11JP3nwhtko+1ChUPYixvdE7H/7rY354uuZOZ9vre8vvOJMY70V7L1qW19WxPXYpqv6XC6/Od4ndKcD2F7od0m5PZCEGN36VuNyLHcMWz/7ol2eVis5v2kjZW47Pazb3DZHxqvFz1eZefxQTwG7tKgzTP4xAtlyudxG8PLyUoCutxP7YCM7Aez+YOV9oVvXTdu2Ns/ubX7dvlyvfGzbab5udo7aenavZW5aj3WrytuV+P3vTmy/ltW1xZ52ja0ZgbwsTs2aVdcCfDWXulx7ztWVk9+IQLMVtGvKgrd7t6wu7dZ107a+zbN7m1+3L9crH9t25Xx7bPdaz03XtbP5vvuUAqG+xV5Vd327Rf0AAoAPgEaTWAL+Aq0SoZNVV4e6WRHaHm8LNHNXR8ttvRtpqVqe2jy714a6yfGN/V3Ozrekrat99nanebqpmZvNHtjCm4JqG7b4xr7bp72tcp9q21jftkLc/qZLezO7m+3mPurq78gUH6rZy4Bdr5eP1X1VXnl05TrlY9eObXvbpz3alWr72zm2VTd7O4PcGeHjuapdVd4xm7aN3Wt9N32svW95iG1twxZPIECgBb1L/0aAtTO2oFRn209b5tbbFuz/s+V2b7PLxzbfbXsg76ZayY7mV2RVVy9VvDXmmxYle6U2TrXQZNmir1jV1d/ZLayX65WPtf9VeeVxleuUjw/Z8alb9tvWcUGoFGoPh64N26wqz5bV7W0bu9d6brquXWh+m7ZD+3QK7dL+HtgpEGOMEIAABDoigEB3BBo3EIAABHwJINC+xKgPAQhAoCMCCHRHoHEDAQhAwJcAAu1LjPoQgAAEOiKAQHcEGjcQgAAEfAkg0L7EqA8BCECgIwIIdEegcQMBCEDAlwAC7UuM+hCAAAQ6IoBAdwQaNxCAAAR8CSDQvsSoDwEIQKAjAgh0R6BxMwEC9tuSJjCURkMIHW9ou0adOq1KCPRpxZvRxhA4tW8MCh1vaLuY2Ey0rfe32ekXPrI1JyBvx2te+UhNfUsIW3MCKb+wf8s+XSibD2KkNe1Xy460+4PpdkOBVtzn5ix/35zn/yhv3HlRBhDxGqdDw9eTYPQ6JAPIr80me81cZe/IgF6QH30/nv/AVGRm8rqr6+sr8y//8E1zffXczPSlvfY7qcUq220CO2Yz87O/9AXzypOnZr2S9zeGXNx0Lso95uZqYz741ofm2XcvzfxC3n8F+9vA3SPhvLpcmzd/6w3z0tMHZr1s8JYmtz3pWwQ8BFreKJ3/j7nY/JHojLwM1Xx0y1DSg9GvVOSN3uZ9eXnpH5rl7Asi1PcDpLkgmskrpTfyOrJv/e1fmY/+/dvm/NFnTL7Wt5iylQlk8ib1jQjyPFubV37iT8xrT39ye2ks12tyrMuSmdq7ys13v/Hf5j//9ENzYc5kaVJMUL3kFkd3rbrlbvpuzV2OW8dNl+vbsvLerWfLjuW55VVp146brqo7k5l+KW/X/cyXHpvHs4e7F/dqI7YgAg0F2tqW1YPAzs0T2ckLUdkqCeRmLnxekrIHleXemVsFyMy9h4/M/Vd/ypzdf0neQ5vmjdXefRl4A10pb+SV0iKr2zuPuO7ulUV28/szkeaFOfvlucl3r/GMMz3R1tk9ebHuPy2MLioOX7omCiDxsDwF2q4VdPXGCq4uFrvnztdSLLz253hdXZ/8XIRHV9Iqzgh0DTkRaOWUJ3kEp/N9H8Dt1Jc1tYQ1X9b4JtvINUzYW50ASCwBfosjlmBt+2KSxmt0YYtVSS3wouAG102iKPNO7WyksOTteowNAJU0agh0UpzVxuLnrCvxbrraH7kpCcA7JU1s+RFAoP149VQ7XuJ76vgE3MJ+AkEc7RAQ6FZDx8ndKt4ujBPCLijjo4YAAl0DJk02t8dpOPZohRD2CB/XCHQHcyD+HI+30MEwJ+hCue/YE4EJhncEQ0KgOwhS/F1yvIUOhjlBF8p9x54ITDC8IxgSAj2CINFFCEDgNAkg0KcZd0YNAQiMgAACPYIg0UUIQOA0CQQJNB+Y+E2WeF7xFvx6TO0dAeW+Y08EmBN9EAgSaD4w6SNU+OyeADO9e+Z4dAkECbRrgPRxAvGnebyF472kRjWBHXsiUE2H3HYJINDt8sU6BCAAgWACCHQwOhpCAAIQaJcAAt0uX6xDAAIQCCaAQAej67Ihv0PQJe3CF7/FUbAg1QcBzzeq9NFFfNo/N4aEL4HYC1vx0WCR8u0D9SEQToAVdDg7Wg6eALI6+BDRwYMEEOiDeNIUxq7j7B9LpOnNKVmJJc8jjlOaLUMca5BAx077IYJos0/x67h4C22Ob7i2Y7kV7YvUcEdLz6ZHIEigmazTmwiMqI4As72ODPntEwgS6Pa7NRUPcnJzfncezKTIkxrrHAUOR04AgW41gPIwKMnzoCRGWh3pkIwnpZUltTYkTPRlBAQQ6BEEiWV4n0FiCd0n/VP37S/QzFePOSOw4OXBa4BVid8Ag3I6XfIXaLnj46av6QRJBQviTYknr7d/xEEEkpPFYAMC/gItRllUNCCbtIpL3E0ndYKxKgL5jjfUq+CQ1zaBIIFuu1PYP0SAtdwhOsnLwJ0cKQabE0Cgm7PqsSYqEQY/BbedjRSWwsZAq1MmgECfcvQZ+xECyPIRQBS3TIBvs2sZcBrzPAEN4xjLrWhfpMJ6QisIhBBgBR1CjTanQwBlPp1YD3CkCPQAg3K3S9xq32XSUQ6/ZtcRaNxUEfAT6L1OIBdVKOvz4FXPZtglReRYSA87UlPtnZ9A72cpk9VvOsTzirfg12Nq7wgod9gzG/oj4CfQ/fUTzxCAAAROjgACfXIhZ8AQgMBYCCDQY4kU/YQABE6OAAI9ipAXH1aNoruD6WQsN22/sxFraTBI6MioCPCHKqMIFx9U9ROmgnuR6qcneD1NAqygRxF31m9hYUohq6ygw9jTKgUBBDoFRWxMnkAKqZ88JAaYnICfQO8Xcqzn/OIALz9e6WrHki/aF6l0vcMSBI4R8BPo/TKC1cQxrLfL4XWbR3dHseSL9kWqu97jCQJ+Aq28ZKaymvCYOEnO7CRGPDpN1YIA7AsWpLom4C/Qos5MWY8wJbmaJTHi0WmqFgRgX7Ag1TUBf4Huuof4EwJcEvubBrDvjz2eEehW5wAnd6t4MQ6BiRNAoFsNcKrb41R2Wh3sRI3DfqKBHcWwEOhRhIlOQgACp0gAgR5F1HlU0l+YYN8fezwj0MwBCEAAAgMlgEAPNDB0CwIQgICfQO8/L+Fjk64nDsS7Jr7zp9xh3w97vCoBP4HeP47jqVzXkwfiXRPf+VPusO+HPV6VgJ9Aw6wnAqziegIvbmHfH3s8I9CjmAOs4voLE+z7Y49nP4HeLyZYU3Q9cSDeNfHCH+wLFqS6JuAn0PvFBGuKrsME8a6JF/5gX7Ag1TUBP4Huunf4gwAEIHDCBBDoEw4+Q4cABIZNAIEednzoHQQgcMIEEOi2g88jzLYJYx8CkyWAQLcdWn4JoG3C2IfAZAkg0KMILSrfT5iUO+z7YY9XJYBAMw8mTABxnXBwT2Joi5MY5egHyYPssBDGcottH9ZrWkHAEmAFbUmwnyCBFCvoFDYmiJYhdUIAgW4Vc+oVmNpLbbNVAL0Yz28YpWBVYaMiq5eB4nTyBDwfcdjVxIWcAs1mqW1ha+uxTbt06/LdOm2l0/uey0dL96W7nniPDHC2ODPz83tmfnZm8s38SO0TLc4kmrO1mWc6yzSykdt+smbzzGQPMzN7ITObtGGN7OCwms8uhBEfbSULitdUy8xqq65Z9s/SgQ+9OuGeKm7aNeJ9TlWdg+U8e7w/0bbnrM1T5/v0tk9uvtuxuny3TjktbbLsZ8S+JsqFnsciOnpBfPbhf5kff+fvzb2XP282qysxEmvYsx8jqJ7N5mYtbBbZtVmvZb7GMlLBlxBe/2hlnj9bms27GgmdEGxVBOQyZi7NldmslRHzs4qRT15jgZZ1g1lnn5Kfr5o8eyyTdrPjb+eqxqIurT1yy9xjTetmY2nt2P2u9Pb/tsy2cUvLeXrs5rlpbWfL7XzSY9tXW677Q9udNpohApG9JvYVsWvwkKHqslxEfjabmbe//Jvmk1/5te0Keiv81dVPPld5aQRefOmxYJJ5GryJFbW1yMyr7zwysyeZWdyby9SPi2dwd0bQMJtlZvm/1+biRb3LE04aCLZgAtlyudzOtsvLS1lxrGXVV0dU1w16W82tdXPaKhS6ikuzLRYq9hofDZnd19k+Vp6ynevLTbs+6vLdOofSx9qXyzOz3qxNLnNaJvUhw43KZotZCjONfE2h0mYleoFAR4ey8QpaBUGmvDjUH7bmBOLFwfq6vr62SfYNCGzJJxBndbW+1pW4XTmXLwYNOuNd5ZAPW2b3PsbdNm76kI2m9Qob24WeNmOLIuAh0OoH4lG0IxvX391EGqb5UQI7nXfnv5s+2jywwiEftszufVy4bdz0IRtN6x2yQZkvAX7NzpcY9SEAAQh0RACB7gg0biAAAQj4EkCgfYlRHwIQgEBHBBDojkDjBgIQgIAvgf8HGrmxAVepfY0AAAAASUVORK5CYII=\" alt=\"图4-8\"></p> <p>可以看到我们主要的任务就是实现<code>paintChildren</code>，它的主要任务是确定每个子widget位置。由于Flow不能自适应子widget的大小，我们通过在<code>getSize</code>返回一个固定大小来指定Flow的大小。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/53.56438d06.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter5/clip.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>5.7 剪裁（Clip） | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/74.3253492f.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter5/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_5-7-剪裁-clip\"><a href=\"#_5-7-剪裁-clip\" class=\"header-anchor\">#</a> 5.7 剪裁（Clip）</h1> <p>Flutter中提供了一些剪裁函数，用于对组件进行剪裁。</p> <table><thead><tr><th>剪裁Widget</th> <th>作用</th></tr></thead> <tbody><tr><td>ClipOval</td> <td>子组件为正方形时剪裁为内贴圆形，为矩形时，剪裁为内贴椭圆</td></tr> <tr><td>ClipRRect</td> <td>将子组件剪裁为圆角矩形</td></tr> <tr><td>ClipRect</td> <td>剪裁子组件到实际占用的矩形大小（溢出部分剪裁）</td></tr></tbody></table> <p>下面看一个例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">ClipTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 头像  </span>\n    Widget avatar <span class=\"token operator\">=</span> Image<span class=\"token punctuation\">.</span><span class=\"token function\">asset</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;imgs/avatar.png&quot;</span><span class=\"token punctuation\">,</span> width<span class=\"token punctuation\">:</span> <span class=\"token number\">60.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n        children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n          avatar<span class=\"token punctuation\">,</span> <span class=\"token comment\">//不剪裁</span>\n          <span class=\"token function\">ClipOval</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">:</span> avatar<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//剪裁为圆形</span>\n          <span class=\"token function\">ClipRRect</span><span class=\"token punctuation\">(</span> <span class=\"token comment\">//剪裁为圆角矩形</span>\n            borderRadius<span class=\"token punctuation\">:</span> BorderRadius<span class=\"token punctuation\">.</span><span class=\"token function\">circular</span><span class=\"token punctuation\">(</span><span class=\"token number\">5.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> avatar<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> \n          <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n            mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n            children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n              <span class=\"token function\">Align</span><span class=\"token punctuation\">(</span>\n                alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>topLeft<span class=\"token punctuation\">,</span>\n                widthFactor<span class=\"token punctuation\">:</span> <span class=\"token number\">.5</span><span class=\"token punctuation\">,</span><span class=\"token comment\">//宽度设为原来宽度一半，另一半会溢出</span>\n                child<span class=\"token punctuation\">:</span> avatar<span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;你好世界&quot;</span><span class=\"token punctuation\">,</span> style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>green<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n            mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n            children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n              <span class=\"token function\">ClipRect</span><span class=\"token punctuation\">(</span><span class=\"token comment\">//将溢出部分剪裁</span>\n                child<span class=\"token punctuation\">:</span> <span class=\"token function\">Align</span><span class=\"token punctuation\">(</span>\n                  alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>topLeft<span class=\"token punctuation\">,</span>\n                  widthFactor<span class=\"token punctuation\">:</span> <span class=\"token number\">.5</span><span class=\"token punctuation\">,</span><span class=\"token comment\">//宽度设为原来宽度一半</span>\n                  child<span class=\"token punctuation\">:</span> avatar<span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;你好世界&quot;</span><span class=\"token punctuation\">,</span>style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>green<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行效果如图5-24所示：</p> <p><img src=\"/assets/img/5-24.f113edc6.png\" alt=\"图5-24\"></p> <p>上面示例代码注释比较详细，在此不再赘述。但值得一提的是最后的两个<code>Row</code>！它们通过<code>Align</code>设置<code>widthFactor</code>为0.5后，图片的实际宽度等于60×0.5，即原宽度一半，但此时图片溢出部分依然会显示，所以第一个“你好世界”会和图片的另一部分重合，为了剪裁掉溢出部分，我们在第二个<code>Row</code>中通过<code>ClipRect</code>将溢出部分剪裁掉了。</p> <h3 id=\"customclipper\"><a href=\"#customclipper\" class=\"header-anchor\">#</a> CustomClipper</h3> <p>如果我们想剪裁子组件的特定区域，比如，在上面示例的图片中，如果我们只想截取图片中部40×30像素的范围应该怎么做？这时我们可以使用<code>CustomClipper</code>来自定义剪裁区域，实现代码如下：</p> <p>首先，自定义一个<code>CustomClipper</code>：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">MyClipper</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">CustomClipper</span><span class=\"token operator\">&lt;</span>Rect<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Rect <span class=\"token function\">getClip</span><span class=\"token punctuation\">(</span>Size size<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Rect<span class=\"token punctuation\">.</span><span class=\"token function\">fromLTWH</span><span class=\"token punctuation\">(</span><span class=\"token number\">10.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">15.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">40.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">30.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  bool <span class=\"token function\">shouldReclip</span><span class=\"token punctuation\">(</span>CustomClipper<span class=\"token operator\">&lt;</span>Rect<span class=\"token operator\">&gt;</span> oldClipper<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><ul><li><code>getClip()</code>是用于获取剪裁区域的接口，由于图片大小是60×60，我们返回剪裁区域为<code>Rect.fromLTWH(10.0, 15.0, 40.0, 30.0)</code>，即图片中部40×30像素的范围。</li> <li><code>shouldReclip()</code> 接口决定是否重新剪裁。如果在应用中，剪裁区域始终不会发生变化时应该返回<code>false</code>，这样就不会触发重新剪裁，避免不必要的性能开销。如果剪裁区域会发生变化（比如在对剪裁区域执行一个动画），那么变化后应该返回<code>true</code>来重新执行剪裁。</li></ul> <p>然后，我们通过<code>ClipRect</code>来执行剪裁，为了看清图片实际所占用的位置，我们设置一个红色背景：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">DecoratedBox</span><span class=\"token punctuation\">(</span>\n  decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>\n    color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">ClipRect</span><span class=\"token punctuation\">(</span>\n      clipper<span class=\"token punctuation\">:</span> <span class=\"token function\">MyClipper</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//使用自定义的clipper</span>\n      child<span class=\"token punctuation\">:</span> avatar\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行效果如图5-25所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图5-25\"></p> <p>可以看到我们的剪裁成功了，但是图片所占用的空间大小仍然是60×60（红色区域），这是因为剪裁是在layout完成后的绘制阶段进行的，所以不会影响组件的大小，这和<code>Transform</code>原理是相似的。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/74.3253492f.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter5/constrainedbox_and_sizebox.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>5.2 尺寸限制类容器 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/11.2026a71f.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter5/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_5-2-尺寸限制类容器\"><a href=\"#_5-2-尺寸限制类容器\" class=\"header-anchor\">#</a> 5.2 尺寸限制类容器</h1> <p>尺寸限制类容器用于限制容器大小，Flutter中提供了多种这样的容器，如<code>ConstrainedBox</code>、<code>SizedBox</code>、<code>UnconstrainedBox</code>、<code>AspectRatio</code>等，本节将介绍一些常用的。</p> <h2 id=\"_5-2-1-constrainedbox\"><a href=\"#_5-2-1-constrainedbox\" class=\"header-anchor\">#</a> 5.2.1 ConstrainedBox</h2> <p><code>ConstrainedBox</code>用于对子组件添加额外的约束。例如，如果你想让子组件的最小高度是80像素，你可以使用<code>const BoxConstraints(minHeight: 80.0)</code>作为子组件的约束。</p> <h4 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h4> <p>我们先定义一个<code>redBox</code>，它是一个背景颜色为红色的盒子，不指定它的宽度和高度：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Widget redBox<span class=\"token operator\">=</span><span class=\"token function\">DecoratedBox</span><span class=\"token punctuation\">(</span>\n  decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>我们实现一个最小高度为50，宽度尽可能大的红色容器。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">ConstrainedBox</span><span class=\"token punctuation\">(</span>\n  constraints<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxConstraints</span><span class=\"token punctuation\">(</span>\n    minWidth<span class=\"token punctuation\">:</span> double<span class=\"token punctuation\">.</span>infinity<span class=\"token punctuation\">,</span> <span class=\"token comment\">//宽度尽可能大</span>\n    minHeight<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span> <span class=\"token comment\">//最小高度为50像素</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n      height<span class=\"token punctuation\">:</span> <span class=\"token number\">5.0</span><span class=\"token punctuation\">,</span> \n      child<span class=\"token punctuation\">:</span> redBox \n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行效果如图5-2所示：</p> <p><img src=\"/assets/img/5-2.40b01667.png\" alt=\"图5-2\"></p> <p>可以看到，我们虽然将Container的高度设置为5像素，但是最终却是50像素，这正是ConstrainedBox的最小高度限制生效了。如果将Container的高度设置为80像素，那么最终红色区域的高度也会是80像素，因为在此示例中，ConstrainedBox只限制了最小高度，并未限制最大高度。</p> <h4 id=\"boxconstraints\"><a href=\"#boxconstraints\" class=\"header-anchor\">#</a> BoxConstraints</h4> <p>BoxConstraints用于设置限制条件，它的定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">const</span> <span class=\"token function\">BoxConstraints</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>minWidth <span class=\"token operator\">=</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//最小宽度</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>maxWidth <span class=\"token operator\">=</span> double<span class=\"token punctuation\">.</span>infinity<span class=\"token punctuation\">,</span> <span class=\"token comment\">//最大宽度</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>minHeight <span class=\"token operator\">=</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//最小高度</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>maxHeight <span class=\"token operator\">=</span> double<span class=\"token punctuation\">.</span>infinity <span class=\"token comment\">//最大高度</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>BoxConstraints还定义了一些便捷的构造函数，用于快速生成特定限制规则的BoxConstraints，如<code>BoxConstraints.tight(Size size)</code>，它可以生成给定大小的限制；<code>const BoxConstraints.expand()</code>可以生成一个尽可能大的用以填充另一个容器的BoxConstraints。除此之外还有一些其它的便捷函数，读者可以查看<a href=\"https://docs.flutter.io/flutter/rendering/BoxConstraints-class.html\" target=\"_blank\" rel=\"noopener noreferrer\">API文档<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>。</p> <h2 id=\"_5-2-2-sizedbox\"><a href=\"#_5-2-2-sizedbox\" class=\"header-anchor\">#</a> 5.2.2 SizedBox</h2> <p><code>SizedBox</code>用于给子元素指定固定的宽高，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span>\n  width<span class=\"token punctuation\">:</span> <span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span>\n  height<span class=\"token punctuation\">:</span> <span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> redBox\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行效果如图5-3所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图5-3\"></p> <p>实际上<code>SizedBox</code>只是<code>ConstrainedBox</code>的一个定制，上面代码等价于：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">ConstrainedBox</span><span class=\"token punctuation\">(</span>\n  constraints<span class=\"token punctuation\">:</span> BoxConstraints<span class=\"token punctuation\">.</span><span class=\"token function\">tightFor</span><span class=\"token punctuation\">(</span>width<span class=\"token punctuation\">:</span> <span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span>height<span class=\"token punctuation\">:</span> <span class=\"token number\">80.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> redBox<span class=\"token punctuation\">,</span> \n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>而<code>BoxConstraints.tightFor(width: 80.0,height: 80.0)</code>等价于：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">BoxConstraints</span><span class=\"token punctuation\">(</span>minHeight<span class=\"token punctuation\">:</span> <span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span>maxHeight<span class=\"token punctuation\">:</span> <span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span>minWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span>maxWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">80.0</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>而实际上<code>ConstrainedBox</code>和<code>SizedBox</code>都是通过<code>RenderConstrainedBox</code>来渲染的，我们可以看到<code>ConstrainedBox</code>和<code>SizedBox</code>的<code>createRenderObject()</code>方法都返回的是一个<code>RenderConstrainedBox</code>对象：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token metadata symbol\">@override</span>\nRenderConstrainedBox <span class=\"token function\">createRenderObject</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">RenderConstrainedBox</span><span class=\"token punctuation\">(</span>\n    additionalConstraints<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h2 id=\"_5-2-3-多重限制\"><a href=\"#_5-2-3-多重限制\" class=\"header-anchor\">#</a> 5.2.3 多重限制</h2> <p>如果某一个组件有多个父级<code>ConstrainedBox</code>限制，那么最终会是哪个生效？我们看一个例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">ConstrainedBox</span><span class=\"token punctuation\">(</span>\n    constraints<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxConstraints</span><span class=\"token punctuation\">(</span>minWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">60.0</span><span class=\"token punctuation\">,</span> minHeight<span class=\"token punctuation\">:</span> <span class=\"token number\">60.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//父</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">ConstrainedBox</span><span class=\"token punctuation\">(</span>\n      constraints<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxConstraints</span><span class=\"token punctuation\">(</span>minWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">90.0</span><span class=\"token punctuation\">,</span> minHeight<span class=\"token punctuation\">:</span> <span class=\"token number\">20.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token comment\">//子</span>\n      child<span class=\"token punctuation\">:</span> redBox<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>上面我们有父子两个<code>ConstrainedBox</code>，他们的限制条件不同，运行后效果如图5-4所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图5-4\"></p> <p>最终显示效果是宽90，高60，也就是说是子<code>ConstrainedBox</code>的<code>minWidth</code>生效，而<code>minHeight</code>是父<code>ConstrainedBox</code>生效。单凭这个例子，我们还总结不出什么规律，我们将上例中父子限制条件换一下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">ConstrainedBox</span><span class=\"token punctuation\">(</span>\n    constraints<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxConstraints</span><span class=\"token punctuation\">(</span>minWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">90.0</span><span class=\"token punctuation\">,</span> minHeight<span class=\"token punctuation\">:</span> <span class=\"token number\">20.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">ConstrainedBox</span><span class=\"token punctuation\">(</span>\n      constraints<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxConstraints</span><span class=\"token punctuation\">(</span>minWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">60.0</span><span class=\"token punctuation\">,</span> minHeight<span class=\"token punctuation\">:</span> <span class=\"token number\">60.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> redBox<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行效果如图5-5所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图5-5\"></p> <p>最终的显示效果仍然是90，高60，效果相同，但意义不同，因为此时<code>minWidth</code>生效的是父<code>ConstrainedBox</code>，而<code>minHeight</code>是子<code>ConstrainedBox</code>生效。</p> <p>通过上面示例，我们发现有多重限制时，对于<code>minWidth</code>和<code>minHeight</code>来说，是取父子中相应数值较大的。实际上，只有这样才能保证父限制与子限制不冲突。</p> <blockquote><p>思考题：对于<code>maxWidth</code>和<code>maxHeight</code>，多重限制的策略是什么样的呢？</p></blockquote> <h2 id=\"_5-2-4-unconstrainedbox\"><a href=\"#_5-2-4-unconstrainedbox\" class=\"header-anchor\">#</a> 5.2.4 UnconstrainedBox</h2> <p><code>UnconstrainedBox</code>不会对子组件产生任何限制，它允许其子组件按照其本身大小绘制。一般情况下，我们会很少直接使用此组件，但在&quot;去除&quot;多重限制的时候也许会有帮助，我们看下下面的代码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">ConstrainedBox</span><span class=\"token punctuation\">(</span>\n    constraints<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxConstraints</span><span class=\"token punctuation\">(</span>minWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">60.0</span><span class=\"token punctuation\">,</span> minHeight<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>  <span class=\"token comment\">//父</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">UnconstrainedBox</span><span class=\"token punctuation\">(</span> <span class=\"token comment\">//“去除”父级限制</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">ConstrainedBox</span><span class=\"token punctuation\">(</span>\n        constraints<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxConstraints</span><span class=\"token punctuation\">(</span>minWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">90.0</span><span class=\"token punctuation\">,</span> minHeight<span class=\"token punctuation\">:</span> <span class=\"token number\">20.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token comment\">//子</span>\n        child<span class=\"token punctuation\">:</span> redBox<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>上面代码中，如果没有中间的<code>UnconstrainedBox</code>，那么根据上面所述的多重限制规则，那么最终将显示一个90×100的红色框。但是由于<code>UnconstrainedBox</code> “去除”了父<code>ConstrainedBox</code>的限制，则最终会按照子<code>ConstrainedBox</code>的限制来绘制<code>redBox</code>，即90×20：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图5-6\"></p> <p>但是，读者请注意，<code>UnconstrainedBox</code>对父组件限制的“去除”并非是真正的去除：上面例子中虽然红色区域大小是90×20，但上方仍然有80的空白空间。也就是说父限制的<code>minHeight</code>(100.0)仍然是生效的，只不过它不影响最终子元素<code>redBox</code>的大小，但仍然还是占有相应的空间，可以认为此时的父<code>ConstrainedBox</code>是作用于子<code>UnconstrainedBox</code>上，而<code>redBox</code>只受子<code>ConstrainedBox</code>限制，这一点请读者务必注意。</p> <p>那么有什么方法可以彻底去除父<code>ConstrainedBox</code>的限制吗？答案是否定的！所以在此提示读者，在定义一个通用的组件时，如果要对子组件指定限制，那么一定要注意，因为一旦指定限制条件，子组件如果要进行相关自定义大小时将可能非常困难，因为子组件在不更改父组件的代码的情况下无法彻底去除其限制条件。</p> <p>在实际开发中，当我们发现已经使用<code>SizedBox</code>或<code>ConstrainedBox</code>给子元素指定了宽高，但是仍然没有效果时，几乎可以断定：已经有父元素已经设置了限制！举个例子，如Material组件库中的<code>AppBar</code>（导航栏）的右侧菜单中，我们使用<code>SizedBox</code>指定了loading按钮的大小，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>\n   title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n   actions<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n         <span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span>\n             width<span class=\"token punctuation\">:</span> <span class=\"token number\">20</span><span class=\"token punctuation\">,</span> \n             height<span class=\"token punctuation\">:</span> <span class=\"token number\">20</span><span class=\"token punctuation\">,</span>\n             child<span class=\"token punctuation\">:</span> <span class=\"token function\">CircularProgressIndicator</span><span class=\"token punctuation\">(</span>\n                 strokeWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">3</span><span class=\"token punctuation\">,</span>\n                 valueColor<span class=\"token punctuation\">:</span> <span class=\"token function\">AlwaysStoppedAnimation</span><span class=\"token punctuation\">(</span>Colors<span class=\"token punctuation\">.</span>white70<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n             <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n         <span class=\"token punctuation\">)</span>\n   <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>上面代码运行后，效果如图5-7所示：</p> <p><img src=\"/assets/img/5-7.27479289.png\" alt=\"图5-6\"></p> <p>我们会发现右侧loading按钮大小并没有发生变化！这正是因为<code>AppBar</code>中已经指定了<code>actions</code>按钮的限制条件，所以我们要自定义loading按钮大小，就必须通过<code>UnconstrainedBox</code>来“去除”父元素的限制，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>\n  title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  actions<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n      <span class=\"token function\">UnconstrainedBox</span><span class=\"token punctuation\">(</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span>\n              width<span class=\"token punctuation\">:</span> <span class=\"token number\">20</span><span class=\"token punctuation\">,</span>\n              height<span class=\"token punctuation\">:</span> <span class=\"token number\">20</span><span class=\"token punctuation\">,</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">CircularProgressIndicator</span><span class=\"token punctuation\">(</span>\n                strokeWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">3</span><span class=\"token punctuation\">,</span>\n                valueColor<span class=\"token punctuation\">:</span> <span class=\"token function\">AlwaysStoppedAnimation</span><span class=\"token punctuation\">(</span>Colors<span class=\"token punctuation\">.</span>white70<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行后效果如图5-8所示：</p> <p><img src=\"/assets/img/5-8.5b913c51.png\" alt=\"图5-8\"></p> <p>生效了！</p> <h2 id=\"_5-2-4-其它尺寸限制类容器\"><a href=\"#_5-2-4-其它尺寸限制类容器\" class=\"header-anchor\">#</a> 5.2.4 其它尺寸限制类容器</h2> <p>除了上面介绍的这些常用的尺寸限制类容器外，还有一些其他的尺寸限制类容器，比如<code>AspectRatio</code>，它可以指定子组件的长宽比、<code>LimitedBox</code> 用于指定最大宽高、<code>FractionallySizedBox</code> 可以根据父容器宽高的百分比来设置子组件宽高等，由于这些容器使用起来都比较简单，我们便不再赘述，读者可以自行了解。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/11.2026a71f.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter5/container.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>5.5 Container | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/75.bef82a69.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter5/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_5-5-container\"><a href=\"#_5-5-container\" class=\"header-anchor\">#</a> 5.5 Container</h1> <p>我们在前面的章节示例中多次用到过<code>Container</code>组件，本节我们就详细介绍一下<code>Container</code>组件。<code>Container</code>是一个组合类容器，它本身不对应具体的<code>RenderObject</code>，它是<code>DecoratedBox</code>、<code>ConstrainedBox、Transform</code>、<code>Padding</code>、<code>Align</code>等组件组合的一个多功能容器，所以我们只需通过一个<code>Container</code>组件可以实现同时需要装饰、变换、限制的场景。下面是<code>Container</code>的定义：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Container</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>alignment<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>padding<span class=\"token punctuation\">,</span> <span class=\"token comment\">//容器内补白，属于decoration的装饰范围</span>\n  Color color<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 背景色</span>\n  Decoration decoration<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 背景装饰</span>\n  Decoration foregroundDecoration<span class=\"token punctuation\">,</span> <span class=\"token comment\">//前景装饰</span>\n  double width<span class=\"token punctuation\">,</span><span class=\"token comment\">//容器的宽度</span>\n  double height<span class=\"token punctuation\">,</span> <span class=\"token comment\">//容器的高度</span>\n  BoxConstraints constraints<span class=\"token punctuation\">,</span> <span class=\"token comment\">//容器大小的限制条件</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>margin<span class=\"token punctuation\">,</span><span class=\"token comment\">//容器外补白，不属于decoration的装饰范围</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>transform<span class=\"token punctuation\">,</span> <span class=\"token comment\">//变换</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p><code>Container</code>的大多数属性在介绍其它容器时都已经介绍过了，不再赘述，但有两点需要说明：</p> <ul><li>容器的大小可以通过<code>width</code>、<code>height</code>属性来指定，也可以通过<code>constraints</code>来指定；如果它们同时存在时，<code>width</code>、<code>height</code>优先。实际上Container内部会根据<code>width</code>、<code>height</code>来生成一个<code>constraints</code>。</li> <li><code>color</code>和<code>decoration</code>是互斥的，如果同时设置它们则会报错！实际上，当指定<code>color</code>时，<code>Container</code>内会自动创建一个<code>decoration</code>。</li></ul> <h3 id=\"实例\"><a href=\"#实例\" class=\"header-anchor\">#</a> 实例</h3> <p>我们通过<code>Container</code>来实现如图5-16所示的卡片：</p> <p><img src=\"/assets/img/5-16.24d30a6e.png\" alt=\"图5-16\"></p> <p>实现代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n  margin<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">only</span><span class=\"token punctuation\">(</span>top<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span> left<span class=\"token punctuation\">:</span> <span class=\"token number\">120.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//容器外填充</span>\n  constraints<span class=\"token punctuation\">:</span> BoxConstraints<span class=\"token punctuation\">.</span><span class=\"token function\">tightFor</span><span class=\"token punctuation\">(</span>width<span class=\"token punctuation\">:</span> <span class=\"token number\">200.0</span><span class=\"token punctuation\">,</span> height<span class=\"token punctuation\">:</span> <span class=\"token number\">150.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//卡片大小</span>\n  decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span><span class=\"token comment\">//背景装饰</span>\n      gradient<span class=\"token punctuation\">:</span> <span class=\"token function\">RadialGradient</span><span class=\"token punctuation\">(</span> <span class=\"token comment\">//背景径向渐变</span>\n          colors<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span> Colors<span class=\"token punctuation\">.</span>orange<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n          center<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>topLeft<span class=\"token punctuation\">,</span>\n          radius<span class=\"token punctuation\">:</span> <span class=\"token number\">.98</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      boxShadow<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span> <span class=\"token comment\">//卡片阴影</span>\n        <span class=\"token function\">BoxShadow</span><span class=\"token punctuation\">(</span>\n            color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>black54<span class=\"token punctuation\">,</span>\n            offset<span class=\"token punctuation\">:</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token number\">2.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">2.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            blurRadius<span class=\"token punctuation\">:</span> <span class=\"token number\">4.0</span>\n        <span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">]</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  transform<span class=\"token punctuation\">:</span> Matrix4<span class=\"token punctuation\">.</span><span class=\"token function\">rotationZ</span><span class=\"token punctuation\">(</span><span class=\"token number\">.2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//卡片倾斜变换</span>\n  alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span> <span class=\"token comment\">//卡片内文字居中</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span> <span class=\"token comment\">//卡片文字</span>\n    <span class=\"token string\">&quot;5.20&quot;</span><span class=\"token punctuation\">,</span> style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">,</span> fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">40.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>可以看到<code>Container</code>具备多种组件的功能，通过查看<code>Container</code>源码，我们会很容易发现它正是前面我们介绍过的多种组件组合而成。在Flutter中，<code>Container</code>组件也正是组合优先于继承的实例。</p> <h3 id=\"padding和margin\"><a href=\"#padding和margin\" class=\"header-anchor\">#</a> Padding和Margin</h3> <p>接下来我们来研究一下<code>Container</code>组件<code>margin</code>和<code>padding</code>属性的区别:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n  margin<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">20.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//容器外补白</span>\n  color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>orange<span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hello world!&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n  padding<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">20.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//容器内补白</span>\n  color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>orange<span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hello world!&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n</code></pre></div><p><img src=\"data:image/png;base64,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\" alt=\"图5-17\"></p> <p>可以发现，直观的感觉就是<code>margin</code>的留白是在容器外部，而<code>padding</code>的留白是在容器内部，读者需要记住这个差异。事实上，<code>Container</code>内<code>margin</code>和<code>padding</code>都是通过<code>Padding</code> 组件来实现的，上面的示例代码实际上等价于：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n  padding<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">20.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">DecoratedBox</span><span class=\"token punctuation\">(</span>\n    decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>orange<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hello world!&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token function\">DecoratedBox</span><span class=\"token punctuation\">(</span>\n  decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>orange<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n    padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">20.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hello world!&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>    \n</code></pre></div></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/75.bef82a69.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter5/decoratedbox.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>5.3 装饰容器DecoratedBox | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/123.b83bac47.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter5/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_5-3-装饰容器decoratedbox\"><a href=\"#_5-3-装饰容器decoratedbox\" class=\"header-anchor\">#</a> 5.3 装饰容器DecoratedBox</h1> <p><code>DecoratedBox</code>可以在其子组件绘制前(或后)绘制一些装饰（Decoration），如背景、边框、渐变等。<code>DecoratedBox</code>定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">const</span> <span class=\"token function\">DecoratedBox</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  Decoration decoration<span class=\"token punctuation\">,</span>\n  DecorationPosition position <span class=\"token operator\">=</span> DecorationPosition<span class=\"token punctuation\">.</span>background<span class=\"token punctuation\">,</span>\n  Widget child\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><ul><li><code>decoration</code>：代表将要绘制的装饰，它的类型为<code>Decoration</code>。<code>Decoration</code>是一个抽象类，它定义了一个接口 <code>createBoxPainter()</code>，子类的主要职责是需要通过实现它来创建一个画笔，该画笔用于绘制装饰。</li> <li><code>position</code>：此属性决定在哪里绘制<code>Decoration</code>，它接收<code>DecorationPosition</code>的枚举类型，该枚举类有两个值：\n<ul><li><code>background</code>：在子组件之后绘制，即背景装饰。</li> <li><code>foreground</code>：在子组件之上绘制，即前景。</li></ul></li></ul> <h4 id=\"boxdecoration\"><a href=\"#boxdecoration\" class=\"header-anchor\">#</a> BoxDecoration</h4> <p>我们通常会直接使用<code>BoxDecoration</code>类，它是一个Decoration的子类，实现了常用的装饰元素的绘制。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  Color color<span class=\"token punctuation\">,</span> <span class=\"token comment\">//颜色</span>\n  DecorationImage image<span class=\"token punctuation\">,</span><span class=\"token comment\">//图片</span>\n  BoxBorder border<span class=\"token punctuation\">,</span> <span class=\"token comment\">//边框</span>\n  BorderRadiusGeometry borderRadius<span class=\"token punctuation\">,</span> <span class=\"token comment\">//圆角</span>\n  List<span class=\"token operator\">&lt;</span>BoxShadow<span class=\"token operator\">&gt;</span> boxShadow<span class=\"token punctuation\">,</span> <span class=\"token comment\">//阴影,可以指定多个</span>\n  Gradient gradient<span class=\"token punctuation\">,</span> <span class=\"token comment\">//渐变</span>\n  BlendMode backgroundBlendMode<span class=\"token punctuation\">,</span> <span class=\"token comment\">//背景混合模式</span>\n  BoxShape shape <span class=\"token operator\">=</span> BoxShape<span class=\"token punctuation\">.</span>rectangle<span class=\"token punctuation\">,</span> <span class=\"token comment\">//形状</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>各个属性名都是自解释的，详情读者可以查看API文档。下面我们实现一个带阴影的背景色渐变的按钮：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code> <span class=\"token function\">DecoratedBox</span><span class=\"token punctuation\">(</span>\n    decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>\n      gradient<span class=\"token punctuation\">:</span> <span class=\"token function\">LinearGradient</span><span class=\"token punctuation\">(</span>colors<span class=\"token punctuation\">:</span><span class=\"token punctuation\">[</span>Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span>Colors<span class=\"token punctuation\">.</span>orange<span class=\"token punctuation\">[</span><span class=\"token number\">700</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//背景渐变</span>\n      borderRadius<span class=\"token punctuation\">:</span> BorderRadius<span class=\"token punctuation\">.</span><span class=\"token function\">circular</span><span class=\"token punctuation\">(</span><span class=\"token number\">3.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//3像素圆角</span>\n      boxShadow<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span> <span class=\"token comment\">//阴影</span>\n        <span class=\"token function\">BoxShadow</span><span class=\"token punctuation\">(</span>\n            color<span class=\"token punctuation\">:</span>Colors<span class=\"token punctuation\">.</span>black54<span class=\"token punctuation\">,</span>\n            offset<span class=\"token punctuation\">:</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token number\">2.0</span><span class=\"token punctuation\">,</span><span class=\"token number\">2.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            blurRadius<span class=\"token punctuation\">:</span> <span class=\"token number\">4.0</span>\n        <span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">]</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>padding<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">symmetric</span><span class=\"token punctuation\">(</span>horizontal<span class=\"token punctuation\">:</span> <span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span> vertical<span class=\"token punctuation\">:</span> <span class=\"token number\">18.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Login&quot;</span><span class=\"token punctuation\">,</span> style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行后效果如图5-9所示：</p> <p><img src=\"/assets/img/5-9.47017753.png\" alt=\"图5-9\"></p> <p>怎么样，通过<code>BoxDecoration</code>我们实现了一个渐变按钮的外观，但此示例还不是一个标准的按钮，因为它还不能响应点击事件，我们将在后面“自定义组件”一章中实现一个完整功能的<code>GradientButton</code>。另外，上面的例子中使用了<code>LinearGradient</code>类，它用于定义线性渐变的类，Flutter中还提供了其它渐变配置类，如<code>RadialGradient</code>、<code>SweepGradient</code>，读者若有需要可以自行查看API文档。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/123.b83bac47.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter5/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>容器类Widget | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/211.43a19344.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter5/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"容器类widget\"><a href=\"#容器类widget\" class=\"header-anchor\">#</a> 容器类Widget</h1> <p>容器类Widget和布局类Widget都作用于其子Widget，不同的是：</p> <ul><li>布局类Widget一般都需要接收一个widget数组（children），他们直接或间接继承自（或包含）MultiChildRenderObjectWidget ；而容器类Widget一般只需要接收一个子Widget（child），他们直接或间接继承自（或包含）SingleChildRenderObjectWidget。</li> <li>布局类Widget是按照一定的排列方式来对其子Widget进行排列；而容器类Widget一般只是包装其子Widget，对其添加一些修饰（补白或背景色等）、变换(旋转或剪裁等)、或限制(大小等)。</li></ul> <p>注意，Flutter官方并没有对Widget进行官方分类，我们对其分类主要是为了方便讨论和对Widget功能区分的记忆。</p> <h2 id=\"本章目录\"><a href=\"#本章目录\" class=\"header-anchor\">#</a> 本章目录</h2> <ul><li><a href=\"/v2/chapter5/padding.html\">5.1：填充（Padding）</a></li> <li><a href=\"/v2/chapter5/constrainedbox_and_sizebox.html\">5.2：尺寸限制类容器（ConstrainedBox等）</a></li> <li><a href=\"/v2/chapter5/decoratedbox.html\">5.3：装饰容器（DecoratedBox）</a></li> <li><a href=\"/v2/chapter5/transform.html\">5.4：变换（Transform）</a></li> <li><a href=\"/v2/chapter5/container.html\">5.5：Container容器</a></li> <li><a href=\"/v2/chapter5/material_scaffold.html\">5.6：Scaffold、TabBar、底部导航</a></li> <li><a href=\"/v2/chapter5/clip.html\">5.7：剪裁（Clip）</a></li></ul></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/211.43a19344.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter5/material_scaffold.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>5.6 Scaffold、TabBar、底部导航 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/19.175563ad.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter5/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_5-6-scaffold、tabbar、底部导航\"><a href=\"#_5-6-scaffold、tabbar、底部导航\" class=\"header-anchor\">#</a> 5.6 Scaffold、TabBar、底部导航</h1> <p>Material组件库提供了丰富多样的组件，本节介绍一些常用的组件，其余的读者可以自行查看文档或Flutter Gallery中Material组件部分的示例。</p> <blockquote><p>Flutter Gallery是Flutter官方提供的Flutter Demo，源码位于flutter源码中的examples目录下，笔者强烈建议用户将Flutter Gallery示例跑起来，它是一个很全面的Flutter示例应用，是非常好的参考Demo，也是笔者学习Flutter的第一手资料。</p></blockquote> <h2 id=\"_5-6-1-scaffold\"><a href=\"#_5-6-1-scaffold\" class=\"header-anchor\">#</a> 5.6.1 Scaffold</h2> <p>一个完整的路由页可能会包含导航栏、抽屉菜单(Drawer)以及底部Tab导航菜单等。如果每个路由页面都需要开发者自己手动去实现这些，这会是一件非常麻烦且无聊的事。幸运的是，Flutter Material组件库提供了一些现成的组件来减少我们的开发任务。<code>Scaffold</code>是一个路由页的骨架，我们使用它可以很容易地拼装出一个完整的页面。</p> <h3 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h3> <p>我们实现一个页面，它包含：</p> <ol><li>一个导航栏</li> <li>导航栏右边有一个分享按钮</li> <li>有一个抽屉菜单</li> <li>有一个底部导航</li> <li>右下角有一个悬浮的动作按钮</li></ol> <p>最终效果如图5-18、图5-19所示：</p> <p><img src=\"/assets/img/5-18.f83914b2.png\" alt=\"图5-18\"> <img src=\"/assets/img/5-19.a2dab018.png\" alt=\"图5-19\"></p> <p>实现代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">ScaffoldRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _ScaffoldRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">_ScaffoldRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_ScaffoldRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>ScaffoldRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  int _selectedIndex <span class=\"token operator\">=</span> <span class=\"token number\">1</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n      appBar<span class=\"token punctuation\">:</span> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span> <span class=\"token comment\">//导航栏</span>\n        title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;App Name&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> \n        actions<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span> <span class=\"token comment\">//导航栏右侧菜单</span>\n          <span class=\"token function\">IconButton</span><span class=\"token punctuation\">(</span>icon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>share<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      drawer<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">MyDrawer</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//抽屉</span>\n      bottomNavigationBar<span class=\"token punctuation\">:</span> <span class=\"token function\">BottomNavigationBar</span><span class=\"token punctuation\">(</span> <span class=\"token comment\">// 底部导航</span>\n        items<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>BottomNavigationBarItem<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n          <span class=\"token function\">BottomNavigationBarItem</span><span class=\"token punctuation\">(</span>icon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>home<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Home'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">BottomNavigationBarItem</span><span class=\"token punctuation\">(</span>icon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>business<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Business'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">BottomNavigationBarItem</span><span class=\"token punctuation\">(</span>icon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>school<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'School'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        currentIndex<span class=\"token punctuation\">:</span> _selectedIndex<span class=\"token punctuation\">,</span>\n        fixedColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span>\n        onTap<span class=\"token punctuation\">:</span> _onItemTapped<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      floatingActionButton<span class=\"token punctuation\">:</span> <span class=\"token function\">FloatingActionButton</span><span class=\"token punctuation\">(</span> <span class=\"token comment\">//悬浮按钮</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>add<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          onPressed<span class=\"token punctuation\">:</span>_onAdd\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_onItemTapped</span><span class=\"token punctuation\">(</span>int index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _selectedIndex <span class=\"token operator\">=</span> index<span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_onAdd</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面代码中我们用到了如下组件：</p> <table><thead><tr><th>组件名称</th> <th>解释</th></tr></thead> <tbody><tr><td>AppBar</td> <td>一个导航栏骨架</td></tr> <tr><td>MyDrawer</td> <td>抽屉菜单</td></tr> <tr><td>BottomNavigationBar</td> <td>底部导航栏</td></tr> <tr><td>FloatingActionButton</td> <td>漂浮按钮</td></tr></tbody></table> <p>下面我们来分别介绍一下它们。</p> <h2 id=\"_5-6-2-appbar\"><a href=\"#_5-6-2-appbar\" class=\"header-anchor\">#</a> 5.6.2 AppBar</h2> <p><code>AppBar</code>是一个Material风格的导航栏，通过它可以设置导航栏标题、导航栏菜单、导航栏底部的Tab标题等。下面我们看看AppBar的定义：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  Key key<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>leading<span class=\"token punctuation\">,</span> <span class=\"token comment\">//导航栏最左侧Widget，常见为抽屉菜单按钮或返回按钮。</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>automaticallyImplyLeading <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//如果leading为null，是否自动实现默认的leading按钮</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>title<span class=\"token punctuation\">,</span><span class=\"token comment\">// 页面标题</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>actions<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 导航栏右侧菜单</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>bottom<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 导航栏底部菜单，通常为Tab按钮组</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>elevation <span class=\"token operator\">=</span> <span class=\"token number\">4.0</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 导航栏阴影</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>centerTitle<span class=\"token punctuation\">,</span> <span class=\"token comment\">//标题是否居中 </span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>backgroundColor<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>   <span class=\"token comment\">//其它属性见源码注释</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>如果给<code>Scaffold</code>添加了抽屉菜单，默认情况下<code>Scaffold</code>会自动将<code>AppBar</code>的<code>leading</code>设置为菜单按钮（如上面截图所示），点击它便可打开抽屉菜单。如果我们想自定义菜单图标，可以手动来设置<code>leading</code>，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n  appBar<span class=\"token punctuation\">:</span> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>\n    title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;App Name&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    leading<span class=\"token punctuation\">:</span> <span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token function\">IconButton</span><span class=\"token punctuation\">(</span>\n        icon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>dashboard<span class=\"token punctuation\">,</span> color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//自定义图标</span>\n        onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token comment\">// 打开抽屉菜单  </span>\n          Scaffold<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">openDrawer</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> \n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>  \n  <span class=\"token punctuation\">)</span>  \n</code></pre></div><p>代码运行效果如图5-20所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图5-20\"></p> <p>可以看到左侧菜单已经替换成功。</p> <p>代码中打开抽屉菜单的方法在<code>ScaffoldState</code>中，通过<code>Scaffold.of(context)</code>可以获取父级最近的<code>Scaffold</code> 组件的<code>State</code>对象。</p> <h3 id=\"tabbar\"><a href=\"#tabbar\" class=\"header-anchor\">#</a> TabBar</h3> <p>下面我们通过“bottom”属性来添加一个导航栏底部Tab按钮组，将要实现的效果如图5-21所示：</p> <p><img src=\"/assets/img/5-21.946b0c47.png\" alt=\"图5-21\"></p> <p>Material组件库中提供了一个<code>TabBar</code>组件，它可以快速生成<code>Tab</code>菜单，下面是上图对应的源码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">_ScaffoldRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>ScaffoldRoute<span class=\"token operator\">&gt;</span>\n    <span class=\"token keyword\">with</span> SingleTickerProviderStateMixin <span class=\"token punctuation\">{</span>\n\n  TabController _tabController<span class=\"token punctuation\">;</span> <span class=\"token comment\">//需要定义一个Controller</span>\n  List tabs <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token string\">&quot;新闻&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;历史&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;图片&quot;</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">// 创建Controller  </span>\n    _tabController <span class=\"token operator\">=</span> <span class=\"token function\">TabController</span><span class=\"token punctuation\">(</span>length<span class=\"token punctuation\">:</span> tabs<span class=\"token punctuation\">.</span>length<span class=\"token punctuation\">,</span> vsync<span class=\"token punctuation\">:</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n      appBar<span class=\"token punctuation\">:</span> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>\n        <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n        bottom<span class=\"token punctuation\">:</span> <span class=\"token function\">TabBar</span><span class=\"token punctuation\">(</span>   <span class=\"token comment\">//生成Tab菜单</span>\n          controller<span class=\"token punctuation\">:</span> _tabController<span class=\"token punctuation\">,</span>\n          tabs<span class=\"token punctuation\">:</span> tabs<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">Tab</span><span class=\"token punctuation\">(</span>text<span class=\"token punctuation\">:</span> e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toList</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n\n  <span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面代码首先创建了一个<code>TabController</code> ，它是用于控制/监听<code>Tab</code>菜单切换的。接下来通过TabBar生成了一个底部菜单栏，<code>TabBar</code>的<code>tabs</code>属性接受一个Widget数组，表示每一个Tab子菜单，我们可以自定义，也可以像示例中一样直接使用<code>Tab</code> 组件，它是Material组件库提供的Material风格的Tab菜单。</p> <p><code>Tab</code>组件有三个可选参数，除了可以指定文字外，还可以指定Tab菜单图标，或者直接自定义组件样式。<code>Tab</code>组件定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Tab</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  Key key<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>text<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 菜单文本</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>icon<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 菜单图标</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 自定义组件样式</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>开发者可以根据实际需求来定制。</p> <h3 id=\"tabbarview\"><a href=\"#tabbarview\" class=\"header-anchor\">#</a> TabBarView</h3> <p>通过<code>TabBar</code>我们只能生成一个静态的菜单，真正的Tab页还没有实现。由于<code>Tab</code>菜单和Tab页的切换需要同步，我们需要通过<code>TabController</code>去监听Tab菜单的切换去切换Tab页，代码如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>_tabController<span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>  \n  <span class=\"token keyword\">switch</span><span class=\"token punctuation\">(</span>_tabController<span class=\"token punctuation\">.</span>index<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">case</span> <span class=\"token number\">1</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">case</span> <span class=\"token number\">2</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token punctuation\">;</span>   \n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>如果我们Tab页可以滑动切换的话，还需要在滑动过程中更新TabBar指示器的偏移！显然，要手动处理这些是很麻烦的，为此，Material库提供了一个<code>TabBarView</code>组件，通过它不仅可以轻松的实现Tab页，而且可以非常容易的配合TabBar来实现同步切换和滑动状态同步，示例如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n  appBar<span class=\"token punctuation\">:</span> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n    bottom<span class=\"token punctuation\">:</span> <span class=\"token function\">TabBar</span><span class=\"token punctuation\">(</span>\n      controller<span class=\"token punctuation\">:</span> _tabController<span class=\"token punctuation\">,</span>\n      tabs<span class=\"token punctuation\">:</span> tabs<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">Tab</span><span class=\"token punctuation\">(</span>text<span class=\"token punctuation\">:</span> e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toList</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  drawer<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">MyDrawer</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  body<span class=\"token punctuation\">:</span> <span class=\"token function\">TabBarView</span><span class=\"token punctuation\">(</span>\n    controller<span class=\"token punctuation\">:</span> _tabController<span class=\"token punctuation\">,</span>\n    children<span class=\"token punctuation\">:</span> tabs<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span> <span class=\"token comment\">//创建3个Tab页</span>\n      <span class=\"token keyword\">return</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n        alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">,</span> textScaleFactor<span class=\"token punctuation\">:</span> <span class=\"token number\">5</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toList</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">// 省略无关代码  </span>\n<span class=\"token punctuation\">)</span>    \n</code></pre></div><p>运行后效果如图5-22所示：</p> <p><img src=\"/assets/img/5-22.e38796ed.png\" alt=\"图5-22\"></p> <p>现在，无论是点击导航栏Tab菜单还是在页面上左右滑动，Tab页面都会切换，并且Tab菜单的状态和Tab页面始终保持同步！那它们是如何实现同步的呢？细心的读者可能已经发现，上例中<code>TabBar</code>和<code>TabBarView</code>的<code>controller</code>是同一个！正是如此，<code>TabBar</code>和<code>TabBarView</code>正是通过同一个<code>controller</code>来实现菜单切换和滑动状态同步的，有关<code>TabController</code>的详细信息，我们不在本书做过多介绍，使用时读者直接查看SDK即可。</p> <p>另外，Material组件库也提供了一个<code>PageView</code> 组件，它和<code>TabBarView</code>功能相似，读者可以自行了解一下。</p> <h2 id=\"_5-6-3-抽屉菜单drawer\"><a href=\"#_5-6-3-抽屉菜单drawer\" class=\"header-anchor\">#</a> 5.6.3 抽屉菜单Drawer</h2> <p><code>Scaffold</code>的<code>drawer</code>和<code>endDrawer</code>属性可以分别接受一个Widget来作为页面的左、右抽屉菜单。如果开发者提供了抽屉菜单，那么当用户手指从屏幕左（或右）侧向里滑动时便可打开抽屉菜单。本节开始部分的示例中实现了一个左抽屉菜单<code>MyDrawer</code>，它的源码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">MyDrawer</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function\">MyDrawer</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Drawer</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> MediaQuery<span class=\"token punctuation\">.</span><span class=\"token function\">removePadding</span><span class=\"token punctuation\">(</span>\n        context<span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">,</span>\n        <span class=\"token comment\">//移除抽屉菜单顶部默认留白</span>\n        removeTop<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n          crossAxisAlignment<span class=\"token punctuation\">:</span> CrossAxisAlignment<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n              padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">only</span><span class=\"token punctuation\">(</span>top<span class=\"token punctuation\">:</span> <span class=\"token number\">38.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n                children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                  <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n                    padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">symmetric</span><span class=\"token punctuation\">(</span>horizontal<span class=\"token punctuation\">:</span> <span class=\"token number\">16.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                    child<span class=\"token punctuation\">:</span> <span class=\"token function\">ClipOval</span><span class=\"token punctuation\">(</span>\n                      child<span class=\"token punctuation\">:</span> Image<span class=\"token punctuation\">.</span><span class=\"token function\">asset</span><span class=\"token punctuation\">(</span>\n                        <span class=\"token string\">&quot;imgs/avatar.png&quot;</span><span class=\"token punctuation\">,</span>\n                        width<span class=\"token punctuation\">:</span> <span class=\"token number\">80</span><span class=\"token punctuation\">,</span>\n                      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>\n                    <span class=\"token string\">&quot;Wendux&quot;</span><span class=\"token punctuation\">,</span>\n                    style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>fontWeight<span class=\"token punctuation\">:</span> FontWeight<span class=\"token punctuation\">.</span>bold<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">)</span>\n                <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token function\">Expanded</span><span class=\"token punctuation\">(</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">ListView</span><span class=\"token punctuation\">(</span>\n                children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                  <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>\n                    leading<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>add<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                    title<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Add account'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>\n                    leading<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>settings<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                    title<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Manage accounts'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>抽屉菜单通常将<code>Drawer</code>组件作为根节点，它实现了Material风格的菜单面板，<code>MediaQuery.removePadding</code>可以移除Drawer默认的一些留白（比如Drawer默认顶部会留和手机状态栏等高的留白），读者可以尝试传递不同的参数来看看实际效果。抽屉菜单页由顶部和底部组成，顶部由用户头像和昵称组成，底部是一个菜单列表，用ListView实现，关于ListView我们将在后面“可滚动组件”一节详细介绍。</p> <h2 id=\"_5-6-4-floatingactionbutton\"><a href=\"#_5-6-4-floatingactionbutton\" class=\"header-anchor\">#</a> 5.6.4 FloatingActionButton</h2> <p><code>FloatingActionButton</code>是Material设计规范中的一种特殊Button，通常悬浮在页面的某一个位置作为某种常用动作的快捷入口，如本节示例中页面右下角的&quot;➕&quot;号按钮。我们可以通过<code>Scaffold</code>的<code>floatingActionButton</code>属性来设置一个<code>FloatingActionButton</code>，同时通过<code>floatingActionButtonLocation</code>属性来指定其在页面中悬浮的位置，这个比较简单，不再赘述。</p> <h2 id=\"_5-6-5-底部tab导航栏\"><a href=\"#_5-6-5-底部tab导航栏\" class=\"header-anchor\">#</a> 5.6.5  底部Tab导航栏</h2> <p>我们可以通过<code>Scaffold</code>的<code>bottomNavigationBar</code>属性来设置底部导航，如本节开始示例所示，我们通过Material组件库提供的<code>BottomNavigationBar</code>和<code>BottomNavigationBarItem</code>两种组件来实现Material风格的底部导航栏。可以看到上面的实现代码非常简单，所以不再赘述，但是如果我们想实现如图5-23所示效果的底部导航栏应该怎么做呢？</p> <p><img src=\"/assets/img/5-23.9b16ea45.png\" alt=\"图5-23\"></p> <p>Material组件库中提供了一个<code>BottomAppBar</code> 组件，它可以和<code>FloatingActionButton</code>配合实现这种“打洞”效果，源码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>bottomNavigationBar<span class=\"token punctuation\">:</span> <span class=\"token function\">BottomAppBar</span><span class=\"token punctuation\">(</span>\n  color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">,</span>\n  shape<span class=\"token punctuation\">:</span> <span class=\"token function\">CircularNotchedRectangle</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 底部导航栏打一个圆形的洞</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n    children<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>\n      <span class=\"token function\">IconButton</span><span class=\"token punctuation\">(</span>icon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>home<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//中间位置空出</span>\n      <span class=\"token function\">IconButton</span><span class=\"token punctuation\">(</span>icon<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>business<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>spaceAround<span class=\"token punctuation\">,</span> <span class=\"token comment\">//均分底部导航栏横向空间</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>可以看到，上面代码中没有控制打洞位置的属性，实际上，打洞的位置取决于<code>FloatingActionButton</code>的位置，上面<code>FloatingActionButton</code>的位置为：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>floatingActionButtonLocation<span class=\"token punctuation\">:</span> FloatingActionButtonLocation<span class=\"token punctuation\">.</span>centerDocked<span class=\"token punctuation\">,</span>\n</code></pre></div><p>所以打洞位置在底部导航栏的正中间。</p> <p><code>BottomAppBar</code>的<code>shape</code>属性决定洞的外形，<code>CircularNotchedRectangle</code>实现了一个圆形的外形，我们也可以自定义外形，比如，Flutter Gallery示例中就有一个“钻石”形状的示例，读者感兴趣可以自行查看。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/19.175563ad.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter5/padding.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>5.1 填充（Padding） | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/124.7a950a7c.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter5/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h2 id=\"_5-1-填充-padding\"><a href=\"#_5-1-填充-padding\" class=\"header-anchor\">#</a> 5.1 填充（Padding）</h2> <p><code>Padding</code>可以给其子节点添加填充（留白），和边距效果类似。我们在前面很多示例中都已经使用过它了，现在来看看它的定义：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  EdgeInsetsGeometry padding<span class=\"token punctuation\">,</span>\n  Widget child<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p><code>EdgeInsetsGeometry</code>是一个抽象类，开发中，我们一般都使用<code>EdgeInsets</code>类，它是<code>EdgeInsetsGeometry</code>的一个子类，定义了一些设置填充的便捷方法。</p> <h3 id=\"edgeinsets\"><a href=\"#edgeinsets\" class=\"header-anchor\">#</a> EdgeInsets</h3> <p>我们看看<code>EdgeInsets</code>提供的便捷方法：</p> <ul><li><code>fromLTRB(double left, double top, double right, double bottom)</code>：分别指定四个方向的填充。</li> <li><code>all(double value)</code> : 所有方向均使用相同数值的填充。</li> <li><code>only({left, top, right ,bottom })</code>：可以设置具体某个方向的填充(可以同时指定多个方向)。</li> <li><code>symmetric({ vertical, horizontal })</code>：用于设置对称方向的填充，<code>vertical</code>指<code>top</code>和<code>bottom</code>，<code>horizontal</code>指<code>left</code>和<code>right</code>。</li></ul> <h3 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h3> <p>下面的示例主要展示了<code>EdgeInsets</code>的不同用法，比较简单，源码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">PaddingTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n      <span class=\"token comment\">//上下左右各添加16像素补白</span>\n      padding<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">16.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n        <span class=\"token comment\">//显式指定对齐方式为左对齐，排除对齐干扰</span>\n        crossAxisAlignment<span class=\"token punctuation\">:</span> CrossAxisAlignment<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n        children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n          <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n            <span class=\"token comment\">//左边添加8像素补白</span>\n            padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">only</span><span class=\"token punctuation\">(</span>left<span class=\"token punctuation\">:</span> <span class=\"token number\">8.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hello world&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n            <span class=\"token comment\">//上下各添加8像素补白</span>\n            padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">symmetric</span><span class=\"token punctuation\">(</span>vertical<span class=\"token punctuation\">:</span> <span class=\"token number\">8.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;I am Jack&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n            <span class=\"token comment\">// 分别指定四个方向的补白</span>\n            padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">fromLTRB</span><span class=\"token punctuation\">(</span><span class=\"token number\">20.0</span><span class=\"token punctuation\">,</span><span class=\"token number\">.0</span><span class=\"token punctuation\">,</span><span class=\"token number\">20.0</span><span class=\"token punctuation\">,</span><span class=\"token number\">20.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Your friend&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行效果如图5-1所示：</p> <p><img src=\"/assets/img/5-1.239dadc0.png\" alt=\"图5-1\"></p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/124.7a950a7c.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter5/transform.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>5.4 变换（Transform） | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/20.6c5c8986.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter5/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_5-4-变换-transform\"><a href=\"#_5-4-变换-transform\" class=\"header-anchor\">#</a> 5.4 变换（Transform）</h1> <p><code>Transform</code>可以在其子组件绘制时对其应用一些矩阵变换来实现一些特效。<code>Matrix4</code>是一个4D矩阵，通过它我们可以实现各种矩阵操作，下面是一个例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n  color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>black<span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Transform</span><span class=\"token punctuation\">(</span>\n    alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>topRight<span class=\"token punctuation\">,</span> <span class=\"token comment\">//相对于坐标系原点的对齐方式</span>\n    transform<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Matrix4<span class=\"token punctuation\">.</span>skewY</span><span class=\"token punctuation\">(</span><span class=\"token number\">0.3</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//沿Y轴倾斜0.3弧度</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Container</span><span class=\"token punctuation\">(</span>\n      padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">8.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>deepOrange<span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Apartment for rent!'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>运行效果如图5-10所示：</p> <p><img src=\"/assets/img/5-10.62f0e5d6.png\" alt=\"图5-10\"></p> <blockquote><p>关于矩阵变换的相关内容属于线性代数范畴，本书不做讨论，读者有兴趣可以自行了解。本书中，我们把焦点放在Flutter中一些常见的变换效果上。另外，由于矩阵变化时发生在绘制时，而无需重新布局和构建等过程，所以性能很好。</p></blockquote> <h3 id=\"平移\"><a href=\"#平移\" class=\"header-anchor\">#</a> 平移</h3> <p><code>Transform.translate</code>接收一个<code>offset</code>参数，可以在绘制时沿<code>x</code>、<code>y</code>轴对子组件平移指定的距离。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">DecoratedBox</span><span class=\"token punctuation\">(</span>\n  decoration<span class=\"token punctuation\">:</span><span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token comment\">//默认原点为左上角，左移20像素，向上平移5像素  </span>\n  child<span class=\"token punctuation\">:</span> Transform<span class=\"token punctuation\">.</span><span class=\"token function\">translate</span><span class=\"token punctuation\">(</span>\n    offset<span class=\"token punctuation\">:</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token operator\">-</span><span class=\"token number\">20.0</span><span class=\"token punctuation\">,</span> <span class=\"token operator\">-</span><span class=\"token number\">5.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hello world&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>效果如图5-11所示：</p> <p><img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASQAAAA+CAYAAACCw2alAAAMJWlDQ1BJQ0MgUHJvZmlsZQAASImVVwdUk8kWnr8kISGhBUKREnoTpUiXGloEAamCjZAEEkoMgSBiBVlUYC2oWLCiqyKKrgWQxYZdWRTsdUFERVlFXWyovEkC6LqvnHfPmZnv3Ln3znennRkAVCM5YnEGqgZApihHEhXsz5yckMgkPQIo0AbKgATGcrjZYr/IyDAAZbj9u7y7CRBZe81OFuuf/f9V1Hn8bC4ASCTEybxsbibEhwHAXbhiSQ4AhF6oN52VI4aYCFkCTQkkCLGZDKcqsJsMJytwmNwmJooFcRIASlQOR5IKgIqMFzOXmwrjqJRBbC/iCUUQN0HszRVweBB/hnh0ZuZMiFWtILZK/i5O6t9iJo/E5HBSR7AiF7koBQizxRmc2f/ndPxvycyQDo9hCgtVIAmJkuUsm7f0maEyTIX4gig5PAJiDYivC3lyexl+IpCGxA7Zf+Bms+CcAQYAKJXHCQiFWB9iE2l6rN8Q9uZI5L7QHk3MF8TEK+KjIsnMqKH4aL4oIzxsKE6ZgM8exlX87MDoYZsUYRAbYriGaIMwhx0zFPNCrjAuHGIViO9np0eHDvk+zxewwkfGkkbJOMM1x0Bm9nAumFmKJChKYY+5CITs8CF9WI4gJkThi03ncuQcdCBO42dPDhvmw+MHBCr4YIV8UewQT6xcnOMfNWS/Q5wROWSPNfEzgmV6E4hbs3Ojh337cuBmU+SCgzTOhEjFuLimOCcyRsENZ4IwwAIBgAmksCSDmSANCFt763vBcE8Q4AAJSAV8YDekGfaIl/eIYB0N8sGfEPFB9oifv7yXD3Kh/suIVlHbgRR5b67cIx08gTgT18O9cU88DNa+sDjibrj7sB9TdXhUYiAxgBhCDCJazxAWSn6IywRcmEEGLBIQCls+zErGQTTM/VscwhNCG+ER4Qahg3AHxIHH0E74jwy/RROO6CaCDhg1aCi75O+zwy0ga2fcH/eC/CF3nIHrATt8HMzED/eBuTlD7bdZ+3fcpcOsyfZklKxN9iVb/WinYqPiPOIjy+17ngpeySOZsEZ6fhyN9V1uPNiG/miJLcEOYeexU9hFrAmrB0zsBNaAtWDHZHhkbzyW743h0aLkfNJhHOGwjX2NfY/95x/G5gyNL5GvP8jh5+XIDg5rpni2RJgqyGH6wduaz2SLuGNGMx3tHeAtKrv7FVfLW4b8TkcYl77pCuYAMP7Q4OBg0zddGLxZjhgAQHn1TWf1Hh5newAuFHClklyFDpdVBEABqvCk6AJDeHdZwYwcgQvwBL4gEEwAESAGJIDpcJ4FIBOyngXmggJQDErBCrAGbABbwHawG+wDB0E9aAKnwDlwGVwFN8A9uFe6wQvQB96BAQRBSAgNoSO6iBFijtgijogb4o0EImFIFJKAJCGpiAiRInORRUgpUo5sQLYh1civyFHkFHIRaUPuIJ1ID/IG+YRiKBXVRA1QC3Qs6ob6oaFoDDoNTUWz0Hy0CF2GrkOr0L1oHXoKvYzeQDvQF2g/BjBljIEZY3aYG8bCIrBELAWTYPOxEqwCq8JqsUa40tewDqwX+4gTcTrOxO3gfg3BY3EunoXPx8vwDfhuvA4/g1/DO/E+/CuBRtAn2BI8CGzCZEIqYRahmFBB2Ek4QjgLz1Q34R2RSGQQLYmu8KwmENOIc4hlxE3E/cSTxDZiF7GfRCLpkmxJXqQIEoeUQyomrSftJZ0gtZO6SR+UlJWMlByVgpQSlURKhUoVSnuUjiu1Kz1VGiCrkc3JHuQIMo88m7ycvIPcSL5C7iYPUNQplhQvSgwljVJAWUeppZyl3Ke8VVZWNlF2V56kLFReqLxO+YDyBeVO5Y9UDaoNlUWdSpVSl1F3UU9S71Df0mg0C5ovLZGWQ1tGq6adpj2kfVChq4xRYavwVBaoVKrUqbSrvFQlq5qr+qlOV81XrVA9pHpFtVeNrGahxlLjqM1Xq1Q7qnZLrV+dru6gHqGeqV6mvkf9ovozDZKGhUagBk+jSGO7xmmNLjpGN6Wz6Fz6IvoO+ll6tyZR01KTrZmmWaq5T7NVs09LQ2ucVpxWnlal1jGtDgbGsGCwGRmM5YyDjJuMT9oG2n7afO2l2rXa7drvdUbp+OrwdUp09uvc0Pmky9QN1E3XXalbr/tAD9ez0ZukN0tvs95Zvd5RmqM8R3FHlYw6OOquPqpvox+lP0d/u36Lfr+BoUGwgdhgvcFpg15DhqGvYZrhasPjhj1GdCNvI6HRaqMTRs+ZWkw/ZgZzHfMMs89Y3zjEWGq8zbjVeMDE0iTWpNBkv8kDU4qpm2mK6WrTZtM+MyOziWZzzWrM7pqTzd3MBeZrzc+bv7ewtIi3WGxRb/HMUseSbZlvWWN534pm5WOVZVVldd2aaO1mnW69yfqqDWrjbCOwqbS5YovautgKbTfZto0mjHYfLRpdNfqWHdXOzy7XrsaucwxjTNiYwjH1Y16ONRubOHbl2PNjv9o722fY77C/56DhMMGh0KHR4Y2jjSPXsdLxuhPNKchpgVOD0+txtuP44zaPu+1Md57ovNi52fmLi6uLxKXWpcfVzDXJdaPrLTdNt0i3MrcL7gR3f/cF7k3uHz1cPHI8Dnq88rTzTPfc4/lsvOV4/vgd47u8TLw4Xtu8OryZ3kneW707fIx9OD5VPo98TX15vjt9n/pZ+6X57fV76W/vL/E/4v+e5cGaxzoZgAUEB5QEtAZqBMYGbgh8GGQSlBpUE9QX7Bw8J/hkCCEkNGRlyC22AZvLrmb3TXCdMG/CmVBqaHTohtBHYTZhkrDGiejECRNXTbwfbh4uCq+PABHsiFURDyItI7Mif5tEnBQ5qXLSkyiHqLlR56Pp0TOi90S/i/GPWR5zL9YqVhrbHKcaNzWuOu59fEB8eXzH5LGT502+nKCXIExoSCQlxiXuTOyfEjhlzZTuqc5Ti6fenGY5LW/axel60zOmH5uhOoMz41ASISk+aU/SZ04Ep4rTn8xO3pjcx2Vx13Jf8Hx5q3k9fC9+Of9pildKecqzVK/UVak9Ah9BhaBXyBJuEL5OC0nbkvY+PSJ9V/pgRnzG/kylzKTMoyINUbrozEzDmXkz28S24mJxR5ZH1pqsPkmoZGc2kj0tuyFHEz6yW6RW0p+knbneuZW5H2bFzTqUp54nymuZbTN76eyn+UH5v8zB53DnNM81nlswt3Oe37xt85H5yfObF5guKFrQvTB44e4CSkF6we+F9oXlhX8til/UWGRQtLCo66fgn2qKVYolxbcWey7esgRfIlzSutRp6fqlX0t4JZdK7UsrSj+Xccsu/ezw87qfB5elLGtd7rJ88wriCtGKmyt9Vu4uVy/PL+9aNXFV3Wrm6pLVf62ZseZixbiKLWspa6VrO9aFrWtYb7Z+xfrPGwQbblT6V+7fqL9x6cb3m3ib2jf7bq7dYrCldMunrcKtt7cFb6ursqiq2E7cnrv9yY64Hed/cfuleqfeztKdX3aJdnXsjtp9ptq1unqP/p7lNWiNtKZn79S9V/cF7Guotavdtp+xv/QAOCA98PzXpF9vHgw92HzI7VDtYfPDG4/Qj5TUIXWz6/rqBfUdDQkNbUcnHG1u9Gw88tuY33Y1GTdVHtM6tvw45XjR8cET+Sf6T4pP9p5KPdXVPKP53unJp6+fmXSm9Wzo2Qvngs6dPu93/sQFrwtNFz0uHr3kdqn+ssvluhbnliO/O/9+pNWlte6K65WGq+5XG9vGtx1v92k/dS3g2rnr7OuXb4TfaLsZe/P2ram3Om7zbj+7k3Hn9d3cuwP3Ft4n3C95oPag4qH+w6o/rP/Y3+HScawzoLPlUfSje13crhePsx9/7i56QntS8dToafUzx2dNPUE9V59Ped79QvxioLf4T/U/N760enn4le+rlr7Jfd2vJa8H35S91X27669xfzX3R/Y/fJf5buB9yQfdD7s/un08/yn+09OBWZ9Jn9d9sf7S+DX06/3BzMFBMUfCkT8FMFjQlBQA3uwCgJYAAP0qfD9MUfzN5IIo/pNyBP4TVvzf5OICQC1sZM9w1kkADsBi6QtjwyJ7jsf4AtTJaaQMSXaKk6MiFhX+cAgfBgffwncMqRGAL5LBwYFNg4NfdkCydwA4maX4E8pE9gfdKo/RzshbCH6QfwEOk3CtyyHjHgAAAAlwSFlzAAAWJQAAFiUBSVIk8AAACjpJREFUeAHtnWtsFNcVx8961++3DTa2MVACmKRRiKooJE0LH6q2JLSSLbflS+1YNAUJTFOQGilSorYSbV2pURGUJqGqQoJALfSpfkgRxRWiUitIUmra2KkJNrbB68fi9XrtfdnennPXsx7bu9n11OvMrv8HmZmduffMvb878/e5Z+6Cxe/3BwkGAiAAAiYgkGaCNqAJIAACIKAIQJBwI4AACJiGAATJNEOBhoAACECQcA+AAAiYhgAEyTRDgYaAAAhAkHAPgAAImIYABMk0Q4GGgAAIQJBwD4AACJiGAATJNEOBhoAACECQcA+AAAiYhgAEyTRDgYaAAAhAkHAPgAAImIYABMk0Q4GGgAAIQJBwD4AACJiGAATJNEOBhoAACECQcA+AAAiYhgAEyTRDgYaAAAhAkHAPgAAImIYABMk0Q4GGgAAIQJBwD4AACJiGAATJNEOBhoAACECQcA+AAAiYhgAEyTRDEWpIb28vdXV1UTAY+t+pAoGA+uzxeEzW0sU1p7u7mwYGBmJWGhkZUf2dnp6OWRYFUo8ABMnAmPb391NHR0fUmlNTU9Te3k5utztqmWgnGhsbadeuXaQ9kIODg+rzjRs3olVJiuP19fV09OjRmG09d+6c6q/P54tZFgVSjwAEycCYnjx5kurq6qLWHB8fp9raWrp27VrUMjgBAiCwkIBt4SEcMQOBoGuUghYLyZbDJSL3GAWdI2ZomrE2TE8R+X0x+xD0TIT6O+qkoM9r7FoGa1nS+PdzQaHB2qi2FAQgSEtBcRE+vF4vDQ8Pk0zrCgsLqaioKGLtyeefo+mhQQr4/ESDdpr87gsUKMybU9Y1OUXOyUlKIwsV2qyUzz/R7H5gkkb4Z312JtlY6DS7y/4z+PPqjHTtEPlYAPu8flqTmUG51tkg2s1tdgamKMh/Cmw2dc1wJd4Zn5omO/tbm5XBPoIk1yxjvzni4043TY8OU6DnfX0VGuU+jHIf0i1ptCrDRtN9g6q//sZ6suquPadSgj5Yyiso/fSFBHmH23gIQJDiobREZTo7O8NTPRs/0JKo3rdvHx0+fJjS5LdznCbp7pM9dnq9b4AyuZ4IhJ8F4DsbKunrFasoTSc4msv/uD10oP02/eqRzfTJvBx1WPw03LzF4mKl3z9aw7IWsreHnfTyrV51bFNOFnsn+rXdQT+43UcZaRYuZ1Gi1VS5mp5fXxEWuH+MjtG3O7rpQPUa+nmvXTl7/aGN9Omi/BnPsxvxebZ/iH7cdY/bS0pURbw+W7yw7Gwt7KU6AQjSMo2w0+lUeaUdO3bQiRMnSATp9OnT1NLSQlu3bqXdu3fH3ZJXuu/Rm/eGqJkf/G+uLVNC0sLH5LgI1J41pQt8PVmUR1l87h3XeFiQ2sbGacgfUD93OSKSyEbsyoiLox8bbczOUp81MfpaeSm9uLGKrCx4v7w7SD9jUeSJGL3AQqg3OfetdRX0GRaiiqzZyEtf5m98DRGjJzjq+2nNBo6irEqgXrnTry+G/RVGAIJkcMDlLVhNTU3ctffs2aPKamIkH5qamujMmTN05MgR9WbJyg9lLJvi5QC/GbhPT68qov3V5eHiL36iitrHPXSUo5hn+Nz86ZtM07ZwtNN630XPcmQjdoH9SPTSOeGl6y43C1KJOn6LP+9iHxK5cOBFv+BI7HEWjpcfWKvOy1/PVZVRr9dHZ1gYxV+5bsr3EotWbVnIV7jCvJ03uJ5ME1/lCEqbQjawnyGe5r3BggZbmQQgSAbH3cIPuLyej2STnBO5dOnSnFN2u12Vl63edu7cSWfPnlXTt7y8uTkifTlt/z2OasY5l9M4IyracdnurSyjQ64u6uM8zoO2bP0ptf8UT4de7R0gyT3JNO0m+3qWhSWLRerPPE2rYxGRiKnb41PTP6nUOeGhQT52QCd+muP6slL6HYvauxx1iQhqtj4rU9uNuPWymF8fddMXS4vCYqQVzFnE1FWrg23qEIAgGRxLEaRjx45FrO1yuRYIkiSxL168SK2trQvq5OTkhNcdLTg574BMrcRkSjXfqmemXJJMjmRfWl1Mr7EgdbHgrOYE8m3ePl6QR6XpNmpu71JJ6L873WoK+PBMnmnYH/JVmRmazun9aonwPo6UFmOS/BYTUYSBgJ7Awrtafxb7S0ZABGz//v3U3Nz8f/nUHuLJmZXcemdjMw96RpQoYx1HLpUsWjI9m+S5mAiY5I0qMkNvwv41NkF/vT9KnyrIpRIWKbEcWyjZ7otwvYmZ62XHMdXUtzOdWYhF6oO+HPZXHoH4X+2sPDZL2mNJYssqZO0rIUadi1jI4/wui8p8e3s4tE6pigUmmm3iRHWrY5SuclK5jqdcYpKk3pafS5ccTvrQ46WdJQXh6ps57yTnZYo13667xtShR2aiqfnno30WUa3iiOtOhMgKXxiJRm1lHIcgLdM47927lxwOB12+fDksSpIYP3/+PB0/fjzuVhSxsG3Jzabvf9hHds7taDbE33n74+AIPcFJ6kjTK62cJJtvuieojX/0r9if5nzOn4b4e2Q8jXuycPbVex5HP58rKaS3OAn9X052aybrn07xmqEHuS3b8kPLCLRz8Wxry0voPc49/YXFUZYAiMlU87x9OPQBf69IApiyLdOwHzp0iK5evUoHDx6k/Px82r59O125ckWJk7x5W4y99fAm+vI/O+jz77xPj3JkI1Off7PArEpPp59sWf+RrnYUF/AiRF5JxGHWAxz9aPYFTkp/73avWqAoa4/09sPN1Spyqr/xgRLDPF6wKGIi4nhs6wZ90bj3v8HJdBGjwx90q1ySCJ8IbDVHTo4oObC4naNg0hKAIBkYOklCFxcXR60pixxLSkoonQVCbxINtbW10alTp6inp4caGhpUTik3NzdcTFZuy2puzaw8QSvmfI6Wd5HjsvL58mMPqYhGlgBYWJB+tHkdPcNJ61ghryxsfIxf4dfkZM/xKSuyn+LoqprzTNpreK0NsrbpD7xwUvJL5/odanX4SxvX0ld4XZI1lA5SRWXFt7TVJusF5llRupVEdDST/lzYtoV+O+DgqMhB63gF+Wu8rkqWLrR03VWCqZXFduUQsPj9fi1iXjm9ToKeBpq+SsEBLBJczqHCV0eWk3bka8X6hRq5Fo6CAAiAQAIIQJASABUuQQAEjBGAIBnjhlogAAIJIABBSgBUuAQBEDBGAIJkjBtqgQAIJIAABCkBUOESBEDAGAEIkjFuqAUCIJAAAhCkBECFSxAAAWMEIEjGuKEWCIBAAghgpXYCoC6Jywj/3MeS+IWTjyYgX/KDfWwE8F22jw19jAvjwYgBCKdTkQCmbKk4qugTCCQpAQhSkg4cmg0CqUgAgpSKo4o+gUCSEoAgJenAodkgkIoEIEipOKroEwgkKQEIUpIOHJoNAqlIAIKUiqOKPoFAkhKAICXpwKHZIJCKBCBIqTiq6BMIJCkBCFKSDhyaDQKpSACClIqjij6BQJISgCAl6cCh2SCQigT+B2Ta4w/7JqzpAAAAAElFTkSuQmCC\" alt=\"图5-11\"></p> <h3 id=\"旋转\"><a href=\"#旋转\" class=\"header-anchor\">#</a> 旋转</h3> <p><code>Transform.rotate</code>可以对子组件进行旋转变换，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">DecoratedBox</span><span class=\"token punctuation\">(</span>\n  decoration<span class=\"token punctuation\">:</span><span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> Transform<span class=\"token punctuation\">.</span><span class=\"token function\">rotate</span><span class=\"token punctuation\">(</span>\n    <span class=\"token comment\">//旋转90度</span>\n    angle<span class=\"token punctuation\">:</span>math<span class=\"token punctuation\">.</span>pi<span class=\"token operator\">/</span><span class=\"token number\">2</span> <span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hello world&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>；\n</code></pre></div><blockquote><p>注意：要使用<code>math.pi</code>需先进行如下导包。</p></blockquote> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'dart:math'</span> <span class=\"token operator\">as</span> math<span class=\"token punctuation\">;</span>  \n</code></pre></div><p>效果如图5-12所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图5-12\"></p> <h3 id=\"缩放\"><a href=\"#缩放\" class=\"header-anchor\">#</a> 缩放</h3> <p><code>Transform.scale</code>可以对子组件进行缩小或放大，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">DecoratedBox</span><span class=\"token punctuation\">(</span>\n  decoration<span class=\"token punctuation\">:</span><span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> Transform<span class=\"token punctuation\">.</span><span class=\"token function\">scale</span><span class=\"token punctuation\">(</span>\n      scale<span class=\"token punctuation\">:</span> <span class=\"token number\">1.5</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//放大到1.5倍</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hello world&quot;</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>效果如图5-13所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图5-13\"></p> <h3 id=\"注意\"><a href=\"#注意\" class=\"header-anchor\">#</a> 注意</h3> <ul><li><p><code>Transform</code>的变换是应用在绘制阶段，而并不是应用在布局(layout)阶段，所以无论对子组件应用何种变化，其占用空间的大小和在屏幕上的位置都是固定不变的，因为这些是在布局阶段就确定的。下面我们具体说明：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code> <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n  mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token function\">DecoratedBox</span><span class=\"token punctuation\">(</span>\n      decoration<span class=\"token punctuation\">:</span><span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> Transform<span class=\"token punctuation\">.</span><span class=\"token function\">scale</span><span class=\"token punctuation\">(</span>scale<span class=\"token punctuation\">:</span> <span class=\"token number\">1.5</span><span class=\"token punctuation\">,</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hello world&quot;</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;你好&quot;</span><span class=\"token punctuation\">,</span> style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>green<span class=\"token punctuation\">,</span> fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">18.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行效果如图5-14所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图5-14\"></p> <p>由于第一个<code>Text</code>应用变换(放大)后，其在绘制时会放大，但其占用的空间依然为红色部分，所以第二个<code>Text</code>会紧挨着红色部分，最终就会出现文字重合。</p></li> <li><p>由于矩阵变化只会作用在绘制阶段，所以在某些场景下，在UI需要变化时，可以直接通过矩阵变化来达到视觉上的UI改变，而不需要去重新触发build流程，这样会节省layout的开销，所以性能会比较好。如之前介绍的<code>Flow</code>组件，它内部就是用矩阵变换来更新UI，除此之外，Flutter的动画组件中也大量使用了<code>Transform</code>以提高性能。</p></li></ul> <blockquote><p>思考题：使用<code>Transform</code>对其子组件先进行平移然后再旋转和先旋转再平移，两者最终的效果一样吗？为什么？</p></blockquote> <h3 id=\"rotatedbox\"><a href=\"#rotatedbox\" class=\"header-anchor\">#</a> RotatedBox</h3> <p><code>RotatedBox</code>和<code>Transform.rotate</code>功能相似，它们都可以对子组件进行旋转变换，但是有一点不同：<code>RotatedBox</code>的变换是在layout阶段，会影响在子组件的位置和大小。我们将上面介绍<code>Transform.rotate</code>时的示例改一下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n  mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token function\">DecoratedBox</span><span class=\"token punctuation\">(</span>\n      decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token comment\">//将Transform.rotate换成RotatedBox  </span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">RotatedBox</span><span class=\"token punctuation\">(</span>\n        quarterTurns<span class=\"token punctuation\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//旋转90度(1/4圈)</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hello world&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;你好&quot;</span><span class=\"token punctuation\">,</span> style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>green<span class=\"token punctuation\">,</span> fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">18.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</code></pre></div><p>效果如图5-15所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图5-15\"></p> <p>由于<code>RotatedBox</code>是作用于layout阶段，所以子组件会旋转90度（而不只是绘制的内容），<code>decoration</code>会作用到子组件所占用的实际空间上，所以最终就是上图的效果，读者可以和前面<code>Transform.rotate</code>示例对比理解。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/20.6c5c8986.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter6/custom_scrollview.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>6.5 CustomScrollView | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/76.29cac87f.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter6/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_6-5-customscrollview\"><a href=\"#_6-5-customscrollview\" class=\"header-anchor\">#</a> 6.5 CustomScrollView</h1> <p><code>CustomScrollView</code>是可以使用Sliver来自定义滚动模型（效果）的组件。它可以包含多种滚动模型，举个例子，假设有一个页面，顶部需要一个<code>GridView</code>，底部需要一个<code>ListView</code>，而要求整个页面的滑动效果是统一的，即它们看起来是一个整体。如果使用<code>GridView</code>+<code>ListView</code>来实现的话，就不能保证一致的滑动效果，因为它们的滚动效果是分离的，所以这时就需要一个&quot;胶水&quot;，把这些彼此独立的可滚动组件&quot;粘&quot;起来，而<code>CustomScrollView</code>的功能就相当于“胶水”。</p> <h3 id=\"可滚动组件的sliver版\"><a href=\"#可滚动组件的sliver版\" class=\"header-anchor\">#</a> 可滚动组件的Sliver版</h3> <p>Sliver在前面讲过，有细片、薄片之意，在Flutter中，Sliver通常指可滚动组件子元素（就像一个个薄片一样）。但是在<code>CustomScrollView</code>中，需要粘起来的可滚动组件就是<code>CustomScrollView</code>的Sliver了，如果直接将<code>ListView</code>、<code>GridView</code>作为<code>CustomScrollView</code>是不行的，因为它们本身是可滚动组件而并不是Sliver！因此，为了能让可滚动组件能和<code>CustomScrollView</code>配合使用，Flutter提供了一些可滚动组件的Sliver版，如SliverList、SliverGrid等。实际上Sliver版的可滚动组件和非Sliver版的可滚动组件最大的区别就是<strong>前者不包含滚动模型（自身不能再滚动），而后者包含滚动模型</strong> ，也正因如此，<code>CustomScrollView</code>才可以将多个Sliver&quot;粘&quot;在一起，这些Sliver共用<code>CustomScrollView</code>的<code>Scrollable</code>，所以最终才实现了统一的滑动效果。</p> <blockquote><p>Sliver系列Widget比较多，我们不会一一介绍，读者只需记住它的特点，需要时再去查看文档即可。上面之所以说“大多数”Sliver都和可滚动组件对应，是由于还有一些如SliverPadding、SliverAppBar等是和可滚动组件无关的，它们主要是为了结合CustomScrollView一起使用，这是因为<strong>CustomScrollView的子组件必须都是Sliver</strong>。</p></blockquote> <h3 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h3> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">CustomScrollViewTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//因为本路由没有使用Scaffold，为了让子级Widget(如Text)使用</span>\n    <span class=\"token comment\">//Material Design 默认的样式风格,我们使用Material作为本路由的根。</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Material</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">CustomScrollView</span><span class=\"token punctuation\">(</span>\n        slivers<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n          <span class=\"token comment\">//AppBar，包含一个导航栏</span>\n          <span class=\"token function\">SliverAppBar</span><span class=\"token punctuation\">(</span>\n            pinned<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n            expandedHeight<span class=\"token punctuation\">:</span> <span class=\"token number\">250.0</span><span class=\"token punctuation\">,</span>\n            flexibleSpace<span class=\"token punctuation\">:</span> <span class=\"token function\">FlexibleSpaceBar</span><span class=\"token punctuation\">(</span>\n              title<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Demo'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              background<span class=\"token punctuation\">:</span> Image<span class=\"token punctuation\">.</span><span class=\"token function\">asset</span><span class=\"token punctuation\">(</span>\n                <span class=\"token string\">&quot;./images/avatar.png&quot;</span><span class=\"token punctuation\">,</span> fit<span class=\"token punctuation\">:</span> BoxFit<span class=\"token punctuation\">.</span>cover<span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n\n          <span class=\"token function\">SliverPadding</span><span class=\"token punctuation\">(</span>\n            padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">8.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            sliver<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">SliverGrid</span><span class=\"token punctuation\">(</span> <span class=\"token comment\">//Grid</span>\n              gridDelegate<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">SliverGridDelegateWithFixedCrossAxisCount</span><span class=\"token punctuation\">(</span>\n                crossAxisCount<span class=\"token punctuation\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//Grid按两列显示</span>\n                mainAxisSpacing<span class=\"token punctuation\">:</span> <span class=\"token number\">10.0</span><span class=\"token punctuation\">,</span>\n                crossAxisSpacing<span class=\"token punctuation\">:</span> <span class=\"token number\">10.0</span><span class=\"token punctuation\">,</span>\n                childAspectRatio<span class=\"token punctuation\">:</span> <span class=\"token number\">4.0</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              delegate<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">SliverChildBuilderDelegate</span><span class=\"token punctuation\">(</span>\n                    <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> int index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                  <span class=\"token comment\">//创建子widget      </span>\n                  <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Container</span><span class=\"token punctuation\">(</span>\n                    alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n                    color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>cyan<span class=\"token punctuation\">[</span><span class=\"token number\">100</span> <span class=\"token operator\">*</span> <span class=\"token punctuation\">(</span>index <span class=\"token operator\">%</span> <span class=\"token number\">9</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                    child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'grid item $index'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n                childCount<span class=\"token punctuation\">:</span> <span class=\"token number\">20</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token comment\">//List</span>\n          <span class=\"token keyword\">new</span> <span class=\"token class-name\">SliverFixedExtentList</span><span class=\"token punctuation\">(</span>\n            itemExtent<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n            delegate<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">SliverChildBuilderDelegate</span><span class=\"token punctuation\">(</span>\n                    <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> int index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                  <span class=\"token comment\">//创建列表项      </span>\n                  <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Container</span><span class=\"token punctuation\">(</span>\n                    alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n                    color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>lightBlue<span class=\"token punctuation\">[</span><span class=\"token number\">100</span> <span class=\"token operator\">*</span> <span class=\"token punctuation\">(</span>index <span class=\"token operator\">%</span> <span class=\"token number\">9</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                    child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'list item $index'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n                childCount<span class=\"token punctuation\">:</span> <span class=\"token number\">50</span> <span class=\"token comment\">//50个列表项</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>代码分为三部分：</p> <ul><li>头部<code>SliverAppBar</code>：<code>SliverAppBar</code>对应<code>AppBar</code>，两者不同之处在于<code>SliverAppBar</code>可以集成到<code>CustomScrollView</code>。<code>SliverAppBar</code>可以结合<code>FlexibleSpaceBar</code>实现Material Design中头部伸缩的模型，具体效果，读者可以运行该示例查看。</li> <li>中间的<code>SliverGrid</code>：它用<code>SliverPadding</code>包裹以给<code>SliverGrid</code>添加补白。<code>SliverGrid</code>是一个两列，宽高比为4的网格，它有20个子组件。</li> <li>底部<code>SliverFixedExtentList</code>：它是一个所有子元素高度都为50像素的列表。</li></ul> <p>运行效果如图：</p> <p><img src=\"/assets/img/6-12.d5a5a078.png\" alt=\"图6-12\"><img src=\"/assets/img/6-13.7ffa2d43.png\" alt=\"图6-13\"></p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/76.29cac87f.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter6/gridview.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>6.4 GridView | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/54.4afb8008.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter6/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_6-4-gridview\"><a href=\"#_6-4-gridview\" class=\"header-anchor\">#</a> 6.4 GridView</h1> <p><code>GridView</code>可以构建一个二维网格列表，其默认构造函数定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">GridView</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  Axis scrollDirection <span class=\"token operator\">=</span> Axis<span class=\"token punctuation\">.</span>vertical<span class=\"token punctuation\">,</span>\n  bool reverse <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n  ScrollController controller<span class=\"token punctuation\">,</span>\n  bool primary<span class=\"token punctuation\">,</span>\n  ScrollPhysics physics<span class=\"token punctuation\">,</span>\n  bool shrinkWrap <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n  EdgeInsetsGeometry padding<span class=\"token punctuation\">,</span>\n  <span class=\"token metadata symbol\">@required</span> SliverGridDelegate gridDelegate<span class=\"token punctuation\">,</span> <span class=\"token comment\">//控制子widget layout的委托</span>\n  bool addAutomaticKeepAlives <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n  bool addRepaintBoundaries <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n  double cacheExtent<span class=\"token punctuation\">,</span>\n  List<span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span> children <span class=\"token operator\">=</span> <span class=\"token keyword\">const</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>我们可以看到，<code>GridView</code>和<code>ListView</code>的大多数参数都是相同的，它们的含义也都相同的，如有疑惑读者可以翻阅ListView一节，在此不再赘述。我们唯一需要关注的是<code>gridDelegate</code>参数，类型是<code>SliverGridDelegate</code>，它的作用是控制<code>GridView</code>子组件如何排列(layout)。</p> <p><code>SliverGridDelegate</code>是一个抽象类，定义了<code>GridView</code> Layout相关接口，子类需要通过实现它们来实现具体的布局算法。Flutter中提供了两个<code>SliverGridDelegate</code>的子类<code>SliverGridDelegateWithFixedCrossAxisCount</code>和<code>SliverGridDelegateWithMaxCrossAxisExtent</code>，我们可以直接使用，下面我们分别来介绍一下它们。</p> <h3 id=\"slivergriddelegatewithfixedcrossaxiscount\"><a href=\"#slivergriddelegatewithfixedcrossaxiscount\" class=\"header-anchor\">#</a> SliverGridDelegateWithFixedCrossAxisCount</h3> <p>该子类实现了一个横轴为固定数量子元素的layout算法，其构造函数为：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">SliverGridDelegateWithFixedCrossAxisCount</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@required</span> double crossAxisCount<span class=\"token punctuation\">,</span> \n  double mainAxisSpacing <span class=\"token operator\">=</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span>\n  double crossAxisSpacing <span class=\"token operator\">=</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span>\n  double childAspectRatio <span class=\"token operator\">=</span> <span class=\"token number\">1.0</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><ul><li><code>crossAxisCount</code>：横轴子元素的数量。此属性值确定后子元素在横轴的长度就确定了，即ViewPort横轴长度除以<code>crossAxisCount</code>的商。</li> <li><code>mainAxisSpacing</code>：主轴方向的间距。</li> <li><code>crossAxisSpacing</code>：横轴方向子元素的间距。</li> <li><code>childAspectRatio</code>：子元素在横轴长度和主轴长度的比例。由于<code>crossAxisCount</code>指定后，子元素横轴长度就确定了，然后通过此参数值就可以确定子元素在主轴的长度。</li></ul> <p>可以发现，子元素的大小是通过<code>crossAxisCount</code>和<code>childAspectRatio</code>两个参数共同决定的。注意，这里的子元素指的是子组件的最大显示空间，注意确保子组件的实际大小不要超出子元素的空间。</p> <p>下面看一个例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">GridView</span><span class=\"token punctuation\">(</span>\n  gridDelegate<span class=\"token punctuation\">:</span> <span class=\"token function\">SliverGridDelegateWithFixedCrossAxisCount</span><span class=\"token punctuation\">(</span>\n      crossAxisCount<span class=\"token punctuation\">:</span> <span class=\"token number\">3</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//横轴三个子widget</span>\n      childAspectRatio<span class=\"token punctuation\">:</span> <span class=\"token number\">1.0</span> <span class=\"token comment\">//宽高比为1时，子widget</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  children<span class=\"token punctuation\">:</span><span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>ac_unit<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>airport_shuttle<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>all_inclusive<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>beach_access<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>cake<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>free_breakfast<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p><img src=\"/assets/img/6-9.865c35f8.png\" alt=\"图6-9\"></p> <h4 id=\"gridview-count\"><a href=\"#gridview-count\" class=\"header-anchor\">#</a> GridView.count</h4> <p><code>GridView.count</code>构造函数内部使用了<code>SliverGridDelegateWithFixedCrossAxisCount</code>，我们通过它可以快速的创建横轴固定数量子元素的<code>GridView</code>，上面的示例代码等价于：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>GridView<span class=\"token punctuation\">.</span><span class=\"token function\">count</span><span class=\"token punctuation\">(</span> \n  crossAxisCount<span class=\"token punctuation\">:</span> <span class=\"token number\">3</span><span class=\"token punctuation\">,</span>\n  childAspectRatio<span class=\"token punctuation\">:</span> <span class=\"token number\">1.0</span><span class=\"token punctuation\">,</span>\n  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>ac_unit<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>airport_shuttle<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>all_inclusive<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>beach_access<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>cake<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>free_breakfast<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><h3 id=\"slivergriddelegatewithmaxcrossaxisextent\"><a href=\"#slivergriddelegatewithmaxcrossaxisextent\" class=\"header-anchor\">#</a> SliverGridDelegateWithMaxCrossAxisExtent</h3> <p>该子类实现了一个横轴子元素为固定最大长度的layout算法，其构造函数为：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">SliverGridDelegateWithMaxCrossAxisExtent</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  double maxCrossAxisExtent<span class=\"token punctuation\">,</span>\n  double mainAxisSpacing <span class=\"token operator\">=</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span>\n  double crossAxisSpacing <span class=\"token operator\">=</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span>\n  double childAspectRatio <span class=\"token operator\">=</span> <span class=\"token number\">1.0</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p><code>maxCrossAxisExtent</code>为子元素在横轴上的最大长度，之所以是“最大”长度，是<strong>因为横轴方向每个子元素的长度仍然是等分的</strong>，举个例子，如果ViewPort的横轴长度是450，那么当<code>maxCrossAxisExtent</code>的值在区间[450/4，450/3)内的话，子元素最终实际长度都为112.5，而<code>childAspectRatio</code>所指的子元素横轴和主轴的长度比为<strong>最终的长度比</strong>。其它参数和<code>SliverGridDelegateWithFixedCrossAxisCount</code>相同。</p> <p>下面我们看一个例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">GridView</span><span class=\"token punctuation\">(</span>\n  padding<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span>zero<span class=\"token punctuation\">,</span>\n  gridDelegate<span class=\"token punctuation\">:</span> <span class=\"token function\">SliverGridDelegateWithMaxCrossAxisExtent</span><span class=\"token punctuation\">(</span>\n      maxCrossAxisExtent<span class=\"token punctuation\">:</span> <span class=\"token number\">120.0</span><span class=\"token punctuation\">,</span>\n      childAspectRatio<span class=\"token punctuation\">:</span> <span class=\"token number\">2.0</span> <span class=\"token comment\">//宽高比为2</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>ac_unit<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>airport_shuttle<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>all_inclusive<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>beach_access<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>cake<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>free_breakfast<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p><img src=\"/assets/img/6-10.202cef73.png\" alt=\"图6-10\"></p> <h4 id=\"gridview-extent\"><a href=\"#gridview-extent\" class=\"header-anchor\">#</a> GridView.extent</h4> <p>GridView.extent构造函数内部使用了SliverGridDelegateWithMaxCrossAxisExtent，我们通过它可以快速的创建纵轴子元素为固定最大长度的的GridView，上面的示例代码等价于：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>GridView<span class=\"token punctuation\">.</span><span class=\"token function\">extent</span><span class=\"token punctuation\">(</span>\n   maxCrossAxisExtent<span class=\"token punctuation\">:</span> <span class=\"token number\">120.0</span><span class=\"token punctuation\">,</span>\n   childAspectRatio<span class=\"token punctuation\">:</span> <span class=\"token number\">2.0</span><span class=\"token punctuation\">,</span>\n   children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n     <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>ac_unit<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n     <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>airport_shuttle<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n     <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>all_inclusive<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n     <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>beach_access<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n     <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>cake<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n     <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>free_breakfast<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n   <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><h3 id=\"gridview-builder\"><a href=\"#gridview-builder\" class=\"header-anchor\">#</a> GridView.builder</h3> <p>上面我们介绍的GridView都需要一个widget数组作为其子元素，这些方式都会提前将所有子widget都构建好，所以只适用于子widget数量比较少时，当子widget比较多时，我们可以通过<code>GridView.builder</code>来动态创建子widget。<code>GridView.builder</code> 必须指定的参数有两个：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>GridView<span class=\"token punctuation\">.</span><span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>\n <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n <span class=\"token metadata symbol\">@required</span> SliverGridDelegate gridDelegate<span class=\"token punctuation\">,</span> \n <span class=\"token metadata symbol\">@required</span> IndexedWidgetBuilder itemBuilder<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>其中<code>itemBuilder</code>为子widget构建器。</p> <h4 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h4> <p>假设我们需要从一个异步数据源（如网络）分批获取一些<code>Icon</code>，然后用<code>GridView</code>来展示：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">InfiniteGridView</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _InfiniteGridViewState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_InfiniteGridViewState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_InfiniteGridViewState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>InfiniteGridView<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n\n  List<span class=\"token operator\">&lt;</span>IconData<span class=\"token operator\">&gt;</span> _icons <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//保存Icon数据</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 初始化数据  </span>\n    <span class=\"token function\">_retrieveIcons</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> GridView<span class=\"token punctuation\">.</span><span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>\n        gridDelegate<span class=\"token punctuation\">:</span> <span class=\"token function\">SliverGridDelegateWithFixedCrossAxisCount</span><span class=\"token punctuation\">(</span>\n            crossAxisCount<span class=\"token punctuation\">:</span> <span class=\"token number\">3</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//每行三列</span>\n            childAspectRatio<span class=\"token punctuation\">:</span> <span class=\"token number\">1.0</span> <span class=\"token comment\">//显示区域宽高相等</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        itemCount<span class=\"token punctuation\">:</span> _icons<span class=\"token punctuation\">.</span>length<span class=\"token punctuation\">,</span>\n        itemBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token comment\">//如果显示到最后一个并且Icon总数小于200时继续获取数据</span>\n          <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>index <span class=\"token operator\">==</span> _icons<span class=\"token punctuation\">.</span>length <span class=\"token operator\">-</span> <span class=\"token number\">1</span> <span class=\"token operator\">&amp;&amp;</span> _icons<span class=\"token punctuation\">.</span>length <span class=\"token operator\">&lt;</span> <span class=\"token number\">200</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token function\">_retrieveIcons</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span>\n          <span class=\"token keyword\">return</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>_icons<span class=\"token punctuation\">[</span>index<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">//模拟异步获取数据</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_retrieveIcons</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    Future<span class=\"token punctuation\">.</span><span class=\"token function\">delayed</span><span class=\"token punctuation\">(</span><span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>milliseconds<span class=\"token punctuation\">:</span> <span class=\"token number\">200</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        _icons<span class=\"token punctuation\">.</span><span class=\"token function\">addAll</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span>\n          Icons<span class=\"token punctuation\">.</span>ac_unit<span class=\"token punctuation\">,</span>\n          Icons<span class=\"token punctuation\">.</span>airport_shuttle<span class=\"token punctuation\">,</span>\n          Icons<span class=\"token punctuation\">.</span>all_inclusive<span class=\"token punctuation\">,</span>\n          Icons<span class=\"token punctuation\">.</span>beach_access<span class=\"token punctuation\">,</span> Icons<span class=\"token punctuation\">.</span>cake<span class=\"token punctuation\">,</span>\n          Icons<span class=\"token punctuation\">.</span>free_breakfast\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><ul><li><code>_retrieveIcons()</code>：在此方法中我们通过<code>Future.delayed</code>来模拟从异步数据源获取数据，每次获取数据需要200毫秒，获取成功后将新数据添加到_icons，然后调用setState重新构建。</li> <li>在itemBuilder中，如果显示到最后一个时，判断是否需要继续获取数据，然后返回一个Icon。</li></ul> <h3 id=\"更多\"><a href=\"#更多\" class=\"header-anchor\">#</a> 更多</h3> <p>Flutter的<code>GridView</code>默认子元素显示空间是相等的，但在实际开发中，你可能会遇到子元素大小不等的情况，如下面这样的布局：</p> <p><img src=\"/assets/img/6-11.a491f457.png\" alt=\"图6-11\"></p> <p>Pub上有一个包“flutter_staggered_grid_view” ，它实现了一个交错GridView的布局模型，可以很轻松的实现这种布局，详情读者可以自行了解。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/54.4afb8008.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter6/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>本章目录 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/212.f9dee082.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter6/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"本章目录\"><a href=\"#本章目录\" class=\"header-anchor\">#</a> 本章目录</h1> <ul><li><a href=\"/v2/chapter6/intro.html\">6.1：可滚动组件简介</a></li> <li><a href=\"/v2/chapter6/single_child_scrollview.html\">6.2：SingleChildScrollView</a></li> <li><a href=\"/v2/chapter6/listview.html\">6.3：ListView</a></li> <li><a href=\"/v2/chapter6/gridview.html\">6.4：GridView</a></li> <li><a href=\"/v2/chapter6/custom_scrollview.html\">6.5：CustomScrollView</a></li> <li><a href=\"/v2/chapter6/scroll_controller.html\">6.6：滚动监听及控制（ScrollController）</a></li></ul></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/212.f9dee082.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter6/intro.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>6.1 可滚动组件简介 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/213.f55d441e.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter6/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_6-1-可滚动组件简介\"><a href=\"#_6-1-可滚动组件简介\" class=\"header-anchor\">#</a> 6.1 可滚动组件简介</h1> <p>当组件内容超过当前显示视口(ViewPort)时，如果没有特殊处理，Flutter则会提示Overflow错误。为此，Flutter提供了多种可滚动组件（Scrollable Widget）用于显示列表和长布局。在本章中，我们先介绍一下常用的可滚动组件（如<code>ListView</code>、<code>GridView</code>等），然后介绍一下<code>ScrollController</code>。可滚动组件都直接或间接包含一个<code>Scrollable</code>组件，因此它们包括一些共同的属性，为了避免重复介绍，我们在此统一介绍一下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Scrollable</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>axisDirection <span class=\"token operator\">=</span> AxisDirection<span class=\"token punctuation\">.</span>down<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>controller<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>physics<span class=\"token punctuation\">,</span>\n  <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>viewportBuilder<span class=\"token punctuation\">,</span> <span class=\"token comment\">//后面介绍</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><ul><li><code>axisDirection</code>滚动方向。</li> <li><code>physics</code>：此属性接受一个<code>ScrollPhysics</code>类型的对象，它决定可滚动组件如何响应用户操作，比如用户滑动完抬起手指后，继续执行动画；或者滑动到边界时，如何显示。默认情况下，Flutter会根据具体平台分别使用不同的<code>ScrollPhysics</code>对象，应用不同的显示效果，如当滑动到边界时，继续拖动的话，在iOS上会出现弹性效果，而在Android上会出现微光效果。如果你想在所有平台下使用同一种效果，可以显式指定一个固定的<code>ScrollPhysics</code>，Flutter SDK中包含了两个<code>ScrollPhysics</code>的子类，他们可以直接使用：\n<ul><li><code>ClampingScrollPhysics</code>：Android下微光效果。</li> <li><code>BouncingScrollPhysics</code>：iOS下弹性效果。</li></ul></li> <li><code>controller</code>：此属性接受一个<code>ScrollController</code>对象。<code>ScrollController</code>的主要作用是控制滚动位置和监听滚动事件。默认情况下，Widget树中会有一个默认的<code>PrimaryScrollController</code>，如果子树中的可滚动组件没有显式的指定<code>controller</code>，并且<code>primary</code>属性值为<code>true</code>时（默认就为<code>true</code>），可滚动组件会使用这个默认的<code>PrimaryScrollController</code>。这种机制带来的好处是父组件可以控制子树中可滚动组件的滚动行为，例如，<code>Scaffold</code>正是使用这种机制在iOS中实现了点击导航栏回到顶部的功能。我们将在本章后面“滚动控制”一节详细介绍<code>ScrollController</code>。</li></ul> <h3 id=\"scrollbar\"><a href=\"#scrollbar\" class=\"header-anchor\">#</a> Scrollbar</h3> <p><code>Scrollbar</code>是一个Material风格的滚动指示器（滚动条），如果要给可滚动组件添加滚动条，只需将<code>Scrollbar</code>作为可滚动组件的任意一个父级组件即可，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Scrollbar</span><span class=\"token punctuation\">(</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">SingleChildScrollView</span><span class=\"token punctuation\">(</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p><code>Scrollbar</code>和<code>CupertinoScrollbar</code>都是通过监听滚动通知来确定滚动条位置的。关于的滚动通知的详细内容我们将在本章最后一节中专门介绍。</p> <h4 id=\"cupertinoscrollbar\"><a href=\"#cupertinoscrollbar\" class=\"header-anchor\">#</a> CupertinoScrollbar</h4> <p><code>CupertinoScrollbar</code>是iOS风格的滚动条，如果你使用的是<code>Scrollbar</code>，那么在iOS平台它会自动切换为<code>CupertinoScrollbar</code>。</p> <h3 id=\"viewport视口\"><a href=\"#viewport视口\" class=\"header-anchor\">#</a> ViewPort视口</h3> <p>在很多布局系统中都有ViewPort的概念，在Flutter中，术语ViewPort（视口），如无特别说明，则是指一个Widget的实际显示区域。例如，一个<code>ListView</code>的显示区域高度是800像素，虽然其列表项总高度可能远远超过800像素，但是其ViewPort仍然是800像素。</p> <h3 id=\"基于sliver的延迟构建\"><a href=\"#基于sliver的延迟构建\" class=\"header-anchor\">#</a> 基于Sliver的延迟构建</h3> <p>通常可滚动组件的子组件可能会非常多、占用的总高度也会非常大；如果要一次性将子组件全部构建出将会非常昂贵！为此，Flutter中提出一个Sliver（中文为“薄片”的意思）概念，如果一个可滚动组件支持Sliver模型，那么该滚动可以将子组件分成好多个“薄片”（Sliver），只有当Sliver出现在视口中时才会去构建它，这种模型也称为“基于Sliver的延迟构建模型”。可滚动组件中有很多都支持基于Sliver的延迟构建模型，如<code>ListView</code>、<code>GridView</code>，但是也有不支持该模型的，如<code>SingleChildScrollView</code>。</p> <h3 id=\"主轴和纵轴\"><a href=\"#主轴和纵轴\" class=\"header-anchor\">#</a> 主轴和纵轴</h3> <p>在可滚动组件的坐标描述中，通常将滚动方向称为主轴，非滚动方向称为纵轴。由于可滚动组件的默认方向一般都是沿垂直方向，所以默认情况下主轴就是指垂直方向，水平方向同理。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/213.f55d441e.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter6/listview.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>6.3 ListView | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/12.ef586fcc.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter6/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_6-3-listview\"><a href=\"#_6-3-listview\" class=\"header-anchor\">#</a> 6.3 ListView</h1> <p><code>ListView</code>是最常用的可滚动组件之一，它可以沿一个方向线性排布所有子组件，并且它也支持基于Sliver的延迟构建模型。我们看看ListView的默认构造函数定义：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">ListView</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>  \n  <span class=\"token comment\">//可滚动widget公共参数</span>\n  Axis scrollDirection <span class=\"token operator\">=</span> Axis<span class=\"token punctuation\">.</span>vertical<span class=\"token punctuation\">,</span>\n  bool reverse <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n  ScrollController controller<span class=\"token punctuation\">,</span>\n  bool primary<span class=\"token punctuation\">,</span>\n  ScrollPhysics physics<span class=\"token punctuation\">,</span>\n  EdgeInsetsGeometry padding<span class=\"token punctuation\">,</span>\n  \n  <span class=\"token comment\">//ListView各个构造函数的共同参数  </span>\n  double itemExtent<span class=\"token punctuation\">,</span>\n  bool shrinkWrap <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n  bool addAutomaticKeepAlives <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n  bool addRepaintBoundaries <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n  double cacheExtent<span class=\"token punctuation\">,</span>\n    \n  <span class=\"token comment\">//子widget列表</span>\n  List<span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span> children <span class=\"token operator\">=</span> <span class=\"token keyword\">const</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>上面参数分为两组：第一组是可滚动组件的公共参数，本章第一节中已经介绍过，不再赘述；第二组是<code>ListView</code>各个构造函数（<code>ListView</code>有多个构造函数）的共同参数，我们重点来看看这些参数，：</p> <ul><li><code>itemExtent</code>：该参数如果不为<code>null</code>，则会强制<code>children</code>的“长度”为<code>itemExtent</code>的值；这里的“长度”是指滚动方向上子组件的长度，也就是说如果滚动方向是垂直方向，则<code>itemExtent</code>代表子组件的高度；如果滚动方向为水平方向，则<code>itemExtent</code>就代表子组件的宽度。在<code>ListView</code>中，指定<code>itemExtent</code>比让子组件自己决定自身长度会更高效，这是因为指定<code>itemExtent</code>后，滚动系统可以提前知道列表的长度，而无需每次构建子组件时都去再计算一下，尤其是在滚动位置频繁变化时（滚动系统需要频繁去计算列表高度）。</li> <li><code>shrinkWrap</code>：该属性表示是否根据子组件的总长度来设置<code>ListView</code>的长度，默认值为<code>false</code> 。默认情况下，<code>ListView</code>的会在滚动方向尽可能多的占用空间。当<code>ListView</code>在一个无边界(滚动方向上)的容器中时，<code>shrinkWrap</code>必须为<code>true</code>。</li> <li><code>addAutomaticKeepAlives</code>：该属性表示是否将列表项（子组件）包裹在<code>AutomaticKeepAlive</code> 组件中；典型地，在一个懒加载列表中，如果将列表项包裹在<code>AutomaticKeepAlive</code>中，在该列表项滑出视口时它也不会被GC（垃圾回收），它会使用<code>KeepAliveNotification</code>来保存其状态。如果列表项自己维护其<code>KeepAlive</code>状态，那么此参数必须置为<code>false</code>。</li> <li><code>addRepaintBoundaries</code>：该属性表示是否将列表项（子组件）包裹在<code>RepaintBoundary</code>组件中。当可滚动组件滚动时，将列表项包裹在<code>RepaintBoundary</code>中可以避免列表项重绘，但是当列表项重绘的开销非常小（如一个颜色块，或者一个较短的文本）时，不添加<code>RepaintBoundary</code>反而会更高效。和<code>addAutomaticKeepAlive</code>一样，如果列表项自己维护其<code>KeepAlive</code>状态，那么此参数必须置为<code>false</code>。</li></ul> <blockquote><p>注意：上面这些参数并非<code>ListView</code>特有，在本章后面介绍的其它可滚动组件也可能会拥有这些参数，它们的含义是相同的。</p></blockquote> <h3 id=\"默认构造函数\"><a href=\"#默认构造函数\" class=\"header-anchor\">#</a> 默认构造函数</h3> <p>默认构造函数有一个<code>children</code>参数，它接受一个Widget列表（List<Widget>）。这种方式适合只有少量的子组件的情况，因为这种方式需要将所有<code>children</code>都提前创建好（这需要做大量工作），而不是等到子widget真正显示的时候再创建，也就是说通过默认构造函数构建的ListView没有应用基于Sliver的懒加载模型。实际上通过此方式创建的<code>ListView</code>和使用<code>SingleChildScrollView</code>+<code>Column</code>的方式没有本质的区别。下面是一个例子：</Widget></p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">ListView</span><span class=\"token punctuation\">(</span>\n  shrinkWrap<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span> \n  padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">20.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token keyword\">const</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'I\\'m dedicating every day to you'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">const</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Domestic life was never quite my style'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">const</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'When you smile, you knock me out, I fall apart'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">const</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'And I thought I was so smart'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><blockquote><p>再次强调，可滚动组件通过一个List<Widget>来作为其children属性时，只适用于子组件较少的情况，这是一个通用规律，并非<code>ListView</code>自己的特性，像<code>GridView</code>也是如此。</Widget></p></blockquote> <h3 id=\"listview-builder\"><a href=\"#listview-builder\" class=\"header-anchor\">#</a> ListView.builder</h3> <p><code>ListView.builder</code>适合列表项比较多（或者无限）的情况，因为只有当子组件真正显示的时候才会被创建，也就说通过该构造函数创建的<code>ListView</code>是支持基于Sliver的懒加载模型的。下面看一下<code>ListView.builder</code>的核心参数列表：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>ListView<span class=\"token punctuation\">.</span><span class=\"token function\">builder</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// ListView公共参数已省略  </span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token metadata symbol\">@required</span> IndexedWidgetBuilder itemBuilder<span class=\"token punctuation\">,</span>\n  int itemCount<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><ul><li><code>itemBuilder</code>：它是列表项的构建器，类型为<code>IndexedWidgetBuilder</code>，返回值为一个widget。当列表滚动到具体的<code>index</code>位置时，会调用该构建器构建列表项。</li> <li><code>itemCount</code>：列表项的数量，如果为<code>null</code>，则为无限列表。</li></ul> <blockquote><p>可滚动组件的构造函数如果需要一个列表项Builder，那么通过该构造函数构建的可滚动组件通常就是支持基于Sliver的懒加载模型的，反之则不支持，这是个一般规律。我们在后面在介绍可滚动组件的构造函数时将不再专门说明其是否支持基于Sliver的懒加载模型了。</p></blockquote> <p>下面看一个例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>ListView<span class=\"token punctuation\">.</span><span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>\n    itemCount<span class=\"token punctuation\">:</span> <span class=\"token number\">100</span><span class=\"token punctuation\">,</span>\n    itemExtent<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//强制高度为50.0</span>\n    itemBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> int index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;$index&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>运行效果如图6-2所示：</p> <p><img src=\"/assets/img/6-2.1d35c6fc.png\" alt=\"图6-2\"></p> <h3 id=\"listview-separated\"><a href=\"#listview-separated\" class=\"header-anchor\">#</a> ListView.separated</h3> <p><code>ListView.separated</code>可以在生成的列表项之间添加一个分割组件，它比<code>ListView.builder</code>多了一个<code>separatorBuilder</code>参数，该参数是一个分割组件生成器。</p> <p>下面我们看一个例子：奇数行添加一条蓝色下划线，偶数行添加一条绿色下划线。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">ListView3</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//下划线widget预定义以供复用。  </span>\n    Widget divider1<span class=\"token operator\">=</span><span class=\"token function\">Divider</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    Widget divider2<span class=\"token operator\">=</span><span class=\"token function\">Divider</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>green<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> ListView<span class=\"token punctuation\">.</span><span class=\"token function\">separated</span><span class=\"token punctuation\">(</span>\n        itemCount<span class=\"token punctuation\">:</span> <span class=\"token number\">100</span><span class=\"token punctuation\">,</span>\n        <span class=\"token comment\">//列表项构造器</span>\n        itemBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> int index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">return</span> <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;$index&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token comment\">//分割器构造器</span>\n        separatorBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> int index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">return</span> index<span class=\"token operator\">%</span><span class=\"token number\">2</span><span class=\"token operator\">==</span><span class=\"token number\">0</span><span class=\"token operator\">?</span>divider1<span class=\"token punctuation\">:</span>divider2<span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p><img src=\"/assets/img/6-3.e023acf1.png\" alt=\"图6-3\"></p> <h3 id=\"实例-无限加载列表\"><a href=\"#实例-无限加载列表\" class=\"header-anchor\">#</a> 实例：无限加载列表</h3> <p>假设我们要从数据源异步分批拉取一些数据，然后用<code>ListView</code>展示，当我们滑动到列表末尾时，判断是否需要再去拉取数据，如果是，则去拉取，拉取过程中在表尾显示一个loading，拉取成功后将数据插入列表；如果不需要再去拉取，则在表尾提示&quot;没有更多&quot;。代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">InfiniteListView</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _InfiniteListViewState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_InfiniteListViewState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_InfiniteListViewState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>InfiniteListView<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">static</span> <span class=\"token keyword\">const</span> loadingTag <span class=\"token operator\">=</span> <span class=\"token string\">&quot;##loading##&quot;</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//表尾标记</span>\n  <span class=\"token keyword\">var</span> _words <span class=\"token operator\">=</span> <span class=\"token operator\">&lt;</span>String<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>loadingTag<span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token function\">_retrieveData</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> ListView<span class=\"token punctuation\">.</span><span class=\"token function\">separated</span><span class=\"token punctuation\">(</span>\n      itemCount<span class=\"token punctuation\">:</span> _words<span class=\"token punctuation\">.</span>length<span class=\"token punctuation\">,</span>\n      itemBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">//如果到了表尾</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>_words<span class=\"token punctuation\">[</span>index<span class=\"token punctuation\">]</span> <span class=\"token operator\">==</span> loadingTag<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token comment\">//不足100条，继续获取数据</span>\n          <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>_words<span class=\"token punctuation\">.</span>length <span class=\"token operator\">-</span> <span class=\"token number\">1</span> <span class=\"token operator\">&lt;</span> <span class=\"token number\">100</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">//获取数据</span>\n            <span class=\"token function\">_retrieveData</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token comment\">//加载时显示loading</span>\n            <span class=\"token keyword\">return</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n              padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">16.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span>\n                  width<span class=\"token punctuation\">:</span> <span class=\"token number\">24.0</span><span class=\"token punctuation\">,</span>\n                  height<span class=\"token punctuation\">:</span> <span class=\"token number\">24.0</span><span class=\"token punctuation\">,</span>\n                  child<span class=\"token punctuation\">:</span> <span class=\"token function\">CircularProgressIndicator</span><span class=\"token punctuation\">(</span>strokeWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">2.0</span><span class=\"token punctuation\">)</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">//已经加载了100条数据，不再获取数据。</span>\n            <span class=\"token keyword\">return</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n                alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n                padding<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">16.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;没有更多了&quot;</span><span class=\"token punctuation\">,</span> style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span>\n        <span class=\"token punctuation\">}</span>\n        <span class=\"token comment\">//显示单词列表项</span>\n        <span class=\"token keyword\">return</span> <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>_words<span class=\"token punctuation\">[</span>index<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      separatorBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> index<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">Divider</span><span class=\"token punctuation\">(</span>height<span class=\"token punctuation\">:</span> <span class=\"token number\">.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_retrieveData</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    Future<span class=\"token punctuation\">.</span><span class=\"token function\">delayed</span><span class=\"token punctuation\">(</span><span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">//重新构建列表</span>\n\t\t_words<span class=\"token punctuation\">.</span><span class=\"token function\">insertAll</span><span class=\"token punctuation\">(</span>_words<span class=\"token punctuation\">.</span>length <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n          <span class=\"token comment\">//每次生成20个单词</span>\n          <span class=\"token function\">generateWordPairs</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">take</span><span class=\"token punctuation\">(</span><span class=\"token number\">20</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> e<span class=\"token punctuation\">.</span>asPascalCase<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toList</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n      \t<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行后效果如图6-4、6-5所示：</p> <p><img src=\"/assets/img/6-4.288d3ff1.png\" alt=\"图6-4\"><img src=\"/assets/img/6-5.2947b7f7.png\" alt=\"图6-5\"></p> <p>代码比较简单，读者可以参照代码中的注释理解，故不再赘述。需要说明的是，<code>_retrieveData()</code>的功能是模拟从数据源异步获取数据，我们使用english_words包的<code>generateWordPairs()</code>方法每次生成20个单词。</p> <h3 id=\"添加固定列表头\"><a href=\"#添加固定列表头\" class=\"header-anchor\">#</a> 添加固定列表头</h3> <p>很多时候我们需要给列表添加一个固定表头，比如我们想实现一个商品列表，需要在列表顶部添加一个“商品列表”标题，期望的效果如图6-6所示：</p> <p><img src=\"/assets/img/6-6.e48bfae5.png\" alt=\"图6-6\"></p> <p>我们按照之前经验，写出如下代码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token metadata symbol\">@override</span>\nWidget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span><span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;商品列表&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    ListView<span class=\"token punctuation\">.</span><span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>itemBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> int index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">return</span> <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;$index&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>然后运行，发现并没有出现我们期望的效果，相反触发了一个异常；</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>Error caught by rendering library, thrown during performResize()。\nVertical viewport was given unbounded height ...\n</code></pre></div><p>从异常信息中我们可以看到是因为<code>ListView</code>高度边界无法确定引起，所以解决的办法也很明显，我们需要给<code>ListView</code>指定边界，我们通过<code>SizedBox</code>指定一个列表高度看看是否生效：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n<span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span>\n    height<span class=\"token punctuation\">:</span> <span class=\"token number\">400</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//指定列表高度为400</span>\n    child<span class=\"token punctuation\">:</span> ListView<span class=\"token punctuation\">.</span><span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>itemBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> int index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">return</span> <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;$index&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n</code></pre></div><p>运行效果如图6-7所示：</p> <p><img src=\"/assets/img/6-7.c443522f.png\" alt=\"图6-7\"></p> <p>可以看到，现在没有触发异常并且列表已经显示出来了，但是我们的手机屏幕高度要大于400，所以底部会有一些空白。那如果我们要实现列表铺满除表头以外的屏幕空间应该怎么做？直观的方法是我们去动态计算，用屏幕高度减去状态栏、导航栏、表头的高度即为剩余屏幕高度，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n<span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span>\n  <span class=\"token comment\">//Material设计规范中状态栏、导航栏、ListTile高度分别为24、56、56 </span>\n  height<span class=\"token punctuation\">:</span> MediaQuery<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>size<span class=\"token punctuation\">.</span>height<span class=\"token operator\">-</span><span class=\"token number\">24</span><span class=\"token operator\">-</span><span class=\"token number\">56</span><span class=\"token operator\">-</span><span class=\"token number\">56</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> ListView<span class=\"token punctuation\">.</span><span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>itemBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> int index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;$index&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>    \n</code></pre></div><p>运行效果如下图6-8所示：</p> <p><img src=\"/assets/img/6-8.e48bfae5.png\" alt=\"图6-8\"></p> <p>可以看到，我们期望的效果实现了，但是这种方法并不优雅，如果页面布局发生变化，比如表头布局调整导致表头高度改变，那么剩余空间的高度就得重新计算。那么有什么方法可以自动拉伸<code>ListView</code>以填充屏幕剩余空间的方法吗？当然有！答案就是<code>Flex</code>。前面已经介绍过在弹性布局中，可以使用<code>Expanded</code>自动拉伸组件大小，并且我们也说过<code>Column</code>是继承自<code>Flex</code>的，所以我们可以直接使用<code>Column</code>+<code>Expanded</code>来实现，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token metadata symbol\">@override</span>\nWidget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span><span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;商品列表&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Expanded</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> ListView<span class=\"token punctuation\">.</span><span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>itemBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> int index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">return</span> <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;$index&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行后，和上图一样，完美实现了！</p> <h3 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h3> <p>本节主要介绍了<code>ListView</code>的一些公共参数以及常用的构造函数。不同的构造函数对应了不同的列表项生成模型，如果需要自定义列表项生成模型，可以通过<code>ListView.custom</code>来自定义，它需要实现一个<code>SliverChildDelegate</code>用来给ListView生成列表项组件，更多详情请参考API文档。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/12.ef586fcc.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter6/scroll_controller.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>6.6 滚动监听及控制 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/55.a7c27448.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter6/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_6-6-滚动监听及控制\"><a href=\"#_6-6-滚动监听及控制\" class=\"header-anchor\">#</a> 6.6 滚动监听及控制</h1> <p>在前几节中，我们介绍了Flutter中常用的可滚动组件，也说过可以用<code>ScrollController</code>来控制可滚动组件的滚动位置，本节先介绍一下<code>ScrollController</code>，然后以<code>ListView</code>为例，展示一下<code>ScrollController</code>的具体用法。最后，再介绍一下路由切换时如何来保存滚动位置。</p> <h2 id=\"_6-6-1-scrollcontroller\"><a href=\"#_6-6-1-scrollcontroller\" class=\"header-anchor\">#</a> 6.6.1 ScrollController</h2> <p><code>ScrollController</code>构造函数如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">ScrollController</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  double initialScrollOffset <span class=\"token operator\">=</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//初始滚动位置</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>keepScrollOffset <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span><span class=\"token comment\">//是否保存滚动位置</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>我们介绍一下<code>ScrollController</code>常用的属性和方法：</p> <ul><li><code>offset</code>：可滚动组件当前的滚动位置。</li> <li><code>jumpTo(double offset)</code>、<code>animateTo(double offset,...)</code>：这两个方法用于跳转到指定的位置，它们不同之处在于，后者在跳转时会执行一个动画，而前者不会。</li></ul> <p><code>ScrollController</code>还有一些属性和方法，我们将在后面原理部分解释。</p> <h4 id=\"滚动监听\"><a href=\"#滚动监听\" class=\"header-anchor\">#</a> 滚动监听</h4> <p><code>ScrollController</code>间接继承自<code>Listenable</code>，我们可以根据<code>ScrollController</code>来监听滚动事件，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>controller<span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span><span class=\"token function\">print</span><span class=\"token punctuation\">(</span>controller<span class=\"token punctuation\">.</span>offset<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n</code></pre></div><h3 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h3> <p>我们创建一个<code>ListView</code>，当滚动位置发生变化时，我们先打印出当前滚动位置，然后判断当前位置是否超过1000像素，如果超过则在屏幕右下角显示一个“返回顶部”的按钮，该按钮点击后可以使ListView恢复到初始位置；如果没有超过1000像素，则隐藏“返回顶部”按钮。代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">ScrollControllerTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  ScrollControllerTestRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">ScrollControllerTestRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">ScrollControllerTestRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>ScrollControllerTestRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  ScrollController _controller <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">ScrollController</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  bool showToTopBtn <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//是否显示“返回到顶部”按钮</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//监听滚动事件，打印滚动位置</span>\n    _controller<span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>_controller<span class=\"token punctuation\">.</span>offset<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//打印滚动位置</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>_controller<span class=\"token punctuation\">.</span>offset <span class=\"token operator\">&lt;</span> <span class=\"token number\">1000</span> <span class=\"token operator\">&amp;&amp;</span> showToTopBtn<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          showToTopBtn <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>_controller<span class=\"token punctuation\">.</span>offset <span class=\"token operator\">&gt;=</span> <span class=\"token number\">1000</span> <span class=\"token operator\">&amp;&amp;</span> showToTopBtn <span class=\"token operator\">==</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          showToTopBtn <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//为了避免内存泄露，需要调用_controller.dispose</span>\n    _controller<span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n      appBar<span class=\"token punctuation\">:</span> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;滚动控制&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      body<span class=\"token punctuation\">:</span> <span class=\"token function\">Scrollbar</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> ListView<span class=\"token punctuation\">.</span><span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>\n            itemCount<span class=\"token punctuation\">:</span> <span class=\"token number\">100</span><span class=\"token punctuation\">,</span>\n            itemExtent<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//列表项高度固定时，显式指定高度是一个好习惯(性能消耗小)</span>\n            controller<span class=\"token punctuation\">:</span> _controller<span class=\"token punctuation\">,</span>\n            itemBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token keyword\">return</span> <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;$index&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      floatingActionButton<span class=\"token punctuation\">:</span> <span class=\"token operator\">!</span>showToTopBtn <span class=\"token operator\">?</span> <span class=\"token keyword\">null</span> <span class=\"token punctuation\">:</span> <span class=\"token function\">FloatingActionButton</span><span class=\"token punctuation\">(</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>arrow_upward<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">//返回到顶部时执行动画</span>\n            _controller<span class=\"token punctuation\">.</span><span class=\"token function\">animateTo</span><span class=\"token punctuation\">(</span><span class=\"token number\">.0</span><span class=\"token punctuation\">,</span>\n                duration<span class=\"token punctuation\">:</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>milliseconds<span class=\"token punctuation\">:</span> <span class=\"token number\">200</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                curve<span class=\"token punctuation\">:</span> Curves<span class=\"token punctuation\">.</span>ease\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>代码说明已经包含在注释里，下面我们看看运行效果：</p> <p><img src=\"/assets/img/6-14.1b612437.png\" alt=\"图6-14\"><img src=\"/assets/img/6-15.00ac68b6.png\" alt=\"图6-15\"></p> <p>由于列表项高度为50像素，当滑动到第20个列表项后，右下角“返回顶部”按钮会显示，点击该按钮，ListView会在返回顶部的过程中执行一个滚动动画，动画时间是200毫秒，动画曲线是<code>Curves.ease</code>，关于动画的详细内容我们将在后面“动画”一章中详细介绍。</p> <h3 id=\"滚动位置恢复\"><a href=\"#滚动位置恢复\" class=\"header-anchor\">#</a> 滚动位置恢复</h3> <p><code>PageStorage</code>是一个用于保存页面(路由)相关数据的组件，它并不会影响子树的UI外观，其实，<code>PageStorage</code>是一个功能型组件，它拥有一个存储桶（bucket），子树中的Widget可以通过指定不同的<code>PageStorageKey</code>来存储各自的数据或状态。</p> <p>每次滚动结束，可滚动组件都会将滚动位置<code>offset</code>存储到<code>PageStorage</code>中，当可滚动组件重新创建时再恢复。如果<code>ScrollController.keepScrollOffset</code>为<code>false</code>，则滚动位置将不会被存储，可滚动组件重新创建时会使用<code>ScrollController.initialScrollOffset</code>；<code>ScrollController.keepScrollOffset</code>为<code>true</code>时，可滚动组件在<strong>第一次</strong>创建时，会滚动到<code>initialScrollOffset</code>处，因为这时还没有存储过滚动位置。在接下来的滚动中就会存储、恢复滚动位置，而<code>initialScrollOffset</code>会被忽略。</p> <p>当一个路由中包含多个可滚动组件时，如果你发现在进行一些跳转或切换操作后，滚动位置不能正确恢复，这时你可以通过显式指定<code>PageStorageKey</code>来分别跟踪不同的可滚动组件的位置，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">ListView</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> <span class=\"token function\">PageStorageKey</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token function\">ListView</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> <span class=\"token function\">PageStorageKey</span><span class=\"token punctuation\">(</span><span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>不同的<code>PageStorageKey</code>，需要不同的值，这样才可以为不同可滚动组件保存其滚动位置。</p> <blockquote><p>注意：一个路由中包含多个可滚动组件时，如果要分别跟踪它们的滚动位置，并非一定就得给他们分别提供<code>PageStorageKey</code>。这是因为Scrollable本身是一个StatefulWidget，它的状态中也会保存当前滚动位置，所以，只要可滚动组件本身没有被从树上detach掉，那么其State就不会销毁(dispose)，滚动位置就不会丢失。只有当Widget发生结构变化，导致可滚动组件的State销毁或重新构建时才会丢失状态，这种情况就需要显式指定<code>PageStorageKey</code>，通过<code>PageStorage</code>来存储滚动位置，一个典型的场景是在使用<code>TabBarView</code>时，在Tab发生切换时，Tab页中的可滚动组件的State就会销毁，这时如果想恢复滚动位置就需要指定<code>PageStorageKey</code>。</p></blockquote> <h3 id=\"scrollposition\"><a href=\"#scrollposition\" class=\"header-anchor\">#</a> ScrollPosition</h3> <p>ScrollPosition是用来保存可滚动组件的滚动位置的。一个<code>ScrollController</code>对象可以同时被多个可滚动组件使用，<code>ScrollController</code>会为每一个可滚动组件创建一个<code>ScrollPosition</code>对象，这些<code>ScrollPosition</code>保存在<code>ScrollController</code>的<code>positions</code>属性中（<code>List&lt;ScrollPosition&gt;</code>）。<code>ScrollPosition</code>是真正保存滑动位置信息的对象，<code>offset</code>只是一个便捷属性：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>double <span class=\"token keyword\">get</span> offset <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> position<span class=\"token punctuation\">.</span>pixels<span class=\"token punctuation\">;</span>\n</code></pre></div><p>一个<code>ScrollController</code>虽然可以对应多个可滚动组件，但是有一些操作，如读取滚动位置<code>offset</code>，则需要一对一！但是我们仍然可以在一对多的情况下，通过其它方法读取滚动位置，举个例子，假设一个<code>ScrollController</code>同时被两个可滚动组件使用，那么我们可以通过如下方式分别读取他们的滚动位置：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\ncontroller<span class=\"token punctuation\">.</span>positions<span class=\"token punctuation\">.</span><span class=\"token function\">elementAt</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>pixels\ncontroller<span class=\"token punctuation\">.</span>positions<span class=\"token punctuation\">.</span><span class=\"token function\">elementAt</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>pixels\n<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>    \n</code></pre></div><p>我们可以通过<code>controller.positions.length</code>来确定<code>controller</code>被几个可滚动组件使用。</p> <h4 id=\"scrollposition的方法\"><a href=\"#scrollposition的方法\" class=\"header-anchor\">#</a> ScrollPosition的方法</h4> <p><code>ScrollPosition</code>有两个常用方法：<code>animateTo()</code> 和 <code>jumpTo()</code>，它们是真正来控制跳转滚动位置的方法，<code>ScrollController</code>的这两个同名方法，内部最终都会调用<code>ScrollPosition</code>的。</p> <h3 id=\"scrollcontroller控制原理\"><a href=\"#scrollcontroller控制原理\" class=\"header-anchor\">#</a> ScrollController控制原理</h3> <p>我们来介绍一下<code>ScrollController</code>的另外三个方法：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>ScrollPosition <span class=\"token function\">createScrollPosition</span><span class=\"token punctuation\">(</span>\n    ScrollPhysics physics<span class=\"token punctuation\">,</span>\n    ScrollContext context<span class=\"token punctuation\">,</span>\n    ScrollPosition oldPosition<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">void</span> <span class=\"token function\">attach</span><span class=\"token punctuation\">(</span>ScrollPosition position<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">void</span> <span class=\"token function\">detach</span><span class=\"token punctuation\">(</span>ScrollPosition position<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">;</span>\n</code></pre></div><p>当<code>ScrollController</code>和可滚动组件关联时，可滚动组件首先会调用<code>ScrollController</code>的<code>createScrollPosition()</code>方法来创建一个<code>ScrollPosition</code>来存储滚动位置信息，接着，可滚动组件会调用<code>attach()</code>方法，将创建的<code>ScrollPosition</code>添加到<code>ScrollController</code>的<code>positions</code>属性中，这一步称为“注册位置”，只有注册后<code>animateTo()</code> 和 <code>jumpTo()</code>才可以被调用。</p> <p>当可滚动组件销毁时，会调用<code>ScrollController</code>的<code>detach()</code>方法，将其<code>ScrollPosition</code>对象从<code>ScrollController</code>的<code>positions</code>属性中移除，这一步称为“注销位置”，注销后<code>animateTo()</code> 和 <code>jumpTo()</code> 将不能再被调用。</p> <p>需要注意的是，<code>ScrollController</code>的<code>animateTo()</code> 和 <code>jumpTo()</code>内部会调用所有<code>ScrollPosition</code>的<code>animateTo()</code> 和 <code>jumpTo()</code>，以实现所有和该<code>ScrollController</code>关联的可滚动组件都滚动到指定的位置。</p> <h2 id=\"_6-6-2-滚动监听\"><a href=\"#_6-6-2-滚动监听\" class=\"header-anchor\">#</a> 6.6.2 滚动监听</h2> <p>Flutter Widget树中子Widget可以通过发送通知（Notification）与父(包括祖先)Widget通信。父级组件可以通过<code>NotificationListener</code>组件来监听自己关注的通知，这种通信方式类似于Web开发中浏览器的事件冒泡，我们在Flutter中沿用“冒泡”这个术语，关于通知冒泡我们将在后面“事件处理与通知”一章中详细介绍。</p> <p>可滚动组件在滚动时会发送<code>ScrollNotification</code>类型的通知，<code>ScrollBar</code>正是通过监听滚动通知来实现的。通过<code>NotificationListener</code>监听滚动事件和通过<code>ScrollController</code>有两个主要的不同：</p> <ol><li>通过NotificationListener可以在从可滚动组件到widget树根之间任意位置都能监听。而<code>ScrollController</code>只能和具体的可滚动组件关联后才可以。</li> <li>收到滚动事件后获得的信息不同；<code>NotificationListener</code>在收到滚动事件时，通知中会携带当前滚动位置和ViewPort的一些信息，而<code>ScrollController</code>只能获取当前滚动位置。</li></ol> <h3 id=\"示例-2\"><a href=\"#示例-2\" class=\"header-anchor\">#</a> 示例</h3> <p>下面，我们监听<code>ListView</code>的滚动通知，然后显示当前滚动进度百分比：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">ScrollNotificationTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _ScrollNotificationTestRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span>\n      <span class=\"token keyword\">new</span> <span class=\"token class-name\">_ScrollNotificationTestRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_ScrollNotificationTestRouteState</span>\n    <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>ScrollNotificationTestRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  String _progress <span class=\"token operator\">=</span> <span class=\"token string\">&quot;0%&quot;</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//保存进度百分比</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Scrollbar</span><span class=\"token punctuation\">(</span> <span class=\"token comment\">//进度条</span>\n      <span class=\"token comment\">// 监听滚动通知</span>\n      child<span class=\"token punctuation\">:</span> NotificationListener<span class=\"token operator\">&lt;</span>ScrollNotification<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n        onNotification<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>ScrollNotification notification<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          double progress <span class=\"token operator\">=</span> notification<span class=\"token punctuation\">.</span>metrics<span class=\"token punctuation\">.</span>pixels <span class=\"token operator\">/</span>\n              notification<span class=\"token punctuation\">.</span>metrics<span class=\"token punctuation\">.</span>maxScrollExtent<span class=\"token punctuation\">;</span>\n          <span class=\"token comment\">//重新构建</span>\n          <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            _progress <span class=\"token operator\">=</span> <span class=\"token string\">&quot;${(progress * 100).toInt()}%&quot;</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;BottomEdge: ${notification.metrics.extentAfter == 0}&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token comment\">//return true; //放开此行注释后，进度条将失效</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Stack</span><span class=\"token punctuation\">(</span>\n          alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            ListView<span class=\"token punctuation\">.</span><span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>\n                itemCount<span class=\"token punctuation\">:</span> <span class=\"token number\">100</span><span class=\"token punctuation\">,</span>\n                itemExtent<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n                itemBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                  <span class=\"token keyword\">return</span> <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;$index&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                <span class=\"token punctuation\">}</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token function\">CircleAvatar</span><span class=\"token punctuation\">(</span>  <span class=\"token comment\">//显示进度百分比</span>\n              radius<span class=\"token punctuation\">:</span> <span class=\"token number\">30.0</span><span class=\"token punctuation\">,</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>_progress<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              backgroundColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>black54<span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行结果如图6-16所示：</p> <p><img src=\"/assets/img/6-16.20d0839c.png\" alt=\"图6-16\"></p> <p>在接收到滚动事件时，参数类型为<code>ScrollNotification</code>，它包括一个<code>metrics</code>属性，它的类型是<code>ScrollMetrics</code>，该属性包含当前ViewPort及滚动位置等信息：</p> <ul><li><code>pixels</code>：当前滚动位置。</li> <li><code>maxScrollExtent</code>：最大可滚动长度。</li> <li><code>extentBefore</code>：滑出ViewPort顶部的长度；此示例中相当于顶部滑出屏幕上方的列表长度。</li> <li><code>extentInside</code>：ViewPort内部长度；此示例中屏幕显示的列表部分的长度。</li> <li><code>extentAfter</code>：列表中未滑入ViewPort部分的长度；此示例中列表底部未显示到屏幕范围部分的长度。</li> <li><code>atEdge</code>：是否滑到了可滚动组件的边界（此示例中相当于列表顶或底部）。</li></ul> <p>ScrollMetrics还有一些其它属性，读者可以自行查阅API文档。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/55.a7c27448.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter6/single_child_scrollview.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>6.2 SingleChildScrollView | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/125.275c37b1.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter6/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_6-2-singlechildscrollview\"><a href=\"#_6-2-singlechildscrollview\" class=\"header-anchor\">#</a> 6.2 SingleChildScrollView</h1> <p><code>SingleChildScrollView</code>类似于Android中的<code>ScrollView</code>，它只能接收一个子组件。定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">SingleChildScrollView</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>scrollDirection <span class=\"token operator\">=</span> Axis<span class=\"token punctuation\">.</span>vertical<span class=\"token punctuation\">,</span> <span class=\"token comment\">//滚动方向，默认是垂直方向</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>reverse <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span> \n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>padding<span class=\"token punctuation\">,</span> \n  bool primary<span class=\"token punctuation\">,</span> \n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>physics<span class=\"token punctuation\">,</span> \n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>controller<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>除了上一节我们介绍过的可滚动组件的通用属性外，我们重点看一下<code>reverse</code>和<code>primary</code>两个属性：</p> <ul><li><code>reverse</code>：该属性API文档解释是：是否按照阅读方向相反的方向滑动，如：<code>scrollDirection</code>值为<code>Axis.horizontal</code>，如果阅读方向是从左到右(取决于语言环境，阿拉伯语就是从右到左)。<code>reverse</code>为<code>true</code>时，那么滑动方向就是从右往左。其实此属性本质上是决定可滚动组件的初始滚动位置是在“头”还是“尾”，取<code>false</code>时，初始滚动位置在“头”，反之则在“尾”，读者可以自己试验。</li> <li><code>primary</code>：指是否使用widget树中默认的<code>PrimaryScrollController</code>；当滑动方向为垂直方向（<code>scrollDirection</code>值为<code>Axis.vertical</code>）并且没有指定<code>controller</code>时，<code>primary</code>默认为<code>true</code>.</li></ul> <p>需要注意的是，通常<code>SingleChildScrollView</code>只应在期望的内容不会超过屏幕太多时使用，这是因为<code>SingleChildScrollView</code>不支持基于Sliver的延迟实例化模型，所以如果预计视口可能包含超出屏幕尺寸太多的内容时，那么使用<code>SingleChildScrollView</code>将会非常昂贵（性能差），此时应该使用一些支持Sliver延迟加载的可滚动组件，如<code>ListView</code>。</p> <h3 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h3> <p>下面是一个将大写字母A-Z沿垂直方向显示的例子，由于垂直方向空间会超过屏幕视口高度，所以我们使用<code>SingleChildScrollView</code>：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">SingleChildScrollViewTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    String str <span class=\"token operator\">=</span> <span class=\"token string\">&quot;ABCDEFGHIJKLMNOPQRSTUVWXYZ&quot;</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Scrollbar</span><span class=\"token punctuation\">(</span> <span class=\"token comment\">// 显示进度条</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">SingleChildScrollView</span><span class=\"token punctuation\">(</span>\n        padding<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span><span class=\"token number\">16.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span> \n            <span class=\"token comment\">//动态创建一个List&lt;Widget&gt;  </span>\n            children<span class=\"token punctuation\">:</span> str<span class=\"token punctuation\">.</span><span class=\"token function\">split</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;&quot;</span><span class=\"token punctuation\">)</span> \n                <span class=\"token comment\">//每一个字母都用一个Text显示,字体为原来的两倍</span>\n                <span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>c<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>c<span class=\"token punctuation\">,</span> textScaleFactor<span class=\"token punctuation\">:</span> <span class=\"token number\">2.0</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> \n                <span class=\"token punctuation\">.</span><span class=\"token function\">toList</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行效果如图6-1所示：</p> <p><img src=\"/assets/img/6-1.a5c8558b.png\" alt=\"图6-1\"></p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/125.275c37b1.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter7/dailog.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>7.6 对话框详解 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/4.2eb3fd02.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter7/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_7-6-对话框详解\"><a href=\"#_7-6-对话框详解\" class=\"header-anchor\">#</a> 7.6 对话框详解</h1> <p>本节将详细介绍一下Flutter中对话框的使用方式、实现原理、样式定制及状态管理。</p> <h2 id=\"_7-6-1-使用对话框\"><a href=\"#_7-6-1-使用对话框\" class=\"header-anchor\">#</a> 7.6.1 使用对话框</h2> <p>对话框本质上也是UI布局，通常一个对话框会包含标题、内容，以及一些操作按钮，为此，Material库中提供了一些现成的对话框组件来用于快速的构建出一个完整的对话框。</p> <h3 id=\"alertdialog\"><a href=\"#alertdialog\" class=\"header-anchor\">#</a> AlertDialog</h3> <p>下面我们主要介绍一下Material库中的<code>AlertDialog</code>组件，它的构造函数定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">const</span> <span class=\"token function\">AlertDialog</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  Key key<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>title<span class=\"token punctuation\">,</span> <span class=\"token comment\">//对话框标题组件</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>titlePadding<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 标题填充</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>titleTextStyle<span class=\"token punctuation\">,</span> <span class=\"token comment\">//标题文本样式</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>content<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 对话框内容组件</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>contentPadding <span class=\"token operator\">=</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">fromLTRB</span><span class=\"token punctuation\">(</span><span class=\"token number\">24.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">20.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">24.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">24.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//内容的填充</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>contentTextStyle<span class=\"token punctuation\">,</span><span class=\"token comment\">// 内容文本样式</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>actions<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 对话框操作按钮组</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>backgroundColor<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 对话框背景色</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>elevation<span class=\"token punctuation\">,</span><span class=\"token comment\">// 对话框的阴影</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>semanticLabel<span class=\"token punctuation\">,</span> <span class=\"token comment\">//对话框语义化标签(用于读屏软件)</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>shape<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 对话框外形</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>参数都比较简单，不在赘述。下面我们看一个例子，假如我们要在删除文件时弹出一个确认对话框，该对话框如图7-10所示：</p> <p><img src=\"/assets/img/7-10.47645234.png\" alt=\"图7-10\"></p> <p>该对话框样式代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">AlertDialog</span><span class=\"token punctuation\">(</span>\n  title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;提示&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  content<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;您确定要删除当前文件吗?&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  actions<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;取消&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//关闭对话框</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;删除&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">// ... 执行删除操作</span>\n        Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span><span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//关闭对话框</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>实现代码很简单，不在赘述。唯一需要注意的是我们是通过<code>Navigator.of(context).pop(…)</code>方法来关闭对话框的，这和路由返回的方式是一致的，并且都可以返回一个结果数据。现在，对话框我们已经构建好了，那么如何将它弹出来呢？还有对话框返回的数据应如何被接收呢？这些问题的答案都在<code>showDialog()</code>方法中。</p> <p><code>showDialog()</code>是Material组件库提供的一个用于弹出Material风格对话框的方法，签名如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Future<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span> showDialog<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@required</span> BuildContext context<span class=\"token punctuation\">,</span>\n  bool barrierDismissible <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//点击对话框barrier(遮罩)时是否关闭它</span>\n  WidgetBuilder builder<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 对话框UI的builder</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>该方法只有两个参数，含义见注释。该方法返回一个<code>Future</code>，它正是用于接收对话框的返回值：如果我们是通过点击对话框遮罩关闭的，则<code>Future</code>的值为<code>null</code>，否则为我们通过<code>Navigator.of(context).pop(result)</code>返回的result值，下面我们看一下整个示例：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//点击该按钮后弹出对话框</span>\n<span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;对话框1&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//弹出对话框并等待其关闭</span>\n    bool delete <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">showDeleteConfirmDialog1</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>delete <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;取消删除&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;已确认删除&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token comment\">//... 删除文件</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n\n<span class=\"token comment\">// 弹出对话框</span>\nFuture<span class=\"token operator\">&lt;</span>bool<span class=\"token operator\">&gt;</span> <span class=\"token function\">showDeleteConfirmDialog1</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> showDialog<span class=\"token operator\">&lt;</span>bool<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n    context<span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">,</span>\n    builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token function\">AlertDialog</span><span class=\"token punctuation\">(</span>\n        title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;提示&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        content<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;您确定要删除当前文件吗?&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        actions<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n          <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;取消&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 关闭对话框</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;删除&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token comment\">//关闭对话框并返回true</span>\n              Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span><span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>示例运行后，我们点击对话框“取消”按钮或遮罩，控制台就会输出&quot;取消删除&quot;，如果点击“删除”按钮，控制台就会输出&quot;已确认删除&quot;。</p> <blockquote><p>注意：如果<code>AlertDialog</code>的内容过长，内容将会溢出，这在很多时候可能不是我们期望的，所以如果对话框内容过长时，可以用<code>SingleChildScrollView</code>将内容包裹起来。</p></blockquote> <h3 id=\"simpledialog\"><a href=\"#simpledialog\" class=\"header-anchor\">#</a> SimpleDialog</h3> <p><code>SimpleDialog</code>也是Material组件库提供的对话框，它会展示一个列表，用于列表选择的场景。下面是一个选择APP语言的示例，运行结果如图7-11。</p> <p><img src=\"/assets/img/7-11.8cffb68f.png\" alt=\"图7-11\"></p> <p>实现代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Future<span class=\"token operator\">&lt;</span><span class=\"token keyword\">void</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">changeLanguage</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n  int i <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> showDialog<span class=\"token operator\">&lt;</span>int<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n      context<span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">,</span>\n      builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">return</span> <span class=\"token function\">SimpleDialog</span><span class=\"token punctuation\">(</span>\n          title<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'请选择语言'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            <span class=\"token function\">SimpleDialogOption</span><span class=\"token punctuation\">(</span>\n              onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token comment\">// 返回1</span>\n                Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n                padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">symmetric</span><span class=\"token punctuation\">(</span>vertical<span class=\"token punctuation\">:</span> <span class=\"token number\">6</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'中文简体'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token function\">SimpleDialogOption</span><span class=\"token punctuation\">(</span>\n              onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token comment\">// 返回2</span>\n                Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n                padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">symmetric</span><span class=\"token punctuation\">(</span>vertical<span class=\"token punctuation\">:</span> <span class=\"token number\">6</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'美国英语'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>i <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;选择了：${i == 1 ? &quot;</span>中文简体<span class=\"token string\">&quot; : &quot;</span>美国英语<span class=\"token string\">&quot;}&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>列表项组件我们使用了<code>SimpleDialogOption</code>组件来包装了一下，它相当于一个FlatButton，只不过按钮文案是左对齐的，并且padding较小。上面示例运行后，用户选择一种语言后，控制台就会打印出它。</p> <h3 id=\"dialog\"><a href=\"#dialog\" class=\"header-anchor\">#</a> Dialog</h3> <p>实际上<code>AlertDialog</code>和<code>SimpleDialog</code>都使用了<code>Dialog</code>类。由于<code>AlertDialog</code>和<code>SimpleDialog</code>中使用了<code>IntrinsicWidth</code>来尝试通过子组件的实际尺寸来调整自身尺寸，这就导致他们的子组件不能是延迟加载模型的组件（如<code>ListView</code>、<code>GridView</code> 、 <code>CustomScrollView</code>等），如下面的代码运行后会报错。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">AlertDialog</span><span class=\"token punctuation\">(</span>\n  content<span class=\"token punctuation\">:</span> <span class=\"token function\">ListView</span><span class=\"token punctuation\">(</span>\n    children<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token comment\">//省略</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>如果我们就是需要嵌套一个<code>ListView</code>应该怎么做？这时，我们可以直接使用<code>Dialog</code>类，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Dialog</span><span class=\"token punctuation\">(</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">ListView</span><span class=\"token punctuation\">(</span>\n    children<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token comment\">//省略</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>下面我们看一个弹出一个有30个列表项的对话框示例，运行效果如图7-12所示：</p> <p><img src=\"/assets/img/7-12.df06c0b8.png\" alt=\"图7-12\"></p> <p>实现代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Future<span class=\"token operator\">&lt;</span><span class=\"token keyword\">void</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">showListDialog</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n  int index <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> showDialog<span class=\"token operator\">&lt;</span>int<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n    context<span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">,</span>\n    builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">var</span> child <span class=\"token operator\">=</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n        children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n          <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;请选择&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">Expanded</span><span class=\"token punctuation\">(</span>\n              child<span class=\"token punctuation\">:</span> ListView<span class=\"token punctuation\">.</span><span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>\n            itemCount<span class=\"token punctuation\">:</span> <span class=\"token number\">30</span><span class=\"token punctuation\">,</span>\n            itemBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> int index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token keyword\">return</span> <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>\n                title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;$index&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                onTap<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span>index<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token comment\">//使用AlertDialog会报错</span>\n      <span class=\"token comment\">//return AlertDialog(content: child);</span>\n      <span class=\"token keyword\">return</span> <span class=\"token function\">Dialog</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">:</span> child<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>index <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;点击了：$index&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>现在，我们己经介绍完了<code>AlertDialog</code>、<code>SimpleDialog</code>以及<code>Dialog</code>。上面的示例中，我们在调用<code>showDialog</code>时，在<code>builder</code>中都是构建了这三个对话框组件的一种，可能有些读者会惯性的以为在<code>builder</code>中只能返回这三者之一，其实这不是必须的！就拿<code>Dialog</code>的示例来举例，我们完全可以用下面的代码来替代<code>Dialog</code>：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">// return Dialog(child: child) </span>\n<span class=\"token keyword\">return</span> <span class=\"token function\">UnconstrainedBox</span><span class=\"token punctuation\">(</span>\n  constrainedAxis<span class=\"token punctuation\">:</span> Axis<span class=\"token punctuation\">.</span>vertical<span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">ConstrainedBox</span><span class=\"token punctuation\">(</span>\n    constraints<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxConstraints</span><span class=\"token punctuation\">(</span>maxWidth<span class=\"token punctuation\">:</span> <span class=\"token number\">280</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">Material</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> child<span class=\"token punctuation\">,</span>\n      type<span class=\"token punctuation\">:</span> MaterialType<span class=\"token punctuation\">.</span>card<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>上面代码运行后可以实现一样的效果。现在我们总结一下：<code>AlertDialog</code>、<code>SimpleDialog</code>以及<code>Dialog</code>是Material组件库提供的三种对话框，旨在帮助开发者快速构建出符合Material设计规范的对话框，但读者完全可以自定义对话框样式，因此，我们仍然可以实现各种样式的对话框，这样即带来了易用性，又有很强的扩展性。</p> <h2 id=\"_7-6-2-对话框打开动画及遮罩\"><a href=\"#_7-6-2-对话框打开动画及遮罩\" class=\"header-anchor\">#</a> 7.6.2 对话框打开动画及遮罩</h2> <p>我们可以把对话框分为内部样式和外部样式两部分。内部样式指对话框中显示的具体内容，这部分内容我们已经在上面介绍过了；外部样式包含对话框遮罩样式、打开动画等，本节主要介绍如何自定义这些外部样式。</p> <blockquote><p>关于动画相关内容我们将在本书后面章节介绍，下面内容读者可以先了解一下（不必深究），读者可以在学习完动画相关内容后再回头来看。</p></blockquote> <p>我们已经介绍过了<code>showDialog</code>方法，它是Material组件库中提供的一个打开Material风格对话框的方法。那如何打开一个普通风格的对话框呢（非Material风格）？ Flutter 提供了一个<code>showGeneralDialog</code>方法，签名如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Future<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span> showGeneralDialog<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@required</span> BuildContext context<span class=\"token punctuation\">,</span>\n  <span class=\"token metadata symbol\">@required</span> RoutePageBuilder pageBuilder<span class=\"token punctuation\">,</span> <span class=\"token comment\">//构建对话框内部UI</span>\n  bool barrierDismissible<span class=\"token punctuation\">,</span> <span class=\"token comment\">//点击遮罩是否关闭对话框</span>\n  String barrierLabel<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 语义化标签(用于读屏软件)</span>\n  Color barrierColor<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 遮罩颜色</span>\n  Duration transitionDuration<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 对话框打开/关闭的动画时长</span>\n  RouteTransitionsBuilder transitionBuilder<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 对话框打开/关闭的动画</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>实际上，<code>showDialog</code>方法正是<code>showGeneralDialog</code>的一个封装，定制了Material风格对话框的遮罩颜色和动画。Material风格对话框打开/关闭动画是一个Fade（渐隐渐显）动画，如果我们想使用一个缩放动画就可以通过<code>transitionBuilder</code>来自定义。下面我们自己封装一个<code>showCustomDialog</code>方法，它定制的对话框动画为缩放动画，并同时制定遮罩颜色为<code>Colors.black87</code>：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Future<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span> showCustomDialog<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@required</span> BuildContext context<span class=\"token punctuation\">,</span>\n  bool barrierDismissible <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n  WidgetBuilder builder<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">final</span> ThemeData theme <span class=\"token operator\">=</span> Theme<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> shadowThemeOnly<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">showGeneralDialog</span><span class=\"token punctuation\">(</span>\n    context<span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">,</span>\n    pageBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext buildContext<span class=\"token punctuation\">,</span> Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> animation<span class=\"token punctuation\">,</span>\n        Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> secondaryAnimation<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">final</span> Widget pageChild <span class=\"token operator\">=</span> <span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> builder<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">return</span> <span class=\"token function\">SafeArea</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">return</span> theme <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span>\n              <span class=\"token operator\">?</span> <span class=\"token function\">Theme</span><span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">:</span> theme<span class=\"token punctuation\">,</span> child<span class=\"token punctuation\">:</span> pageChild<span class=\"token punctuation\">)</span>\n              <span class=\"token punctuation\">:</span> pageChild<span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    barrierDismissible<span class=\"token punctuation\">:</span> barrierDismissible<span class=\"token punctuation\">,</span>\n    barrierLabel<span class=\"token punctuation\">:</span> MaterialLocalizations<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>modalBarrierDismissLabel<span class=\"token punctuation\">,</span>\n    barrierColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>black87<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 自定义遮罩颜色</span>\n    transitionDuration<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>milliseconds<span class=\"token punctuation\">:</span> <span class=\"token number\">150</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    transitionBuilder<span class=\"token punctuation\">:</span> _buildMaterialDialogTransitions<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\nWidget <span class=\"token function\">_buildMaterialDialogTransitions</span><span class=\"token punctuation\">(</span>\n    BuildContext context<span class=\"token punctuation\">,</span>\n    Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> animation<span class=\"token punctuation\">,</span>\n    Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> secondaryAnimation<span class=\"token punctuation\">,</span>\n    Widget child<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// 使用缩放动画</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">ScaleTransition</span><span class=\"token punctuation\">(</span>\n    scale<span class=\"token punctuation\">:</span> <span class=\"token function\">CurvedAnimation</span><span class=\"token punctuation\">(</span>\n      parent<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">,</span>\n      curve<span class=\"token punctuation\">:</span> Curves<span class=\"token punctuation\">.</span>easeOut<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> child<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>现在，我们使用<code>showCustomDialog</code>打开文件删除确认对话框，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\nshowCustomDialog<span class=\"token operator\">&lt;</span>bool<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n  context<span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">,</span>\n  builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">AlertDialog</span><span class=\"token punctuation\">(</span>\n      title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;提示&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      content<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;您确定要删除当前文件吗?&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      actions<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n        <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;取消&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;删除&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">// 执行删除操作</span>\n            Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span><span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>运行效果如图7-13所示：</p> <p><img src=\"/assets/img/7-13.45803ea4.png\" alt=\"图7-13\"></p> <p>可以发现，遮罩颜色比通过<code>showDialog</code>方法打开的对话框更深。另外对话框打开/关闭的动画已变为缩放动画了，读者可以亲自运行示例查看效果。</p> <h2 id=\"_7-6-3-对话框实现原理\"><a href=\"#_7-6-3-对话框实现原理\" class=\"header-anchor\">#</a> 7.6.3 对话框实现原理</h2> <p>我们已经知道对话框最终都是由<code>showGeneralDialog</code>方法打开的，我们来看看它的具体实现：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Future<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span> showGeneralDialog<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@required</span> BuildContext context<span class=\"token punctuation\">,</span>\n  <span class=\"token metadata symbol\">@required</span> RoutePageBuilder pageBuilder<span class=\"token punctuation\">,</span>\n  bool barrierDismissible<span class=\"token punctuation\">,</span>\n  String barrierLabel<span class=\"token punctuation\">,</span>\n  Color barrierColor<span class=\"token punctuation\">,</span>\n  Duration transitionDuration<span class=\"token punctuation\">,</span>\n  RouteTransitionsBuilder transitionBuilder<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> rootNavigator<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>push<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>_DialogRoute<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n    pageBuilder<span class=\"token punctuation\">:</span> pageBuilder<span class=\"token punctuation\">,</span>\n    barrierDismissible<span class=\"token punctuation\">:</span> barrierDismissible<span class=\"token punctuation\">,</span>\n    barrierLabel<span class=\"token punctuation\">:</span> barrierLabel<span class=\"token punctuation\">,</span>\n    barrierColor<span class=\"token punctuation\">:</span> barrierColor<span class=\"token punctuation\">,</span>\n    transitionDuration<span class=\"token punctuation\">:</span> transitionDuration<span class=\"token punctuation\">,</span>\n    transitionBuilder<span class=\"token punctuation\">:</span> transitionBuilder<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>实现很简单，直接调用<code>Navigator</code>的<code>push</code>方法打开了一个新的对话框路由<code>_DialogRoute</code>，然后返回了<code>push</code>的返回值。可见对话框实际上正是通过路由的形式实现的，这也是为什么我们可以使用<code>Navigator</code>的<code>pop</code> 方法来退出对话框的原因。关于对话框的样式定制在<code>_DialogRoute</code>中，没有什么新的东西，读者可以自行查看。</p> <h2 id=\"_7-6-4-对话框状态管理\"><a href=\"#_7-6-4-对话框状态管理\" class=\"header-anchor\">#</a> 7.6.4 对话框状态管理</h2> <p>我们在用户选择删除一个文件时，会询问是否删除此文件；在用户选择一个文件夹是，应该再让用户确认是否删除子文件夹。为了在用户选择了文件夹时避免二次弹窗确认是否删除子目录，我们在确认对话框底部添加一个“同时删除子目录？”的复选框，如图7-14所示：</p> <p><img src=\"/assets/img/7-14.edea3e0f.png\" alt=\"图7-14\"></p> <p>现在就有一个问题：如何管理复选框的选中状态？习惯上，我们会在路由页的State中来管理选中状态，我们可能会写出如下这样的代码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">_DialogRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>DialogRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  bool withTree <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// 复选框选中状态</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n      children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n        <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;对话框2&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n            bool delete <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">showDeleteConfirmDialog2</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>delete <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;取消删除&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;同时删除子目录: $delete&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  Future<span class=\"token operator\">&lt;</span>bool<span class=\"token operator\">&gt;</span> <span class=\"token function\">showDeleteConfirmDialog2</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    withTree <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// 默认复选框不选中</span>\n    <span class=\"token keyword\">return</span> showDialog<span class=\"token operator\">&lt;</span>bool<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n      context<span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">,</span>\n      builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">return</span> <span class=\"token function\">AlertDialog</span><span class=\"token punctuation\">(</span>\n          title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;提示&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          content<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n            crossAxisAlignment<span class=\"token punctuation\">:</span> CrossAxisAlignment<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n            mainAxisSize<span class=\"token punctuation\">:</span> MainAxisSize<span class=\"token punctuation\">.</span>min<span class=\"token punctuation\">,</span>\n            children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n              <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;您确定要删除当前文件吗?&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n                children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                  <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;同时删除子目录？&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token function\">Checkbox</span><span class=\"token punctuation\">(</span>\n                    value<span class=\"token punctuation\">:</span> withTree<span class=\"token punctuation\">,</span>\n                    onChanged<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>bool value<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                      <span class=\"token comment\">//复选框选中状态发生变化时重新构建UI</span>\n                      <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                        <span class=\"token comment\">//更新复选框状态</span>\n                        withTree <span class=\"token operator\">=</span> <span class=\"token operator\">!</span>withTree<span class=\"token punctuation\">;</span>\n                      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          actions<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;取消&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;删除&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token comment\">//执行删除操作</span>\n                Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span>withTree<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>然后，当我们运行上面的代码时我们会发现复选框根本选不中！为什么会这样呢？其实原因很简单，我们知道<code>setState</code>方法只会针对当前context的子树重新build，但是我们的对话框并不是在<code>_DialogRouteState</code>的<code>build</code> 方法中构建的，而是通过<code>showDialog</code>单独构建的，所以在<code>_DialogRouteState</code>的context中调用<code>setState</code>是无法影响通过<code>showDialog</code>构建的UI的。另外，我们可以从另外一个角度来理解这个现象，前面说过对话框也是通过路由的方式来实现的，那么上面的代码实际上就等同于企图在父路由中调用<code>setState</code>来让子路由更新，这显然是不行的！简尔言之，根本原因就是context不对。那如何让复选框可点击呢？通常有如下三种方法：</p> <h3 id=\"单独抽离出statefulwidget\"><a href=\"#单独抽离出statefulwidget\" class=\"header-anchor\">#</a> 单独抽离出StatefulWidget</h3> <p>既然是context不对，那么直接的思路就是将复选框的选中逻辑单独封装成一个<code>StatefulWidget</code>，然后在其内部管理复选状态。我们先来看看这种方法，下面是实现代码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">// 单独封装一个内部管理选中状态的复选框组件</span>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">DialogCheckbox</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">DialogCheckbox</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span>\n    <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>onChanged<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> ValueChanged<span class=\"token operator\">&lt;</span>bool<span class=\"token operator\">&gt;</span> onChanged<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> bool value<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  _DialogCheckboxState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">_DialogCheckboxState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_DialogCheckboxState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>DialogCheckbox<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  bool value<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    value <span class=\"token operator\">=</span> widget<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Checkbox</span><span class=\"token punctuation\">(</span>\n      value<span class=\"token punctuation\">:</span> value<span class=\"token punctuation\">,</span>\n      onChanged<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>v<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">//将选中状态通过事件的形式抛出</span>\n        widget<span class=\"token punctuation\">.</span><span class=\"token function\">onChanged</span><span class=\"token punctuation\">(</span>v<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token comment\">//更新自身选中状态</span>\n          value <span class=\"token operator\">=</span> v<span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>下面是弹出对话框的代码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Future<span class=\"token operator\">&lt;</span>bool<span class=\"token operator\">&gt;</span> <span class=\"token function\">showDeleteConfirmDialog3</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  bool _withTree <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//记录复选框是否选中</span>\n  <span class=\"token keyword\">return</span> showDialog<span class=\"token operator\">&lt;</span>bool<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n    context<span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">,</span>\n    builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token function\">AlertDialog</span><span class=\"token punctuation\">(</span>\n        title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;提示&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        content<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n          crossAxisAlignment<span class=\"token punctuation\">:</span> CrossAxisAlignment<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n          mainAxisSize<span class=\"token punctuation\">:</span> MainAxisSize<span class=\"token punctuation\">.</span>min<span class=\"token punctuation\">,</span>\n          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;您确定要删除当前文件吗?&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n              children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;同时删除子目录？&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token function\">DialogCheckbox</span><span class=\"token punctuation\">(</span>\n                  value<span class=\"token punctuation\">:</span> _withTree<span class=\"token punctuation\">,</span> <span class=\"token comment\">//默认不选中</span>\n                  onChanged<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>bool value<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                    <span class=\"token comment\">//更新选中状态</span>\n                    _withTree <span class=\"token operator\">=</span> <span class=\"token operator\">!</span>_withTree<span class=\"token punctuation\">;</span>\n                  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        actions<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n          <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;取消&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;删除&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token comment\">// 将选中状态返回</span>\n              Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span>_withTree<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>最后，就是使用：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;话框3（复选框可点击）&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//弹出删除确认对话框，等待用户确认</span>\n    bool deleteTree <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">showDeleteConfirmDialog3</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>deleteTree <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;取消删除&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;同时删除子目录: $deleteTree&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</code></pre></div><p>运行后效果如图7-15所示：</p> <p><img src=\"/assets/img/7-15.8624d4d8.png\" alt=\"图7-15\"></p> <p>可见复选框能选中了，点击“取消”或“删除”后，控制台就会打印出最终的确认状态。</p> <h3 id=\"使用statefulbuilder方法\"><a href=\"#使用statefulbuilder方法\" class=\"header-anchor\">#</a> 使用StatefulBuilder方法</h3> <p>上面的方法虽然能解决对话框状态更新的问题，但是有一个明显的缺点——对话框上所有可能会改变状态的组件都得单独封装在一个在内部管理状态的<code>StatefulWidget</code>中，这样不仅麻烦，而且复用性不大。因此，我们来想想能不能找到一种更简单的方法？上面的方法本质上就是将对话框的状态置于一个<code>StatefulWidget</code>的上下文中，由<code>StatefulWidget</code>在内部管理，那么我们有没有办法在不需要单独抽离组件的情况下创建一个<code>StatefulWidget</code>的上下文呢？想到这里，我们可以从<code>Builder</code>组件的实现获得灵感。在前面介绍过<code>Builder</code>组件可以获得组件所在位置的真正的Context，那它是怎么实现的呢，我们看看它的源码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">Builder</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n    <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>builder<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span>builder <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n       <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> WidgetBuilder builder<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以看到，<code>Builder</code>实际上只是继承了<code>StatelessWidget</code>，然后在<code>build</code>方法中获取当前context后将构建方法代理到了<code>builder</code>回调，可见，<code>Builder</code>实际上是获取了<code>StatelessWidget</code> 的上下文（context）。那么我们能否用相同的方法获取<code>StatefulWidget</code> 的上下文，并代理其<code>build</code>方法呢？下面我们照猫画虎，来封装一个<code>StatefulBuilder</code>方法：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">StatefulBuilder</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function\">StatefulBuilder</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n    <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>builder<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span>builder <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n       <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> StatefulWidgetBuilder builder<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  _StatefulBuilderState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">_StatefulBuilderState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_StatefulBuilderState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>StatefulBuilder<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> widget<span class=\"token punctuation\">.</span><span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> setState<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>代码很简单，<code>StatefulBuilder</code>获取了<code>StatefulWidget</code>的上下文，并代理了其构建过程。下面我们就可以通过<code>StatefulBuilder</code>来重构上面的代码了（变动只在<code>DialogCheckbox</code>部分）：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n<span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;同时删除子目录？&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token comment\">//使用StatefulBuilder来构建StatefulWidget上下文</span>\n    <span class=\"token function\">StatefulBuilder</span><span class=\"token punctuation\">(</span>\n      builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> _setState<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">return</span> <span class=\"token function\">Checkbox</span><span class=\"token punctuation\">(</span>\n          value<span class=\"token punctuation\">:</span> _withTree<span class=\"token punctuation\">,</span> <span class=\"token comment\">//默认不选中</span>\n          onChanged<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>bool value<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">//_setState方法实际就是该StatefulWidget的setState方法，</span>\n            <span class=\"token comment\">//调用后builder方法会重新被调用</span>\n            <span class=\"token function\">_setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token comment\">//更新选中状态</span>\n              _withTree <span class=\"token operator\">=</span> <span class=\"token operator\">!</span>_withTree<span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</code></pre></div><p>实际上，这种方法本质上就是子组件通知父组件（StatefulWidget）重新build子组件本身来实现UI更新的，读者可以对比代码理解。实际上<code>StatefulBuilder</code>正是Flutter SDK中提供的一个类，它和<code>Builder</code>的原理是一样的，在此，提醒读者一定要将<code>StatefulBuilder</code>和<code>Builder</code>理解透彻，因为它们在Flutter中是非常实用的。</p> <h3 id=\"精妙的解法\"><a href=\"#精妙的解法\" class=\"header-anchor\">#</a> 精妙的解法</h3> <p>是否还有更简单的解决方案呢？要确认这个问题，我们就得先搞清楚UI是怎么更新的，我们知道在调用<code>setState</code>方法后<code>StatefulWidget</code>就会重新build，那<code>setState</code>方法做了什么呢？我们能不能从中找到方法？顺着这个思路，我们就得看一下<code>setState</code>的核心源码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span>VoidCallback fn<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n  _element<span class=\"token punctuation\">.</span><span class=\"token function\">markNeedsBuild</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以发现，<code>setState</code>中调用了<code>Element</code>的<code>markNeedsBuild()</code>方法，我们前面说过，Flutter是一个响应式框架，要更新UI只需改变状态后通知框架页面需要重构即可，而<code>Element</code>的<code>markNeedsBuild()</code>方法正是来实现这个功能的！<code>markNeedsBuild()</code>方法会将当前的<code>Element</code>对象标记为“dirty”（脏的），在每一个Frame，Flutter都会重新构建被标记为“dirty”<code>Element</code>对象。既然如此，我们有没有办法获取到对话框内部UI的<code>Element</code>对象，然后将其标示为为“dirty”呢？答案是肯定的！我们可以通过Context来得到<code>Element</code>对象，至于<code>Element</code>与<code>Context</code>的关系我们将会在后面“Flutter核心原理”一章中再深入介绍，现在只需要简单的认为：在组件树中，<code>context</code>实际上就是<code>Element</code>对象的引用。知道这个后，那么解决的方案就呼之欲出了，我们可以通过如下方式来让复选框可以更新：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Future<span class=\"token operator\">&lt;</span>bool<span class=\"token operator\">&gt;</span> <span class=\"token function\">showDeleteConfirmDialog4</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  bool _withTree <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">return</span> showDialog<span class=\"token operator\">&lt;</span>bool<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n    context<span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">,</span>\n    builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token function\">AlertDialog</span><span class=\"token punctuation\">(</span>\n        title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;提示&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        content<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n          crossAxisAlignment<span class=\"token punctuation\">:</span> CrossAxisAlignment<span class=\"token punctuation\">.</span>start<span class=\"token punctuation\">,</span>\n          mainAxisSize<span class=\"token punctuation\">:</span> MainAxisSize<span class=\"token punctuation\">.</span>min<span class=\"token punctuation\">,</span>\n          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;您确定要删除当前文件吗?&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n              children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;同时删除子目录？&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token function\">Checkbox</span><span class=\"token punctuation\">(</span> <span class=\"token comment\">// 依然使用Checkbox组件</span>\n                  value<span class=\"token punctuation\">:</span> _withTree<span class=\"token punctuation\">,</span>\n                  onChanged<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>bool value<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                    <span class=\"token comment\">// 此时context为对话框UI的根Element，我们 </span>\n                    <span class=\"token comment\">// 直接将对话框UI对应的Element标记为dirty</span>\n                    <span class=\"token punctuation\">(</span>context <span class=\"token operator\">as</span> Element<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">markNeedsBuild</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                    _withTree <span class=\"token operator\">=</span> <span class=\"token operator\">!</span>_withTree<span class=\"token punctuation\">;</span>\n                  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        actions<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n          <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;取消&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;删除&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token comment\">// 执行删除操作</span>\n              Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span>_withTree<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面的代码运行后复选框也可以正常选中。可以看到，我们只用了一行代码便解决了这个问题！当然上面的代码并不是最优，因为我们只需要更新复选框的状态，而此时的<code>context</code>我们用的是对话框的根<code>context</code>，所以会导致整个对话框UI组件全部rebuild，因此最好的做法是将<code>context</code>的“范围”缩小，也就是说只将<code>Checkbox</code>的Element标记为<code>dirty</code>，优化后的代码为：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n<span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;同时删除子目录？&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token comment\">// 通过Builder来获得构建Checkbox的`context`，</span>\n    <span class=\"token comment\">// 这是一种常用的缩小`context`范围的方式</span>\n    <span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span>\n      builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">return</span> <span class=\"token function\">Checkbox</span><span class=\"token punctuation\">(</span>\n          value<span class=\"token punctuation\">:</span> _withTree<span class=\"token punctuation\">,</span>\n          onChanged<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>bool value<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token punctuation\">(</span>context <span class=\"token operator\">as</span> Element<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">markNeedsBuild</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            _withTree <span class=\"token operator\">=</span> <span class=\"token operator\">!</span>_withTree<span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</code></pre></div><h2 id=\"_7-6-5-其它类型的对话框\"><a href=\"#_7-6-5-其它类型的对话框\" class=\"header-anchor\">#</a> 7.6.5 其它类型的对话框</h2> <h3 id=\"底部菜单列表\"><a href=\"#底部菜单列表\" class=\"header-anchor\">#</a> 底部菜单列表</h3> <p><code>showModalBottomSheet</code>方法可以弹出一个Material风格的底部菜单列表模态对话框，示例如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">// 弹出底部菜单列表模态对话框</span>\nFuture<span class=\"token operator\">&lt;</span>int<span class=\"token operator\">&gt;</span> <span class=\"token function\">_showModalBottomSheet</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> showModalBottomSheet<span class=\"token operator\">&lt;</span>int<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n    context<span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">,</span>\n    builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> ListView<span class=\"token punctuation\">.</span><span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>\n        itemCount<span class=\"token punctuation\">:</span> <span class=\"token number\">30</span><span class=\"token punctuation\">,</span>\n        itemBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> int index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">return</span> <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>\n            title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;$index&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            onTap<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span>index<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>点击按钮，弹出该对话框：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;显示底部菜单列表&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    int type <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">_showModalBottomSheet</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>type<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</code></pre></div><p>运行后效果如图7-16所示：</p> <p><img src=\"/assets/img/7-16.da05101a.png\" alt=\"图7-16\"></p> <p><code>showModalBottomSheet</code>的实现原理和<code>showGeneralDialog</code>实现原理相同，都是通过路由的方式来实现的，读者可以查看源码对比。但值得一提的是还有一个<code>showBottomSheet</code>方法，该方法会从设备底部向上弹出一个全屏的菜单列表，示例如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">// 返回的是一个controller</span>\nPersistentBottomSheetController<span class=\"token operator\">&lt;</span>int<span class=\"token operator\">&gt;</span> <span class=\"token function\">_showBottomSheet</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> showBottomSheet<span class=\"token operator\">&lt;</span>int<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n    context<span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">,</span>\n    builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> ListView<span class=\"token punctuation\">.</span><span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>\n        itemCount<span class=\"token punctuation\">:</span> <span class=\"token number\">30</span><span class=\"token punctuation\">,</span>\n        itemBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> int index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">return</span> <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>\n            title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;$index&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            onTap<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n              <span class=\"token comment\">// do something</span>\n              <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;$index&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行效果如图7-17所示：</p> <p><img src=\"/assets/img/7-17.c0e2d9be.png\" alt=\"图7-17\"></p> <p><code>PersistentBottomSheetController</code>中包含了一些控制对话框的方法比如<code>close</code>方法可以关闭该对话框，功能比较简单，读者可以自行查看源码。唯一需要注意的是，<code>showBottomSheet</code>和我们上面介绍的弹出对话框的方法原理不同：<code>showBottomSheet</code>是调用widget树顶部的<code>Scaffold</code>组件的<code>ScaffoldState</code>的<code>showBottomSheet</code>同名方法实现，也就是说要调用<code>showBottomSheet</code>方法就必须得保证父级组件中有<code>Scaffold</code>。</p> <h3 id=\"loading框\"><a href=\"#loading框\" class=\"header-anchor\">#</a> Loading框</h3> <p>其实Loading框可以直接通过<code>showDialog</code>+<code>AlertDialog</code>来自定义：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">showLoadingDialog</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">showDialog</span><span class=\"token punctuation\">(</span>\n    context<span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">,</span>\n    barrierDismissible<span class=\"token punctuation\">:</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//点击遮罩不关闭对话框</span>\n    builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token function\">AlertDialog</span><span class=\"token punctuation\">(</span>\n        content<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n          mainAxisSize<span class=\"token punctuation\">:</span> MainAxisSize<span class=\"token punctuation\">.</span>min<span class=\"token punctuation\">,</span>\n          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            <span class=\"token function\">CircularProgressIndicator</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n              padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">only</span><span class=\"token punctuation\">(</span>top<span class=\"token punctuation\">:</span> <span class=\"token number\">26.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;正在加载，请稍后...&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>显示效果如图7-18所示：</p> <p><img src=\"/assets/img/7-18.d0ccba9d.png\" alt=\"图7-18\"></p> <p>如果我们嫌Loading框太宽，想自定义对话框宽度，这时只使用<code>SizedBox</code>或<code>ConstrainedBox</code>是不行的，原因是<code>showDialog</code>中已经给对话框设置了宽度限制，根据我们在第五章“尺寸限制类容器”一节中所述，我们可以使用<code>UnconstrainedBox</code>先抵消<code>showDialog</code>对宽度的限制，然后再使用<code>SizedBox</code>指定宽度，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n<span class=\"token function\">UnconstrainedBox</span><span class=\"token punctuation\">(</span>\n  constrainedAxis<span class=\"token punctuation\">:</span> Axis<span class=\"token punctuation\">.</span>vertical<span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span>\n    width<span class=\"token punctuation\">:</span> <span class=\"token number\">280</span><span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">AlertDialog</span><span class=\"token punctuation\">(</span>\n      content<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n        mainAxisSize<span class=\"token punctuation\">:</span> MainAxisSize<span class=\"token punctuation\">.</span>min<span class=\"token punctuation\">,</span>\n        children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n          <span class=\"token function\">CircularProgressIndicator</span><span class=\"token punctuation\">(</span>value<span class=\"token punctuation\">:</span> <span class=\"token number\">.8</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n            padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">only</span><span class=\"token punctuation\">(</span>top<span class=\"token punctuation\">:</span> <span class=\"token number\">26.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;正在加载，请稍后...&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>代码运行后，效果如图7-19所示：</p> <p><img src=\"/assets/img/7-19.4c3306a3.png\" alt=\"图7-19\"></p> <h3 id=\"日历选择\"><a href=\"#日历选择\" class=\"header-anchor\">#</a> 日历选择</h3> <p>我们先看一下Material风格的日历选择器，如图7-20所示：</p> <p><img src=\"/assets/img/7-20.f8ec9897.png\" alt=\"图7-20\"></p> <p>实现代码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Future<span class=\"token operator\">&lt;</span>DateTime<span class=\"token operator\">&gt;</span> <span class=\"token function\">_showDatePicker1</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">var</span> date <span class=\"token operator\">=</span> DateTime<span class=\"token punctuation\">.</span><span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">showDatePicker</span><span class=\"token punctuation\">(</span>\n    context<span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">,</span>\n    initialDate<span class=\"token punctuation\">:</span> date<span class=\"token punctuation\">,</span>\n    firstDate<span class=\"token punctuation\">:</span> date<span class=\"token punctuation\">,</span>\n    lastDate<span class=\"token punctuation\">:</span> date<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span> <span class=\"token comment\">//未来30天可选</span>\n      <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>days<span class=\"token punctuation\">:</span> <span class=\"token number\">30</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>iOS风格的日历选择器需要使用<code>showCupertinoModalPopup</code>方法和<code>CupertinoDatePicker</code>组件来实现：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Future<span class=\"token operator\">&lt;</span>DateTime<span class=\"token operator\">&gt;</span> <span class=\"token function\">_showDatePicker2</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">var</span> date <span class=\"token operator\">=</span> DateTime<span class=\"token punctuation\">.</span><span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">showCupertinoModalPopup</span><span class=\"token punctuation\">(</span>\n    context<span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">,</span>\n    builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>ctx<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span>\n        height<span class=\"token punctuation\">:</span> <span class=\"token number\">200</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">CupertinoDatePicker</span><span class=\"token punctuation\">(</span>\n          mode<span class=\"token punctuation\">:</span> CupertinoDatePickerMode<span class=\"token punctuation\">.</span>dateAndTime<span class=\"token punctuation\">,</span>\n          minimumDate<span class=\"token punctuation\">:</span> date<span class=\"token punctuation\">,</span>\n          maximumDate<span class=\"token punctuation\">:</span> date<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span>\n            <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>days<span class=\"token punctuation\">:</span> <span class=\"token number\">30</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          maximumYear<span class=\"token punctuation\">:</span> date<span class=\"token punctuation\">.</span>year <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n          onDateTimeChanged<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>DateTime value<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>value<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行效果如图7-21所示：</p> <p><img src=\"/assets/img/7-21.d3d1d15f.png\" alt=\"图7-21\"></p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/4.2eb3fd02.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter7/futurebuilder_and_streambuilder.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>7.5 异步UI更新（FutureBuilder、StreamBuilder） | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/77.8fef1f03.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter7/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_7-5-异步ui更新-futurebuilder、streambuilder\"><a href=\"#_7-5-异步ui更新-futurebuilder、streambuilder\" class=\"header-anchor\">#</a> 7.5 异步UI更新（FutureBuilder、StreamBuilder）</h1> <p>很多时候我们会依赖一些异步数据来动态更新UI，比如在打开一个页面时我们需要先从互联网上获取数据，在获取数据的过程中我们显示一个加载框，等获取到数据时我们再渲染页面；又比如我们想展示Stream（比如文件流、互联网数据接收流）的进度。当然，通过StatefulWidget我们完全可以实现上述这些功能。但由于在实际开发中依赖异步数据更新UI的这种场景非常常见，因此Flutter专门提供了<code>FutureBuilder</code>和<code>StreamBuilder</code>两个组件来快速实现这种功能。</p> <h2 id=\"_7-5-1-futurebuilder\"><a href=\"#_7-5-1-futurebuilder\" class=\"header-anchor\">#</a> 7.5.1 FutureBuilder</h2> <p><code>FutureBuilder</code>会依赖一个<code>Future</code>，它会根据所依赖的<code>Future</code>的状态来动态构建自身。我们看一下<code>FutureBuilder</code>构造函数：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">FutureBuilder</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>future<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>initialData<span class=\"token punctuation\">,</span>\n  <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>builder<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><ul><li><p><code>future</code>：<code>FutureBuilder</code>依赖的<code>Future</code>，通常是一个异步耗时任务。</p></li> <li><p><code>initialData</code>：初始数据，用户设置默认数据。</p></li> <li><p><code>builder</code>：Widget构建器；该构建器会在<code>Future</code>执行的不同阶段被多次调用，构建器签名如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">Function</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> AsyncSnapshot snapshot<span class=\"token punctuation\">)</span> \n</code></pre></div><p><code>snapshot</code>会包含当前异步任务的状态信息及结果信息 ，比如我们可以通过<code>snapshot.connectionState</code>获取异步任务的状态信息、通过<code>snapshot.hasError</code>判断异步任务是否有错误等等，完整的定义读者可以查看<code>AsyncSnapshot</code>类定义。</p> <p>另外，<code>FutureBuilder</code>的<code>builder</code>函数签名和<code>StreamBuilder</code>的<code>builder</code>是相同的。</p></li></ul> <h3 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h3> <p>我们实现一个路由，当该路由打开时我们从网上获取数据，获取数据时弹一个加载框；获取结束时，如果成功则显示获取到的数据，如果失败则显示错误。由于我们还没有介绍在flutter中如何发起网络请求，所以在这里我们不真正去网络请求数据，而是模拟一下这个过程，隔3秒后返回一个字符串：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Future<span class=\"token operator\">&lt;</span>String<span class=\"token operator\">&gt;</span> <span class=\"token function\">mockNetworkData</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> Future<span class=\"token punctuation\">.</span><span class=\"token function\">delayed</span><span class=\"token punctuation\">(</span><span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token string\">&quot;我是从互联网上获取的数据&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p><code>FutureBuilder</code>使用代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\nWidget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n    child<span class=\"token punctuation\">:</span> FutureBuilder<span class=\"token operator\">&lt;</span>String<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n      future<span class=\"token punctuation\">:</span> <span class=\"token function\">mockNetworkData</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> AsyncSnapshot snapshot<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">// 请求已结束</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>snapshot<span class=\"token punctuation\">.</span>connectionState <span class=\"token operator\">==</span> ConnectionState<span class=\"token punctuation\">.</span>done<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>snapshot<span class=\"token punctuation\">.</span>hasError<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">// 请求失败，显示错误</span>\n            <span class=\"token keyword\">return</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Error: ${snapshot.error}&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">// 请求成功，显示数据</span>\n            <span class=\"token keyword\">return</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Contents: ${snapshot.data}&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span>\n        <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token comment\">// 请求未结束，显示loading</span>\n          <span class=\"token keyword\">return</span> <span class=\"token function\">CircularProgressIndicator</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行结果如图7-8、7-9所示：</p> <p><img src=\"/assets/img/7-8.c316cc7f.png\" alt=\"图7-8\"><img src=\"/assets/img/7-9.62d054b0.png\" alt=\"图7-9\"></p> <p>上面代码中我们在<code>builder</code>中根据当前异步任务状态<code>ConnectionState</code>来返回不同的widget。<code>ConnectionState</code>是一个枚举类，定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">enum</span> ConnectionState <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">/// 当前没有异步任务，比如[FutureBuilder]的[future]为null时</span>\n  none<span class=\"token punctuation\">,</span>\n\n  <span class=\"token comment\">/// 异步任务处于等待状态</span>\n  waiting<span class=\"token punctuation\">,</span>\n\n  <span class=\"token comment\">/// Stream处于激活状态（流上已经有数据传递了），对于FutureBuilder没有该状态。</span>\n  active<span class=\"token punctuation\">,</span>\n\n  <span class=\"token comment\">/// 异步任务已经终止.</span>\n  done<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>注意，<code>ConnectionState.active</code>只在<code>StreamBuilder</code>中才会出现。</p> <h2 id=\"_7-5-2-streambuilder\"><a href=\"#_7-5-2-streambuilder\" class=\"header-anchor\">#</a> 7.5.2 StreamBuilder</h2> <p>我们知道，在Dart中<code>Stream</code> 也是用于接收异步事件数据，和<code>Future</code> 不同的是，它可以接收多个异步操作的结果，它常用于会多次读取数据的异步任务场景，如网络内容下载、文件读写等。<code>StreamBuilder</code>正是用于配合<code>Stream</code>来展示流上事件（数据）变化的UI组件。下面看一下<code>StreamBuilder</code>的默认构造函数：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">StreamBuilder</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  Key key<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>initialData<span class=\"token punctuation\">,</span>\n  Stream<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span> stream<span class=\"token punctuation\">,</span>\n  <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>builder<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> \n</code></pre></div><p>可以看到和<code>FutureBuilder</code>的构造函数只有一点不同：前者需要一个<code>future</code>，而后者需要一个<code>stream</code>。</p> <h3 id=\"示例-2\"><a href=\"#示例-2\" class=\"header-anchor\">#</a> 示例</h3> <p>我们创建一个计时器的示例：每隔1秒，计数加1。这里，我们使用<code>Stream</code>来实现每隔一秒生成一个数字:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Stream<span class=\"token operator\">&lt;</span>int<span class=\"token operator\">&gt;</span> <span class=\"token function\">counter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> Stream<span class=\"token punctuation\">.</span><span class=\"token function\">periodic</span><span class=\"token punctuation\">(</span><span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span>i<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> i<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p><code>StreamBuilder</code>使用代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>  \n Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> StreamBuilder<span class=\"token operator\">&lt;</span>int<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n      stream<span class=\"token punctuation\">:</span> <span class=\"token function\">counter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//</span>\n      <span class=\"token comment\">//initialData: ,// a Stream&lt;int&gt; or null</span>\n      builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> AsyncSnapshot<span class=\"token operator\">&lt;</span>int<span class=\"token operator\">&gt;</span> snapshot<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>snapshot<span class=\"token punctuation\">.</span>hasError<span class=\"token punctuation\">)</span>\n          <span class=\"token keyword\">return</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Error: ${snapshot.error}'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">switch</span> <span class=\"token punctuation\">(</span>snapshot<span class=\"token punctuation\">.</span>connectionState<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">case</span> ConnectionState<span class=\"token punctuation\">.</span>none<span class=\"token punctuation\">:</span>\n            <span class=\"token keyword\">return</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'没有Stream'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token keyword\">case</span> ConnectionState<span class=\"token punctuation\">.</span>waiting<span class=\"token punctuation\">:</span>\n            <span class=\"token keyword\">return</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'等待数据...'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token keyword\">case</span> ConnectionState<span class=\"token punctuation\">.</span>active<span class=\"token punctuation\">:</span>\n            <span class=\"token keyword\">return</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'active: ${snapshot.data}'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token keyword\">case</span> ConnectionState<span class=\"token punctuation\">.</span>done<span class=\"token punctuation\">:</span>\n            <span class=\"token keyword\">return</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Stream已关闭'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n        <span class=\"token keyword\">return</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// unreachable</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n <span class=\"token punctuation\">}</span>\n</code></pre></div><p>读者可以自己运行本示例查看运行结果。注意，本示例只是为了演示<code>StreamBuilder</code>的使用，在实战中，凡是UI会依赖多个异步数据而发生变化的场景都可以使用<code>StreamBuilder</code>。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/77.8fef1f03.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter7/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>功能型Widget简介 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/214.0836f39e.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter7/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h2 id=\"功能型widget简介\"><a href=\"#功能型widget简介\" class=\"header-anchor\">#</a> 功能型Widget简介</h2> <p>功能型Widget指的是不会影响UI布局及外观的Widget，它们通常具有一定的功能，如事件监听、数据存储等，我们之前介绍过的FocusScope（焦点控制）、PageStorage（数据存储）、NotificationListener（事件监听）都属于功能型Widget。由于Widget是Flutter的一等公民，功能型Widget非常多，我们不会去一一介绍，本章中主要介绍几种常用的功能型Widget。</p> <h2 id=\"本章目录\"><a href=\"#本章目录\" class=\"header-anchor\">#</a> 本章目录</h2> <ul><li><a href=\"/v2/chapter7/willpopscope.html\">7.1：导航返回拦截（WillPopScope）</a></li> <li><a href=\"/v2/chapter7/inherited_widget.html\">7.2：数据共享（InheritedWidget）</a></li> <li><a href=\"/v2/chapter7/provider.html\">7.3： 跨组件状态共享（Provider）</a></li> <li><a href=\"/v2/chapter7/theme.html\">7.4：颜色和主题（Theme）</a></li> <li><a href=\"/v2/chapter7/futurebuilder_and_streambuilder.html\">7.5：异步UI更新（FutureBuilder、StreamBuilder）</a></li> <li><a href=\"/v2/chapter7/dailog.html\">7.6：对话框详解</a></li></ul></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/214.0836f39e.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter7/inherited_widget.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>7.2 数据共享（InheritedWidget） | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/126.a62ac904.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter7/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_7-2-数据共享-inheritedwidget\"><a href=\"#_7-2-数据共享-inheritedwidget\" class=\"header-anchor\">#</a> 7.2 数据共享（InheritedWidget）</h1> <p><code>InheritedWidget</code>是Flutter中非常重要的一个功能型组件，它提供了一种数据在widget树中从上到下传递、共享的方式，比如我们在应用的根widget中通过<code>InheritedWidget</code>共享了一个数据，那么我们便可以在任意子widget中来获取该共享的数据！这个特性在一些需要在widget树中共享数据的场景中非常方便！如Flutter SDK中正是通过InheritedWidget来共享应用主题（<code>Theme</code>）和Locale (当前语言环境)信息的。</p> <blockquote><p><code>InheritedWidget</code>和React中的context功能类似，和逐级传递数据相比，它们能实现组件跨级传递数据。<code>InheritedWidget</code>的在widget树中数据传递方向是从上到下的，这和通知<code>Notification</code>（将在下一章中介绍）的传递方向正好相反。</p></blockquote> <h3 id=\"didchangedependencies\"><a href=\"#didchangedependencies\" class=\"header-anchor\">#</a> didChangeDependencies</h3> <p>在之前介绍<code>StatefulWidget</code>时，我们提到<code>State</code>对象有一个<code>didChangeDependencies</code>回调，它会在“依赖”发生变化时被Flutter Framework调用。而这个“依赖”指的就是子widget是否使用了父widget中<code>InheritedWidget</code>的数据！如果使用了，则代表子widget依赖有依赖<code>InheritedWidget</code>；如果没有使用则代表没有依赖。这种机制可以使子组件在所依赖的<code>InheritedWidget</code>变化时来更新自身！比如当主题、locale(语言)等发生变化时，依赖其的子widget的<code>didChangeDependencies</code>方法将会被调用。</p> <p>下面我们看一下之前“计数器”示例应用程序的<code>InheritedWidget</code>版本。需要说明的是，本示例主要是为了演示<code>InheritedWidget</code>的功能特性，并不是计数器的推荐实现方式。</p> <p>首先，我们通过继承<code>InheritedWidget</code>，将当前计数器点击次数保存在<code>ShareDataWidget</code>的<code>data</code>属性中：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">ShareDataWidget</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">InheritedWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">ShareDataWidget</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">,</span>\n    Widget child\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span><span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">:</span> child<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    \n  <span class=\"token keyword\">final</span> int data<span class=\"token punctuation\">;</span> <span class=\"token comment\">//需要在子树中共享的数据，保存点击次数</span>\n    \n  <span class=\"token comment\">//定义一个便捷方法，方便子树中的widget获取共享数据  </span>\n  <span class=\"token keyword\">static</span> ShareDataWidget <span class=\"token function\">of</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> context<span class=\"token punctuation\">.</span>dependOnInheritedWidgetOfExactType<span class=\"token operator\">&lt;</span>ShareDataWidget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">//该回调决定当data发生变化时，是否通知子树中依赖data的Widget  </span>\n  <span class=\"token metadata symbol\">@override</span>\n  bool <span class=\"token function\">updateShouldNotify</span><span class=\"token punctuation\">(</span>ShareDataWidget old<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//如果返回true，则子树中依赖(build函数中有调用)本widget</span>\n    <span class=\"token comment\">//的子widget的`state.didChangeDependencies`会被调用</span>\n    <span class=\"token keyword\">return</span> old<span class=\"token punctuation\">.</span>data <span class=\"token operator\">!=</span> data<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>然后我们实现一个子组件<code>_TestWidget</code>，在其<code>build</code>方法中引用<code>ShareDataWidget</code>中的数据。同时，在其<code>didChangeDependencies()</code> 回调中打印日志：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">_TestWidget</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  __TestWidgetState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">__TestWidgetState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">__TestWidgetState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>_TestWidget<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//使用InheritedWidget中的共享数据</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>ShareDataWidget\n        <span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">.</span>data\n        <span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">didChangeDependencies</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">didChangeDependencies</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//父或祖先widget中的InheritedWidget改变(updateShouldNotify返回true)时会被调用。</span>\n    <span class=\"token comment\">//如果build中没有依赖InheritedWidget，则此回调不会被调用。</span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Dependencies change&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>最后，我们创建一个按钮，每点击一次，就将<code>ShareDataWidget</code>的值自增：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">InheritedWidgetTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _InheritedWidgetTestRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_InheritedWidgetTestRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_InheritedWidgetTestRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>InheritedWidgetTestRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  int count <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span>  <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">ShareDataWidget</span><span class=\"token punctuation\">(</span> <span class=\"token comment\">//使用ShareDataWidget</span>\n        data<span class=\"token punctuation\">:</span> count<span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n          mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n              padding<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">only</span><span class=\"token punctuation\">(</span>bottom<span class=\"token punctuation\">:</span> <span class=\"token number\">20.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">_TestWidget</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token comment\">//子widget中依赖ShareDataWidget</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Increment&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token comment\">//每点击一次，将count自增，然后重新build,ShareDataWidget的data将被更新  </span>\n              onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token operator\">++</span>count<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行后界面如图7-1所示：</p> <p><img src=\"/assets/img/7-1.fc1ee2fb.png\" alt=\"图7-1\"></p> <p>每点击一次按钮，计数器就会自增，控制台就会打印一句日志：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>I/flutter ( 8513): Dependencies change\n</code></pre></div><p>可见依赖发生变化后，其<code>didChangeDependencies()</code>会被调用。但是读者要注意，<strong>如果_TestWidget的build方法中没有使用ShareDataWidget的数据，那么它的<code>didChangeDependencies()</code>将不会被调用，因为它并没有依赖ShareDataWidget</strong>。例如，我们将<code>__TestWidgetState</code>代码改为下面这样，<code>didChangeDependencies()</code>将不会被调用:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">__TestWidgetState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>_TestWidget<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 使用InheritedWidget中的共享数据</span>\n    <span class=\"token comment\">//    return Text(ShareDataWidget</span>\n    <span class=\"token comment\">//        .of(context)</span>\n    <span class=\"token comment\">//        .data</span>\n    <span class=\"token comment\">//        .toString());</span>\n     <span class=\"token keyword\">return</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;text&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">didChangeDependencies</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">didChangeDependencies</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">// build方法中没有依赖InheritedWidget，此回调不会被调用。</span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Dependencies change&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面的代码中，我们将<code>build()</code>方法中依赖<code>ShareDataWidget</code>的代码注释掉了，然后返回一个固定<code>Text</code>，这样一来，当点击Increment按钮后，<code>ShareDataWidget</code>的<code>data</code>虽然发生变化，但由于<code>__TestWidgetState</code>并未依赖<code>ShareDataWidget</code>，所以<code>__TestWidgetState</code>的<code>didChangeDependencies</code>方法不会被调用。其实，这个机制很好理解，因为在数据发生变化时只对使用该数据的Widget更新是合理并且性能友好的。</p> <blockquote><p>思考题：Flutter framework是怎么知道子widget有没有依赖InheritedWidget的？</p></blockquote> <h4 id=\"应该在didchangedependencies-中做什么\"><a href=\"#应该在didchangedependencies-中做什么\" class=\"header-anchor\">#</a> 应该在didChangeDependencies()中做什么？</h4> <p>一般来说，子widget很少会重写此方法，因为在依赖改变后framework也都会调用<code>build()</code>方法。但是，如果你需要在依赖改变后执行一些昂贵的操作，比如网络请求，这时最好的方式就是在此方法中执行，这样可以避免每次<code>build()</code>都执行这些昂贵操作。</p> <h3 id=\"深入了解inheritedwidget\"><a href=\"#深入了解inheritedwidget\" class=\"header-anchor\">#</a> 深入了解InheritedWidget</h3> <p>现在来思考一下，如果我们只想在<code>__TestWidgetState</code>中引用<code>ShareDataWidget</code>数据，但却不希望在<code>ShareDataWidget</code>发生变化时调用<code>__TestWidgetState</code>的<code>didChangeDependencies()</code>方法应该怎么办？其实答案很简单，我们只需要将<code>ShareDataWidget.of()</code>的实现改一下即可：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//定义一个便捷方法，方便子树中的widget获取共享数据</span>\n<span class=\"token keyword\">static</span> ShareDataWidget <span class=\"token function\">of</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">//return context.dependOnInheritedWidgetOfExactType&lt;ShareDataWidget&gt;();</span>\n  <span class=\"token keyword\">return</span> context<span class=\"token punctuation\">.</span>getElementForInheritedWidgetOfExactType<span class=\"token operator\">&lt;</span>ShareDataWidget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>widget<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>唯一的改动就是获取<code>ShareDataWidget</code>对象的方式，把<code>dependOnInheritedWidgetOfExactType()</code>方法换成了<code>context.getElementForInheritedWidgetOfExactType&lt;ShareDataWidget&gt;().widget</code>，那么他们到底有什么区别呢，我们看一下这两个方法的源码（实现代码在<code>Element</code>类中，<code>Context</code>和<code>Element</code>的关系我们将在后面专门介绍）：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token metadata symbol\">@override</span>\nInheritedElement getElementForInheritedWidgetOfExactType<span class=\"token operator\">&lt;</span>T <span class=\"token keyword\">extends</span> <span class=\"token class-name\">InheritedWidget</span><span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span><span class=\"token function\">_debugCheckStateIsActiveForAncestorLookup</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> InheritedElement ancestor <span class=\"token operator\">=</span> _inheritedWidgets <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">?</span> <span class=\"token keyword\">null</span> <span class=\"token punctuation\">:</span> _inheritedWidgets<span class=\"token punctuation\">[</span>T<span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">return</span> ancestor<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n<span class=\"token metadata symbol\">@override</span>\nInheritedWidget <span class=\"token function\">dependOnInheritedWidgetOfExactType</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> Object aspect <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span><span class=\"token function\">_debugCheckStateIsActiveForAncestorLookup</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> InheritedElement ancestor <span class=\"token operator\">=</span> _inheritedWidgets <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">?</span> <span class=\"token keyword\">null</span> <span class=\"token punctuation\">:</span> _inheritedWidgets<span class=\"token punctuation\">[</span>T<span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">//多出的部分</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>ancestor <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span>ancestor <span class=\"token operator\">is</span> InheritedElement<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">dependOnInheritedElement</span><span class=\"token punctuation\">(</span>ancestor<span class=\"token punctuation\">,</span> aspect<span class=\"token punctuation\">:</span> aspect<span class=\"token punctuation\">)</span> <span class=\"token operator\">as</span> T<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  _hadUnsatisfiedDependencies <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">return</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们可以看到，<code>dependOnInheritedWidgetOfExactType()</code> 比 <code>getElementForInheritedWidgetOfExactType()</code>多调了<code>dependOnInheritedElement</code>方法，<code>dependOnInheritedElement</code>源码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>  <span class=\"token metadata symbol\">@override</span>\n  InheritedWidget <span class=\"token function\">dependOnInheritedElement</span><span class=\"token punctuation\">(</span>InheritedElement ancestor<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> Object aspect <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span>ancestor <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    _dependencies <span class=\"token operator\">?</span><span class=\"token operator\">?</span><span class=\"token operator\">=</span> HashSet<span class=\"token operator\">&lt;</span>InheritedElement<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    _dependencies<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span>ancestor<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    ancestor<span class=\"token punctuation\">.</span><span class=\"token function\">updateDependencies</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">,</span> aspect<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> ancestor<span class=\"token punctuation\">.</span>widget<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以看到<code>dependOnInheritedElement</code>方法中主要是注册了依赖关系！看到这里也就清晰了，<strong>调用<code>dependOnInheritedWidgetOfExactType()</code> 和 <code>getElementForInheritedWidgetOfExactType()</code>的区别就是前者会注册依赖关系，而后者不会</strong>，所以在调用<code>dependOnInheritedWidgetOfExactType()</code>时，<code>InheritedWidget</code>和依赖它的子孙组件关系便完成了注册，之后当<code>InheritedWidget</code>发生变化时，就会更新依赖它的子孙组件，也就是会调这些子孙组件的<code>didChangeDependencies()</code>方法和<code>build()</code>方法。而当调用的是 <code>getElementForInheritedWidgetOfExactType()</code>时，由于没有注册依赖关系，所以之后当<code>InheritedWidget</code>发生变化时，就不会更新相应的子孙Widget。</p> <p>注意，如果将上面示例中<code>ShareDataWidget.of()</code>方法实现改成调用<code>getElementForInheritedWidgetOfExactType()</code>，运行示例后，点击&quot;Increment&quot;按钮，会发现<code>__TestWidgetState</code>的<code>didChangeDependencies()</code>方法确实不会再被调用，但是其<code>build()</code>仍然会被调用！造成这个的原因其实是，点击&quot;Increment&quot;按钮后，会调用<code>_InheritedWidgetTestRouteState</code>的<code>setState()</code>方法，此时会重新构建整个页面，由于示例中，<code>__TestWidget</code> 并没有任何缓存，所以它也都会被重新构建，所以也会调用<code>build()</code>方法。</p> <p>那么，现在就带来了一个问题：实际上，我们只想更新子树中依赖了<code>ShareDataWidget</code>的组件，而现在只要调用<code>_InheritedWidgetTestRouteState</code>的<code>setState()</code>方法，所有子节点都会被重新build，这很没必要，那么有什么办法可以避免呢？答案是缓存！一个简单的做法就是通过封装一个<code>StatefulWidget</code>，将子Widget树缓存起来，具体做法下一节我们将通过实现一个<code>Provider</code> Widget 来演示如何缓存，以及如何利用<code>InheritedWidget</code> 来实现Flutter全局状态共享。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/126.a62ac904.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter7/provider.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>7.3 跨组件状态共享（Provider） | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/78.28647d83.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter7/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_7-3-跨组件状态共享-provider\"><a href=\"#_7-3-跨组件状态共享-provider\" class=\"header-anchor\">#</a> 7.3 跨组件状态共享（Provider）</h1> <p>在Flutter开发中，状态管理是一个永恒的话题。一般的原则是：如果状态是组件私有的，则应该由组件自己管理；如果状态要跨组件共享，则该状态应该由各个组件共同的父元素来管理。对于组件私有的状态管理很好理解，但对于跨组件共享的状态，管理的方式就比较多了，如使用全局事件总线EventBus（将在下一章中介绍），它是一个观察者模式的实现，通过它就可以实现跨组件状态同步：状态持有方（发布者）负责更新、发布状态，状态使用方（观察者）监听状态改变事件来执行一些操作。下面我们看一个登陆状态同步的简单示例：</p> <p>定义事件：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">enum</span> Event<span class=\"token punctuation\">{</span>\n  login<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略其它事件</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>登录页代码大致如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">// 登录状态改变后发布状态改变事件</span>\nbus<span class=\"token punctuation\">.</span><span class=\"token function\">emit</span><span class=\"token punctuation\">(</span>Event<span class=\"token punctuation\">.</span>login<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>依赖登录状态的页面：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">onLoginChanged</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">//登录状态变化处理逻辑</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token metadata symbol\">@override</span>\n<span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">//订阅登录状态改变事件</span>\n  bus<span class=\"token punctuation\">.</span><span class=\"token keyword\">on</span><span class=\"token punctuation\">(</span>Event<span class=\"token punctuation\">.</span>login<span class=\"token punctuation\">,</span>onLogin<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token metadata symbol\">@override</span>\n<span class=\"token keyword\">void</span> <span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">//取消订阅</span>\n  bus<span class=\"token punctuation\">.</span><span class=\"token function\">off</span><span class=\"token punctuation\">(</span>Event<span class=\"token punctuation\">.</span>login<span class=\"token punctuation\">,</span>onLogin<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们可以发现，通过观察者模式来实现跨组件状态共享有一些明显的缺点：</p> <ol><li>必须显式定义各种事件，不好管理</li> <li>订阅者必须需显式注册状态改变回调，也必须在组件销毁时手动去解绑回调以避免内存泄露。</li></ol> <p>在Flutter当中有没有更好的跨组件状态管理方式了呢？答案是肯定的，那怎么做的？我们想想前面介绍的<code>InheritedWidget</code>，它的天生特性就是能绑定<code>InheritedWidget</code>与依赖它的子孙组件的依赖关系，并且当<code>InheritedWidget</code>数据发生变化时，可以自动更新依赖的子孙组件！利用这个特性，我们可以将需要跨组件共享的状态保存在<code>InheritedWidget</code>中，然后在子组件中引用<code>InheritedWidget</code>即可，Flutter社区著名的Provider包正是基于这个思想实现的一套跨组件状态共享解决方案，接下来我们便详细介绍一下Provider的用法及原理。</p> <h2 id=\"provider\"><a href=\"#provider\" class=\"header-anchor\">#</a> Provider</h2> <p>为了加强读者的理解，我们不直接去看Provider包的源代码，相反，我会带着你根据上面描述的通过<code>InheritedWidget</code>实现的思路来一步一步地实现一个最小功能的Provider。</p> <p>首先，我们需要一个保存需要共享的数据<code>InheritedWidget</code>，由于具体业务数据类型不可预期，为了通用性，我们使用泛型，定义一个通用的<code>InheritedProvider</code>类，它继承自<code>InheritedWidget</code>：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">// 一个通用的InheritedWidget，保存任需要跨组件共享的状态</span>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">InheritedProvider</span><span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">InheritedWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">InheritedProvider</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span><span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">,</span> Widget child<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">:</span> child<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  \n  <span class=\"token comment\">//共享状态使用泛型</span>\n  <span class=\"token keyword\">final</span> T data<span class=\"token punctuation\">;</span>\n  \n  <span class=\"token metadata symbol\">@override</span>\n  bool <span class=\"token function\">updateShouldNotify</span><span class=\"token punctuation\">(</span>InheritedProvider<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span> old<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//在此简单返回true，则每次更新都会调用依赖其的子孙节点的`didChangeDependencies`。</span>\n    <span class=\"token keyword\">return</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>数据保存的地方有了，那么接下来我们需要做的就是在数据发生变化的时候来重新构建<code>InheritedProvider</code>，那么现在就面临两个问题：</p> <ol><li>数据发生变化怎么通知？</li> <li>谁来重新构建<code>InheritedProvider</code>？</li></ol> <p>第一个问题其实很好解决，我们当然可以使用之前介绍的eventBus来进行事件通知，但是为了更贴近Flutter开发，我们使用Flutter SDK中提供的<code>ChangeNotifier</code>类 ，它继承自<code>Listenable</code>，也实现了一个Flutter风格的发布者-订阅者模式，<code>ChangeNotifier</code>定义大致如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">ChangeNotifier</span> <span class=\"token keyword\">implements</span> <span class=\"token class-name\">Listenable</span> <span class=\"token punctuation\">{</span>\n  List listeners<span class=\"token operator\">=</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span>VoidCallback listener<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n     <span class=\"token comment\">//添加监听器</span>\n     listeners<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span>listener<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">removeListener</span><span class=\"token punctuation\">(</span>VoidCallback listener<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//移除监听器</span>\n    listeners<span class=\"token punctuation\">.</span><span class=\"token function\">remove</span><span class=\"token punctuation\">(</span>listener<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  \n  <span class=\"token keyword\">void</span> <span class=\"token function\">notifyListeners</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//通知所有监听器，触发监听器回调 </span>\n    listeners<span class=\"token punctuation\">.</span><span class=\"token function\">forEach</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>item<span class=\"token punctuation\">)</span><span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span><span class=\"token function\">item</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n   \n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span> <span class=\"token comment\">//省略无关代码</span>\n<span class=\"token punctuation\">}</span>\n\n</code></pre></div><p>我们可以通过调用<code>addListener()</code>和<code>removeListener()</code>来添加、移除监听器（订阅者）；通过调用<code>notifyListeners()</code> 可以触发所有监听器回调。</p> <p>现在，我们将要共享的状态放到一个Model类中，然后让它继承自<code>ChangeNotifier</code>，这样当共享的状态改变时，我们只需要调用<code>notifyListeners()</code> 来通知订阅者，然后由订阅者来重新构建<code>InheritedProvider</code>，这也是第二个问题的答案！接下来我们便实现这个订阅者类：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">ChangeNotifierProvider</span><span class=\"token operator\">&lt;</span>T <span class=\"token keyword\">extends</span> <span class=\"token class-name\">ChangeNotifier</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">ChangeNotifierProvider</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> Widget child<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> T data<span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">//定义一个便捷方法，方便子树中的widget获取共享数据</span>\n  <span class=\"token keyword\">static</span> T of<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">final</span> type <span class=\"token operator\">=</span> _typeOf<span class=\"token operator\">&lt;</span>InheritedProvider<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">final</span> provider <span class=\"token operator\">=</span>  context<span class=\"token punctuation\">.</span>dependOnInheritedWidgetOfExactType<span class=\"token operator\">&lt;</span>InheritedProvider<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> provider<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  _ChangeNotifierProviderState<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span> <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _ChangeNotifierProviderState<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>该类继承<code>StatefulWidget</code>，然后定义了一个<code>of()</code>静态方法供子类方便获取Widget树中的<code>InheritedProvider</code>中保存的共享状态(model)，下面我们实现该类对应的<code>_ChangeNotifierProviderState</code>类：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">_ChangeNotifierProviderState</span><span class=\"token operator\">&lt;</span>T <span class=\"token keyword\">extends</span> <span class=\"token class-name\">ChangeNotifier</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>ChangeNotifierProvider<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">update</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//如果数据发生变化（model类调用了notifyListeners），重新构建InheritedProvider</span>\n    <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">didUpdateWidget</span><span class=\"token punctuation\">(</span>ChangeNotifierProvider<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span> oldWidget<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//当Provider更新时，如果新旧数据不&quot;==&quot;，则解绑旧数据监听，同时添加新数据监听</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>widget<span class=\"token punctuation\">.</span>data <span class=\"token operator\">!=</span> oldWidget<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      oldWidget<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">.</span><span class=\"token function\">removeListener</span><span class=\"token punctuation\">(</span>update<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      widget<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span>update<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">didUpdateWidget</span><span class=\"token punctuation\">(</span>oldWidget<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 给model添加监听器</span>\n    widget<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span>update<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 移除model的监听器</span>\n    widget<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">.</span><span class=\"token function\">removeListener</span><span class=\"token punctuation\">(</span>update<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> InheritedProvider<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n      data<span class=\"token punctuation\">:</span> widget<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> widget<span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以看到<code>_ChangeNotifierProviderState</code>类的主要作用就是监听到共享状态（model）改变时重新构建Widget树。注意，在<code>_ChangeNotifierProviderState</code>类中调用<code>setState()</code>方法，<code>widget.child</code>始终是同一个，所以执行build时，<code>InheritedProvider</code>的child引用的始终是同一个子widget，所以<code>widget.child</code>并不会重新<code>build</code>，这也就相当于对<code>child</code>进行了缓存！当然如果<code>ChangeNotifierProvider</code>父级Widget重新build时，则其传入的<code>child</code>便有可能会发生变化。</p> <p>现在我们所需要的各个工具类都已完成，下面我们通过一个购物车的例子来看看怎么使用上面的这些类。</p> <h3 id=\"购物车示例\"><a href=\"#购物车示例\" class=\"header-anchor\">#</a> 购物车示例</h3> <p>我们需要实现一个显示购物车中所有商品总价的功能：</p> <ol><li>向购物车中添加新商品时总价更新</li></ol> <p>定义一个<code>Item</code>类，用于表示商品信息：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">Item</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">Item</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>price<span class=\"token punctuation\">,</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>count<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  double price<span class=\"token punctuation\">;</span> <span class=\"token comment\">//商品单价</span>\n  int count<span class=\"token punctuation\">;</span> <span class=\"token comment\">// 商品份数</span>\n  <span class=\"token comment\">//... 省略其它属性</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>定义一个保存购物车内商品数据的<code>CartModel</code>类:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">CartModel</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">ChangeNotifier</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// 用于保存购物车中商品列表</span>\n  <span class=\"token keyword\">final</span> List<span class=\"token operator\">&lt;</span>Item<span class=\"token operator\">&gt;</span> _items <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// 禁止改变购物车里的商品信息</span>\n  UnmodifiableListView<span class=\"token operator\">&lt;</span>Item<span class=\"token operator\">&gt;</span> <span class=\"token keyword\">get</span> items <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">UnmodifiableListView</span><span class=\"token punctuation\">(</span>_items<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// 购物车中商品的总价</span>\n  double <span class=\"token keyword\">get</span> totalPrice <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span>\n      _items<span class=\"token punctuation\">.</span><span class=\"token function\">fold</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span>value<span class=\"token punctuation\">,</span> item<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> value <span class=\"token operator\">+</span> item<span class=\"token punctuation\">.</span>count <span class=\"token operator\">*</span> item<span class=\"token punctuation\">.</span>price<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// 将 [item] 添加到购物车。这是唯一一种能从外部改变购物车的方法。</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">add</span><span class=\"token punctuation\">(</span>Item item<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    _items<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span>item<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">// 通知监听器（订阅者），重新构建InheritedProvider， 更新状态。</span>\n    <span class=\"token function\">notifyListeners</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p><code>CartModel</code>即要跨组件共享的model类。最后我们构建示例页面：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">ProviderRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _ProviderRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">_ProviderRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_ProviderRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>ProviderRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> ChangeNotifierProvider<span class=\"token operator\">&lt;</span>CartModel<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n        data<span class=\"token punctuation\">:</span> <span class=\"token function\">CartModel</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">return</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n            children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n              <span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n                <span class=\"token keyword\">var</span> cart<span class=\"token operator\">=</span>ChangeNotifierProvider<span class=\"token punctuation\">.</span>of<span class=\"token operator\">&lt;</span>CartModel<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                <span class=\"token keyword\">return</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;总价: ${cart.totalPrice}&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n                <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;RaisedButton build&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//在后面优化部分会用到</span>\n                <span class=\"token keyword\">return</span> <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n                  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;添加商品&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                    <span class=\"token comment\">//给购物车中添加商品，添加后总价会更新</span>\n                    ChangeNotifierProvider<span class=\"token punctuation\">.</span>of<span class=\"token operator\">&lt;</span>CartModel<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token function\">Item</span><span class=\"token punctuation\">(</span><span class=\"token number\">20.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行示例后效果如图7-2所示：</p> <p><img src=\"/assets/img/7-2.20458eff.png\" alt=\"provider\"></p> <p>每次点击”添加商品“按钮，总价就会增加20，我们期望的功能实现了！可能有些读者会疑惑，我们饶了一大圈实现这么简单的功能有意义么？其实，就这个例子来看，只是更新同一个路由页中的一个状态，我们使用<code>ChangeNotifierProvider</code>的优势并不明显，但是如果我们是做一个购物APP呢？由于购物车数据是通常是会在整个APP中共享的，比如会跨路由共享。如果我们将<code>ChangeNotifierProvider</code>放在整个应用的Widget树的根上，那么整个APP就可以共享购物车的数据了，这时<code>ChangeNotifierProvider</code>的优势将会非常明显。</p> <p>虽然上面的例子比较简单，但它却将Provider的原理和流程体现的很清楚，图7-3是Provider的原理图：</p> <p><img src=\"/assets/img/7-3.531c5fdf.png\" alt=\"图7-3\"></p> <p>Model变化后会自动通知<code>ChangeNotifierProvider</code>（订阅者），<code>ChangeNotifierProvider</code>内部会重新构建<code>InheritedWidget</code>，而依赖该<code>InheritedWidget</code>的子孙Widget就会更新。</p> <p>我们可以发现使用Provider，将会带来如下收益：</p> <ol><li>我们的业务代码更关注数据了，只要更新Model，则UI会自动更新，而不用在状态改变后再去手动调用<code>setState()</code>来显式更新页面。</li> <li>数据改变的消息传递被屏蔽了，我们无需手动去处理状态改变事件的发布和订阅了，这一切都被封装在Provider中了。这真的很棒，帮我们省掉了大量的工作！</li> <li>在大型复杂应用中，尤其是需要全局共享的状态非常多时，使用Provider将会大大简化我们的代码逻辑，降低出错的概率，提高开发效率。</li></ol> <h3 id=\"优化\"><a href=\"#优化\" class=\"header-anchor\">#</a> 优化</h3> <p>我们上面实现的<code>ChangeNotifierProvider</code>是有两个明显缺点：代码组织问题和性能问题，下面我们一一讨论。</p> <h4 id=\"代码组织问题\"><a href=\"#代码组织问题\" class=\"header-anchor\">#</a> 代码组织问题</h4> <p>我们先看一下构建显示总价Text的代码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">var</span> cart<span class=\"token operator\">=</span>ChangeNotifierProvider<span class=\"token punctuation\">.</span>of<span class=\"token operator\">&lt;</span>CartModel<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;总价: ${cart.totalPrice}&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>这段代码有两点可以优化：</p> <ol><li>需要显式调用<code>ChangeNotifierProvider.of</code>，当APP内部依赖<code>CartModel</code>很多时，这样的代码将很冗余。</li> <li>语义不明确；由于<code>ChangeNotifierProvider</code>是订阅者，那么依赖<code>CartModel</code>的Widget自然就是订阅者，其实也就是状态的消费者，如果我们用<code>Builder</code> 来构建，语义就不是很明确；如果我们能使用一个具有明确语义的Widget，比如就叫<code>Consumer</code>，这样最终的代码语义将会很明确，只要看到<code>Consumer</code>，我们就知道它是依赖某个跨组件或全局的状态。</li></ol> <p>为了优化这两个问题，我们可以封装一个<code>Consumer</code> Widget，实现如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">// 这是一个便捷类，会获得当前context和指定数据类型的Provider</span>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">Consumer</span><span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">Consumer</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n    <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>builder<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>  <span class=\"token punctuation\">:</span> <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span>builder <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> Widget child<span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> Widget <span class=\"token keyword\">Function</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> T value<span class=\"token punctuation\">)</span> builder<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>\n      context<span class=\"token punctuation\">,</span>\n      ChangeNotifierProvider<span class=\"token punctuation\">.</span>of<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//自动获取Model</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p><code>Consumer</code>实现非常简单，它通过指定模板参数，然后再内部自动调用<code>ChangeNotifierProvider.of</code>获取相应的Model，并且<code>Consumer</code>这个名字本身也是具有确切语义（消费者）。现在上面的代码块可以优化为如下这样：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Consumer<span class=\"token operator\">&lt;</span>CartModel<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n  builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> cart<span class=\"token punctuation\">)</span><span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;总价: ${cart.totalPrice}&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>是不是很优雅！</p> <h4 id=\"性能问题\"><a href=\"#性能问题\" class=\"header-anchor\">#</a> 性能问题</h4> <p>上面的代码还有一个性能问题，就在构建”添加按钮“的代码处：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;RaisedButton build&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// 构建时输出日志</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;添加商品&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      ChangeNotifierProvider<span class=\"token punctuation\">.</span>of<span class=\"token operator\">&lt;</span>CartModel<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token function\">Item</span><span class=\"token punctuation\">(</span><span class=\"token number\">20.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们点击”添加商品“按钮后，由于购物车商品总价会变化，所以显示总价的Text更新是符合预期的，但是”添加商品“按钮本身没有变化，是不应该被重新build的。但是我们运行示例，每次点击”添加商品“按钮，控制台都会输出&quot;RaisedButton build&quot;日志，也就是说”添加商品“按钮在每次点击时其自身都会重新build！这是为什么呢？如果你已经理解了<code>InheritedWidget</code>的更新机制，那么答案一眼就能看出：这是因为构建<code>RaisedButton</code>的<code>Builder</code>中调用了<code>ChangeNotifierProvider.of</code>，也就是说依赖了Widget树上面的<code>InheritedWidget</code>（即<code>InheritedProvider</code> ）Widget，所以当添加完商品后，<code>CartModel</code>发生变化，会通知<code>ChangeNotifierProvider</code>, 而<code>ChangeNotifierProvider</code>则会重新构建子树，所以<code>InheritedProvider</code>将会更新，此时依赖它的子孙Widget就会被重新构建。</p> <p>问题的原因搞清楚了，那么我们如何避免这不必要重构呢？既然按钮重新被build是因为按钮和<code>InheritedWidget</code>建立了依赖关系，那么我们只要打破或解除这种依赖关系就可以了。那么如何解除按钮和<code>InheritedWidget</code>的依赖关系呢？我们上一节介绍<code>InheritedWidget</code>时已经讲过了：调用<code>dependOnInheritedWidgetOfExactType()</code> 和 <code>getElementForInheritedWidgetOfExactType()</code>的区别就是前者会注册依赖关系，而后者不会。所以我们只需要将<code>ChangeNotifierProvider.of</code>的实现改为下面这样即可：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code> <span class=\"token comment\">//添加一个listen参数，表示是否建立依赖关系</span>\n  <span class=\"token keyword\">static</span> T of<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>bool listen <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">final</span> type <span class=\"token operator\">=</span> _typeOf<span class=\"token operator\">&lt;</span>InheritedProvider<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">final</span> provider <span class=\"token operator\">=</span> listen\n        <span class=\"token operator\">?</span> context<span class=\"token punctuation\">.</span>dependOnInheritedWidgetOfExactType<span class=\"token operator\">&lt;</span>InheritedProvider<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">:</span> context<span class=\"token punctuation\">.</span>getElementForInheritedWidgetOfExactType<span class=\"token operator\">&lt;</span>InheritedProvider<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">?</span><span class=\"token punctuation\">.</span>widget\n            <span class=\"token operator\">as</span> InheritedProvider<span class=\"token operator\">&lt;</span>T<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> provider<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n</code></pre></div><p>然后我们将调用部分代码改为：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n    children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n      Consumer<span class=\"token operator\">&lt;</span>CartModel<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n        builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> cart<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span><span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;总价: ${cart.totalPrice}&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;RaisedButton build&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">return</span> <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;添加商品&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">// listen 设为false，不建立依赖关系</span>\n            ChangeNotifierProvider<span class=\"token punctuation\">.</span>of<span class=\"token operator\">&lt;</span>CartModel<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> listen<span class=\"token punctuation\">:</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span>\n                <span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token function\">Item</span><span class=\"token punctuation\">(</span><span class=\"token number\">20.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span>\n</code></pre></div><p>修改后再次运行上面的示例，我们会发现点击”添加商品“按钮后，控制台不会再输出&quot;RaisedButton build&quot;了，即按钮不会被重新构建了。而总价仍然会更新，这是因为<code>Consumer</code>中调用<code>ChangeNotifierProvider.of</code>时<code>listen</code>值为默认值true，所以还是会建立依赖关系。</p> <p>至此我们便实现了一个迷你的Provider，它具备Pub上Provider Package中的核心功能；但是我们的迷你版功能并不全面，如只实现了一个可监听的ChangeNotifierProvider，并没有实现只用于数据共享的Provider；另外，我们的实现有些边界也没有考虑的到，比如如何保证在Widget树重新build时Model始终是单例等。所以建议读者在实战中还是使用Provider Package，而本节实现这个迷你Provider的主要目的主要是为了帮助读者了解Provider Package底层的原理。</p> <h3 id=\"其它状态管理包\"><a href=\"#其它状态管理包\" class=\"header-anchor\">#</a> 其它状态管理包</h3> <p>现在Flutter社区已经有很多专门用于状态管理的包了，在此我们列出几个相对评分比较高的：</p> <table><thead><tr><th>包名</th> <th>介绍</th></tr></thead> <tbody><tr><td><a href=\"https://pub.flutter-io.cn/packages/provider\" target=\"_blank\" rel=\"noopener noreferrer\">Provider<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> &amp; <a href=\"https://pub.flutter-io.cn/packages/scoped_model\" target=\"_blank\" rel=\"noopener noreferrer\">Scoped Model<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></td> <td>这两个包都是基于<code>InheritedWidget</code>的，原理相似</td></tr> <tr><td><a href=\"https://pub.flutter-io.cn/packages/flutter_redux\" target=\"_blank\" rel=\"noopener noreferrer\">Redux<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></td> <td>是Web开发中React生态链中Redux包的Flutter实现</td></tr> <tr><td><a href=\"https://pub.dev/packages/flutter_mobx\" target=\"_blank\" rel=\"noopener noreferrer\">MobX<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></td> <td>是Web开发中React生态链中MobX包的Flutter实现</td></tr> <tr><td><a href=\"https://pub.dev/packages/flutter_bloc\" target=\"_blank\" rel=\"noopener noreferrer\">BLoC<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></td> <td>是BLoC模式的Flutter实现</td></tr></tbody></table> <p>在此笔者不对这些包做推荐，读者有兴趣都可以研究一下，了解它们各自的思想。</p> <h3 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h3> <p>本节通过介绍事件总线在跨组件共享中的一些缺点引出了通过<code>InheritedWidget</code>来实现状态的共享的思想，然后基于该思想实现了一个简单的Provider，在实现的过程中也更深入的探索了<code>InheritedWidget</code>与其依赖项的注册机制和更新机制。通过本节的学习，读者应该达到两个目标，首先是对<code>InheritedWidget</code>彻底吃透，其次是Provider的设计思想。</p> <p><code>InheritedWidget</code>是Flutter中非常重要的一个Widget，像国际化、主题等都是通过它来实现，所以我们也不惜篇幅，通过好几节来介绍它的，在下一节中，我们将介绍另一个基于<code>InheritedWidget</code>的组件Theme(主题)。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/78.28647d83.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter7/theme.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>7.4 颜色和主题 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/37.7ecb6ef5.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter7/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_7-4-颜色和主题\"><a href=\"#_7-4-颜色和主题\" class=\"header-anchor\">#</a> 7.4 颜色和主题</h1> <h2 id=\"_7-4-1-颜色\"><a href=\"#_7-4-1-颜色\" class=\"header-anchor\">#</a> 7.4.1 颜色</h2> <p>在介绍主题前我们先了解一些Flutter中的Color类。Color类中颜色以一个int值保存，我们知道显示器颜色是由红、绿、蓝三基色组成，每种颜色占8比特，存储结构如下：</p> <table><thead><tr><th>Bit（位）</th> <th>颜色</th></tr></thead> <tbody><tr><td>0-7</td> <td>蓝色</td></tr> <tr><td>8-15</td> <td>绿色</td></tr> <tr><td>16-23</td> <td>红色</td></tr> <tr><td>24-31</td> <td>Alpha (不透明度)</td></tr></tbody></table> <p>上面表格中的的字段在Color类中都有对应的属性，而Color中的众多方法也就是操作这些属性的，由于大多比较简单，读者可以查看类定义了解。在此我们主要讨论两点：色值转换和亮度。</p> <h3 id=\"如何将颜色字符串转成color对象\"><a href=\"#如何将颜色字符串转成color对象\" class=\"header-anchor\">#</a> <strong>如何将颜色字符串转成Color对象</strong></h3> <p>如Web开发中的色值通常是一个字符串如&quot;#dc380d&quot;，它是一个RGB值，我们可以通过下面这些方法将其转为Color类：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Color</span><span class=\"token punctuation\">(</span><span class=\"token number\">0xffdc380d</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//如果颜色固定可以直接使用整数值</span>\n<span class=\"token comment\">//颜色是一个字符串变量</span>\n<span class=\"token keyword\">var</span> c <span class=\"token operator\">=</span> <span class=\"token string\">&quot;dc380d&quot;</span><span class=\"token punctuation\">;</span>\n<span class=\"token function\">Color</span><span class=\"token punctuation\">(</span>int<span class=\"token punctuation\">.</span><span class=\"token function\">parse</span><span class=\"token punctuation\">(</span>c<span class=\"token punctuation\">,</span>radix<span class=\"token punctuation\">:</span><span class=\"token number\">16</span><span class=\"token punctuation\">)</span><span class=\"token operator\">|</span><span class=\"token number\">0xFF000000</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//通过位运算符将Alpha设置为FF</span>\n<span class=\"token function\">Color</span><span class=\"token punctuation\">(</span>int<span class=\"token punctuation\">.</span><span class=\"token function\">parse</span><span class=\"token punctuation\">(</span>c<span class=\"token punctuation\">,</span>radix<span class=\"token punctuation\">:</span><span class=\"token number\">16</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">withAlpha</span><span class=\"token punctuation\">(</span><span class=\"token number\">255</span><span class=\"token punctuation\">)</span>  <span class=\"token comment\">//通过方法将Alpha设置为FF</span>\n</code></pre></div><h3 id=\"颜色亮度\"><a href=\"#颜色亮度\" class=\"header-anchor\">#</a> 颜色亮度</h3> <p>假如，我们要实现一个背景颜色和Title可以自定义的导航栏，并且背景色为深色时我们应该让Title显示为浅色；背景色为浅色时，Title显示为深色。要实现这个功能，我们就需要来计算背景色的亮度，然后动态来确定Title的颜色。Color类中提供了一个<code>computeLuminance()</code>方法，它可以返回一个[0-1]的一个值，数字越大颜色就越浅，我们可以根据它来动态确定Title的颜色，下面是导航栏NavBar的简单实现：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">NavBar</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">final</span> String title<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> Color color<span class=\"token punctuation\">;</span> <span class=\"token comment\">//背景颜色</span>\n\n  <span class=\"token function\">NavBar</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>color<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>title<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n      constraints<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxConstraints</span><span class=\"token punctuation\">(</span>\n        minHeight<span class=\"token punctuation\">:</span> <span class=\"token number\">52</span><span class=\"token punctuation\">,</span>\n        minWidth<span class=\"token punctuation\">:</span> double<span class=\"token punctuation\">.</span>infinity<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>\n        color<span class=\"token punctuation\">:</span> color<span class=\"token punctuation\">,</span>\n        boxShadow<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>\n          <span class=\"token comment\">//阴影</span>\n          <span class=\"token function\">BoxShadow</span><span class=\"token punctuation\">(</span>\n            color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>black26<span class=\"token punctuation\">,</span>\n            offset<span class=\"token punctuation\">:</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">3</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            blurRadius<span class=\"token punctuation\">:</span> <span class=\"token number\">3</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>\n        title<span class=\"token punctuation\">,</span>\n        style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>\n          fontWeight<span class=\"token punctuation\">:</span> FontWeight<span class=\"token punctuation\">.</span>bold<span class=\"token punctuation\">,</span>\n          <span class=\"token comment\">//根据背景色亮度来确定Title颜色</span>\n          color<span class=\"token punctuation\">:</span> color<span class=\"token punctuation\">.</span><span class=\"token function\">computeLuminance</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">&lt;</span> <span class=\"token number\">0.5</span> <span class=\"token operator\">?</span> Colors<span class=\"token punctuation\">.</span>white <span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>black<span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>测试代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token comment\">//背景为蓝色，则title自动为白色</span>\n    <span class=\"token function\">NavBar</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span> title<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;标题&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> \n    <span class=\"token comment\">//背景为白色，则title自动为黑色</span>\n    <span class=\"token function\">NavBar</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">,</span> title<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;标题&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行效果如图7-4所示：</p> <p><img src=\"data:image/png;base64,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xsZG22+//WzBggWm/axfv960jyFDhlhTU1N4HQWJww47jKBQUZoNCMQlQFCIq74oLQI/SmDr1q3hpP7dd99VfL5aEtJFAeCbb76xESNGhKCgboxly5aF4HDJJZeElge1Kjz55JPW0tJi559/vg0fPjw8fdy4cTZgwID0pbhFAIHIBQgKkVcgxUdAAvoUP2fOHPv4449t7dq1Nm/evHACf+yxx2zGjBl2xBFH2NVXXx3GLVQSS1sEKm3X+sGDB9ukSZPsvffeCy0MCh56nropGhoabPz48XbwwQdb3759d/cybEMAgYgECAoRVRZFRaCSgD7Vv/vuu3brrbeGLgGNSVALwb333mtJkthtt91mJ510UqWn97he3Qynn3566EJQl8XSpUvD4+bPn2833XST9enTJwSELVu2hJYFdUGcccYZNnHiRNtnn316fE1WIoBAfAIEhfjqjBIjsIuAxhacfPLJ4VP922+/bS+88EIYS3D22WeHVgCNUdAJfk/Ldddd1/kQjUt47bXXwrgEhYENGzaEbWq90P1TTjklhAU9RiHkq6++CsGhaxdG54txBwEEohUgKERbdRQcgR0C+vR/9NFHh+mQ+uSvgYbqDlC3wAknnGD33Xefvf/++zueUOHetddeG8YkXHDBBTZ16tSdHqXWBQ1u1Gtqf6NHjw5dDGo9mDx5cmeQ2OlJ/IIAAtELEBSir0IOAAELgwvVxbB69eow40FBQZ/6NUZh4MCBdt5559m5554bqGbPnm0PPvignXnmmaEloOvYhEKhELop1PrQU8uAXj+dGqkBjury0HgEBi/yLkSgdgUICrVbtxxZnQnopD1r1qzOloNDDjkkCLzyyit21FFHhYGGagnQdMennnrKjjzySNM1DzTD4ZhjjgndFrpOgrop1CqhoKBWiUWLFoUZDfvvv394vXSKpbom9BgFEQ1yZEEAgdoUICjUZr1yVHUooOmMb775ZggCmtao2Qe68JFO4goIN998cwgL6YWVtO6JJ54I4xnuv/9+O/zww4Pa9ddfby+99JI988wzIUBoBsX06dPt4osvDtvfeecdu+GGG0JrgsYraHwEsxzq8A3HIdeNAEGhbqqaA61lAU1T1Il95syZYaaCPvWrK+LUU08N4weee+45+/DDD8OJX7MV0kUtDXfddZe9+uqr4XFqbTjooINMXRBffPGFTZs2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\" alt=\"NavBar\"></p> <h3 id=\"materialcolor\"><a href=\"#materialcolor\" class=\"header-anchor\">#</a> MaterialColor</h3> <p><code>MaterialColor</code>是实现Material Design中的颜色的类，它包含一种颜色的10个级别的渐变色。<code>MaterialColor</code>通过&quot;[]&quot;运算符的索引值来代表颜色的深度，有效的索引有：50，100，200，…，900，数字越大，颜色越深。<code>MaterialColor</code>的默认值为索引等于500的颜色。举个例子，<code>Colors.blue</code>是预定义的一个<code>MaterialColor</code>类对象，定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">static</span> <span class=\"token keyword\">const</span> MaterialColor blue <span class=\"token operator\">=</span> <span class=\"token function\">MaterialColor</span><span class=\"token punctuation\">(</span>\n  _bluePrimaryValue<span class=\"token punctuation\">,</span>\n  <span class=\"token operator\">&lt;</span>int<span class=\"token punctuation\">,</span> Color<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">{</span>\n     <span class=\"token number\">50</span><span class=\"token punctuation\">:</span> <span class=\"token function\">Color</span><span class=\"token punctuation\">(</span><span class=\"token number\">0xFFE3F2FD</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token number\">100</span><span class=\"token punctuation\">:</span> <span class=\"token function\">Color</span><span class=\"token punctuation\">(</span><span class=\"token number\">0xFFBBDEFB</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token number\">200</span><span class=\"token punctuation\">:</span> <span class=\"token function\">Color</span><span class=\"token punctuation\">(</span><span class=\"token number\">0xFF90CAF9</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token number\">300</span><span class=\"token punctuation\">:</span> <span class=\"token function\">Color</span><span class=\"token punctuation\">(</span><span class=\"token number\">0xFF64B5F6</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token number\">400</span><span class=\"token punctuation\">:</span> <span class=\"token function\">Color</span><span class=\"token punctuation\">(</span><span class=\"token number\">0xFF42A5F5</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token number\">500</span><span class=\"token punctuation\">:</span> <span class=\"token function\">Color</span><span class=\"token punctuation\">(</span>_bluePrimaryValue<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token number\">600</span><span class=\"token punctuation\">:</span> <span class=\"token function\">Color</span><span class=\"token punctuation\">(</span><span class=\"token number\">0xFF1E88E5</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token number\">700</span><span class=\"token punctuation\">:</span> <span class=\"token function\">Color</span><span class=\"token punctuation\">(</span><span class=\"token number\">0xFF1976D2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token number\">800</span><span class=\"token punctuation\">:</span> <span class=\"token function\">Color</span><span class=\"token punctuation\">(</span><span class=\"token number\">0xFF1565C0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token number\">900</span><span class=\"token punctuation\">:</span> <span class=\"token function\">Color</span><span class=\"token punctuation\">(</span><span class=\"token number\">0xFF0D47A1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">static</span> <span class=\"token keyword\">const</span> int _bluePrimaryValue <span class=\"token operator\">=</span> <span class=\"token number\">0xFF2196F3</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p><code>Colors.blue[50]</code>到<code>Colors.blue[900]</code>的色值从浅蓝到深蓝渐变，效果如图7-5所示：</p> <p><img src=\"/assets/img/7-5.6f1c5012.jpeg\" alt=\"NavBar\"></p> <h2 id=\"_7-4-2-theme\"><a href=\"#_7-4-2-theme\" class=\"header-anchor\">#</a> 7.4.2 Theme</h2> <p><code>Theme</code>组件可以为Material APP定义主题数据（ThemeData）。Material组件库里很多组件都使用了主题数据，如导航栏颜色、标题字体、Icon样式等。<code>Theme</code>内会使用<code>InheritedWidget</code>来为其子树共享样式数据。</p> <h3 id=\"themedata\"><a href=\"#themedata\" class=\"header-anchor\">#</a> ThemeData</h3> <p><code>ThemeData</code>用于保存是Material 组件库的主题数据，Material组件需要遵守相应的设计规范，而这些规范可自定义部分都定义在ThemeData中了，所以我们可以通过ThemeData来自定义应用主题。在子组件中，我们可以通过<code>Theme.of</code>方法来获取当前的<code>ThemeData</code>。</p> <blockquote><p>注意：Material Design 设计规范中有些是不能自定义的，如导航栏高度，ThemeData只包含了可自定义部分。</p></blockquote> <p>我们看看<code>ThemeData</code>部分数据定义：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">ThemeData</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  Brightness brightness<span class=\"token punctuation\">,</span> <span class=\"token comment\">//深色还是浅色</span>\n  MaterialColor primarySwatch<span class=\"token punctuation\">,</span> <span class=\"token comment\">//主题颜色样本，见下面介绍</span>\n  Color primaryColor<span class=\"token punctuation\">,</span> <span class=\"token comment\">//主色，决定导航栏颜色</span>\n  Color accentColor<span class=\"token punctuation\">,</span> <span class=\"token comment\">//次级色，决定大多数Widget的颜色，如进度条、开关等。</span>\n  Color cardColor<span class=\"token punctuation\">,</span> <span class=\"token comment\">//卡片颜色</span>\n  Color dividerColor<span class=\"token punctuation\">,</span> <span class=\"token comment\">//分割线颜色</span>\n  ButtonThemeData buttonTheme<span class=\"token punctuation\">,</span> <span class=\"token comment\">//按钮主题</span>\n  Color cursorColor<span class=\"token punctuation\">,</span> <span class=\"token comment\">//输入框光标颜色</span>\n  Color dialogBackgroundColor<span class=\"token punctuation\">,</span><span class=\"token comment\">//对话框背景颜色</span>\n  String fontFamily<span class=\"token punctuation\">,</span> <span class=\"token comment\">//文字字体</span>\n  TextTheme textTheme<span class=\"token punctuation\">,</span><span class=\"token comment\">// 字体主题，包括标题、body等文字样式</span>\n  IconThemeData iconTheme<span class=\"token punctuation\">,</span> <span class=\"token comment\">// Icon的默认样式</span>\n  TargetPlatform platform<span class=\"token punctuation\">,</span> <span class=\"token comment\">//指定平台，应用特定平台控件风格</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>上面只是<code>ThemeData</code>的一小部分属性，完整的数据定义读者可以查看SDK。上面属性中需要说明的是<code>primarySwatch</code>，它是主题颜色的一个&quot;样本色&quot;，通过这个样本色可以在一些条件下生成一些其它的属性，例如，如果没有指定<code>primaryColor</code>，并且当前主题不是深色主题，那么<code>primaryColor</code>就会默认为<code>primarySwatch</code>指定的颜色，还有一些相似的属性如<code>accentColor</code> 、<code>indicatorColor</code>等也会受<code>primarySwatch</code>影响。</p> <h3 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h3> <p>我们实现一个路由换肤功能：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">ThemeTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _ThemeTestRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_ThemeTestRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_ThemeTestRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>ThemeTestRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  Color _themeColor <span class=\"token operator\">=</span> Colors<span class=\"token punctuation\">.</span>teal<span class=\"token punctuation\">;</span> <span class=\"token comment\">//当前路由主题色</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    ThemeData themeData <span class=\"token operator\">=</span> Theme<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Theme</span><span class=\"token punctuation\">(</span>\n      data<span class=\"token punctuation\">:</span> <span class=\"token function\">ThemeData</span><span class=\"token punctuation\">(</span>\n          primarySwatch<span class=\"token punctuation\">:</span> _themeColor<span class=\"token punctuation\">,</span> <span class=\"token comment\">//用于导航栏、FloatingActionButton的背景色等</span>\n          iconTheme<span class=\"token punctuation\">:</span> <span class=\"token function\">IconThemeData</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> _themeColor<span class=\"token punctuation\">)</span> <span class=\"token comment\">//用于Icon颜色</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n        appBar<span class=\"token punctuation\">:</span> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;主题测试&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        body<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n          mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n            <span class=\"token comment\">//第一行Icon使用主题中的iconTheme</span>\n            <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n                mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n                children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                  <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>favorite<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>airport_shuttle<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;  颜色跟随主题&quot;</span><span class=\"token punctuation\">)</span>\n                <span class=\"token punctuation\">]</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token comment\">//为第二行Icon自定义颜色（固定为黑色)</span>\n            <span class=\"token function\">Theme</span><span class=\"token punctuation\">(</span>\n              data<span class=\"token punctuation\">:</span> themeData<span class=\"token punctuation\">.</span><span class=\"token function\">copyWith</span><span class=\"token punctuation\">(</span>\n                iconTheme<span class=\"token punctuation\">:</span> themeData<span class=\"token punctuation\">.</span>iconTheme<span class=\"token punctuation\">.</span><span class=\"token function\">copyWith</span><span class=\"token punctuation\">(</span>\n                    color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>black\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Row</span><span class=\"token punctuation\">(</span>\n                  mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n                  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>favorite<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                    <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>airport_shuttle<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                    <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;  颜色固定黑色&quot;</span><span class=\"token punctuation\">)</span>\n                  <span class=\"token punctuation\">]</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        floatingActionButton<span class=\"token punctuation\">:</span> <span class=\"token function\">FloatingActionButton</span><span class=\"token punctuation\">(</span>\n            onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span>  <span class=\"token comment\">//切换主题</span>\n                <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span>\n                _themeColor <span class=\"token operator\">=</span>\n                _themeColor <span class=\"token operator\">==</span> Colors<span class=\"token punctuation\">.</span>teal <span class=\"token operator\">?</span> Colors<span class=\"token punctuation\">.</span>blue <span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>teal\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Icon</span><span class=\"token punctuation\">(</span>Icons<span class=\"token punctuation\">.</span>palette<span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行后点击右下角悬浮按钮则可以切换主题，如图7-6、7-7所示：</p> <p><img src=\"/assets/img/7-6.3bce9f21.png\" alt=\"图7-6\"><img src=\"/assets/img/7-7.b9c5d9fe.png\" alt=\"图7-7\"></p> <p>需要注意的有三点：</p> <ul><li><p>可以通过局部主题覆盖全局主题，正如代码中通过Theme为第二行图标指定固定颜色（黑色）一样，这是一种常用的技巧，Flutter中会经常使用这种方法来自定义子树主题。那么为什么局部主题可以覆盖全局主题？这主要是因为widget中使用主题样式时是通过<code>Theme.of(BuildContext context)</code>来获取的，我们看看其简化后的代码：</p></li> <li><div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">static</span> ThemeData <span class=\"token function\">of</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> bool shadowThemeOnly <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n   <span class=\"token comment\">// 简化代码，并非源码  </span>\n   <span class=\"token keyword\">return</span> context<span class=\"token punctuation\">.</span>dependOnInheritedWidgetOfExactType<span class=\"token operator\">&lt;</span>_InheritedTheme<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>theme<span class=\"token punctuation\">.</span>data\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p><code>context.dependOnInheritedWidgetOfExactType</code> 会在widget树中从当前位置向上查找第一个类型为<code>_InheritedTheme</code>的widget。所以当局部指定<code>Theme</code>后，其子树中通过<code>Theme.of()</code>向上查找到的第一个<code>_InheritedTheme</code>便是我们指定的<code>Theme</code>。</p></li> <li><p>本示例是对单个路由换肤，如果想要对整个应用换肤，则可以去修改<code>MaterialApp</code>的<code>theme</code>属性。</p></li></ul></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/37.7ecb6ef5.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter7/willpopscope.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>7.1 导航返回拦截（WillPopScope） | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/215.807d3ade.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter7/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_7-1-导航返回拦截-willpopscope\"><a href=\"#_7-1-导航返回拦截-willpopscope\" class=\"header-anchor\">#</a> 7.1 导航返回拦截（WillPopScope）</h1> <p>为了避免用户误触返回按钮而导致APP退出，在很多APP中都拦截了用户点击返回键的按钮，然后进行一些防误触判断，比如当用户在某一个时间段内点击两次时，才会认为用户是要退出（而非误触）。Flutter中可以通过<code>WillPopScope</code>来实现返回按钮拦截，我们看看<code>WillPopScope</code>的默认构造函数：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">const</span> <span class=\"token function\">WillPopScope</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token metadata symbol\">@required</span> WillPopCallback onWillPop<span class=\"token punctuation\">,</span>\n  <span class=\"token metadata symbol\">@required</span> Widget child\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p><code>onWillPop</code>是一个回调函数，当用户点击返回按钮时被调用（包括导航返回按钮及Android物理返回按钮）。该回调需要返回一个<code>Future</code>对象，如果返回的<code>Future</code>最终值为<code>false</code>时，则当前路由不出栈(不会返回)；最终值为<code>true</code>时，当前路由出栈退出。我们需要提供这个回调来决定是否退出。</p> <h3 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h3> <p>为了防止用户误触返回键退出，我们拦截返回事件。当用户在1秒内点击两次返回按钮时，则退出；如果间隔超过1秒则不退出，并重新记时。代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">WillPopScopeTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  WillPopScopeTestRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">WillPopScopeTestRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">WillPopScopeTestRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>WillPopScopeTestRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  DateTime _lastPressedAt<span class=\"token punctuation\">;</span> <span class=\"token comment\">//上次点击时间</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">WillPopScope</span><span class=\"token punctuation\">(</span>\n        onWillPop<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>_lastPressedAt <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">||</span>\n              DateTime<span class=\"token punctuation\">.</span><span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">difference</span><span class=\"token punctuation\">(</span>_lastPressedAt<span class=\"token punctuation\">)</span> <span class=\"token operator\">&gt;</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">//两次点击间隔超过1秒则重新计时</span>\n            _lastPressedAt <span class=\"token operator\">=</span> DateTime<span class=\"token punctuation\">.</span><span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token keyword\">return</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span>\n          <span class=\"token keyword\">return</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n          alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;1秒内连续按两次返回键退出&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>读者可以运行示例看看效果。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/215.807d3ade.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter8/eventbus.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>8.3 事件总线 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/216.a7c3d830.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter8/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_8-3-事件总线\"><a href=\"#_8-3-事件总线\" class=\"header-anchor\">#</a> 8.3 事件总线</h1> <p>在APP中，我们经常会需要一个广播机制，用以跨页面事件通知，比如一个需要登录的APP中，页面会关注用户登录或注销事件，来进行一些状态更新。这时候，一个事件总线便会非常有用，事件总线通常实现了订阅者模式，订阅者模式包含发布者和订阅者两种角色，可以通过事件总线来触发事件和监听事件，本节我们实现一个简单的全局事件总线，我们使用单例模式，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//订阅者回调签名</span>\n<span class=\"token keyword\">typedef</span> <span class=\"token keyword\">void</span> <span class=\"token function\">EventCallback</span><span class=\"token punctuation\">(</span>arg<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">EventBus</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">//私有构造函数</span>\n  EventBus<span class=\"token punctuation\">.</span><span class=\"token function\">_internal</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">//保存单例</span>\n  <span class=\"token keyword\">static</span> EventBus _singleton <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">EventBus<span class=\"token punctuation\">.</span>_internal</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">//工厂构造函数</span>\n  <span class=\"token keyword\">factory</span> <span class=\"token function\">EventBus</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _singleton<span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">//保存事件订阅者队列，key:事件名(id)，value: 对应事件的订阅者队列</span>\n  <span class=\"token keyword\">var</span> _emap <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Map</span><span class=\"token operator\">&lt;</span>Object<span class=\"token punctuation\">,</span> List<span class=\"token operator\">&lt;</span>EventCallback<span class=\"token operator\">&gt;&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">//添加订阅者</span>\n  <span class=\"token keyword\">void</span> <span class=\"token keyword\">on</span><span class=\"token punctuation\">(</span>eventName<span class=\"token punctuation\">,</span> EventCallback f<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>eventName <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">||</span> f <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span><span class=\"token punctuation\">;</span>\n    _emap<span class=\"token punctuation\">[</span>eventName<span class=\"token punctuation\">]</span> <span class=\"token operator\">?</span><span class=\"token operator\">?</span><span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">List</span><span class=\"token operator\">&lt;</span>EventCallback<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    _emap<span class=\"token punctuation\">[</span>eventName<span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span>f<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">//移除订阅者</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">off</span><span class=\"token punctuation\">(</span>eventName<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>EventCallback f<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">var</span> list <span class=\"token operator\">=</span> _emap<span class=\"token punctuation\">[</span>eventName<span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>eventName <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">||</span> list <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>f <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _emap<span class=\"token punctuation\">[</span>eventName<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n      list<span class=\"token punctuation\">.</span><span class=\"token function\">remove</span><span class=\"token punctuation\">(</span>f<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">//触发事件，事件触发后该事件所有订阅者会被调用</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">emit</span><span class=\"token punctuation\">(</span>eventName<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>arg<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">var</span> list <span class=\"token operator\">=</span> _emap<span class=\"token punctuation\">[</span>eventName<span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>list <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span><span class=\"token punctuation\">;</span>\n    int len <span class=\"token operator\">=</span> list<span class=\"token punctuation\">.</span>length <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//反向遍历，防止订阅者在回调中移除自身带来的下标错位 </span>\n    <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">var</span> i <span class=\"token operator\">=</span> len<span class=\"token punctuation\">;</span> i <span class=\"token operator\">&gt;</span> <span class=\"token operator\">-</span><span class=\"token number\">1</span><span class=\"token punctuation\">;</span> <span class=\"token operator\">--</span>i<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      list<span class=\"token punctuation\">[</span>i<span class=\"token punctuation\">]</span><span class=\"token punctuation\">(</span>arg<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">//定义一个top-level（全局）变量，页面引入该文件后可以直接使用bus</span>\n<span class=\"token keyword\">var</span> bus <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">EventBus</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>使用示例：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//页面A中</span>\n<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n <span class=\"token comment\">//监听登录事件</span>\nbus<span class=\"token punctuation\">.</span><span class=\"token keyword\">on</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;login&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span>arg<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// do something</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">//登录页B中</span>\n<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token comment\">//登录成功后触发登录事件，页面A中订阅者会被调用</span>\nbus<span class=\"token punctuation\">.</span><span class=\"token function\">emit</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;login&quot;</span><span class=\"token punctuation\">,</span> userInfo<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n</code></pre></div><blockquote><p>注意：Dart中实现单例模式的标准做法就是使用static变量+工厂构造函数的方式，这样就可以保证<code>new EventBus()</code>始终返回都是同一个实例，读者应该理解并掌握这种方法。</p></blockquote> <p>事件总线通常用于组件之间状态共享，但关于组件之间状态共享也有一些专门的包如redux、以及前面介绍过的Provider。对于一些简单的应用，事件总线是足以满足业务需求的，如果你决定使用状态管理包的话，一定要想清楚您的APP是否真的有必要使用它，防止“化简为繁”、过度设计。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/216.a7c3d830.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter8/gesture.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>8.2 手势识别 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/38.260a994c.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter8/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_8-2-手势识别\"><a href=\"#_8-2-手势识别\" class=\"header-anchor\">#</a> 8.2 手势识别</h1> <p>本节先介绍一些Flutter中用于处理手势的<code>GestureDetector</code>和<code>GestureRecognizer</code>，然后再仔细讨论一下手势竞争与冲突问题。</p> <h2 id=\"_8-2-1-gesturedetector\"><a href=\"#_8-2-1-gesturedetector\" class=\"header-anchor\">#</a> 8.2.1 GestureDetector</h2> <p><code>GestureDetector</code>是一个用于手势识别的功能性组件，我们通过它可以来识别各种手势。<code>GestureDetector</code>实际上是指针事件的语义化封装，接下来我们详细介绍一下各种手势识别。</p> <h3 id=\"点击、双击、长按\"><a href=\"#点击、双击、长按\" class=\"header-anchor\">#</a> 点击、双击、长按</h3> <p>我们通过<code>GestureDetector</code>对<code>Container</code>进行手势识别，触发相应事件后，在<code>Container</code>上显示事件名，为了增大点击区域，将<code>Container</code>设置为200×100，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">GestureDetectorTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _GestureDetectorTestRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span>\n      <span class=\"token keyword\">new</span> <span class=\"token class-name\">_GestureDetectorTestRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_GestureDetectorTestRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>GestureDetectorTestRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  String _operation <span class=\"token operator\">=</span> <span class=\"token string\">&quot;No Gesture detected!&quot;</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//保存事件名</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">GestureDetector</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n          alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n          color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span>\n          width<span class=\"token punctuation\">:</span> <span class=\"token number\">200.0</span><span class=\"token punctuation\">,</span> \n          height<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>_operation<span class=\"token punctuation\">,</span>\n            style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        onTap<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">updateText</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Tap&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token comment\">//点击</span>\n        onDoubleTap<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">updateText</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;DoubleTap&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//双击</span>\n        onLongPress<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">updateText</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;LongPress&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//长按</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">updateText</span><span class=\"token punctuation\">(</span>String text<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//更新显示的事件名</span>\n    <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      _operation <span class=\"token operator\">=</span> text<span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行效果如图8-2所示：</p> <p><img src=\"/assets/img/8-2.52bd91bd.png\" alt=\"图8-2\"></p> <blockquote><p><strong>注意</strong>： 当同时监听<code>onTap</code>和<code>onDoubleTap</code>事件时，当用户触发tap事件时，会有200毫秒左右的延时，这是因为当用户点击完之后很可能会再次点击以触发双击事件，所以<code>GestureDetector</code>会等一段时间来确定是否为双击事件。如果用户只监听了<code>onTap</code>（没有监听<code>onDoubleTap</code>）事件时，则没有延时。</p></blockquote> <h3 id=\"拖动、滑动\"><a href=\"#拖动、滑动\" class=\"header-anchor\">#</a> 拖动、滑动</h3> <p>一次完整的手势过程是指用户手指按下到抬起的整个过程，期间，用户按下手指后可能会移动，也可能不会移动。<code>GestureDetector</code>对于拖动和滑动事件是没有区分的，他们本质上是一样的。<code>GestureDetector</code>会将要监听的组件的原点（左上角）作为本次手势的原点，当用户在监听的组件上按下手指时，手势识别就会开始。下面我们看一个拖动圆形字母A的示例：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">_Drag</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _DragState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_DragState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_DragState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>_Drag<span class=\"token operator\">&gt;</span> <span class=\"token keyword\">with</span> SingleTickerProviderStateMixin <span class=\"token punctuation\">{</span>\n  double _top <span class=\"token operator\">=</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//距顶部的偏移</span>\n  double _left <span class=\"token operator\">=</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">;</span><span class=\"token comment\">//距左边的偏移</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Stack</span><span class=\"token punctuation\">(</span>\n      children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n        <span class=\"token function\">Positioned</span><span class=\"token punctuation\">(</span>\n          top<span class=\"token punctuation\">:</span> _top<span class=\"token punctuation\">,</span>\n          left<span class=\"token punctuation\">:</span> _left<span class=\"token punctuation\">,</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">GestureDetector</span><span class=\"token punctuation\">(</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">CircleAvatar</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;A&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token comment\">//手指按下时会触发此回调</span>\n            onPanDown<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>DragDownDetails e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token comment\">//打印手指按下的位置(相对于屏幕)</span>\n              <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;用户手指按下：${e.globalPosition}&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n            <span class=\"token comment\">//手指滑动时会触发此回调</span>\n            onPanUpdate<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>DragUpdateDetails e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token comment\">//用户手指滑动时，更新偏移，重新构建</span>\n              <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                _left <span class=\"token operator\">+=</span> e<span class=\"token punctuation\">.</span>delta<span class=\"token punctuation\">.</span>dx<span class=\"token punctuation\">;</span>\n                _top <span class=\"token operator\">+=</span> e<span class=\"token punctuation\">.</span>delta<span class=\"token punctuation\">.</span>dy<span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n            onPanEnd<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>DragEndDetails e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n              <span class=\"token comment\">//打印滑动结束时在x、y轴上的速度</span>\n              <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">.</span>velocity<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行后，就可以在任意方向拖动了，运行效果如图8-3所示：</p> <p><img src=\"/assets/img/8-3.975afea6.png\" alt=\"图8-3\"></p> <p>日志：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>I/flutter ( 8513): 用户手指按下：Offset(26.3, 101.8)\nI/flutter ( 8513): Velocity(235.5, 125.8)\n</code></pre></div><p>代码解释：</p> <ul><li><code>DragDownDetails.globalPosition</code>：当用户按下时，此属性为用户按下的位置相对于<strong>屏幕</strong>（而非父组件）原点(左上角)的偏移。</li> <li><code>DragUpdateDetails.delta</code>：当用户在屏幕上滑动时，会触发多次Update事件，<code>delta</code>指一次Update事件的滑动的偏移量。</li> <li><code>DragEndDetails.velocity</code>：该属性代表用户抬起手指时的滑动速度(包含x、y两个轴的），示例中并没有处理手指抬起时的速度，常见的效果是根据用户抬起手指时的速度做一个减速动画。</li></ul> <h3 id=\"单一方向拖动\"><a href=\"#单一方向拖动\" class=\"header-anchor\">#</a> 单一方向拖动</h3> <p>在本示例中，是可以朝任意方向拖动的，但是在很多场景，我们只需要沿一个方向来拖动，如一个垂直方向的列表，<code>GestureDetector</code>可以只识别特定方向的手势事件，我们将上面的例子改为只能沿垂直方向拖动：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">_DragVertical</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _DragVerticalState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_DragVerticalState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_DragVerticalState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>_DragVertical<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  double _top <span class=\"token operator\">=</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Stack</span><span class=\"token punctuation\">(</span>\n      children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n        <span class=\"token function\">Positioned</span><span class=\"token punctuation\">(</span>\n          top<span class=\"token punctuation\">:</span> _top<span class=\"token punctuation\">,</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">GestureDetector</span><span class=\"token punctuation\">(</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">CircleAvatar</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;A&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token comment\">//垂直方向拖动事件</span>\n            onVerticalDragUpdate<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>DragUpdateDetails details<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                _top <span class=\"token operator\">+=</span> details<span class=\"token punctuation\">.</span>delta<span class=\"token punctuation\">.</span>dy<span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>这样就只能在垂直方向拖动了，如果只想在水平方向滑动同理。</p> <h3 id=\"缩放\"><a href=\"#缩放\" class=\"header-anchor\">#</a> 缩放</h3> <p><code>GestureDetector</code>可以监听缩放事件，下面示例演示了一个简单的图片缩放效果：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">_ScaleTestRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>_ScaleTestRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  double _width <span class=\"token operator\">=</span> <span class=\"token number\">200.0</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//通过修改图片宽度来达到缩放效果</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n   <span class=\"token keyword\">return</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n     child<span class=\"token punctuation\">:</span> <span class=\"token function\">GestureDetector</span><span class=\"token punctuation\">(</span>\n        <span class=\"token comment\">//指定宽度，高度自适应</span>\n        child<span class=\"token punctuation\">:</span> Image<span class=\"token punctuation\">.</span><span class=\"token function\">asset</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;./images/sea.png&quot;</span><span class=\"token punctuation\">,</span> width<span class=\"token punctuation\">:</span> _width<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        onScaleUpdate<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>ScaleUpdateDetails details<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">//缩放倍数在0.8到10倍之间</span>\n            _width<span class=\"token operator\">=</span><span class=\"token number\">200</span><span class=\"token operator\">*</span>details<span class=\"token punctuation\">.</span>scale<span class=\"token punctuation\">.</span><span class=\"token function\">clamp</span><span class=\"token punctuation\">(</span><span class=\"token number\">.8</span><span class=\"token punctuation\">,</span> <span class=\"token number\">10.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n   <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行效果如图8-4所示：</p> <p><img src=\"/assets/img/8-4.6948ddf4.png\" alt=\"图8-4\"></p> <p>现在在图片上双指张开、收缩就可以放大、缩小图片。本示例比较简单，实际中我们通常还需要一些其它功能，如双击放大或缩小一定倍数、双指张开离开屏幕时执行一个减速放大动画等，读者可以在学习完后面“动画”一章中的内容后自己来尝试实现一下。</p> <h2 id=\"_8-2-2-gesturerecognizer\"><a href=\"#_8-2-2-gesturerecognizer\" class=\"header-anchor\">#</a> 8.2.2 GestureRecognizer</h2> <p><code>GestureDetector</code>内部是使用一个或多个<code>GestureRecognizer</code>来识别各种手势的，而<code>GestureRecognizer</code>的作用就是通过<code>Listener</code>来将原始指针事件转换为语义手势，<code>GestureDetector</code>直接可以接收一个子widget。<code>GestureRecognizer</code>是一个抽象类，一种手势的识别器对应一个<code>GestureRecognizer</code>的子类，Flutter实现了丰富的手势识别器，我们可以直接使用。</p> <h4 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h4> <p>假设我们要给一段富文本（<code>RichText</code>）的不同部分分别添加点击事件处理器，但是<code>TextSpan</code>并不是一个widget，这时我们不能用<code>GestureDetector</code>，但<code>TextSpan</code>有一个<code>recognizer</code>属性，它可以接收一个<code>GestureRecognizer</code>。</p> <p>假设我们需要在点击时给文本变色:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/gestures.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_GestureRecognizerTestRouteState</span>\n    <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>_GestureRecognizerTestRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  TapGestureRecognizer _tapGestureRecognizer <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">TapGestureRecognizer</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  bool _toggle <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//变色开关</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n     <span class=\"token comment\">//用到GestureRecognizer的话一定要调用其dispose方法释放资源</span>\n    _tapGestureRecognizer<span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> Text<span class=\"token punctuation\">.</span><span class=\"token function\">rich</span><span class=\"token punctuation\">(</span>\n          <span class=\"token function\">TextSpan</span><span class=\"token punctuation\">(</span>\n              children<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>\n                <span class=\"token function\">TextSpan</span><span class=\"token punctuation\">(</span>text<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;你好世界&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token function\">TextSpan</span><span class=\"token punctuation\">(</span>\n                  text<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;点我变色&quot;</span><span class=\"token punctuation\">,</span>\n                  style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>\n                      fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">30.0</span><span class=\"token punctuation\">,</span>\n                      color<span class=\"token punctuation\">:</span> _toggle <span class=\"token operator\">?</span> Colors<span class=\"token punctuation\">.</span>blue <span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  recognizer<span class=\"token punctuation\">:</span> _tapGestureRecognizer\n                    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>onTap <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                      <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                        _toggle <span class=\"token operator\">=</span> <span class=\"token operator\">!</span>_toggle<span class=\"token punctuation\">;</span>\n                      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token function\">TextSpan</span><span class=\"token punctuation\">(</span>text<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;你好世界&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">]</span>\n          <span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行效果：</p> <p><img src=\"/assets/img/8-5.ba487ffd.png\" alt=\"图8-5\"></p> <blockquote><p>注意：使用<code>GestureRecognizer</code>后一定要调用其<code>dispose()</code>方法来释放资源（主要是取消内部的计时器）。</p></blockquote> <h2 id=\"_8-2-3-手势竞争与冲突\"><a href=\"#_8-2-3-手势竞争与冲突\" class=\"header-anchor\">#</a> 8.2.3 手势竞争与冲突</h2> <h3 id=\"竞争\"><a href=\"#竞争\" class=\"header-anchor\">#</a> 竞争</h3> <p>如果在上例中我们同时监听水平和垂直方向的拖动事件，那么我们斜着拖动时哪个方向会生效？实际上取决于第一次移动时两个轴上的位移分量，哪个轴的大，哪个轴在本次滑动事件竞争中就胜出。实际上Flutter中的手势识别引入了一个Arena的概念，Arena直译为“竞技场”的意思，每一个手势识别器（<code>GestureRecognizer</code>）都是一个“竞争者”（<code>GestureArenaMember</code>），当发生滑动事件时，他们都要在“竞技场”去竞争本次事件的处理权，而最终只有一个“竞争者”会胜出(win)。例如，假设有一个<code>ListView</code>，它的第一个子组件也是<code>ListView</code>，如果现在滑动这个子<code>ListView</code>，父<code>ListView</code>会动吗？答案是否定的，这时只有子<code>ListView</code>会动，因为这时子<code>ListView</code>会胜出而获得滑动事件的处理权。</p> <h3 id=\"示例-2\"><a href=\"#示例-2\" class=\"header-anchor\">#</a> <strong>示例</strong></h3> <p>我们以拖动手势为例，同时识别水平和垂直方向的拖动手势，当用户按下手指时就会触发竞争（水平方向和垂直方向），一旦某个方向“获胜”，则直到当次拖动手势结束都会沿着该方向移动。代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">BothDirectionTestRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  BothDirectionTestRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span>\n      <span class=\"token keyword\">new</span> <span class=\"token class-name\">BothDirectionTestRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">BothDirectionTestRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>BothDirectionTestRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  double _top <span class=\"token operator\">=</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">;</span>\n  double _left <span class=\"token operator\">=</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Stack</span><span class=\"token punctuation\">(</span>\n      children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n        <span class=\"token function\">Positioned</span><span class=\"token punctuation\">(</span>\n          top<span class=\"token punctuation\">:</span> _top<span class=\"token punctuation\">,</span>\n          left<span class=\"token punctuation\">:</span> _left<span class=\"token punctuation\">,</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">GestureDetector</span><span class=\"token punctuation\">(</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">CircleAvatar</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;A&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token comment\">//垂直方向拖动事件</span>\n            onVerticalDragUpdate<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>DragUpdateDetails details<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                _top <span class=\"token operator\">+=</span> details<span class=\"token punctuation\">.</span>delta<span class=\"token punctuation\">.</span>dy<span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n            onHorizontalDragUpdate<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>DragUpdateDetails details<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                _left <span class=\"token operator\">+=</span> details<span class=\"token punctuation\">.</span>delta<span class=\"token punctuation\">.</span>dx<span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>此示例运行后，每次拖动只会沿一个方向移动（水平或垂直），而竞争发生在手指按下后首次移动（move）时，此例中具体的“获胜”条件是：首次移动时的位移在水平和垂直方向上的分量大的一个获胜。</p> <h3 id=\"手势冲突\"><a href=\"#手势冲突\" class=\"header-anchor\">#</a> 手势冲突</h3> <p>由于手势竞争最终只有一个胜出者，所以，当有多个手势识别器时，可能会产生冲突。假设有一个widget，它可以左右拖动，现在我们也想检测在它上面手指按下和抬起的事件，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">GestureConflictTestRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>GestureConflictTestRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  double _left <span class=\"token operator\">=</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">;</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Stack</span><span class=\"token punctuation\">(</span>\n      children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n        <span class=\"token function\">Positioned</span><span class=\"token punctuation\">(</span>\n          left<span class=\"token punctuation\">:</span> _left<span class=\"token punctuation\">,</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">GestureDetector</span><span class=\"token punctuation\">(</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">CircleAvatar</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;A&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//要拖动和点击的widget</span>\n              onHorizontalDragUpdate<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>DragUpdateDetails details<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                  _left <span class=\"token operator\">+=</span> details<span class=\"token punctuation\">.</span>delta<span class=\"token punctuation\">.</span>dx<span class=\"token punctuation\">;</span>\n                <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n              onHorizontalDragEnd<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>details<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n                <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;onHorizontalDragEnd&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n              onTapDown<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>details<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n                <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;down&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n              onTapUp<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>details<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n                <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;up&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>现在我们按住圆形“A”拖动然后抬起手指，控制台日志如下:</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>I/flutter (17539): down\nI/flutter (17539): onHorizontalDragEnd\n</code></pre></div><p>我们发现没有打印&quot;up&quot;，这是因为在拖动时，刚开始按下手指时在没有移动时，拖动手势还没有完整的语义，此时TapDown手势胜出(win)，此时打印&quot;down&quot;，而拖动时，拖动手势会胜出，当手指抬起时，<code>onHorizontalDragEnd</code> 和 <code>onTapUp</code>发生了冲突，但是因为是在拖动的语义中，所以<code>onHorizontalDragEnd</code>胜出，所以就会打印 “onHorizontalDragEnd”。如果我们的代码逻辑中，对于手指按下和抬起是强依赖的，比如在一个轮播图组件中，我们希望手指按下时，暂停轮播，而抬起时恢复轮播，但是由于轮播图组件中本身可能已经处理了拖动手势（支持手动滑动切换），甚至可能也支持了缩放手势，这时我们如果在外部再用<code>onTapDown</code>、<code>onTapUp</code>来监听的话是不行的。这时我们应该怎么做？其实很简单，通过Listener监听原始指针事件就行：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Positioned</span><span class=\"token punctuation\">(</span>\n  top<span class=\"token punctuation\">:</span><span class=\"token number\">80.0</span><span class=\"token punctuation\">,</span>\n  left<span class=\"token punctuation\">:</span> _leftB<span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Listener</span><span class=\"token punctuation\">(</span>\n    onPointerDown<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>details<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;down&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    onPointerUp<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>details<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">//会触发</span>\n      <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;up&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">GestureDetector</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">CircleAvatar</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;B&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      onHorizontalDragUpdate<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>DragUpdateDetails details<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          _leftB <span class=\"token operator\">+=</span> details<span class=\"token punctuation\">.</span>delta<span class=\"token punctuation\">.</span>dx<span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      onHorizontalDragEnd<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>details<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;onHorizontalDragEnd&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>手势冲突只是手势级别的，而手势是对原始指针的语义化的识别，所以在遇到复杂的冲突场景时，都可以通过<code>Listener</code>直接识别原始指针事件来解决冲突。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/38.260a994c.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter8/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>事件处理与通知 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/217.ad95582b.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter8/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h2 id=\"事件处理与通知\"><a href=\"#事件处理与通知\" class=\"header-anchor\">#</a> 事件处理与通知</h2> <p>Flutter中的手势系统有两个独立的层。第一层为原始指针(pointer)事件，它描述了屏幕上指针（例如，触摸、鼠标和触控笔）的位置和移动。 第二层为手势，描述由一个或多个指针移动组成的语义动作，如拖动、缩放、双击等。本章将先分别介绍如何处理这两种事件，最后再介绍一下Flutter中重要的Notification机制。</p> <h2 id=\"本章目录\"><a href=\"#本章目录\" class=\"header-anchor\">#</a> 本章目录</h2> <ul><li><a href=\"/v2/chapter8/listener.html\">原始指针事件处理</a></li> <li><a href=\"/v2/chapter8/gesture.html\">手势识别</a></li> <li><a href=\"/v2/chapter8/eventbus.html\">全局事件总线</a></li> <li><a href=\"/v2/chapter8/notification.html\">通知Notification</a></li></ul></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/217.ad95582b.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter8/listener.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>8.1 原始指针事件处理 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/127.76aaf7da.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter8/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_8-1-原始指针事件处理\"><a href=\"#_8-1-原始指针事件处理\" class=\"header-anchor\">#</a> 8.1 原始指针事件处理</h1> <p>本节先来介绍一下原始指针事件(Pointer Event，在移动设备上通常为触摸事件)，下一节再介绍手势处理。</p> <p>在移动端，各个平台或UI系统的原始指针事件模型基本都是一致，即：一次完整的事件分为三个阶段：手指按下、手指移动、和手指抬起，而更高级别的手势（如点击、双击、拖动等）都是基于这些原始事件的。</p> <p>当指针按下时，Flutter会对应用程序执行<strong>命中测试(Hit Test)</strong>，以确定指针与屏幕接触的位置存在哪些组件（widget）， 指针按下事件（以及该指针的后续事件）然后被分发到由命中测试发现的最内部的组件，然后从那里开始，事件会在组件树中向上冒泡，这些事件会从最内部的组件被分发到组件树根的路径上的所有组件，这和Web开发中浏览器的<strong>事件冒泡</strong>机制相似， 但是Flutter中没有机制取消或停止“冒泡”过程，而浏览器的冒泡是可以停止的。注意，只有通过命中测试的组件才能触发事件。</p> <p>Flutter中可以使用<code>Listener</code>来监听原始触摸事件，按照本书对组件的分类，则<code>Listener</code>也是一个功能性组件。下面是<code>Listener</code>的构造函数定义：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Listener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  Key key<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>onPointerDown<span class=\"token punctuation\">,</span> <span class=\"token comment\">//手指按下回调</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>onPointerMove<span class=\"token punctuation\">,</span> <span class=\"token comment\">//手指移动回调</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>onPointerUp<span class=\"token punctuation\">,</span><span class=\"token comment\">//手指抬起回调</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>onPointerCancel<span class=\"token punctuation\">,</span><span class=\"token comment\">//触摸事件取消回调</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>behavior <span class=\"token operator\">=</span> HitTestBehavior<span class=\"token punctuation\">.</span>deferToChild<span class=\"token punctuation\">,</span> <span class=\"token comment\">//在命中测试期间如何表现</span>\n  Widget child\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>我们先看一个示例，后面再单独讨论一下<code>behavior</code>属性。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token comment\">//定义一个状态，保存当前指针位置</span>\nPointerEvent _event<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n<span class=\"token function\">Listener</span><span class=\"token punctuation\">(</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n    alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n    color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span>\n    width<span class=\"token punctuation\">:</span> <span class=\"token number\">300.0</span><span class=\"token punctuation\">,</span>\n    height<span class=\"token punctuation\">:</span> <span class=\"token number\">150.0</span><span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>_event<span class=\"token operator\">?</span><span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">?</span><span class=\"token operator\">?</span><span class=\"token string\">&quot;&quot;</span><span class=\"token punctuation\">,</span>style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  onPointerDown<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>PointerDownEvent event<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span>_event<span class=\"token operator\">=</span>event<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  onPointerMove<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>PointerMoveEvent event<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span>_event<span class=\"token operator\">=</span>event<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  onPointerUp<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>PointerUpEvent event<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span>_event<span class=\"token operator\">=</span>event<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</code></pre></div><p>运行后效果如图8-1所示：</p> <p><img src=\"/assets/img/8-1.fd0e65f1.png\" alt=\"图8-1\"></p> <p>手指在蓝色矩形区域内移动即可看到当前指针偏移，当触发指针事件时，参数<code>PointerDownEvent</code>、<code>PointerMoveEvent</code>、<code>PointerUpEvent</code>都是<code>PointerEvent</code>的一个子类，<code>PointerEvent</code>类中包括当前指针的一些信息，如：</p> <ul><li><code>position</code>：它是鼠标相对于当对于全局坐标的偏移。</li> <li><code>delta</code>：两次指针移动事件（<code>PointerMoveEvent</code>）的距离。</li> <li><code>pressure</code>：按压力度，如果手机屏幕支持压力传感器(如iPhone的3D Touch)，此属性会更有意义，如果手机不支持，则始终为1。</li> <li><code>orientation</code>：指针移动方向，是一个角度值。</li></ul> <p>上面只是<code>PointerEvent</code>一些常用属性，除了这些它还有很多属性，读者可以查看API文档。</p> <p>现在，我们重点来介绍一下<code>behavior</code>属性，它决定子组件如何响应命中测试，它的值类型为<code>HitTestBehavior</code>，这是一个枚举类，有三个枚举值：</p> <ul><li><p><code>deferToChild</code>：子组件会一个接一个的进行命中测试，如果子组件中有测试通过的，则当前组件通过，这就意味着，如果指针事件作用于子组件上时，其父级组件也肯定可以收到该事件。</p></li> <li><p><code>opaque</code>：在命中测试时，将当前组件当成不透明处理(即使本身是透明的)，最终的效果相当于当前Widget的整个区域都是点击区域。举个例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Listener</span><span class=\"token punctuation\">(</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">ConstrainedBox</span><span class=\"token punctuation\">(</span>\n        constraints<span class=\"token punctuation\">:</span> BoxConstraints<span class=\"token punctuation\">.</span><span class=\"token function\">tight</span><span class=\"token punctuation\">(</span><span class=\"token function\">Size</span><span class=\"token punctuation\">(</span><span class=\"token number\">300.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">150.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Box A&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token comment\">//behavior: HitTestBehavior.opaque,</span>\n    onPointerDown<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;down A&quot;</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</code></pre></div><p>上例中，只有点击文本内容区域才会触发点击事件，因为 <code>deferToChild</code> 会去子组件判断是否命中测试，而该例中子组件就是 <code>Text(&quot;Box A&quot;)</code> 。\n如果我们想让整个300×150的矩形区域都能点击我们可以将<code>behavior</code>设为<code>HitTestBehavior.opaque</code>。注意，该属性并不能用于在组件树中拦截（忽略）事件，它只是决定命中测试时的组件大小。</p></li> <li><p><code>translucent</code>：当点击组件透明区域时，可以对自身边界内及底部可视区域都进行命中测试，这意味着点击顶部组件透明区域时，顶部组件和底部组件都可以接收到事件，例如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Stack</span><span class=\"token punctuation\">(</span>\n  children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n    <span class=\"token function\">Listener</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">ConstrainedBox</span><span class=\"token punctuation\">(</span>\n        constraints<span class=\"token punctuation\">:</span> BoxConstraints<span class=\"token punctuation\">.</span><span class=\"token function\">tight</span><span class=\"token punctuation\">(</span><span class=\"token function\">Size</span><span class=\"token punctuation\">(</span><span class=\"token number\">300.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">200.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">DecoratedBox</span><span class=\"token punctuation\">(</span>\n            decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      onPointerDown<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;down0&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">Listener</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">ConstrainedBox</span><span class=\"token punctuation\">(</span>\n        constraints<span class=\"token punctuation\">:</span> BoxConstraints<span class=\"token punctuation\">.</span><span class=\"token function\">tight</span><span class=\"token punctuation\">(</span><span class=\"token function\">Size</span><span class=\"token punctuation\">(</span><span class=\"token number\">200.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;左上角200*100范围内非文本区域点击&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      onPointerDown<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;down1&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token comment\">//behavior: HitTestBehavior.translucent, //放开此行注释后可以&quot;点透&quot;</span>\n    <span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>上例中，当注释掉最后一行代码后，在左上角200*100范围内非文本区域点击时（顶部组件透明区域），控制台只会打印“down0”，也就是说顶部组件没有接收到事件，而只有底部接收到了。当放开注释后，再点击时顶部和底部都会接收到事件，此时会打印：</p> <div class=\"language- extra-class\"><pre class=\"language-text\"><code>I/flutter ( 3039): down1\nI/flutter ( 3039): down0\n</code></pre></div><p>如果<code>behavior</code>值改为<code>HitTestBehavior.opaque</code>，则只会打印&quot;down1&quot;。</p></li></ul> <h3 id=\"忽略pointerevent\"><a href=\"#忽略pointerevent\" class=\"header-anchor\">#</a> 忽略PointerEvent</h3> <p>假如我们不想让某个子树响应<code>PointerEvent</code>的话，我们可以使用<code>IgnorePointer</code>和<code>AbsorbPointer</code>，这两个组件都能阻止子树接收指针事件，不同之处在于<code>AbsorbPointer</code>本身会参与命中测试，而<code>IgnorePointer</code>本身不会参与，这就意味着<code>AbsorbPointer</code>本身是可以接收指针事件的(但其子树不行)，而<code>IgnorePointer</code>不可以。一个简单的例子如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">Listener</span><span class=\"token punctuation\">(</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">AbsorbPointer</span><span class=\"token punctuation\">(</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">Listener</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n        color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span>\n        width<span class=\"token punctuation\">:</span> <span class=\"token number\">200.0</span><span class=\"token punctuation\">,</span>\n        height<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      onPointerDown<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span><span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span><span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;in&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  onPointerDown<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span><span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span><span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;up&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>点击<code>Container</code>时，由于它在<code>AbsorbPointer</code>的子树上，所以不会响应指针事件，所以日志不会输出&quot;in&quot;，但<code>AbsorbPointer</code>本身是可以接收指针事件的，所以会输出&quot;up&quot;。如果将<code>AbsorbPointer</code>换成<code>IgnorePointer</code>，那么两个都不会输出。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/127.76aaf7da.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter8/notification.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>8.4 Notification | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/128.c75c7015.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter8/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_8-4-notification\"><a href=\"#_8-4-notification\" class=\"header-anchor\">#</a> 8.4 Notification</h1> <p>通知（Notification）是Flutter中一个重要的机制，在widget树中，每一个节点都可以分发通知，通知会沿着当前节点向上传递，所有父节点都可以通过<code>NotificationListener</code>来监听通知。Flutter中将这种由子向父的传递通知的机制称为<strong>通知冒泡</strong>（Notification Bubbling）。通知冒泡和用户触摸事件冒泡是相似的，但有一点不同：通知冒泡可以中止，但用户触摸事件不行。</p> <blockquote><p>通知冒泡和Web开发中浏览器事件冒泡原理是相似的，都是事件从出发源逐层向上传递，我们可以在上层节点任意位置来监听通知/事件，也可以终止冒泡过程，终止冒泡后，通知将不会再向上传递。</p></blockquote> <p>Flutter中很多地方使用了通知，如可滚动组件（Scrollable Widget）滑动时就会分发<strong>滚动通知</strong>（ScrollNotification），而Scrollbar正是通过监听ScrollNotification来确定滚动条位置的。</p> <p>下面是一个监听可滚动组件滚动通知的例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">NotificationListener</span><span class=\"token punctuation\">(</span>\n  onNotification<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>notification<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">switch</span> <span class=\"token punctuation\">(</span>notification<span class=\"token punctuation\">.</span>runtimeType<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">case</span> ScrollStartNotification<span class=\"token punctuation\">:</span> <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;开始滚动&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">case</span> ScrollUpdateNotification<span class=\"token punctuation\">:</span> <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;正在滚动&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">case</span> ScrollEndNotification<span class=\"token punctuation\">:</span> <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;滚动停止&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">case</span> OverscrollNotification<span class=\"token punctuation\">:</span> <span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;滚动到边界&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> ListView<span class=\"token punctuation\">.</span><span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>\n      itemCount<span class=\"token punctuation\">:</span> <span class=\"token number\">100</span><span class=\"token punctuation\">,</span>\n      itemBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">return</span> <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;$index&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>上例中的滚动通知如<code>ScrollStartNotification</code>、<code>ScrollUpdateNotification</code>等都是继承自<code>ScrollNotification</code>类，不同类型的通知子类会包含不同的信息，比如<code>ScrollUpdateNotification</code>有一个<code>scrollDelta</code>属性，它记录了移动的位移，其它通知属性读者可以自己查看SDK文档。</p> <p>上例中，我们通过<code>NotificationListener</code>来监听子<code>ListView</code>的滚动通知的，<code>NotificationListener</code>定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">NotificationListener</span><span class=\"token operator\">&lt;</span>T <span class=\"token keyword\">extends</span> <span class=\"token class-name\">Notification</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function\">NotificationListener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n    <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>onNotification<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token comment\">//省略无关代码 </span>\n<span class=\"token punctuation\">}</span>  \n</code></pre></div><p>我们可以看到：</p> <ol><li><p><code>NotificationListener</code> 继承自<code>StatelessWidget</code>类，所以它可以直接嵌套到Widget树中。</p></li> <li><p><code>NotificationListener</code> 可以指定一个模板参数，该模板参数类型必须是继承自<code>Notification</code>；当显式指定模板参数时，<code>NotificationListener</code> 便只会接收该参数类型的通知。举个例子，如果我们将上例子代码改为：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//指定监听通知的类型为滚动结束通知(ScrollEndNotification)</span>\nNotificationListener<span class=\"token operator\">&lt;</span>ScrollEndNotification<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n  onNotification<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>notification<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//只会在滚动结束时才会触发此回调</span>\n    <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>notification<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> ListView<span class=\"token punctuation\">.</span><span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>\n      itemCount<span class=\"token punctuation\">:</span> <span class=\"token number\">100</span><span class=\"token punctuation\">,</span>\n      itemBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> index<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">return</span> <span class=\"token function\">ListTile</span><span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;$index&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>上面代码运行后便只会在滚动结束时在控制台打印出通知的信息。</p></li> <li><p><code>onNotification</code>回调为通知处理回调，其函数签名如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">typedef</span> NotificationListenerCallback<span class=\"token operator\">&lt;</span>T <span class=\"token keyword\">extends</span> <span class=\"token class-name\">Notification</span><span class=\"token operator\">&gt;</span> <span class=\"token operator\">=</span> bool <span class=\"token keyword\">Function</span><span class=\"token punctuation\">(</span>T notification<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>它的返回值类型为布尔值，当返回值为<code>true</code>时，阻止冒泡，其父级Widget将再也收不到该通知；当返回值为<code>false</code> 时继续向上冒泡通知。</p></li></ol> <p>Flutter的UI框架实现中，除了在可滚动组件在滚动过程中会发出<code>ScrollNotification</code>之外，还有一些其它的通知，如<code>SizeChangedLayoutNotification</code>、<code>KeepAliveNotification</code> 、<code>LayoutChangedNotification</code>等，Flutter正是通过这种通知机制来使父元素可以在一些特定时机来做一些事情。</p> <h4 id=\"自定义通知\"><a href=\"#自定义通知\" class=\"header-anchor\">#</a> 自定义通知</h4> <p>除了Flutter内部通知，我们也可以自定义通知，下面我们看看如何实现自定义通知：</p> <ol><li><p>定义一个通知类，要继承自Notification类；</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">MyNotification</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">Notification</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">MyNotification</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>msg<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> String msg<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div></li> <li><p>分发通知。</p> <p><code>Notification</code>有一个<code>dispatch(context)</code>方法，它是用于分发通知的，我们说过<code>context</code>实际上就是操作<code>Element</code>的一个接口，它与<code>Element</code>树上的节点是对应的，通知会从<code>context</code>对应的<code>Element</code>节点向上冒泡。</p></li></ol> <p>下面我们看一个完整的例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">NotificationRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  NotificationRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">NotificationRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">NotificationRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>NotificationRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  String _msg<span class=\"token operator\">=</span><span class=\"token string\">&quot;&quot;</span><span class=\"token punctuation\">;</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//监听通知  </span>\n    <span class=\"token keyword\">return</span> NotificationListener<span class=\"token operator\">&lt;</span>MyNotification<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n      onNotification<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>notification<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          _msg<span class=\"token operator\">+=</span>notification<span class=\"token punctuation\">.</span>msg<span class=\"token operator\">+</span><span class=\"token string\">&quot;  &quot;</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n       <span class=\"token keyword\">return</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n          mainAxisSize<span class=\"token punctuation\">:</span> MainAxisSize<span class=\"token punctuation\">.</span>min<span class=\"token punctuation\">,</span>\n          children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n<span class=\"token comment\">//          RaisedButton(</span>\n<span class=\"token comment\">//           onPressed: () =&gt; MyNotification(&quot;Hi&quot;).dispatch(context),</span>\n<span class=\"token comment\">//           child: Text(&quot;Send Notification&quot;),</span>\n<span class=\"token comment\">//          ),  </span>\n            <span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span>\n              builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token keyword\">return</span> <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n                  <span class=\"token comment\">//按钮点击时分发通知  </span>\n                  onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">MyNotification</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hi&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">dispatch</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Send Notification&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>_msg<span class=\"token punctuation\">)</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">MyNotification</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">Notification</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">MyNotification</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>msg<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> String msg<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面代码中，我们每点一次按钮就会分发一个<code>MyNotification</code>类型的通知，我们在Widget根上监听通知，收到通知后我们将通知通过Text显示在屏幕上。</p> <blockquote><p>注意：代码中注释的部分是不能正常工作的，因为这个<code>context</code>是根Context，而NotificationListener是监听的子树，所以我们通过<code>Builder</code>来构建RaisedButton，来获得按钮位置的context。</p></blockquote> <p>运行效果如图8-6所示：</p> <p><img src=\"/assets/img/8-6.ce4b57e6.png\" alt=\"图8-6\"></p> <h3 id=\"阻止冒泡\"><a href=\"#阻止冒泡\" class=\"header-anchor\">#</a> 阻止冒泡</h3> <p>我们将上面的例子改为：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">NotificationRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>NotificationRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  String _msg<span class=\"token operator\">=</span><span class=\"token string\">&quot;&quot;</span><span class=\"token punctuation\">;</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//监听通知</span>\n    <span class=\"token keyword\">return</span> NotificationListener<span class=\"token operator\">&lt;</span>MyNotification<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n      onNotification<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>notification<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n        <span class=\"token function\">print</span><span class=\"token punctuation\">(</span>notification<span class=\"token punctuation\">.</span>msg<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">//打印通知</span>\n        <span class=\"token keyword\">return</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> NotificationListener<span class=\"token operator\">&lt;</span>MyNotification<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n        onNotification<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>notification<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            _msg<span class=\"token operator\">+=</span>notification<span class=\"token punctuation\">.</span>msg<span class=\"token operator\">+</span><span class=\"token string\">&quot;  &quot;</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token keyword\">return</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span> \n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token comment\">//省略重复代码</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>上列中两个<code>NotificationListener</code>进行了嵌套，子<code>NotificationListener</code>的<code>onNotification</code>回调返回了<code>false</code>，表示不阻止冒泡，所以父<code>NotificationListener</code>仍然会受到通知，所以控制台会打印出通知信息；如果将子<code>NotificationListener</code>的<code>onNotification</code>回调的返回值改为<code>true</code>，则父<code>NotificationListener</code>便不会再打印通知了，因为子<code>NotificationListener</code>已经终止通知冒泡了。</p> <h3 id=\"通知冒泡原理\"><a href=\"#通知冒泡原理\" class=\"header-anchor\">#</a> 通知冒泡原理</h3> <p>我们在上面介绍了通知冒泡的现象及使用，现在我们更深入一些，介绍一下Flutter框架中是如何实现通知冒泡的。为了搞清楚这个问题，就必须看一下源码，我们从通知分发的的源头出发，然后再顺藤摸瓜。由于通知是通过<code>Notification</code>的<code>dispatch(context)</code>方法发出的，那我们先看看<code>dispatch(context)</code>方法中做了什么，下面是相关源码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">void</span> <span class=\"token function\">dispatch</span><span class=\"token punctuation\">(</span>BuildContext target<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  target<span class=\"token operator\">?</span><span class=\"token punctuation\">.</span><span class=\"token function\">visitAncestorElements</span><span class=\"token punctuation\">(</span>visitAncestor<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p><code>dispatch(context)</code>中调用了当前context的<code>visitAncestorElements</code>方法，该方法会从当前Element开始向上遍历父级元素；<code>visitAncestorElements</code>有一个遍历回调参数，在遍历过程中对遍历到的父级元素都会执行该回调。遍历的终止条件是：已经遍历到根Element或某个遍历回调返回<code>false</code>。源码中传给<code>visitAncestorElements</code>方法的遍历回调为<code>visitAncestor</code>方法，我们看看<code>visitAncestor</code>方法的实现：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">//遍历回调，会对每一个父级Element执行此回调</span>\nbool <span class=\"token function\">visitAncestor</span><span class=\"token punctuation\">(</span>Element element<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">//判断当前element对应的Widget是否是NotificationListener。</span>\n  \n  <span class=\"token comment\">//由于NotificationListener是继承自StatelessWidget，</span>\n  <span class=\"token comment\">//故先判断是否是StatelessElement</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>element <span class=\"token operator\">is</span> StatelessElement<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//是StatelessElement，则获取element对应的Widget，判断</span>\n    <span class=\"token comment\">//是否是NotificationListener 。</span>\n    <span class=\"token keyword\">final</span> StatelessWidget widget <span class=\"token operator\">=</span> element<span class=\"token punctuation\">.</span>widget<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>widget <span class=\"token operator\">is</span> NotificationListener<span class=\"token operator\">&lt;</span>Notification<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">//是NotificationListener，则调用该NotificationListener的_dispatch方法</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>widget<span class=\"token punctuation\">.</span><span class=\"token function\">_dispatch</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">,</span> element<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> \n        <span class=\"token keyword\">return</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token keyword\">return</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p><code>visitAncestor</code>会判断每一个遍历到的父级Widget是否是<code>NotificationListener</code>，如果不是，则返回<code>true</code>继续向上遍历，如果是，则调用<code>NotificationListener</code>的<code>_dispatch</code>方法，我们看看<code>_dispatch</code>方法的源码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>  bool <span class=\"token function\">_dispatch</span><span class=\"token punctuation\">(</span>Notification notification<span class=\"token punctuation\">,</span> Element element<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 如果通知监听器不为空，并且当前通知类型是该NotificationListener</span>\n    <span class=\"token comment\">// 监听的通知类型，则调用当前NotificationListener的onNotification</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>onNotification <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">&amp;&amp;</span> notification <span class=\"token operator\">is</span> T<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">final</span> bool result <span class=\"token operator\">=</span> <span class=\"token function\">onNotification</span><span class=\"token punctuation\">(</span>notification<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token comment\">// 返回值决定是否继续向上遍历</span>\n      <span class=\"token keyword\">return</span> result <span class=\"token operator\">==</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span> \n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">return</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们可以看到<code>NotificationListener</code>的<code>onNotification</code>回调最终是在<code>_dispatch</code>方法中执行的，然后会根据返回值来确定是否继续向上冒泡。上面的源码实现其实并不复杂，通过阅读这些源码，一些额外的点读者可以注意一下：</p> <ol><li><code>Context</code>上也提供了遍历Element树的方法。</li> <li>我们可以通过<code>Element.widget</code>得到<code>element</code>节点对应的widget；我们已经反复讲过Widget和Element的对应关系，读者通过这些源码来加深理解。</li></ol> <h3 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h3> <p>Flutter中通过通知冒泡实现了一套自低向上的消息传递机制，这个和Web开发中浏览器的事件冒泡原理类似，Web开发者可以类比学习。另外我们通过源码了解了Flutter 通知冒泡的流程和原理，便于读者加深理解和学习Flutter的框架设计思想，在此，再次建议读者在平时学习中能多看看源码，定会受益匪浅。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/128.c75c7015.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter9/animated_switcher.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>9.6 通用“动画切换”组件（AnimatedSwitcher） | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/56.8a9849da.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter9/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_9-6-通用-动画切换-组件-animatedswitcher\"><a href=\"#_9-6-通用-动画切换-组件-animatedswitcher\" class=\"header-anchor\">#</a> 9.6 通用“动画切换”组件（AnimatedSwitcher）</h1> <p>实际开发中，我们经常会遇到切换UI元素的场景，比如Tab切换、路由切换。为了增强用户体验，通常在切换时都会指定一个动画，以使切换过程显得平滑。Flutter SDK组件库中已经提供了一些常用的切换组件，如<code>PageView</code>、<code>TabView</code>等，但是，这些组件并不能覆盖全部的需求场景，为此，Flutter SDK中提供了一个<code>AnimatedSwitcher</code>组件，它定义了一种通用的UI切换抽象。</p> <h2 id=\"_9-6-1-animatedswitcher\"><a href=\"#_9-6-1-animatedswitcher\" class=\"header-anchor\">#</a> 9.6.1 AnimatedSwitcher</h2> <p><code>AnimatedSwitcher</code> 可以同时对其新、旧子元素添加显示、隐藏动画。也就是说在<code>AnimatedSwitcher</code>的子元素发生变化时，会对其旧元素和新元素，我们先看看<code>AnimatedSwitcher</code> 的定义：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">const</span> <span class=\"token function\">AnimatedSwitcher</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  Key key<span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span>\n  <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>duration<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 新child显示动画时长</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>reverseDuration<span class=\"token punctuation\">,</span><span class=\"token comment\">// 旧child隐藏的动画时长</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>switchInCurve <span class=\"token operator\">=</span> Curves<span class=\"token punctuation\">.</span>linear<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 新child显示的动画曲线</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>switchOutCurve <span class=\"token operator\">=</span> Curves<span class=\"token punctuation\">.</span>linear<span class=\"token punctuation\">,</span><span class=\"token comment\">// 旧child隐藏的动画曲线</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>transitionBuilder <span class=\"token operator\">=</span> AnimatedSwitcher<span class=\"token punctuation\">.</span>defaultTransitionBuilder<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 动画构建器</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>layoutBuilder <span class=\"token operator\">=</span> AnimatedSwitcher<span class=\"token punctuation\">.</span>defaultLayoutBuilder<span class=\"token punctuation\">,</span> <span class=\"token comment\">//布局构建器</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre></div><p>当<code>AnimatedSwitcher</code>的child发生变化时（类型或Key不同），旧child会执行隐藏动画，新child会执行执行显示动画。究竟执行何种动画效果则由<code>transitionBuilder</code>参数决定，该参数接受一个<code>AnimatedSwitcherTransitionBuilder</code>类型的builder，定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">typedef</span> AnimatedSwitcherTransitionBuilder <span class=\"token operator\">=</span>\n  Widget <span class=\"token keyword\">Function</span><span class=\"token punctuation\">(</span>Widget child<span class=\"token punctuation\">,</span> Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> animation<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>该<code>builder</code>在<code>AnimatedSwitcher</code>的child切换时会分别对新、旧child绑定动画：</p> <ol><li>对旧child，绑定的动画会反向执行（reverse）</li> <li>对新child，绑定的动画会正向指向（forward）</li></ol> <p>这样一下，便实现了对新、旧child的动画绑定。<code>AnimatedSwitcher</code>的默认值是<code>AnimatedSwitcher.defaultTransitionBuilder</code> ：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Widget <span class=\"token function\">defaultTransitionBuilder</span><span class=\"token punctuation\">(</span>Widget child<span class=\"token punctuation\">,</span> Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> animation<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">FadeTransition</span><span class=\"token punctuation\">(</span>\n    opacity<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> child<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以看到，返回了<code>FadeTransition</code>对象，也就是说默认情况，<code>AnimatedSwitcher</code>会对新旧child执行“渐隐”和“渐显”动画。</p> <h3 id=\"例子\"><a href=\"#例子\" class=\"header-anchor\">#</a> 例子</h3> <p>下面我们看一个列子：实现一个计数器，然后再每一次自增的过程中，旧数字执行缩小动画隐藏，新数字执行放大动画显示，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">AnimatedSwitcherCounterRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n   <span class=\"token keyword\">const</span> <span class=\"token function\">AnimatedSwitcherCounterRoute</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>Key key<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n   <span class=\"token metadata symbol\">@override</span>\n   _AnimatedSwitcherCounterRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">_AnimatedSwitcherCounterRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n <span class=\"token punctuation\">}</span>\n\n <span class=\"token keyword\">class</span> <span class=\"token class-name\">_AnimatedSwitcherCounterRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>AnimatedSwitcherCounterRoute<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n   int _count <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span>\n\n   <span class=\"token metadata symbol\">@override</span>\n   Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n     <span class=\"token keyword\">return</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n       child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n         mainAxisAlignment<span class=\"token punctuation\">:</span> MainAxisAlignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n         children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n           <span class=\"token function\">AnimatedSwitcher</span><span class=\"token punctuation\">(</span>\n             duration<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>milliseconds<span class=\"token punctuation\">:</span> <span class=\"token number\">500</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n             transitionBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>Widget child<span class=\"token punctuation\">,</span> Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> animation<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n               <span class=\"token comment\">//执行缩放动画</span>\n               <span class=\"token keyword\">return</span> <span class=\"token function\">ScaleTransition</span><span class=\"token punctuation\">(</span>child<span class=\"token punctuation\">:</span> child<span class=\"token punctuation\">,</span> scale<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n             <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n             child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>\n               <span class=\"token string\">'$_count'</span><span class=\"token punctuation\">,</span>\n               <span class=\"token comment\">//显示指定key，不同的key会被认为是不同的Text，这样才能执行动画</span>\n               key<span class=\"token punctuation\">:</span> ValueKey<span class=\"token operator\">&lt;</span>int<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>_count<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n               style<span class=\"token punctuation\">:</span> Theme<span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>textTheme<span class=\"token punctuation\">.</span>headline4<span class=\"token punctuation\">,</span>\n             <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n           <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n           <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n             child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">'+1'</span><span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n             onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n               <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                 _count <span class=\"token operator\">+=</span> <span class=\"token number\">1</span><span class=\"token punctuation\">;</span>\n               <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n             <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n           <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n         <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n       <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n     <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n   <span class=\"token punctuation\">}</span>\n <span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行示例代码，当点击“+1”按钮时，原先的数字会逐渐缩小直至隐藏，而新数字会逐渐放大，我截取了动画执行过程的一帧，如图9-5所示：</p> <p><img src=\"data:image/png;base64,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\" alt=\"图9-5\"></p> <p>上图是第一次点击“+1”按钮后切换动画的一帧，此时“0”正在逐渐缩小，而“1”正在“0”的中间，正在逐渐放大。</p> <blockquote><p>注意：AnimatedSwitcher的新旧child，如果类型相同，则Key必须不相等。</p></blockquote> <h3 id=\"animatedswitcher实现原理\"><a href=\"#animatedswitcher实现原理\" class=\"header-anchor\">#</a> AnimatedSwitcher实现原理</h3> <p>实际上，<code>AnimatedSwitcher</code>的实现原理是比较简单的，我们根据<code>AnimatedSwitcher</code>的使用方式也可以猜个大概。要想实现新旧child切换动画，只需要明确两个问题：动画执行的时机是和如何对新旧child执行动画。从<code>AnimatedSwitcher</code>的使用方式我们可以看到，当child发生变化时（子widget的key和类型<strong>不</strong>同时相等则认为发生变化），则重新会重新执行<code>build</code>，然后动画开始执行。我们可以通过继承StatefulWidget来实现<code>AnimatedSwitcher</code>，具体做法是在<code>didUpdateWidget</code> 回调中判断其新旧child是否发生变化，如果发生变化，则对旧child执行反向退场（reverse）动画，对新child执行正向（forward）入场动画即可。下面是<code>AnimatedSwitcher</code>实现的部分核心伪代码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Widget _widget<span class=\"token punctuation\">;</span> <span class=\"token comment\">//</span>\n<span class=\"token keyword\">void</span> <span class=\"token function\">didUpdateWidget</span><span class=\"token punctuation\">(</span>AnimatedSwitcher oldWidget<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">didUpdateWidget</span><span class=\"token punctuation\">(</span>oldWidget<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">// 检查新旧child是否发生变化(key和类型同时相等则返回true，认为没变化)</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>Widget<span class=\"token punctuation\">.</span><span class=\"token function\">canUpdate</span><span class=\"token punctuation\">(</span>widget<span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span> oldWidget<span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// child没变化，...</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//child发生了变化，构建一个Stack来分别给新旧child执行动画</span>\n   _widget<span class=\"token operator\">=</span> <span class=\"token function\">Stack</span><span class=\"token punctuation\">(</span>\n      alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">,</span>\n      children<span class=\"token punctuation\">:</span><span class=\"token punctuation\">[</span>\n        <span class=\"token comment\">//旧child应用FadeTransition</span>\n        <span class=\"token function\">FadeTransition</span><span class=\"token punctuation\">(</span>\n         opacity<span class=\"token punctuation\">:</span> _controllerOldAnimation<span class=\"token punctuation\">,</span>\n         child <span class=\"token punctuation\">:</span> oldWidget<span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token comment\">//新child应用FadeTransition</span>\n        <span class=\"token function\">FadeTransition</span><span class=\"token punctuation\">(</span>\n         opacity<span class=\"token punctuation\">:</span> _controllerNewAnimation<span class=\"token punctuation\">,</span>\n         child <span class=\"token punctuation\">:</span> widget<span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">]</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">// 给旧child执行反向退场动画</span>\n    _controllerOldAnimation<span class=\"token punctuation\">.</span><span class=\"token function\">reverse</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//给新child执行正向入场动画</span>\n    _controllerNewAnimation<span class=\"token punctuation\">.</span><span class=\"token function\">forward</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">//build方法</span>\nWidget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> _widget<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面伪代码展示了<code>AnimatedSwitcher</code>实现的核心逻辑，当然<code>AnimatedSwitcher</code>真正的实现比这个复杂，它可以自定义进退场过渡动画以及执行动画时的布局等。在此，我们删繁就简，通过伪代码形式让读者能够清楚看到主要的实现思路，具体的实现读者可以参考<code>AnimatedSwitcher</code>源码。</p> <p>另外，Flutter SDK中还提供了一个<code>AnimatedCrossFade</code>组件，它也可以切换两个子元素，切换过程执行渐隐渐显的动画，和<code>AnimatedSwitcher</code>不同的是<code>AnimatedCrossFade</code>是针对两个子元素，而<code>AnimatedSwitcher</code>是在一个子元素的新旧值之间切换。<code>AnimatedCrossFade</code>实现原理比较简单，也有和<code>AnimatedSwitcher</code>类似的地方，因此不再赘述，读者有兴趣可以查看其源码。</p> <h2 id=\"_9-6-2-animatedswitcher高级用法\"><a href=\"#_9-6-2-animatedswitcher高级用法\" class=\"header-anchor\">#</a> 9.6.2 AnimatedSwitcher高级用法</h2> <p>假设现在我们想实现一个类似路由平移切换的动画：旧页面屏幕中向左侧平移退出，新页面重屏幕右侧平移进入。如果要用AnimatedSwitcher的话，我们很快就会发现一个问题：做不到！我们可能会写出下面的代码：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">AnimatedSwitcher</span><span class=\"token punctuation\">(</span>\n  duration<span class=\"token punctuation\">:</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>milliseconds<span class=\"token punctuation\">:</span> <span class=\"token number\">200</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  transitionBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>Widget child<span class=\"token punctuation\">,</span> Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> animation<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">var</span> tween<span class=\"token operator\">=</span>Tween<span class=\"token operator\">&lt;</span>Offset<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>begin<span class=\"token punctuation\">:</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">:</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n     <span class=\"token keyword\">return</span> <span class=\"token function\">SlideTransition</span><span class=\"token punctuation\">(</span>\n       child<span class=\"token punctuation\">:</span> child<span class=\"token punctuation\">,</span>\n       position<span class=\"token punctuation\">:</span> tween<span class=\"token punctuation\">.</span><span class=\"token function\">animate</span><span class=\"token punctuation\">(</span>animation<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token comment\">//省略</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>上面的代码有什么问题呢？我们前面说过在<code>AnimatedSwitcher</code>的child切换时会分别对新child执行正向动画（forward），而对旧child执行反向动画（reverse），所以真正的效果便是：新child确实从屏幕右侧平移进入了，但旧child却会从屏幕<strong>右侧</strong>（而不是左侧）退出。其实也很容易理解，因为在没有特殊处理的情况下，同一个动画的正向和逆向正好是相反（对称）的。</p> <p>那么问题来了，难道就不能使用<code>AnimatedSwitcher</code>了？答案当然是否定的！仔细想想这个问题，究其原因，就是因为同一个<code>Animation</code>正向（forward）和反向（reverse）是对称的。所以如果我们可以打破这种对称性，那么便可以实现这个功能了，下面我们来封装一个<code>MySlideTransition</code>，它与<code>SlideTransition</code>唯一的不同就是对动画的反向执行进行了定制（从左边滑出隐藏），代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">MySlideTransition</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">AnimatedWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">MySlideTransition</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n    <span class=\"token metadata symbol\">@required</span> Animation<span class=\"token operator\">&lt;</span>Offset<span class=\"token operator\">&gt;</span> position<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>transformHitTests <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">:</span> <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span>position <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">,</span> listenable<span class=\"token punctuation\">:</span> position<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">;</span>\n\n  Animation<span class=\"token operator\">&lt;</span>Offset<span class=\"token operator\">&gt;</span> <span class=\"token keyword\">get</span> position <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> listenable<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> bool transformHitTests<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> Widget child<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    Offset offset<span class=\"token operator\">=</span>position<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//动画反向执行时，调整x偏移，实现“从左边滑出隐藏”</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>position<span class=\"token punctuation\">.</span>status <span class=\"token operator\">==</span> AnimationStatus<span class=\"token punctuation\">.</span>reverse<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n         offset <span class=\"token operator\">=</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token operator\">-</span>offset<span class=\"token punctuation\">.</span>dx<span class=\"token punctuation\">,</span> offset<span class=\"token punctuation\">.</span>dy<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">FractionalTranslation</span><span class=\"token punctuation\">(</span>\n      translation<span class=\"token punctuation\">:</span> offset<span class=\"token punctuation\">,</span>\n      transformHitTests<span class=\"token punctuation\">:</span> transformHitTests<span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> child<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>调用时，将<code>SlideTransition</code>替换成<code>MySlideTransition</code>即可：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">AnimatedSwitcher</span><span class=\"token punctuation\">(</span>\n  duration<span class=\"token punctuation\">:</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>milliseconds<span class=\"token punctuation\">:</span> <span class=\"token number\">200</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  transitionBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>Widget child<span class=\"token punctuation\">,</span> Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> animation<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">var</span> tween<span class=\"token operator\">=</span>Tween<span class=\"token operator\">&lt;</span>Offset<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>begin<span class=\"token punctuation\">:</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">:</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n     <span class=\"token keyword\">return</span> <span class=\"token function\">MySlideTransition</span><span class=\"token punctuation\">(</span>\n              child<span class=\"token punctuation\">:</span> child<span class=\"token punctuation\">,</span>\n              position<span class=\"token punctuation\">:</span> tween<span class=\"token punctuation\">.</span><span class=\"token function\">animate</span><span class=\"token punctuation\">(</span>animation<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    \t      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token comment\">//省略</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行后，我截取动画执行过程中的一帧，如图9-6所示：</p> <p><img src=\"/assets/img/9-6.7d1d25c1.png\" alt=\"图9-6\"></p> <p>上图中“0”从左侧滑出，而“1”从右侧滑入。可以看到，我们通过这种巧妙的方式实现了类似路由进场切换的动画，实际上Flutter路由切换也正是通过<code>AnimatedSwitcher</code>来实现的。</p> <h3 id=\"slidetransitionx\"><a href=\"#slidetransitionx\" class=\"header-anchor\">#</a> SlideTransitionX</h3> <p>上面的示例我们实现了“左出右入”的动画，那如果要实现“右入左出”、“上入下出”或者 “下入上出”怎么办？当然，我们可以分别修改上面的代码，但是这样每种动画都得单独定义一个“Transition”，这很麻烦。本节将封装一个通用的<code>SlideTransitionX</code> 来实现这种“出入滑动动画”，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">SlideTransitionX</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">AnimatedWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">SlideTransitionX</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n    <span class=\"token metadata symbol\">@required</span> Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> position<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>transformHitTests <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>direction <span class=\"token operator\">=</span> AxisDirection<span class=\"token punctuation\">.</span>down<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">:</span> <span class=\"token keyword\">assert</span><span class=\"token punctuation\">(</span>position <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">,</span> listenable<span class=\"token punctuation\">:</span> position<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 偏移在内部处理      </span>\n    <span class=\"token keyword\">switch</span> <span class=\"token punctuation\">(</span>direction<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">case</span> AxisDirection<span class=\"token punctuation\">.</span>up<span class=\"token punctuation\">:</span>\n        _tween <span class=\"token operator\">=</span> <span class=\"token function\">Tween</span><span class=\"token punctuation\">(</span>begin<span class=\"token punctuation\">:</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">:</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">case</span> AxisDirection<span class=\"token punctuation\">.</span>right<span class=\"token punctuation\">:</span>\n        _tween <span class=\"token operator\">=</span> <span class=\"token function\">Tween</span><span class=\"token punctuation\">(</span>begin<span class=\"token punctuation\">:</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token operator\">-</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">:</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">case</span> AxisDirection<span class=\"token punctuation\">.</span>down<span class=\"token punctuation\">:</span>\n        _tween <span class=\"token operator\">=</span> <span class=\"token function\">Tween</span><span class=\"token punctuation\">(</span>begin<span class=\"token punctuation\">:</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token operator\">-</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">:</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">case</span> AxisDirection<span class=\"token punctuation\">.</span>left<span class=\"token punctuation\">:</span>\n        _tween <span class=\"token operator\">=</span> <span class=\"token function\">Tween</span><span class=\"token punctuation\">(</span>begin<span class=\"token punctuation\">:</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">:</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n\n  Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> <span class=\"token keyword\">get</span> position <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> listenable<span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> bool transformHitTests<span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> Widget child<span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">//退场（出）方向</span>\n  <span class=\"token keyword\">final</span> AxisDirection direction<span class=\"token punctuation\">;</span>\n\n  Tween<span class=\"token operator\">&lt;</span>Offset<span class=\"token operator\">&gt;</span> _tween<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    Offset offset <span class=\"token operator\">=</span> _tween<span class=\"token punctuation\">.</span><span class=\"token function\">evaluate</span><span class=\"token punctuation\">(</span>position<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>position<span class=\"token punctuation\">.</span>status <span class=\"token operator\">==</span> AnimationStatus<span class=\"token punctuation\">.</span>reverse<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">switch</span> <span class=\"token punctuation\">(</span>direction<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">case</span> AxisDirection<span class=\"token punctuation\">.</span>up<span class=\"token punctuation\">:</span>\n          offset <span class=\"token operator\">=</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span>offset<span class=\"token punctuation\">.</span>dx<span class=\"token punctuation\">,</span> <span class=\"token operator\">-</span>offset<span class=\"token punctuation\">.</span>dy<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">case</span> AxisDirection<span class=\"token punctuation\">.</span>right<span class=\"token punctuation\">:</span>\n          offset <span class=\"token operator\">=</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token operator\">-</span>offset<span class=\"token punctuation\">.</span>dx<span class=\"token punctuation\">,</span> offset<span class=\"token punctuation\">.</span>dy<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">case</span> AxisDirection<span class=\"token punctuation\">.</span>down<span class=\"token punctuation\">:</span>\n          offset <span class=\"token operator\">=</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span>offset<span class=\"token punctuation\">.</span>dx<span class=\"token punctuation\">,</span> <span class=\"token operator\">-</span>offset<span class=\"token punctuation\">.</span>dy<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">case</span> AxisDirection<span class=\"token punctuation\">.</span>left<span class=\"token punctuation\">:</span>\n          offset <span class=\"token operator\">=</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token operator\">-</span>offset<span class=\"token punctuation\">.</span>dx<span class=\"token punctuation\">,</span> offset<span class=\"token punctuation\">.</span>dy<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">FractionalTranslation</span><span class=\"token punctuation\">(</span>\n      translation<span class=\"token punctuation\">:</span> offset<span class=\"token punctuation\">,</span>\n      transformHitTests<span class=\"token punctuation\">:</span> transformHitTests<span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> child<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>现在如果我们想实现各种“滑动出入动画”便非常容易，只需给<code>direction</code>传递不同的方向值即可，比如要实现“上入下出”，则：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">AnimatedSwitcher</span><span class=\"token punctuation\">(</span>\n  duration<span class=\"token punctuation\">:</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>milliseconds<span class=\"token punctuation\">:</span> <span class=\"token number\">200</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  transitionBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>Widget child<span class=\"token punctuation\">,</span> Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> animation<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">var</span> tween<span class=\"token operator\">=</span>Tween<span class=\"token operator\">&lt;</span>Offset<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>begin<span class=\"token punctuation\">:</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">:</span> <span class=\"token function\">Offset</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n     <span class=\"token keyword\">return</span> <span class=\"token function\">SlideTransitionX</span><span class=\"token punctuation\">(</span>\n              child<span class=\"token punctuation\">:</span> child<span class=\"token punctuation\">,</span>\n     \t\t\t\t  direction<span class=\"token punctuation\">:</span> AxisDirection<span class=\"token punctuation\">.</span>down<span class=\"token punctuation\">,</span> <span class=\"token comment\">//上入下出</span>\n              position<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">,</span>\n    \t      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token comment\">//省略其余代码</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>运行后，我截取动画执行过程中的一帧，如图9-7所示：</p> <p><img src=\"/assets/img/9-7.d8052e62.png\" alt=\"图9-7\"></p> <p>上图中“1”从底部滑出，而“2”从顶部滑入。读者可以尝试给<code>SlideTransitionX</code>的<code>direction</code>取不同的值来查看运行效果。</p> <h2 id=\"总结\"><a href=\"#总结\" class=\"header-anchor\">#</a> 总结</h2> <p>本节我们学习了<code>AnimatedSwitcher</code>的详细用法，同时也介绍了打破<code>AnimatedSwitcher</code>动画对称性的方法。我们可以发现：在需要切换新旧UI元素的场景，<code>AnimatedSwitcher</code>将十分实用。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/56.8a9849da.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter9/animated_widgets.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>9.7 动画过渡组件 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/57.371d9d12.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter9/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_9-7-动画过渡组件\"><a href=\"#_9-7-动画过渡组件\" class=\"header-anchor\">#</a> 9.7 动画过渡组件</h1> <p>为了表述方便，本书约定，将在Widget属性发生变化时会执行过渡动画的组件统称为”动画过渡组件“，而动画过渡组件最明显的一个特征就是它会在内部自管理<code>AnimationController</code>。我们知道，为了方便使用者可以自定义动画的曲线、执行时长、方向等，在前面介绍过的动画封装方法中，通常都需要使用者自己提供一个<code>AnimationController</code>对象来自定义这些属性值。但是，如此一来，使用者就必须得手动管理<code>AnimationController</code>，这又会增加使用的复杂性。因此，如果也能将<code>AnimationController</code>进行封装，则会大大提高动画组件的易用性。</p> <h2 id=\"_9-7-1-自定义动画过渡组件\"><a href=\"#_9-7-1-自定义动画过渡组件\" class=\"header-anchor\">#</a> 9.7.1 自定义动画过渡组件</h2> <p>我们要实现一个<code>AnimatedDecoratedBox</code>，它可以在<code>decoration</code>属性发生变化时，从旧状态变成新状态的过程可以执行一个过渡动画。根据前面所学的知识，我们实现了一个<code>AnimatedDecoratedBox1</code>组件：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">AnimatedDecoratedBox1</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">AnimatedDecoratedBox1</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n    <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>decoration<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>curve <span class=\"token operator\">=</span> Curves<span class=\"token punctuation\">.</span>linear<span class=\"token punctuation\">,</span>\n    <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>duration<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>reverseDuration<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> BoxDecoration decoration<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> Widget child<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> Duration duration<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> Curve curve<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> Duration reverseDuration<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  _AnimatedDecoratedBox1State <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">_AnimatedDecoratedBox1State</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_AnimatedDecoratedBox1State</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>AnimatedDecoratedBox1<span class=\"token operator\">&gt;</span>\n    <span class=\"token keyword\">with</span> SingleTickerProviderStateMixin <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@protected</span>\n  AnimationController <span class=\"token keyword\">get</span> controller <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _controller<span class=\"token punctuation\">;</span>\n  AnimationController _controller<span class=\"token punctuation\">;</span>\n\n  Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> <span class=\"token keyword\">get</span> animation <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _animation<span class=\"token punctuation\">;</span>\n  Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> _animation<span class=\"token punctuation\">;</span>\n\n  DecorationTween _tween<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">AnimatedBuilder</span><span class=\"token punctuation\">(</span>\n      animation<span class=\"token punctuation\">:</span> _animation<span class=\"token punctuation\">,</span>\n      builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> child<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">return</span> <span class=\"token function\">DecoratedBox</span><span class=\"token punctuation\">(</span>\n          decoration<span class=\"token punctuation\">:</span> _tween<span class=\"token punctuation\">.</span><span class=\"token function\">animate</span><span class=\"token punctuation\">(</span>_animation<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span>\n          child<span class=\"token punctuation\">:</span> child<span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> widget<span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    _controller <span class=\"token operator\">=</span> <span class=\"token function\">AnimationController</span><span class=\"token punctuation\">(</span>\n      duration<span class=\"token punctuation\">:</span> widget<span class=\"token punctuation\">.</span>duration<span class=\"token punctuation\">,</span>\n      reverseDuration<span class=\"token punctuation\">:</span> widget<span class=\"token punctuation\">.</span>reverseDuration<span class=\"token punctuation\">,</span>\n      vsync<span class=\"token punctuation\">:</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    _tween <span class=\"token operator\">=</span> <span class=\"token function\">DecorationTween</span><span class=\"token punctuation\">(</span>begin<span class=\"token punctuation\">:</span> widget<span class=\"token punctuation\">.</span>decoration<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token function\">_updateCurve</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">void</span> <span class=\"token function\">_updateCurve</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>widget<span class=\"token punctuation\">.</span>curve <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span>\n      _animation <span class=\"token operator\">=</span> <span class=\"token function\">CurvedAnimation</span><span class=\"token punctuation\">(</span>parent<span class=\"token punctuation\">:</span> _controller<span class=\"token punctuation\">,</span> curve<span class=\"token punctuation\">:</span> widget<span class=\"token punctuation\">.</span>curve<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">else</span>\n      _animation <span class=\"token operator\">=</span> _controller<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">didUpdateWidget</span><span class=\"token punctuation\">(</span>AnimatedDecoratedBox1 oldWidget<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">didUpdateWidget</span><span class=\"token punctuation\">(</span>oldWidget<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>widget<span class=\"token punctuation\">.</span>curve <span class=\"token operator\">!=</span> oldWidget<span class=\"token punctuation\">.</span>curve<span class=\"token punctuation\">)</span>\n      <span class=\"token function\">_updateCurve</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    _controller<span class=\"token punctuation\">.</span>duration <span class=\"token operator\">=</span> widget<span class=\"token punctuation\">.</span>duration<span class=\"token punctuation\">;</span>\n    _controller<span class=\"token punctuation\">.</span>reverseDuration <span class=\"token operator\">=</span> widget<span class=\"token punctuation\">.</span>reverseDuration<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>widget<span class=\"token punctuation\">.</span>decoration<span class=\"token operator\">!=</span> <span class=\"token punctuation\">(</span>_tween<span class=\"token punctuation\">.</span>end <span class=\"token operator\">?</span><span class=\"token operator\">?</span> _tween<span class=\"token punctuation\">.</span>begin<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n      _tween\n        <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>begin <span class=\"token operator\">=</span> _tween<span class=\"token punctuation\">.</span><span class=\"token function\">evaluate</span><span class=\"token punctuation\">(</span>_animation<span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>end <span class=\"token operator\">=</span> widget<span class=\"token punctuation\">.</span>decoration<span class=\"token punctuation\">;</span>\n      _controller\n        <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>value <span class=\"token operator\">=</span> <span class=\"token number\">0.0</span>\n        <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token function\">forward</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    _controller<span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>下面我们来使用<code>AnimatedDecoratedBox1</code>来实现按钮点击后背景色从蓝色过渡到红色的效果：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Color _decorationColor <span class=\"token operator\">=</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">var</span> duration <span class=\"token operator\">=</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token comment\">//省略无关代码</span>\n<span class=\"token function\">AnimatedDecoratedBox</span><span class=\"token punctuation\">(</span>\n  duration<span class=\"token punctuation\">:</span> duration<span class=\"token punctuation\">,</span>\n  decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> _decorationColor<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n    onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        _decorationColor <span class=\"token operator\">=</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>\n      <span class=\"token string\">&quot;AnimatedDecoratedBox&quot;</span><span class=\"token punctuation\">,</span>\n      style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>点击前效果如图9-8所示，点击后截取了过渡过程的一帧如图9-9所示： <img src=\"data:image/png;base64,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Ow40Fzl2XaO5Ns9TBXb6hjrZov1lZSAU7atpnpqLrtU+fLYwLfvOsb+0JaFtAPWjJs6yWefZm5vG7MzcZZO61jVysPvhKZGgJg4aX3boZ72s436al9ZHvytToCOZWg/VHLRc0z+xj2t1ZFF9i2vLNRU/nV5oXnfxlZjRtoPs0PXqn5dCCr5Kwzez4uXsWqkuQT9rfN0HndivF3PNSf1Ww1m2tmxirvXUmus+ajl3ZRKaGWb75bqD8FFBpGwQuw66x+hQRz4lHDRHfTWzxlOfuh++7PbYxaV8RwXFj192x+6v6AlYABT9En95Bn7ZAuDLm5l7MoHoErAAiO7an67l6FI3EzABEzABEzCBqBGwAIjaitteEzABEzABE4CABYBvAxMwARMwAROIIAELgAguuk02ARMwARMwAQsA3wMmYAImYAImEEECFgARXHSbbAImYAImYAIWAL4HTMAETMAETCCCBCwAIrjoNtkETMAETMAELAB8D5iACZiACZhABAlYAERw0W2yCZiACZiACVgA+B4wARMwARMwgQgSsACI4KLbZBMwARMwAROwAPA9YAImYAImYAIRJGABEMFFt8kmYAImYAImYAHge8AETMAETMAEIkjAAiCCi26TTcAETMAETMACwPeACZiACZiACUSQgAVABBfdJpuACZiACZiABYDvARMwARMwAROIIAELgAguuk02ARMwARMwAQsA3wMmYAImYAImEEECFgARXHSbbAImYAImYAKxZDLZaAwm0BoCW/c3hnc2N7TmUl9jAiZwhghM610SaipjZ2g0D1NMBCwAimk182xLBt+ftlzMM2V3bwJtI5DA98edy20bNF+dJZAwBxNoLQE9ZOKtvdjXmYAJmIAJFDQB68aCXh5PzgRMwARMwATyQ8ACID9c3asJmIAJmIAJFDQBC4CCXh5PzgRMwARMwATyQ8ACID9c3asJmIAJmIAJFDQBC4CCXh5PzgRMwARMwATyQ8ACID9c3asJmIAJmIAJFDQBC4CCXh5PzgRMwARMwATyQ8ACID9c3asJmIAJmIAJFDQBC4CCXh5PzgRMwARMwATyQ8ACID9c3asJmIAJmIAJFDQBC4CCXh5PzgRMwARMwATyQ8ACID9c3asJmIAJmIAJFDQBC4CCXh5PzgRMwARMwATyQ8ACID9c3asJmIAJmIAJFDQBC4CCXh5PzgRMwARMwATyQ8ACID9c3asJmIAJmIAJFDSB/wN9/AZcme7YQQAAAABJRU5ErkJggg==\" alt=\"img\"><img src=\"data:image/png;base64,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+XFQoXmzf25v5Tx4PYrcn7p9/M+Hffvpj6l5+KnHouu9nQQkAN7OdYvqqNdCAER1AupMBAJIQAIggIsa5SkpBRBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSYgARBl4OpOBERABERABGKBgARALKyCxiACIiACIiACUSaQMD8/vxzlPtXdW0Zg/NGU3bs98JaNWsMVgWATqGkotbyCrGBPUrNbUwISAGuKNxgXX1patqWlpWBMRrMQgYAQSExMtMTEhIDMRtNYDwLJ69Gp+ny7CPAhk5iY9HYNWqMVAREQARGISEA1ABHx6KAIiIAIiIAIBJOABEAw11WzEgEREAEREIGIBCQAIuLRQREQAREQAREIJgEJgGCuq2YlAiIgAiIgAhEJSABExKODIiACIiACIhBMAhIAwVxXzUoEREAEREAEIhKQAIiIRwdFQAREQAREIJgEJACCua6alQiIgAiIgAhEJCABEBGPDoqACIiACIhAMAlIAARzXTUrERABERABEYhIQAIgIh4dFAEREAEREIFgEpAACOa6alYiIAIiIAIiEJGABEBEPDooAiIgAiIgAsEkIAEQzHXVrERABERABEQgIgEJgIh4dFAEREAEREAEgklAAiCY66pZiYAIiIAIiEBEAv8PGQS+vtZoAOcAAAAASUVORK5CYII=\" alt=\"img\"></p> <p>点击后，按钮背景色会从蓝色向红色过渡，图9-9是过渡过程中的一帧，有点偏紫色，整个过渡动画结束后背景会变为红色。</p> <p>上面的代码虽然实现了我们期望的功能，但是代码却比较复杂。稍加思考后，我们就可以发现，<code>AnimationController</code>的管理以及Tween更新部分的代码都是可以抽象出来的，如果我们这些通用逻辑封装成基类，那么要实现动画过渡组件只需要继承这些基类，然后定制自身不同的代码（比如动画每一帧的构建方法）即可，这样将会简化代码。</p> <p>为了方便开发者来实现动画过渡组件的封装，Flutter提供了一个<code>ImplicitlyAnimatedWidget</code>抽象类，它继承自StatefulWidget，同时提供了一个对应的<code>ImplicitlyAnimatedWidgetState</code>类，<code>AnimationController</code>的管理就在<code>ImplicitlyAnimatedWidgetState</code>类中。开发者如果要封装动画，只需要分别继承<code>ImplicitlyAnimatedWidget</code>和<code>ImplicitlyAnimatedWidgetState</code>类即可，下面我们演示一下具体如何实现。</p> <p>我们需要分两步实现：</p> <ol><li><p>继承<code>ImplicitlyAnimatedWidget</code>类。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">AnimatedDecoratedBox</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">ImplicitlyAnimatedWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">AnimatedDecoratedBox</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n    <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>decoration<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span>\n    Curve curve <span class=\"token operator\">=</span> Curves<span class=\"token punctuation\">.</span>linear<span class=\"token punctuation\">,</span> <span class=\"token comment\">//动画曲线</span>\n    <span class=\"token metadata symbol\">@required</span> Duration duration<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 正向动画执行时长</span>\n    Duration reverseDuration<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 反向动画执行时长</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>\n          key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">,</span>\n          curve<span class=\"token punctuation\">:</span> curve<span class=\"token punctuation\">,</span>\n          duration<span class=\"token punctuation\">:</span> duration<span class=\"token punctuation\">,</span>\n          reverseDuration<span class=\"token punctuation\">:</span> reverseDuration<span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> BoxDecoration decoration<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> Widget child<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  _AnimatedDecoratedBoxState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">_AnimatedDecoratedBoxState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>其中<code>curve</code>、<code>duration</code>、<code>reverseDuration</code>三个属性在<code>ImplicitlyAnimatedWidget</code>中已定义。 可以看到<code>AnimatedDecoratedBox</code>类和普通继承自<code>StatefulWidget</code>的类没有什么不同。</p></li> <li><p>State类继承自<code>AnimatedWidgetBaseState</code>（该类继承自<code>ImplicitlyAnimatedWidgetState</code>类）。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">_AnimatedDecoratedBoxState</span>\n    <span class=\"token keyword\">extends</span> <span class=\"token class-name\">AnimatedWidgetBaseState</span><span class=\"token operator\">&lt;</span>AnimatedDecoratedBox<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  DecorationTween _decoration<span class=\"token punctuation\">;</span> <span class=\"token comment\">//定义一个Tween</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">DecoratedBox</span><span class=\"token punctuation\">(</span>\n      decoration<span class=\"token punctuation\">:</span> _decoration<span class=\"token punctuation\">.</span><span class=\"token function\">evaluate</span><span class=\"token punctuation\">(</span>animation<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> widget<span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">forEachTween</span><span class=\"token punctuation\">(</span>visitor<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 在需要更新Tween时，基类会调用此方法</span>\n    _decoration <span class=\"token operator\">=</span> <span class=\"token function\">visitor</span><span class=\"token punctuation\">(</span>_decoration<span class=\"token punctuation\">,</span> widget<span class=\"token punctuation\">.</span>decoration<span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">(</span>value<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">DecorationTween</span><span class=\"token punctuation\">(</span>begin<span class=\"token punctuation\">:</span> value<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>可以看到我们实现了<code>build</code>和<code>forEachTween</code>两个方法。在动画执行过程中，每一帧都会调用<code>build</code>方法（调用逻辑在<code>ImplicitlyAnimatedWidgetState</code>中），所以在<code>build</code>方法中我们需要构建每一帧的<code>DecoratedBox</code>状态，因此得算出每一帧的<code>decoration</code> 状态，这个我们可以通过<code>_decoration.evaluate(animation)</code> 来算出，其中<code>animation</code>是<code>ImplicitlyAnimatedWidgetState</code>基类中定义的对象，<code>_decoration</code>是我们自定义的一个<code>DecorationTween</code>类型的对象，那么现在的问题就是它是在什么时候被赋值的呢？要回答这个问题，我们就得搞清楚什么时候需要对<code>_decoration</code>赋值。我们知道<code>_decoration</code>是一个Tween，而Tween的主要职责就是定义动画的起始状态（begin）和终止状态(end)。对于<code>AnimatedDecoratedBox</code>来说，<code>decoration</code>的终止状态就是用户传给它的值，而起始状态是不确定的，有以下两种情况：</p> <ol><li><code>AnimatedDecoratedBox</code>首次build，此时直接将其<code>decoration</code>值置为起始状态，即<code>_decoration</code>值为<code>DecorationTween(begin: decoration)</code> 。</li> <li><code>AnimatedDecoratedBox</code>的<code>decoration</code>更新时，则起始状态为<code>_decoration.animate(animation)</code>，即<code>_decoration</code>值为<code>DecorationTween(begin: _decoration.animate(animation)，end:decoration)</code>。</li></ol></li></ol> <p>现在<code>forEachTween</code>的作用就很明显了，它正是用于来更新Tween的初始值的，在上述两种情况下会被调用，而开发者只需重写此方法，并在此方法中更新Tween的起始状态值即可。而一些更新的逻辑被屏蔽在了<code>visitor</code>回调，我们只需要调用它并给它传递正确的参数即可，<code>visitor</code>方法签名如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>   Tween <span class=\"token function\">visitor</span><span class=\"token punctuation\">(</span>\n     Tween<span class=\"token operator\">&lt;</span><span class=\"token keyword\">dynamic</span><span class=\"token operator\">&gt;</span> tween<span class=\"token punctuation\">,</span> <span class=\"token comment\">//当前的tween，第一次调用为null</span>\n     <span class=\"token keyword\">dynamic</span> targetValue<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 终止状态</span>\n     TweenConstructor<span class=\"token operator\">&lt;</span><span class=\"token keyword\">dynamic</span><span class=\"token operator\">&gt;</span> constructor，<span class=\"token comment\">//Tween构造器，在上述三种情况下会被调用以更新tween</span>\n   <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>可以看到，通过继承<code>ImplicitlyAnimatedWidget</code>和<code>ImplicitlyAnimatedWidgetState</code>类可以快速的实现动画过渡组件的封装，这和我们纯手工实现相比，代码简化了很多。</p> <blockquote><p>如果读者还有疑惑，建议查看<code>ImplicitlyAnimatedWidgetState</code>的源码并结合本示例代码对比理解。</p></blockquote> <h3 id=\"动画过渡组件的反向动画\"><a href=\"#动画过渡组件的反向动画\" class=\"header-anchor\">#</a> 动画过渡组件的反向动画</h3> <p>在使用动画过渡组件，我们只需要在改变一些属性值后重新build组件即可，所以要实现状态反向过渡，只需要将前后状态值互换即可实现，这本来是不需要再浪费笔墨的。但是<code>ImplicitlyAnimatedWidget</code>构造函数中却有一个<code>reverseDuration</code>属性用于设置反向动画的执行时长，这貌似在告诉读者<code>ImplicitlyAnimatedWidget</code>本身也提供了执行反向动画的接口，于是笔者查看了<code>ImplicitlyAnimatedWidgetState</code>源码并未发现有执行反向动画的接口，唯一有用的是它暴露了控制动画的<code>controller</code>。所以如果要让<code>reverseDuration</code>生效，我们只能先获取<code>controller</code>，然后再通过<code>controller.reverse()</code>来启动反向动画，比如我们在上面示例的基础上实现一个循环的点击背景颜色变换效果，要求从蓝色变为红色时动画执行时间为400ms，从红变蓝为2s，如果要使<code>reverseDuration</code>生效，我们需要这么做：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">AnimatedDecoratedBox</span><span class=\"token punctuation\">(</span>\n  duration<span class=\"token punctuation\">:</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span> milliseconds<span class=\"token punctuation\">:</span> <span class=\"token number\">400</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> _decorationColor<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  reverseDuration<span class=\"token punctuation\">:</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n      onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>_decorationColor <span class=\"token operator\">==</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          ImplicitlyAnimatedWidgetState _state <span class=\"token operator\">=</span>\n              context<span class=\"token punctuation\">.</span>findAncestorStateOfType<span class=\"token operator\">&lt;</span>ImplicitlyAnimatedWidgetState<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n           <span class=\"token comment\">// 通过controller来启动反向动画</span>\n          _state<span class=\"token punctuation\">.</span>controller<span class=\"token punctuation\">.</span><span class=\"token function\">reverse</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">// 经验证必须调用setState来触发rebuild，否则状态同步会有问题</span>\n            <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              _decorationColor <span class=\"token operator\">=</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            _decorationColor <span class=\"token operator\">=</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>\n        <span class=\"token string\">&quot;AnimatedDecoratedBox toggle&quot;</span><span class=\"token punctuation\">,</span>\n        style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>上面的代码实际上是非常糟糕且没必要的，它需要我们了解<code>ImplicitlyAnimatedWidgetState</code>内部实现，并且要手动去启动反向动画。我们完全可以通过如下代码实现相同的效果：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token function\">AnimatedDecoratedBox</span><span class=\"token punctuation\">(</span>\n  duration<span class=\"token punctuation\">:</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>\n      milliseconds<span class=\"token punctuation\">:</span> _decorationColor <span class=\"token operator\">==</span> Colors<span class=\"token punctuation\">.</span>red <span class=\"token operator\">?</span> <span class=\"token number\">400</span> <span class=\"token punctuation\">:</span> <span class=\"token number\">2000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> _decorationColor<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Builder</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n      onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          _decorationColor <span class=\"token operator\">=</span> _decorationColor <span class=\"token operator\">==</span> Colors<span class=\"token punctuation\">.</span>blue\n              <span class=\"token operator\">?</span> Colors<span class=\"token punctuation\">.</span>red\n              <span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>\n        <span class=\"token string\">&quot;AnimatedDecoratedBox toggle&quot;</span><span class=\"token punctuation\">,</span>\n        style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n</code></pre></div><p>这样的代码是不是优雅的多！那么现在问题来了，为什么<code>ImplicitlyAnimatedWidgetState</code>要提供一个<code>reverseDuration</code>参数呢？笔者仔细研究了<code>ImplicitlyAnimatedWidgetState</code>的实现，发现唯一的解释就是该参数并非是给<code>ImplicitlyAnimatedWidgetState</code>用的，而是给子类用的！原因正如我们前面说的，要使<code>reverseDuration</code> 有用就必须得获取<code>controller</code> 属性来手动启动反向动画，<code>ImplicitlyAnimatedWidgetState</code>中的<code>controller</code> 属性是一个保护属性，定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code> <span class=\"token metadata symbol\">@protected</span>\n  AnimationController <span class=\"token keyword\">get</span> controller <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> _controller<span class=\"token punctuation\">;</span>\n</code></pre></div><p>而保护属性原则上只应该在子类中使用，而不应该像上面示例代码一样在外部使用。综上，我们可以得出两条结论：</p> <ol><li><p>使用动画过渡组件时如果需要执行反向动画的场景，应尽量使用状态互换的方法，而不应该通过获取<code>ImplicitlyAnimatedWidgetState</code>中<code>controller</code>的方式。</p></li> <li><p>如果我们自定义的动画过渡组件用不到<code>reverseDuration</code> ，那么最好就不要暴露此参数，比如我们上面自定义的<code>AnimatedDecoratedBox</code>定义中就可以去除<code>reverseDuration</code> 可选参数，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">AnimatedDecoratedBox</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">ImplicitlyAnimatedWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">AnimatedDecoratedBox</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    Key key<span class=\"token punctuation\">,</span>\n    <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>decoration<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span>\n    Curve curve <span class=\"token operator\">=</span> Curves<span class=\"token punctuation\">.</span>linear<span class=\"token punctuation\">,</span>\n    <span class=\"token metadata symbol\">@required</span> Duration duration<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>\n          key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">,</span>\n          curve<span class=\"token punctuation\">:</span> curve<span class=\"token punctuation\">,</span>\n          duration<span class=\"token punctuation\">:</span> duration<span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div></li></ol> <h2 id=\"_9-7-2-flutter预置的动画过渡组件\"><a href=\"#_9-7-2-flutter预置的动画过渡组件\" class=\"header-anchor\">#</a> 9.7.2 Flutter预置的动画过渡组件</h2> <p>Flutter SDK中也预置了很多动画过渡组件，实现方式和大都和<code>AnimatedDecoratedBox</code>差不多，如表9-1所示：</p> <table><thead><tr><th>组件名</th> <th>功能</th></tr></thead> <tbody><tr><td>AnimatedPadding</td> <td>在padding发生变化时会执行过渡动画到新状态</td></tr> <tr><td>AnimatedPositioned</td> <td>配合Stack一起使用，当定位状态发生变化时会执行过渡动画到新的状态。</td></tr> <tr><td>AnimatedOpacity</td> <td>在透明度opacity发生变化时执行过渡动画到新状态</td></tr> <tr><td>AnimatedAlign</td> <td>当<code>alignment</code>发生变化时会执行过渡动画到新的状态。</td></tr> <tr><td>AnimatedContainer</td> <td>当Container属性发生变化时会执行过渡动画到新的状态。</td></tr> <tr><td>AnimatedDefaultTextStyle</td> <td>当字体样式发生变化时，子组件中继承了该样式的文本组件会动态过渡到新样式。</td></tr></tbody></table> <center>表9-1：Flutter预置的动画过渡组件</center>\n下面我们通过一个示例来感受一下这些预置的动画过渡组件效果：\n<div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">import</span> <span class=\"token string\">'package:flutter/material.dart'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">AnimatedWidgetsTest</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _AnimatedWidgetsTestState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">_AnimatedWidgetsTestState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_AnimatedWidgetsTestState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>AnimatedWidgetsTest<span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  double _padding <span class=\"token operator\">=</span> <span class=\"token number\">10</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">var</span> _align <span class=\"token operator\">=</span> Alignment<span class=\"token punctuation\">.</span>topRight<span class=\"token punctuation\">;</span>\n  double _height <span class=\"token operator\">=</span> <span class=\"token number\">100</span><span class=\"token punctuation\">;</span>\n  double _left <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span>\n  Color _color <span class=\"token operator\">=</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">;</span>\n  TextStyle _style <span class=\"token operator\">=</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>black<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  Color _decorationColor <span class=\"token operator\">=</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">var</span> duration <span class=\"token operator\">=</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">5</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">SingleChildScrollView</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Column</span><span class=\"token punctuation\">(</span>\n        children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n          <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n            onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                _padding <span class=\"token operator\">=</span> <span class=\"token number\">20</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">AnimatedPadding</span><span class=\"token punctuation\">(</span>\n              duration<span class=\"token punctuation\">:</span> duration<span class=\"token punctuation\">,</span>\n              padding<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span>_padding<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;AnimatedPadding&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">SizedBox</span><span class=\"token punctuation\">(</span>\n            height<span class=\"token punctuation\">:</span> <span class=\"token number\">50</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">Stack</span><span class=\"token punctuation\">(</span>\n              children<span class=\"token punctuation\">:</span> <span class=\"token operator\">&lt;</span>Widget<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">[</span>\n                <span class=\"token function\">AnimatedPositioned</span><span class=\"token punctuation\">(</span>\n                  duration<span class=\"token punctuation\">:</span> duration<span class=\"token punctuation\">,</span>\n                  left<span class=\"token punctuation\">:</span> _left<span class=\"token punctuation\">,</span>\n                  child<span class=\"token punctuation\">:</span> <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n                    onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                      <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                        _left <span class=\"token operator\">=</span> <span class=\"token number\">100</span><span class=\"token punctuation\">;</span>\n                      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n                    child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;AnimatedPositioned&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span>\n              <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n            height<span class=\"token punctuation\">:</span> <span class=\"token number\">100</span><span class=\"token punctuation\">,</span>\n            color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>grey<span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">AnimatedAlign</span><span class=\"token punctuation\">(</span>\n              duration<span class=\"token punctuation\">:</span> duration<span class=\"token punctuation\">,</span>\n              alignment<span class=\"token punctuation\">:</span> _align<span class=\"token punctuation\">,</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">RaisedButton</span><span class=\"token punctuation\">(</span>\n                onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                  <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                    _align <span class=\"token operator\">=</span> Alignment<span class=\"token punctuation\">.</span>center<span class=\"token punctuation\">;</span>\n                  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n                child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;AnimatedAlign&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">AnimatedContainer</span><span class=\"token punctuation\">(</span>\n            duration<span class=\"token punctuation\">:</span> duration<span class=\"token punctuation\">,</span>\n            height<span class=\"token punctuation\">:</span> _height<span class=\"token punctuation\">,</span>\n            color<span class=\"token punctuation\">:</span> _color<span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n              onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                  _height <span class=\"token operator\">=</span> <span class=\"token number\">150</span><span class=\"token punctuation\">;</span>\n                  _color <span class=\"token operator\">=</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">;</span>\n                <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>\n                <span class=\"token string\">&quot;AnimatedContainer&quot;</span><span class=\"token punctuation\">,</span>\n                style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">AnimatedDefaultTextStyle</span><span class=\"token punctuation\">(</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">GestureDetector</span><span class=\"token punctuation\">(</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;hello world&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              onTap<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                  _style <span class=\"token operator\">=</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>\n                    color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span>\n                    decorationStyle<span class=\"token punctuation\">:</span> TextDecorationStyle<span class=\"token punctuation\">.</span>solid<span class=\"token punctuation\">,</span>\n                    decorationColor<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>blue<span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            style<span class=\"token punctuation\">:</span> _style<span class=\"token punctuation\">,</span>\n            duration<span class=\"token punctuation\">:</span> duration<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">AnimatedDecoratedBox</span><span class=\"token punctuation\">(</span>\n            duration<span class=\"token punctuation\">:</span> duration<span class=\"token punctuation\">,</span>\n            decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> _decorationColor<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> <span class=\"token function\">FlatButton</span><span class=\"token punctuation\">(</span>\n              onPressed<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                  _decorationColor <span class=\"token operator\">=</span> Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">;</span>\n                <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n              child<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span>\n                <span class=\"token string\">&quot;AnimatedDecoratedBox&quot;</span><span class=\"token punctuation\">,</span>\n                style<span class=\"token punctuation\">:</span> <span class=\"token function\">TextStyle</span><span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n              <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">return</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>\n            padding<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">symmetric</span><span class=\"token punctuation\">(</span>vertical<span class=\"token punctuation\">:</span> <span class=\"token number\">16</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            child<span class=\"token punctuation\">:</span> e<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toList</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>运行后效果如图9-10所示：</p> <p><img src=\"/assets/img/9-10.fefb9fd0.png\" alt=\"图9-10\"></p> <p>读者可以点击一下相应组件来查看一下实际的运行效果。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/57.371d9d12.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter9/animation_structure.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>9.2 动画基本结构及状态监听 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/79.1a0eb05b.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter9/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_9-2-动画基本结构及状态监听\"><a href=\"#_9-2-动画基本结构及状态监听\" class=\"header-anchor\">#</a> 9.2 动画基本结构及状态监听</h1> <h2 id=\"_9-2-1-动画基本结构\"><a href=\"#_9-2-1-动画基本结构\" class=\"header-anchor\">#</a> 9.2.1 动画基本结构</h2> <p>在Flutter中我们可以通过多种方式来实现动画，下面通过一个图片逐渐放大示例的不同实现来演示Flutter中动画的不同实现方式的区别。</p> <h3 id=\"基础版本\"><a href=\"#基础版本\" class=\"header-anchor\">#</a> 基础版本</h3> <p>下面我们演示一下最基础的动画实现方式：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">ScaleAnimationRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _ScaleAnimationRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_ScaleAnimationRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">//需要继承TickerProvider，如果有多个AnimationController，则应该使用TickerProviderStateMixin。</span>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_ScaleAnimationRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>ScaleAnimationRoute<span class=\"token operator\">&gt;</span>  <span class=\"token keyword\">with</span> SingleTickerProviderStateMixin<span class=\"token punctuation\">{</span> \n    \n  Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> animation<span class=\"token punctuation\">;</span>\n  AnimationController controller<span class=\"token punctuation\">;</span>\n    \n  <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    controller <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AnimationController</span><span class=\"token punctuation\">(</span>\n        duration<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">3</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> vsync<span class=\"token punctuation\">:</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//图片宽高从0变到300</span>\n    animation <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Tween</span><span class=\"token punctuation\">(</span>begin<span class=\"token punctuation\">:</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">:</span> <span class=\"token number\">300.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">animate</span><span class=\"token punctuation\">(</span>controller<span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//启动动画(正向执行)</span>\n    controller<span class=\"token punctuation\">.</span><span class=\"token function\">forward</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Center</span><span class=\"token punctuation\">(</span>\n       child<span class=\"token punctuation\">:</span> Image<span class=\"token punctuation\">.</span><span class=\"token function\">asset</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;imgs/avatar.png&quot;</span><span class=\"token punctuation\">,</span>\n          width<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span>\n          height<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">.</span>value\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//路由销毁时需要释放动画资源</span>\n    controller<span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面代码中<code>addListener()</code>函数调用了<code>setState()</code>，所以每次动画生成一个新的数字时，当前帧被标记为脏(dirty)，这会导致widget的<code>build()</code>方法再次被调用，而在<code>build()</code>中，改变Image的宽高，因为它的高度和宽度现在使用的是<code>animation.value</code> ，所以就会逐渐放大。值得注意的是动画完成时要释放控制器(调用<code>dispose()</code>方法)以防止内存泄漏。</p> <p>上面的例子中并没有指定Curve，所以放大的过程是线性的（匀速），下面我们指定一个Curve，来实现一个类似于弹簧效果的动画过程，我们只需要将<code>initState</code>中的代码改为下面这样即可：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>  <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    controller <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AnimationController</span><span class=\"token punctuation\">(</span>\n        duration<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">3</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> vsync<span class=\"token punctuation\">:</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//使用弹性曲线</span>\n    animation<span class=\"token operator\">=</span><span class=\"token function\">CurvedAnimation</span><span class=\"token punctuation\">(</span>parent<span class=\"token punctuation\">:</span> controller<span class=\"token punctuation\">,</span> curve<span class=\"token punctuation\">:</span> Curves<span class=\"token punctuation\">.</span>bounceIn<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//图片宽高从0变到300</span>\n    animation <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Tween</span><span class=\"token punctuation\">(</span>begin<span class=\"token punctuation\">:</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">:</span> <span class=\"token number\">300.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">animate</span><span class=\"token punctuation\">(</span>animation<span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token function\">addListener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//启动动画</span>\n    controller<span class=\"token punctuation\">.</span><span class=\"token function\">forward</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面代码执行后截取了其中的两帧，效果如图9-1、9-2所示：</p> <p><img src=\"/assets/img/9-1.67235fc3.png\" alt=\"图9-1\"><img src=\"/assets/img/9-2.53c1a9c2.png\" alt=\"图9-2\"></p> <h3 id=\"使用animatedwidget简化\"><a href=\"#使用animatedwidget简化\" class=\"header-anchor\">#</a> 使用AnimatedWidget简化</h3> <p>细心的读者可能已经发现上面示例中通过<code>addListener()</code>和<code>setState()</code> 来更新UI这一步其实是通用的，如果每个动画中都加这么一句是比较繁琐的。<code>AnimatedWidget</code>类封装了调用<code>setState()</code>的细节，并允许我们将widget分离出来，重构后的代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">AnimatedImage</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">AnimatedWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">AnimatedImage</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>Key key<span class=\"token punctuation\">,</span> Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> animation<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">,</span> listenable<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">final</span> Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> animation <span class=\"token operator\">=</span> listenable<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Center</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> Image<span class=\"token punctuation\">.</span><span class=\"token function\">asset</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;imgs/avatar.png&quot;</span><span class=\"token punctuation\">,</span>\n          width<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span>\n          height<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">.</span>value\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">ScaleAnimationRoute1</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _ScaleAnimationRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">_ScaleAnimationRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_ScaleAnimationRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>ScaleAnimationRoute1<span class=\"token operator\">&gt;</span>\n    <span class=\"token keyword\">with</span> SingleTickerProviderStateMixin <span class=\"token punctuation\">{</span>\n\n  Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> animation<span class=\"token punctuation\">;</span>\n  AnimationController controller<span class=\"token punctuation\">;</span>\n\n  <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    controller <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AnimationController</span><span class=\"token punctuation\">(</span>\n        duration<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">3</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> vsync<span class=\"token punctuation\">:</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//图片宽高从0变到300</span>\n    animation <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Tween</span><span class=\"token punctuation\">(</span>begin<span class=\"token punctuation\">:</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">:</span> <span class=\"token number\">300.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">animate</span><span class=\"token punctuation\">(</span>controller<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//启动动画</span>\n    controller<span class=\"token punctuation\">.</span><span class=\"token function\">forward</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">AnimatedImage</span><span class=\"token punctuation\">(</span>animation<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//路由销毁时需要释放动画资源</span>\n    controller<span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">dispose</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h3 id=\"用animatedbuilder重构\"><a href=\"#用animatedbuilder重构\" class=\"header-anchor\">#</a> 用AnimatedBuilder重构</h3> <p>用AnimatedWidget可以从动画中分离出widget，而动画的渲染过程（即设置宽高）仍然在AnimatedWidget中，假设如果我们再添加一个widget透明度变化的动画，那么我们需要再实现一个AnimatedWidget，这样不是很优雅，如果我们能把渲染过程也抽象出来，那就会好很多，而AnimatedBuilder正是将渲染逻辑分离出来, 上面的build方法中的代码可以改为：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token metadata symbol\">@override</span>\nWidget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">//return AnimatedImage(animation: animation,);</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">AnimatedBuilder</span><span class=\"token punctuation\">(</span>\n      animation<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> Image<span class=\"token punctuation\">.</span><span class=\"token function\">asset</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;images/avatar.png&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext ctx<span class=\"token punctuation\">,</span> Widget child<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Center</span><span class=\"token punctuation\">(</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n              height<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span> \n              width<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span> \n              child<span class=\"token punctuation\">:</span> child<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>上面的代码中有一个迷惑的问题是，<code>child</code>看起来像被指定了两次。但实际发生的事情是：将外部引用<code>child</code>传递给<code>AnimatedBuilder</code>后<code>AnimatedBuilder</code>再将其传递给匿名构造器， 然后将该对象用作其子对象。最终的结果是<code>AnimatedBuilder</code>返回的对象插入到widget树中。</p> <p>也许你会说这和我们刚开始的示例差不了多少，其实它会带来三个好处：</p> <ol><li><p>不用显式的去添加帧监听器，然后再调用<code>setState()</code> 了，这个好处和<code>AnimatedWidget</code>是一样的。</p></li> <li><p>动画构建的范围缩小了，如果没有<code>builder</code>，<code>setState()</code>将会在父组件上下文中调用，这将会导致父组件的<code>build</code>方法重新调用；而有了<code>builder</code>之后，只会导致动画widget自身的<code>build</code>重新调用，避免不必要的rebuild。</p></li> <li><p>通过<code>AnimatedBuilder</code>可以封装常见的过渡效果来复用动画。下面我们通过封装一个<code>GrowTransition</code>来说明，它可以对子widget实现放大动画：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">GrowTransition</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">GrowTransition</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>child<span class=\"token punctuation\">,</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>animation<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> Widget child<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">final</span> Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> animation<span class=\"token punctuation\">;</span>\n    \n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Center</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AnimatedBuilder</span><span class=\"token punctuation\">(</span>\n          animation<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">,</span>\n          builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> Widget child<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Container</span><span class=\"token punctuation\">(</span>\n                height<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span> \n                width<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span> \n                child<span class=\"token punctuation\">:</span> child\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          child<span class=\"token punctuation\">:</span> child\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>这样，最初的示例就可以改为：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\nWidget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">GrowTransition</span><span class=\"token punctuation\">(</span>\n    child<span class=\"token punctuation\">:</span> Image<span class=\"token punctuation\">.</span><span class=\"token function\">asset</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;images/avatar.png&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> \n    animation<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p><strong>Flutter中正是通过这种方式封装了很多动画，如：FadeTransition、ScaleTransition、SizeTransition等，很多时候都可以复用这些预置的过渡类。</strong></p></li></ol> <h2 id=\"_9-2-2-动画状态监听\"><a href=\"#_9-2-2-动画状态监听\" class=\"header-anchor\">#</a> 9.2.2 动画状态监听</h2> <p>上面说过，我们可以通过<code>Animation</code>的<code>addStatusListener()</code>方法来添加动画状态改变监听器。Flutter中，有四种动画状态，在<code>AnimationStatus</code>枚举类中定义，下面我们逐个说明：</p> <table><thead><tr><th>枚举值</th> <th>含义</th></tr></thead> <tbody><tr><td><code>dismissed</code></td> <td>动画在起始点停止</td></tr> <tr><td><code>forward</code></td> <td>动画正在正向执行</td></tr> <tr><td><code>reverse</code></td> <td>动画正在反向执行</td></tr> <tr><td><code>completed</code></td> <td>动画在终点停止</td></tr></tbody></table> <h4 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h4> <p>我们将上面图片放大的示例改为先放大再缩小再放大……这样的循环动画。要实现这种效果，我们只需要监听动画状态的改变即可，即：在动画正向执行结束时反转动画，在动画反向执行结束时再正向执行动画。代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>  <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    controller <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AnimationController</span><span class=\"token punctuation\">(</span>\n        duration<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>seconds<span class=\"token punctuation\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> vsync<span class=\"token punctuation\">:</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">//图片宽高从0变到300</span>\n    animation <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Tween</span><span class=\"token punctuation\">(</span>begin<span class=\"token punctuation\">:</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">:</span> <span class=\"token number\">300.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">animate</span><span class=\"token punctuation\">(</span>controller<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    animation<span class=\"token punctuation\">.</span><span class=\"token function\">addStatusListener</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>status<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>status <span class=\"token operator\">==</span> AnimationStatus<span class=\"token punctuation\">.</span>completed<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">//动画执行结束时反向执行动画</span>\n        controller<span class=\"token punctuation\">.</span><span class=\"token function\">reverse</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>status <span class=\"token operator\">==</span> AnimationStatus<span class=\"token punctuation\">.</span>dismissed<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">//动画恢复到初始状态时执行动画（正向）</span>\n        controller<span class=\"token punctuation\">.</span><span class=\"token function\">forward</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token comment\">//启动动画（正向）</span>\n    controller<span class=\"token punctuation\">.</span><span class=\"token function\">forward</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n</code></pre></div></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/79.1a0eb05b.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter9/hero.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>9.4 Hero动画 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/218.059e8703.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter9/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_9-4-hero动画\"><a href=\"#_9-4-hero动画\" class=\"header-anchor\">#</a> 9.4 Hero动画</h1> <p>Hero指的是可以在路由(页面)之间“飞行”的widget，简单来说Hero动画就是在路由切换时，有一个共享的widget可以在新旧路由间切换。由于共享的widget在新旧路由页面上的位置、外观可能有所差异，所以在路由切换时会从旧路逐渐过渡到新路由中的指定位置，这样就会产生一个Hero动画。</p> <p>你可能多次看到过 hero 动画。例如，一个路由中显示待售商品的缩略图列表，选择一个条目会将其跳转到一个新路由，新路由中包含该商品的详细信息和“购买”按钮。 在Flutter中将图片从一个路由“飞”到另一个路由称为<strong>hero动画</strong>，尽管相同的动作有时也称为 <strong>共享元素转换</strong>。下面我们通过一个示例来体验一下hero 动画。</p> <blockquote><p>为什么要将这种可飞行的共享组件称为hero（英雄），有一种说法是说美国文化中的超人是可以飞的，那是美国人心中的大英雄，还有漫威中的超级英雄基本上都是会飞的，所以Flutter开发人员就对这种“会飞的widget”就起了一个富有浪漫主义的名字hero。当然这种说法并非官方解释，但却很有意思。</p></blockquote> <h4 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h4> <p>假设有两个路由A和B，他们的内容交互如下：</p> <p>A：包含一个用户头像，圆形，点击后跳到B路由，可以查看大图。</p> <p>B：显示用户头像原图，矩形；</p> <p>在AB两个路由之间跳转的时候，用户头像会逐渐过渡到目标路由页的头像上，接下来我们先看看代码，然后再解析：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token comment\">// 路由A</span>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">HeroAnimationRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n      alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>topCenter<span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">InkWell</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Hero</span><span class=\"token punctuation\">(</span>\n          tag<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;avatar&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//唯一标记，前后两个路由页Hero的tag必须相同</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">ClipOval</span><span class=\"token punctuation\">(</span>\n            child<span class=\"token punctuation\">:</span> Image<span class=\"token punctuation\">.</span><span class=\"token function\">asset</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;images/avatar.png&quot;</span><span class=\"token punctuation\">,</span>\n              width<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        onTap<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token comment\">//打开B路由  </span>\n          Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> <span class=\"token function\">PageRouteBuilder</span><span class=\"token punctuation\">(</span>\n              pageBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> Animation animation<span class=\"token punctuation\">,</span>\n                  Animation secondaryAnimation<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">FadeTransition</span><span class=\"token punctuation\">(</span>\n                  opacity<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">,</span>\n                  child<span class=\"token punctuation\">:</span> <span class=\"token function\">Scaffold</span><span class=\"token punctuation\">(</span>\n                    appBar<span class=\"token punctuation\">:</span> <span class=\"token function\">AppBar</span><span class=\"token punctuation\">(</span>\n                      title<span class=\"token punctuation\">:</span> <span class=\"token function\">Text</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;原图&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                    body<span class=\"token punctuation\">:</span> <span class=\"token function\">HeroAnimationRouteB</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>路由B:</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">HeroAnimationRouteB</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Hero</span><span class=\"token punctuation\">(</span>\n          tag<span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;avatar&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//唯一标记，前后两个路由页Hero的tag必须相同</span>\n          child<span class=\"token punctuation\">:</span> Image<span class=\"token punctuation\">.</span><span class=\"token function\">asset</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;images/avatar.png&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>我们可以看到，实现Hero动画只需要用<code>Hero</code>组件将要共享的widget包装起来，并提供一个相同的tag即可，中间的过渡帧都是Flutter Framework自动完成的。必须要注意， 前后路由页的共享<code>Hero</code>的tag必须是相同的，Flutter Framework内部正是通过tag来确定新旧路由页widget的对应关系的。</p> <p>Hero动画的原理比较简单，Flutter Framework知道新旧路由页中共享元素的位置和大小，所以根据这两个端点，在动画执行过程中求出过渡时的插值（中间态）即可，而感到幸运的是，这些事情不需要我们自己动手，Flutter已经帮我们做了！</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/218.059e8703.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter9/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>简介 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/219.70d1d263.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter9/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h2 id=\"简介\"><a href=\"#简介\" class=\"header-anchor\">#</a> 简介</h2> <p>精心设计的动画会让用户界面感觉更直观、流畅，能改善用户体验。 Flutter可以轻松实现各种动画类型，对于许多widget，特别是<a href=\"https://flutter.io/docs/reference/widgets/material\" target=\"_blank\" rel=\"noopener noreferrer\">Material Design widgets<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>，都带有在其设计规范中定义的标准动画效果(但也可以自定义这些效果)。本章将详细介绍Flutter的动画系统，并会通过几个小实例来演示，以帮助开发者迅速理解并掌握动画的开发流程与原理。</p> <h2 id=\"本章目录\"><a href=\"#本章目录\" class=\"header-anchor\">#</a> 本章目录</h2> <ul><li><a href=\"/v2/chapter9/intro.html\">9.1：Flutter动画简介</a></li> <li><a href=\"/v2/chapter9/animation_structure.html\">9.2：动画结构</a></li> <li><a href=\"/v2/chapter9/route_transition.html\">9.3：自定义路由过渡动画</a></li> <li><a href=\"/v2/chapter9/hero.html\">9.4：Hero动画</a></li> <li><a href=\"/v2/chapter9/stagger_animation.html\">9.5：交织动画</a></li> <li><a href=\"/v2/chapter9/animated_switcher.html\">9.6：通用“动画切换”组件（AnimatedSwitcher）</a></li> <li><a href=\"/v2/chapter9/animated_widgets.html\">9.7：动画过渡组件</a></li></ul></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/219.70d1d263.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter9/intro.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>9.1 Flutter动画简介 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/220.69e2bb54.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter9/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_9-1-flutter动画简介\"><a href=\"#_9-1-flutter动画简介\" class=\"header-anchor\">#</a> 9.1 Flutter动画简介</h1> <p>在任何系统的UI框架中，动画实现的原理都是相同的，即：在一段时间内，快速地多次改变UI外观；由于人眼会产生视觉暂留，所以最终看到的就是一个“连续”的动画，这和电影的原理是一样的。我们将UI的一次改变称为一个动画帧，对应一次屏幕刷新，而决定动画流畅度的一个重要指标就是帧率FPS（Frame Per Second），即每秒的动画帧数。很明显，帧率越高则动画就会越流畅！一般情况下，对于人眼来说，动画帧率超过16FPS，就比较流畅了，超过32FPS就会非常的细腻平滑，而超过32FPS，人眼基本上就感受不到差别了。由于动画的每一帧都是要改变UI输出，所以在一个时间段内连续的改变UI输出是比较耗资源的，对设备的软硬件系统要求都较高，所以在UI系统中，动画的平均帧率是重要的性能指标，而在Flutter中，理想情况下是可以实现60FPS的，这和原生应用能达到的帧率是基本是持平的。</p> <h3 id=\"flutter中动画抽象\"><a href=\"#flutter中动画抽象\" class=\"header-anchor\">#</a> Flutter中动画抽象</h3> <p>为了方便开发者创建动画，不同的UI系统对动画都进行了一些抽象，比如在Android中可以通过XML来描述一个动画然后设置给View。Flutter中也对动画进行了抽象，主要涉及Animation、Curve、Controller、Tween这四个角色，它们一起配合来完成一个完整动画，下面我们一一来介绍它们。</p> <h3 id=\"animation\"><a href=\"#animation\" class=\"header-anchor\">#</a> Animation</h3> <p><code>Animation</code>是一个抽象类，它本身和UI渲染没有任何关系，而它主要的功能是保存动画的插值和状态；其中一个比较常用的<code>Animation</code>类是<code>Animation&lt;double&gt;</code>。<code>Animation</code>对象是一个在一段时间内依次生成一个区间(Tween)之间值的类。<code>Animation</code>对象在整个动画执行过程中输出的值可以是线性的、曲线的、一个步进函数或者任何其他曲线函数等等，这由<code>Curve</code>来决定。 根据<code>Animation</code>对象的控制方式，动画可以正向运行（从起始状态开始，到终止状态结束），也可以反向运行，甚至可以在中间切换方向。<code>Animation</code>还可以生成除<code>double</code>之外的其他类型值，如：<code>Animation&lt;Color&gt;</code> 或<code>Animation&lt;Size&gt;</code>。在动画的每一帧中，我们可以通过<code>Animation</code>对象的<code>value</code>属性获取动画的当前状态值。</p> <h4 id=\"动画通知\"><a href=\"#动画通知\" class=\"header-anchor\">#</a> 动画通知</h4> <p>我们可以通过<code>Animation</code>来监听动画每一帧以及执行状态的变化，<code>Animation</code>有如下两个方法：</p> <ol><li><code>addListener()</code>；它可以用于给<code>Animation</code>添加帧监听器，在每一帧都会被调用。帧监听器中最常见的行为是改变状态后调用<code>setState()</code>来触发UI重建。</li> <li><code>addStatusListener()</code>；它可以给<code>Animation</code>添加“动画状态改变”监听器；动画开始、结束、正向或反向（见<code>AnimationStatus</code>定义）时会调用状态改变的监听器。</li></ol> <p>读者在此只需要知道帧监听器和状态监听器的区别，在后面的章节中我们将会举例说明。</p> <h3 id=\"curve\"><a href=\"#curve\" class=\"header-anchor\">#</a> Curve</h3> <p>动画过程可以是匀速的、匀加速的或者先加速后减速等。Flutter中通过<code>Curve</code>（曲线）来描述动画过程，我们把匀速动画称为线性的(Curves.linear)，而非匀速动画称为非线性的。</p> <p>我们可以通过<code>CurvedAnimation</code>来指定动画的曲线，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">final</span> CurvedAnimation curve <span class=\"token operator\">=</span>\n    <span class=\"token keyword\">new</span> <span class=\"token class-name\">CurvedAnimation</span><span class=\"token punctuation\">(</span>parent<span class=\"token punctuation\">:</span> controller<span class=\"token punctuation\">,</span> curve<span class=\"token punctuation\">:</span> Curves<span class=\"token punctuation\">.</span>easeIn<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p><code>CurvedAnimation</code>和<code>AnimationController</code>（下面介绍）都是<code>Animation&lt;double&gt;</code>类型。<code>CurvedAnimation</code>可以通过包装<code>AnimationController</code>和<code>Curve</code>生成一个新的动画对象 ，我们正是通过这种方式来将动画和动画执行的曲线关联起来的。我们指定动画的曲线为<code>Curves.easeIn</code>，它表示动画开始时比较慢，结束时比较快。 <a href=\"https://docs.flutter.io/flutter/animation/Curves-class.html\" target=\"_blank\" rel=\"noopener noreferrer\">Curves<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 类是一个预置的枚举类，定义了许多常用的曲线，下面列几种常用的：</p> <table><thead><tr><th>Curves曲线</th> <th>动画过程</th></tr></thead> <tbody><tr><td>linear</td> <td>匀速的</td></tr> <tr><td>decelerate</td> <td>匀减速</td></tr> <tr><td>ease</td> <td>开始加速，后面减速</td></tr> <tr><td>easeIn</td> <td>开始慢，后面快</td></tr> <tr><td>easeOut</td> <td>开始快，后面慢</td></tr> <tr><td>easeInOut</td> <td>开始慢，然后加速，最后再减速</td></tr></tbody></table> <p>除了上面列举的， <a href=\"https://docs.flutter.io/flutter/animation/Curves-class.html\" target=\"_blank\" rel=\"noopener noreferrer\">Curves<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 类中还定义了许多其它的曲线，在此便不一一介绍，读者可以自行查看Curves类定义。</p> <p>当然我们也可以创建自己Curve，例如我们定义一个正弦曲线：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">ShakeCurve</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">Curve</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  double <span class=\"token function\">transform</span><span class=\"token punctuation\">(</span>double t<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> math<span class=\"token punctuation\">.</span><span class=\"token function\">sin</span><span class=\"token punctuation\">(</span>t <span class=\"token operator\">*</span> math<span class=\"token punctuation\">.</span>PI <span class=\"token operator\">*</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><h3 id=\"animationcontroller\"><a href=\"#animationcontroller\" class=\"header-anchor\">#</a> AnimationController</h3> <p><code>AnimationController</code>用于控制动画，它包含动画的启动<code>forward()</code>、停止<code>stop()</code> 、反向播放 <code>reverse()</code>等方法。<code>AnimationController</code>会在动画的每一帧，就会生成一个新的值。默认情况下，<code>AnimationController</code>在给定的时间段内线性的生成从0.0到1.0（默认区间）的数字。 例如，下面代码创建一个<code>Animation</code>对象（但不会启动动画）：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">final</span> AnimationController controller <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AnimationController</span><span class=\"token punctuation\">(</span>\n    duration<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>milliseconds<span class=\"token punctuation\">:</span> <span class=\"token number\">2000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> vsync<span class=\"token punctuation\">:</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p><code>AnimationController</code>生成数字的区间可以通过<code>lowerBound</code>和<code>upperBound</code>来指定，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">final</span> AnimationController controller <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AnimationController</span><span class=\"token punctuation\">(</span> \n duration<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>milliseconds<span class=\"token punctuation\">:</span> <span class=\"token number\">2000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> \n lowerBound<span class=\"token punctuation\">:</span> <span class=\"token number\">10.0</span><span class=\"token punctuation\">,</span>\n upperBound<span class=\"token punctuation\">:</span> <span class=\"token number\">20.0</span><span class=\"token punctuation\">,</span>\n vsync<span class=\"token punctuation\">:</span> <span class=\"token keyword\">this</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p><code>AnimationController</code>派生自<code>Animation&lt;double&gt;</code>，因此可以在需要<code>Animation</code>对象的任何地方使用。 但是，<code>AnimationController</code>具有控制动画的其他方法，例如<code>forward()</code>方法可以启动正向动画，<code>reverse()</code>可以启动反向动画。在动画开始执行后开始生成动画帧，屏幕每刷新一次就是一个动画帧，在动画的每一帧，会随着根据动画的曲线来生成当前的动画值（<code>Animation.value</code>），然后根据当前的动画值去构建UI，当所有动画帧依次触发时，动画值会依次改变，所以构建的UI也会依次变化，所以最终我们可以看到一个完成的动画。 另外在动画的每一帧，<code>Animation</code>对象会调用其帧监听器，等动画状态发生改变时（如动画结束）会调用状态改变监听器。</p> <p><code>duration</code>表示动画执行的时长，通过它我们可以控制动画的速度。</p> <blockquote><p><strong>注意</strong>： 在某些情况下，动画值可能会超出<code>AnimationController</code>的[0.0，1.0]的范围，这取决于具体的曲线。例如，<code>fling()</code>函数可以根据我们手指滑动（甩出）的速度(velocity)、力量(force)等来模拟一个手指甩出动画，因此它的动画值可以在[0.0，1.0]范围之外 。也就是说，根据选择的曲线，<code>CurvedAnimation</code>的输出可以具有比输入更大的范围。例如，Curves.elasticIn等弹性曲线会生成大于或小于默认范围的值。</p></blockquote> <h4 id=\"ticker\"><a href=\"#ticker\" class=\"header-anchor\">#</a> Ticker</h4> <p>当创建一个<code>AnimationController</code>时，需要传递一个<code>vsync</code>参数，它接收一个<code>TickerProvider</code>类型的对象，它的主要职责是创建<code>Ticker</code>，定义如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">abstract</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">TickerProvider</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">//通过一个回调创建一个Ticker</span>\n  Ticker <span class=\"token function\">createTicker</span><span class=\"token punctuation\">(</span>TickerCallback onTick<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>Flutter应用在启动时都会绑定一个<code>SchedulerBinding</code>，通过<code>SchedulerBinding</code>可以给每一次屏幕刷新添加回调，而<code>Ticker</code>就是通过<code>SchedulerBinding</code>来添加屏幕刷新回调，这样一来，每次屏幕刷新都会调用<code>TickerCallback</code>。使用<code>Ticker</code>(而不是<code>Timer</code>)来驱动动画会防止屏幕外动画（动画的UI不在当前屏幕时，如锁屏时）消耗不必要的资源，因为Flutter中屏幕刷新时会通知到绑定的<code>SchedulerBinding</code>，而<code>Ticker</code>是受<code>SchedulerBinding</code>驱动的，由于锁屏后屏幕会停止刷新，所以<code>Ticker</code>就不会再触发。</p> <p>通常我们会将<code>SingleTickerProviderStateMixin</code>添加到<code>State</code>的定义中，然后将State对象作为<code>vsync</code>的值，这在后面的例子中可以见到。</p> <h3 id=\"tween\"><a href=\"#tween\" class=\"header-anchor\">#</a> Tween</h3> <p>默认情况下，<code>AnimationController</code>对象值的范围是[0.0，1.0]。如果我们需要构建UI的动画值在不同的范围或不同的数据类型，则可以使用<code>Tween</code>来添加映射以生成不同的范围或数据类型的值。例如，像下面示例，<code>Tween</code>生成[-200.0，0.0]的值：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">final</span> Tween doubleTween <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Tween</span><span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>begin<span class=\"token punctuation\">:</span> <span class=\"token operator\">-</span><span class=\"token number\">200.0</span><span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">:</span> <span class=\"token number\">0.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p><code>Tween</code>构造函数需要<code>begin</code>和<code>end</code>两个参数。<code>Tween</code>的唯一职责就是定义从输入范围到输出范围的映射。输入范围通常为[0.0，1.0]，但这不是必须的，我们可以自定义需要的范围。</p> <p><code>Tween</code>继承自<code>Animatable&lt;T&gt;</code>，而不是继承自<code>Animation&lt;T&gt;</code>，<code>Animatable</code>中主要定义动画值的映射规则。</p> <p>下面我们看一个ColorTween将动画输入范围映射为两种颜色值之间过渡输出的例子：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">final</span> Tween colorTween <span class=\"token operator\">=</span>\n    <span class=\"token keyword\">new</span> <span class=\"token class-name\">ColorTween</span><span class=\"token punctuation\">(</span>begin<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>transparent<span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>black54<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p><code>Tween</code>对象不存储任何状态，相反，它提供了<code>evaluate(Animation&lt;double&gt; animation)</code>方法，它可以获取动画当前映射值。 <code>Animation</code>对象的当前值可以通过<code>value()</code>方法取到。<code>evaluate</code>函数还执行一些其它处理，例如分别确保在动画值为0.0和1.0时返回开始和结束状态。</p> <h4 id=\"tween-animate\"><a href=\"#tween-animate\" class=\"header-anchor\">#</a> Tween.animate</h4> <p>要使用Tween对象，需要调用其<code>animate()</code>方法，然后传入一个控制器对象。例如，以下代码在500毫秒内生成从0到255的整数值。</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">final</span> AnimationController controller <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AnimationController</span><span class=\"token punctuation\">(</span>\n    duration<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>milliseconds<span class=\"token punctuation\">:</span> <span class=\"token number\">500</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> vsync<span class=\"token punctuation\">:</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\nAnimation<span class=\"token operator\">&lt;</span>int<span class=\"token operator\">&gt;</span> alpha <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">IntTween</span><span class=\"token punctuation\">(</span>begin<span class=\"token punctuation\">:</span> <span class=\"token number\">0</span><span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">:</span> <span class=\"token number\">255</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">animate</span><span class=\"token punctuation\">(</span>controller<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>注意<code>animate()</code>返回的是一个<code>Animation</code>，而不是一个<code>Animatable</code>。</p> <p>以下示例构建了一个控制器、一条曲线和一个Tween：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">final</span> AnimationController controller <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AnimationController</span><span class=\"token punctuation\">(</span>\n    duration<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>milliseconds<span class=\"token punctuation\">:</span> <span class=\"token number\">500</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> vsync<span class=\"token punctuation\">:</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">final</span> Animation curve <span class=\"token operator\">=</span>\n    <span class=\"token keyword\">new</span> <span class=\"token class-name\">CurvedAnimation</span><span class=\"token punctuation\">(</span>parent<span class=\"token punctuation\">:</span> controller<span class=\"token punctuation\">,</span> curve<span class=\"token punctuation\">:</span> Curves<span class=\"token punctuation\">.</span>easeOut<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\nAnimation<span class=\"token operator\">&lt;</span>int<span class=\"token operator\">&gt;</span> alpha <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">IntTween</span><span class=\"token punctuation\">(</span>begin<span class=\"token punctuation\">:</span> <span class=\"token number\">0</span><span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">:</span> <span class=\"token number\">255</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">animate</span><span class=\"token punctuation\">(</span>curve<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/220.69e2bb54.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter9/route_transition.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>9.3 自定义路由切换动画 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/221.51aa50d8.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter9/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_9-3-自定义路由切换动画\"><a href=\"#_9-3-自定义路由切换动画\" class=\"header-anchor\">#</a> 9.3 自定义路由切换动画</h1> <p>我们在第二章“路由管理”一节中讲过：Material组件库中提供了一个<code>MaterialPageRoute</code>组件，它可以使用和平台风格一致的路由切换动画，如在iOS上会左右滑动切换，而在Android上会上下滑动切换。现在，我们如果在Android上也想使用左右切换风格，该怎么做？一个简单的作法是可以直接使用<code>CupertinoPageRoute</code>，如：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code> Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> <span class=\"token function\">CupertinoPageRoute</span><span class=\"token punctuation\">(</span>  \n   builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span><span class=\"token function\">PageB</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n <span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p><code>CupertinoPageRoute</code>是Cupertino组件库提供的iOS风格的路由切换组件，它实现的就是左右滑动切换。那么我们如何来自定义路由切换动画呢？答案就是<code>PageRouteBuilder</code>。下面我们来看看如何使用<code>PageRouteBuilder</code>来自定义路由切换动画。例如我们想以渐隐渐入动画来实现路由过渡，实现代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span>\n  context<span class=\"token punctuation\">,</span>\n  <span class=\"token function\">PageRouteBuilder</span><span class=\"token punctuation\">(</span>\n    transitionDuration<span class=\"token punctuation\">:</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>milliseconds<span class=\"token punctuation\">:</span> <span class=\"token number\">500</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//动画时间为500毫秒</span>\n    pageBuilder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> Animation animation<span class=\"token punctuation\">,</span>\n        Animation secondaryAnimation<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">FadeTransition</span><span class=\"token punctuation\">(</span>\n        <span class=\"token comment\">//使用渐隐渐入过渡,</span>\n        opacity<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">,</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">PageB</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//路由B</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div><p>我们可以看到<code>pageBuilder</code> 有一个<code>animation</code>参数，这是Flutter路由管理器提供的，在路由切换时<code>pageBuilder</code>在每个动画帧都会被回调，因此我们可以通过<code>animation</code>对象来自定义过渡动画。</p> <p>无论是<code>MaterialPageRoute</code>、<code>CupertinoPageRoute</code>，还是<code>PageRouteBuilder</code>，它们都继承自PageRoute类，而<code>PageRouteBuilder</code>其实只是<code>PageRoute</code>的一个包装，我们可以直接继承<code>PageRoute</code>类来实现自定义路由，上面的例子可以通过如下方式实现：</p> <ol><li><p>定义一个路由类<code>FadeRoute</code></p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">FadeRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">PageRoute</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">FadeRoute</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    <span class=\"token metadata symbol\">@required</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>builder<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>transitionDuration <span class=\"token operator\">=</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>milliseconds<span class=\"token punctuation\">:</span> <span class=\"token number\">300</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>opaque <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>barrierDismissible <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>barrierColor<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>barrierLabel<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>maintainState <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">final</span> WidgetBuilder builder<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">final</span> Duration transitionDuration<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">final</span> bool opaque<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">final</span> bool barrierDismissible<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">final</span> Color barrierColor<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">final</span> String barrierLabel<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">final</span> bool maintainState<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">buildPage</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> animation<span class=\"token punctuation\">,</span>\n      Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> secondaryAnimation<span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">buildTransitions</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> animation<span class=\"token punctuation\">,</span>\n      Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> secondaryAnimation<span class=\"token punctuation\">,</span> Widget child<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n     <span class=\"token keyword\">return</span> <span class=\"token function\">FadeTransition</span><span class=\"token punctuation\">(</span> \n       opacity<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">,</span>\n       child<span class=\"token punctuation\">:</span> <span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n     <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div></li> <li><p>使用<code>FadeRoute</code></p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code>Navigator<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">,</span> <span class=\"token function\">FadeRoute</span><span class=\"token punctuation\">(</span>builder<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">PageB</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div></li></ol> <p>虽然上面的两种方法都可以实现自定义切换动画，但实际使用时应优先考虑使用PageRouteBuilder，这样无需定义一个新的路由类，使用起来会比较方便。但是有些时候<code>PageRouteBuilder</code>是不能满足需求的，例如在应用过渡动画时我们需要读取当前路由的一些属性，这时就只能通过继承<code>PageRoute</code>的方式了，举个例子，假如我们只想在打开新路由时应用动画，而在返回时不使用动画，那么我们在构建过渡动画时就必须判断当前路由<code>isActive</code>属性是否为<code>true</code>，代码如下：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token metadata symbol\">@override</span>\nWidget <span class=\"token function\">buildTransitions</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> animation<span class=\"token punctuation\">,</span>\n    Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> secondaryAnimation<span class=\"token punctuation\">,</span> Widget child<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token comment\">//当前路由被激活，是打开新路由</span>\n <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>isActive<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n   <span class=\"token keyword\">return</span> <span class=\"token function\">FadeTransition</span><span class=\"token punctuation\">(</span>\n     opacity<span class=\"token punctuation\">:</span> animation<span class=\"token punctuation\">,</span>\n     child<span class=\"token punctuation\">:</span> <span class=\"token function\">builder</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n   <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n <span class=\"token punctuation\">}</span><span class=\"token keyword\">else</span><span class=\"token punctuation\">{</span>\n   <span class=\"token comment\">//是返回，则不应用过渡动画</span>\n   <span class=\"token keyword\">return</span> <span class=\"token function\">Padding</span><span class=\"token punctuation\">(</span>padding<span class=\"token punctuation\">:</span> EdgeInsets<span class=\"token punctuation\">.</span>zero<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>关于路由参数的详细信息读者可以自行查阅API文档，比较简单，不再赘述。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/221.51aa50d8.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/chapter9/stagger_animation.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>9.5 交织动画 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/80.8eacbc37.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter9/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"_9-5-交织动画\"><a href=\"#_9-5-交织动画\" class=\"header-anchor\">#</a> 9.5 交织动画</h1> <p>有些时候我们可能会需要一些复杂的动画，这些动画可能由一个动画序列或重叠的动画组成，比如：有一个柱状图，需要在高度增长的同时改变颜色，等到增长到最大高度后，我们需要在X轴上平移一段距离。可以发现上述场景在不同阶段包含了多种动画，要实现这种效果，使用交织动画（Stagger Animation）会非常简单。交织动画需要注意以下几点：</p> <ol><li>要创建交织动画，需要使用多个动画对象（<code>Animation</code>）。</li> <li>一个<code>AnimationController</code>控制所有的动画对象。</li> <li>给每一个动画对象指定时间间隔（Interval）</li></ol> <p>所有动画都由同一个<a href=\"https://docs.flutter.io/flutter/animation/AnimationController-class.html\" target=\"_blank\" rel=\"noopener noreferrer\">AnimationController<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>驱动，无论动画需要持续多长时间，控制器的值必须在0.0到1.0之间，而每个动画的间隔（Interval）也必须介于0.0和1.0之间。对于在间隔中设置动画的每个属性，需要分别创建一个<a href=\"https://docs.flutter.io/flutter/animation/Tween-class.html\" target=\"_blank\" rel=\"noopener noreferrer\">Tween<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> 用于指定该属性的开始值和结束值。也就是说0.0到1.0代表整个动画过程，我们可以给不同动画指定不同的起始点和终止点来决定它们的开始时间和终止时间。</p> <h3 id=\"示例\"><a href=\"#示例\" class=\"header-anchor\">#</a> 示例</h3> <p>下面我们看一个例子，实现一个柱状图增长的动画：</p> <ol><li>开始时高度从0增长到300像素，同时颜色由绿色渐变为红色；这个过程占据整个动画时间的60%。</li> <li>高度增长到300后，开始沿X轴向右平移100像素；这个过程占用整个动画时间的40%。</li></ol> <p>我们将执行动画的Widget分离出来：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">StaggerAnimation</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatelessWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">StaggerAnimation</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> Key key<span class=\"token punctuation\">,</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>controller <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span> <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">:</span> key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">//高度动画</span>\n    height <span class=\"token operator\">=</span> Tween<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n      begin<span class=\"token punctuation\">:</span><span class=\"token number\">.0</span> <span class=\"token punctuation\">,</span>\n      end<span class=\"token punctuation\">:</span> <span class=\"token number\">300.0</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">animate</span><span class=\"token punctuation\">(</span>\n      <span class=\"token function\">CurvedAnimation</span><span class=\"token punctuation\">(</span>\n        parent<span class=\"token punctuation\">:</span> controller<span class=\"token punctuation\">,</span>\n        curve<span class=\"token punctuation\">:</span> <span class=\"token function\">Interval</span><span class=\"token punctuation\">(</span>\n          <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0.6</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//间隔，前60%的动画时间</span>\n          curve<span class=\"token punctuation\">:</span> Curves<span class=\"token punctuation\">.</span>ease<span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    color <span class=\"token operator\">=</span> <span class=\"token function\">ColorTween</span><span class=\"token punctuation\">(</span>\n      begin<span class=\"token punctuation\">:</span>Colors<span class=\"token punctuation\">.</span>green <span class=\"token punctuation\">,</span>\n      end<span class=\"token punctuation\">:</span>Colors<span class=\"token punctuation\">.</span>red<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">animate</span><span class=\"token punctuation\">(</span>\n      <span class=\"token function\">CurvedAnimation</span><span class=\"token punctuation\">(</span>\n        parent<span class=\"token punctuation\">:</span> controller<span class=\"token punctuation\">,</span>\n        curve<span class=\"token punctuation\">:</span> <span class=\"token function\">Interval</span><span class=\"token punctuation\">(</span>\n          <span class=\"token number\">0.0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0.6</span><span class=\"token punctuation\">,</span><span class=\"token comment\">//间隔，前60%的动画时间</span>\n          curve<span class=\"token punctuation\">:</span> Curves<span class=\"token punctuation\">.</span>ease<span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    padding <span class=\"token operator\">=</span> Tween<span class=\"token operator\">&lt;</span>EdgeInsets<span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span>\n      begin<span class=\"token punctuation\">:</span>EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">only</span><span class=\"token punctuation\">(</span>left<span class=\"token punctuation\">:</span> <span class=\"token number\">.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      end<span class=\"token punctuation\">:</span>EdgeInsets<span class=\"token punctuation\">.</span><span class=\"token function\">only</span><span class=\"token punctuation\">(</span>left<span class=\"token punctuation\">:</span> <span class=\"token number\">100.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">animate</span><span class=\"token punctuation\">(</span>\n      <span class=\"token function\">CurvedAnimation</span><span class=\"token punctuation\">(</span>\n        parent<span class=\"token punctuation\">:</span> controller<span class=\"token punctuation\">,</span>\n        curve<span class=\"token punctuation\">:</span> <span class=\"token function\">Interval</span><span class=\"token punctuation\">(</span>\n          <span class=\"token number\">0.6</span><span class=\"token punctuation\">,</span> <span class=\"token number\">1.0</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//间隔，后40%的动画时间</span>\n          curve<span class=\"token punctuation\">:</span> Curves<span class=\"token punctuation\">.</span>ease<span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n\n  <span class=\"token keyword\">final</span> Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> controller<span class=\"token punctuation\">;</span>\n  Animation<span class=\"token operator\">&lt;</span>double<span class=\"token operator\">&gt;</span> height<span class=\"token punctuation\">;</span>\n  Animation<span class=\"token operator\">&lt;</span>EdgeInsets<span class=\"token operator\">&gt;</span> padding<span class=\"token punctuation\">;</span>\n  Animation<span class=\"token operator\">&lt;</span>Color<span class=\"token operator\">&gt;</span> color<span class=\"token punctuation\">;</span>\n\n  Widget <span class=\"token function\">_buildAnimation</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">,</span> Widget child<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n      alignment<span class=\"token punctuation\">:</span> Alignment<span class=\"token punctuation\">.</span>bottomCenter<span class=\"token punctuation\">,</span>\n      padding<span class=\"token punctuation\">:</span>padding<span class=\"token punctuation\">.</span>value <span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n        color<span class=\"token punctuation\">:</span> color<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span>\n        width<span class=\"token punctuation\">:</span> <span class=\"token number\">50.0</span><span class=\"token punctuation\">,</span>\n        height<span class=\"token punctuation\">:</span> height<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">AnimatedBuilder</span><span class=\"token punctuation\">(</span>\n      builder<span class=\"token punctuation\">:</span> _buildAnimation<span class=\"token punctuation\">,</span>\n      animation<span class=\"token punctuation\">:</span> controller<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p><code>StaggerAnimation</code>中定义了三个动画，分别是对<code>Container</code>的<code>height</code>、<code>color</code>、<code>padding</code>属性设置的动画，然后通过<code>Interval</code>来为每个动画指定在整个动画过程中的起始点和终点。下面我们来实现启动动画的路由：</p> <div class=\"language-dart extra-class\"><pre class=\"language-dart\"><code><span class=\"token keyword\">class</span> <span class=\"token class-name\">StaggerRoute</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">StatefulWidget</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token metadata symbol\">@override</span>\n  _StaggerRouteState <span class=\"token function\">createState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token function\">_StaggerRouteState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">_StaggerRouteState</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">State</span><span class=\"token operator\">&lt;</span>StaggerRoute<span class=\"token operator\">&gt;</span> <span class=\"token keyword\">with</span> TickerProviderStateMixin <span class=\"token punctuation\">{</span>\n  AnimationController _controller<span class=\"token punctuation\">;</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  <span class=\"token keyword\">void</span> <span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">.</span><span class=\"token function\">initState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    _controller <span class=\"token operator\">=</span> <span class=\"token function\">AnimationController</span><span class=\"token punctuation\">(</span>\n        duration<span class=\"token punctuation\">:</span> <span class=\"token keyword\">const</span> <span class=\"token function\">Duration</span><span class=\"token punctuation\">(</span>milliseconds<span class=\"token punctuation\">:</span> <span class=\"token number\">2000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        vsync<span class=\"token punctuation\">:</span> <span class=\"token keyword\">this</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n\n  Future<span class=\"token operator\">&lt;</span>Null<span class=\"token operator\">&gt;</span> <span class=\"token function\">_playAnimation</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">//先正向执行动画</span>\n      <span class=\"token keyword\">await</span> _controller<span class=\"token punctuation\">.</span><span class=\"token function\">forward</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>orCancel<span class=\"token punctuation\">;</span>\n      <span class=\"token comment\">//再反向执行动画</span>\n      <span class=\"token keyword\">await</span> _controller<span class=\"token punctuation\">.</span><span class=\"token function\">reverse</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>orCancel<span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">on</span> TickerCanceled <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">// the animation got canceled, probably because we were disposed</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token metadata symbol\">@override</span>\n  Widget <span class=\"token function\">build</span><span class=\"token punctuation\">(</span>BuildContext context<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span>  <span class=\"token function\">GestureDetector</span><span class=\"token punctuation\">(</span>\n      behavior<span class=\"token punctuation\">:</span> HitTestBehavior<span class=\"token punctuation\">.</span>opaque<span class=\"token punctuation\">,</span>\n      onTap<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">_playAnimation</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token function\">Center</span><span class=\"token punctuation\">(</span>\n        child<span class=\"token punctuation\">:</span> <span class=\"token function\">Container</span><span class=\"token punctuation\">(</span>\n          width<span class=\"token punctuation\">:</span> <span class=\"token number\">300.0</span><span class=\"token punctuation\">,</span>\n          height<span class=\"token punctuation\">:</span> <span class=\"token number\">300.0</span><span class=\"token punctuation\">,</span>\n          decoration<span class=\"token punctuation\">:</span> <span class=\"token function\">BoxDecoration</span><span class=\"token punctuation\">(</span>\n            color<span class=\"token punctuation\">:</span> Colors<span class=\"token punctuation\">.</span>black<span class=\"token punctuation\">.</span><span class=\"token function\">withOpacity</span><span class=\"token punctuation\">(</span><span class=\"token number\">0.1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            border<span class=\"token punctuation\">:</span> Border<span class=\"token punctuation\">.</span><span class=\"token function\">all</span><span class=\"token punctuation\">(</span>\n              color<span class=\"token punctuation\">:</span>  Colors<span class=\"token punctuation\">.</span>black<span class=\"token punctuation\">.</span><span class=\"token function\">withOpacity</span><span class=\"token punctuation\">(</span><span class=\"token number\">0.5</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token comment\">//调用我们定义的交织动画Widget</span>\n          child<span class=\"token punctuation\">:</span> <span class=\"token function\">StaggerAnimation</span><span class=\"token punctuation\">(</span>\n              controller<span class=\"token punctuation\">:</span> _controller\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div><p>执行效果如图，点击图9-3灰色矩形，就可以看到整个动画效果，图9-4是动画执行过程中的一帧。</p> <p><img src=\"/assets/img/9-3.ede423b7.png\" alt=\"图9-3\"><img src=\"/assets/img/9-4.cc3139b7.png\" alt=\"图9-4\"></p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/80.8eacbc37.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/imgs/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>前言 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/222.8f955066.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/imgs/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"前言\"><a href=\"#前言\" class=\"header-anchor\">#</a> 前言</h1> <p>本书是第一本系统介绍Flutter技术的中文书籍，它是Flutter中文社区发起的开源项目之一，旨在帮助开发者入门，系统地、循序渐进的了解Flutter。</p> <blockquote><p>本书官网访问量较大，由于服务器配置有限，所以某些时段访问本站可能略有延迟，如果延迟较久，读者可以直接在<a href=\"https://github.com/flutterchina/flutter-in-action/blob/master/docs/SUMMARY.md\" target=\"_blank\" rel=\"noopener noreferrer\">Github上阅读本书<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>。</p></blockquote> <h2 id=\"缘起\"><a href=\"#缘起\" class=\"header-anchor\">#</a> 缘起</h2> <p>在全球，随着Flutter被越来越多的知名公司应用在自己的商业APP中，Flutter这门新技术也逐渐进入了移动开发者的视野，尤其是当Google在2018年IO大会上发布了第一个Preview版本后，国内刮起来一股学习Flutter的热潮。</p> <p>在Flutter发布之初，当时，我在看完Flutter原理介绍后，就对它产生了浓厚的兴趣。当时笔者身边也一些人比较关注Flutter，我也被经常问到关于Flutter的一些问题，比如Flutter怎么样？和RN有什么区别？Flutter为什么要用Dart？当时也听到了一些批评的声音，比如有些人说Flutter只是重复造轮子，没啥亮点、Flutter最大的缺点就是使用了Dart语言等。在听到这些问题及论调后，我深知这是对Flutter的不了解而造成的，这和当时国内缺乏Flutter中文文档和教程有直接关系，很多人对Flutter的了解都只停留在Google的发布会介绍（有中文翻译）。</p> <p>在笔者深入的了解Flutter后，深知Flutter必将是一个会改变移动开发格局的里程碑级作品，它从设计之初就对性能和开发效率兼顾，并且借鉴了React（一个Web开发框架）的响应式的UI框架设计思想等，总之，很难用一两句话说完Flutter的优点，同时我也很快成为了Flutter的路转粉。</p> <p>为了更好的帮助中国开发者了解这门新技术，我在2018年初开始翻译Flutter官网文档，同年4月份上线了<a href=\"https://flutterchina.club/\" target=\"_blank\" rel=\"noopener noreferrer\">Flutter中文网<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>，上线后反响很强烈，Flutter中文网也很快被传播开，百度搜索排名迅速蹿升到前三，截止目前，Flutter中文官网日PV在7万左右，每日独立访问人数近一万多。</p> <p>虽然Flutter中文网给中国开发者提供了很好的第一手了解Flutter的资料，但是笔者还会经常遇到一些对Flutter技术处于围观而不愿尝试的开发者。这主要是因为当时Flutter在国内没有成功案例，再加上新技术都有学习成本，所以即使有文档，也会有一些开发者犹豫是否来学习。为了解决这部分开发者的疑虑，我就想如果能用Flutter开发一个完整的APP发布到应用商店，这样开发者就可以在犹豫的时候可以先实际感受一下Flutter应用，这样有个直观的了解后，就会容易做出判断，为此，我开发了<a href=\"https://flutterchina.club/app/gm.html\" target=\"_blank\" rel=\"noopener noreferrer\">Gitme<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>，它是一个Github客户端，它支持了源码浏览、Issue、Label等Github的大多数功能，到目前为止，通过Gitme登录过Github账号的用户有8000多人，日活用户有1000人。更重要的是，有很多人正是用了Gitme后，才来学Flutter的。</p> <p>无论是做Flutter中文网，还是写Gitme，主要目标都是帮助开发者学习Flutter，同时消除围观开发者疑虑。但当开发者们真正开始动手时，Flutter的生态问题就变得尤为突出。由于在2018年初Flutter刚起步时，很多基础的包和库都是空白，少数已有的一些库也大都是预览版（未到1.0），存在很多bug。这个状况不是一两个人花一两天能搞定的，这是需要聚整个Flutter开发者社群之力，耗费数年时间才可能有所改善。因此，在2018年4月份，我以Flutter中文网名义发起了Flutter开源计划，该计划主要是开发一些常用的包来丰富Flutter生态，帮助开发者提高开发效率。自在github建立<a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\">Flutter中文开发者社区账号<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>以来，前后开源了dio、cookieJar、flukit等多个项目，而dio在开源两周后，就迅速成为Flutter第三方包中Star排名第一的开源库。</p> <p>虽然做的事情已经够多了，但是仍有一些很有必要去做的事情，由于时间原因，一直被搁置。</p> <p>随着学习Flutter的人越来越多，一部分开发者通过查看官网的文档就能入门，但也有很多开发者感觉学习时仍然有些吃力，主要原因有两个，首先官网的文档主要是为了引导开发者快速上手的，讲的并不是很细；其次是我们虽然翻译了官方文档，但是对于Flutter SDK文档并没有翻译，而在开发中遇到的一些具体问题通常都得去查看SDK文档。所以，要解决这两个问题，必须得有一个系统化的Flutter教程，它不仅可以快速引导开发者入门，而且也能触及到一些细节和原理，最好也能提供一些学习和研究Flutter的方法。因此，如果能有一本能系统地介绍Flutter的书籍便是便是非常棒的！但是，当时没有一本关于Flutter的中文书籍，因此，我便计划写一本能由浅入深、系统介绍Flutter的书。2018年12月，《Flutter实战》完成初稿，并在Github上开源，同时上线了<a href=\"https://book.flutterchina.club/\" target=\"_blank\" rel=\"noopener noreferrer\">《Flutter实战》电子书官网<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> ，至今每天有3000多人在线浏览本书。那为什么不直接出版？如果直接出版，不仅有稿费，而且也能保护知识产权，而直接开源，不就只能当雷锋了？其实，无论是做中文网、写Gitme、做Flutter开源项目，我的出发点都是为了能帮助中国开发者了解、学习Flutter，而这是一件非常有意义的事，只要坚持做对别人有价值的事，那么上帝迟早会奖赏你；当然我们也在网站中留了打赏的按钮，如果读者觉得有帮助，可以扫码打赏，请笔者喝一杯咖啡。另外由于成书比较仓促，当时书中也有很多错误，开源后，有很多读者提PR来纠正书中的错别字，时至今日，有78名开发者为本书提过PR，我在此，衷心的感谢你们。</p> <p>起初，我没有出版实体书的打算，当时我以为开发者直接通过在线访问本书官网即方便又不用付费，何乐而不为。但在本书上线后，很多读者来添加微信好友，表示非常有收益，很期待出版纸质书，甚至有比较热心的读者想提前付定金预定！我理解，这是大家对我所做之事的认可和鼓励。考虑到，确实有一部分读者，尤其是还没毕业的同学，可能更喜欢通过书籍去学习，为此，我已经和机械工业出版社合作，目前本书纸质版正在出版中，敬请关注。</p> <h3 id=\"本书组织结构\"><a href=\"#本书组织结构\" class=\"header-anchor\">#</a> 本书组织结构</h3> <p>本书采用由浅入深的方式介绍Flutter技术原理，分为三篇，总计15章，各篇的主要内容如下。</p> <ul><li>第一篇，入门篇（第1章~第7章），包括Flutter技术的出现背景和简介、Flutter的各种类型的Widget以及如何构建UI。通过学习本章，读者可以掌握如何使用Flutter来构建UI界面。</li> <li>第二篇，进阶篇（第8章~第14章），包括Flutter中的事件机制、动画、自定义组件、文件和网络、插件、国际化以及Flutter核心原理等。通过本章内容，读者可以对Flutter整体构建及原理有一个深入的认识。</li> <li>第三篇，实例篇（第15章），本章主要通过一个简版的Github APP来将前面介绍的内容串起来，让开发者对一个完整的Flutter APP开发流有个了解。</li></ul> <h2 id=\"本书特色\"><a href=\"#本书特色\" class=\"header-anchor\">#</a> 本书特色</h2> <p>笔者在大学时读过候捷（真名侯俊杰）写的一些C++相关书籍，在他的《深入浅出MFC》一书中，有一句话我印象像非常深 “唯有深入，方能浅出”。我非常认同这句话，对于一门技术，只有了解的深入，才能用最浅显、通俗的话语描述出。我在写作本书时，深入浅出就是一个主要目标。所以，本书的目标不仅是想告诉读者如何使用Flutter，而且也非常关注各个知识点的底层实现以及设计思想。从本书章节划分上来看，入门篇为“浅出”，进阶篇则是“深入”。另外由于PC客户端开发、移动开发、Web开发这些经验我都有，而Flutter本质上是一个UI系统，而UI系统的设计和实现在”大前端“下有很多相通之处，所以在本书中的一些知识点我也会对比一些其他UI系统（主要是Android或Web）相应的实现，便于有相关开发经验的读者对比理解。</p> <h2 id=\"本书读者对象\"><a href=\"#本书读者对象\" class=\"header-anchor\">#</a> 本书读者对象</h2> <ul><li>读者至少熟悉一种编程语言。</li> <li>读者最好接触过PC客户端、移动开发或Web前端开发中的一种。</li> <li>本书不适合做为编程的入门读物。</li></ul> <h2 id=\"关于随书源码\"><a href=\"#关于随书源码\" class=\"header-anchor\">#</a> 关于随书源码</h2> <p>由于篇幅所限，本书中大多数示例代码都只是部分核心代码，读者可以去<a href=\"https://github.com/wendux/flutter_in_action_source_code\" target=\"_blank\" rel=\"noopener noreferrer\">这里<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>查看下载。</p> <h2 id=\"致谢\"><a href=\"#致谢\" class=\"header-anchor\">#</a> 致谢</h2> <p>感谢一直以来支持Flutter中文网、Flutter开源项目的人以及所有对本书提过PR的人，正是因为有你们，才有这本书。另外尤其感谢给本书打赏过的同学，你们的支持给了我很大的鼓励。</p> <h2 id=\"权益\"><a href=\"#权益\" class=\"header-anchor\">#</a> 权益</h2> <p>最后，知识是应该付费的，创作不易，开源不等于免费，如果您是本书读者并手头宽裕，可以点击下面打赏按钮打赏；当然，如果您囊中羞涩，您也可以阅读本书，但我对您有个小小的要求，希望您在阅读的过程中能积极参与到本书的纠错以及未完成内容的创作上来，也算是有所付出。</p> <p>近来在网上发现很多<strong>原封不动复制本书</strong>的镜像网站和大量复制或<strong>引用了本书但未注明出处</strong>的博客、文章甚至书籍；对此，笔者在此声明，本书著作权归wendux所有，任何组织或个人在未经授权的情况下复制、拷贝、抄袭本书用于商业目的，笔者保留追究其法律责任的权利。如果是非商业目的的转载和引用，请注明出处并附上本书网址。另外如有出版机构愿意为本书出版实体书或者想转载本书内容，亦或是想合作，请加微信Demons-du.</p> <h3 id=\"勘误\"><a href=\"#勘误\" class=\"header-anchor\">#</a> 勘误</h3> <p>由于Flutter SDK在不断更新，本书中的部分内容（如类的继承关系、参数等）可能会和新版本的Flutter不一致，读者以最新的Flutter SDK为准。另外，由于时间仓促，书中难免有错误之处，如果您发现本书中的错误，欢迎点击右上角的”编辑按钮“，提PR。如果您想一起参与本书创作，可以参考<a href=\"https://github.com/flutterchina/flutter-in-action#%E8%B4%A1%E7%8C%AE%E9%A1%BB%E7%9F%A5\" target=\"_blank\" rel=\"noopener noreferrer\">《Flutter实战》贡献指南<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/222.8f955066.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>前言 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/223.3230e791.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"前言\"><a href=\"#前言\" class=\"header-anchor\">#</a> 前言</h1> <p>本书是第一本系统介绍Flutter技术的中文书籍，它是Flutter中文社区发起的开源项目之一，旨在帮助开发者入门，系统地、循序渐进的了解Flutter。</p> <blockquote><p>本书官网访问量较大，由于服务器配置有限，所以某些时段访问本站可能略有延迟，如果延迟较久，读者可以直接在<a href=\"https://github.com/flutterchina/flutter-in-action/blob/master/docs/SUMMARY.md\" target=\"_blank\" rel=\"noopener noreferrer\">Github上阅读本书<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>。</p></blockquote> <h2 id=\"缘起\"><a href=\"#缘起\" class=\"header-anchor\">#</a> 缘起</h2> <p>在全球，随着Flutter被越来越多的知名公司应用在自己的商业APP中，Flutter这门新技术也逐渐进入了移动开发者的视野，尤其是当Google在2018年IO大会上发布了第一个Preview版本后，国内刮起来一股学习Flutter的热潮。</p> <p>在Flutter发布之初，当时，我在看完Flutter原理介绍后，就对它产生了浓厚的兴趣。当时笔者身边也一些人比较关注Flutter，我也被经常问到关于Flutter的一些问题，比如Flutter怎么样？和RN有什么区别？Flutter为什么要用Dart？当时也听到了一些批评的声音，比如有些人说Flutter只是重复造轮子，没啥亮点、Flutter最大的缺点就是使用了Dart语言等。在听到这些问题及论调后，我深知这是对Flutter的不了解而造成的，这和当时国内缺乏Flutter中文文档和教程有直接关系，很多人对Flutter的了解都只停留在Google的发布会介绍（有中文翻译）。</p> <p>在笔者深入的了解Flutter后，深知Flutter必将是一个会改变移动开发格局的里程碑级作品，它从设计之初就对性能和开发效率兼顾，并且借鉴了React（一个Web开发框架）的响应式的UI框架设计思想等，总之，很难用一两句话说完Flutter的优点，同时我也很快成为了Flutter的路转粉。</p> <p>为了更好的帮助中国开发者了解这门新技术，我在2018年初开始翻译Flutter官网文档，同年4月份上线了<a href=\"https://flutterchina.club/\" target=\"_blank\" rel=\"noopener noreferrer\">Flutter中文网<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>，上线后反响很强烈，Flutter中文网也很快被传播开，百度搜索排名迅速蹿升到前三，截止目前，Flutter中文官网日PV在7万左右，每日独立访问人数近一万多。</p> <p>虽然Flutter中文网给中国开发者提供了很好的第一手了解Flutter的资料，但是笔者还会经常遇到一些对Flutter技术处于围观而不愿尝试的开发者。这主要是因为当时Flutter在国内没有成功案例，再加上新技术都有学习成本，所以即使有文档，也会有一些开发者犹豫是否来学习。为了解决这部分开发者的疑虑，我就想如果能用Flutter开发一个完整的APP发布到应用商店，这样开发者就可以在犹豫的时候可以先实际感受一下Flutter应用，这样有个直观的了解后，就会容易做出判断，为此，我开发了<a href=\"https://flutterchina.club/app/gm.html\" target=\"_blank\" rel=\"noopener noreferrer\">Gitme<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>，它是一个Github客户端，它支持了源码浏览、Issue、Label等Github的大多数功能，到目前为止，通过Gitme登录过Github账号的用户有8000多人，日活用户有1000人。更重要的是，有很多人正是用了Gitme后，才来学Flutter的。</p> <p>无论是做Flutter中文网，还是写Gitme，主要目标都是帮助开发者学习Flutter，同时消除围观开发者疑虑。但当开发者们真正开始动手时，Flutter的生态问题就变得尤为突出。由于在2018年初Flutter刚起步时，很多基础的包和库都是空白，少数已有的一些库也大都是预览版（未到1.0），存在很多bug。这个状况不是一两个人花一两天能搞定的，这是需要聚整个Flutter开发者社群之力，耗费数年时间才可能有所改善。因此，在2018年4月份，我以Flutter中文网名义发起了Flutter开源计划，该计划主要是开发一些常用的包来丰富Flutter生态，帮助开发者提高开发效率。自在github建立<a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\">Flutter中文开发者社区账号<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>以来，前后开源了dio、cookieJar、flukit等多个项目，而dio在开源两周后，就迅速成为Flutter第三方包中Star排名第一的开源库。</p> <p>虽然做的事情已经够多了，但是仍有一些很有必要去做的事情，由于时间原因，一直被搁置。</p> <p>随着学习Flutter的人越来越多，一部分开发者通过查看官网的文档就能入门，但也有很多开发者感觉学习时仍然有些吃力，主要原因有两个，首先官网的文档主要是为了引导开发者快速上手的，讲的并不是很细；其次是我们虽然翻译了官方文档，但是对于Flutter SDK文档并没有翻译，而在开发中遇到的一些具体问题通常都得去查看SDK文档。所以，要解决这两个问题，必须得有一个系统化的Flutter教程，它不仅可以快速引导开发者入门，而且也能触及到一些细节和原理，最好也能提供一些学习和研究Flutter的方法。因此，如果能有一本能系统地介绍Flutter的书籍便是便是非常棒的！但是，当时没有一本关于Flutter的中文书籍，因此，我便计划写一本能由浅入深、系统介绍Flutter的书。2018年12月，《Flutter实战》完成初稿，并在Github上开源，同时上线了<a href=\"https://book.flutterchina.club/\" target=\"_blank\" rel=\"noopener noreferrer\">《Flutter实战》电子书官网<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> ，至今每天有3000多人在线浏览本书。那为什么不直接出版？如果直接出版，不仅有稿费，而且也能保护知识产权，而直接开源，不就只能当雷锋了？其实，无论是做中文网、写Gitme、做Flutter开源项目，我的出发点都是为了能帮助中国开发者了解、学习Flutter，而这是一件非常有意义的事，只要坚持做对别人有价值的事，那么上帝迟早会奖赏你；当然我们也在网站中留了打赏的按钮，如果读者觉得有帮助，可以扫码打赏，请笔者喝一杯咖啡。另外由于成书比较仓促，当时书中也有很多错误，开源后，有很多读者提PR来纠正书中的错别字，时至今日，有78名开发者为本书提过PR，我在此，衷心的感谢你们。</p> <p>起初，我没有出版实体书的打算，当时我以为开发者直接通过在线访问本书官网即方便又不用付费，何乐而不为。但在本书上线后，很多读者来添加微信好友，表示非常有收益，很期待出版纸质书，甚至有比较热心的读者想提前付定金预定！我理解，这是大家对我所做之事的认可和鼓励。考虑到，确实有一部分读者，尤其是还没毕业的同学，可能更喜欢通过书籍去学习，为此，我已经和机械工业出版社合作，目前本书纸质版正在出版中，敬请关注。</p> <h3 id=\"本书组织结构\"><a href=\"#本书组织结构\" class=\"header-anchor\">#</a> 本书组织结构</h3> <p>本书采用由浅入深的方式介绍Flutter技术原理，分为三篇，总计15章，各篇的主要内容如下。</p> <ul><li>第一篇，入门篇（第1章~第7章），包括Flutter技术的出现背景和简介、Flutter的各种类型的Widget以及如何构建UI。通过学习本章，读者可以掌握如何使用Flutter来构建UI界面。</li> <li>第二篇，进阶篇（第8章~第14章），包括Flutter中的事件机制、动画、自定义组件、文件和网络、插件、国际化以及Flutter核心原理等。通过本章内容，读者可以对Flutter整体构建及原理有一个深入的认识。</li> <li>第三篇，实例篇（第15章），本章主要通过一个简版的Github APP来将前面介绍的内容串起来，让开发者对一个完整的Flutter APP开发流有个了解。</li></ul> <h2 id=\"本书特色\"><a href=\"#本书特色\" class=\"header-anchor\">#</a> 本书特色</h2> <p>笔者在大学时读过候捷（真名侯俊杰）写的一些C++相关书籍，在他的《深入浅出MFC》一书中，有一句话我印象像非常深 “唯有深入，方能浅出”。我非常认同这句话，对于一门技术，只有了解的深入，才能用最浅显、通俗的话语描述出。我在写作本书时，深入浅出就是一个主要目标。所以，本书的目标不仅是想告诉读者如何使用Flutter，而且也非常关注各个知识点的底层实现以及设计思想。从本书章节划分上来看，入门篇为“浅出”，进阶篇则是“深入”。另外由于PC客户端开发、移动开发、Web开发这些经验我都有，而Flutter本质上是一个UI系统，而UI系统的设计和实现在”大前端“下有很多相通之处，所以在本书中的一些知识点我也会对比一些其他UI系统（主要是Android或Web）相应的实现，便于有相关开发经验的读者对比理解。</p> <h2 id=\"本书读者对象\"><a href=\"#本书读者对象\" class=\"header-anchor\">#</a> 本书读者对象</h2> <ul><li>读者至少熟悉一种编程语言。</li> <li>读者最好接触过PC客户端、移动开发或Web前端开发中的一种。</li> <li>本书不适合做为编程的入门读物。</li></ul> <h2 id=\"关于随书源码\"><a href=\"#关于随书源码\" class=\"header-anchor\">#</a> 关于随书源码</h2> <p>由于篇幅所限，本书中大多数示例代码都只是部分核心代码，读者可以去<a href=\"https://github.com/wendux/flutter_in_action_source_code\" target=\"_blank\" rel=\"noopener noreferrer\">这里<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>查看下载。</p> <h2 id=\"致谢\"><a href=\"#致谢\" class=\"header-anchor\">#</a> 致谢</h2> <p>感谢一直以来支持Flutter中文网、Flutter开源项目的人以及所有对本书提过PR的人，正是因为有你们，才有这本书。另外尤其感谢给本书打赏过的同学，你们的支持给了我很大的鼓励。</p> <h2 id=\"权益\"><a href=\"#权益\" class=\"header-anchor\">#</a> 权益</h2> <p>最后，知识是应该付费的，创作不易，开源不等于免费，如果您是本书读者并手头宽裕，可以点击下面打赏按钮打赏；当然，如果您囊中羞涩，您也可以阅读本书，但我对您有个小小的要求，希望您在阅读的过程中能积极参与到本书的纠错以及未完成内容的创作上来，也算是有所付出。</p> <p>近来在网上发现很多<strong>原封不动复制本书</strong>的镜像网站和大量复制或<strong>引用了本书但未注明出处</strong>的博客、文章甚至书籍；对此，笔者在此声明，本书著作权归wendux所有，任何组织或个人在未经授权的情况下复制、拷贝、抄袭本书用于商业目的，笔者保留追究其法律责任的权利。如果是非商业目的的转载和引用，请注明出处并附上本书网址。另外如有出版机构愿意为本书出版实体书或者想转载本书内容，亦或是想合作，请加微信Demons-du.</p> <h3 id=\"勘误\"><a href=\"#勘误\" class=\"header-anchor\">#</a> 勘误</h3> <p>由于Flutter SDK在不断更新，本书中的部分内容（如类的继承关系、参数等）可能会和新版本的Flutter不一致，读者以最新的Flutter SDK为准。另外，由于时间仓促，书中难免有错误之处，如果您发现本书中的错误，欢迎点击右上角的”编辑按钮“，提PR。如果您想一起参与本书创作，可以参考<a href=\"https://github.com/flutterchina/flutter-in-action#%E8%B4%A1%E7%8C%AE%E9%A1%BB%E7%9F%A5\" target=\"_blank\" rel=\"noopener noreferrer\">《Flutter实战》贡献指南<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/223.3230e791.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/intro.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>前言 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/224.35c9623f.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"前言\"><a href=\"#前言\" class=\"header-anchor\">#</a> 前言</h1> <p>本书是第一本系统介绍Flutter技术的中文书籍，它是Flutter中文社区发起的开源项目之一，旨在帮助开发者入门，系统地、循序渐进的了解Flutter。</p> <blockquote><p>本书官网访问量较大，由于服务器配置有限，所以某些时段访问本站可能略有延迟，如果延迟较久，读者可以直接在<a href=\"https://github.com/flutterchina/flutter-in-action/blob/master/docs/SUMMARY.md\" target=\"_blank\" rel=\"noopener noreferrer\">Github上阅读本书<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>。</p></blockquote> <h2 id=\"缘起\"><a href=\"#缘起\" class=\"header-anchor\">#</a> 缘起</h2> <p>在全球，随着Flutter被越来越多的知名公司应用在自己的商业APP中，Flutter这门新技术也逐渐进入了移动开发者的视野，尤其是当Google在2018年IO大会上发布了第一个Preview版本后，国内刮起来一股学习Flutter的热潮。</p> <p>在Flutter发布之初，当时，我在看完Flutter原理介绍后，就对它产生了浓厚的兴趣。当时笔者身边也一些人比较关注Flutter，我也被经常问到关于Flutter的一些问题，比如Flutter怎么样？和RN有什么区别？Flutter为什么要用Dart？当时也听到了一些批评的声音，比如有些人说Flutter只是重复造轮子，没啥亮点、Flutter最大的缺点就是使用了Dart语言等。在听到这些问题及论调后，我深知这是对Flutter的不了解而造成的，这和当时国内缺乏Flutter中文文档和教程有直接关系，很多人对Flutter的了解都只停留在Google的发布会介绍（有中文翻译）。</p> <p>在笔者深入的了解Flutter后，深知Flutter必将是一个会改变移动开发格局的里程碑级作品，它从设计之初就对性能和开发效率兼顾，并且借鉴了React（一个Web开发框架）的响应式的UI框架设计思想等，总之，很难用一两句话说完Flutter的优点，同时我也很快成为了Flutter的路转粉。</p> <p>为了更好的帮助中国开发者了解这门新技术，我在2018年初开始翻译Flutter官网文档，同年4月份上线了<a href=\"https://flutterchina.club/\" target=\"_blank\" rel=\"noopener noreferrer\">Flutter中文网<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>，上线后反响很强烈，Flutter中文网也很快被传播开，百度搜索排名迅速蹿升到前三，截止目前，Flutter中文官网日PV在7万左右，每日独立访问人数近一万多。</p> <p>虽然Flutter中文网给中国开发者提供了很好的第一手了解Flutter的资料，但是笔者还会经常遇到一些对Flutter技术处于围观而不愿尝试的开发者。这主要是因为当时Flutter在国内没有成功案例，再加上新技术都有学习成本，所以即使有文档，也会有一些开发者犹豫是否来学习。为了解决这部分开发者的疑虑，我就想如果能用Flutter开发一个完整的APP发布到应用商店，这样开发者就可以在犹豫的时候可以先实际感受一下Flutter应用，这样有个直观的了解后，就会容易做出判断，为此，我开发了<a href=\"https://flutterchina.club/app/gm.html\" target=\"_blank\" rel=\"noopener noreferrer\">Gitme<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>，它是一个Github客户端，它支持了源码浏览、Issue、Label等Github的大多数功能，到目前为止，通过Gitme登录过Github账号的用户有8000多人，日活用户有1000人。更重要的是，有很多人正是用了Gitme后，才来学Flutter的。</p> <p>无论是做Flutter中文网，还是写Gitme，主要目标都是帮助开发者学习Flutter，同时消除围观开发者疑虑。但当开发者们真正开始动手时，Flutter的生态问题就变得尤为突出。由于在2018年初Flutter刚起步时，很多基础的包和库都是空白，少数已有的一些库也大都是预览版（未到1.0），存在很多bug。这个状况不是一两个人花一两天能搞定的，这是需要聚整个Flutter开发者社群之力，耗费数年时间才可能有所改善。因此，在2018年4月份，我以Flutter中文网名义发起了Flutter开源计划，该计划主要是开发一些常用的包来丰富Flutter生态，帮助开发者提高开发效率。自在github建立<a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\">Flutter中文开发者社区账号<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>以来，前后开源了dio、cookieJar、flukit等多个项目，而dio在开源两周后，就迅速成为Flutter第三方包中Star排名第一的开源库。</p> <p>虽然做的事情已经够多了，但是仍有一些很有必要去做的事情，由于时间原因，一直被搁置。</p> <p>随着学习Flutter的人越来越多，一部分开发者通过查看官网的文档就能入门，但也有很多开发者感觉学习时仍然有些吃力，主要原因有两个，首先官网的文档主要是为了引导开发者快速上手的，讲的并不是很细；其次是我们虽然翻译了官方文档，但是对于Flutter SDK文档并没有翻译，而在开发中遇到的一些具体问题通常都得去查看SDK文档。所以，要解决这两个问题，必须得有一个系统化的Flutter教程，它不仅可以快速引导开发者入门，而且也能触及到一些细节和原理，最好也能提供一些学习和研究Flutter的方法。因此，如果能有一本能系统地介绍Flutter的书籍便是便是非常棒的！但是，当时没有一本关于Flutter的中文书籍，因此，我便计划写一本能由浅入深、系统介绍Flutter的书。2018年12月，《Flutter实战》完成初稿，并在Github上开源，同时上线了<a href=\"https://book.flutterchina.club/\" target=\"_blank\" rel=\"noopener noreferrer\">《Flutter实战》电子书官网<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a> ，至今每天有3000多人在线浏览本书。那为什么不直接出版？如果直接出版，不仅有稿费，而且也能保护知识产权，而直接开源，不就只能当雷锋了？其实，无论是做中文网、写Gitme、做Flutter开源项目，我的出发点都是为了能帮助中国开发者了解、学习Flutter，而这是一件非常有意义的事，只要坚持做对别人有价值的事，那么上帝迟早会奖赏你；当然我们也在网站中留了打赏的按钮，如果读者觉得有帮助，可以扫码打赏，请笔者喝一杯咖啡。另外由于成书比较仓促，当时书中也有很多错误，开源后，有很多读者提PR来纠正书中的错别字，时至今日，有78名开发者为本书提过PR，我在此，衷心的感谢你们。</p> <p>起初，我没有出版实体书的打算，当时我以为开发者直接通过在线访问本书官网即方便又不用付费，何乐而不为。但在本书上线后，很多读者来添加微信好友，表示非常有收益，很期待出版纸质书，甚至有比较热心的读者想提前付定金预定！我理解，这是大家对我所做之事的认可和鼓励。考虑到，确实有一部分读者，尤其是还没毕业的同学，可能更喜欢通过书籍去学习，为此，我已经和机械工业出版社合作，目前本书纸质版正在出版中，敬请关注。</p> <h3 id=\"本书组织结构\"><a href=\"#本书组织结构\" class=\"header-anchor\">#</a> 本书组织结构</h3> <p>本书采用由浅入深的方式介绍Flutter技术原理，分为三篇，总计15章，各篇的主要内容如下。</p> <ul><li>第一篇，入门篇（第1章~第7章），包括Flutter技术的出现背景和简介、Flutter的各种类型的Widget以及如何构建UI。通过学习本章，读者可以掌握如何使用Flutter来构建UI界面。</li> <li>第二篇，进阶篇（第8章~第14章），包括Flutter中的事件机制、动画、自定义组件、文件和网络、插件、国际化以及Flutter核心原理等。通过本章内容，读者可以对Flutter整体构建及原理有一个深入的认识。</li> <li>第三篇，实例篇（第15章），本章主要通过一个简版的Github APP来将前面介绍的内容串起来，让开发者对一个完整的Flutter APP开发流有个了解。</li></ul> <h2 id=\"本书特色\"><a href=\"#本书特色\" class=\"header-anchor\">#</a> 本书特色</h2> <p>笔者在大学时读过候捷（真名侯俊杰）写的一些C++相关书籍，在他的《深入浅出MFC》一书中，有一句话我印象像非常深 “唯有深入，方能浅出”。我非常认同这句话，对于一门技术，只有了解的深入，才能用最浅显、通俗的话语描述出。我在写作本书时，深入浅出就是一个主要目标。所以，本书的目标不仅是想告诉读者如何使用Flutter，而且也非常关注各个知识点的底层实现以及设计思想。从本书章节划分上来看，入门篇为“浅出”，进阶篇则是“深入”。另外由于PC客户端开发、移动开发、Web开发这些经验我都有，而Flutter本质上是一个UI系统，而UI系统的设计和实现在”大前端“下有很多相通之处，所以在本书中的一些知识点我也会对比一些其他UI系统（主要是Android或Web）相应的实现，便于有相关开发经验的读者对比理解。</p> <h2 id=\"本书读者对象\"><a href=\"#本书读者对象\" class=\"header-anchor\">#</a> 本书读者对象</h2> <ul><li>读者至少熟悉一种编程语言。</li> <li>读者最好接触过PC客户端、移动开发或Web前端开发中的一种。</li> <li>本书不适合做为编程的入门读物。</li></ul> <h2 id=\"关于随书源码\"><a href=\"#关于随书源码\" class=\"header-anchor\">#</a> 关于随书源码</h2> <p>由于篇幅所限，本书中大多数示例代码都只是部分核心代码，读者可以去<a href=\"https://github.com/wendux/flutter_in_action_source_code\" target=\"_blank\" rel=\"noopener noreferrer\">这里<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>查看下载。</p> <h2 id=\"致谢\"><a href=\"#致谢\" class=\"header-anchor\">#</a> 致谢</h2> <p>感谢一直以来支持Flutter中文网、Flutter开源项目的人以及所有对本书提过PR的人，正是因为有你们，才有这本书。另外尤其感谢给本书打赏过的同学，你们的支持给了我很大的鼓励。</p> <h2 id=\"权益\"><a href=\"#权益\" class=\"header-anchor\">#</a> 权益</h2> <p>最后，知识是应该付费的，创作不易，开源不等于免费，如果您是本书读者并手头宽裕，可以点击下面打赏按钮打赏；当然，如果您囊中羞涩，您也可以阅读本书，但我对您有个小小的要求，希望您在阅读的过程中能积极参与到本书的纠错以及未完成内容的创作上来，也算是有所付出。</p> <p>近来在网上发现很多<strong>原封不动复制本书</strong>的镜像网站和大量复制或<strong>引用了本书但未注明出处</strong>的博客、文章甚至书籍；对此，笔者在此声明，本书著作权归wendux所有，任何组织或个人在未经授权的情况下复制、拷贝、抄袭本书用于商业目的，笔者保留追究其法律责任的权利。如果是非商业目的的转载和引用，请注明出处并附上本书网址。另外如有出版机构愿意为本书出版实体书或者想转载本书内容，亦或是想合作，请加微信Demons-du.</p> <h3 id=\"勘误\"><a href=\"#勘误\" class=\"header-anchor\">#</a> 勘误</h3> <p>由于Flutter SDK在不断更新，本书中的部分内容（如类的继承关系、参数等）可能会和新版本的Flutter不一致，读者以最新的Flutter SDK为准。另外，由于时间仓促，书中难免有错误之处，如果您发现本书中的错误，欢迎点击右上角的”编辑按钮“，提PR。如果您想一起参与本书创作，可以参考<a href=\"https://github.com/flutterchina/flutter-in-action#%E8%B4%A1%E7%8C%AE%E9%A1%BB%E7%9F%A5\" target=\"_blank\" rel=\"noopener noreferrer\">《Flutter实战》贡献指南<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/224.35c9623f.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/join_us.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>字节跳动-内推 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/225.8687c2ec.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading open\"><span>字节跳动-内推</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/v2/join_us.html#前端团队介绍\" class=\"sidebar-link\">前端团队介绍</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/v2/join_us.html#业务线介绍\" class=\"sidebar-link\">业务线介绍</a></li><li class=\"sidebar-sub-header\"><a href=\"/v2/join_us.html#团队福利\" class=\"sidebar-link\">团队福利</a></li></ul></li><li><a href=\"/v2/join_us.html#职位介绍\" class=\"sidebar-link\">职位介绍</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/v2/join_us.html#职位-前端开发工程师-校招-社招-急\" class=\"sidebar-link\">职位—：前端开发工程师（校招/社招）急</a></li><li class=\"sidebar-sub-header\"><a href=\"/v2/join_us.html#职位二-校招前端开发实习生\" class=\"sidebar-link\">职位二：校招前端开发实习生</a></li><li class=\"sidebar-sub-header\"><a href=\"/v2/join_us.html#职位三-web3d开发工程师-急\" class=\"sidebar-link\">职位三：web3D开发工程师（急）</a></li></ul></li><li><a href=\"/v2/join_us.html#其它职位-实习-校招-社招均可\" class=\"sidebar-link\">其它职位（实习/校招/社招均可）</a><ul class=\"sidebar-sub-headers\"><li class=\"sidebar-sub-header\"><a href=\"/v2/join_us.html#android开发工程师\" class=\"sidebar-link\">Android开发工程师</a></li><li class=\"sidebar-sub-header\"><a href=\"/v2/join_us.html#ios开发工程师\" class=\"sidebar-link\">iOS开发工程师</a></li><li class=\"sidebar-sub-header\"><a href=\"/v2/join_us.html#后端开发工程师\" class=\"sidebar-link\">后端开发工程师</a></li></ul></li><li><a href=\"/v2/join_us.html#返回书籍菜单列表\" class=\"sidebar-link\">返回书籍菜单列表</a><ul class=\"sidebar-sub-headers\"></ul></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"字节跳动-内推\"><a href=\"#字节跳动-内推\" class=\"header-anchor\">#</a> 字节跳动-内推</h1> <p>欢迎来字节跳动，和作者（wendux） 一起做同事！我们有大量HC，社招、校招、实习，前端、客户端、后端都有，欢迎对技术有热情的同学来投递！风里雨里，我在字节跳动等你~</p> <blockquote><p>投递方式：加微信（Demons-du），好友申请时请按 &quot;姓名+学校+职位+来自flutter社区&quot; 备注信息，微信请求通过后，字节跳动VIP通道就建立了（后续发简历、进度跟进、问题咨询都可以直接微信联系哦）。</p></blockquote> <h2 id=\"前端团队介绍\"><a href=\"#前端团队介绍\" class=\"header-anchor\">#</a> 前端团队介绍</h2> <p>我们是字节跳动-幸福里FE团队，诞生于2018年9月，从最初的4人组成长到今天的30多人，成员年龄跨度从90后到00后。技术栈覆盖当下前端主流全方向（Vue/React/Typescript/nodejs/webgl/flutter/Taro)，团队内大牛多多，技术氛围浓厚，有VR专家老吴、3D渲染一哥博哥、《Flutter实战》作者wendux、深谙 Web 框架及工程化的董老师、以及技能树满点的杰哥等等，还有，团队经常组织线下学习及娱乐活动，是一个开心且战斗力极强的team。只要你觉得自己够出色，或想让自己变得更出色，还等什么，放肆地加入我们吧。</p> <h3 id=\"业务线介绍\"><a href=\"#业务线介绍\" class=\"header-anchor\">#</a> 业务线介绍</h3> <p>幸福里是字节跳动旗下集内容、社区、工具于一体的房产信息、服务、交易平台。产品基于个性化推荐引擎向用户推荐优质的房产内容和全面、真实的房源信息，致力于为用户提供全面、专业、可靠的购房决策支持。</p> <p>幸福里始于2018年8月，是国内发展最快的，集内容、社区、工具于一体的房产信息与服务平台，业务覆盖一二线共23城，现累积注册用户千万，目前进入高速增长期。</p> <h3 id=\"团队福利\"><a href=\"#团队福利\" class=\"header-anchor\">#</a> 团队福利</h3> <p>五险一金、补充医疗保险、定期体检、年终奖、股票期权、带薪年假、员工旅游、交通补助、包吃、节日福利、住房补贴，不限量零食下午茶、弹性工作制</p> <h4 id=\"实习生\"><a href=\"#实习生\" class=\"header-anchor\">#</a> 实习生</h4> <ol><li>团队为每一位实习生提供专职mentor，手把手带入工作业务。</li> <li>团队为没有基础的实习生提供“筑基计划”的课程学习，轻松进阶前端技能。</li></ol> <h2 id=\"职位介绍\"><a href=\"#职位介绍\" class=\"header-anchor\">#</a> 职位介绍</h2> <h3 id=\"职位-前端开发工程师-校招-社招-急\"><a href=\"#职位-前端开发工程师-校招-社招-急\" class=\"header-anchor\">#</a> 职位—：前端开发工程师（校招/社招）急</h3> <p><strong>职位描述:</strong></p> <ol><li><p>负责移动端 /PC 端业务系统、小程序、跨端页面开发；</p></li> <li><p>负责推动与优化业务线中前端基础架构、组件抽象、技术调研；</p></li> <li><p>积极推动改进产品，包括技术、用户体验、产品等各个维度的改进。</p></li></ol> <p><strong>职位要求:</strong></p> <ol><li><p>本科及以上学历，计算机、通信等相关专业；</p></li> <li><p>熟练掌握 EcmaScript，CSS，HTML，DOM、绘图、动画、协议、安全、网络、性能优化等前端技术，对主流前端框架（ React \\ Vue 等）至少一种有深入应用并深入理解其设计原理；</p></li> <li><p>有安卓、iOS 开发经验或跨端技术flutter者优先；</p></li> <li><p>对用户体验、交互操作流程，及用户需求有一定了解；</p></li> <li><p>积极乐观，责任心强，工作认真细致，具备良好的服务意识，具有良好的团队沟通与协作能力；</p></li> <li><p>热爱前端技术，有较强的学习能力，有强烈的求知欲、好奇心和进取心 ，能及时关注和学习业界最新的前端技术。</p></li></ol> <h3 id=\"职位二-校招前端开发实习生\"><a href=\"#职位二-校招前端开发实习生\" class=\"header-anchor\">#</a> 职位二：校招前端开发实习生</h3> <p><strong>职位描述：</strong></p> <p>1、负责字节跳动-幸福里业务h5、小程序、中台系统的维护与开发；</p> <p>2、负责根据已有前端项目的基础架构进行合理的技术优化；</p> <p>3、积极推动改进产品，包括技术、用户体验、产品等各个维度。</p> <p><strong>职位要求：</strong></p> <p>1、计算机基础扎实：数据结构、算法、操作系统；</p> <p>2、熟悉掌握javascript、ES6 语言特性，熟练掌握css中常见布局方式，以及CSS3动画技术；</p> <p>3、熟练掌握VUE或React技术（非必须）；</p> <p>4、有ACM竞赛且获奖者优先；</p> <p>5、具较强的学习能力、主动、自驱、有责任心。</p> <p><strong>实习生培养计划</strong>：</p> <p>我们会为每一位实习生配备一名mentor进行日常决疑解惑和指导，同时我们针对不太熟悉前端的同学进行一个专门的【筑基培训】，旨在帮助快速补齐前端基础，以及明确后续学习和成长路线，有老司机带，不迷路！</p> <h3 id=\"职位三-web3d开发工程师-急\"><a href=\"#职位三-web3d开发工程师-急\" class=\"header-anchor\">#</a> 职位三：web3D开发工程师（急）</h3> <p><strong>职位描述：</strong></p> <ol><li>负责 VR 看房相关的业务开发，包括渲染SDK、标注平台等。</li> <li>负责 VR 数据平台相关开发：包括数据预处理</li></ol> <p><strong>职位要求：</strong></p> <ol><li><p>熟练掌握 JavaScript，WebGL；</p></li> <li><p>熟悉计算机图形学，渲染管线/线性代数；</p></li> <li><p>熟悉常用 Shader 原理及编写；</p></li> <li><p>熟悉至少一款 H5 渲染引擎，如ThreeJS，Babylon等；</p></li> <li><p>热爱钻研新技术，有强烈的好奇心和求知欲，有良好的编码规范；</p></li> <li><p>加分项：</p></li></ol> <ul><li>熟悉VR看房相关业务</li> <li>熟悉后端开发（Node）、对服务稳定性、并发了解同学优先（VR数据平台）。</li> <li>熟悉 ThreeJS，Babylon, Unity3D 有相关 3D 作品或 DEMO。</li> <li>各大前端技术社区活跃者、有自己的开源项目；</li></ul> <h2 id=\"其它职位-实习-校招-社招均可\"><a href=\"#其它职位-实习-校招-社招均可\" class=\"header-anchor\">#</a> 其它职位（实习/校招/社招均可）</h2> <h3 id=\"android开发工程师\"><a href=\"#android开发工程师\" class=\"header-anchor\">#</a> Android开发工程师</h3> <p>职位描述</p> <p>1、负责公司移动产品的研发, 编写高质量的代码；</p> <p>2、和产品经理配合, 深度参与手机产品需求讨论, 功能定义等；</p> <p>3、设计良好的代码结构, 不断迭代重构 。</p> <p>职位要求</p> <p>1、智能手机爱好者和使用者, 追求良好的用户体验；</p> <p>2、热爱移动产品研发, 愿意在移动开发领域深入钻研, 并成为专家；</p> <p>3、熟练掌握JAVA, 熟悉Android SDK；</p> <p>4、一年以上Android开发经验, 能独立开发Android App； 5、对软件产品有强烈的责任心, 具备良好的沟通能力和优秀的团队协作能力。</p> <p>5、有flutter开发经验者加分。</p> <h3 id=\"ios开发工程师\"><a href=\"#ios开发工程师\" class=\"header-anchor\">#</a> iOS开发工程师</h3> <p>职位描述</p> <p>1、负责公司移动产品的研发，编写高质量的代码；</p> <p>2、和产品经理配合，深度参与手机产品需求讨论，功能定义等；</p> <p>3、设计良好的代码结构，不断迭代重构 ；</p> <p>4、导并带领初级工程师共同完成研发任务。</p> <p>职位要求</p> <p>1、有强烈的求知欲和进取心；</p> <p>2、具有扎实的编程工底，良好的设计能力和编程习惯；</p> <p>3、至少精通一门编程语言 ，熟练掌握Objective-C，熟悉Swift的优先 ；</p> <p>4、一年以上iOS开发经验，能独立开发iPhoneApp者先。</p> <p>5、有flutter开发经验者加分。</p> <h3 id=\"后端开发工程师\"><a href=\"#后端开发工程师\" class=\"header-anchor\">#</a> 后端开发工程师</h3> <p><strong>职位描述</strong></p> <p>1、主导或参与系统设计、研发、部署等相关工作；</p> <p>2、研发基础服务组件，解决共性需求，减少重复开发与运维；</p> <p>3、有较强的系统问题分析经验和能力，能够解决复杂的系统问题；</p> <p>4、参与部分生产系统维护工作，解决生产系统问题及进行系统调优。</p> <p><strong>职位要求</strong></p> <p>1、本科及以上学历，计算机相关专业；</p> <p>2、热爱计算机科学和互联网技术，精通至少一门编程语言，包括但不仅限于：Java、C、C++、PHP、 Python、Go；</p> <p>3、掌握扎实的计算机基础知识，深入理解数据结构、算法和操作系统知识；</p> <p>4、有优秀的逻辑分析能力，能够对业务逻辑进行合理的抽象和拆分；</p> <p>5、有强烈的求知欲，优秀的学习和沟通能力。</p> <h2 id=\"返回书籍菜单列表\"><a href=\"#返回书籍菜单列表\" class=\"header-anchor\">#</a> <a href=\"/index\">返回书籍菜单列表</a></h2></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/225.8687c2ec.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/next.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>下一步 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/226.d982aa8d.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"下一步\"><a href=\"#下一步\" class=\"header-anchor\">#</a> 下一步</h1> <h3 id=\"其它平台\"><a href=\"#其它平台\" class=\"header-anchor\">#</a> 其它平台</h3> <p>本书主要讲的是Flutter在移动端开发</p> <ul><li></li></ul></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/226.d982aa8d.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/preface.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>前言 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/227.c297d2cb.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"前言\"><a href=\"#前言\" class=\"header-anchor\">#</a> 前言</h1> <h3 id=\"缘起\"><a href=\"#缘起\" class=\"header-anchor\">#</a> 缘起</h3> <p>在全球，随着Flutter被越来越多的知名公司应用在自己的商业APP中，Flutter这门新技术也逐渐进入了移动开发者的视野，尤其是当Google在2018年IO大会上发布了第一个Preview版本后，国内刮起来一股学习Flutter的热潮。</p> <p>在Flutter发布之初，当时，我在看完Flutter原理介绍后，就对它产生了浓厚的兴趣。当时笔者身边也一些人比较关注Flutter，我也被经常问到关于Flutter的一些问题，比如Flutter怎么样？和RN有什么区别？Flutter为什么要用Dart？当时也听到了一些批评的声音，比如有些人说Flutter只是重复造轮子，没啥亮点、Flutter最大的缺点就是使用了Dart语言等。在听到这些问题及论调后，我深知这是对Flutter的不了解而造成的，这和当时国内缺乏Flutter中文文档和教程有直接关系，很多人对Flutter的了解都只停留在Google的发布会介绍（有中文翻译）。</p> <p>在笔者深入的了解Flutter后，深知Flutter必将是一个会改变移动开发格局的里程碑级作品，它从设计之初就对性能和开发效率兼顾，并且借鉴了React（一个Web开发框架）的响应式的UI框架设计思想等，总之，很难用一两句话说完Flutter的优点，同时我也很快成为了Flutter的路转粉。</p> <p>为了更好的帮助中国开发者了解这门新技术，我在2018年初开始翻译Flutter官网文档，同年4月份上线了<a href=\"https://flutterchina.club/\" target=\"_blank\" rel=\"noopener noreferrer\">Flutter中文网<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>，上线后反响很强烈，Flutter中文网也很快被传播开，百度搜索排名迅速蹿升到前三，截止目前，Flutter中文官网日PV在7万左右，每日独立访问人数近一万多。</p> <p>虽然Flutter中文网给中国开发者提供了很好的第一手了解Flutter的资料，但是笔者还会经常遇到一些对Flutter技术处于围观而不愿尝试的开发者。这主要是因为当时Flutter在国内没有成功案例，再加上新技术都有学习成本，所以即使有文档，也会有一些开发者犹豫是否来学习。为了解决这部分开发者的疑虑，我就想如果能用Flutter开发一个完整的APP发布到应用商店，这样开发者就可以在犹豫的时候可以先实际感受一下Flutter应用，这样有个直观的了解后，就会容易做出判断，为此，我开发了<a href=\"https://flutterchina.club/app/gm.html\" target=\"_blank\" rel=\"noopener noreferrer\">Gitme<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>，它是一个Github客户端，它支持了源码浏览、Issue、Label等Github的大多数功能，到目前为止，通过Gitme登录过Github账号的用户有8000多人，日活用户有1000人。更重要的是，有很多人正是用了Gitme后，才来学Flutter的。</p> <p>无论是做Flutter中文网，还是写Gitme，主要目标都是帮助开发者学习Flutter，同时消除围观开发者疑虑。但当开发者们真正开始动手时，Flutter的生态问题就变得尤为突出。由于在2018年初Flutter刚起步时，很多基础的包和库都是空白，少数已有的一些库也大都是预览版（未到1.0），存在很多bug。这个状况不是一两个人花一两天能搞定的，这是需要聚整个Flutter开发者社群之力，耗费数年时间才可能有所改善。因此，在2018年4月份，我以Flutter中文网名义发起了Flutter开源计划，该计划主要是开发一些常用的包来丰富Flutter生态，帮助开发者提高开发效率。自在github建立<a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\">Flutter中文开发者社区官方账号<span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a>以来，前后开源了dio、cookieJar、flukit等多个项目，而dio在开源两周后，就迅速成为Flutter第三方包中Star排名第一的开源库。</p> <p>虽然做的事情已经够多了，但是仍有一些很有必要去做的事情，由于时间原因，一直被搁置。</p> <p>随着学习Flutter的人越来越多，一部分开发者通过查看官网的文档就能入门，但也有很多开发者感觉学习时仍然有些吃力，主要原因有两个，首先官网的文档主要是为了引导开发者快速上手的，讲的并不是很细；其次是我们虽然翻译了官方文档，但是对于Flutter SDK文档并没有翻译，而在开发中遇到的一些具体问题通常都得去查看SDK文档。所以，要解决这两个问题，必须得有一个系统化的Flutter教程，它不仅可以快速引导开发者入门，而且也能触及到一些细节和原理，最好也能提供一些学习和研究Flutter的方法。因此，如果能有一本能系统地介绍Flutter的书籍便是便是非常棒的！但是，当时没有一本关于Flutter的中文书籍，因此，我便计划写一本能由浅入深、系统介绍Flutter的书。2018年12月，《Flutter实战》完成初稿，并在Github上开源，同时上线了《Flutter实战》电子书官网（https://book.flutterchina.club/） ，至今每天有3000多人在线浏览本书。那为什么不直接出版？如果直接出版，不仅有稿费，而且也能保护知识产权，而直接开源，不就就只能当雷锋了？其实，无论是做中文网、写Gitme、做Flutter开源项目，我的出发点都是为了能帮助中国开发者了解、学习Flutter，而这是一件非常有意义的事，只要坚持做对别人有价值的事，那么上帝迟早会奖赏你；当然我们也在网站中留了打赏的按钮，如果读者觉得有帮助，可以扫码打赏，请笔者喝一杯咖啡。另外由于成书比较仓促，当时书中也有很多错误，开源后，有很多读者提PR来纠正书中的错别字，时至今日，有78名开发者为本书提过PR，我在此，衷心的感谢你们。</p> <p>起初，我没有出版实体书的打算，当时我以为开发者直接通过在线访问本书官网即方便又不用付费，何乐而不为。但在本书上线后，很多读者来添加微信好友，表示非常有收益，很期待出版纸质书，甚至有比较热心的读者想提前付定金预定！我理解，这是大家对我所做之事的认可和鼓励。考虑到，确实有一部分读者，尤其是还没毕业的同学，可能更喜欢通过书籍去学习，为此，我已经和机械工业出版社合作，目前本书纸质版正在出版中，敬请关注。</p> <h3 id=\"本书组织结构\"><a href=\"#本书组织结构\" class=\"header-anchor\">#</a> 本书组织结构</h3> <p>本书采用由浅入深的方式介绍Flutter技术原理，分为三篇，总计15章，各篇的主要内容如下。</p> <ul><li>第一篇，入门篇（第1章~第7章），包括Flutter技术的出现背景和简介、Flutter的各种类型的Widget以及如何构建UI。通过学习本章，读者可以掌握如何使用Flutter来构建UI界面。</li> <li>第二篇，进阶篇（第8章~第14章），包括Flutter中的事件机制、动画、自定义组件、文件和网络、插件、国际化以及Flutter核心原理等。通过本章内容，读者可以对Flutter整体构建及原理有一个深入的认识。</li> <li>第三篇，实例篇（第15张），本章主要通过一个简版的Github APP来将前面介绍的内容串起来，让开发者对一个完整的Flutter APP开发流有个了解。</li></ul> <p>由于Flutter的很多知识点是相互交织的，很难将它们彻底划分开，所以本书中也难免会出现一些在前面章节会使用在后面章节的场景，比如我们在入门篇介绍进度指示器时会用到在进阶篇中才介绍的动画相关知识。本书中对于这种情况会在相应的章节进行说明。读者可以直接跳到后面相应知识点章节阅读后再返回，也可以先有个印象，待学习到后面相关章节后再回头来看。</p> <h3 id=\"本书特色\"><a href=\"#本书特色\" class=\"header-anchor\">#</a> 本书特色</h3> <p>笔者在大学时读过候捷（真名侯俊杰）写的一些C++相关书籍，在他的《深入浅出MFC》一书中，有一句话我印象非常深 “唯有深入，方能浅出”。我非常认同这句话，对于一门技术，只有了解的深入，才能用最浅显、通俗的话语描述出。我在写作本书时，深入浅出就是一个主要目标。所以，本书的目标不仅是想告诉读者如何使用Flutter，而且也非常关注各个知识点的底层实现以及设计思想。从本书章节划分上来看，入门篇为“浅出”，进阶篇则是“深入”。另外由于PC客户端开发、移动开发、Web开发这些经验我都有，而Flutter本质上是一个UI系统，而UI系统的设计和实现在”大前端“下有很多相通之处，所以在本书中的一些知识点我也会对比一些其他UI系统（主要是Android或Web）相应的实现，便于有相关开发经验的读者对比理解。</p> <h3 id=\"本书读者对象\"><a href=\"#本书读者对象\" class=\"header-anchor\">#</a> 本书读者对象</h3> <ul><li>读者至少熟悉一种编程语言。</li> <li>读者最好接触过PC客户端、移动开发或Web前端开发中的一种。</li> <li>本书不适合做为编程的入门读物。</li></ul> <h3 id=\"关于随书源码\"><a href=\"#关于随书源码\" class=\"header-anchor\">#</a> 关于随书源码</h3> <p>由于篇幅所限，本书中大多数示例代码都只是部分核心代码，读者可以去https://github.com/wendux/flutter_in_action_source_code 查看</p> <h3 id=\"勘误\"><a href=\"#勘误\" class=\"header-anchor\">#</a> 勘误</h3> <p>由于Flutter SDK在不断更新，本书中的部分内容（如类的继承关系、参数等）可能会和新版本的Flutter不一致，读者以最新的Flutter SDK为准。另外，由于时间仓促，书中难免有错误之处，如果发现错误，可以在本书Github项目issue列表中去反馈，地址是https://github.com/flutterchina/flutter-in-action/issues 。</p> <h3 id=\"致谢\"><a href=\"#致谢\" class=\"header-anchor\">#</a> 致谢</h3> <p>感谢一直以来支持Flutter中文网、Flutter开源项目的人以及所有对本书提过PR的人，正是因为有你们，才有这本书。另外尤其感谢给本书打赏过的同学，你们的支持给了我很大的鼓励。</p></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/227.c297d2cb.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/reference.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>参考文献 | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/228.4dcdd69c.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/229.91061a53.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"参考文献\"><a href=\"#参考文献\" class=\"header-anchor\">#</a> 参考文献</h1> <ul><li><p>React Native官网：https://facebook.github.io/react-native/</p></li> <li><p>Weex：https://weex.apache.org/zh/guide/introduction.html</p></li> <li><p>快应用：https://www.quickapp.cn/</p></li> <li><p>QT for mobile：https://www.qt.io/mobile-app-development/</p></li> <li><p>Flutter官网：https://flutter.dev/</p></li> <li><p>Flutter中文网社区：https://flutterchina.club/docs/</p></li> <li><p>Dart Packages官网：https://pub.dev/</p></li> <li><p>Flutter中文开发者社区开源项目：https://github.com/flutterchina</p></li> <li><p>Material Design：https://material.io/</p></li> <li><p>Github 开发者中心官网：https://developer.github.com/v3/</p></li> <li><p>Android开发者中心官网：https://developer.android.google.cn/</p></li> <li><p>Apple开发者中心官网：https://developer.apple.com/</p></li></ul></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/228.4dcdd69c.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/v2/summary.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <title>Summary | 《Flutter实战》电子书</title>\n    <meta name=\"generator\" content=\"VuePress 1.8.2\">\n    <link rel=\"icon\" href=\"/logo.png\">\n    <meta name=\"description\" content=\"\">\n    \n    <link rel=\"preload\" href=\"/assets/css/0.styles.9ca8d33d.css\" as=\"style\"><link rel=\"preload\" href=\"/assets/js/app.189f52e4.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/39.c3d0c942.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/2.f4fe4405.js\" as=\"script\"><link rel=\"preload\" href=\"/assets/js/229.91061a53.js\" as=\"script\"><link rel=\"prefetch\" href=\"/assets/js/10.5f481ee1.js\"><link rel=\"prefetch\" href=\"/assets/js/100.ce32ed9e.js\"><link rel=\"prefetch\" href=\"/assets/js/101.60ad385d.js\"><link rel=\"prefetch\" href=\"/assets/js/102.53edb23b.js\"><link rel=\"prefetch\" href=\"/assets/js/103.21193c45.js\"><link rel=\"prefetch\" href=\"/assets/js/104.02d5761d.js\"><link rel=\"prefetch\" href=\"/assets/js/105.b6044eeb.js\"><link rel=\"prefetch\" href=\"/assets/js/106.9b857fc8.js\"><link rel=\"prefetch\" href=\"/assets/js/107.188f42e4.js\"><link rel=\"prefetch\" href=\"/assets/js/108.47fd6022.js\"><link rel=\"prefetch\" href=\"/assets/js/109.9119c4a8.js\"><link rel=\"prefetch\" href=\"/assets/js/11.2026a71f.js\"><link rel=\"prefetch\" href=\"/assets/js/110.fec0c84f.js\"><link rel=\"prefetch\" href=\"/assets/js/111.95623244.js\"><link rel=\"prefetch\" href=\"/assets/js/112.e8e91632.js\"><link rel=\"prefetch\" href=\"/assets/js/113.d0f44add.js\"><link rel=\"prefetch\" href=\"/assets/js/114.df1f86a1.js\"><link rel=\"prefetch\" href=\"/assets/js/115.374ee72b.js\"><link rel=\"prefetch\" href=\"/assets/js/116.5453b788.js\"><link rel=\"prefetch\" href=\"/assets/js/117.f7002db2.js\"><link rel=\"prefetch\" href=\"/assets/js/118.d4908451.js\"><link rel=\"prefetch\" href=\"/assets/js/119.baead276.js\"><link rel=\"prefetch\" href=\"/assets/js/12.ef586fcc.js\"><link rel=\"prefetch\" href=\"/assets/js/120.681a706e.js\"><link rel=\"prefetch\" href=\"/assets/js/121.aef1325d.js\"><link rel=\"prefetch\" href=\"/assets/js/122.59b7f284.js\"><link rel=\"prefetch\" href=\"/assets/js/123.b83bac47.js\"><link rel=\"prefetch\" href=\"/assets/js/124.7a950a7c.js\"><link rel=\"prefetch\" href=\"/assets/js/125.275c37b1.js\"><link rel=\"prefetch\" href=\"/assets/js/126.a62ac904.js\"><link rel=\"prefetch\" href=\"/assets/js/127.76aaf7da.js\"><link rel=\"prefetch\" href=\"/assets/js/128.c75c7015.js\"><link rel=\"prefetch\" href=\"/assets/js/129.88b4391d.js\"><link rel=\"prefetch\" href=\"/assets/js/13.e93e35ca.js\"><link rel=\"prefetch\" href=\"/assets/js/130.f1922c51.js\"><link rel=\"prefetch\" href=\"/assets/js/131.7f51717c.js\"><link rel=\"prefetch\" href=\"/assets/js/132.4b82358d.js\"><link rel=\"prefetch\" href=\"/assets/js/133.85003caa.js\"><link rel=\"prefetch\" href=\"/assets/js/134.68a25fe9.js\"><link rel=\"prefetch\" href=\"/assets/js/135.765bad57.js\"><link rel=\"prefetch\" href=\"/assets/js/136.46f11ea2.js\"><link rel=\"prefetch\" href=\"/assets/js/137.0345664d.js\"><link rel=\"prefetch\" href=\"/assets/js/138.1a184d5a.js\"><link rel=\"prefetch\" href=\"/assets/js/139.94803f35.js\"><link rel=\"prefetch\" href=\"/assets/js/14.4d0ac363.js\"><link rel=\"prefetch\" href=\"/assets/js/140.c76b4d30.js\"><link rel=\"prefetch\" href=\"/assets/js/141.592ee9d6.js\"><link rel=\"prefetch\" href=\"/assets/js/142.d7d8dbaf.js\"><link rel=\"prefetch\" href=\"/assets/js/143.77583b74.js\"><link rel=\"prefetch\" href=\"/assets/js/144.2ce6c614.js\"><link rel=\"prefetch\" href=\"/assets/js/145.8fe852a4.js\"><link rel=\"prefetch\" href=\"/assets/js/146.86bb1e0f.js\"><link rel=\"prefetch\" href=\"/assets/js/147.e9009fb7.js\"><link rel=\"prefetch\" href=\"/assets/js/148.0c341180.js\"><link rel=\"prefetch\" href=\"/assets/js/149.46210223.js\"><link rel=\"prefetch\" href=\"/assets/js/15.7f957f4b.js\"><link rel=\"prefetch\" href=\"/assets/js/150.220fd8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/151.a1cacc6d.js\"><link rel=\"prefetch\" href=\"/assets/js/152.5323fd98.js\"><link rel=\"prefetch\" href=\"/assets/js/153.26e3ed31.js\"><link rel=\"prefetch\" href=\"/assets/js/154.40b97c4a.js\"><link rel=\"prefetch\" href=\"/assets/js/155.b55f4193.js\"><link rel=\"prefetch\" href=\"/assets/js/156.f4128841.js\"><link rel=\"prefetch\" href=\"/assets/js/157.fb299d51.js\"><link rel=\"prefetch\" href=\"/assets/js/158.49064f28.js\"><link rel=\"prefetch\" href=\"/assets/js/159.277037a1.js\"><link rel=\"prefetch\" href=\"/assets/js/16.2d42d5bc.js\"><link rel=\"prefetch\" href=\"/assets/js/160.0664832e.js\"><link rel=\"prefetch\" href=\"/assets/js/161.38ea1a1a.js\"><link rel=\"prefetch\" href=\"/assets/js/162.f96e9c80.js\"><link rel=\"prefetch\" href=\"/assets/js/163.cfb15292.js\"><link rel=\"prefetch\" href=\"/assets/js/164.46dd9498.js\"><link rel=\"prefetch\" href=\"/assets/js/165.86c8b8c1.js\"><link rel=\"prefetch\" href=\"/assets/js/166.3938fc5f.js\"><link rel=\"prefetch\" href=\"/assets/js/167.09cbb683.js\"><link rel=\"prefetch\" href=\"/assets/js/168.aab68e4f.js\"><link rel=\"prefetch\" href=\"/assets/js/169.b01d210b.js\"><link rel=\"prefetch\" href=\"/assets/js/17.59a459c4.js\"><link rel=\"prefetch\" href=\"/assets/js/170.e68bf3e7.js\"><link rel=\"prefetch\" href=\"/assets/js/171.4f0e7d6b.js\"><link rel=\"prefetch\" href=\"/assets/js/172.ae542eee.js\"><link rel=\"prefetch\" href=\"/assets/js/173.c985ca5c.js\"><link rel=\"prefetch\" href=\"/assets/js/174.ece20a6a.js\"><link rel=\"prefetch\" href=\"/assets/js/175.891c0a2f.js\"><link rel=\"prefetch\" href=\"/assets/js/176.93a74e23.js\"><link rel=\"prefetch\" href=\"/assets/js/177.20c492a7.js\"><link rel=\"prefetch\" href=\"/assets/js/178.a9c56f1f.js\"><link rel=\"prefetch\" href=\"/assets/js/179.b42bb139.js\"><link rel=\"prefetch\" href=\"/assets/js/18.318a79e1.js\"><link rel=\"prefetch\" href=\"/assets/js/180.d048122a.js\"><link rel=\"prefetch\" href=\"/assets/js/181.e3633acb.js\"><link rel=\"prefetch\" href=\"/assets/js/182.d30b9f69.js\"><link rel=\"prefetch\" href=\"/assets/js/183.802b20e7.js\"><link rel=\"prefetch\" href=\"/assets/js/184.90e5f242.js\"><link rel=\"prefetch\" href=\"/assets/js/185.5cca4d69.js\"><link rel=\"prefetch\" href=\"/assets/js/186.56dca3cc.js\"><link rel=\"prefetch\" href=\"/assets/js/187.405078e0.js\"><link rel=\"prefetch\" href=\"/assets/js/188.766e2a5e.js\"><link rel=\"prefetch\" href=\"/assets/js/189.c927c79a.js\"><link rel=\"prefetch\" href=\"/assets/js/19.175563ad.js\"><link rel=\"prefetch\" href=\"/assets/js/190.92378aeb.js\"><link rel=\"prefetch\" href=\"/assets/js/191.167ff3f3.js\"><link rel=\"prefetch\" href=\"/assets/js/192.53c8ab5d.js\"><link rel=\"prefetch\" href=\"/assets/js/193.05fa90e3.js\"><link rel=\"prefetch\" href=\"/assets/js/194.df254afb.js\"><link rel=\"prefetch\" href=\"/assets/js/195.1c40c74b.js\"><link rel=\"prefetch\" href=\"/assets/js/196.3dbd36a7.js\"><link rel=\"prefetch\" href=\"/assets/js/197.7fd8856f.js\"><link rel=\"prefetch\" href=\"/assets/js/198.2a531f59.js\"><link rel=\"prefetch\" href=\"/assets/js/199.7dc1f153.js\"><link rel=\"prefetch\" href=\"/assets/js/20.6c5c8986.js\"><link rel=\"prefetch\" href=\"/assets/js/200.92165c5b.js\"><link rel=\"prefetch\" href=\"/assets/js/201.2953b2e7.js\"><link rel=\"prefetch\" href=\"/assets/js/202.de6ee6b8.js\"><link rel=\"prefetch\" href=\"/assets/js/203.ac627436.js\"><link rel=\"prefetch\" href=\"/assets/js/204.fb9289cb.js\"><link rel=\"prefetch\" href=\"/assets/js/205.90d0284c.js\"><link rel=\"prefetch\" href=\"/assets/js/206.2dbac1ab.js\"><link rel=\"prefetch\" href=\"/assets/js/207.d052f90b.js\"><link rel=\"prefetch\" href=\"/assets/js/208.0818aeb7.js\"><link rel=\"prefetch\" href=\"/assets/js/209.95753ceb.js\"><link rel=\"prefetch\" href=\"/assets/js/21.c0363ec5.js\"><link rel=\"prefetch\" href=\"/assets/js/210.0c9a3892.js\"><link rel=\"prefetch\" href=\"/assets/js/211.43a19344.js\"><link rel=\"prefetch\" href=\"/assets/js/212.f9dee082.js\"><link rel=\"prefetch\" href=\"/assets/js/213.f55d441e.js\"><link rel=\"prefetch\" href=\"/assets/js/214.0836f39e.js\"><link rel=\"prefetch\" href=\"/assets/js/215.807d3ade.js\"><link rel=\"prefetch\" href=\"/assets/js/216.a7c3d830.js\"><link rel=\"prefetch\" href=\"/assets/js/217.ad95582b.js\"><link rel=\"prefetch\" href=\"/assets/js/218.059e8703.js\"><link rel=\"prefetch\" href=\"/assets/js/219.70d1d263.js\"><link rel=\"prefetch\" href=\"/assets/js/22.4fdaa56f.js\"><link rel=\"prefetch\" href=\"/assets/js/220.69e2bb54.js\"><link rel=\"prefetch\" href=\"/assets/js/221.51aa50d8.js\"><link rel=\"prefetch\" href=\"/assets/js/222.8f955066.js\"><link rel=\"prefetch\" href=\"/assets/js/223.3230e791.js\"><link rel=\"prefetch\" href=\"/assets/js/224.35c9623f.js\"><link rel=\"prefetch\" href=\"/assets/js/225.8687c2ec.js\"><link rel=\"prefetch\" href=\"/assets/js/226.d982aa8d.js\"><link rel=\"prefetch\" href=\"/assets/js/227.c297d2cb.js\"><link rel=\"prefetch\" href=\"/assets/js/228.4dcdd69c.js\"><link rel=\"prefetch\" href=\"/assets/js/23.87c6db58.js\"><link rel=\"prefetch\" href=\"/assets/js/24.bf1834df.js\"><link rel=\"prefetch\" href=\"/assets/js/25.869317d7.js\"><link rel=\"prefetch\" href=\"/assets/js/26.e656381b.js\"><link rel=\"prefetch\" href=\"/assets/js/27.d1fa65c2.js\"><link rel=\"prefetch\" href=\"/assets/js/28.fbba6f6f.js\"><link rel=\"prefetch\" href=\"/assets/js/29.e809e77a.js\"><link rel=\"prefetch\" href=\"/assets/js/3.cc1736a7.js\"><link rel=\"prefetch\" href=\"/assets/js/30.b84a8f21.js\"><link rel=\"prefetch\" href=\"/assets/js/31.dca209c1.js\"><link rel=\"prefetch\" href=\"/assets/js/32.c0efa5ac.js\"><link rel=\"prefetch\" href=\"/assets/js/33.8b948e2d.js\"><link rel=\"prefetch\" href=\"/assets/js/34.9dff7b9d.js\"><link rel=\"prefetch\" href=\"/assets/js/35.a9c7aa47.js\"><link rel=\"prefetch\" href=\"/assets/js/36.d9eeb78f.js\"><link rel=\"prefetch\" href=\"/assets/js/37.7ecb6ef5.js\"><link rel=\"prefetch\" href=\"/assets/js/38.260a994c.js\"><link rel=\"prefetch\" href=\"/assets/js/4.2eb3fd02.js\"><link rel=\"prefetch\" href=\"/assets/js/40.31216541.js\"><link rel=\"prefetch\" href=\"/assets/js/41.7b70eaf0.js\"><link rel=\"prefetch\" href=\"/assets/js/42.057d8df6.js\"><link rel=\"prefetch\" href=\"/assets/js/43.b17a6aae.js\"><link rel=\"prefetch\" href=\"/assets/js/44.5f61dab7.js\"><link rel=\"prefetch\" href=\"/assets/js/45.381b04c9.js\"><link rel=\"prefetch\" href=\"/assets/js/46.1c911d7c.js\"><link rel=\"prefetch\" href=\"/assets/js/47.692e2e27.js\"><link rel=\"prefetch\" href=\"/assets/js/48.6747a87a.js\"><link rel=\"prefetch\" href=\"/assets/js/49.e636869b.js\"><link rel=\"prefetch\" href=\"/assets/js/5.0941abdb.js\"><link rel=\"prefetch\" href=\"/assets/js/50.57b758f4.js\"><link rel=\"prefetch\" href=\"/assets/js/51.4c0d2270.js\"><link rel=\"prefetch\" href=\"/assets/js/52.61d5b4f1.js\"><link rel=\"prefetch\" href=\"/assets/js/53.56438d06.js\"><link rel=\"prefetch\" href=\"/assets/js/54.4afb8008.js\"><link rel=\"prefetch\" href=\"/assets/js/55.a7c27448.js\"><link rel=\"prefetch\" href=\"/assets/js/56.8a9849da.js\"><link rel=\"prefetch\" href=\"/assets/js/57.371d9d12.js\"><link rel=\"prefetch\" href=\"/assets/js/58.3d2501b6.js\"><link rel=\"prefetch\" href=\"/assets/js/59.ab37b38d.js\"><link rel=\"prefetch\" href=\"/assets/js/6.15c5e328.js\"><link rel=\"prefetch\" href=\"/assets/js/60.7043cbf5.js\"><link rel=\"prefetch\" href=\"/assets/js/61.470fa853.js\"><link rel=\"prefetch\" href=\"/assets/js/62.b51637d5.js\"><link rel=\"prefetch\" href=\"/assets/js/63.ff70c9ee.js\"><link rel=\"prefetch\" href=\"/assets/js/64.4621481d.js\"><link rel=\"prefetch\" href=\"/assets/js/65.2d58a524.js\"><link rel=\"prefetch\" href=\"/assets/js/66.5df03e62.js\"><link rel=\"prefetch\" href=\"/assets/js/67.73c671d9.js\"><link rel=\"prefetch\" href=\"/assets/js/68.4de1c271.js\"><link rel=\"prefetch\" href=\"/assets/js/69.46ed47d5.js\"><link rel=\"prefetch\" href=\"/assets/js/7.9aacc405.js\"><link rel=\"prefetch\" href=\"/assets/js/70.a85fca0c.js\"><link rel=\"prefetch\" href=\"/assets/js/71.f6151eb7.js\"><link rel=\"prefetch\" href=\"/assets/js/72.2c5ab8cf.js\"><link rel=\"prefetch\" href=\"/assets/js/73.dcf07497.js\"><link rel=\"prefetch\" href=\"/assets/js/74.3253492f.js\"><link rel=\"prefetch\" href=\"/assets/js/75.bef82a69.js\"><link rel=\"prefetch\" href=\"/assets/js/76.29cac87f.js\"><link rel=\"prefetch\" href=\"/assets/js/77.8fef1f03.js\"><link rel=\"prefetch\" href=\"/assets/js/78.28647d83.js\"><link rel=\"prefetch\" href=\"/assets/js/79.1a0eb05b.js\"><link rel=\"prefetch\" href=\"/assets/js/8.cb5899ca.js\"><link rel=\"prefetch\" href=\"/assets/js/80.8eacbc37.js\"><link rel=\"prefetch\" href=\"/assets/js/81.f8e7c85e.js\"><link rel=\"prefetch\" href=\"/assets/js/82.a6b89300.js\"><link rel=\"prefetch\" href=\"/assets/js/83.daf1b549.js\"><link rel=\"prefetch\" href=\"/assets/js/84.f330e398.js\"><link rel=\"prefetch\" href=\"/assets/js/85.f647b247.js\"><link rel=\"prefetch\" href=\"/assets/js/86.e82005d4.js\"><link rel=\"prefetch\" href=\"/assets/js/87.e1f95a69.js\"><link rel=\"prefetch\" href=\"/assets/js/88.9709422a.js\"><link rel=\"prefetch\" href=\"/assets/js/89.4ff5c882.js\"><link rel=\"prefetch\" href=\"/assets/js/9.f995bbe8.js\"><link rel=\"prefetch\" href=\"/assets/js/90.fff47bcf.js\"><link rel=\"prefetch\" href=\"/assets/js/91.9eb501f7.js\"><link rel=\"prefetch\" href=\"/assets/js/92.7b12896a.js\"><link rel=\"prefetch\" href=\"/assets/js/93.54d0e6b6.js\"><link rel=\"prefetch\" href=\"/assets/js/94.c6e7a2f7.js\"><link rel=\"prefetch\" href=\"/assets/js/95.bf70fc45.js\"><link rel=\"prefetch\" href=\"/assets/js/96.b382caf8.js\"><link rel=\"prefetch\" href=\"/assets/js/97.14909b27.js\"><link rel=\"prefetch\" href=\"/assets/js/98.8023c3ac.js\"><link rel=\"prefetch\" href=\"/assets/js/99.3db30893.js\">\n    <link rel=\"stylesheet\" href=\"/assets/css/0.styles.9ca8d33d.css\">\n  </head>\n  <body>\n    <div id=\"app\" data-server-rendered=\"true\"><div class=\"theme-container\"><header class=\"navbar\"><div class=\"sidebar-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" role=\"img\" viewBox=\"0 0 448 512\" class=\"icon\"><path fill=\"currentColor\" d=\"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z\"></path></svg></div> <a href=\"/\" class=\"home-link router-link-active\"><img src=\"/logo.png\" alt=\"《Flutter实战》电子书\" class=\"logo\"> <span class=\"site-name can-hide\">《Flutter实战》电子书</span></a> <div class=\"links\"><div class=\"search-box\"><input aria-label=\"Search\" autocomplete=\"off\" spellcheck=\"false\" value=\"\"> <!----></div> <nav class=\"nav-links can-hide\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class=\"sidebar-mask\"></div> <aside class=\"sidebar\"><nav class=\"nav-links\"><div class=\"nav-item\"><a href=\"/join_us.html\" class=\"nav-link\">\n  和作者做同事\n</a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Flutter中国\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://github.com/flutterchina/flutter-in-action\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  Github\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div><div class=\"nav-item\"><a href=\"https://item.jd.com/12816296.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"nav-link external\">\n  实体书\n  <span><svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" class=\"icon outbound\"><path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\"></path> <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\"></polygon></svg> <span class=\"sr-only\">(opens new window)</span></span></a></div> <!----></nav>  <ul class=\"sidebar-links\"><li><a href=\"/\" aria-current=\"page\" class=\"sidebar-link\">首页</a></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter1/index\" class=\"sidebar-heading clickable\"><span>第一章：起步</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter1/mobile_development_intro.html\" class=\"sidebar-link\">1.1 移动开发技术简介</a></li><li><a href=\"/chapter1/flutter_intro.html\" class=\"sidebar-link\">1.2 初识Flutter</a></li><li><a href=\"/chapter1/install_flutter.html\" class=\"sidebar-link\">1.3 搭建Flutter开发环境</a></li><li><a href=\"/chapter1/dart.html\" class=\"sidebar-link\">1.4 Dart语言简介</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter2/index\" class=\"sidebar-heading clickable\"><span>第二章：第一个Flutter应用</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter2/first_flutter_app.html\" class=\"sidebar-link\">2.1 计数器应用示例</a></li><li><a href=\"/chapter2/flutter_router.html\" class=\"sidebar-link\">2.2 路由管理</a></li><li><a href=\"/chapter2/flutter_package_mgr.html\" class=\"sidebar-link\">2.3 包管理</a></li><li><a href=\"/chapter2/flutter_assets_mgr.html\" class=\"sidebar-link\">2.4 资源管理</a></li><li><a href=\"/chapter2/flutter_app_debug.html\" class=\"sidebar-link\">2.5 调试Flutter应用</a></li><li><a href=\"/chapter2/thread_model_and_error_report.html\" class=\"sidebar-link\">2.6 Flutter异常捕获</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter3/index\" class=\"sidebar-heading clickable\"><span>第三章：基础组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter3/flutter_widget_intro.html\" class=\"sidebar-link\">3.1 Widget简介</a></li><li><a href=\"/chapter3/state_manage.html\" class=\"sidebar-link\">3.2 状态管理</a></li><li><a href=\"/chapter3/text.html\" class=\"sidebar-link\">3.3 文本及样式</a></li><li><a href=\"/chapter3/buttons.html\" class=\"sidebar-link\">3.4 按钮</a></li><li><a href=\"/chapter3/img_and_icon.html\" class=\"sidebar-link\">3.5 图片及ICON</a></li><li><a href=\"/chapter3/radio_and_checkbox.html\" class=\"sidebar-link\">3.6 单选开关和复选框</a></li><li><a href=\"/chapter3/input_and_form.html\" class=\"sidebar-link\">3.7 输入框及表单</a></li><li><a href=\"/chapter3/progress.html\" class=\"sidebar-link\">3.8 进度指示器</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/v2/chapter4/index.md\" class=\"sidebar-heading clickable\"><span>第四章：布局类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter4/intro.html\" class=\"sidebar-link\">4.1 布局类组件简介</a></li><li><a href=\"/chapter4/row_and_column.html\" class=\"sidebar-link\">4.2 线性布局（Row和Column）</a></li><li><a href=\"/chapter4/flex.html\" class=\"sidebar-link\">4.3 弹性布局（Flex）</a></li><li><a href=\"/chapter4/wrap_and_flow.html\" class=\"sidebar-link\">4.4 流式布局</a></li><li><a href=\"/chapter4/stack.html\" class=\"sidebar-link\">4.5 层叠布局 Stack、Positioned</a></li><li><a href=\"/chapter4/alignment.html\" class=\"sidebar-link\">4.6 对齐与相对定位（Align）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter5/index\" class=\"sidebar-heading clickable\"><span>第五章：容器类组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter5/padding.html\" class=\"sidebar-link\">5.1 填充（Padding）</a></li><li><a href=\"/chapter5/constrainedbox_and_sizebox.html\" class=\"sidebar-link\">5.2 尺寸限制类容器</a></li><li><a href=\"/chapter5/decoratedbox.html\" class=\"sidebar-link\">5.3 装饰容器DecoratedBox</a></li><li><a href=\"/chapter5/transform.html\" class=\"sidebar-link\">5.4 变换（Transform）</a></li><li><a href=\"/chapter5/container.html\" class=\"sidebar-link\">5.5 Container</a></li><li><a href=\"/chapter5/material_scaffold.html\" class=\"sidebar-link\">5.6 Scaffold、TabBar、底部导航</a></li><li><a href=\"/chapter5/clip.html\" class=\"sidebar-link\">5.7 剪裁（Clip）</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter6/index\" class=\"sidebar-heading clickable\"><span>第六章：可滚动组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter6/intro.html\" class=\"sidebar-link\">6.1 可滚动组件简介</a></li><li><a href=\"/chapter6/single_child_scrollview.html\" class=\"sidebar-link\">6.2 SingleChildScrollView</a></li><li><a href=\"/chapter6/listview.html\" class=\"sidebar-link\">6.3 ListView</a></li><li><a href=\"/chapter6/gridview.html\" class=\"sidebar-link\">6.4 GridView</a></li><li><a href=\"/chapter6/custom_scrollview.html\" class=\"sidebar-link\">6.5 CustomScrollView</a></li><li><a href=\"/chapter6/scroll_controller.html\" class=\"sidebar-link\">6.6 滚动监听及控制</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter7/index\" class=\"sidebar-heading clickable\"><span>第七章：功能型组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter7/willpopscope.html\" class=\"sidebar-link\">7.1 导航返回拦截（WillPopScope）</a></li><li><a href=\"/chapter7/inherited_widget.html\" class=\"sidebar-link\">7.2 数据共享（InheritedWidget）</a></li><li><a href=\"/chapter7/provider.html\" class=\"sidebar-link\">7.3 跨组件状态共享（Provider）</a></li><li><a href=\"/chapter7/theme.html\" class=\"sidebar-link\">7.4 颜色和主题</a></li><li><a href=\"/chapter7/futurebuilder_and_streambuilder.html\" class=\"sidebar-link\">7.5 异步UI更新（FutureBuilder、StreamBuilder）</a></li><li><a href=\"/chapter7/dailog.html\" class=\"sidebar-link\">7.6 对话框详解</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter8/index\" class=\"sidebar-heading clickable\"><span>第八章：事件处理与通知</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter8/listener.html\" class=\"sidebar-link\">8.1 原始指针事件处理</a></li><li><a href=\"/chapter8/gesture.html\" class=\"sidebar-link\">8.2 手势识别</a></li><li><a href=\"/chapter8/eventbus.html\" class=\"sidebar-link\">8.3 事件总线</a></li><li><a href=\"/chapter8/notification.html\" class=\"sidebar-link\">8.4 Notification</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter9/index\" class=\"sidebar-heading clickable\"><span>第九章：动画</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter9/intro.html\" class=\"sidebar-link\">9.1 Flutter动画简介</a></li><li><a href=\"/chapter9/animation_structure.html\" class=\"sidebar-link\">9.2 动画基本结构及状态监听</a></li><li><a href=\"/chapter9/route_transition.html\" class=\"sidebar-link\">9.3 自定义路由切换动画</a></li><li><a href=\"/chapter9/hero.html\" class=\"sidebar-link\">9.4 Hero动画</a></li><li><a href=\"/chapter9/stagger_animation.html\" class=\"sidebar-link\">9.5 交织动画</a></li><li><a href=\"/chapter9/animated_switcher.html\" class=\"sidebar-link\">9.6 通用“动画切换”组件（AnimatedSwitcher）</a></li><li><a href=\"/chapter9/animated_widgets.html\" class=\"sidebar-link\">9.7 动画过渡组件</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter10/index\" class=\"sidebar-heading clickable\"><span>第十章：自定义组件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter10/intro.html\" class=\"sidebar-link\">10.1 自定义组件方法简介</a></li><li><a href=\"/chapter10/combine.html\" class=\"sidebar-link\">10.2 组合现有组件</a></li><li><a href=\"/chapter10/turn_box.html\" class=\"sidebar-link\">10.3 组合实例：TurnBox</a></li><li><a href=\"/chapter10/custom_paint.html\" class=\"sidebar-link\">10.4 自绘组件 （CustomPaint与Canvas）</a></li><li><a href=\"/chapter10/gradient_circular_progress_demo.html\" class=\"sidebar-link\">10.5 自绘实例：圆形背景渐变进度条</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter11/index\" class=\"sidebar-heading clickable\"><span>第十一章：文件操作与网络请求</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter11/file_operation.html\" class=\"sidebar-link\">11.1 文件操作</a></li><li><a href=\"/chapter11/http.html\" class=\"sidebar-link\">11.2 通过HttpClient发起HTTP请求</a></li><li><a href=\"/chapter11/dio.html\" class=\"sidebar-link\">11.3 Http请求-Dio http库</a></li><li><a href=\"/chapter11/download_with_chunks.html\" class=\"sidebar-link\">11.4 实例：Http分块下载</a></li><li><a href=\"/chapter11/websocket.html\" class=\"sidebar-link\">使用WebSockets</a></li><li><a href=\"/chapter11/socket.html\" class=\"sidebar-link\">11.6 使用Socket API</a></li><li><a href=\"/chapter11/json_model.html\" class=\"sidebar-link\">11.7 Json转Dart Model类</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter12/index\" class=\"sidebar-heading clickable\"><span>第十二章：包与插件</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter12/develop_package.html\" class=\"sidebar-link\">12.1 开发Package</a></li><li><a href=\"/chapter12/platform-channel.html\" class=\"sidebar-link\">12.2 插件开发：平台通道简介</a></li><li><a href=\"/chapter12/develop_plugin.html\" class=\"sidebar-link\">12.3 开发Flutter插件</a></li><li><a href=\"/chapter12/android_implement.html\" class=\"sidebar-link\">12.4 插件开发：Android端API实现</a></li><li><a href=\"/chapter12/ios_implement.html\" class=\"sidebar-link\">12.5 插件开发：iOS端API实现</a></li><li><a href=\"/chapter12/texture_platformview.html\" class=\"sidebar-link\">12.6 Texture和PlatformView</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter13/index\" class=\"sidebar-heading clickable\"><span>第十三章：国际化</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter13/multi_languages_support.html\" class=\"sidebar-link\">13.1 让App支持多语言</a></li><li><a href=\"/chapter13/locallization_implement.html\" class=\"sidebar-link\">13.2 实现Localizations</a></li><li><a href=\"/chapter13/intl.html\" class=\"sidebar-link\">使用Intl包</a></li><li><a href=\"/chapter13/faq.html\" class=\"sidebar-link\">13.4 国际化常见问题</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><a href=\"/chapter14/index\" class=\"sidebar-heading clickable\"><span>第十四章：Flutter核心原理</span> <!----></a> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter14/flutter_ui_system.html\" class=\"sidebar-link\">14.1 Flutter UI系统</a></li><li><a href=\"/chapter14/element_buildcontext.html\" class=\"sidebar-link\">14.2 Element与BuildContext</a></li><li><a href=\"/chapter14/render_object.html\" class=\"sidebar-link\">14.3 RenderObject和RenderBox</a></li><li><a href=\"/chapter14/flutter_app_startup.html\" class=\"sidebar-link\">14.4 Flutter运行机制-从启动到显示</a></li><li><a href=\"/chapter14/image_and_cache.html\" class=\"sidebar-link\">14.5 图片加载原理与缓存</a></li></ul></section></li><li><section class=\"sidebar-group depth-0\"><p class=\"sidebar-heading\"><span>第十五章：一个完整的Flutter应用</span> <!----></p> <ul class=\"sidebar-links sidebar-group-items\"><li><a href=\"/chapter15/intro.html\" class=\"sidebar-link\">15.1 Github客户端示例</a></li><li><a href=\"/chapter15/code_structure.html\" class=\"sidebar-link\">15.2 Flutter APP代码结构</a></li><li><a href=\"/chapter15/models.html\" class=\"sidebar-link\">15.3 Model类定义</a></li><li><a href=\"/chapter15/globals.html\" class=\"sidebar-link\">15.4 全局变量及共享状态</a></li><li><a href=\"/chapter15/network.html\" class=\"sidebar-link\">15.5 网络请求封装</a></li><li><a href=\"/chapter15/entry.html\" class=\"sidebar-link\">15.6 APP入口及主页</a></li><li><a href=\"/chapter15/login_page.html\" class=\"sidebar-link\">15.7 登录页</a></li><li><a href=\"/chapter15/language_and_theme_setting.html\" class=\"sidebar-link\">15.8 多语言和多主题</a></li></ul></section></li></ul> </aside> <main class=\"page\"> <!----> <div class=\"theme-default-content content__default\"><h1 id=\"summary\"><a href=\"#summary\" class=\"header-anchor\">#</a> Summary</h1> <ul><li><a href=\"/v2/\" class=\"router-link-active\">简介</a></li> <li><a href=\"/v2/intro.html\">前言</a></li></ul> <h2 id=\"入门篇\"><a href=\"#入门篇\" class=\"header-anchor\">#</a> 入门篇</h2> <ul><li><p><a href=\"/v2/chapter1/\">第一章：起步</a></p> <ul><li><a href=\"/v2/chapter1/mobile_development_intro.html\">1.1：移动开发技术简介</a></li> <li><a href=\"/v2/chapter1/flutter_intro.html\">1.2：初识Flutter</a></li> <li><a href=\"/v2/chapter1/install_flutter.html\">1.3：搭建Flutter开发环境</a></li> <li><a href=\"/v2/chapter1/dart.html\">1.4：Dart语言简介</a></li></ul></li> <li><p><a href=\"/v2/chapter2/\">第二章：第一个Flutter应用</a></p> <ul><li><a href=\"/v2/chapter2/first_flutter_app.html\">2.1：计数器示例</a></li> <li><a href=\"/v2/chapter2/flutter_router.html\">2.2：路由管理</a></li> <li><a href=\"/v2/chapter2/flutter_package_mgr.html\">2.3：包管理</a></li> <li><a href=\"/v2/chapter2/flutter_assets_mgr.html\">2.4：资源管理</a></li> <li><a href=\"/v2/chapter2/flutter_app_debug.html\">2.5：调试Flutter APP</a></li> <li><a href=\"/v2/chapter2/thread_model_and_error_report.html\">2.6：Dart线程模型及异常捕获</a></li></ul></li> <li><p><a href=\"/v2/chapter3/\">第三章：基础组件</a></p> <ul><li><a href=\"/v2/chapter3/flutter_widget_intro.html\">3.1：Widget简介</a></li> <li><a href=\"/v2/chapter3/state_manage.html\">3.2：状态管理</a></li> <li><a href=\"/v2/chapter3/text.html\">3.3：文本、字体样式</a></li> <li><a href=\"/v2/chapter3/buttons.html\">3.4：按钮</a></li> <li><a href=\"/v2/chapter3/img_and_icon.html\">3.5：图片和Icon</a></li> <li><a href=\"/v2/chapter3/radio_and_checkbox.html\">3.6：单选框和复选框</a></li> <li><a href=\"/v2/chapter3/input_and_form.html\">3.7：输入框和表单</a></li> <li><a href=\"/v2/chapter3/progress.html\">3.8：进度指示器</a></li></ul></li> <li><p><a href=\"/v2/chapter4/\">第四章：布局类组件</a></p> <ul><li><a href=\"/v2/chapter4/intro.html\">4.1：布局类组件简介</a></li> <li><a href=\"/v2/chapter4/row_and_column.html\">4.2：线性布局（Row、Column）</a></li> <li><a href=\"/v2/chapter4/flex.html\">4.3：弹性布局（Flex）</a></li> <li><a href=\"/v2/chapter4/wrap_and_flow.html\">4.4：流式布局（Wrap、Flow）</a></li> <li><a href=\"/v2/chapter4/stack.html\">4.5：层叠布局（Stack、Positioned）</a></li> <li><a href=\"/v2/chapter4/alignment.html\">4.6：对齐与相对定位（Align）</a></li></ul></li> <li><p><a href=\"/v2/chapter5/\">第五章：容器类组件</a></p> <ul><li><a href=\"/v2/chapter5/padding.html\">5.1：填充（Padding）</a></li> <li><a href=\"/v2/chapter5/constrainedbox_and_sizebox.html\">5.2：尺寸限制类容器（ConstrainedBox等）</a></li> <li><a href=\"/v2/chapter5/decoratedbox.html\">5.3：装饰容器（DecoratedBox）</a></li> <li><a href=\"/v2/chapter5/transform.html\">5.4：变换（Transform）</a></li> <li><a href=\"/v2/chapter5/container.html\">5.5：Container容器</a></li> <li><a href=\"/v2/chapter5/material_scaffold.html\">5.6：Scaffold、TabBar、底部导航</a></li> <li><a href=\"/v2/chapter5/clip.html\">5.7：剪裁（Clip）</a></li></ul></li> <li><p><a href=\"/v2/chapter6/\">第六章：可滚动组件</a></p> <ul><li><a href=\"/v2/chapter6/intro.html\">6.1：可滚动组件简介</a></li> <li><a href=\"/v2/chapter6/single_child_scrollview.html\">6.2：SingleChildScrollView</a></li> <li><a href=\"/v2/chapter6/listview.html\">6.3：ListView</a></li> <li><a href=\"/v2/chapter6/gridview.html\">6.4：GridView</a></li> <li><a href=\"/v2/chapter6/custom_scrollview.html\">6.5：CustomScrollView</a></li> <li><a href=\"/v2/chapter6/scroll_controller.html\">6.6：滚动监听及控制（ScrollController）</a></li></ul></li> <li><p><a href=\"/v2/chapter7/\">第七章：功能型组件</a></p> <ul><li><a href=\"/v2/chapter7/willpopscope.html\">7.1：导航返回拦截（WillPopScope）</a></li> <li><a href=\"/v2/chapter7/inherited_widget.html\">7.2：数据共享（InheritedWidget）</a></li> <li><a href=\"/v2/chapter7/provider.html\">7.3： 跨组件状态共享（Provider）</a></li> <li><a href=\"/v2/chapter7/theme.html\">7.4：颜色和主题（Theme）</a></li> <li><a href=\"/v2/chapter7/futurebuilder_and_streambuilder.html\">7.5：异步UI更新（FutureBuilder、StreamBuilder）</a></li> <li><a href=\"/v2/chapter7/dailog.html\">7.6：对话框详解</a></li></ul></li></ul> <h2 id=\"进阶篇\"><a href=\"#进阶篇\" class=\"header-anchor\">#</a> 进阶篇</h2> <ul><li><p><a href=\"/v2/chapter8/\">第八章：事件处理与通知</a></p> <ul><li><a href=\"/v2/chapter8/listener.html\">8.1：原始指针事件处理</a></li> <li><a href=\"/v2/chapter8/gesture.html\">8.2：手势识别</a></li> <li><a href=\"/v2/chapter8/eventbus.html\">8.3：全局事件总线</a></li> <li><a href=\"/v2/chapter8/notification.html\">8.4：通知(Notification)</a></li></ul></li> <li><p><a href=\"/v2/chapter9/\">第九章：动画</a></p> <ul><li><a href=\"/v2/chapter9/intro.html\">9.1：Flutter动画简介</a></li> <li><a href=\"/v2/chapter9/animation_structure.html\">9.2：动画结构</a></li> <li><a href=\"/v2/chapter9/route_transition.html\">9.3：自定义路由过渡动画</a></li> <li><a href=\"/v2/chapter9/hero.html\">9.4：Hero动画</a></li> <li><a href=\"/v2/chapter9/stagger_animation.html\">9.5：交织动画</a></li> <li><a href=\"/v2/chapter9/animated_switcher.html\">9.6：通用“动画切换”组件（AnimatedSwitcher）</a></li> <li><a href=\"/v2/chapter9/animated_widgets.html\">9.7：动画过渡组件</a></li></ul></li> <li><p><a href=\"/v2/chapter10/\">第十章：自定义组件</a></p> <ul><li><a href=\"/v2/chapter10/intro.html\">10.1：自定义组件方法简介</a></li> <li><a href=\"/v2/chapter10/combine.html\">10.2：组合现有组件</a></li> <li><a href=\"/v2/chapter10/turn_box.html\">10.3：组合实例：TurnBox</a></li> <li><a href=\"/v2/chapter10/custom_paint.html\">10.4：自绘组件（CustomPaint与Canvas）</a></li> <li><a href=\"/v2/chapter10/gradient_circular_progress_demo.html\">10.5：自绘实例：圆形渐变进度条(自绘)</a></li></ul></li> <li><p><a href=\"/v2/chapter11/\">第十一章：文件操作与网络请求</a></p> <ul><li><a href=\"/v2/chapter11/file_operation.html\">11.1：文件操作</a></li> <li><a href=\"/v2/chapter11/http.html\">11.2：Http请求-HttpClient</a></li> <li><a href=\"/v2/chapter11/dio.html\">11.3：Http请求-Dio package</a></li> <li><a href=\"/v2/chapter11/download_with_chunks.html\">11.4：实例：Http分块下载</a></li> <li><a href=\"/v2/chapter11/websocket.html\">11.5：WebSocket</a></li> <li><a href=\"/v2/chapter11/socket.html\">11.6：使用Socket API</a></li> <li><a href=\"/v2/chapter11/json_model.html\">11.7：Json转Dart Model类</a></li></ul></li> <li><p><a href=\"/v2/chapter12/\">第十二章：包与插件</a></p> <ul><li><a href=\"/v2/chapter12/develop_package.html\">12.1：开发package</a></li> <li><a href=\"/v2/chapter12/platform-channel.html\">12.2：平台通道简介</a></li> <li><a href=\"/v2/chapter12/develop_plugin.html\">12.3：开发Flutter插件</a></li> <li><a href=\"/v2/chapter12/android_implement.html\">12.4：插件开发：实现Android端API</a></li> <li><a href=\"/v2/chapter12/ios_implement.html\">12.5：插件开发：实现IOS端API</a></li> <li><a href=\"/v2/chapter12/texture_platformview.html\">12.6：Texture和PlatformView</a></li></ul></li> <li><p><a href=\"/v2/chapter13/\">第十三章：国际化</a></p> <ul><li><a href=\"/v2/chapter13/multi_languages_support.html\">13.1：让App支持多语言</a></li> <li><a href=\"/v2/chapter13/locallization_implement.html\">13.2：实现Localizations</a></li> <li><a href=\"/v2/chapter13/intl.html\">13.3：使用Intl包</a></li> <li><a href=\"/v2/chapter13/faq.html\">13.4：国际化常见问题</a></li></ul></li> <li><p><a href=\"/v2/chapter14/\">第十四章：Flutter核心原理</a></p> <ul><li><a href=\"/v2/chapter14/flutter_ui_system.html\">14.1：Flutter UI系统</a></li> <li><a href=\"/v2/chapter14/element_buildcontext.html\">14.2：Element和BuildContext</a></li> <li><a href=\"/v2/chapter14/render_object.html\">14.3：RenderObject与RenderBox</a></li> <li><a href=\"/v2/chapter14/flutter_app_startup.html\">14.4：Flutter从启动到显示</a></li> <li><a href=\"/v2/chapter14/image_and_cache.html\">14.5：Flutter图片加载与缓存</a></li></ul></li></ul> <h2 id=\"实例篇\"><a href=\"#实例篇\" class=\"header-anchor\">#</a> 实例篇</h2> <ul><li><a href=\"/v2/chapter15/intro.html\">第十五章：一个完整的Flutter应用</a> <ul><li><a href=\"/v2/chapter15/intro.html\">15.1：应用简介</a></li> <li><a href=\"/v2/chapter15/code_structure.html\">15.2：APP代码结构</a></li> <li><a href=\"/v2/chapter15/models.html\">15.3：Model类定义</a></li> <li><a href=\"/v2/chapter15/globals.html\">15.4：全局变量及共享状态</a></li> <li><a href=\"/v2/chapter15/network.html\">15.5：网络请求封装</a></li> <li><a href=\"/v2/chapter15/entry.html\">15.6：App入口及首页</a></li> <li><a href=\"/v2/chapter15/login_page.html\">15.7：登录页</a></li> <li><a href=\"/v2/chapter15/language_and_theme_setting.html\">15.8：多语言和多主题</a></li></ul></li></ul></div> <footer class=\"page-edit\"><!----> <!----></footer> <!----> <div class=\"copyright\"> 版权所有，禁止私自转发、克隆网站。</div> <div class=\"f-links\" style=\"text-align:center;\"><a title=\"点击购买\" target=\"_blank\"> 购买实体书\n        </a> |\n        <a href=\"https://github.com/flutterchina\">\n          Flutter中国开源项目\n        </a> |\n        <a href=\"/join_us.html\">\n          和作者做同事\n        </a></div></main></div><div class=\"global-ui\"></div></div>\n    <script src=\"/assets/js/app.189f52e4.js\" defer></script><script src=\"/assets/js/39.c3d0c942.js\" defer></script><script src=\"/assets/js/2.f4fe4405.js\" defer></script><script src=\"/assets/js/229.91061a53.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"book\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"dev\": \"vuepress dev src\",\n    \"build\": \"vuepress build src\"\n  },\n  \"author\": \"wendux\",\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"@antv/g2\": \"^4.0.6\"\n  },\n  \"devDependencies\": {\n    \"html-minifier\": \"^3.5.21\",\n    \"vuepress\": \"^1.8.2\"\n  }\n}\n"
  },
  {
    "path": "src/.vuepress/config.js",
    "content": "// .vuepress/config.js\nmodule.exports = {\n  title:'《Flutter实战》电子书',\n  dest:'docs',\n  markdown: {\n   // lineNumbers: true\n  },\n  head: [\n    ['link', { rel: 'icon', href: '/logo.png' }]\n  ],\n  sidebarDepth: 2,\n  themeConfig: {\n    logo:'/logo.png',\n    nav: [\n      { text: '和作者做同事', link: '/join_us' },\n      { text: 'Flutter中国', link: 'https://github.com/flutterchina' },\n      { text: 'Github', link: 'https://github.com/flutterchina/flutter-in-action' },\n      { text: '实体书', link: 'https://item.jd.com/12816296.html' },\n    ],\n    sidebar: [\n      {\n        title:\"首页\",\n        path:\"/\"\n      },\n      {\n        title:\"第一章：起步\",\n        path:\"/chapter1/index\",\n        collapsable: false,\n        children:[\n          '/chapter1/mobile_development_intro',\n          '/chapter1/flutter_intro',\n          '/chapter1/install_flutter',\n          '/chapter1/dart'\n        ]\n      },\n      {\n        title:\"第二章：第一个Flutter应用\",\n        path:\"/chapter2/index\",\n        collapsable: false,\n        children:[\n          '/chapter2/first_flutter_app',\n          '/chapter2/flutter_router',\n          '/chapter2/flutter_package_mgr',\n          '/chapter2/flutter_assets_mgr',\n          '/chapter2/flutter_app_debug',\n          '/chapter2/thread_model_and_error_report'\n        ]\n      },\n      {\n        title:\"第三章：基础组件\",\n        path:\"/chapter3/index\",\n        collapsable: false,\n        children:[\n          '/chapter3/flutter_widget_intro',\n          '/chapter3/state_manage',\n          '/chapter3/text',\n          '/chapter3/buttons',\n          '/chapter3/img_and_icon',\n          '/chapter3/radio_and_checkbox',\n          '/chapter3/input_and_form',\n          '/chapter3/progress',\n        ]\n      },\n      {\n        title:'第四章：布局类组件',\n        path:\"chapter4/index.md\",\n        collapsable: false,\n        children:[\n          '/chapter4/intro',\n          '/chapter4/row_and_column',\n          '/chapter4/flex',\n          '/chapter4/wrap_and_flow',\n          '/chapter4/stack',\n          '/chapter4/alignment',\n        ]\n      },\n      {\n        title:\"第五章：容器类组件\",\n        path:\"/chapter5/index\",\n        collapsable: false,\n        children:[\n          '/chapter5/padding',\n          '/chapter5/constrainedbox_and_sizebox',\n          '/chapter5/decoratedbox',\n          '/chapter5/transform',\n          '/chapter5/container',\n          '/chapter5/material_scaffold',\n          '/chapter5/clip',\n        ]\n      },\n      {\n        title:\"第六章：可滚动组件\",\n        path:\"/chapter6/index\",\n        collapsable: false,\n        children:[\n          '/chapter6/intro',\n          '/chapter6/single_child_scrollview',\n          '/chapter6/listview',\n          '/chapter6/gridview',\n          '/chapter6/custom_scrollview',\n          '/chapter6/scroll_controller',\n        ]\n      },\n      {\n        title:\"第七章：功能型组件\",\n        path:\"/chapter7/index\",\n        collapsable: false,\n        children:[\n          '/chapter7/willpopscope',\n          '/chapter7/inherited_widget',\n          '/chapter7/provider',\n          '/chapter7/theme',\n          '/chapter7/futurebuilder_and_streambuilder',\n          '/chapter7/dailog',\n        ]\n      },\n      {\n        title:\"第八章：事件处理与通知\",\n        path:\"/chapter8/index\",\n        collapsable: false,\n        children:[\n          '/chapter8/listener',\n          '/chapter8/gesture',\n          '/chapter8/eventbus',\n          '/chapter8/notification',\n        ]\n      },\n      {\n        title:\"第九章：动画\",\n        path:\"/chapter9/index\",\n        collapsable: false,\n        children:[\n          '/chapter9/intro',\n          '/chapter9/animation_structure',\n          '/chapter9/route_transition',\n          '/chapter9/hero',\n          '/chapter9/stagger_animation',\n          '/chapter9/animated_switcher',\n          '/chapter9/animated_widgets',\n        ]\n      },\n      {\n        title:\"第十章：自定义组件\",\n        path:\"/chapter10/index\",\n        collapsable: false,\n        children:[\n          '/chapter10/intro',\n          '/chapter10/combine',\n          '/chapter10/turn_box',\n          '/chapter10/custom_paint',\n          '/chapter10/gradient_circular_progress_demo',\n        ]\n      },\n      {\n        title:\"第十一章：文件操作与网络请求\",\n        path:\"/chapter11/index\",\n        collapsable: false,\n        children:[\n          '/chapter11/file_operation',\n          '/chapter11/http',\n          '/chapter11/dio',\n          '/chapter11/download_with_chunks',\n          '/chapter11/websocket',\n          '/chapter11/socket',\n          '/chapter11/json_model',\n        ]\n      },\n      {\n        title:\"第十二章：包与插件\",\n        path:\"/chapter12/index\",\n        collapsable: false,\n        children:[\n          '/chapter12/develop_package',\n          '/chapter12/platform-channel',\n          '/chapter12/develop_plugin',\n          '/chapter12/android_implement',\n          '/chapter12/ios_implement',\n          '/chapter12/texture_platformview',\n        ]\n      },\n      {\n        title:\"第十三章：国际化\",\n        path:\"/chapter13/index\",\n        collapsable: false,\n        children:[\n          '/chapter13/multi_languages_support',\n          '/chapter13/locallization_implement',\n          '/chapter13/intl',\n          '/chapter13/faq',\n        ]\n      },\n      {\n        title:\"第十四章：Flutter核心原理\",\n        path:\"/chapter14/index\",\n        collapsable: false,\n        children:[\n          '/chapter14/flutter_ui_system',\n          '/chapter14/element_buildcontext',\n          '/chapter14/render_object',\n          '/chapter14/flutter_app_startup',\n          '/chapter14/image_and_cache',\n        ]\n      },\n      {\n        title:\"第十五章：一个完整的Flutter应用\",\n        collapsable: false,\n        children:[\n          '/chapter15/intro',\n          '/chapter15/code_structure',\n          '/chapter15/models',\n          '/chapter15/globals',\n          '/chapter15/network',\n          '/chapter15/entry',\n          '/chapter15/login_page',\n          '/chapter15/language_and_theme_setting',\n        ]\n      },\n    ]\n  }\n}\n"
  },
  {
    "path": "src/.vuepress/styles/palette.styl",
    "content": "\n$accentColor = #1389FD\n"
  },
  {
    "path": "src/.vuepress/theme/index.js",
    "content": "module.exports = {\n  extend: '@vuepress/theme-default',\n  globalLayout: './layouts/GlobalLayout.vue'\n}\n"
  },
  {
    "path": "src/.vuepress/theme/layouts/Layout.vue",
    "content": "<template>\n  <div\n    class=\"theme-container\"\n    :class=\"pageClasses\"\n    @touchstart=\"onTouchStart\"\n    @touchend=\"onTouchEnd\"\n  >\n    <Navbar\n      v-if=\"shouldShowNavbar\"\n      @toggle-sidebar=\"toggleSidebar\"\n      ref=\"nav\"\n    />\n\n    <div\n      class=\"sidebar-mask\"\n      @click=\"toggleSidebar(false)\"\n    />\n\n    <Sidebar\n      :items=\"sidebarItems\"\n      @toggle-sidebar=\"toggleSidebar\"\n    >\n      <template #top>\n        <slot name=\"sidebar-top\"/>\n      </template>\n      <template #bottom>\n        <slot name=\"sidebar-bottom\"/>\n      </template>\n    </Sidebar>\n\n    <Home v-if=\"$page.frontmatter.home\"/>\n\n    <Page\n      v-else\n      :sidebar-items=\"sidebarItems\"\n    >\n\n      <template #top>\n        <slot name=\"page-top\"/>\n        <div v-if=\"showBook\" style=\" text-align:center; margin-top: 100px\">\n          <img src=\"./book.png\" class=\"book\" title=\"点击去购买\" @click=\"buy('btn')\"/>\n        </div>\n      </template>\n      <template #bottom>\n        <div class='copyright'> 版权所有，禁止私自转发、克隆网站。</div>\n        <div style='text-align: center' class='f-links'>\n          <a @click='buy(\"link\")'\n             title='点击购买'\n             target='_blank'> 购买实体书\n          </a> |\n          <a href='https://github.com/flutterchina'>\n            Flutter中国开源项目\n          </a> |\n          <a href='/join_us.html'>\n            和作者做同事\n          </a>\n\n        </div>\n\n      </template>\n    </Page>\n  </div>\n</template>\n\n<script>\n  import Home from '@theme/components/Home.vue'\n  import Navbar from '@theme/components/Navbar.vue'\n  import Page from '@theme/components/Page.vue'\n  import Sidebar from '@theme/components/Sidebar.vue'\n  import {resolveSidebarItems} from '@vuepress/theme-default/util'\n\n  let _hmt;\n\n  function initPVSDK() {\n    _hmt = _hmt || [];\n    let hm = document.createElement(\"script\");\n    hm.src = \"https://hm.baidu.com/hm.js?170231fea4f81697eb046edc1a91fe5b\";\n    let s = document.getElementsByTagName(\"script\")[0];\n    hm.id = \"bd\"\n    s.parentNode.insertBefore(hm, s);\n  }\n\n  const hideBookRoutes = [\n    '/join_us.html'\n  ]\n\n  export default {\n    name: 'Layout',\n\n    components: {\n      Home,\n      Page,\n      Sidebar,\n      Navbar\n    },\n\n    data() {\n      return {\n        isSidebarOpen: false,\n        showBook: false,\n      }\n    },\n\n    computed: {\n      shouldShowNavbar() {\n        const {themeConfig} = this.$site\n        const {frontmatter} = this.$page\n        if (\n          frontmatter.navbar === false\n          || themeConfig.navbar === false) {\n          return false\n        }\n        return (\n          this.$title\n          || themeConfig.logo\n          || themeConfig.repo\n          || themeConfig.nav\n          || this.$themeLocaleConfig.nav\n        )\n      },\n\n      shouldShowSidebar() {\n        const {frontmatter} = this.$page\n        return (\n          !frontmatter.home\n          && frontmatter.sidebar !== false\n          && this.sidebarItems.length\n        )\n      },\n\n      sidebarItems() {\n        return resolveSidebarItems(\n          this.$page,\n          this.$page.regularPath,\n          this.$site,\n          this.$localePath\n        )\n      },\n\n      pageClasses() {\n        const userPageClass = this.$page.frontmatter.pageClass\n        return [\n          {\n            'no-navbar': !this.shouldShowNavbar,\n            'sidebar-open': this.isSidebarOpen,\n            'no-sidebar': !this.shouldShowSidebar\n          },\n          userPageClass\n        ]\n      }\n    },\n\n    mounted() {\n      initPVSDK()\n      this.lastPathname = ''\n      this.$router.afterEach(() => {\n        if (hideBookRoutes.indexOf(location.pathname) === -1) {\n          this.showBook = true;\n        }\n        if (this.lastPathname !== location.pathname) {\n          console.log(location.pathname);\n          _hmt.push(['_trackPageview', this.lastPathname = location.pathname])\n\n          //百度统计\n          var e = /([http|https]:\\/\\/[a-zA-Z0-9\\_\\.]+\\.baidu\\.com)/gi, r = window.location.href,\n            t = document.referrer;\n          if (!e.test(r)) {\n            var o = \"https://sp0.baidu.com/9_Q4simg2RQJ8t7jm9iCKT-xh_/s.gif\";\n            t ? (o += \"?r=\" + encodeURIComponent(document.referrer), r && (o += \"&l=\" + r)) : r && (o += \"?l=\" + r);\n            var i = new Image;\n            i.src = o\n          }\n        }\n        this.isSidebarOpen = false\n      })\n      const {themeConfig} = this.$site\n      let logo = themeConfig.logo;\n\n      let checkIfShowLogo = () => {\n        if (window.innerWidth < 720 && themeConfig.log !== \"\") {\n          themeConfig.logo = \"\"\n          this.$refs.nav.$forceUpdate()\n        } else if (window.innerWidth >= 720 && themeConfig.logo === \"\") {\n          themeConfig.logo = logo\n          console.log(\"xx\")\n          this.$refs.nav.$forceUpdate()\n        }\n      }\n\n      checkIfShowLogo()\n\n      window.addEventListener('resize', checkIfShowLogo)\n\n    },\n\n    methods: {\n\n      buy(p) {\n        _hmt.push(['_trackEvent', 'buy', 'click', p]);\n        open('https://item.jd.com/12816296.html', '_blank');\n      },\n\n      toggleSidebar(to) {\n        this.isSidebarOpen = typeof to === 'boolean' ? to : !this.isSidebarOpen\n        this.$emit('toggle-sidebar', this.isSidebarOpen)\n      },\n\n      // side swipe\n      onTouchStart(e) {\n        this.touchStart = {\n          x: e.changedTouches[0].clientX,\n          y: e.changedTouches[0].clientY\n        }\n      },\n\n      onTouchEnd(e) {\n        const dx = e.changedTouches[0].clientX - this.touchStart.x\n        const dy = e.changedTouches[0].clientY - this.touchStart.y\n        if (Math.abs(dx) > Math.abs(dy) && Math.abs(dx) > 40) {\n          if (dx > 0 && this.touchStart.x <= 80) {\n            this.toggleSidebar(true)\n          } else {\n            this.toggleSidebar(false)\n          }\n        }\n      }\n    }\n  }\n</script>\n\n<style>\n  .copyright {\n    text-align: center;\n    margin: 50px 16px 8px 16px;\n    color: grey;\n    font-size: .9em;\n  }\n\n  .f-links a {\n    font-weight: normal;\n    text-decoration: underline;\n    font-size: .9em;\n    color: dodgerblue !important;\n  }\n\n  .f-links a:hover {\n    opacity: .8 !important;\n  }\n\n  .book {\n    transition: 200ms box-shadow;\n    max-width: 180px;\n    box-shadow: 2px 2px 5px #aaa;\n    cursor: pointer;\n  }\n\n  .book:hover {\n    box-shadow: 5px 5px 8px #888;\n  }\n\n</style>\n"
  },
  {
    "path": "src/README.md",
    "content": "## 关于作者\n\n作者杜文（网名[wendux](https://github.com/wendux)），高级技术专家、[掘金知名专栏作者](https://juejin.im/user/58211b88a0bb9f0058c25b7f)、[Flutter中文网社区](https://flutterchina.club/)创办者、[Flutter中文社区开源项目](https://github.com/flutterchina)发起人、Github社区知名开发者，是[dio](https://github.com/flutterchina/dio)、[fly](https://github.com/wendux/fly)、[dsBridge](https://github.com/wendux/DSBridge-Android)等多个知名开源项目作者。作者曾就职于百度、小赢科技等互联网公司，从事过PC桌面开发、移动端开发以及Web开发，负责过多次核心技术攻关，现就职于字节跳动。目前闲暇时，主要关注大前端行业发展。\n\n## 内容简介\n\n本书由浅入深的介绍了Flutter技术和开发流程。本书包含不仅包含大量示例、图片，还有配套的示例源码，可帮助读者循序渐进的掌握Flutter开发技术。本书分为入门、进阶、实例三大篇，其中入门篇（第1章~第7章）主要介绍了Flutter技术产生的背景、常用的组件以及布局方式，通过入门篇的学习，读者可以掌握如何使用Flutter来构建UI界面。进阶篇（第8章~第14章），包括Flutter中的事件机制、动画、自定义组件、文件和网络、插件、国际化以及Flutter核心原理等。通过本章内容，读者可以对Flutter整体构建及原理有一个深入的认识。实例篇（第15章），本章主要通过一个简版的Github APP来将前面介绍的内容串起来，让开发者对一个完整的Flutter APP开发流有个了解。\n"
  },
  {
    "path": "src/_layouts/ebook/page.html",
    "content": "{% extends \"layout.html\" %}\n\n{% block title %}{{ page.title }}{% endblock %}\n{% block description %}{{ page.description }}{% endblock %}\n\n{% block style %}\n    {### Include theme css before plugins css ###}\n    {% if not fileExists(config.styles.print) %}\n        {% if options.format %}\n        <link rel=\"stylesheet\" href=\"{{ (options.format + \".css\")|resolveAsset }}\">\n        {% else %}\n        <link rel=\"stylesheet\" href=\"{{ \"ebook.css\"|resolveAsset }}\">\n        {% endif %}\n    {% endif %}\n\n    {{ super() }}\n\n    {### Custom stylesheets for the book ###}\n\n    {% for type, style in config.styles %}\n        {% if fileExists(style) and (type == \"ebook\" or type == \"print\" or type == options.format) %}\n        <link rel=\"stylesheet\" href=\"{{ style|resolveFile }}\">\n        {% endif %}\n    {% endfor %}\n{% endblock %}\n\n{% block body %}\n<div class=\"page\">\n    {% block page %}\n        <h1 class=\"book-chapter book-chapter-{{ page.depth }}\">{{ page.title }}</h1>\n        <div class=\"section\">\n            {{ page.content|safe }}\n        </div>\n    {% endblock %}\n</div>\n{% endblock %}\n"
  },
  {
    "path": "src/_layouts/ebook/pdf_footer.html",
    "content": "{% extends \"./page.html\" %}\n\n{% block body %}\n<div class=\"pdf-footer\">\n    <span>{{ page.section }}</span>\n    <span class=\"footer-pages-count\">{{ page.num }}</span>\n</div>\n{% endblock %}\n"
  },
  {
    "path": "src/_layouts/ebook/pdf_header.html",
    "content": "{% extends \"./page.html\" %}\n\n{% block body %}\n<div class=\"pdf-header\">\n    <span>{{ page.title }}</span>\n</div>\n{% endblock %}\n"
  },
  {
    "path": "src/_layouts/ebook/summary.html",
    "content": "{% extends \"./page.html\" %}\n\n{% block title %}{{ \"SUMMARY\"|t }}{% endblock %}\n\n{% macro articles(_articles) %}\n    {% for article in _articles %}\n        <li>\n            <span class=\"inner\">\n                {% if article.path or article.url %}\n                    {% if article.path %}\n                        <a href=\"{{ article.path|contentURL }}{{ article.anchor }}\">{{ article.title }}</a>\n                    {% else %}\n                        <a target=\"_blank\" href=\"{{ article.url }}\">{{ article.title }}</a>\n                    {% endif %}\n                {% else %}\n                    <span>{{ article.title }}</span>\n                {% endif %}\n                {% if 1 %}\n                <span class=\"page\">{{ article.level }}</span>\n                {% endif %}\n            </span>\n            {% if article.articles.length > 0 %}\n            <ol>\n                {{ articles(article.articles) }}\n            </ol>\n            {% endif %}\n        </li>\n    {% endfor %}\n{% endmacro %}\n\n{% block page %}\n<div class=\"section toc\">\n    <h1>{{ \"SUMMARY\"|t }}</h1>\n    <ol>\n        {% for part in summary.parts %}\n            {% if part.title %}\n            <li class=\"part-title\">\n                <h2>{{ part.title }}</h2>\n            </li>\n            {% endif %}\n            {{ articles(part.articles) }}\n\n            {% if not loop.last %}\n            <li class=\"divider\"></li>\n            {% endif %}\n        {% endfor %}\n\n        {% if glossary.path %}\n        <li>\n            <span class=\"inner\">\n                <a href=\"{{ ('/' + glossary.path)|contentURL }}\">{{ \"GLOSSARY\"|t }}</a>\n            </span>\n        </li>\n        {% endif %}\n    </ol>\n</div>\n{% endblock %}\n\n"
  },
  {
    "path": "src/_layouts/layout.html",
    "content": "<!DOCTYPE HTML>\n<html lang=\"{{ config.language }}\" {% if page.dir== \"rtl\" %}dir=\"rtl\"{% endif %}>\n<head>\n    <meta charset=\"UTF-8\">\n    <meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\">\n    <title>{% block title %}{{ config.title|d(\"GitBook\", true) }}{% endblock %}</title>\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"/>\n    <meta name=\"baidu-site-verification\" content=\"Y34sAqDKUH\"/>\n    <meta name=\"theme-color\" content=\"#ffffff\">\n    <!--<meta name=\"description\" content=\"{% block description %}{% endblock %}\">-->\n    <meta name=\"description\" content=\"《Flutter实战》是Flutter中文网（https://flutterchina.club）开源的电子书，旨在帮助开发者系统化的了解并学习Flutter。\">\n    <meta name=\"keywords\" content=\"flutter实战,flutter书籍,flutter电子书,flutter中文网 \">\n    <meta name=\"generator\" content=\"GitBook {{ gitbook.version }}\">\n    {% if config.author %}\n    <meta name=\"author\" content=\"{{ config.author }}\">\n    {% endif %}\n    {% if config.isbn %}\n    <meta name=\"identifier\" content=\"{{ config.isbn }}\" scheme=\"ISBN\">\n    {% endif %}\n    {% block style %}\n    {% for resource in plugins.resources.css %}\n    {% if resource.url %}\n    <link rel=\"stylesheet\" href=\"{{ resource.url }}\">\n    {% else %}\n    <link rel=\"stylesheet\" href=\"{{ resource.path|resolveAsset }}\">\n    {% endif %}\n    {% endfor %}\n    {% endblock %}\n    {% block head %}{% endblock %}\n    <style>\n        .pull-right > i {\n            font-size: 25px;\n            color: #777 !important;\n            cursor: pointer;\n\n        }\n\n        .pull-right > i:hover {\n            color: #777 !important;\n        }\n\n        body {\n            font-size: 15px !important;\n        }\n\n        a[href='../todo.html'], a[href='todo.html'] {\n            color: #888 !important;\n        }\n\n        .copyright {\n            text-align: center;\n            margin: 80px 16px 8px 16px;\n            padding-top: 16px;\n            border-top: #eee 1px solid;\n            color: grey;\n            font-size: .9em;\n        }\n\n        .f-links a{\n            text-decoration: underline;\n            color: dodgerblue !important;\n        }\n\n        .f-links a:hover{\n            opacity: .8 !important;\n        }\n\n        /*.ad {*/\n            /*color: white;*/\n            /*text-align: center;*/\n            /*background: url(https://img.alicdn.com/tfs/TB1Huu7vMmTBuNjy1XbXXaMrVXa-2880-400.png);*/\n            /*background-size: cover;*/\n            /*cursor: pointer;*/\n            /*margin-bottom: 30px;*/\n            /*padding: 18px*/\n        /*}*/\n\n        /*.ad:hover {*/\n            /*opacity: .8;*/\n        /*}*/\n\n        .buy-btn {\n            text-decoration: none !important;\n            padding: 8px 18px;\n            border: #268bd2 1px solid !important;\n            display: inline-block !important;\n            border-radius: 25px;\n            color: #268bd2;\n            margin-top: 10px;\n        }\n\n        .buy-btn:hover {\n            transition: all ease 200ms;\n            box-shadow: #555 2px 5px 10px;\n            background: #eee;\n            color: dodgerblue !important;\n        }\n\n        .maoyun{\n            padding-top: 5px;\n        }\n\n\n    </style>\n</head>\n<body>\n<!--<div id=\"ad\" style=\"display: none\">-->\n<!--<div class=\"ad ad0\"-->\n<!--onclick=\"_track('aliyun','https://promotion.aliyun.com/ntms/yunparter/invite.html?userCode=8hhi4dts')\">-->\n<!--<div style=\"font-size: 1.3em; padding-right: 120px; position: relative; top: 12px\">阿里云新春特惠</div>-->\n<!--<div style=\"color:orange; font-size: 13px\">限时红包 最高￥<span style=\"font-size: 40px\">1888</span></div>-->\n<!--</div>-->\n<!--<div class=\"ad ad1\"-->\n<!--onclick=\"_track('aliyun-puhui','https://promotion.aliyun.com/ntms/act/qwbk.html?spm=5176.11533457.1089570.10.74e877e33jtaGy&userCode=8hhi4dts')\">-->\n<!--<div style=\"font-size: 1.5em;\">阿里云全民云计算-新春特惠</div>-->\n<!--<div style=\"color:orange; font-size: 18px;padding-top: 4px;\">普惠上云，云服务器1核1G仅需293元/年</div>-->\n<!--</div>-->\n<!--</div>-->\n\n<div class=\"ad\"  style=\"text-align:center; padding-bottom:30px; display: none\"><a href=\"https://item.jd.com/12816296.html\" onclick=\"buy('img')\" title='点击购买' target=\"_blank\"><img style=\"height:250px;box-shadow: #aaa 5px 5px 10px;\" src=\"../imgs/book.png\"/></a>  <br/> <a class=\"buy-btn\" onclick=\"buy('btn')\" href=\"https://item.jd.com/12816296.html\" title='点击购买' target=\"_blank\"> 购买实体书 </a></div>\n\n\n<!--<div class=\"ad\"  style=\"text-align:center; padding-bottom:30px; display: none\"><a href=\"https://item.jd.com/12816296.html\" onclick=\"buy('img')\" title='点击购买' target=\"_blank\"><img style=\"height:250px;box-shadow: #aaa 5px 5px 10px;\" src=\"../imgs/book.png\"/></a>  <br/> <a class=\"buy-btn\" onclick=\"buy('img')\" href=\"https://item.jd.com/12816296.html\" title='点击购买' target=\"_blank\"> 购买实体书 </a>-->\n    <!--<a id=\"jd\" href=\"https://item.jd.com/12816296.html\" onclick=\"buy('jd')\"  style=\" display: block; padding: 10px; color: red; text-decoration: underline; \">即日起至4.23日，京东图书每满100减50！ </a>-->\n<!--</div>-->\n\n{% block body %}{% endblock %}\n\n{% block javascript %}{% endblock %}\n\n<script src=\"gitbook/pub.js\"></script>\n</body>\n\n</html>\n"
  },
  {
    "path": "src/_layouts/website/_page.html",
    "content": "{% extends \"./layout.html\" %}\n\n{% block title %}{{ page.title }} · {{ super() }}{% endblock %}\n\n{% block description %}{{ page.description }}{% endblock %}\n\n{% block head %}\n    {{ super() }}\n    {% if page.next and page.next.path %}\n    <link rel=\"next\" href=\"{{ page.next.path|resolveFile }}\" />\n    {% endif %}\n    {% if page.previous and page.previous.path %}\n    <link rel=\"prev\" href=\"{{ page.previous.path|resolveFile }}\" />\n    {% endif %}\n{% endblock %}\n\n{% block javascript %}\n    <script src=\"{{ \"gitbook.js\"|resolveAsset }}\"></script>\n    <script src=\"{{ \"theme.js\"|resolveAsset }}\"></script>\n    {% for resource in plugins.resources.js %}\n        {% if resource.url %}\n        <script src=\"{{ resource.url }}\"></script>\n        {% else %}\n        <script src=\"{{ resource.path|resolveAsset }}\"></script>\n        {% endif %}\n    {% endfor %}\n{% endblock %}\n\n{% block body %}\n<div class=\"book\">\n    <div class=\"book-summary\">\n        {% block book_sidebar %}\n            {% block search_input %}{% endblock %}\n            {% block book_summary %}\n                <nav role=\"navigation\">\n                {% include \"website/summary.html\" %}\n                </nav>\n            {% endblock %}\n        {% endblock %}\n    </div>\n\n    <div class=\"book-body\">\n        {% block book_body %}\n            <div class=\"body-inner\">\n                {% block book_inner %}\n                    {% include \"website/header.html\" %}\n\n                    <div class=\"page-wrapper\" tabindex=\"-1\" role=\"main\">\n                        <div class=\"page-inner\">\n                            {% block search_results %}\n                                <section class=\"normal markdown-section\">\n                                {% block page %}\n                                {{ page.content|safe }}\n                                {% endblock %}\n                                </section>\n                            {% endblock %}\n                        </div>\n                    </div>\n                {% endblock %}\n            </div>\n\n            {% block book_navigation %}\n                {% if page.previous and page.previous.path %}\n                <a href=\"{{ page.previous.path|resolveFile }}{{ page.previous.anchor }}\" class=\"navigation navigation-prev {% if not (page.next and page.next.path) %}navigation-unique{% endif %}\" aria-label=\"Previous page: {{ page.previous.title }}\">\n                    <i class=\"fa fa-angle-left\"></i>\n                </a>\n                {% endif %}\n                {% if page.next and page.next.path %}\n                <a href=\"{{ page.next.path|resolveFile }}{{ page.next.anchor }}\" class=\"navigation navigation-next {% if not (page.previous and page.previous.path) %}navigation-unique{% endif %}\" aria-label=\"Next page: {{ page.next.title }}\">\n                    <i class=\"fa fa-angle-right\"></i>\n                </a>\n                {% endif %}\n            {% endblock %}\n        {% endblock %}\n    </div>\n\n    <script>\n        var gitbook = gitbook || [];\n        gitbook.push(function() {\n            gitbook.page.hasChanged({{ template.getJSContext()|dump|safe }});\n        });\n    </script>\n</div>\n{% endblock %}\n"
  },
  {
    "path": "src/_layouts/website/header.html",
    "content": "{% block book_header %}\n<div class=\"book-header\" role=\"navigation\">\n    {% if glossary.path %}\n    <a href=\"{{ ('/' + glossary.path)|resolveFile }}\" class=\"btn pull-left\" aria-label=\"{{ \"GLOSSARY_OPEN\"|t }}\">\n    <i class=\"fa fa-sort-alpha-asc\"></i>\n    </a>\n    {% endif %}\n    <a class=\"btn pull-right\" aria-label=\"GitHub\">\n        <i class=\"fa fa-edit\" title=\"编辑本文\"></i>\n    </a>\n    <a class=\"btn pull-right\" aria-label=\"GitHub\" href=\"https://github.com/flutterchina/flutter-in-action\">\n        <i class=\"fa fa-github\" title=\"查看本书开源项目\"></i>\n    </a>\n    <!-- Title -->\n    <h1>\n        <i class=\"fa fa-circle-o-notch fa-spin\"></i>\n        <a href=\"{{ \"/\"|resolveFile }}\" >{{ page.title }}</a>\n    </h1>\n</div>\n{% endblock %}\n"
  },
  {
    "path": "src/_layouts/website/languages.html",
    "content": "{% extends \"./layout.html\" %}\n\n{% block title %}{{ \"LANGS_CHOOSE\"|t }} · {{ super() }}{% endblock %}\n\n{% block body %}\n<div class=\"book-langs-index\" role=\"navigation\">\n    <div class=\"inner\">\n        <h3>{{ \"LANGS_CHOOSE\"|t }}</h3>\n\n        <ul class=\"languages\">\n        {%  for lang in languages.list %}\n            <li>\n                <a href=\"{{ (lang.id + \"/README.md\")|contentURL }}\">{{ lang.title }}</a>\n            </li>\n        {% endfor %}\n        </ul>\n    </div>\n</div>\n{% endblock %}\n"
  },
  {
    "path": "src/_layouts/website/layout.html",
    "content": "{% extends \"layout.html\" %}\n\n{% block head %}\n    {{ super() }}\n    <meta name=\"HandheldFriendly\" content=\"false\"/>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, user-scalable=no\">\n    <meta name=\"apple-mobile-web-app-capable\" content=\"yes\">\n    <meta name=\"apple-mobile-web-app-status-bar-style\" content=\"black\">\n    <link rel=\"apple-touch-icon-precomposed\" sizes=\"152x152\" href=\"{{ \"images/apple-touch-icon-precomposed-152.png\"|resolveAsset }}\">\n    <link rel=\"shortcut icon\" href=\"{{ \"imgs/favicon.png\"|resolveAsset }}\" type=\"image/x-icon\">\n{% endblock %}\n\n{% block style %}\n    {### Include theme css before plugins css ###}\n    <link rel=\"stylesheet\" href=\"{{ \"style.css\"|resolveAsset }}\">\n\n    {{ super() }}\n\n    {### Custom stylesheets for the book ###}\n\n    {% for type, style in config.styles %}\n        {% if fileExists(style) and type == \"website\" %}\n        <link rel=\"stylesheet\" href=\"{{ style|resolveFile }}\">\n        {% endif %}\n    {% endfor %}\n{% endblock %}\n\n{% block body %}{% endblock %}\n"
  },
  {
    "path": "src/_layouts/website/summary.html",
    "content": "{% macro articles(_articles) %}\n    {% for article in _articles %}\n        <li class=\"chapter {% if article.path == file.path and not article.anchor %}active{% endif %}\" data-level=\"{{ article.level }}\" {% if article.path %}data-path=\"{{ article.path|resolveFile }}\"{% endif %}>\n            {% if article.path and getPageByPath(article.path) %}\n                <a href=\"{{ article.path|resolveFile }}{{ article.anchor }}\">\n            {% elif article.url %}\n                <a target=\"_blank\" href=\"{{ article.url }}\">\n            {% else %}\n                <span>\n            {% endif %}\n                    {% if article.level != \"0\" and config.pluginsConfig['theme-default'].showLevel %}\n                        <b>{{ article.level }}.</b>\n                    {% endif %}\n                    {{ article.title }}\n            {% if article.path  or article.url %}\n                </a>\n            {% else %}\n                </span>\n            {% endif %}\n\n            {% if article.articles.length > 0 %}\n            <ul class=\"articles\">\n                {{ articles(article.articles, file, config) }}\n            </ul>\n            {% endif %}\n        </li>\n    {% endfor %}\n{% endmacro %}\n\n<ul class=\"summary\">\n    {% set _divider = false %}\n    {% if config.links.sidebar  %}\n    {% for linkTitle, link in config.links.sidebar  %}\n        {% set _divider = true %}\n        <li>\n            <a href=\"{{ link }}\" target=\"_blank\" class=\"custom-link\">{{ linkTitle }}</a>\n        </li>\n    {% endfor %}\n    {% endif %}\n\n    {% if _divider %}\n    <li class=\"divider\"></li>\n    {% endif %}\n\n    {% for part in summary.parts %}\n        {% if part.title %}\n        <li class=\"header\">{{ part.title }}</li>\n        {% elif not loop.first %}\n        <li class=\"divider\"></li>\n        {% endif %}\n        {{ articles(part.articles, file, config) }}\n    {% endfor %}\n\n    <li class=\"divider\"></li>\n\n    <li>\n        <a href=\"https://www.gitbook.com\" target=\"blank\" class=\"gitbook-link\">\n            {{ \"GITBOOK_LINK\"|t }}\n        </a>\n    </li>\n</ul>\n"
  },
  {
    "path": "src/chapter1/dart.md",
    "content": "# 1.4 Dart语言简介\n\n在之前我们已经介绍过Dart语言的相关特性，读者可以翻看一下，如果读者已经熟悉Dart语法，可以跳过本节，如果你还不了解Dart，也不用担心，按照笔者经验，如果你有过其他编程语言经验（尤其是Java和JavaScript）的话会非常容易上手Dart。当然，如果你是iOS开发者，也不用担心，Dart中也有一些与Swift比较相似的特性，如命名参数等，笔者当时学习Dart时，只是花了一个小时，看完Dart官网的Language Tour，就开始动手写Flutter了。\n\n在笔者看来，Dart的设计目标应该是同时借鉴了Java和JavaScript。Dart在静态语法方面和Java非常相似，如类型定义、函数声明、泛型等，而在动态特性方面又和JavaScript很像，如函数式特性、异步支持等。除了融合Java和JavaScript语言之所长之外，Dart也具有一些其它具有表现力的语法，如可选命名参数、`..`（级联运算符）和`?.`（条件成员访问运算符）以及`??`（判空赋值运算符）。其实，对编程语言了解比较多的读者会发现，在Dart中其实看到的不仅有Java和JavaScript的影子，它还具有其它编程语言中的身影，如命名参数在Objective-C和Swift中早就很普遍，而`??`操作符在PHP 7.0语法中就已经存在了，因此我们可以看到Google对Dart语言给予厚望，是想把Dart打造成一门集百家之所长的编程语言。\n\n接下来，我们先对Dart语法做一个简单的介绍，然后再将Dart与JavaScript和Java做一个简要的对比，方便读者更好的理解。\n\n> 注意：由于本书并非专门介绍Dart语言的书籍，所以本章主要会介绍一下在Flutter开发中常用的语法特性，如果想更多了解Dart，读者可以去Dart官网学习，现在互联网上Dart相关资料已经很多了。另外Dart 2.0已经正式发布，所以本书所有示例均采用Dart 2.0语法。\n\n\n\n## 1.4.1 变量声明\n\n1. **var**\n\n   类似于JavaScript中的`var`，它可以接收任何类型的变量，但最大的不同是Dart中var变量一旦赋值，类型便会确定，则不能再改变其类型，如：\n\n   ```dart\n   var t;\n   t = \"hi world\";\n   // 下面代码在dart中会报错，因为变量t的类型已经确定为String，\n   // 类型一旦确定后则不能再更改其类型。\n   t = 1000;\n   ```\n\n   上面的代码在JavaScript是没有问题的，前端开发者需要注意一下，之所以有此差异是因为Dart本身是一个强类型语言，任何变量都是有确定类型的，在Dart中，当用`var`声明一个变量后，Dart在编译时会根据第一次赋值数据的类型来推断其类型，编译结束后其类型就已经被确定，而JavaScript是纯粹的弱类型脚本语言，var只是变量的声明方式而已。\n\n2. **dynamic**和**Object**\n\n    `Object` 是Dart所有对象的根基类，也就是说所有类型都是`Object`的子类(包括Function和Null)，所以任何类型的数据都可以赋值给`Object`声明的对象.\n    `dynamic`与`var`一样都是关键词,声明的变量可以赋值任意对象。\n    而`dynamic`与`Object`相同之处在于,他们声明的变量可以在后期改变赋值类型。\n    \n    ```dart\n    dynamic t;\n    Object x;\n    t = \"hi world\";\n    x = 'Hello Object';\n    //下面代码没有问题\n    t = 1000;\n    x = 1000;\n    ```\n   \n   `dynamic`与`Object`不同的是,`dynamic`声明的对象编译器会提供所有可能的组合,\n   而`Object`声明的对象只能使用Object的属性与方法, 否则编译器会报错。如:\n   \n   ```dart\n    dynamic a;\n    Object b;\n    main() {\n        a = \"\";\n        b = \"\";\n        printLengths();\n    }   \n\n    printLengths() {\n        // no warning\n        print(a.length);\n        // warning:\n        // The getter 'length' is not defined for the class 'Object'\n        print(b.length);\n    }\n   ```\n   \n   变量a不会报错, 变量b编译器会报错\n\n   `dynamic`的这个特性与`Objective-C`中的`id`作用很像.\n   `dynamic`的这个特点使得我们在使用它时需要格外注意,这很容易引入一个运行时错误.\n   \n3. **final**和**const**\n\n   如果您从未打算更改一个变量，那么使用 `final` 或 `const`，不是`var`，也不是一个类型。 一个 `final` 变量只能被设置一次，两者区别在于：`const` 变量是一个编译时常量，`final`变量在第一次使用时被初始化。被`final`或者`const`修饰的变量，变量类型可以省略，如：\n\n   ```dart\n   //可以省略String这个类型声明\n   final str = \"hi world\";\n   //final String str = \"hi world\"; \n   const str1 = \"hi world\";\n   //const String str1 = \"hi world\";\n   ```\n\n    \n\n## 1.4.2 函数\n\nDart是一种真正的面向对象的语言，所以即使是函数也是对象，并且有一个类型**Function**。这意味着函数可以赋值给变量或作为参数传递给其他函数，这是函数式编程的典型特征。\n\n1. 函数声明\n\n   ```dart\n   bool isNoble(int atomicNumber) {\n     return _nobleGases[atomicNumber] != null;\n   }\n   ```\n\n   Dart函数声明如果没有显式声明返回值类型时会默认当做`dynamic`处理，注意，函数返回值没有类型推断：\n\n   ```dart\n   typedef bool CALLBACK();\n   \n   //不指定返回类型，此时默认为dynamic，不是bool\n   isNoble(int atomicNumber) {\n     return _nobleGases[atomicNumber] != null;\n   }\n   \n   void test(CALLBACK cb){\n      print(cb()); \n   }\n   //报错，isNoble不是bool类型\n   test(isNoble);\n   ```\n\n2. 对于只包含一个表达式的函数，可以使用简写语法\n\n   ```dart\n   bool isNoble (int atomicNumber)=> _nobleGases [ atomicNumber ] ！= null ;   \n   ```\n\n3. 函数作为变量\n\n   ```dart\n   var say = (str){\n     print(str);\n   };\n   say(\"hi world\");\n   ```\n\n4. 函数作为参数传递\n\n   ```dart\n   void execute(var callback) {\n       callback();\n   }\n   execute(() => print(\"xxx\"))\n   ```\n\n5. 可选的位置参数\n\n   包装一组函数参数，用[]标记为可选的位置参数，并放在参数列表的最后面：\n\n   ```dart\n   String say(String from, String msg, [String device]) {\n     var result = '$from says $msg';\n     if (device != null) {\n       result = '$result with a $device';\n     }\n     return result;\n   }\n   ```\n\n   下面是一个不带可选参数调用这个函数的例子：\n\n   ```dart\n   say('Bob', 'Howdy'); //结果是： Bob says Howdy\n   ```\n\n   下面是用第三个参数调用这个函数的例子：\n\n   ```dart\n   say('Bob', 'Howdy', 'smoke signal'); //结果是：Bob says Howdy with a smoke signal\n   ```\n\n6. 可选的命名参数\n\n   定义函数时，使用{param1, param2, …}，放在参数列表的最后面，用于指定命名参数。例如：\n\n   ```dart\n   //设置[bold]和[hidden]标志\n   void enableFlags({bool bold, bool hidden}) {\n       // ... \n   }\n   ```\n\n   调用函数时，可以使用指定命名参数。例如：`paramName: value`\n\n   ```dart\n   enableFlags(bold: true, hidden: false);\n   ```\n\n   可选命名参数在Flutter中使用非常多。\n   \n   **注意，不能同时使用可选的位置参数和可选的命名参数**\n\n## 1.4.3 异步支持\n\nDart类库有非常多的返回`Future`或者`Stream`对象的函数。 这些函数被称为**异步函数**：它们只会在设置好一些耗时操作之后返回，比如像 IO操作。而不是等到这个操作完成。\n\n`async`和`await`关键词支持了异步编程，允许您写出和同步代码很像的异步代码。\n\n### Future\n\n`Future`与JavaScript中的`Promise`非常相似，表示一个异步操作的最终完成（或失败）及其结果值的表示。简单来说，它就是用于处理异步操作的，异步处理成功了就执行成功的操作，异步处理失败了就捕获错误或者停止后续操作。一个Future只会对应一个结果，要么成功，要么失败。\n\n由于本身功能较多，这里我们只介绍其常用的API及特性。还有，请记住，`Future` 的所有API的返回值仍然是一个`Future`对象，所以可以很方便的进行链式调用。\n\n#### Future.then\n\n为了方便示例，在本例中我们使用`Future.delayed` 创建了一个延时任务（实际场景会是一个真正的耗时任务，比如一次网络请求），即2秒后返回结果字符串\"hi world!\"，然后我们在`then`中接收异步结果并打印结果，代码如下：\n\n```dart\nFuture.delayed(new Duration(seconds: 2),(){\n   return \"hi world!\";\n}).then((data){\n   print(data);\n});\n```\n\n#### Future.catchError\n\n如果异步任务发生错误，我们可以在`catchError`中捕获错误，我们将上面示例改为：\n\n```dart\nFuture.delayed(new Duration(seconds: 2),(){\n   //return \"hi world!\";\n   throw AssertionError(\"Error\");  \n}).then((data){\n   //执行成功会走到这里  \n   print(\"success\");\n}).catchError((e){\n   //执行失败会走到这里  \n   print(e);\n});\n```\n\n在本示例中，我们在异步任务中抛出了一个异常，`then `的回调函数将不会被执行，取而代之的是 `catchError`回调函数将被调用；但是，并不是只有 `catchError`回调才能捕获错误，`then`方法还有一个可选参数`onError`，我们也可以它来捕获异常：\n\n```dart\nFuture.delayed(new Duration(seconds: 2), () {\n\t//return \"hi world!\";\n\tthrow AssertionError(\"Error\");\n}).then((data) {\n\tprint(\"success\");\n}, onError: (e) {\n\tprint(e);\n});\n```\n\n#### Future.whenComplete\n\n有些时候，我们会遇到无论异步任务执行成功或失败都需要做一些事的场景，比如在网络请求前弹出加载对话框，在请求结束后关闭对话框。这种场景，有两种方法，第一种是分别在`then`或`catch`中关闭一下对话框，第二种就是使用`Future`的`whenComplete`回调，我们将上面示例改一下：\n\n```dart\nFuture.delayed(new Duration(seconds: 2),(){\n   //return \"hi world!\";\n   throw AssertionError(\"Error\");\n}).then((data){\n   //执行成功会走到这里 \n   print(data);\n}).catchError((e){\n   //执行失败会走到这里   \n   print(e);\n}).whenComplete((){\n   //无论成功或失败都会走到这里\n});\n```\n\n\n\n#### Future.wait\n\n有些时候，我们需要等待多个异步任务都执行结束后才进行一些操作，比如我们有一个界面，需要先分别从两个网络接口获取数据，获取成功后，我们需要将两个接口数据进行特定的处理后再显示到UI界面上，应该怎么做？答案是`Future.wait`，它接受一个`Future`数组参数，只有数组中所有`Future`都执行成功后，才会触发`then`的成功回调，只要有一个`Future`执行失败，就会触发错误回调。下面，我们通过模拟`Future.delayed` 来模拟两个数据获取的异步任务，等两个异步任务都执行成功时，将两个异步任务的结果拼接打印出来，代码如下：\n\n```dart\nFuture.wait([\n  // 2秒后返回结果  \n  Future.delayed(new Duration(seconds: 2), () {\n    return \"hello\";\n  }),\n  // 4秒后返回结果  \n  Future.delayed(new Duration(seconds: 4), () {\n    return \" world\";\n  })\n]).then((results){\n  print(results[0]+results[1]);\n}).catchError((e){\n  print(e);\n});\n```\n\n执行上面代码，4秒后你会在控制台中看到“hello world”。\n\n### Async/await\n\nDart中的`async/await` 和JavaScript中的`async/await`功能和用法是一模一样的，如果你已经了解JavaScript中的`async/await`的用法，可以直接跳过本节。\n\n#### 回调地狱(Callback Hell)\n\n如果代码中有大量异步逻辑，并且出现大量异步任务依赖其它异步任务的结果时，必然会出现`Future.then`回调中套回调情况。举个例子，比如现在有个需求场景是用户先登录，登录成功后会获得用户ID，然后通过用户ID，再去请求用户个人信息，获取到用户个人信息后，为了使用方便，我们需要将其缓存在本地文件系统，代码如下：\n\n```dart\n//先分别定义各个异步任务\nFuture<String> login(String userName, String pwd){\n\t...\n    //用户登录\n};\nFuture<String> getUserInfo(String id){\n\t...\n    //获取用户信息 \n};\nFuture saveUserInfo(String userInfo){\n\t...\n\t// 保存用户信息 \n}; \n```\n\n接下来，执行整个任务流：\n\n```dart\nlogin(\"alice\",\"******\").then((id){\n //登录成功后通过，id获取用户信息    \n getUserInfo(id).then((userInfo){\n    //获取用户信息后保存 \n    saveUserInfo(userInfo).then((){\n       //保存用户信息，接下来执行其它操作\n        ...\n    });\n  });\n})\n```\n\n可以感受一下，如果业务逻辑中有大量异步依赖的情况，将会出现上面这种在回调里面套回调的情况，过多的嵌套会导致的代码可读性下降以及出错率提高，并且非常难维护，这个问题被形象的称为**回调地狱（Callback Hell）**。回调地狱问题在之前JavaScript中非常突出，也是JavaScript被吐槽最多的点，但随着ECMAScript6和ECMAScript7标准发布后，这个问题得到了非常好的解决，而解决回调地狱的两大神器正是ECMAScript6引入了`Promise`，以及ECMAScript7中引入的`async/await`。 而在Dart中几乎是完全平移了JavaScript中的这两者：`Future`相当于`Promise`，而`async/await`连名字都没改。接下来我们看看通过`Future`和`async/await`如何消除上面示例中的嵌套问题。\n\n##### 使用Future消除Callback Hell\n\n```dart\nlogin(\"alice\",\"******\").then((id){\n  \treturn getUserInfo(id);\n}).then((userInfo){\n    return saveUserInfo(userInfo);\n}).then((e){\n   //执行接下来的操作 \n}).catchError((e){\n  //错误处理  \n  print(e);\n});\n```\n\n正如上文所述， *“`Future` 的所有API的返回值仍然是一个`Future`对象，所以可以很方便的进行链式调用”* ，如果在then中返回的是一个`Future`的话，该`future`会执行，执行结束后会触发后面的`then`回调，这样依次向下，就避免了层层嵌套。\n\n##### 使用async/await消除callback hell\n\n通过`Future`回调中再返回`Future`的方式虽然能避免层层嵌套，但是还是有一层回调，有没有一种方式能够让我们可以像写同步代码那样来执行异步任务而不使用回调的方式？答案是肯定的，这就要使用`async/await`了，下面我们先直接看代码，然后再解释，代码如下：\n\n```dart\ntask() async {\n   try{\n    String id = await login(\"alice\",\"******\");\n    String userInfo = await getUserInfo(id);\n    await saveUserInfo(userInfo);\n    //执行接下来的操作   \n   } catch(e){\n    //错误处理   \n    print(e);   \n   }  \n}\n```\n\n- `async`用来表示函数是异步的，定义的函数会返回一个`Future`对象，可以使用then方法添加回调函数。\n- `await` 后面是一个`Future`，表示等待该异步任务完成，异步完成后才会往下走；`await`必须出现在 `async` 函数内部。\n\n可以看到，我们通过`async/await`将一个异步流用同步的代码表示出来了。\n\n> 其实，无论是在JavaScript还是Dart中，`async/await`都只是一个语法糖，编译器或解释器最终都会将其转化为一个Promise（Future）的调用链。\n\n\n\n## 1.4.4 Stream\n\n`Stream` 也是用于接收异步事件数据，和`Future` 不同的是，它可以接收多个异步操作的结果（成功或失败）。 也就是说，在执行异步任务时，可以通过多次触发成功或失败事件来传递结果数据或错误异常。 `Stream` 常用于会多次读取数据的异步任务场景，如网络内容下载、文件读写等。举个例子：\n\n```dart\nStream.fromFutures([\n  // 1秒后返回结果\n  Future.delayed(new Duration(seconds: 1), () {\n    return \"hello 1\";\n  }),\n  // 抛出一个异常\n  Future.delayed(new Duration(seconds: 2),(){\n    throw AssertionError(\"Error\");\n  }),\n  // 3秒后返回结果\n  Future.delayed(new Duration(seconds: 3), () {\n    return \"hello 3\";\n  })\n]).listen((data){\n   print(data);\n}, onError: (e){\n   print(e.message);\n},onDone: (){\n\n});\n```\n\n上面的代码依次会输出：\n\n```\nI/flutter (17666): hello 1\nI/flutter (17666): Error\nI/flutter (17666): hello 3\n```\n\n代码很简单，就不赘述了。\n\n> 思考题：既然Stream可以接收多次事件，那能不能用Stream来实现一个订阅者模式的事件总线？\n\n\n\n## 1.4.5 Dart和Java及JavaScript对比\n\n通过上面介绍，相信你对Dart应该有了一个初步的印象，由于笔者平时也使用Java和JavaScript，下面笔者根据自己的经验，结合Java和JavaScript，谈一下自己的看法。\n\n> 之所以将Dart与Java和JavaScript对比，是因为，这两者分别是强类型语言和弱类型语言的典型代表，并且Dart 语法中很多地方也都借鉴了Java和JavaScript。\n\n### Dart vs Java\n\n客观的来讲，Dart在语法层面确实比Java更有表现力；在VM层面，Dart VM在内存回收和吞吐量都进行了反复的优化，但具体的性能对比，笔者没有找到相关测试数据，但在笔者看来，只要Dart语言能流行，VM的性能就不用担心，毕竟Google在Go（没用VM但有GC）、JavaScript（v8）、Dalvik（Android上的Java VM）上已经有了很多技术积淀。值得注意的是Dart在Flutter中已经可以将GC做到10ms以内，所以Dart和Java相比，决胜因素并不会是在性能方面。而在语法层面，Dart要比Java更有表现力，最重要的是Dart对函数式编程支持要远强于Java(目前只停留在Lambda表达式)，而Dart目前真正的不足是**生态**，但笔者相信，随着Flutter的逐渐火热，会回过头来反推Dart生态加速发展，对于Dart来说，现在需要的是时间。\n\n### Dart vs JavaScript\n\nJavaScript的弱类型一直被抓短，所以TypeScript、CoffeeScript甚至是Facebook的flow（虽然并不能算JavaScript的一个超集，但也通过标注和打包工具提供了静态类型检查）才有市场。就笔者使用过的脚本语言中（笔者曾使用过Python、PHP），JavaScript无疑是**动态化**支持最好的脚本语言，比如在JavaScript中，可以给任何对象在任何时候动态扩展属性，对于精通JavaScript的高手来说，这无疑是一把利剑。但是，任何事物都有两面性，JavaScript的强大的动态化特性也是把双刃剑，你可经常听到另一个声音，认为JavaScript的这种动态性糟糕透了，太过灵活反而导致代码很难预期，无法限制不被期望的修改。毕竟有些人总是对自己或别人写的代码不放心，他们希望能够让代码变得可控，并期望有一套静态类型检查系统来帮助自己减少错误。正因如此，在Flutter中，Dart几乎放弃了脚本语言动态化的特性，如不支持反射、也不支持动态创建函数等。并且Dart在2.0强制开启了类型检查（Strong Mode），原先的检查模式（checked mode）和可选类型（optional type）将淡出，所以在类型安全这个层面来说，Dart和TypeScript、CoffeeScript是差不多的，所以单从这一点来看，Dart并不具备什么明显优势，但综合起来看，Dart既能进行服务端脚本、APP开发、web开发，这就有优势了！\n\n综上所述，笔者还是很看好Dart语言的将来，之所以表这个态，是因为在新技术发展初期，很多人可能还有所摇摆，有所犹豫，所以有必要给大家打一剂强心针，当然，这是一个见仁见智的问题，大家可以各抒己见。\n\n"
  },
  {
    "path": "src/chapter1/flutter_intro.md",
    "content": "# 1.2 初识Flutter\n\n## 1.2.1 Flutter简介\n\nFlutter 是 Google推出并开源的移动应用开发框架，主打跨平台、高保真、高性能。开发者可以通过 Dart语言开发 App，一套代码同时运行在 iOS 和 Android平台。 Flutter提供了丰富的组件、接口，开发者可以很快地为 Flutter添加 native扩展。同时 Flutter还使用 Native引擎渲染视图，这无疑能为用户提供良好的体验。\n\n#### 跨平台自绘引擎\n\nFlutter与用于构建移动应用程序的其它大多数框架不同，因为Flutter既不使用WebView，也不使用操作系统的原生控件。 相反，Flutter使用自己的高性能渲染引擎来绘制widget。这样不仅可以保证在Android和iOS上UI的一致性，而且也可以避免对原生控件依赖而带来的限制及高昂的维护成本。\n\nFlutter使用Skia作为其2D渲染引擎，Skia是Google的一个2D图形处理函数库，包含字型、坐标转换，以及点阵图都有高效能且简洁的表现，Skia是跨平台的，并提供了非常友好的API，目前Google Chrome浏览器和Android均采用Skia作为其绘图引擎。\n\n目前Flutter默认支持iOS、Android、Fuchsia（Google新的自研操作系统）三个移动平台。但Flutter亦可支持Web开发（Flutter for web）和PC开发，本书的示例和介绍主要是基于iOS和Android平台的，其它平台读者可以自行了解。\n\n#### 高性能\n\nFlutter高性能主要靠两点来保证，首先，Flutter APP采用Dart语言开发。Dart在 JIT（即时编译）模式下，速度与 JavaScript基本持平。但是 Dart支持 AOT，当以 AOT模式运行时，JavaScript便远远追不上了。速度的提升对高帧率下的视图数据计算很有帮助。其次，Flutter使用自己的渲染引擎来绘制UI，布局数据等由Dart语言直接控制，所以在布局过程中不需要像RN那样要在JavaScript和Native之间通信，这在一些滑动和拖动的场景下具有明显优势，因为在滑动和拖动过程往往都会引起布局发生变化，所以JavaScript需要和Native之间不停的同步布局信息，这和在浏览器中要JavaScript频繁操作DOM所带来的问题是相同的，都会带来比较可观的性能开销。\n\n\n\n#### 采用Dart语言开发\n\n这是一个很有意思，但也很有争议的问题，在了解Flutter为什么选择了 Dart而不是 JavaScript之前我们先来介绍两个概念：JIT和AOT。\n\n目前，程序主要有两种运行方式：静态编译与动态解释。静态编译的程序在执行前全部被翻译为机器码，通常将这种类型称为**AOT** （Ahead of time）即 “提前编译”；而解释执行的则是一句一句边翻译边运行，通常将这种类型称为**JIT**（Just-in-time）即“即时编译”。AOT程序的典型代表是用C/C++开发的应用，它们必须在执行前编译成机器码，而JIT的代表则非常多，如JavaScript、python等，事实上，所有脚本语言都支持JIT模式。但需要注意的是JIT和AOT指的是程序运行方式，和编程语言并非强关联的，有些语言既可以以JIT方式运行也可以以AOT方式运行，如Java、Python，它们可以在第一次执行时编译成中间字节码、然后在之后执行时可以直接执行字节码，也许有人会说，中间字节码并非机器码，在程序执行时仍然需要动态将字节码转为机器码，是的，这没有错，不过通常我们区分是否为AOT的标准就是看代码在执行之前是否需要编译，只要需要编译，无论其编译产物是字节码还是机器码，都属于AOT。在此，读者不必纠结于概念，概念就是为了传达精神而发明的，只要读者能够理解其原理即可，得其神忘其形。\n\n现在我们看看Flutter为什么选择Dart语言？笔者根据官方解释以及自己对Flutter的理解总结了以下几条（由于其它跨平台框架都将JavaScript作为其开发语言，所以主要将Dart和JavaScript做一个对比）：\n\n1. **开发效率高**\n\n   Dart运行时和编译器支持Flutter的两个关键特性的组合：\n\n   **基于JIT的快速开发周期**：Flutter在开发阶段采用，采用JIT模式，这样就避免了每次改动都要进行编译，极大的节省了开发时间；\n\n   **基于AOT的发布包**:  Flutter在发布时可以通过AOT生成高效的ARM代码以保证应用性能。而JavaScript则不具有这个能力。\n\n2. **高性能**\n\n   Flutter旨在提供流畅、高保真的的UI体验。为了实现这一点，Flutter中需要能够在每个动画帧中运行大量的代码。这意味着需要一种既能提供高性能的语言，而不会出现会丢帧的周期性暂停，而Dart支持AOT，在这一点上可以做的比JavaScript更好。\n\n3. **快速内存分配**\n\n   Flutter框架使用函数式流，这使得它在很大程度上依赖于底层的内存分配器。因此，拥有一个能够有效地处理琐碎任务的内存分配器将显得十分重要，在缺乏此功能的语言中，Flutter将无法有效地工作。当然Chrome V8的JavaScript引擎在内存分配上也已经做的很好，事实上Dart开发团队的很多成员都是来自Chrome团队的，所以在内存分配上Dart并不能作为超越JavaScript的优势，而对于Flutter来说，它需要这样的特性，而Dart也正好满足而已。\n\n4. **类型安全**\n\n   由于Dart是类型安全的语言，支持静态类型检测，所以可以在编译前发现一些类型的错误，并排除潜在问题，这一点对于前端开发者来说可能会更具有吸引力。与之不同的，JavaScript是一个弱类型语言，也因此前端社区出现了很多给JavaScript代码添加静态类型检测的扩展语言和工具，如：微软的TypeScript以及Facebook的Flow。相比之下，Dart本身就支持静态类型，这是它的一个重要优势。\n\n5. **Dart团队就在你身边**\n\n   看似不起眼，实则举足轻重。由于有Dart团队的积极投入，Flutter团队可以获得更多、更方便的支持，正如Flutter官网所述“我们正与Dart社区进行密切合作，以改进Dart在Flutter中的使用。例如，当我们最初采用Dart时，该语言并没有提供生成原生二进制文件的工具链（这对于实现可预测的高性能具有很大的帮助），但是现在它实现了，因为Dart团队专门为Flutter构建了它。同样，Dart VM之前已经针对吞吐量进行了优化，但团队现在正在优化VM的延迟时间，这对于Flutter的工作负载更为重要。” \n\n#### 总结\n\n本节主要介绍了一下Flutter的特点，如果你感到有些点还不是很好理解，不用着急，随着日后对Flutter细节的了解，再回过头来看，相信你会有更深的体会。\n\n\n\n   \n\n## 1.2.2 Flutter框架结构\n\n本节我们先对Flutter的框架做一个整体介绍，旨在让读者心中有一个整体的印象，这对初学者来说非常重要。如果一下子便深入到Flutter中，就会像是一个在沙漠中没有地图的人，即使可以找到一个绿洲，但是他也不会知道下一个绿洲在哪。因此，无论学什么技术，都要先有一张清晰的“地图”，而我们的学习过程就是“按图索骥”，这样我们才不会陷于细节而“目无全牛”。言归正传，我们看一下Flutter官方提供的Flutter框架图，如图1-1所示：\n\n![图1-1](../imgs/1-1.png)\n\n\n\n### Flutter Framework\n\n这是一个纯 Dart实现的 SDK，它实现了一套基础库，自底向上，我们来简单介绍一下：\n\n- 底下两层（Foundation和Animation、Painting、Gestures）在Google的一些视频中被合并为一个dart UI层，对应的是Flutter中的`dart:ui`包，它是Flutter引擎暴露的底层UI库，提供动画、手势及绘制能力。\n\n- Rendering层，这一层是一个抽象的布局层，它依赖于dart UI层，Rendering层会构建一个UI树，当UI树有变化时，会计算出有变化的部分，然后更新UI树，最终将UI树绘制到屏幕上，这个过程类似于React中的虚拟DOM。Rendering层可以说是Flutter UI框架最核心的部分，它除了确定每个UI元素的位置、大小之外还要进行坐标变换、绘制(调用底层dart:ui)。\n\n- Widgets层是Flutter提供的的一套基础组件库，在基础组件库之上，Flutter还提供了 Material 和Cupertino两种视觉风格的组件库。而**我们Flutter开发的大多数场景，只是和这两层打交道**。\n\n\n### Flutter Engine\n\n这是一个纯 C++实现的 SDK，其中包括了 Skia引擎、Dart运行时、文字排版引擎等。在代码调用 `dart:ui`库时，调用最终会走到Engine层，然后实现真正的绘制逻辑。\n\n### 总结\n\nFlutter框架本身有着良好的分层设计，本节旨在让读者对Flutter整体框架有个大概的印象，相信到现在为止，读者已经对Flutter有一个初始印象，在我们正式动手之前，我们还需要了解一下Flutter的开发语言Dart。\n\n\n## 1.2.3 如何学习Flutter\n\n本节给大家一些学习建议，分享一下笔者在学习Flutter中的一些心得，希望可以帮助你提高学习效率，避免不必要的坑。\n\n### 资源\n\n- **官网**：阅读Flutter官网的资源是快速入门的最佳方式，同时官网也是了解最新Flutter发展动态的地方，由于目前Flutter仍然处于快速发展阶段，所以建议读者还是时不时的去官网看看有没有新的动态。\n\n- **源码及注释**：源码注释应作为学习Flutter的第一文档，Flutter SDK的源码是开源的，并且注释非常详细，也有很多示例，实际上，Flutter官方的SDK文档就是通过注释生成的。源码结合注释可以帮你解决大多数问题。\n- **Github**：如果遇到的问题在StackOverflow上也没有找到答案，可以去github flutter 项目下提issue。\n- **Gallery源码**：Gallery是Flutter官方示例APP，里面有丰富的示例，读者可以在网上下载安装。Gallery的源码在Flutter源码“examples”目录下。\n\n### 社区\n\n- **StackOverflow**：如果你还没听过StackOverflow，这是目前全球最大的程序员问答社区，现在也是活跃度最高的Flutter问答社区。StackOverflow上面除了世界各地的Flutter使用者会在上面交流之外，Flutter开发团队的成员也经常会在上面回答问题。\n- **Flutter中文网社区**：Flutter中文网(https://flutterchina.club)是笔者维护中文网站，目前也是最大的中文资源社区，上面提供了Flutter官网的文档翻译、开源项目、及案例，还有申请加入组织的入口哦。\n- **博客**：随着Flutter技术的推广，相信很快网上将会有很多Flutter相关的文章、博客，读者可以多去浏览、阅读。\n\n### 总结\n\n有了资料和社区后，对于我们学习者自身来说，最重要的还是要多动手、多实践，在本书后面的章节中，希望读者能够亲自动手写一下示例。准备好了吗，下一章中，我们将正式进入Flutter的世界！\n\n\n\n\n\n\n\n"
  },
  {
    "path": "src/chapter1/index.md",
    "content": "## 本章目录   \n* [移动开发技术简介](mobile_development_intro.md)\n* [Flutter简介](flutter_intro.md)  \n* [搭建Flutter开发环境](install_flutter.md)        \n* [Dart语言简介](dart.md)    \n"
  },
  {
    "path": "src/chapter1/install_flutter.md",
    "content": "# 1.3 搭建Flutter开发环境\n\n工欲善其事必先利其器，本节首先会分别介绍一下在Windows和macOS下Flutter SDK的安装，然后再介绍一下配IDE和模拟器的使用。\n\n## 1.3.1 安装Flutter\n\n由于Flutter会同时构建Android和IOS两个平台的发布包，所以Flutter同时依赖Android SDK和iOS SDK，在安装Flutter时也需要安装相应平台的构建工具和SDK。下面我们分别介绍一下Windows和macOS下的环境搭建。\n\n> 注意：本节介绍的安装方式随着Flutter的升级可能会发生变化，如果下面介绍的内容在您安装Flutter时已经失效，请访问Flutter官网，按照官网最新的安装教程安装。\n\n### 使用镜像\n\n由于在国内访问Flutter有时可能会受到限制，Flutter官方为中国开发者搭建了临时镜像，大家可以将如下环境变量加入到用户环境变量中：\n\n```\nexport PUB_HOSTED_URL=https://pub.flutter-io.cn\nexport FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn\n```\n\n**注意：** 此镜像为临时镜像，并不能保证一直可用，读者可以参考https://flutter.io/community/china 以获得有关镜像服务器的最新动态。\n\n### 在Windows上搭建Flutter开发环境\n\n#### 系统要求\n\n要安装并运行Flutter，您的开发环境必须满足以下最低要求:\n\n- 操作系统: Windows 7 或更高版本 (64-bit)\n\n- 磁盘空间: 400 MB (不包括Android Studio的磁盘空间).\n\n- 工具: Flutter 依赖下面这些命令行工具.\n\n  * [PowerShell 5.0](https://docs.microsoft.com/en-us/powershell/scripting/setup/installing-windows-powershell#upgrading-existing-windows-powershell) 或更新的版本\n\n  * [Git for Windows](https://git-scm.com/download/win)  (Git命令行工具)；\n\n  如果已安装Git for Windows，请确保可以在命令提示符或PowerShell中运行 git 命令\n\n#### 获取Flutter SDK\n\n1. 去flutter官网下载其最新可用的安装包，下载地址：https://flutter.dev/docs/development/tools/sdk/releases ，打开后如图1-2所示：\n\n   ![图1-2](../imgs/1-2.png)\n\n   \n\n   注意，Flutter的渠道版本会不停变动，请以Flutter官网为准。另外，在中国大陆地区，要想正常获取安装包列表或下载安装包，可能需要翻墙，读者也可以去Flutter github项目下去下载安装包，地址：https://github.com/flutter/flutter/releases 。\n\n2. 将安装包zip解压到你想安装Flutter SDK的路径（如：`C:\\src\\flutter`；注意，**不要**将flutter安装到需要一些高权限的路径如`C:\\Program Files\\`）。\n\n3. 在Flutter安装目录的`flutter`文件下找到`flutter_console.bat`，双击运行并启动**flutter命令行**，接下来，你就可以在Flutter命令行运行flutter命令了。\n\n##### 更新环境变量\n\n如果你想在Windows系统自带命令行运行flutter命令，需要添加以下环境变量到用户PATH：\n\n- 转到 “控制面板>用户帐户>用户帐户>更改我的环境变量”\n- 在“用户变量”下检查是否有名为“Path”的条目:\n  * 如果该条目存在， 追加 flutter\\bin的全路径，使用 ; 作为分隔符.\n  * 如果该条目不存在，创建一个新用户变量 Path ，然后将 `flutter\\bin` 的全路径作为它的值.\n\n重启Windows以应用此更改.\n\n##### 运行 flutter doctor命令\n\n在Flutter命令行运行如下命令来查看是否还需要安装其它依赖，如果需要，安装它们：\n\n```shell\nflutter doctor\n```\n\n该命令检查你的环境并在命令行窗口中显示报告。Dart SDK已经在打包在Flutter SDK里了，没有必要单独安装Dart。 仔细检查命令行输出以获取可能需要安装的其他软件或进一步需要执行的任务。\n\n例如：\n\n```\n[-] Android toolchain - develop for Android devices\n    • Android SDK at D:\\Android\\sdk\n    ✗ Android SDK is missing command line tools; download from https://goo.gl/XxQghQ\n    • Try re-installing or updating your Android SDK,\n      visit https://flutter.io/setup/#android-setup for detailed instructions.\n```\n\n第一次运行flutter命令（如`flutter doctor`）时，它会下载它自己的依赖项并自行编译。以后再运行就会快得多。缺失的依赖需要安装一下，安装完成后再运行`flutter doctor`命令来验证是否安装成功。\n\n#### Android设置\n\nFlutter依赖于Android Studio的全量安装。Android Studio不仅可以管理Android 平台依赖、SDK版本等，而且它也是Flutter开发推荐的IDE之一（当然，你也可以使用其它编辑器或IDE，我们将会在后面讨论）。\n\n##### 安装Android Studio\n\n1. 下载并安装 Android Studio，下载地址：https://developer.android.com/studio/index.html 。\n2. 启动Android Studio，然后执行“Android Studio安装向导”。这将安装最新的Android SDK、Android SDK平台工具和Android SDK构建工具，这些是用Flutter进行Android开发所需要的。\n\n#### 安装遇到问题？\n\n如果在安装过程中遇到问题，可以先去flutter官网查看一下安装方式是否发生变化，或者在网上搜索一下解决方案。\n\n\n\n### 在macOS上搭建Flutter开发环境\n\n在masOS下可以同时进行Android和iOS设备的测试。\n\n#### 系统要求\n\n要安装并运行Flutter，您的开发环境必须满足以下最低要求:\n\n- 操作系统: macOS (64-bit)\n- 磁盘空间: 700 MB (不包括Xcode或Android Studio的磁盘空间）.\n- 工具: Flutter 依赖下面这些命令行工具.\n  * `bash、mkdir、rm、git、curl、unzip、which`\n\n#### 获取Flutter SDK\n\n1. 去flutter官网下载其最新可用的安装包，官网地址：https://flutter.io/sdk-archive/#macos\n\n   注意，Flutter的渠道版本会不停变动，请以Flutter官网为准。另外，在中国大陆地区，要想正常获取安装包列表或下载安装包，可能需要翻墙，读者也可以去Flutter github项目下去下载安装包，地址：https://github.com/flutter/flutter/releases 。\n\n2. 解压安装包到你想安装的目录，如：\n\n   ```shell\n   cd ~/development\n   unzip ~/Downloads/flutter_macos_v0.5.1-beta.zip\n   ```\n\n3. 添加`flutter`相关工具到path中：\n\n   ```shell\n   export PATH=`pwd`/flutter/bin:$PATH\n   ```\n\n   此代码只能暂时针对当前命令行窗口设置PATH环境变量，要想永久将Flutter添加到PATH中请参考下面**更新环境变量** 部分。\n\n##### 运行 flutter doctor命令\n\n这一步和Windows下步骤一致，不再赘述。\n\n##### 更新环境变量\n\n将Flutter添加到PATH中，可以在任何终端会话中运行`flutter`命令。\n\n对于所有终端会话永久修改此变量的步骤是和特定计算机系统相关的。通常，您会在打开新窗口时将设置环境变量的命令添加到执行的文件中。例如\n\n1. 确定您Flutter SDK的目录记为“FLUTTER_INSTALL_PATH”，您将在步骤3中用到。\n\n2. 打开(或创建) `$HOME/.bash_profile`。文件路径和文件名可能在你的电脑上不同.\n\n3. 添加以下路径:\n\n   ```shell\n   export PATH=[FLUTTER_INSTALL_PATH]/flutter/bin:$PATH\n   ```\n   例如笔者Flutter 安装目录是“~/code/flutter_dir”，那么代码为：\n\n   ```\n   export PATH=~/code/flutter_dir/flutter/bin:$PATH\n   ```\n\n4. 运行 `source $HOME/.bash_profile` 刷新当前终端窗口。\n\n   > **注意:** 如果你使用终端是zsh，终端启动时 `~/.bash_profile` 将不会被加载，解决办法就是修改 `～/.zshrc` ，在其中添加：source ～/.bash_profile\n\n5. 验证“flutter/bin”是否已在PATH中：\n\n   ```\n   echo $PATH\n   ```\n\n\n\n#### 安装 Xcode\n\n要为iOS开发Flutter应用程序，您需要Xcode 9.0或更高版本:\n\n1. 安装Xcode 9.0或更新版本(通过[链接下载](https://developer.apple.com/xcode/)或[苹果应用商店](https://itunes.apple.com/us/app/xcode/id497799835)).\n2. 配置Xcode命令行工具以使用新安装的Xcode版本 `sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer` 对于大多数情况，当您想要使用最新版本的Xcode时，这是正确的路径。如果您需要使用不同的版本，请指定相应路径。\n3. 确保Xcode许可协议是通过打开一次Xcode或通过命令`sudo xcodebuild -license`同意过了.\n\n使用Xcode，您可以在iOS设备或模拟器上运行Flutter应用程序。\n\n#### 安装Android Studio\n\n和Window一样，要在Android设备上构建并运行Flutter程序都需要先安装Android Studio，读者可以先自行下载并安装Android Studio，在此不再赘述。\n\n### 升级 Flutter\n\n#### Flutter SDK分支\n\nFlutter SDK有多个分支，如beta、dev、master、stable，其中stable分支为稳定分支（日后有新的稳定版本发布后可能也会有新的稳定分支，如1.0.0），dev和master为开发分支，安装flutter后，你可以运行`flutter channel`查看所有分支，如笔者本地运行后，结果如下：\n\n```\nFlutter channels:\n  beta\n  dev\n* master\n```\n\n带\"*\"号的分支即你本地的Flutter SDK 跟踪的分支，要切换分支，可以使用`flutter channel beta` 或 `flutter channel master`，Flutter官方建议跟踪稳定分支，但你也可以跟踪`master`分支，这样可以查看最新的变化，但这样稳定性要低的多。\n\n#### 升级Flutter SDK和依赖包\n\n要升级flutter sdk，只需一句命令：\n\n```shell\nflutter upgrade\n```\n\n该命令会同时更新Flutter SDK和你的flutter项目依赖包。如果你只想更新项目依赖包（不包括Flutter SDK），可以使用如下命令：\n\n- `flutter packages get`获取项目所有的依赖包。\n- `flutter packages upgrade` 获取项目所有依赖包的最新版本。\n\n#### \n\n\n\n## 1.3.2 IDE配置与使用\n\n理论上可以使用任何文本编辑器与命令行工具来构建Flutter应用程序。 不过，Flutter官方建议使用Android Studio和VS Code之一以获得更好的开发体验。Flutter官方提供了这两款编辑器插件，通过IDE和插件可获得代码补全、语法高亮、widget编辑辅助、运行和调试支持等功能，可以帮助我们极大的提高开发效率。下面我们分别介绍一下Android Studio和VS Code的配置及使用（Android Studio和VS Code读者可以在其官网获得最新的安装，由于安装比较简单，故不再赘述）。\n\n### Android Studio 配置与使用\n\n由于Android Studio是基于IntelliJ IDEA开发的，所以读者也可以使用IntelliJ IDEA。\n\n#### 安装Flutter和Dart插件\n\n需要安装两个插件:\n\n- `Flutter`插件： 支持Flutter开发工作流 (运行、调试、热重载等)。\n- `Dart`插件： 提供代码分析 (输入代码时进行验证、代码补全等)。\n\n安装步骤：\n\n1. 启动Android Studio。\n2. 打开插件首选项 (macOS：**Preferences>Plugins**, Windows：**File>Settings>Plugins**)。\n3. 选择 **Browse repositories…**，选择 flutter 插件并点击 `install`。\n4. 重启Android Studio后插件生效。\n\n接下来，让我们用Android Studio创建一个Flutter项目，然后运行它，并体验“热重载”。\n\n#### 创建Flutter应用\n\n1. 选择 **File>New Flutter Project** 。\n2. 选择 **Flutter application** 作为 project 类型, 然后点击 Next。\n3. 输入项目名称 (如 `myapp`)，然后点击 Next。\n4. 点击 **Finish**。\n5. 等待Android Studio安装SDK并创建项目。\n\n上述命令创建一个Flutter项目，项目名为myapp，其中包含一个使用[Material 组件](https://material.io/guidelines/)的简单演示应用程序。\n\n在项目目录中，您应用程序的代码位于 `lib/main.dart`。\n\n#### 运行应用程序\n\n1. 定位到Android Studio工具栏，如图1-3所示：\n\n   ![图1-3](../imgs/1-3.png)\n\n2. 在 **target selector** 中, 选择一个运行该应用的Android设备。如果没有列出可用，请选择 **Tools>Android>AVD Manager** 并在那里创建一个。\n\n3. 在工具栏中点击 **Run图标**。\n\n4. 如果一切正常, 您应该在您的设备或模拟器上会看到启动的应用程序：\n\n   ![图1-4](../imgs/1-4.png)\n\n\n#### 体验热重载\n\nFlutter 可以通过 *热重载（hot reload）* 实现快速的开发周期，热重载就是无需重启应用程序就能实时加载修改后的代码，并且不会丢失状态。简单的对代码进行更改，然后告诉IDE或命令行工具你需要重新加载（点击reload按钮），你就会在你的设备或模拟器上看到更改。\n\n1. 打开`lib/main.dart`文件\n2. 将字符串\n`'You have pushed the button this many times:'` 更改为\n`'You have clicked the button this many times:'`\n3. 不要按“停止”按钮; 让您的应用继续运行.\n4. 要查更改，请调用 **Save** (`cmd-s` / `ctrl-s`)，或者点击 **热重载按钮** (带有闪电⚡️图标的按钮)。\n\n   你会立即在运行的应用程序中看到更新的字符串。\n\n### VS Code的配置与使用\n\nVS Code是一个轻量级编辑器，支持Flutter运行和调试。\n\n#### 安装flutter插件\n\n1. 启动 VS Code。\n2. 调用 **View>Command Palette…**。\n3. 输入 ‘install’, 然后选择 **Extensions: Install Extension** action。\n4. 在搜索框输入 `flutter` ，在搜索结果列表中选择 ‘Flutter’, 然后点击 **Install**。\n5. 选择 ‘OK’ 重新启动 VS Code。\n6. 验证配置\n   * 调用 **View>Command Palette…**\n   * 输入 ‘doctor’, 然后选择 **‘Flutter: Run Flutter Doctor’** action。\n   * 查看“OUTPUT”窗口中的输出是否有问题\n\n#### 创建Flutter应用\n\n1. 启动 VS Code\n2. 调用 **View>Command Palette…**\n3. 输入 ‘flutter’, 然后选择 **‘Flutter: New Project’** action\n4. 输入 Project 名称 (如`myapp`), 然后按回车键\n5. 指定放置项目的位置，然后按蓝色的确定按钮\n6. 等待项目创建继续，并显示main.dart文件\n\n#### 体验热重载\n\n1. 打开`lib/main.dart`文件。\n2. 将字符串\n   `'You have pushed the button this many times:'` 更改为\n   `'You have clicked the button this many times:'`。\n3. 不要按“停止”按钮; 让您的应用继续运行。\n4. 要查看您的更改，请调用 **Save** (`cmd-s` / `ctrl-s`), 或者点击 **热重载按钮** (绿色圆形箭头按钮)。\n\n你会立即在运行的应用程序中看到更新的字符串。\n\n\n\n## 1.3.3 连接设备运行Flutter应用\n\nWindow下只支持为Android设备构建并运行Flutter应用，而macOS同时支持iOS和Android设备。下面分别介绍如何连接Android和iOS设备来运行flutter应用。\n\n### 连接Android模拟器\n\n要准备在Android模拟器上运行并测试Flutter应用，请按照以下步骤操作：\n\n1. 启动 **Android Studio>Tools>Android>AVD Manager** 并选择 **Create Virtual Device**.\n\n2. 选择一个设备并选择 **Next**。\n\n3. 为要模拟的Android版本选择一个或多个系统印象，然后选择 **Next**. 建议使用 *x86* 或 *x86_64* image .\n\n4. 在 “Emulated Performance”下, 选择 **Hardware - GLES 2.0** 以启用 [硬件加速](https://developer.android.com/studio/run/emulator-acceleration.html).\n\n5. 验证AVD配置是否正确，然后选择 **Finish**。\n\n   有关上述步骤的详细信息，请参阅 [Managing AVDs](https://developer.android.com/studio/run/managing-avds.html).\n\n6. 在“Android Virtual Device Manager”中，点击工具栏的 **Run**。模拟器启动并显示所选操作系统版本或设备的启动画面。\n\n7. 运行 `flutter run` 启动您的设备。 连接的设备名是 `Android SDK built for <platform>`，其中 *platform* 是芯片系列，如 x86。\n\n### 连接Android真机设备\n\n要准备在Android设备上运行并测试Flutter应用，需要Android 4.1（API level 16）或更高版本的Android设备.\n\n1. 在Android设备上启用 **开发人员选项** 和 **USB调试** 。详细说明可在[Android文档](https://developer.android.com/studio/debug/dev-options.html)中找到。\n2. 使用USB将手机插入电脑。如果设备出现调试授权提示，请授权你的电脑可以访问该设备。\n3. 在命令行运行 `flutter devices` 命令以验证Flutter识别您连接的Android设备。\n4. 运行启动你应用程序 `flutter run`。\n\n默认情况下，Flutter使用的Android SDK版本是基于你的 `adb` 工具版本。 如果想让Flutter使用不同版本的Android SDK，则必须将该 `ANDROID_HOME` 环境变量设置为相应的SDK安装目录。\n\n### 连接iOS模拟器\n\n要准备在iOS模拟器上运行并测试Flutter应用，请按以下步骤操作：\n\n1. 在你的MAC上，通过 Spotlight 或以下命令找到模拟器：\n\n   ```shell\n   open -a Simulator\n   ```\n\n2. 通过检查模拟器 **Hardware > Device** 菜单中的设置，确保模拟器正在使用64位设备（iPhone 5s或更高版本）。\n\n3. 根据你电脑屏幕大小，模拟高清屏iOS设备可能会溢出屏幕。可以在模拟器的 **Window> Scale** 菜单下设置设备比例。\n\n4. 运行 `flutter run`启动flutter应用程序。\n\n### 连接iOS真机设备\n\n要将Flutter应用安装到iOS真机设备，需要一些额外的工具和一个Apple帐户，还需要在Xcode中进行一些设置。\n\n1. 安装 [homebrew](http://brew.sh/) （如果已经安装了brew,跳过此步骤）。\n\n2. 打开终端并运行如下这些命令:\n\n   ```shell\n   brew update\n   brew install --HEAD libimobiledevice\n   brew install ideviceinstaller ios-deploy cocoapods\n   pod setup\n   ```\n\n   如果这些命令中的任何一个失败并出现错误，请运行brew doctor并按照说明解决问题.\n\n3. 遵循Xcode签名流程来配置您的项目:\n\n   * 在你Flutter项目目录中通过 `open ios/Runner.xcworkspace` 打开默认的Xcode workspace.\n\n   * 在Xcode中，选择导航面板左侧中的`Runner`项目。\n\n   * 在`Runner` target设置页面中，确保在 **General > Signing > Team** 下选择了你的开发团队。当你选择一个团队时，Xcode会创建并下载开发证书，向你的设备注册你的帐户，并创建和下载配置文件（如果需要）。\n\n   * 要开始您的第一个iOS开发项目，您可能需要使用您的Apple ID登录Xcode，如图1-5：\n\n     ![图1-5](../imgs/1-5.png)\n\n     任何Apple ID都支持开发和测试，但若想将应用分发到App Store，就必须注册Apple开发者计划，有关详情读者可以自行了解。\n\n   4. 当您第一次attach真机设备进行iOS开发时，需要同时信任你的Mac和该设备上的开发证书。首次将iOS设备连接到Mac时，请在对话框中选择 `Trust`。 \n\n      ![添加信任](../imgs/1-6.png)\n\n      然后，转到iOS设备上的**设置**菜单，选择 **常规>设备管理** 并信任您的证书。\n\n   5. 如果Xcode中的自动签名失败，请验证项目的 **General > Identity > Bundle Identifier** 值是否唯一，如图1-7所示：\n\n      ![验证bundle id是否唯一](../imgs/1-7.png)\n\n   6. 运行 `flutter run`启动flutter应用程序。\n   \n\n## 1.3.4 常见配置问题\n\n### Android Studio问题\n\n#### 缺少依赖库问题\n\n上手安卓最常遇见的问题之一，错误如图1-8所示，此时点击超链接即可自动跳转到安装页面\n\n![图1-8](../imgs/1-8.png)\n\n安装之后重新运行即可，如图1-9：\n\n![install_request_components.png](../imgs/1-9.png)\n\n#### 连接不上Android Repository\n\n这也是最常见的问题之一，当你发现自己无法下载部分依赖的时候，请优先考虑这种情况。进入 `File` -> `Settings` -> `Appearance & Behavior` -> `System Settings` -> `Android SDK` -> `SDK Update Sites` 列表，可以看到此时的 `Android Repository` 无法连接，如图1-10所示：\n\n![下载依赖失败](../imgs/1-10.png)\n\n这是由于要去Google下载Android SDK，但在国内目前无法访问Google所致，因此，我们可以配置代理或使用vpn。\n\n#### 安卓包配置问题\n\n一般格式为\n\n```\nCould not HEAD **\nCould not Get **\n```\n\n如：`Android Studio Could not GET gradle-3.2.0.pom`\n\n这一类问题是由于无法连接到 Maven 库造成的，解决方法如下：\n\n1. 进入`当前所在项目名/android`\n\n2. 打开 `build.gradle` \n\n3. 找到下面这一部分，并加上 `maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }` \n\n   ```\n   allprojects {\n       repositories {\n         google()\n         jcenter()\n         maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' } //添加这一句\n   \t}\n   }\n   ```\n\n4. 进入 File/ Settings/ Build, Execution, Deployment/ BuildTools/ Gradle/ Android Studio 中，勾选上 Enable embedded Maven repository ，重启 Android Studio 即可解决。\n\n   > **注意：**存在这样的一种情况，当你根据上述步骤设置了之后，依旧无法解决这个问题，并有类似于 `Could not HEAD maven.aliyun.com` 的报错信息，请检查 `C:\\Users\\{user_name}\\.gradle\\gradle.properties` 是否有设置代理。删除后问题即可解决。\n\n#### Hot Reload 热重载失效问题\n\n在给 `Terminal` 之类的终端模拟器设置代理之后，会导致“Hot Reload”重载失效，此时调用 **Save** (`cmd-s` / `ctrl-s`)将不会进行热重载，**热重载按钮** (带有闪电⚡️图标的按钮)也不会显示，将代理移除即可解决。\n\n另外，有些情况下热重载是不生效的，比如修改了`main`函数、修改了全局静态方法等，读者可以认为“Hot Reload”只会重新构建整个widget树，如果变动不在构建widget树的过程中，“Hot Reload”就不会起作用。\n\n\n\n\n\n\n"
  },
  {
    "path": "src/chapter1/mobile_development_intro.md",
    "content": "\n\n# 1.1 移动开发技术简介\n\n本节将主要介绍一下移动开发技术的进化历程，主要是想让读者知道Flutter技术出现的背景。笔者认为，了解一门新技术出现的背景是非常重要的，因为只有了解之前是什么样的，才能理解为什么会是现在这样。\n\n## 1.1.1 原生开发与跨平台技术\n\n### 原生开发\n\n原生应用程序是指某一个移动平台（比如iOS或安卓）所特有的应用，使用相应平台支持的开发工具和语言，并直接调用系统提供的SDK API。比如Android原生应用就是指使用Java或Kotlin语言直接调用Android SDK开发的应用程序；而iOS原生应用就是指通过Objective-C或Swift语言直接调用iOS SDK开发的应用程序。原生开发有以下主要优势：\n\n- 可访问平台全部功能（GPS、摄像头）；\n- 速度快、性能高、可以实现复杂动画及绘制，整体用户体验好；\n\n主要缺点：\n\n- 平台特定，开发成本高；不同平台必须维护不同代码，人力成本随之变大；\n- 内容固定，动态化弱，大多数情况下，有新功能更新时只能发版；\n\n在移动互联网发展初期，业务场景并不复杂，原生开发还可以应对产品需求迭代。 但近几年，随着物联网时代到来、移动互联网高歌猛进，日新月异，在很多业务场景中，传统的纯原生开发已经不能满足日益增长的业务需求。主要表现在：\n\n- 动态化内容需求增大；当需求发生变化时，纯原生应用需要通过版本升级来更新内容，但应用上架、审核是需要周期的，这对高速变化的互联网时代来说是很难接受的，所以，对应用动态化(不发版也可以更新应用内容)的需求就变的迫在眉睫。\n- 业务需求变化快，开发成本变大；由于原生开发一般都要维护Android、iOS两个开发团队，版本迭代时，无论人力成本，还是测试成本都会变大。\n\n总结一下，纯原生开发主要面临动态化和开发成本两个问题，而针对这两个问题，诞生了一些跨平台的动态化框架。\n\n\n\n### 跨平台技术简介\n\n针对原生开发面临问题，人们一直都在努力寻找好的解决方案，而时至今日，已经有很多跨平台框架(注意，本书中所指的“跨平台”若无特殊说明，即特指Android和iOS两个平台)，根据其原理，主要分为三类：\n\n- H5+原生（Cordova、Ionic、微信小程序）\n- JavaScript开发+原生渲染 （React Native、Weex、快应用）\n- 自绘UI+原生(QT for mobile、Flutter)\n\n在接下来的章节中我们逐个来看看这三类框架的原理及优缺点。\n\n\n\n## 1.1.2 Hybrid技术简介\n\n### H5+原生混合开发\n\n这类框架主要原理就是将APP的一部分需要动态变动的内容通过H5来实现，通过原生的网页加载控件WebView (Android)或WKWebView（iOS）来加载（以后若无特殊说明，我们用WebView来统一指代android和iOS中的网页加载控件）。这样以来，H5部分是可以随时改变而不用发版，动态化需求能满足；同时，由于h5代码只需要一次开发，就能同时在Android和iOS两个平台运行，这也可以减小开发成本，也就是说，H5部分功能越多，开发成本就越小。我们称这种h5+原生的开发模式为**混合开发 ** ，采用混合模式开发的APP我们称之为**混合应用**或**Hybrid APP**  ，如果一个应用的大多数功能都是H5实现的话，我们称其为**Web APP** 。\n\n目前混合开发框架的典型代表有：Cordova、Ionic 和微信小程序，值得一提的是微信小程序目前是在webview中渲染的，并非原生渲染，但将来有可能会采用原生渲染。\n\n### 混合开发技术点 \n\n如之前所述，原生开发可以访问平台所有功能，而混合开发中，H5代码是运行在WebView中，而WebView实质上就是一个浏览器内核，其JavaScript依然运行在一个权限受限的沙箱中，所以对于大多数系统能力都没有访问权限，如无法访问文件系统、不能使用蓝牙等。所以，对于H5不能实现的功能，都需要原生去做。而混合框架一般都会在原生代码中预先实现一些访问系统能力的API， 然后暴露给WebView以供JavaScript调用，这样一来，WebView就成为了JavaScript与原生API之间通信的桥梁，主要负责JavaScript与原生之间传递调用消息，而消息的传递必须遵守一个标准的协议，它规定了消息的格式与含义，我们把依赖于WebView的用于在JavaScript与原生之间通信并实现了某种消息传输协议的工具称之为**WebView JavaScript Bridge**, 简称 **JsBridge**，它也是混合开发框架的核心。\n\n#### 示例：JavaScript调用原生API获取手机型号\n\n下面我们以Android为例，实现一个获取手机型号的原生API供JavaScript调用。在这个示例中将展示JavaScript调用原生API的流程，读者可以直观的感受一下调用流程。我们选用笔者在Github上开源的dsBridge作为JsBridge来进行通信。dsBridge是一个支持同步调用的跨平台的JsBridge，此示例中只使用其同步调用功能。\n\n1. 首先在原生中实现获取手机型号的API `getPhoneModel`\n\n   ```java\n   class JSAPI {\n    @JavascriptInterface\n    public Object getPhoneModel(Object msg) {\n      return Build.MODEL;\n    }\n   }    \n   ```\n\n2. 将原生API通过WebView注册到JsBridge中\n\n   ```java\n   import wendu.dsbridge.DWebView\n   ...\n   //DWebView继承自WebView，由dsBridge提供  \n   DWebView dwebView = (DWebView) findViewById(R.id.dwebview);\n   //注册原生API到JsBridge\n   dwebView.addJavascriptObject(new JsAPI(), null);\n   ```\n\n3. 在JavaScript中调用原生API\n\n   ```javascript\n   var dsBridge = require(\"dsbridge\")\n   //直接调用原生API `getPhoneModel`\n   var model = dsBridge.call(\"getPhoneModel\");\n   //打印机型\n   console.log(model);\n   ```\n\n上面示例演示了JavaScript调用原生API的过程，同样的，一般来说优秀的JsBridge也支持原生调用JavaScript，dsBridge也是支持的，如果您感兴趣，可以去github dsBridge项目主页查看。\n\n现在，我们回头来看一下，混合应用无非就是在第一步中预先实现一系列API供JavaScript调用，让JavaScript有访问系统的能力，看到这里，我相信你也可以自己实现一个混合开发框架了。\n\n### 总结\n\n混合应用的优点是动态内容是H5，web技术栈，社区及资源丰富，缺点是性能不好，对于复杂用户界面或动画，WebView不堪重任。\n\n\n\n## 1.1.3 React Native、Weex及快应用\n\n本篇主要介绍一下 **JavaScript开发+原生渲染**的跨平台框架原理。\n\nReact Native (简称RN)是Facebook于2015年4月开源的跨平台移动应用开发框架，是Facebook早先开源的JS框架 React 在原生移动应用平台的衍生产物，目前支持iOS和Android两个平台。RN使用Javascript语言，类似于HTML的JSX，以及CSS来开发移动应用，因此熟悉Web前端开发的技术人员只需很少的学习就可以进入移动应用开发领域。\n\n由于RN和React原理相通，并且Flutter也是受React启发，很多思想也都是相通的，万丈高楼平地起，我们有必要深入了解一下React原理。React是一个响应式的Web框架，我们先了解一下两个重要的概念：DOM树与响应式编程。\n\n### DOM树与控件树\n\n文档对象模型（Document Object Model，简称DOM），是W3C组织推荐的处理可扩展标志语言的标准编程接口，一种独立于平台和语言的方式访问和修改一个文档的内容和结构。换句话说，这是表示和处理一个HTML或XML文档的标准接口。简单来说，DOM就是文档树，与用户界面控件树对应，在前端开发中通常指HTML对应的渲染树，但广义的DOM也可以指Android中的XML布局文件对应的控件树，而术语**DOM操作**就是指直接来操作渲染树（或控件树）， 因此，可以看到其实DOM树和控件树是等价的概念，只不过前者常用于Web开发中，而后者常用于原生开发中。\n\n### 响应式编程\n\nReact中提出一个重要思想：状态改变则UI随之自动改变，而React框架本身就是响应用户状态改变的事件而执行重新构建用户界面的工作，这就是典型的**响应式**编程范式，下面我们总结一下React中响应式原理：\n\n- 开发者只需关注状态转移（数据），当状态发生变化，React框架会自动根据新的状态重新构建UI。\n- React框架在接收到用户状态改变通知后，会根据当前渲染树，结合最新的状态改变，通过Diff算法，计算出树中变化的部分，然后只更新变化的部分（DOM操作），从而避免整棵树重构，提高性能。\n\n值得注意的是，在第二步中，状态变化后React框架并不会立即去计算并渲染DOM树的变化部分，相反，React会在DOM的基础上建立一个抽象层，即**虚拟DOM**树，对数据和状态所做的任何改动，都会被自动且高效的同步到虚拟DOM，最后再批量同步到真实DOM中，而不是每次改变都去操作一下DOM。为什么不能每次改变都直接去操作DOM树？这是因为在浏览器中每一次DOM操作都有可能引起浏览器的重绘或回流：\n\n1. 如果DOM只是外观风格发生变化，如颜色变化，会导致浏览器重绘界面。\n2. 如果DOM树的结构发生变化，如尺寸、布局、节点隐藏等导致，浏览器就需要回流（及重新排版布局）。\n\n而浏览器的重绘和回流都是比较昂贵的操作，如果每一次改变都直接对DOM进行操作，这会带来性能问题，而批量操作只会触发一次DOM更新。\n\n> 思考题：Diff操作和DOM批量更新难道不应该是浏览器的职责吗？第三方框架中去做合不合适？\n\n\n\n> 此处需要有一张插图\n\n\n\n### React Native\n\n上文已经提到React Native 是React 在原生移动应用平台的衍生产物，那两者主要的区别是什么呢？其实，主要的区别在于虚拟DOM映射的对象是什么？React中虚拟DOM最终会映射为浏览器DOM树，而RN中虚拟DOM会通过 JavaScriptCore 映射为原生控件树。\n\nJavaScriptCore 是一个JavaScript解释器，它在React Native中主要有两个作用：\n\n1. 为JavaScript提供运行环境。\n2. 是JavaScript与原生应用之间通信的桥梁，作用和JsBridge一样，事实上，在iOS中，很多JsBridge的实现都是基于 JavaScriptCore 。\n\n而RN中将虚拟DOM映射为原生控件的过程中分两步：\n\n1. 布局消息传递； 将虚拟DOM布局信息传递给原生；\n2. 原生根据布局信息通过对应的原生控件渲染控件树；\n\n至此，React Native 便实现了跨平台。 相对于混合应用，由于React Native是原生控件渲染，所以性能会比混合应用中H5好很多，同时React Native使用了Web开发技术栈，也只需维护一份代码，同样是跨平台框架。\n\n### Weex\n\nWeex是阿里巴巴于2016年发布的跨平台移动端开发框架，思想及原理和React Native类似，最大的不同是语法层面，Weex支持Vue语法和Rax语法，Rax 的 DSL(Domain Specific Language) 语法是基于 React JSX 语法而创造。与 React 不同，在 Rax 中 JSX 是必选的，它不支持通过其它方式创建组件，所以学习 JSX 是使用 Rax 的必要基础。而React Native只支持JSX语法。\n\n### 快应用\n\n快应用是华为、小米、OPPO、魅族等国内9大主流手机厂商共同制定的轻量级应用标准，目标直指微信小程序。它也是采用JavaScript语言开发，原生控件渲染，与React Native和Weex相比主要有两点不同：\n\n1. 快应用自身不支持Vue或React语法，其采用原生JavaScript开发，其开发框架和微信小程序很像，值得一提的是小程序目前已经可以使用Vue语法开发（mpvue），从原理上来讲，Vue的语法也可以移植到快应用上。\n2. React Native和Weex的渲染/排版引擎是集成到框架中的，每一个APP都需要打包一份，安装包体积较大；而快应用渲染/排版引擎是集成到ROM中的，应用中无需打包，安装包体积小，正因如此，快应用才能在保证性能的同时做到快速分发。\n\n### 总结\n\nJavaScript开发+原生渲染的方式主要优点如下：\n\n1. 采用Web开发技术栈，社区庞大、上手快、开发成本相对较低。\n2. 原生渲染，性能相比H5提高很多。\n3. 动态化较好，支持热更新。\n\n不足：\n\n1. 渲染时需要JavaScript和原生之间通信，在有些场景如拖动可能会因为通信频繁导致卡顿。\n2. JavaScript为脚本语言，执行时需要JIT(Just In Time)，执行效率和AOT(Ahead Of Time)代码仍有差距。\n3. 由于渲染依赖原生控件，不同平台的控件需要单独维护，并且当系统更新时，社区控件可能会滞后；除此之外，其控件系统也会受到原生UI系统限制，例如，在Android中，手势冲突消歧规则是固定的，这在使用不同人写的控件嵌套时，手势冲突问题将会变得非常棘手。\n\n\n\n## 1.1.4 QT Mobile\n\n在本篇中，我们看看最后一种跨平台技术：自绘UI+原生。这种技术的思路是，通过在不同平台实现一个统一接口的渲染引擎来绘制UI，而不依赖系统原生控件，所以可以做到不同平台UI的一致性。注意，自绘引擎解决的是UI的跨平台问题，如果涉及其它系统能力调用，依然要涉及原生开发。这种平台技术的优点如下：\n\n1. 性能高；由于自绘引擎是直接调用系统API来绘制UI，所以性能和原生控件接近。\n\n2. 灵活、组件库易维护、UI外观保真度和一致性高；由于UI渲染不依赖原生控件，也就不需要根据不同平台的控件单独维护一套组件库，所以代码容易维护。由于组件库是同一套代码、同一个渲染引擎，所以在不同平台，组件显示外观可以做到高保真和高一致性；另外，由于不依赖原生控件，也就不会受原生布局系统的限制，这样布局系统会非常灵活。\n\n不足：\n\n1. 动态性不足；为了保证UI绘制性能，自绘UI系统一般都会采用AOT模式编译其发布包，所以应用发布后，不能像Hybrid和RN那些使用JavaScript（JIT）作为开发语言的框架那样动态下发代码。\n2. 开发效率低：QT使用C++作为其开发语言，而编程效率是直接会影响APP开发效率的，C++作为一门静态语言，在UI开发方面灵活性不及JavaScript这样的动态语言，另外，C++需要开发者手动去管理内存分配，没有JavaScript及Java中垃圾回收（GC）的机制。\n\n\n\n\n也许你已经猜到Flutter就属于这一类跨平台技术，没错，Flutter正是实现一套自绘引擎，并拥有一套自己的UI布局系统。不过，自绘制引擎的思路并不是什么新概念，Flutter并不是第一个尝试这么做的，在它之前有一个典型的代表，即大名鼎鼎的QT。\n\n### QT简介\n\nQt是一个1991年由Qt Company开发的跨平台C++图形用户界面应用程序开发框架。2008年，Qt Company科技被诺基亚公司收购，Qt也因此成为诺基亚旗下的编程语言工具。2012年，Qt被Digia收购。2014年4月，跨平台集成开发环境Qt Creator 3.1.0正式发布，实现了对于iOS的完全支持，新增WinRT、Beautifier等插件，废弃了无Python接口的GDB调试支持，集成了基于Clang的C/C++代码模块，并对Android支持做出了调整，至此实现了全面支持iOS、Android、WP，它提供给应用程序开发者构建图形用户界面所需的所有功能。但是，QT虽然在PC端获得了巨大成功，备受社区追捧，然而其在移动端却表现不佳，在近几年，虽然偶尔能听到QT的声音，但一直很弱，无论QT本身技术如何、设计思想如何，但事实上终究是败了，究其原因，笔者认为主要有四：\n\n第一：QT移动开发社区太小，学习资料不足，生态不好。\n\n第二：官方推广不利，支持不够。\n\n第三：移动端发力较晚，市场已被其它动态化框架占领（Hybrid和RN)。\n\n第四：在移动开发中，C++开发和Web开发栈相比有着先天的劣势，直接结果就是QT开发效率太低。\n\n基于此四点，尽管QT是移动端开发跨平台自绘引擎的先驱，但却成为了烈士。\n\n## 1.1.5 Flutter出世\n\n“千呼万唤始出来”，铺垫这么久，现在终于等到本书的主角出场了！\n\nFlutter是Google发布的一个用于创建跨平台、高性能移动应用的框架。Flutter和QT mobile一样，都没有使用原生控件，相反都实现了一个自绘引擎，使用自身的布局、绘制系统。那么，我们会担心，QT mobile面对的问题Flutter是否也一样，Flutter会不会步入QT mobile后尘，成为另一个烈士？要回到这个问题，我们先来看看Flutter诞生过程：\n\n- 2017 年 Google I/O 大会上，Google 首次推出了一款新的用于创建跨平台、高性能的移动应用框架——Flutter。\n- 2018年2月，Flutter发布了第一个Beta版本，同年五月， 在2018年Google I/O 大会上，Flutter 更新到了 beta 3 版本。\n- 2018年6月，Flutter发布了首个预览版本，这意味着 Flutter 进入了正式版（1.0）发布前的最后阶段。\n\n观其发展，在2018年5月份，Flutter 进入了 GitHub stars 排行榜前 100 名，已有 27k  star。而今天(2019年5月29日)，已经有65K的Star。经历了短短2年多的时间，Flutter 生态系统得以快速增长，由此可见，Flutter在开发者中受到了热烈的欢迎，其未来发展值得期待！\n\n现在，我们来和QT mobile做一个对比：\n\n1. 生态：从Github上来看，目前Flutter活跃用户正在高速增长。从Stackoverflow上提问来看，Flutter社区现在已经很庞大。Flutter的文档、资源也越来越丰富，开发过程中遇到的很多问题都可以在Stackoverflow或其github issue中找到答案。\n2. 技术支持：现在Google正在大力推广Flutter，Flutter的作者中很多人都是来自Chromium团队，并且github上活跃度很高。另一个角度，从今年上半年Flutter频繁的版本发布也可以看出Google对Flutter的投入的资源不小，所以在官方技术支持这方面，大可不必担心。\n3. 开发效率：Flutter的热重载可帮助开发者快速地进行测试、构建UI、添加功能并更快地修复错误。在iOS和Android模拟器或真机上可以实现毫秒级热重载，并且不会丢失状态。这真的很棒，相信我，如果你是一名原生开发者，体验了Flutter开发流后，很可能就不想重新回去做原生了，毕竟很少有人不吐槽原生开发的编译速度。\n\n基于以上三点，相信读者和笔者一样，Flutter未来如何，心中自有定论。到现在为止，我们已经对移动端开发技术有了一个全面的了解，接下来我们便要进入本书的主题，你准备好了吗！\n\n\n\n## 1.1.6 小结\n\n本章主要介绍了目前移动开发中三种跨平台技术，现在我们从框架角度对比一下它们，如表1-1所示：\n\n| 技术类型            | UI渲染方式      | 性能 | 开发效率 | 动态化     | 框架代表       |\n| ------------------- | --------------- | ---- | -------- | ---------- | -------------- |\n| H5+原生             | WebView渲染     | 一般 | 高       | 支持        | Cordova、Ionic |\n| JavaScript+原生渲染 | 原生控件渲染    | 好   | 中    | 支持        | RN、Weex       |\n| 自绘UI+原生         | 调用系统API渲染 | 好   | Flutter高, QT低       | 默认不支持 | QT、Flutter    |\n\n<center>表1-1: 跨平台技术对比</center>\n上表中开发语言主要指UI的开发语言。而开发效率，是指整个开发周期的效率，包括编码时间、调试时间、以及排错、兼容时间。动态化主要指是否支持动态下发代码和是否支持热更新。值得注意的是Flutter的Release包默认是使用Dart AOT模式编译的，所以不支持动态化，但Dart还有JIT或snapshot运行方式，这些模式都是支持动态化的。\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n  \n\n\n"
  },
  {
    "path": "src/chapter10/combine.md",
    "content": "# 10.2 组合现有组件\n\n在Flutter中页面UI通常都是由一些低阶别的组件组合而成，当我们需要封装一些通用组件时，应该首先考虑是否可以通过组合其它组件来实现，如果可以，则应优先使用组合，因为直接通过现有组件拼装会非常简单、灵活、高效。\n\n### 示例：自定义渐变按钮\n\nFlutter Material组件库中的按钮默认不支持渐变背景，为了实现渐变背景按钮，我们自定义一个`GradientButton `组件，它需要支持一下功能：\n\n1. 背景支持渐变色\n2. 手指按下时有涟漪效果\n3. 可以支持圆角\n\n我们先来看看最终要实现的效果（图10-1）：\n\n![gradient-button](../imgs/10-1.png)\n\n我们`DecoratedBox`可以支持背景色渐变和圆角，`InkWell`在手指按下有涟漪效果，所以我们可以通过组合`DecoratedBox`和`InkWell`来实现`GradientButton`，代码如下：\n\n```dart\nimport 'package:flutter/material.dart';\n\nclass GradientButton extends StatelessWidget {\n  GradientButton({\n    this.colors,\n    this.width,\n    this.height,\n    this.onPressed,\n    this.borderRadius,\n    @required this.child,\n  });\n\n  // 渐变色数组\n  final List<Color> colors;\n\n  // 按钮宽高\n  final double width;\n  final double height;\n\n  final Widget child;\n  final BorderRadius borderRadius;\n\n  //点击回调\n  final GestureTapCallback onPressed;\n\n  @override\n  Widget build(BuildContext context) {\n    ThemeData theme = Theme.of(context);\n\n    //确保colors数组不空\n    List<Color> _colors = colors ??\n        [theme.primaryColor, theme.primaryColorDark ?? theme.primaryColor];\n\n    return DecoratedBox(\n      decoration: BoxDecoration(\n        gradient: LinearGradient(colors: _colors),\n        borderRadius: borderRadius,\n      ),\n      child: Material(\n        type: MaterialType.transparency,\n        child: InkWell(\n          splashColor: _colors.last,\n          highlightColor: Colors.transparent,\n          borderRadius: borderRadius,\n          onTap: onPressed,\n          child: ConstrainedBox(\n            constraints: BoxConstraints.tightFor(height: height, width: width),\n            child: Center(\n              child: Padding(\n                padding: const EdgeInsets.all(8.0),\n                child: DefaultTextStyle(\n                  style: TextStyle(fontWeight: FontWeight.bold),\n                  child: child,\n                ),\n              ),\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n```\n\n可以看到`GradientButton`是由`DecoratedBox`、`Padding`、`Center`、`InkWell`等组件组合而成。当然上面的代码只是一个示例，作为一个按钮它还并不完整，比如没有禁用状态，读者可以根据实际需要来完善。\n\n#### 使用GradientButton\n\n```dart\nimport 'package:flutter/material.dart';\nimport '../widgets/index.dart';\n\nclass GradientButtonRoute extends StatefulWidget {\n  @override\n  _GradientButtonRouteState createState() => _GradientButtonRouteState();\n}\n\nclass _GradientButtonRouteState extends State<GradientButtonRoute> {\n  @override\n  Widget build(BuildContext context) {\n    return Container(\n      child: Column(\n        children: <Widget>[\n          GradientButton(\n            colors: [Colors.orange, Colors.red],\n            height: 50.0,\n            child: Text(\"Submit\"),\n            onPressed: onTap,\n          ),\n          GradientButton(\n            height: 50.0,\n            colors: [Colors.lightGreen, Colors.green[700]],\n            child: Text(\"Submit\"),\n            onPressed: onTap,\n          ),\n          GradientButton(\n            height: 50.0,\n            colors: [Colors.lightBlue[300], Colors.blueAccent],\n            child: Text(\"Submit\"),\n            onPressed: onTap,\n          ),\n        ],\n      ),\n    );\n  }\n  onTap() {\n    print(\"button click\");\n  }\n}\n```\n\n### 总结\n\n通过组合的方式定义组件和我们之前写界面并无差异，不过在抽离出单独的组件时我们要考虑代码规范性，如必要参数要用`@required` 标注，对于可选参数在特定场景需要判空或设置默认值等。这是由于使用者大多时候可能不了解组件的内部细节，所以为了保证代码健壮性，我们需要在用户错误地使用组件时能够兼容或报错提示（使用`assert`断言函数）。\n"
  },
  {
    "path": "src/chapter10/custom_paint.md",
    "content": "# 10.4 自绘组件 （CustomPaint与Canvas）\n\n对于一些复杂或不规则的UI，我们可能无法通过组合其它组件的方式来实现，比如我们需要一个正六边形、一个渐变的圆形进度条、一个棋盘等。当然，有时候我们可以使用图片来实现，但在一些需要动态交互的场景静态图片也是实现不了的，比如要实现一个手写输入面板，这时，我们就需要来自己绘制UI外观。\n\n几乎所有的UI系统都会提供一个自绘UI的接口，这个接口通常会提供一块2D画布`Canvas`，`Canvas`内部封装了一些基本绘制的API，开发者可以通过`Canvas`绘制各种自定义图形。在Flutter中，提供了一个`CustomPaint` 组件，它可以结合画笔`CustomPainter`来实现自定义图形绘制。\n\n### CustomPaint\n\n我们看看`CustomPaint`构造函数：\n\n```dart\nCustomPaint({\n  Key key,\n  this.painter, \n  this.foregroundPainter,\n  this.size = Size.zero, \n  this.isComplex = false, \n  this.willChange = false, \n  Widget child, //子节点，可以为空\n})\n```\n\n- `painter`: 背景画笔，会显示在子节点后面;\n- `foregroundPainter`: 前景画笔，会显示在子节点前面\n- `size`：当child为null时，代表默认绘制区域大小，如果有child则忽略此参数，画布尺寸则为child尺寸。如果有child但是想指定画布为特定大小，可以使用SizeBox包裹CustomPaint实现。\n- `isComplex`：是否复杂的绘制，如果是，Flutter会应用一些缓存策略来减少重复渲染的开销。\n- `willChange`：和`isComplex`配合使用，当启用缓存时，该属性代表在下一帧中绘制是否会改变。\n\n可以看到，绘制时我们需要提供前景或背景画笔，两者也可以同时提供。我们的画笔需要继承`CustomPainter`类，我们在画笔类中实现真正的绘制逻辑。\n\n#### 注意\n\n如果`CustomPaint`有子节点，为了避免子节点不必要的重绘并提高性能，通常情况下都会将子节点包裹在`RepaintBoundary `组件中，这样会在绘制时就会创建一个新的绘制层（Layer），其子组件将在新的Layer上绘制，而父组件将在原来Layer上绘制，也就是说`RepaintBoundary` 子组件的绘制将独立于父组件的绘制，`RepaintBoundary`会隔离其子节点和`CustomPaint`本身的绘制边界。示例如下：\n\n```dart\nCustomPaint(\n  size: Size(300, 300), //指定画布大小\n  painter: MyPainter(),\n  child: RepaintBoundary(child:...)), \n)\n```\n\n### CustomPainter\n\n`CustomPainter`中提定义了一个虚函数`paint`：\n\n```\nvoid paint(Canvas canvas, Size size);\n```\n\n`paint`有两个参数:\n\n- `Canvas`：一个画布，包括各种绘制方法，我们列出一下常用的方法：\n\n  |API名称     | 功能   |\n  | ---------- | ------ |\n  | drawLine   | 画线   |\n  | drawPoint  | 画点   |\n  | drawPath   | 画路径 |\n  | drawImage  | 画图像 |\n  | drawRect   | 画矩形 |\n  | drawCircle | 画圆   |\n  | drawOval   | 画椭圆 |\n  | drawArc    | 画圆弧 |\n\n- `Size`：当前绘制区域大小。\n\n### 画笔Paint\n\n现在画布有了，我们最后还缺一个画笔，Flutter提供了`Paint`类来实现画笔。在`Paint`中，我们可以配置画笔的各种属性如粗细、颜色、样式等。如：\n\n```dart\nvar paint = Paint() //创建一个画笔并配置其属性\n  ..isAntiAlias = true //是否抗锯齿\n  ..style = PaintingStyle.fill //画笔样式：填充\n  ..color=Color(0x77cdb175);//画笔颜色\n```\n\n更多的配置属性读者可以参考Paint类定义。\n\n### 示例：五子棋/盘\n\n下面我们通过一个五子棋游戏中棋盘和棋子的绘制来演示自绘UI的过程，首先我们看一下我们的目标效果，如图10-3所示：\n\n![图10-3](../imgs/10-3.png)\n\n代码：\n\n```dart\nimport 'package:flutter/material.dart';\nimport 'dart:math';\n\nclass CustomPaintRoute extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return Center(\n      child: CustomPaint(\n        size: Size(300, 300), //指定画布大小\n        painter: MyPainter(),\n      ),\n    );\n  }\n}\n\nclass MyPainter extends CustomPainter {\n  @override\n  void paint(Canvas canvas, Size size) {\n    double eWidth = size.width / 15;\n    double eHeight = size.height / 15;\n      \n    //画棋盘背景\n    var paint = Paint()\n      ..isAntiAlias = true\n      ..style = PaintingStyle.fill //填充\n      ..color = Color(0x77cdb175); //背景为纸黄色\n    canvas.drawRect(Offset.zero & size, paint);\n\n    //画棋盘网格\n    paint\n      ..style = PaintingStyle.stroke //线\n      ..color = Colors.black87\n      ..strokeWidth = 1.0;\n\n    for (int i = 0; i <= 15; ++i) {\n      double dy = eHeight * i;\n      canvas.drawLine(Offset(0, dy), Offset(size.width, dy), paint);\n    }\n\n    for (int i = 0; i <= 15; ++i) {\n      double dx = eWidth * i;\n      canvas.drawLine(Offset(dx, 0), Offset(dx, size.height), paint);\n    }\n\n    //画一个黑子\n    paint\n      ..style = PaintingStyle.fill\n      ..color = Colors.black;\n    canvas.drawCircle(\n      Offset(size.width / 2 - eWidth / 2, size.height / 2 - eHeight / 2),\n      min(eWidth / 2, eHeight / 2) - 2,\n      paint,\n    );\n      \n    //画一个白子\n    paint.color = Colors.white;\n    canvas.drawCircle(\n      Offset(size.width / 2 + eWidth / 2, size.height / 2 - eHeight / 2),\n      min(eWidth / 2, eHeight / 2) - 2,\n      paint,\n    );\n  }\n\n  //在实际场景中正确利用此回调可以避免重绘开销，本示例我们简单的返回true\n  @override\n  bool shouldRepaint(CustomPainter oldDelegate) => true;\n}\n```\n\n### 性能\n\n绘制是比较昂贵的操作，所以我们在实现自绘控件时应该考虑到性能开销，下面是两条关于性能优化的建议：\n\n- 尽可能的利用好`shouldRepaint`返回值；在UI树重新build时，控件在绘制前都会先调用该方法以确定是否有必要重绘；假如我们绘制的UI不依赖外部状态，那么就应该始终返回`false`，因为外部状态改变导致重新build时不会影响我们的UI外观；如果绘制依赖外部状态，那么我们就应该在`shouldRepaint`中判断依赖的状态是否改变，如果已改变则应返回`true`来重绘，反之则应返回`false`不需要重绘。\n\n- 绘制尽可能多的分层；在上面五子棋的示例中，我们将棋盘和棋子的绘制放在了一起，这样会有一个问题：由于棋盘始终是不变的，用户每次落子时变的只是棋子，但是如果按照上面的代码来实现，每次绘制棋子时都要重新绘制一次棋盘，这是没必要的。优化的方法就是将棋盘单独抽为一个组件，并设置其`shouldRepaint`回调值为`false`，然后将棋盘组件作为背景。然后将棋子的绘制放到另一个组件中，这样每次落子时只需要绘制棋子。\n\n### 总结\n\n自绘控件非常强大，理论上可以实现任何2D图形外观，实际上Flutter提供的所有组件最终都是通过调用Canvas绘制出来的，只不过绘制的逻辑被封装起来了，读者有兴趣可以查看具有外观样式的组件源码，找到其对应的`RenderObject`对象，如`Text`对应的`RenderParagraph`对象最终会通过`Canvas`实现文本绘制逻辑。下一节我们会再通过一个自绘的圆形背景渐变进度条的实例来帮助读者加深印象。\n"
  },
  {
    "path": "src/chapter10/gradient_circular_progress_demo.md",
    "content": "# 10.5 自绘实例：圆形背景渐变进度条\n\n本节我们实现一个圆形背景渐变进度条，它支持：\n\n1. 支持多种背景渐变色。\n2. 任意弧度；进度条可以不是整圆。\n3. 可以自定义粗细、两端是否圆角等样式。\n\n可以发现要实现这样的一个进度条是无法通过现有组件组合而成的，所以我们通过自绘方式实现，代码如下：\n\n```dart\nimport 'dart:math';\nimport 'package:flutter/material.dart';\n\nclass GradientCircularProgressIndicator extends StatelessWidget {\n  GradientCircularProgressIndicator({\n    this.strokeWidth = 2.0,\n    @required this.radius,\n    @required this.colors,\n    this.stops,\n    this.strokeCapRound = false,\n    this.backgroundColor = const Color(0xFFEEEEEE),\n    this.totalAngle = 2 * pi,\n    this.value\n  });\n\n  ///粗细\n  final double strokeWidth;\n\n  /// 圆的半径\n  final double radius;\n\n  ///两端是否为圆角\n  final bool strokeCapRound;\n\n  /// 当前进度，取值范围 [0.0-1.0]\n  final double value;\n\n  /// 进度条背景色\n  final Color backgroundColor;\n\n  /// 进度条的总弧度，2*PI为整圆，小于2*PI则不是整圆\n  final double totalAngle;\n\n  /// 渐变色数组\n  final List<Color> colors;\n\n  /// 渐变色的终止点，对应colors属性\n  final List<double> stops;\n\n  @override\n  Widget build(BuildContext context) {\n    double _offset = .0;\n    // 如果两端为圆角，则需要对起始位置进行调整，否则圆角部分会偏离起始位置\n    // 下面调整的角度的计算公式是通过数学几何知识得出，读者有兴趣可以研究一下为什么是这样  \n    if (strokeCapRound) {\n      _offset = asin(strokeWidth / (radius * 2 - strokeWidth));\n    }\n    var _colors = colors;\n    if (_colors == null) {\n      Color color = Theme\n          .of(context)\n          .accentColor;\n      _colors = [color, color];\n    }\n    return Transform.rotate(\n      angle: -pi / 2.0 - _offset, \n      child: CustomPaint(\n          size: Size.fromRadius(radius),\n          painter: _GradientCircularProgressPainter(\n            strokeWidth: strokeWidth,\n            strokeCapRound: strokeCapRound,\n            backgroundColor: backgroundColor,\n            value: value,\n            total: totalAngle,\n            radius: radius,\n            colors: _colors,\n          )\n      ),\n    );\n  }\n}\n\n//实现画笔\nclass _GradientCircularProgressPainter extends CustomPainter {\n  _GradientCircularProgressPainter({\n    this.strokeWidth: 10.0,\n    this.strokeCapRound: false,\n    this.backgroundColor = const Color(0xFFEEEEEE),\n    this.radius,\n    this.total = 2 * pi,\n    @required this.colors,\n    this.stops,\n    this.value\n  });\n\n  final double strokeWidth;\n  final bool strokeCapRound;\n  final double value;\n  final Color backgroundColor;\n  final List<Color> colors;\n  final double total;\n  final double radius;\n  final List<double> stops;\n\n  @override\n  void paint(Canvas canvas, Size size) {\n    if (radius != null) {\n      size = Size.fromRadius(radius);\n    }\n    double _offset = strokeWidth / 2.0;\n    double _value = (value ?? .0);\n    _value = _value.clamp(.0, 1.0) * total;\n    double _start = .0;\n\n    if (strokeCapRound) {\n      _start = asin(strokeWidth/ (size.width - strokeWidth));\n    }\n\n    Rect rect = Offset(_offset, _offset) & Size(\n        size.width - strokeWidth,\n        size.height - strokeWidth\n    );\n\n    var paint = Paint()\n      ..strokeCap = strokeCapRound ? StrokeCap.round : StrokeCap.butt\n      ..style = PaintingStyle.stroke\n      ..isAntiAlias = true\n      ..strokeWidth = strokeWidth;\n\n    // 先画背景\n    if (backgroundColor != Colors.transparent) {\n      paint.color = backgroundColor;\n      canvas.drawArc(\n          rect,\n          _start,\n          total,\n          false,\n          paint\n      );\n    }\n\n    // 再画前景，应用渐变\n    if (_value > 0) {\n      paint.shader = SweepGradient(\n        startAngle: 0.0,\n        endAngle: _value,\n        colors: colors,\n        stops: stops,\n      ).createShader(rect);\n\n      canvas.drawArc(\n          rect,\n          _start,\n          _value,\n          false,\n          paint\n      );\n    }\n  }\n\n  @override\n  bool shouldRepaint(CustomPainter oldDelegate) => true;\n\n}\n```\n\n下面我们来测试一下，为了尽可能多的展示`GradientCircularProgressIndicator`的不同外观和用途，这个示例代码会比较长，并且添加了动画，建议读者将此示例运行起来观看实际效果，我们先看看其中的一帧动画的截图：\n\n![gradient_circular_progress](../imgs/gradient_circular_progress.png)\n\n示例代码：\n\n```dart\nimport 'dart:math';\nimport 'package:flutter/material.dart';\nimport '../widgets/index.dart';\n\nclass GradientCircularProgressRoute extends StatefulWidget {\n  @override\n  GradientCircularProgressRouteState createState() {\n    return new GradientCircularProgressRouteState();\n  }\n}\n\nclass GradientCircularProgressRouteState\n    extends State<GradientCircularProgressRoute> with TickerProviderStateMixin {\n  AnimationController _animationController;\n\n  @override\n  void initState() {\n    super.initState();\n    _animationController =\n        new AnimationController(vsync: this, duration: Duration(seconds: 3));\n    bool isForward = true;\n    _animationController.addStatusListener((status) {\n      if (status == AnimationStatus.forward) {\n        isForward = true;\n      } else if (status == AnimationStatus.completed ||\n          status == AnimationStatus.dismissed) {\n        if (isForward) {\n          _animationController.reverse();\n        } else {\n          _animationController.forward();\n        }\n      } else if (status == AnimationStatus.reverse) {\n        isForward = false;\n      }\n    });\n    _animationController.forward();\n  }\n\n  @override\n  void dispose() {\n    _animationController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return SingleChildScrollView(\n      child: Center(\n        child: Column(\n          crossAxisAlignment: CrossAxisAlignment.center,\n          children: <Widget>[\n            AnimatedBuilder(\n              animation: _animationController,\n              builder: (BuildContext context, Widget child) {\n                return Padding(\n                  padding: const EdgeInsets.symmetric(vertical: 16.0),\n                  child: Column(\n                    children: <Widget>[\n                      Wrap(\n                        spacing: 10.0,\n                        runSpacing: 16.0,\n                        children: <Widget>[\n                          GradientCircularProgressIndicator(\n                            // No gradient\n                            colors: [Colors.blue, Colors.blue],\n                            radius: 50.0,\n                            strokeWidth: 3.0,\n                            value: _animationController.value,\n                          ),\n                          GradientCircularProgressIndicator(\n                            colors: [Colors.red, Colors.orange],\n                            radius: 50.0,\n                            strokeWidth: 3.0,\n                            value: _animationController.value,\n                          ),\n                          GradientCircularProgressIndicator(\n                            colors: [Colors.red, Colors.orange, Colors.red],\n                            radius: 50.0,\n                            strokeWidth: 5.0,\n                            value: _animationController.value,\n                          ),\n                          GradientCircularProgressIndicator(\n                            colors: [Colors.teal, Colors.cyan],\n                            radius: 50.0,\n                            strokeWidth: 5.0,\n                            strokeCapRound: true,\n                            value: CurvedAnimation(\n                                    parent: _animationController,\n                                    curve: Curves.decelerate)\n                                .value,\n                          ),\n                          TurnBox(\n                            turns: 1 / 8,\n                            child: GradientCircularProgressIndicator(\n                                colors: [Colors.red, Colors.orange, Colors.red],\n                                radius: 50.0,\n                                strokeWidth: 5.0,\n                                strokeCapRound: true,\n                                backgroundColor: Colors.red[50],\n                                totalAngle: 1.5 * pi,\n                                value: CurvedAnimation(\n                                        parent: _animationController,\n                                        curve: Curves.ease)\n                                    .value),\n                          ),\n                          RotatedBox(\n                            quarterTurns: 1,\n                            child: GradientCircularProgressIndicator(\n                                colors: [Colors.blue[700], Colors.blue[200]],\n                                radius: 50.0,\n                                strokeWidth: 3.0,\n                                strokeCapRound: true,\n                                backgroundColor: Colors.transparent,\n                                value: _animationController.value),\n                          ),\n                          GradientCircularProgressIndicator(\n                            colors: [\n                              Colors.red,\n                              Colors.amber,\n                              Colors.cyan,\n                              Colors.green[200],\n                              Colors.blue,\n                              Colors.red\n                            ],\n                            radius: 50.0,\n                            strokeWidth: 5.0,\n                            strokeCapRound: true,\n                            value: _animationController.value,\n                          ),\n                        ],\n                      ),\n                      GradientCircularProgressIndicator(\n                        colors: [Colors.blue[700], Colors.blue[200]],\n                        radius: 100.0,\n                        strokeWidth: 20.0,\n                        value: _animationController.value,\n                      ),\n\n                      Padding(\n                        padding: const EdgeInsets.symmetric(vertical: 16.0),\n                        child: GradientCircularProgressIndicator(\n                          colors: [Colors.blue[700], Colors.blue[300]],\n                          radius: 100.0,\n                          strokeWidth: 20.0,\n                          value: _animationController.value,\n                          strokeCapRound: true,\n                        ),\n                      ),\n                      //剪裁半圆\n                      ClipRect(\n                        child: Align(\n                          alignment: Alignment.topCenter,\n                          heightFactor: .5,\n                          child: Padding(\n                            padding: const EdgeInsets.only(bottom: 8.0),\n                            child: SizedBox(\n                              //width: 100.0,\n                              child: TurnBox(\n                                turns: .75,\n                                child: GradientCircularProgressIndicator(\n                                  colors: [Colors.teal, Colors.cyan[500]],\n                                  radius: 100.0,\n                                  strokeWidth: 8.0,\n                                  value: _animationController.value,\n                                  totalAngle: pi,\n                                  strokeCapRound: true,\n                                ),\n                              ),\n                            ),\n                          ),\n                        ),\n                      ),\n                      SizedBox(\n                        height: 104.0,\n                        width: 200.0,\n                        child: Stack(\n                          alignment: Alignment.center,\n                          children: <Widget>[\n                            Positioned(\n                              height: 200.0,\n                              top: .0,\n                              child: TurnBox(\n                                turns: .75,\n                                child: GradientCircularProgressIndicator(\n                                  colors: [Colors.teal, Colors.cyan[500]],\n                                  radius: 100.0,\n                                  strokeWidth: 8.0,\n                                  value: _animationController.value,\n                                  totalAngle: pi,\n                                  strokeCapRound: true,\n                                ),\n                              ),\n                            ),\n                            Padding(\n                              padding: const EdgeInsets.only(top: 10.0),\n                              child: Text(\n                                \"${(_animationController.value * 100).toInt()}%\",\n                                style: TextStyle(\n                                  fontSize: 25.0,\n                                  color: Colors.blueGrey,\n                                ),\n                              ),\n                            )\n                          ],\n                        ),\n                      ),\n                    ],\n                  ),\n                );\n              },\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}\n```\n\n怎么样，很炫酷吧！`GradientCircularProgressIndicator`已经被添加进了笔者维护的flukit组件库中了，读者如果有需要，可以直接依赖flukit包。\n\n"
  },
  {
    "path": "src/chapter10/index.md",
    "content": "# 本章目录\n\n* [10.1：自定义组件方法简介](intro.md)\n* [10.2：组合现有组件](combine.md)\n* [10.3：组合实例：TurnBox](turn_box.md)\n* [10.4：自绘组件（CustomPaint与Canvas）](custom_paint.md) \n* [10.5：自绘实例：圆形渐变进度条(自绘)](gradient_circular_progress_demo.md) \n"
  },
  {
    "path": "src/chapter10/intro.md",
    "content": "# 10.1 自定义组件方法简介\n\n当Flutter提供的现有组件无法满足我们的需求，或者我们为了共享代码需要封装一些通用组件，这时我们就需要自定义组件。在Flutter中自定义组件有三种方式：通过组合其它组件、自绘和实现RenderObject。本节我们先分别介绍一下这三种方式的特点，后面章节中则详细介绍它们的细节。\n\n### 组合其它Widget\n\n这种方式是通过拼装其它组件来组合成一个新的组件。例如我们之前介绍的`Container`就是一个组合组件，它是由`DecoratedBox`、`ConstrainedBox`、`Transform`、`Padding`、`Align`等组件组成。\n\n在Flutter中，组合的思想非常重要，Flutter提供了非常多的基础组件，而我们的界面开发其实就是按照需要组合这些组件来实现各种不同的布局而已。 \n\n### 自绘\n\n如果遇到无法通过现有的组件来实现需要的UI时，我们可以通过自绘组件的方式来实现，例如我们需要一个颜色渐变的圆形进度条，而Flutter提供的`CircularProgressIndicator`并不支持在显示精确进度时对进度条应用渐变色（其`valueColor` 属性只支持执行旋转动画时变化Indicator的颜色），这时最好的方法就是通过自定义组件来绘制出我们期望的外观。我们可以通过Flutter中提供的`CustomPaint`和`Canvas`来实现UI自绘。\n\n\n\n### 实现RenderObject\n\nFlutter提供的自身具有UI外观的组件，如文本`Text`、`Image`都是通过相应的`RenderObject`（我们将在“Flutter核心原理”一章中详细介绍`RenderObject`）渲染出来的，如Text是由`RenderParagraph`渲染；而`Image`是由`RenderImage`渲染。`RenderObject`是一个抽象类，它定义了一个抽象方法`paint(...)`：\n\n```dart\nvoid paint(PaintingContext context, Offset offset)\n```\n\n`PaintingContext`代表组件的绘制上下文，通过`PaintingContext.canvas`可以获得`Canvas`，而绘制逻辑主要是通过`Canvas` API来实现。子类需要重写此方法以实现自身的绘制逻辑，如`RenderParagraph`需要实现文本绘制逻辑，而`RenderImage`需要实现图片绘制逻辑。\n\n可以发现，`RenderObject`中最终也是通过`Canvas` API来绘制的，那么通过实现`RenderObject`的方式和上面介绍的通过`CustomPaint`和`Canvas`自绘的方式有什么区别？其实答案很简单，`CustomPaint`只是为了方便开发者封装的一个代理类，它直接继承自`SingleChildRenderObjectWidget`，通过`RenderCustomPaint`的`paint`方法将`Canvas`和画笔`Painter`(需要开发者实现，后面章节介绍)连接起来实现了最终的绘制（绘制逻辑在`Painter`中）。\n\n### 总结\n\n“组合”是自定义组件最简单的方法，在任何需要自定义组件的场景下，我们都应该优先考虑是否能够通过组合来实现。而自绘和通过实现`RenderObject`的方法本质上是一样的，都需要开发者调用`Canvas` API手动去绘制UI，优点是强大灵活，理论上可以实现任何外观的UI，而缺点是必须了解`Canvas` API细节，并且得自己去实现绘制逻辑。\n\n在本章接下来的小节中，我们将通过一些实例来详细介绍自定义UI的过程，由于后两种方法本质是相同的，并且Flutter中很多基础组件都是通过`RenderObject`的形式来实现的，所以后续我们只介绍`CustomPaint`和`Canvas`的方式，读者如果对自定义`RenderObject`的方法好奇，可以查看Flutter中相关基础组件对应的`RenderObject`的实现源码，如`RenderParagraph`或`RenderImage`。\n"
  },
  {
    "path": "src/chapter10/turn_box.md",
    "content": "# 10.3 组合实例：TurnBox\n\n我们之前已经介绍过`RotatedBox`，它可以旋转子组件，但是它有两个缺点：一是只能将其子节点以90度的倍数旋转；二是当旋转的角度发生变化时，旋转角度更新过程没有动画。\n\n本节我们将实现一个`TurnBox`组件，它不仅可以以任意角度来旋转其子节点，而且可以在角度发生变化时执行一个动画以过渡到新状态，同时，我们可以手动指定动画速度。\n\n`TurnBox`的完整代码如下：\n\n```dart\nimport 'package:flutter/widgets.dart';\n\nclass TurnBox extends StatefulWidget {\n  const TurnBox({\n    Key key,\n    this.turns = .0, //旋转的“圈”数,一圈为360度，如0.25圈即90度\n    this.speed = 200, //过渡动画执行的总时长\n    this.child\n  }) :super(key: key);\n\n  final double turns;\n  final int speed;\n  final Widget child;\n\n  @override\n  _TurnBoxState createState() => new _TurnBoxState();\n}\n\nclass _TurnBoxState extends State<TurnBox>\n    with SingleTickerProviderStateMixin {\n  AnimationController _controller;\n\n  @override\n  void initState() {\n    super.initState();\n    _controller = new AnimationController(\n        vsync: this,\n        lowerBound: -double.infinity,\n        upperBound: double.infinity\n    );\n    _controller.value = widget.turns;\n  }\n\n  @override\n  void dispose() {\n    _controller.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return RotationTransition(\n      turns: _controller,\n      child: widget.child,\n    );\n  }\n\n  @override\n  void didUpdateWidget(TurnBox oldWidget) {\n    super.didUpdateWidget(oldWidget);\n    //旋转角度发生变化时执行过渡动画  \n    if (oldWidget.turns != widget.turns) {\n      _controller.animateTo(\n        widget.turns,\n        duration: Duration(milliseconds: widget.speed??200),\n        curve: Curves.easeOut,\n      );\n    }\n  }\n}\n```\n\n上面代码中：\n\n1. 我们是通过组合`RotationTransition`和child来实现的旋转效果。\n2. 在`didUpdateWidget`中，我们判断要旋转的角度是否发生了变化，如果变了，则执行一个过渡动画。\n\n下面我们测试一下`TurnBox`的功能，测试代码如下：\n\n```dart\nimport 'package:flutter/material.dart';\nimport '../widgets/index.dart';\n\nclass TurnBoxRoute extends StatefulWidget {\n  @override\n  _TurnBoxRouteState createState() => new _TurnBoxRouteState();\n}\n\nclass _TurnBoxRouteState extends State<TurnBoxRoute> {\n  double _turns = .0;\n\n  @override\n  Widget build(BuildContext context) {\n\n    return Center(\n      child: Column(\n        children: <Widget>[\n          TurnBox(\n            turns: _turns,\n            speed: 500,\n            child: Icon(Icons.refresh, size: 50,),\n          ),\n          TurnBox(\n            turns: _turns,\n            speed: 1000,\n            child: Icon(Icons.refresh, size: 150.0,),\n          ),\n          RaisedButton(\n            child: Text(\"顺时针旋转1/5圈\"),\n            onPressed: () {\n              setState(() {\n                _turns += .2;\n              });\n            },\n          ),\n          RaisedButton(\n            child: Text(\"逆时针旋转1/5圈\"),\n            onPressed: () {\n              setState(() {\n                _turns -= .2;\n              });\n            },\n          )\n        ],\n      ),\n    );\n  }\n}\n```\n\n测试代码运行后效果如图10-2所示：\n\n![图10-2](../imgs/10-2.png)\n\n\n\n当我们点击旋转按钮时，两个图标的旋转都会旋转1/5圈，但旋转的速度是不同的，读者可以自己运行一下示例看看效果。\n\n实际上本示例只组合了`RotationTransition`一个组件，它是一个最简的组合类组件示例。另外，如果我们封装的是`StatefulWidget`，那么一定要注意在组件更新时是否需要同步状态。比如我们要封装一个富文本展示组件`MyRichText` ，它可以自动处理url链接，定义如下：\n\n```dart\nclass MyRichText extends StatefulWidget {\n  MyRichText({\n    Key key,\n    this.text, // 文本字符串\n    this.linkStyle, // url链接样式\n  }) : super(key: key);\n\n  final String text;\n  final TextStyle linkStyle;\n\n  @override\n  _MyRichTextState createState() => _MyRichTextState();\n}\n```\n\n接下来我们在`_MyRichTextState`中要实现的功能有两个：\n\n1. 解析文本字符串“text”，生成`TextSpan`缓存起来；\n2. 在`build`中返回最终的富文本样式；\n\n`_MyRichTextState` 实现的代码大致如下：\n\n```dart\nclass _MyRichTextState extends State<MyRichText> {\n\n  TextSpan _textSpan;\n\n  @override\n  Widget build(BuildContext context) {\n    return RichText(\n      text: _textSpan,\n    );\n  }\n\n  TextSpan parseText(String text) {\n    // 耗时操作：解析文本字符串，构建出TextSpan。\n    // 省略具体实现。\n  }\n\n  @override\n  void initState() {\n    _textSpan = parseText(widget.text)\n    super.initState();\n  }\n}\n```\n\n由于解析文本字符串，构建出`TextSpan`是一个耗时操作，为了不在每次build的时候都解析一次，所以我们在`initState`中对解析的结果进行了缓存，然后再`build`中直接使用解析的结果`_textSpan`。这看起来很不错，但是上面的代码有一个严重的问题，就是父组件传入的`text`发生变化时（组件树结构不变），那么`MyRichText`显示的内容不会更新，原因就是`initState`只会在State创建时被调用，所以在`text`发生变化时，`parseText`没有重新执行，导致`_textSpan`任然是旧的解析值。要解决这个问题也很简单，我们只需添加一个`didUpdateWidget`回调，然后再里面重新调用`parseText`即可：\n\n```dart\n@override\nvoid didUpdateWidget(MyRichText oldWidget) {\n  if (widget.text != oldWidget.text) {\n    _textSpan = parseText(widget.text);\n  }\n  super.didUpdateWidget(oldWidget);\n}\n```\n\n有些读者可能会觉得这个点也很简单，是的，的确很简单，之所以要在这里反复强调是因为这个点在实际开发中很容易被忽略，它虽然简单，但却很重要。总之，当我们在State中会缓存某些依赖Widget参数的数据时，一定要注意在组件更新时是否需要同步状态。"
  },
  {
    "path": "src/chapter11/dio.md",
    "content": "\n\n# 11.3 Http请求-Dio http库\n\n通过上一节介绍，我们可以发现直接使用HttpClient发起网络请求是比较麻烦的，很多事情得我们手动处理，如果再涉及到文件上传/下载、Cookie管理等就会非常繁琐。幸运的是，Dart社区有一些第三方http请求库，用它们来发起http请求将会简单的多，本节我们介绍一下目前人气较高的[dio](https://github.com/flutterchina/dio)库。\n\n>  dio是一个强大的Dart Http请求库，支持Restful API、FormData、拦截器、请求取消、Cookie管理、文件上传/下载、超时等。dio的使用方式随着其版本升级可能会发生变化，如果本节所述内容和dio官方有差异，请以dio官方文档为准。\n\n### 引入\n\n引入dio:\n\n```yaml\ndependencies:\n  dio: ^x.x.x #请使用pub上的最新版本\n```\n\n导入并创建dio实例：\n\n```dart\nimport 'package:dio/dio.dart';\nDio dio =  Dio();\n```\n\n接下来就可以通过 dio实例来发起网络请求了，注意，一个dio实例可以发起多个http请求，一般来说，APP只有一个http数据源时，dio应该使用单例模式。\n\n### 示例\n\n发起 `GET` 请求 :\n\n```dart\nResponse response;\nresponse=await dio.get(\"/test?id=12&name=wendu\")\nprint(response.data.toString());\n```\n\n对于`GET`请求我们可以将query参数通过对象来传递，上面的代码等同于：\n\n```dart\nresponse=await dio.get(\"/test\",queryParameters:{\"id\":12,\"name\":\"wendu\"})\nprint(response);\n```\n\n发起一个 `POST` 请求:\n\n```dart\nresponse=await dio.post(\"/test\",data:{\"id\":12,\"name\":\"wendu\"})\n```\n\n发起多个并发请求:\n\n```dart\nresponse= await Future.wait([dio.post(\"/info\"),dio.get(\"/token\")]);\n```\n\n下载文件:\n\n```dart\nresponse=await dio.download(\"https://www.google.com/\",_savePath);\n```\n\n发送 FormData:\n\n```dart\nFormData formData = new FormData.from({\n   \"name\": \"wendux\",\n   \"age\": 25,\n});\nresponse = await dio.post(\"/info\", data: formData)\n```\n\n如果发送的数据是FormData，则dio会将请求header的`contentType`设为“multipart/form-data”。\n\n通过FormData上传多个文件:\n\n```dart\nFormData formData = new FormData.from({\n   \"name\": \"wendux\",\n   \"age\": 25,\n   \"file1\": new UploadFileInfo(new File(\"./upload.txt\"), \"upload1.txt\"),\n   \"file2\": new UploadFileInfo(new File(\"./upload.txt\"), \"upload2.txt\"),\n     // 支持文件数组上传\n   \"files\": [\n      new UploadFileInfo(new File(\"./example/upload.txt\"), \"upload.txt\"),\n      new UploadFileInfo(new File(\"./example/upload.txt\"), \"upload.txt\")\n    ]\n});\nresponse = await dio.post(\"/info\", data: formData)\n```\n\n值得一提的是，dio内部仍然使用HttpClient发起的请求，所以代理、请求认证、证书校验等和HttpClient是相同的，我们可以在`onHttpClientCreate `回调中设置，例如：\n\n```dart\n(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) {\n    //设置代理 \n    client.findProxy = (uri) {\n      return \"PROXY 192.168.1.2:8888\";\n    };\n    //校验证书\n    httpClient.badCertificateCallback=(X509Certificate cert, String host, int port){\n      if(cert.pem==PEM){\n      return true; //证书一致，则允许发送数据\n     }\n     return false;\n    };   \n  };\n```\n\n注意，`onHttpClientCreate `会在当前dio实例内部需要创建HttpClient时调用，所以通过此回调配置HttpClient会对整个dio实例生效，如果你想针对某个应用请求单独的代理或证书校验策略，可以创建一个新的dio实例即可。\n\n怎么样，是不是很简单，除了这些基本的用法，dio还支持请求配置、拦截器等，官方资料比较详细，故本书不再赘述，详情可以参考dio主页：https://github.com/flutterchina/dio 。 下一节我们将使用dio实现一个分块下载器。\n\n### 实例\n\n我们通过Github开放的API来请求flutterchina组织下的所有公开的开源项目，实现：\n\n1. 在请求阶段弹出loading\n2. 请求结束后，如果请求失败，则展示错误信息；如果成功，则将项目名称列表展示出来。\n\n代码如下：\n\n```dart\nclass _FutureBuilderRouteState extends State<FutureBuilderRoute> {\n  Dio _dio = new Dio();\n\n  @override\n  Widget build(BuildContext context) {\n\n    return new Container(\n      alignment: Alignment.center,\n      child: FutureBuilder(\n          future: _dio.get(\"https://api.github.com/orgs/flutterchina/repos\"),\n          builder: (BuildContext context, AsyncSnapshot snapshot) {\n            //请求完成\n            if (snapshot.connectionState == ConnectionState.done) {\n              Response response = snapshot.data;\n              //发生错误\n              if (snapshot.hasError) {\n                return Text(snapshot.error.toString());\n              }\n              //请求成功，通过项目信息构建用于显示项目名称的ListView\n              return ListView(\n                children: response.data.map<Widget>((e) =>\n                    ListTile(title: Text(e[\"full_name\"]))\n                ).toList(),\n              );\n            }\n            //请求未完成时弹出loading\n            return CircularProgressIndicator();\n          }\n      ),\n    );\n  }\n}\n```"
  },
  {
    "path": "src/chapter11/download_with_chunks.md",
    "content": "\n# 11.4 实例：Http分块下载\n\n本节将通过一个“Http分块下载”的示例演示一下dio的具体用法。\n\n### 原理\n\nHttp协议定义了分块传输的响应header字段，但具体是否支持取决于Server的实现，我们可以指定请求头的\"range\"字段来验证服务器是否支持分块传输。例如，我们可以利用curl命令来验证：\n\n```shell\nbogon:~ duwen$ curl -H \"Range: bytes=0-10\" http://download.dcloud.net.cn/HBuilder.9.0.2.macosx_64.dmg -v\n# 请求头\n> GET /HBuilder.9.0.2.macosx_64.dmg HTTP/1.1\n> Host: download.dcloud.net.cn\n> User-Agent: curl/7.54.0\n> Accept: */*\n> Range: bytes=0-10\n# 响应头\n< HTTP/1.1 206 Partial Content\n< Content-Type: application/octet-stream\n< Content-Length: 11\n< Connection: keep-alive\n< Date: Thu, 21 Feb 2019 06:25:15 GMT\n< Content-Range: bytes 0-10/233295878\n\n```\n\n我们在请求头中添加\"Range: bytes=0-10\"的作用是，告诉服务器本次请求我们只想获取文件0-10(包括10，共11字节)这块内容。如果服务器支持分块传输，则响应状态码为206，表示“部分内容”，并且同时响应头中包含“Content-Range”字段，如果不支持则不会包含。我们看看上面“Content-Range”的内容：\n\n```\nContent-Range: bytes 0-10/233295878\n```\n\n0-10表示本次返回的区块，233295878代表文件的总长度，单位都是byte,  也就是该文件大概233M多一点。\n\n基于此，我们可以设计一个简单的多线程的文件分块下载器，实现的思路是：\n\n1. 先检测是否支持分块传输，如果不支持，则直接下载；若支持，则将剩余内容分块下载。\n2. 各个分块下载时保存到各自临时文件，等到所有分块下载完后合并临时文件。\n3. 删除临时文件。\n\n### 实现\n\n下面是整体的流程：\n\n```dart\n// 通过第一个分块请求检测服务器是否支持分块传输  \nResponse response = await downloadChunk(url, 0, firstChunkSize, 0);\nif (response.statusCode == 206) {    //如果支持\n    //解析文件总长度，进而算出剩余长度\n    total = int.parse(\n        response.headers.value(HttpHeaders.contentRangeHeader).split(\"/\").last);\n    int reserved = total -\n        int.parse(response.headers.value(HttpHeaders.contentLengthHeader));\n    //文件的总块数(包括第一块)\n    int chunk = (reserved / firstChunkSize).ceil() + 1;\n    if (chunk > 1) {\n        int chunkSize = firstChunkSize;\n        if (chunk > maxChunk + 1) {\n            chunk = maxChunk + 1;\n            chunkSize = (reserved / maxChunk).ceil();\n        }\n        var futures = <Future>[];\n        for (int i = 0; i < maxChunk; ++i) {\n            int start = firstChunkSize + i * chunkSize;\n            //分块下载剩余文件  \n            futures.add(downloadChunk(url, start, start + chunkSize, i + 1));\n        }\n        //等待所有分块全部下载完成\n        await Future.wait(futures);\n    }\n    //合并文件文件  \n    await mergeTempFiles(chunk);\n}\n```\n\n下面我们使用dio的`download` API 实现`downloadChunk`：\n\n```dart\n//start 代表当前块的起始位置，end代表结束位置\n//no 代表当前是第几块\nFuture<Response> downloadChunk(url, start, end, no) async {\n  progress.add(0); //progress记录每一块已接收数据的长度\n  --end;\n  return dio.download(\n    url,\n    savePath + \"temp$no\", //临时文件按照块的序号命名，方便最后合并\n    onReceiveProgress: createCallback(no), // 创建进度回调，后面实现\n    options: Options(\n      headers: {\"range\": \"bytes=$start-$end\"}, //指定请求的内容区间\n    ),\n  );\n}\n```\n\n接下来实现`mergeTempFiles`:\n\n```dart\nFuture mergeTempFiles(chunk) async {\n  File f = File(savePath + \"temp0\");\n  IOSink ioSink= f.openWrite(mode: FileMode.writeOnlyAppend);\n  //合并临时文件  \n  for (int i = 1; i < chunk; ++i) {\n    File _f = File(savePath + \"temp$i\");\n    await ioSink.addStream(_f.openRead());\n    await _f.delete(); //删除临时文件\n  }\n  await ioSink.close();\n  await f.rename(savePath); //合并后的文件重命名为真正的名称\n}\n```\n\n下面我们看一下完整实现：\n\n```dart\n/// Downloading by spiting as file in chunks\nFuture downloadWithChunks(\n  url,\n  savePath, {\n  ProgressCallback onReceiveProgress,\n}) async {\n  const firstChunkSize = 102;\n  const maxChunk = 3;\n\n  int total = 0;\n  var dio = Dio();\n  var progress = <int>[];\n\n  createCallback(no) {\n    return (int received, _) {\n      progress[no] = received;\n      if (onReceiveProgress != null && total != 0) {\n        onReceiveProgress(progress.reduce((a, b) => a + b), total);\n      }\n    };\n  }\n\n  Future<Response> downloadChunk(url, start, end, no) async {\n    progress.add(0);\n    --end;\n    return dio.download(\n      url,\n      savePath + \"temp$no\",\n      onReceiveProgress: createCallback(no),\n      options: Options(\n        headers: {\"range\": \"bytes=$start-$end\"},\n      ),\n    );\n  }\n\n  Future mergeTempFiles(chunk) async {\n    File f = File(savePath + \"temp0\");\n    IOSink ioSink= f.openWrite(mode: FileMode.writeOnlyAppend);\n    for (int i = 1; i < chunk; ++i) {\n      File _f = File(savePath + \"temp$i\");\n      await ioSink.addStream(_f.openRead());\n      await _f.delete();\n    }\n    await ioSink.close();\n    await f.rename(savePath);\n  }\n\n  Response response = await downloadChunk(url, 0, firstChunkSize, 0);\n  if (response.statusCode == 206) {\n    total = int.parse(\n        response.headers.value(HttpHeaders.contentRangeHeader).split(\"/\").last);\n    int reserved = total -\n        int.parse(response.headers.value(HttpHeaders.contentLengthHeader));\n    int chunk = (reserved / firstChunkSize).ceil() + 1;\n    if (chunk > 1) {\n      int chunkSize = firstChunkSize;\n      if (chunk > maxChunk + 1) {\n        chunk = maxChunk + 1;\n        chunkSize = (reserved / maxChunk).ceil();\n      }\n      var futures = <Future>[];\n      for (int i = 0; i < maxChunk; ++i) {\n        int start = firstChunkSize + i * chunkSize;\n        futures.add(downloadChunk(url, start, start + chunkSize, i + 1));\n      }\n      await Future.wait(futures);\n    }\n    await mergeTempFiles(chunk);\n  }\n}\n```\n\n现在可以进行分块下载了：\n\n```dart\nmain() async {\n  var url = \"http://download.dcloud.net.cn/HBuilder.9.0.2.macosx_64.dmg\";\n  var savePath = \"./example/HBuilder.9.0.2.macosx_64.dmg\";\n  await downloadWithChunks(url, savePath, onReceiveProgress: (received, total) {\n    if (total != -1) {\n      print(\"${(received / total * 100).floor()}%\");\n    }\n  });\n}\n```\n\n### 思考\n\n1. 分块下载真的能提高下载速度吗？\n\n   其实下载速度的主要瓶颈是取决于网络速度和服务器的出口速度，如果是同一个数据源，分块下载的意义并不大，因为服务器是同一个，出口速度确定的，主要取决于网速，而上面的例子正式同源分块下载，读者可以自己对比一下分块和不分块的的下载速度。如果有多个下载源，并且每个下载源的出口带宽都是有限制的，这时分块下载可能会更快一下，之所以说“可能”，是由于这并不是一定的，比如有三个源，三个源的出口带宽都为1G/s，而我们设备所连网络的峰值假设只有800M/s，那么瓶颈就在我们的网络。即使我们设备的带宽大于任意一个源，下载速度依然不一定就比单源单线下载快，试想一下，假设有两个源A和B，速度A源是B源的3倍，如果采用分块下载，两个源各下载一半的话，读者可以算一下所需的下载时间，然后再算一下只从A源下载所需的时间，看看哪个更快。\n\n   分块下载的最终速度受设备所在网络带宽、源出口速度、每个块大小、以及分块的数量等诸多因素影响，实际过程中很难保证速度最优。在实际开发中，读者可可以先测试对比后再决定是否使用。\n\n2. 分块下载有什么实际的用处吗？\n\n   分块下载还有一个比较使用的场景是断点续传，可以将文件分为若干个块，然后维护一个下载状态文件用以记录每一个块的状态，这样即使在网络中断后，也可以恢复中断前的状态，具体实现读者可以自己尝试一下，还是有一些细节需要特别注意的，比如分块大小多少合适？下载到一半的块如何处理？要不要维护一个任务队列？\n"
  },
  {
    "path": "src/chapter11/file_operation.md",
    "content": "# 11.1 文件操作\n\nDart的IO库包含了文件读写的相关类，它属于Dart语法标准的一部分，所以通过Dart IO库，无论是Dart VM下的脚本还是Flutter，都是通过Dart IO库来操作文件的，不过和Dart VM相比，Flutter有一个重要差异是文件系统路径不同，这是因为Dart VM是运行在PC或服务器操作系统下，而Flutter是运行在移动操作系统中，他们的文件系统会有一些差异。\n\n#### APP目录\n\nAndroid和iOS的应用存储目录不同，[`PathProvider`](https://pub.dartlang.org/packages/path_provider) 插件提供了一种平台透明的方式来访问设备文件系统上的常用位置。该类当前支持访问两个文件系统位置：\n\n- **临时目录:**  可以使用 `getTemporaryDirectory()` 来获取临时目录； 系统可随时清除的临时目录（缓存）。在iOS上，这对应于[`NSTemporaryDirectory()`](https://developer.apple.com/reference/foundation/1409211-nstemporarydirectory) 返回的值。在Android上，这是[`getCacheDir()`](https://developer.android.com/reference/android/content/Context.html#getCacheDir())返回的值。\n- **文档目录:** 可以使用`getApplicationDocumentsDirectory()`来获取应用程序的文档目录，该目录用于存储只有自己可以访问的文件。只有当应用程序被卸载时，系统才会清除该目录。在iOS上，这对应于`NSDocumentDirectory`。在Android上，这是`AppData`目录。\n- **外部存储目录**：可以使用`getExternalStorageDirectory()`来获取外部存储目录，如SD卡；由于iOS不支持外部目录，所以在iOS下调用该方法会抛出`UnsupportedError `异常，而在Android下结果是android SDK中`getExternalStorageDirectory`的返回值。\n\n一旦你的Flutter应用程序有一个文件位置的引用，你可以使用[dart:io](https://api.dartlang.org/stable/dart-io/dart-io-library.html)API来执行对文件系统的读/写操作。有关使用Dart处理文件和目录的详细内容可以参考Dart语言文档，下面我们看一个简单的例子。\n\n#### 示例\n\n我们还是以计数器为例，实现在应用退出重启后可以恢复点击次数。 这里，我们使用文件来保存数据：\n\n1. 引入PathProvider插件；在`pubspec.yaml`文件中添加如下声明：\n\n   ```yaml\n   path_provider: ^0.4.1\n   ```\n\n   添加后，执行`flutter packages get` 获取一下, 版本号可能随着时间推移会发生变化，读者可以使用最新版。\n\n2. 实现：\n\n   ```dart\n   import 'dart:io';\n   import 'dart:async';\n   import 'package:flutter/material.dart';\n   import 'package:path_provider/path_provider.dart';\n   \n   class FileOperationRoute extends StatefulWidget {\n     FileOperationRoute({Key key}) : super(key: key);\n   \n     @override\n     _FileOperationRouteState createState() => new _FileOperationRouteState();\n   }\n   \n   class _FileOperationRouteState extends State<FileOperationRoute> {\n     int _counter;\n   \n     @override\n     void initState() {\n       super.initState();\n       //从文件读取点击次数\n       _readCounter().then((int value) {\n         setState(() {\n           _counter = value;\n         });\n       });\n     }\n   \n     Future<File> _getLocalFile() async {\n       // 获取应用目录\n       String dir = (await getApplicationDocumentsDirectory()).path;\n       return new File('$dir/counter.txt');\n     }\n   \n     Future<int> _readCounter() async {\n       try {\n         File file = await _getLocalFile();\n         // 读取点击次数（以字符串）\n         String contents = await file.readAsString();\n         return int.parse(contents);\n       } on FileSystemException {\n         return 0;\n       }\n     }\n   \n     Future<Null> _incrementCounter() async {\n       setState(() {\n         _counter++;\n       });\n       // 将点击次数以字符串类型写到文件中\n       await (await _getLocalFile()).writeAsString('$_counter');\n     }\n   \n     @override\n     Widget build(BuildContext context) {\n       return new Scaffold(\n         appBar: new AppBar(title: new Text('文件操作')),\n         body: new Center(\n           child: new Text('点击了 $_counter 次'),\n         ),\n         floatingActionButton: new FloatingActionButton(\n           onPressed: _incrementCounter,\n           tooltip: 'Increment',\n           child: new Icon(Icons.add),\n         ),\n       );\n     }\n   }\n   ```\n\n   上面代码比较简单，不再赘述，需要说明的是，本示例只是为了演示文件读写，而在实际开发中，如果要存储一些简单的数据，使用shared_preferences插件会比较简单。\n\n   > 注意，Dart IO库操作文件的API非常丰富，但本书不是介绍Dart语言的，故不详细说明，读者需要的话可以自行学习。\n\n   \n\n   \n\n   \n\n   "
  },
  {
    "path": "src/chapter11/http.md",
    "content": "# 11.2 通过HttpClient发起HTTP请求\n\nDart IO库中提供了用于发起Http请求的一些类，我们可以直接使用`HttpClient`来发起请求。使用`HttpClient`发起请求分为五步：\n\n1. 创建一个`HttpClient`：\n\n   ```dart\n    HttpClient httpClient = new HttpClient();\n   ```\n\n2. 打开Http连接，设置请求头：\n\n   ```dart\n   HttpClientRequest request = await httpClient.getUrl(uri);\n   ```\n\n   这一步可以使用任意Http Method，如`httpClient.post(...)`、`httpClient.delete(...)`等。如果包含Query参数，可以在构建uri时添加，如：\n\n   ```dart\n   Uri uri=Uri(scheme: \"https\", host: \"flutterchina.club\", queryParameters: {\n       \"xx\":\"xx\",\n       \"yy\":\"dd\"\n     });\n   ```\n\n   通过`HttpClientRequest`可以设置请求header，如：\n\n   ```dart\n   request.headers.add(\"user-agent\", \"test\");\n   ```\n   如果是post或put等可以携带请求体方法，可以通过HttpClientRequest对象发送request body，如：\n\n   ```dart\n   String payload=\"...\";\n   request.add(utf8.encode(payload)); \n   //request.addStream(_inputStream); //可以直接添加输入流\n   ```\n\n3. 等待连接服务器：\n\n   ```dart\n   HttpClientResponse response = await request.close();\n   ```\n\n   这一步完成后，请求信息就已经发送给服务器了，返回一个`HttpClientResponse`对象，它包含响应头（header）和响应流(响应体的Stream)，接下来就可以通过读取响应流来获取响应内容。\n\n4. 读取响应内容：\n\n   ```dart\n   String responseBody = await response.transform(utf8.decoder).join();\n   ```\n\n   我们通过读取响应流来获取服务器返回的数据，在读取时我们可以设置编码格式，这里是utf8。\n\n5. 请求结束，关闭`HttpClient`：\n\n   ```dart\n   httpClient.close();\n   ```\n\n    关闭client后，通过该client发起的所有请求都会中止。\n\n#### 示例\n\n我们实现一个获取百度首页html的例子，示例效果如图11-1所示：\n\n​    ![图11-1](../imgs/11-1.png)\n\n点击“获取百度首页”按钮后，会请求百度首页，请求成功后，我们将返回内容显示出来并在控制台打印响应header，代码如下：\n\n```dart\nimport 'dart:convert';\nimport 'dart:io';\n\nimport 'package:flutter/material.dart';\n\nclass HttpTestRoute extends StatefulWidget {\n  @override\n  _HttpTestRouteState createState() => new _HttpTestRouteState();\n}\n\nclass _HttpTestRouteState extends State<HttpTestRoute> {\n  bool _loading = false;\n  String _text = \"\";\n\n  @override\n  Widget build(BuildContext context) {\n    return ConstrainedBox(\n      constraints: BoxConstraints.expand(),\n      child: SingleChildScrollView(\n        child: Column(\n          children: <Widget>[\n            RaisedButton(\n                child: Text(\"获取百度首页\"),\n                onPressed: _loading ? null : () async {\n                  setState(() {\n                    _loading = true;\n                    _text = \"正在请求...\";\n                  });\n                  try {\n                    //创建一个HttpClient\n                    HttpClient httpClient = new HttpClient();\n                    //打开Http连接\n                    HttpClientRequest request = await httpClient.getUrl(\n                        Uri.parse(\"https://www.baidu.com\"));\n                    //使用iPhone的UA\n                    request.headers.add(\"user-agent\", \"Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1\");\n                    //等待连接服务器（会将请求信息发送给服务器）\n                    HttpClientResponse response = await request.close();\n                    //读取响应内容\n                    _text = await response.transform(utf8.decoder).join();\n                    //输出响应头\n                    print(response.headers);\n\n                    //关闭client后，通过该client发起的所有请求都会中止。\n                    httpClient.close();\n\n                  } catch (e) {\n                    _text = \"请求失败：$e\";\n                  } finally {\n                    setState(() {\n                      _loading = false;\n                    });\n                  }\n                }\n            ),\n            Container(\n                width: MediaQuery.of(context).size.width-50.0,\n                child: Text(_text.replaceAll(new RegExp(r\"\\s\"), \"\"))\n            )\n          ],\n        ),\n      ),\n    );\n  }\n}\n```\n\n控制台输出：\n\n```\nI/flutter (18545): connection: Keep-Alive\nI/flutter (18545): cache-control: no-cache\nI/flutter (18545): set-cookie: ....  //有多个，省略...\nI/flutter (18545): transfer-encoding: chunked\nI/flutter (18545): date: Tue, 30 Oct 2018 10:00:52 GMT\nI/flutter (18545): content-encoding: gzip\nI/flutter (18545): vary: Accept-Encoding\nI/flutter (18545): strict-transport-security: max-age=172800\nI/flutter (18545): content-type: text/html;charset=utf-8\nI/flutter (18545): tracecode: 00525262401065761290103018, 00522983\n```\n\n#### HttpClient配置\n\n`HttpClient`有很多属性可以配置，常用的属性列表如下：\n\n| 属性                  | 含义                                                         |\n| --------------------- | ------------------------------------------------------------ |\n| idleTimeout           | 对应请求头中的keep-alive字段值，为了避免频繁建立连接，httpClient在请求结束后会保持连接一段时间，超过这个阈值后才会关闭连接。 |\n| connectionTimeout     | 和服务器建立连接的超时，如果超过这个值则会抛出SocketException异常。 |\n| maxConnectionsPerHost | 同一个host，同时允许建立连接的最大数量。                     |\n| autoUncompress        | 对应请求头中的Content-Encoding，如果设置为true，则请求头中Content-Encoding的值为当前HttpClient支持的压缩算法列表，目前只有\"gzip\" |\n| userAgent             | 对应请求头中的User-Agent字段。                               |\n\n可以发现，有些属性只是为了更方便的设置请求头，对于这些属性，你完全可以通过`HttpClientRequest`直接设置header，不同的是通过`HttpClient`设置的对整个`httpClient`都生效，而通过`HttpClientRequest`设置的只对当前请求生效。\n\n#### HTTP请求认证\n\nHttp协议的认证（Authentication）机制可以用于保护非公开资源。如果Http服务器开启了认证，那么用户在发起请求时就需要携带用户凭据，如果你在浏览器中访问了启用Basic认证的资源时，浏览就会弹出一个登录框，如：\n\n![image-20181031114207514](https://cdn.jsdelivr.net/gh/flutterchina/flutter-in-action@1.0/docs/imgs/image-20181031114207514.png)\n\n\n\n我们先看看Basic认证的基本过程：\n\n1. 客户端发送http请求给服务器，服务器验证该用户是否已经登录验证过了，如果没有的话，  服务器会返回一个401 Unauthozied给客户端，并且在响应header中添加一个 “WWW-Authenticate” 字段，例如：\n\n   ```\n   WWW-Authenticate: Basic realm=\"admin\"\n   ```\n   其中\"Basic\"为认证方式，realm为用户角色的分组，可以在后台添加分组。\n\n2. 客户端得到响应码后，将用户名和密码进行base64编码（格式为用户名:密码），设置请求头Authorization，继续访问 :\n\n   ```\n   Authorization: Basic YXXFISDJFISJFGIJIJG\n   ```\n\n   服务器验证用户凭据，如果通过就返回资源内容。\n\n注意，Http的方式除了Basic认证之外还有：Digest认证、Client认证、Form Based认证等，目前Flutter的HttpClient只支持Basic和Digest两种认证方式，这两种认证方式最大的区别是发送用户凭据时，对于用户凭据的内容，前者只是简单的通过Base64编码（可逆），而后者会进行哈希运算，相对来说安全一点点，但是为了安全起见，**无论是采用Basic认证还是Digest认证，都应该在Https协议下**，这样可以防止抓包和中间人攻击。\n\n`HttpClient`关于Http认证的方法和属性：\n\n1. `addCredentials(Uri url, String realm, HttpClientCredentials credentials)`\n\n   该方法用于添加用户凭据,如：\n\n   ```dart\n   httpClient.addCredentials(_uri,\n    \"admin\", \n     new HttpClientBasicCredentials(\"username\",\"password\"), //Basic认证凭据\n   );\n   ```\n\n   如果是Digest认证，可以创建Digest认证凭据：\n\n   ```dart\n   HttpClientDigestCredentials(\"username\",\"password\")\n   ```\n\n2. ` authenticate(Future<bool> f(Uri url, String scheme, String realm))`\n\n   这是一个setter，类型是一个回调，当服务器需要用户凭据且该用户凭据未被添加时，httpClient会调用此回调，在这个回调当中，一般会调用`addCredential()`来动态添加用户凭证，例如：\n\n   ```dart\n   httpClient.authenticate=(Uri url, String scheme, String realm) async{\n     if(url.host==\"xx.com\" && realm==\"admin\"){\n       httpClient.addCredentials(url,\n         \"admin\",\n         new HttpClientBasicCredentials(\"username\",\"pwd\"), \n       );\n       return true;\n     }\n     return false;\n   };\n   ```\n\n   一个建议是，如果所有请求都需要认证，那么应该在HttpClient初始化时就调用`addCredentials()`来添加全局凭证，而不是去动态添加。\n\n#### 代理\n\n可以通过`findProxy`来设置代理策略，例如，我们要将所有请求通过代理服务器（192.168.1.2:8888）发送出去：\n\n```dart\n  client.findProxy = (uri) {\n    // 如果需要过滤uri，可以手动判断\n    return \"PROXY 192.168.1.2:8888\";\n };\n```\n\n`findProxy` 回调返回值是一个遵循浏览器PAC脚本格式的字符串，详情可以查看API文档，如果不需要代理，返回\"DIRECT\"即可。\n\n在APP开发中，很多时候我们需要抓包来调试，而抓包软件(如charles)就是一个代理，这时我们就可以将请求发送到我们的抓包软件，我们就可以在抓包软件中看到请求的数据了。\n\n有时代理服务器也启用了身份验证，这和http协议的认证是相似的，HttpClient提供了对应的Proxy认证方法和属性：\n\n```dart\nset authenticateProxy(\n    Future<bool> f(String host, int port, String scheme, String realm));\nvoid addProxyCredentials(\n    String host, int port, String realm, HttpClientCredentials credentials);\n```\n\n他们的使用方法和上面“HTTP请求认证”一节中介绍的`addCredentials`和`authenticate` 相同，故不再赘述。\n\n#### 证书校验\n\nHttps中为了防止通过伪造证书而发起的中间人攻击，客户端应该对自签名或非CA颁发的证书进行校验。`HttpClient`对证书校验的逻辑如下：\n\n1. 如果请求的Https证书是可信CA颁发的，并且访问host包含在证书的domain列表中(或者符合通配规则)并且证书未过期，则验证通过。\n2. 如果第一步验证失败，但在创建HttpClient时，已经通过SecurityContext将证书添加到证书信任链中，那么当服务器返回的证书在信任链中的话，则验证通过。\n3. 如果1、2验证都失败了，如果用户提供了`badCertificateCallback`回调，则会调用它，如果回调返回`true`，则允许继续链接，如果返回`false`，则终止链接。\n\n综上所述，我们的证书校验其实就是提供一个`badCertificateCallback`回调，下面通过一个示例来说明。\n\n##### 示例\n\n假设我们的后台服务使用的是自签名证书，证书格式是PEM格式，我们将证书的内容保存在本地字符串中，那么我们的校验逻辑如下：\n\n```dart\nString PEM=\"XXXXX\";//可以从文件读取\n...\nhttpClient.badCertificateCallback=(X509Certificate cert, String host, int port){\n  if(cert.pem==PEM){\n    return true; //证书一致，则允许发送数据\n  }\n  return false;\n};\n```\n\n`X509Certificate`是证书的标准格式，包含了证书除私钥外所有信息，读者可以自行查阅文档。另外，上面的示例没有校验host，是因为只要服务器返回的证书内容和本地的保存一致就已经能证明是我们的服务器了（而不是中间人），host验证通常是为了防止证书和域名不匹配。\n\n对于自签名的证书，我们也可以将其添加到本地证书信任链中，这样证书验证时就会自动通过，而不会再走到`badCertificateCallback`回调中：\n\n```dart\nSecurityContext sc=new SecurityContext();\n//file为证书路径\nsc.setTrustedCertificates(file);\n//创建一个HttpClient\nHttpClient httpClient = new HttpClient(context: sc);\n```\n\n注意，通过`setTrustedCertificates()`设置的证书格式必须为PEM或PKCS12，如果证书格式为PKCS12，则需将证书密码传入，这样则会在代码中暴露证书密码，所以客户端证书校验不建议使用PKCS12格式的证书。\n\n#### 总结\n\n值得注意的是，`HttpClient`提供的这些属性和方法最终都会作用在请求header里，我们完全可以通过手动去设置header来实现，之所以提供这些方法，只是为了方便开发者而已。另外，Http协议是一个非常重要的、使用最多的网络协议，每一个开发者都应该对http协议非常熟悉。\n\n"
  },
  {
    "path": "src/chapter11/index.md",
    "content": "# 本章目录\n\n* [11.1：文件操作](file_operation.md)\n* [11.2：Http请求-HttpClient](http.md)\n* [11.3：Http请求-Dio package](dio.md) \n* [11.4：实例：Http分块下载](download_with_chunks.md) \n* [11.5：WebSocket](websocket.md) \n* [11.6：使用Socket API](socket.md) \n* [11.7：Json转Dart Model类](json_model.md) \n"
  },
  {
    "path": "src/chapter11/json_model.md",
    "content": "# 11.7 Json转Dart Model类\n\n在实战中，后台接口往往会返回一些结构化数据，如JSON、XML等，如之前我们请求Github API的示例，它返回的数据就是JSON格式的字符串，为了方便我们在代码中操作JSON，我们先将JSON格式的字符串转为Dart对象，这个可以通过`dart:convert`中内置的JSON解码器json.decode() 来实现，该方法可以根据JSON字符串具体内容将其转为List或Map，这样我们就可以通过他们来查找所需的值，如：\n\n```dart\n//一个JSON格式的用户列表字符串\nString jsonStr='[{\"name\":\"Jack\"},{\"name\":\"Rose\"}]';\n//将JSON字符串转为Dart对象(此处是List)\nList items=json.decode(jsonStr);\n//输出第一个用户的姓名\nprint(items[0][\"name\"]);\n```\n\n通过json.decode() 将JSON字符串转为List/Map的方法比较简单，它没有外部依赖或其它的设置，对于小项目很方便。但当项目变大时，这种手动编写序列化逻辑可能变得难以管理且容易出错，例如有如下JSON:\n\n```json\n{\n  \"name\": \"John Smith\",\n  \"email\": \"john@example.com\"\n}\n```\n\n我们可以通过调用`json.decode`方法来解码JSON ，使用JSON字符串作为参数:\n\n```dart\nMap<String, dynamic> user = json.decode(json);\n\nprint('Howdy, ${user['name']}!');\nprint('We sent the verification link to ${user['email']}.');\n```\n\n\n\n由于`json.decode()`仅返回一个`Map<String, dynamic>`，这意味着直到运行时我们才知道值的类型。 通过这种方法，我们失去了大部分静态类型语言特性：类型安全、自动补全和最重要的编译时异常。这样一来，我们的代码可能会变得非常容易出错。例如，当我们访问`name`或`email`字段时，我们输入的很快，导致字段名打错了。但由于这个JSON在map结构中，所以编译器不知道这个错误的字段名，所以编译时不会报错。\n\n其实，这个问题在很多平台上都会遇到，而也早就有了好的解决方法即“Json Model化”，具体做法就是，通过预定义一些与Json结构对应的Model类，然后在请求到数据后再动态根据数据创建出Model类的实例。这样一来，在开发阶段我们使用的是Model类的实例，而不再是Map/List，这样访问内部属性时就不会发生拼写错误。例如，我们可以通过引入一个简单的模型类(Model class)来解决前面提到的问题，我们称之为`User`。在User类内部，我们有：\n\n- 一个`User.fromJson` 构造函数, 用于从一个map构造出一个 `User`实例 map structure\n- 一个`toJson` 方法, 将 `User` 实例转化为一个map.\n\n这样，调用代码现在可以具有类型安全、自动补全字段（name和email）以及编译时异常。如果我们将拼写错误字段视为`int`类型而不是`String`， 那么我们的代码就不会通过编译，而不是在运行时崩溃。\n\n**user.dart**\n\n```dart\nclass User {\n  final String name;\n  final String email;\n\n  User(this.name, this.email);\n\n  User.fromJson(Map<String, dynamic> json)\n      : name = json['name'],\n        email = json['email'];\n\n  Map<String, dynamic> toJson() =>\n    <String, dynamic>{\n      'name': name,\n      'email': email,\n    };\n}\n```\n\n现在，序列化逻辑移到了模型本身内部。采用这种新方法，我们可以非常容易地反序列化user.\n\n```dart\nMap userMap = json.decode(json);\nvar user = new User.fromJson(userMap);\n\nprint('Howdy, ${user.name}!');\nprint('We sent the verification link to ${user.email}.');\n```\n\n要序列化一个user，我们只是将该`User`对象传递给该`json.encode`方法。我们不需要手动调用`toJson`这个方法，因为`JSON.encode内部会自动调用。\n\n```dart\nString json = json.encode(user);\n```\n\n这样，调用代码就不用担心JSON序列化了，但是，Model类还是必须的。在实践中，`User.fromJson`和`User.toJson`方法都需要单元测试到位，以验证正确的行为。\n\n另外，实际场景中，JSON对象很少会这么简单，嵌套的JSON对象并不罕见，如果有什么能为我们自动处理JSON序列化，那将会非常好。幸运的是，有！\n\n### 自动生成Model\n\n尽管还有其他库可用，但在本书中，我们介绍一下官方推荐的[json_serializable package](https://pub.dartlang.org/packages/json_serializable)包。 它是一个自动化的源代码生成器，可以在开发阶段为我们生成JSON序列化模板，这样一来，由于序列化代码不再由我们手写和维护，我们将运行时产生JSON序列化异常的风险降至最低。\n\n### 在项目中设置json_serializable\n\n要包含`json_serializable`到我们的项目中，我们需要一个常规和两个**开发依赖**项。简而言之，**开发依赖项**是不包含在我们的应用程序源代码中的依赖项，它是开发过程中的一些辅助工具、脚本，和node中的开发依赖项相似。\n\n**pubspec.yaml**\n\n```yaml\ndependencies:\n  # Your other regular dependencies here\n  json_annotation: ^2.0.0\n\ndev_dependencies:\n  # Your other dev_dependencies here\n  build_runner: ^1.0.0\n  json_serializable: ^2.0.0\n```\n\n在您的项目根文件夹中运行 `flutter packages get` (或者在编辑器中点击 “Packages Get”) 以在项目中使用这些新的依赖项.\n\n### 以json_serializable的方式创建model类\n\n让我们看看如何将我们的`User`类转换为一个`json_serializable`。为了简单起见，我们使用前面示例中的简化JSON model。\n\n**user.dart**\n\n```dart\nimport 'package:json_annotation/json_annotation.dart';\n\n// user.g.dart 将在我们运行生成命令后自动生成\npart 'user.g.dart';\n\n///这个标注是告诉生成器，这个类是需要生成Model类的\n@JsonSerializable()\n\nclass User{\n  User(this.name, this.email);\n\n  String name;\n  String email;\n  //不同的类使用不同的mixin即可\n  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);\n  Map<String, dynamic> toJson() => _$UserToJson(this);  \n}\n```\n\n有了上面的设置，源码生成器将生成用于序列化`name`和`email`字段的JSON代码。\n\n如果需要，自定义命名策略也很容易。例如，如果我们正在使用的API返回带有_snake_case_的对象，但我们想在我们的模型中使用_lowerCamelCase_， 那么我们可以使用@JsonKey标注：\n\n```dart\n//显式关联JSON字段名与Model属性的对应关系 \n@JsonKey(name: 'registration_date_millis')\nfinal int registrationDateMillis;\n```\n\n### 运行代码生成程序\n\n`json_serializable`第一次创建类时，您会看到与图11-4类似的错误。\n\n![ide_warning](../imgs/11-4.png)\n\n这些错误是完全正常的，这是因为Model类的生成代码还不存在。为了解决这个问题，我们必须运行代码生成器来为我们生成序列化模板。有两种运行代码生成器的方法：\n\n#### 一次性生成\n\n通过在我们的项目根目录下运行:\n\n```shell\nflutter packages pub run build_runner build\n```\n\n 这触发了一次性构建，我们可以在需要时为我们的Model生成json序列化代码，它通过我们的源文件，找出需要生成Model类的源文件（包含@JsonSerializable标注的）来生成对应的.g.dart文件。一个好的建议是将所有Model类放在一个单独的目录下，然后在该目录下执行命令。\n\n虽然这非常方便，但如果我们不需要每次在Model类中进行更改时都要手动运行构建命令的话会更好。\n\n#### 持续生成\n\n使用_watcher_可以使我们的源代码生成的过程更加方便。它会监视我们项目中文件的变化，并在需要时自动构建必要的文件，我们可以通过`flutter packages pub run build_runner watch`在项目根目录下运行来启动_watcher_。只需启动一次观察器，然后它就会在后台运行，这是安全的。\n\n\n\n### 自动化生成模板\n\n上面的方法有一个最大的问题就是要为每一个json写模板，这是比较枯燥的。如果有一个工具可以直接根据JSON文本生成模板，那我们就能彻底解放双手了。笔者自己用dart实现了一个脚本，它可以自动生成模板，并直接将JSON转为Model类，下面我们看看怎么做：\n\n1. 定义一个\"模板的模板\"，名为\"template.dart\"：\n\n   ```dart\n   import 'package:json_annotation/json_annotation.dart';\n   %t\n   part '%s.g.dart';\n   @JsonSerializable()\n   class %s {\n       %s();\n   \n       %s\n       factory %s.fromJson(Map<String,dynamic> json) => _$%sFromJson(json);\n       Map<String, dynamic> toJson() => _$%sToJson(this);\n   }\n   ```\n\n   模板中的“%t”、“%s”为占位符，将在脚本运行时动态被替换为合适的导入头和类名。\n\n2. 写一个自动生成模板的脚本(mo.dart)，它可以根据指定的JSON目录，遍历生成模板，在生成时我们定义一些规则：\n\n   - 如果JSON文件名以下划线“_”开始，则忽略此JSON文件。\n   - 复杂的JSON对象往往会出现嵌套，我们可以通过一个特殊标志来手动指定嵌套的对象（后面举例）。\n\n   脚本我们通过Dart来写，源码如下：\n\n   ```dart\n   import 'dart:convert';\n   import 'dart:io';\n   import 'package:path/path.dart' as path;\n   const TAG=\"\\$\";\n   const SRC=\"./json\"; //JSON 目录\n   const DIST=\"lib/models/\"; //输出model目录\n   \n   void walk() { //遍历JSON目录生成模板\n     var src = new Directory(SRC);\n     var list = src.listSync();\n     var template=new File(\"./template.dart\").readAsStringSync();\n     File file;\n     list.forEach((f) {\n       if (FileSystemEntity.isFileSync(f.path)) {\n         file = new File(f.path);\n         var paths=path.basename(f.path).split(\".\");\n         String name=paths.first;\n         if(paths.last.toLowerCase()!=\"json\"||name.startsWith(\"_\")) return ;\n         if(name.startsWith(\"_\")) return;\n         //下面生成模板\n         var map = json.decode(file.readAsStringSync());\n         //为了避免重复导入相同的包，我们用Set来保存生成的import语句。\n         var set= new Set<String>();\n         StringBuffer attrs= new StringBuffer();\n         (map as Map<String, dynamic>).forEach((key, v) {\n             if(key.startsWith(\"_\")) return ;\n             attrs.write(getType(v,set,name));\n             attrs.write(\" \");\n             attrs.write(key);\n             attrs.writeln(\";\");\n             attrs.write(\"    \");\n         });\n         String  className=name[0].toUpperCase()+name.substring(1);\n         var dist=format(template,[name,className,className,attrs.toString(),\n                                   className,className,className]);\n         var _import=set.join(\";\\r\\n\");\n         _import+=_import.isEmpty?\"\":\";\";\n         dist=dist.replaceFirst(\"%t\",_import );\n         //将生成的模板输出\n         new File(\"$DIST$name.dart\").writeAsStringSync(dist);\n       }\n     });\n   }\n   \n   String changeFirstChar(String str, [bool upper=true] ){\n     return (upper?str[0].toUpperCase():str[0].toLowerCase())+str.substring(1);\n   }\n   \n   //将JSON类型转为对应的dart类型\n    String getType(v,Set<String> set,String current){\n     current=current.toLowerCase();\n     if(v is bool){\n       return \"bool\";\n     }else if(v is num){\n       return \"num\";\n     }else if(v is Map){\n       return \"Map<String,dynamic>\";\n     }else if(v is List){\n       return \"List\";\n     }else if(v is String){ //处理特殊标志\n       if(v.startsWith(\"$TAG[]\")){\n         var className=changeFirstChar(v.substring(3),false);\n         if(className.toLowerCase()!=current) {\n           set.add('import \"$className.dart\"');\n         }\n         return \"List<${changeFirstChar(className)}>\";\n   \n       }else if(v.startsWith(TAG)){\n         var fileName=changeFirstChar(v.substring(1),false);\n         if(fileName.toLowerCase()!=current) {\n           set.add('import \"$fileName.dart\"');\n         }\n         return changeFirstChar(fileName);\n       }\n       return \"String\";\n     }else{\n       return \"String\";\n     }\n    }\n   \n   //替换模板占位符\n   String format(String fmt, List<Object> params) {\n     int matchIndex = 0;\n     String replace(Match m) {\n       if (matchIndex < params.length) {\n         switch (m[0]) {\n           case \"%s\":\n             return params[matchIndex++].toString();\n         }\n       } else {\n         throw new Exception(\"Missing parameter for string format\");\n       }\n       throw new Exception(\"Invalid format string: \" + m[0].toString());\n     }\n     return fmt.replaceAllMapped(\"%s\", replace);\n   }\n   \n   void main(){\n     walk();\n   }\n   ```\n\n3. 写一个shell(mo.sh)，将生成模板和生成model串起来：\n\n   ```sh\n   dart mo.dart\n   flutter packages pub run build_runner build --delete-conflicting-outputs\n   ```\n\n至此，我们的脚本写好了，我们在根目录下新建一个json目录，然后把user.json移进去，然后在lib目录下创建一个models目录，用于保存最终生成的Model类。现在我们只需要一句命令即可生成Model类了:\n\n```\n./mo.sh  \n```\n\n运行后，一切都将自动执行，现在好多了，不是吗？\n\n#### 嵌套JSON\n\n我们定义一个person.json内容修改为：\n\n```json\n{\n  \"name\": \"John Smith\",\n  \"email\": \"john@example.com\",\n  \"mother\":{\n    \"name\": \"Alice\",\n    \"email\":\"alice@example.com\"\n  },\n  \"friends\":[\n    {\n      \"name\": \"Jack\",\n      \"email\":\"Jack@example.com\"\n    },\n    {\n      \"name\": \"Nancy\",\n      \"email\":\"Nancy@example.com\"\n    }\n  ]\n}\n```\n\n每个Person都有`name` 、`email` 、 `mother`和`friends`四个字段，由于`mother`也是一个Person，朋友是多个Person(数组)，所以我们期望生成的Model是下面这样：\n\n```dart\nimport 'package:json_annotation/json_annotation.dart';\npart 'person.g.dart';\n\n@JsonSerializable()\nclass Person {\n    Person();\n    \n    String name;\n    String email;\n    Person mother;\n    List<Person> friends;\n\n    factory Person.fromJson(Map<String,dynamic> json) => _$PersonFromJson(json);\n    Map<String, dynamic> toJson() => _$PersonToJson(this);\n}\n\n```\n\n这时，我们只需要简单修改一下JSON，添加一些特殊标志，重新运行mo.sh即可：\n\n```json\n{\n  \"name\": \"John Smith\",\n  \"email\": \"john@example.com\",\n  \"mother\":\"$person\",\n  \"friends\":\"$[]person\"\n}\n```\n\n我们使用美元符“$”作为特殊标志符(如果与内容冲突，可以修改mo.dart中的`TAG`常量，自定义标志符)，脚本在遇到特殊标志符后会先把相应字段转为相应的对象或对象数组，对象数组需要在标志符后面添加数组符“[]”，符号后面接具体的类型名，此例中是person。其它类型同理，加入我们给User添加一个Person类型的 `boss`字段：\n\n```json\n{\n  \"name\": \"John Smith\",\n  \"email\": \"john@example.com\",\n  \"boss\":\"$person\"\n}\n```\n\n \n\n重新运行mo.sh，生成的user.dart如下：\n\n```dart\nimport 'package:json_annotation/json_annotation.dart';\nimport \"person.dart\";\npart 'user.g.dart';\n\n@JsonSerializable()\n\nclass User {\n    User();\n\n    String name;\n    String email;\n    Person boss;\n    \n    factory User.fromJson(Map<String,dynamic> json) => _$UserFromJson(json);\n    Map<String, dynamic> toJson() => _$UserToJson(this);\n}\n```\n可以看到，`boss`字段已自动添加，并自动导入了“person.dart”。\n\n### Json_model 包\n\n如果每个项目都要构建一个上面这样的脚本显然很麻烦，为此，我们将上面脚本和生成模板封装了一个包,已经发布到了Pub上，包名为[Json_model](https://github.com/flutterchina/json_model)，开发者把该包加入开发依赖后，便可以用一条命令，根据Json文件生成Dart类。另外[Json_model](https://github.com/flutterchina/json_model) 处于迭代中，功能会逐渐完善，所以建议读者直接使用该包（而不是手动复制上面的代码）。\n\n### 使用IDE插件生成model\n\n目前Android Studio(或IntelliJ)有几个插件，可以将json文件转成Model类，但插件质量参差不齐，甚至还有一些沾染上了抄袭风波，故笔者在此不做优先推荐，读者有兴趣可以自行了解。但是，我们还是要了解一下IDE插件和[Json_model](https://link.juejin.im?target=https%3A%2F%2Fgithub.com%2Fflutterchina%2Fjson_model)的优劣：\n\n1. [Json_model](https://link.juejin.im?target=https%3A%2F%2Fgithub.com%2Fflutterchina%2Fjson_model)需要单独维护一个存放Json文件的文件夹，如果有改动，只需修改Json文件便可重新生成Model类；而IDE插件一般需要用户手动将Json内容拷贝复制到一个输入框中，这样生成之后Json文件没有存档的化，之后要改动就需要手动。\n2. [Json_model](https://link.juejin.im?target=https%3A%2F%2Fgithub.com%2Fflutterchina%2Fjson_model)可以手动指定某个字段引用的其它Model类，可以避免生成重复的类；而IDE插件一般会为每一个Json文件中所有嵌套对象都单独生成一个Model类，即使这些嵌套对象可能在其它Model类中已经生成过。\n3. [Json_model](https://link.juejin.im?target=https%3A%2F%2Fgithub.com%2Fflutterchina%2Fjson_model) 提供了命令行转化方式，可以方便集成到CI等非UI环境的场景。\n\n### FAQ\n\n很多人可能会问Flutter中有没有像Java开发中的Gson/Jackson一样的Json序列化类库？答案是没有！因为这样的库需要使用运行时反射，这在Flutter中是禁用的。运行时反射会干扰Dart的_tree shaking_，使用_tree shaking_，可以在release版中“去除”未使用的代码，这可以显著优化应用程序的大小。由于反射会默认应用到所有代码，因此_tree shaking_会很难工作，因为在启用反射时很难知道哪些代码未被使用，因此冗余代码很难剥离，所以Flutter中禁用了Dart的反射功能，而正因如此也就无法实现动态转化Model的功能。\n\n \n\n\n"
  },
  {
    "path": "src/chapter11/socket.md",
    "content": "# 11.6 使用Socket API\n\n我们之前介绍的Http协议和WebSocket协议都属于应用层协议，除了它们，应用层协议还有很多如：SMTP、FTP等，这些应用层协议的实现都是通过Socket API来实现的。其实，操作系统中提供的原生网络请求API是标准的，在C语言的Socket库中，它主要提供了端到端建立链接和发送数据的基础API，而高级编程语言中的Socket库其实都是对操作系统的socket API的一个封装。所以，如果我们需要自定义协议或者想直接来控制管理网络链接、又或者我们觉得自带的HttpClient不好用想重新实现一个，这时我们就需要使用Socket。Flutter的Socket API在dart：io包中，下面我们看一个使用Socket实现简单http请求的示例，以请求百度首页为例：\n\n```dart\n_request() async{\n  //建立连接\n  var socket=await Socket.connect(\"baidu.com\", 80);\n  //根据http协议，发送请求头\n  socket.writeln(\"GET / HTTP/1.1\");\n  socket.writeln(\"Host:baidu.com\");\n  socket.writeln(\"Connection:close\");\n  socket.writeln();\n  await socket.flush(); //发送\n  //读取返回内容\n  _response =await socket.transform(utf8.decoder).join();\n  await socket.close();\n}\n```\n\n可以看到，使用Socket需要我们自己实现Http协议（需要自己实现和服务器的通信过程），本例只是一个简单示例，没有处理重定向、cookie等。本示例完整代码参考示例demo，运行后效果如图11-2所示：\n\n![图11-2](../imgs/11-2.png)\n\n可以看到响应内容分两个部分，第一部分是响应头，第二部分是响应体，服务端可以根据请求信息动态来输出响应体。由于本示例请求头比较简单，所以响应体和浏览器中访问的会有差别，读者可以补充一些请求头(如user-agent)来看看输出的变化。\n\n"
  },
  {
    "path": "src/chapter11/websocket.md",
    "content": "\n# 使用WebSockets\n\nHttp协议是无状态的，只能由客户端主动发起，服务端再被动响应，服务端无法向客户端主动推送内容，并且一旦服务器响应结束，链接就会断开(见注解部分)，所以无法进行实时通信。WebSocket协议正是为解决客户端与服务端实时通信而产生的技术，现在已经被主流浏览器支持，所以对于Web开发者来说应该比较熟悉了，Flutter也提供了专门的包来支持WebSocket协议。\n\n> 注意：Http协议中虽然可以通过keep-alive机制使服务器在响应结束后链接会保持一段时间，但最终还是会断开，keep-alive机制主要是用于避免在同一台服务器请求多个资源时频繁创建链接，它本质上是支持链接复用的技术，而并非用于实时通信，读者需要知道这两者的区别。\n\nWebSocket协议本质上是一个基于tcp的协议，它是先通过HTTP协议发起一条特殊的http请求进行握手后，如果服务端支持WebSocket协议，则会进行协议升级。WebSocket会使用http协议握手后创建的tcp链接，和http协议不同的是，WebSocket的tcp链接是个长链接（不会断开），所以服务端与客户端就可以通过此TCP连接进行实时通信。有关WebSocket协议细节，读者可以看RFC文档，下面我们重点看看Flutter中如何使用WebSocket。\n\n在接下来例子中，我们将连接到由[websocket.org提供的测试服务器](http://www.websocket.org/echo.html)。服务器将简单地返回我们发送给它的相同消息！\n\n### 步骤\n\n1. 连接到WebSocket服务器。\n2. 监听来自服务器的消息。\n3. 将数据发送到服务器。\n4. 关闭WebSocket连接。\n\n### 1. 连接到WebSocket服务器\n\n[web_socket_channel](https://pub.dartlang.org/packages/web_socket_channel) package 提供了我们需要连接到WebSocket服务器的工具。该package提供了一个`WebSocketChannel`允许我们既可以监听来自服务器的消息，又可以将消息发送到服务器的方法。\n\n在Flutter中，我们可以创建一个`WebSocketChannel`连接到一台服务器：\n\n```dart\nfinal channel = IOWebSocketChannel.connect('ws://echo.websocket.org');\n```\n\n### 2. 监听来自服务器的消息\n\n现在我们建立了连接，我们可以监听来自服务器的消息，在我们发送消息给测试服务器之后，它会返回相同的消息。\n\n我们如何收取消息并显示它们？在这个例子中，我们将使用一个[`StreamBuilder`](https://docs.flutter.io/flutter/widgets/StreamBuilder-class.html) 来监听新消息， 并用一个Text来显示它们。\n\n```dart\nnew StreamBuilder(\n  stream: widget.channel.stream,\n  builder: (context, snapshot) {\n    return new Text(snapshot.hasData ? '${snapshot.data}' : '');\n  },\n);\n```\n\n#### 工作原理\n\n`WebSocketChannel`提供了一个来自服务器的消息`Stream` 。该`Stream`类是`dart:async`包中的一个基础类。它提供了一种方法来监听来自数据源的异步事件。与`Future`返回单个异步响应不同，`Stream`类可以随着时间推移传递很多事件。该[`StreamBuilder`](https://docs.flutter.io/flutter/widgets/StreamBuilder-class.html) 组件将连接到一个`Stream`， 并在每次收到消息时通知Flutter重新构建界面。\n\n### 3. 将数据发送到服务器\n\n为了将数据发送到服务器，我们会`add`消息给`WebSocketChannel`提供的sink。\n\n```dart\nchannel.sink.add('Hello!');\n```\n\n#### 工作原理\n\n`WebSocketChannel`提供了一个[`StreamSink`](https://docs.flutter.io/flutter/dart-async/StreamSink-class.html)，它将消息发给服务器。\n\n`StreamSink`类提供了给数据源同步或异步添加事件的一般方法。\n\n### 4. 关闭WebSocket连接\n\n在我们使用`WebSocket`后，要关闭连接：\n\n```dart\nchannel.sink.close();\n```\n\n### 完整的例子\n\n```dart\nimport 'package:flutter/material.dart';\nimport 'package:web_socket_channel/io.dart';\n\nclass WebSocketRoute extends StatefulWidget {\n  @override\n  _WebSocketRouteState createState() => new _WebSocketRouteState();\n}\n\nclass _WebSocketRouteState extends State<WebSocketRoute> {\n  TextEditingController _controller = new TextEditingController();\n  IOWebSocketChannel channel;\n  String _text = \"\";\n\n\n  @override\n  void initState() {\n    //创建websocket连接\n    channel = new IOWebSocketChannel.connect('ws://echo.websocket.org');\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return new Scaffold(\n      appBar: new AppBar(\n        title: new Text(\"WebSocket(内容回显)\"),\n      ),\n      body: new Padding(\n        padding: const EdgeInsets.all(20.0),\n        child: new Column(\n          crossAxisAlignment: CrossAxisAlignment.start,\n          children: <Widget>[\n            new Form(\n              child: new TextFormField(\n                controller: _controller,\n                decoration: new InputDecoration(labelText: 'Send a message'),\n              ),\n            ),\n            new StreamBuilder(\n              stream: channel.stream,\n              builder: (context, snapshot) {\n                //网络不通会走到这\n                if (snapshot.hasError) {\n                  _text = \"网络不通...\";\n                } else if (snapshot.hasData) {\n                  _text = \"echo: \"+snapshot.data;\n                }\n                return new Padding(\n                  padding: const EdgeInsets.symmetric(vertical: 24.0),\n                  child: new Text(_text),\n                );\n              },\n            )\n          ],\n        ),\n      ),\n      floatingActionButton: new FloatingActionButton(\n        onPressed: _sendMessage,\n        tooltip: 'Send message',\n        child: new Icon(Icons.send),\n      ),\n    );\n  }\n\n  void _sendMessage() {\n    if (_controller.text.isNotEmpty) {\n      channel.sink.add(_controller.text);\n    }\n  }\n\n  @override\n  void dispose() {\n    channel.sink.close();\n    super.dispose();\n  }\n}\n```\n\n上面的例子比较简单，不再赘述。我们现在思考一个问题，假如我们想通过WebSocket传输二进制数据应该怎么做（比如要从服务器接收一张图片）？我们发现`StreamBuilder`和`Stream`都没有指定接收类型的参数，并且在创建WebSocket链接时也没有相应的配置，貌似没有什么办法……其实很简单，要接收二进制数据仍然使用`StreamBuilder`，因为WebSocket中所有发送的数据使用帧的形式发送，而帧是有固定格式，每一个帧的数据类型都可以通过Opcode字段指定，它可以指定当前帧是文本类型还是二进制类型（还有其它类型），所以客户端在收到帧时就已经知道了其数据类型，所以flutter完全可以在收到数据后解析出正确的类型，所以就无需开发者去关心，当服务器传输的数据是指定为二进制时，`StreamBuilder`的`snapshot.data`的类型就是`List<int>`，是文本时，则为`String`。\n"
  },
  {
    "path": "src/chapter12/android_implement.md",
    "content": "\n\n# 12.4 插件开发：Android端API实现\n\n本节我们接着上一节\"获取电池电量\"插件的示例，来完成Android端API的实现。以下步骤是使用Java的示例，如果您更喜欢Kotlin，可以直接跳到后面Kotlin部分。\n\n首先在Android Studio中打开您的Flutter应用的Android部分：\n\n1. 启动 Android Studio\n2. 选择 File > Open…\n3. 定位到您 Flutter app目录, 然后选择里面的 `android`文件夹，点击 OK\n4. 在`java`目录下打开 `MainActivity.java`\n\n接下来，在`onCreate`里创建MethodChannel并设置一个`MethodCallHandler`。确保使用和Flutter客户端中使用的通道名称相同的名称。\n\n```dart\nimport io.flutter.app.FlutterActivity;\nimport io.flutter.plugin.common.MethodCall;\nimport io.flutter.plugin.common.MethodChannel;\nimport io.flutter.plugin.common.MethodChannel.MethodCallHandler;\nimport io.flutter.plugin.common.MethodChannel.Result;\n\npublic class MainActivity extends FlutterActivity {\n    private static final String CHANNEL = \"samples.flutter.io/battery\";\n\n    @Override\n    public void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(\n          new MethodCallHandler() {\n             @Override\n             public void onMethodCall(MethodCall call, Result result) {\n                 // TODO\n             }\n          });\n    }\n}\n```\n\n接下来，我们添加Java代码，使用Android电池API来获取电池电量。此代码和在原生Android应用中编写的代码完全相同。\n\n首先，添加需要导入的依赖。\n\n```dart\nimport android.content.ContextWrapper;\nimport android.content.Intent;\nimport android.content.IntentFilter;\nimport android.os.BatteryManager;\nimport android.os.Build.VERSION;\nimport android.os.Build.VERSION_CODES;\nimport android.os.Bundle;\n```\n\n然后，将下面的新方法添加到activity类中的，位于onCreate 方法下方：\n\n```java\nprivate int getBatteryLevel() {\n  int batteryLevel = -1;\n  if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {\n    BatteryManager batteryManager = (BatteryManager) getSystemService(BATTERY_SERVICE);\n    batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);\n  } else {\n    Intent intent = new ContextWrapper(getApplicationContext()).\n        registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));\n    batteryLevel = (intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100) /\n        intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);\n  }\n\n  return batteryLevel;\n}\n```\n\n最后，我们完成之前添加的`onMethodCall`方法。我们需要处理平台方法名为`getBatteryLevel`的调用消息，所以我们需要先在call参数判断调用的方法是否为`getBatteryLevel`。 这个平台方法的实现只需调用我们在前一步中编写的Android代码，并通过result参数返回成功或错误情况的响应信息。如果调用了未定义的API，我们也会通知返回：\n\n```java\n@Override\npublic void onMethodCall(MethodCall call, Result result) {\n    if (call.method.equals(\"getBatteryLevel\")) {\n        int batteryLevel = getBatteryLevel();\n\n        if (batteryLevel != -1) {\n            result.success(batteryLevel);\n        } else {\n            result.error(\"UNAVAILABLE\", \"Battery level not available.\", null);\n        }\n    } else {\n        result.notImplemented();\n    }\n}  \n```\n\n现在就可以在Android上运行该应用程序了，如果使用的是Android模拟器，则可以通过工具栏中的\"...\"按钮访问Extended Controls面板中的电池电量。\n\n### 使用Kotlin添加Android平台特定的实现\n\n使用Kotlin和使用Java的步骤类似，首先在Android Studio中打开您的Flutter应用的Android部分：\n\n1. 启动 Android Studio。\n2. 选择 the menu item \"File > Open…\"。\n3. 定位到 Flutter app目录, 然后选择里面的 `android`文件夹，点击 OK。\n4. 在`kotlin`目录中打开`MainActivity.kt`。\n\n接下来，在`onCreate`里创建MethodChannel并设置一个`MethodCallHandler`。确保使用与在Flutter客户端使用的通道名称相同。\n\n```kotlin\nimport android.os.Bundle\nimport io.flutter.app.FlutterActivity\nimport io.flutter.plugin.common.MethodChannel\nimport io.flutter.plugins.GeneratedPluginRegistrant\n\nclass MainActivity() : FlutterActivity() {\n  private val CHANNEL = \"samples.flutter.io/battery\"\n\n  override fun onCreate(savedInstanceState: Bundle?) {\n    super.onCreate(savedInstanceState)\n    GeneratedPluginRegistrant.registerWith(this)\n\n    MethodChannel(flutterView, CHANNEL).setMethodCallHandler { call, result ->\n      // TODO\n    }\n  }\n}\n```\n\n接下来，我们添加Kotlin代码，使用Android电池API来获取电池电量，这和原生开发是一样的。\n\n首先，添加需要导入的依赖。\n\n```kotlin\nimport android.content.Context\nimport android.content.ContextWrapper\nimport android.content.Intent\nimport android.content.IntentFilter\nimport android.os.BatteryManager\nimport android.os.Build.VERSION\nimport android.os.Build.VERSION_CODES\n```\n\n然后，将下面的新方法添加到activity类中的，位于onCreate 方法下方：\n\n```kotlin\n  private fun getBatteryLevel(): Int {\n    val batteryLevel: Int\n    if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {\n      val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager\n      batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)\n    } else {\n      val intent = ContextWrapper(applicationContext).registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))\n      batteryLevel = intent!!.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100 / intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)\n    }\n\n    return batteryLevel\n  }\n```\n\n最后，我们完成之前添加的`onMethodCall`方法。我们需要处理平台方法名为`getBatteryLevel`的调用消息，所以我们需要先在call参数判断调用的方法是否为`getBatteryLevel`。 这个平台方法的实现只需调用我们在前一步中编写的Android代码，并通过result参数返回成功或错误情况的响应信息。如果调用了未定义的API，我们也会通知返回：\n​           \n```kotlin\nMethodChannel(flutterView, CHANNEL).setMethodCallHandler { call, result ->\n  if (call.method == \"getBatteryLevel\") {\n     val batteryLevel = getBatteryLevel()\n     if (batteryLevel != -1) {\n       result.success(batteryLevel)\n     } else {\n       result.error(\"UNAVAILABLE\", \"Battery level not available.\", null)\n     }\n  } else {\n      result.notImplemented()\n  }\n}\n```\n\n您现在就可以在Android上运行该应用程序。如果您使用的是Android模拟器，则可以通过工具栏中的\"...\"按钮访问Extended Controls面板中的电池电量。\n"
  },
  {
    "path": "src/chapter12/develop_package.md",
    "content": "# 12.1 开发Package\n\n第二章中已经讲过如何使用Package（包），我们知道通过package可以创建共享的模块化代码，本节将重点讲一下如何开发并发布我们自己的Package。一个最小的Package包括：\n\n- 一个`pubspec.yaml`文件：声明了Package的名称、版本、作者等的元数据文件。\n- 一个 `lib` 文件夹：包括包中公开的(public)代码，最少应有一个`<package-name>.dart`文件\n\nFlutter Packages分为两类：\n\n- Dart包：其中一些可能包含Flutter的特定功能，因此对Flutter框架具有依赖性，这种包仅用于Flutter，例如[`fluro`](https://pub.dartlang.org/packages/fluro)包。\n- 插件包：一种专用的Dart包，其中包含用Dart代码编写的API，以及针对Android（使用Java或Kotlin）和针对iOS（使用OC或Swift）平台的特定实现，也就是说插件包括原生代码，一个具体的例子是[`battery`](https://pub.dartlang.org/packages/battery)插件包。\n\n注意，虽然Flutter的Dart运行时和Dart VM运行时不是完全相同，但是如果Package中没有涉及这些存在差异的部分，那么这样的包可以同时支持Flutter和Dart VM，如Dart http网络库[dio](https://github.com/flutterchina/dio)。\n\n下面我将带领读者一步步来开发一个Dart Package。\n\n### 第一步：创建Dart包\n\n您可以通过Android Studio：File>New>New Flutter Project 来创建一个Package工程，如图12-1所示：\n\n![图12-1](../imgs/12-1.png)\n\n您也可以通过使用`--template=package` 来执行 `flutter create` 命令来创建：\n\n```shell\nflutter create --template=package hello\n```\n\n这将在`hello/`文件夹下创建一个具有以下专用内容的package工程：\n\n- `lib/hello.dart`：Package的Dart代码\n\n- `test/hello_test.dart`：Package的单元测试代码。\n\n### 实现package\n\n对于纯Dart包，只需在主`lib/<package name>.dart`文件内或`lib`目录中的文件中添加功能即可 。要测试软件包，请在`test`目录中添加[unit tests](https://flutter.io/testing/#unit-testing)。下面我们看看如何组织Package包的代码，我们以shelf Package为例，它的目录结构如图12-2所示：\n\n![图12-2](../imgs/12-2.png)\n\n在lib根目录下的“shelf.dart”中，导出了多个“lib/src”目录下的dart文件：\n\n```dart\nexport 'src/cascade.dart';\nexport 'src/handler.dart';\nexport 'src/handlers/logger.dart';\nexport 'src/hijack_exception.dart';\nexport 'src/middleware.dart';\nexport 'src/pipeline.dart';\nexport 'src/request.dart';\nexport 'src/response.dart';\nexport 'src/server.dart';\nexport 'src/server_handler.dart';\n```\n\n而Package中主要的功能的源码都在src目录下。shelf Package也导出了一个迷你库: shelf_io，它主要是处理HttpRequest的。\n\n### **导入包**\n\n当需要使用这个Package时，我们可以通过\"package:\"指令来指定包的入口文件：\n\n```dart\nimport 'package:utilities/utilities.dart';\n```\n\n同一个包中的源码文件之间也可以使用相对路径来导入。\n\n### 生成文档\n\n可以使用 [dartdoc](https://github.com/dart-lang/dartdoc#dartdoc) 工具来为Package生成文档，开发者需要做的就是遵守文档注释语法在代码中添加文档注释，最后使用dartdoc可以直接生成API文档（一个静态网站）。文档注释是使用三斜线\"///\"开始，如：\n\n```dart\n/// The event handler responsible for updating the badge in the UI.\nvoid updateBadge() {\n  ...\n}\n```\n\n详细的文档语法请参考[dartdoc](https://github.com/dart-lang/dartdoc#dartdoc) 。\n\n### 处理包的相互依赖\n\n如果我们正在开发一个`hello`包，它依赖于另一个包，则需要将该依赖包添加到`pubspec.yaml`文件的`dependencies`部分。 下面的代码使`url_launcher`插件的API在`hello`包中是可用的：\n\n在 `hello/pubspec.yaml`中:\n\n```yaml\ndependencies:\n  url_launcher: ^0.4.2\n```\n\n现在可以在`hello`中`import 'package:url_launcher/url_launcher.dart'` 然后调用 `launch()`方法了。\n\n这与在Flutter应用程序或任何其他Dart项目中引用软件包没有什么不同。\n\n但是，如果`hello`碰巧是一个插件包，其平台特定的代码需要访问`url_launcher`公开的特定于平台的API，那么我们还需要为特定于平台的构建文件添加合适的依赖声明，如下所示。\n\n**Android**\n\n在 `hello/android/build.gradle`:\n\n```groovy\nandroid {\n    // lines skipped\n    dependencies {\n        provided rootProject.findProject(\":url_launcher\")\n    }\n}\n```\n\n您现在可以在`hello/android/src`源码中`import io.flutter.plugins.urllauncher.UrlLauncherPlugin`访问`UrlLauncherPlugin`类。\n\n**iOS**\n\n在`hello/ios/hello.podspec`:\n\n```ruby\nPod::Spec.new do |s|\n  # lines skipped\n  s.dependency 'url_launcher'\n```\n\n您现在可以在`hello/ios/Classes`源码中 `#import \"UrlLauncherPlugin.h\"` 然后访问 `UrlLauncherPlugin`类。\n\n### 解决依赖冲突\n\n假设我们想在我们的`hello`包中使用`some_package`和`other_package`，并且这两个包都依赖`url_launcher`，但是依赖的是`url_launcher`的不同的版本。 那我们就有潜在的冲突。避免这种情况的最好方法是在指定依赖关系时，程序包作者使用[版本范围](https://www.dartlang.org/tools/pub/dependencies#version-constraints)而不是特定版本。\n\n```yaml\ndependencies:\n  url_launcher: ^0.4.2    # 这样会较好, 任何0.4.x(x >= 2)都可.\n  image_picker: '0.1.1'   # 不是很好，只有0.1.1版本.\n```\n\n如果`some_package`声明了上面的依赖关系,`other_package`声明了`url_launcher`版本像’0.4.5’或’^0.4.0’，pub将能够自动解决问题。 \n\n即使`some_package`和`other_package`声明了不兼容的`url_launcher`版本，它仍然可能会和`url_launcher`以兼容的方式正常工作。 你可以通过向`hello`包的`pubspec.yaml`文件中添加依赖性覆盖声明来处理冲突，从而强制使用特定版本：\n\n强制使用 `0.4.3`版本的`url_launcher`，在 `hello/pubspec.yaml`中:\n\n```yaml\ndependencies:\n  some_package:\n  other_package:\ndependency_overrides:\n  url_launcher: '0.4.3'\n```\n\n如果冲突的依赖不是一个包，而是一个特定于Android的库，比如`guava`，那么必须将依赖重写声明添加到Gradle构建逻辑中。\n\n强制使用`23.0`版本的`guava`库，在`hello/android/build.gradle`中：\n\n```groovy\nconfigurations.all {\n    resolutionStrategy {\n        force 'com.google.guava:guava:23.0-android'\n    }\n}\n```\n\nCocoapods目前不提供依赖覆盖功能。\n\n### 发布Package\n\n一旦实现了一个包，我们可以在[Pub](https://pub.dartlang.org/)上发布它 ，这样其他开发者就可以轻松使用它。\n\n在发布之前，检查`pubspec.yaml`、`README.md`以及`CHANGELOG.md`文件，以确保其内容的完整性和正确性。然后，运行 dry-run 命令以查看是否都准备OK了:\n\n```shell\nflutter packages pub publish --dry-run\n```\n\n验证无误后，我们就可以运行发布命令了：\n\n```shell\nflutter packages pub publish\n```\n\n> 如果你遇到包发布失败的情况，先检查是否因为众所周知的网络原因，如果是网络问题，可以使用VPN，这里需要注意的是一些代理只会代理部分APP的网络请求，如浏览器的，它们可能并不能代理dart的网络请求，所以在这种情况下，即使开了代理也依然无法连接到Pub，因此，在发布Pub包时使用全局代理或全局VPN会保险些。如果网络没有问题，以管理员权限(sudo)运行发布命令重试。  \n> 很多时候开启全局代理也不会让terminal中的流量打代理服务器走，以socks5为例，应该在终端下输入以下指令：\n```shell\nexport all_proxy=socks5://127.0.0.1:1080\n```\n> 此时终端中的http和https流量会打代理服务器走，可以通过<code>curl -i https://ip.cn</code>指令查看代理设置是否成功。\n\n"
  },
  {
    "path": "src/chapter12/develop_plugin.md",
    "content": "# 12.3 开发Flutter插件\n\n下面我们通过一个获取电池电量的插件来介绍一下Flutter插件的开发流程。该插件中我们在Dart中通过`getBatteryLevel` 调用Android `BatteryManager` API和iOS `device.batteryLevel` API。 \n\n### 创建一个新的应用程序项目\n\n首先创建一个新的应用程序:\n\n- 在终端中运行：`flutter create batterylevel`\n\n默认情况下，模板支持使用Java编写Android代码，或使用Objective-C编写iOS代码。要使用Kotlin或Swift，请使用-i和/或-a标志:\n\n- 在终端中运行: `flutter create -i swift -a kotlin batterylevel`\n\n### 创建Flutter平台客户端\n\n该应用的`State`类拥有当前的应用状态。我们需要延长这一点以保持当前的电量\n\n首先，我们构建通道。我们使用`MethodChannel`调用一个方法来返回电池电量。\n\n通道的客户端和宿主通过通道构造函数中传递的通道名称进行连接。单个应用中使用的所有通道名称必须是唯一的; 我们建议在通道名称前加一个唯一的“域名前缀”，例如`samples.flutter.io/battery`。\n\n```dart\nimport 'dart:async';\n\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\n...\nclass _MyHomePageState extends State<MyHomePage> {\n  static const platform = const MethodChannel('samples.flutter.io/battery');\n\n  // Get battery level.\n}\n```\n\n接下来，我们调用通道上的方法，指定通过字符串标识符调用方法`getBatteryLevel`。 该调用可能失败(平台不支持平台API，例如在模拟器中运行时)，所以我们将invokeMethod调用包装在try-catch语句中。\n\n我们使用返回的结果，在`setState`中来更新用户界面状态`batteryLevel`。\n\n```dart\n  // Get battery level.\n  String _batteryLevel = 'Unknown battery level.';\n\n  Future<Null> _getBatteryLevel() async {\n    String batteryLevel;\n    try {\n      final int result = await platform.invokeMethod('getBatteryLevel');\n      batteryLevel = 'Battery level at $result % .';\n    } on PlatformException catch (e) {\n      batteryLevel = \"Failed to get battery level: '${e.message}'.\";\n    }\n\n    setState(() {\n      _batteryLevel = batteryLevel;\n    });\n  }\n```\n\n最后，我们在build创建包含一个小字体显示电池状态和一个用于刷新值的按钮的用户界面。\n\n```dart\n@override\nWidget build(BuildContext context) {\n  return new Material(\n    child: new Center(\n      child: new Column(\n        mainAxisAlignment: MainAxisAlignment.spaceEvenly,\n        children: [\n          new RaisedButton(\n            child: new Text('Get Battery Level'),\n            onPressed: _getBatteryLevel,\n          ),\n          new Text(_batteryLevel),\n        ],\n      ),\n    ),\n  );\n}\n```\n\n至此Flutter部分的测试代码写好了，接下来我们需要实现Android和iOS平台下的API，由于平台API实现部分篇幅较大，我们将在接下来的两节中，分别介绍Android和iOS端API的实现。\n"
  },
  {
    "path": "src/chapter12/index.md",
    "content": "\n# 包与插件\n\n* [12.1：开发package](develop_package.md)\n* [12.2：平台通道简介](platform-channel.md)\n* [12.3：开发Flutter插件](develop_plugin.md)\n* [12.4：插件开发：实现Android端API](android_implement.md)\n* [12.5：插件开发：实现IOS端API](ios_implement.md)\n* [12.6：Texture和PlatformView](texture_platformview.md) \n"
  },
  {
    "path": "src/chapter12/ios_implement.md",
    "content": "# 12.5 插件开发：iOS端API实现\n\n本节我们接着之前\"获取电池电量\"插件的示例，来完成iOS端API的实现。以下步骤使用Objective-C，如果您更喜欢Swift，可以直接跳到后面Swift部分。\n\n首先打开Xcode中Flutter应用程序的iOS部分:\n\n1. 启动 Xcode\n2. 选择 File > Open…\n3. 定位到您 Flutter app目录, 然后选择里面的 `iOS`文件夹，点击 OK\n4. 确保Xcode项目的构建没有错误。\n5. 选择 Runner > Runner ，打开`AppDelegate.m`\n\n接下来，在`application didFinishLaunchingWithOptions:`方法内部创建一个`FlutterMethodChannel`，并添加一个处理方法。 确保与在Flutter客户端使用的通道名称相同。\n\n```objectivec\n#import <Flutter/Flutter.h>\n\n@implementation AppDelegate\n- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {\n  FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController;\n\n  FlutterMethodChannel* batteryChannel = [FlutterMethodChannel\n                                          methodChannelWithName:@\"samples.flutter.io/battery\"\n                                          binaryMessenger:controller];\n\n  [batteryChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {\n    // TODO\n  }];\n\n  return [super application:application didFinishLaunchingWithOptions:launchOptions];\n}\n```\n\n接下来，我们添加Objective-C代码，使用iOS电池API来获取电池电量，这和原生是相同的。\n\n在`AppDelegate`类中添加以下新的方法：\n\n```objectivec\n- (int)getBatteryLevel {\n  UIDevice* device = UIDevice.currentDevice;\n  device.batteryMonitoringEnabled = YES;\n  if (device.batteryState == UIDeviceBatteryStateUnknown) {\n    return -1;\n  } else {\n    return (int)(device.batteryLevel * 100);\n  }\n}\n```\n\n最后，我们完成之前添加的`setMethodCallHandler`方法。我们需要处理的平台方法名为`getBatteryLevel`，所以我们在call参数中需要先判断是否为`getBatteryLevel`。 这个平台方法的实现只需调用我们在前一步中编写的iOS代码，并使用result参数返回成功或错误的响应。如果调用了未定义的API，我们也会通知返回：\n\n```objectivec\n[batteryChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {\n  if ([@\"getBatteryLevel\" isEqualToString:call.method]) {\n    int batteryLevel = [self getBatteryLevel];\n\n    if (batteryLevel == -1) {\n      result([FlutterError errorWithCode:@\"UNAVAILABLE\"\n                                 message:@\"电池信息不可用\"\n                                 details:nil]);\n    } else {\n      result(@(batteryLevel));\n    }\n  } else {\n    result(FlutterMethodNotImplemented);\n  }\n}];\n```\n\n现在可以在iOS上运行该应用程序了，如果使用的是iOS模拟器，请注意，它不支持电池API，因此应用程序将显示“电池信息不可用”。\n\n### 使用Swift实现iOS API\n\n以下步骤与上面使用Objective-C相似，首先打开Xcode中Flutter应用程序的iOS部分:\n\n1. 启动 Xcode\n2. 选择 File > Open…\n3. 定位到您 Flutter app目录, 然后选择里面的 `ios`文件夹，点击 OK\n4. 确保Xcode项目的构建没有错误。\n5. 选择 Runner > Runner ，然后打开`AppDelegate.swift`\n\n接下来，覆盖application方法并创建一个`FlutterMethodChannel`绑定通道名称`samples.flutter.io/battery`：\n\n```swift\n@UIApplicationMain\n@objc class AppDelegate: FlutterAppDelegate {\n  override func application(\n    _ application: UIApplication,\n    didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {\n    GeneratedPluginRegistrant.register(with: self);\n\n    let controller : FlutterViewController = window?.rootViewController as! FlutterViewController;\n    let batteryChannel = FlutterMethodChannel.init(name: \"samples.flutter.io/battery\",\n                                                   binaryMessenger: controller);\n    batteryChannel.setMethodCallHandler({\n      (call: FlutterMethodCall, result: FlutterResult) -> Void in\n      // Handle battery messages.\n    });\n\n    return super.application(application, didFinishLaunchingWithOptions: launchOptions);\n  }\n}\n```\n\n接下来，我们添加Swift代码，使用iOS电池API来获取电池电量，这和原生开发是相同的。\n\n将以下新方法添加到`AppDelegate.swift`底部:\n\n```swift\nprivate func receiveBatteryLevel(result: FlutterResult) {\n  let device = UIDevice.current;\n  device.isBatteryMonitoringEnabled = true;\n  if (device.batteryState == UIDeviceBatteryState.unknown) {\n    result(FlutterError.init(code: \"UNAVAILABLE\",\n                             message: \"电池信息不可用\",\n                             details: nil));\n  } else {\n    result(Int(device.batteryLevel * 100));\n  }\n}\n```\n\n最后，我们完成之前添加的`setMethodCallHandler`方法。我们需要处理的平台方法名为`getBatteryLevel`，所以我们在call参数中需要先判断是否为`getBatteryLevel`。 这个平台方法的实现只需调用我们在前一步中编写的iOS代码，并使用result参数返回成功或错误的响应。如果调用了未定义的API，我们也会通知返回：\n\n```swift\nbatteryChannel.setMethodCallHandler({\n  (call: FlutterMethodCall, result: FlutterResult) -> Void in\n  if (\"getBatteryLevel\" == call.method) {\n    receiveBatteryLevel(result: result);\n  } else {\n    result(FlutterMethodNotImplemented);\n  }\n});\n```\n\n现在可以在iOS上运行应用程序，如果使用的是iOS模拟器，请注意，它不支持电池API，因此应用程序将显示“电池信息不可用”。\n"
  },
  {
    "path": "src/chapter12/platform-channel.md",
    "content": "# 12.2 插件开发：平台通道简介\n\n“平台特定”或“特定平台”中的平台指的就是Flutter应用程序运行的平台，如Android或IOS。我们知道一个完整的Flutter应用程序实际上包括原生代码和Flutter代码两部分。由于Flutter本身只是一个UI系统，它本身是无法提供一些系统能力，比如使用蓝牙、相机、GPS等，因此要在Flutter APP中调用这些能力就必须和原生平台进行通信。为此，Flutter中提供了一个平台通道（platform channel），用于Flutter和原生平台的通信。平台通道正是Flutter和原生之间通信的桥梁，它也是Flutter插件的底层基础设施。\n\nFlutter使用了一个灵活的系统，允许您调用特定平台的API，无论在Android上的Java或Kotlin代码中，还是iOS上的ObjectiveC或Swift代码中均可用。\n\nFlutter与原生之间的通信依赖灵活的消息传递方式：\n\n- 应用的Flutter部分通过平台通道（platform channel）将消息发送到其应用程序的所在的宿主（iOS或Android）应用（原生应用）。\n- 宿主监听平台通道，并接收该消息。然后它会调用该平台的API，并将响应发送回客户端，即应用程序的Flutter部分。\n\n### 平台通道\n\n使用平台通道在Flutter(client)和原生(host)之间传递消息，如下图所示：\n\n![平台通道](../imgs/12-3.png)\n\n当在Flutter中调用原生方法时，调用信息通过平台通道传递到原生，原生收到调用信息后方可执行指定的操作，如需返回数据，则原生会将数据再通过平台通道传递给Flutter。值得注意的是消息传递是异步的，这确保了用户界面在消息传递时不会被挂起。\n\n在客户端，[MethodChannel  API](https://docs.flutter.io/flutter/services/MethodChannel-class.html) 可以发送与方法调用相对应的消息。 在宿主平台上，`MethodChannel` 在[Android API](https://docs.flutter.io/javadoc/io/flutter/plugin/common/MethodChannel.html) 和 [FlutterMethodChannel iOS API](https://docs.flutter.io/objcdoc/Classes/FlutterMethodChannel.html)可以接收方法调用并返回结果。这些类可以帮助我们用很少的代码就能开发平台插件。\n\n> **注意**: 如果需要，方法调用(消息传递)可以是反向的，即宿主作为客户端调用Dart中实现的API。 [`quick_actions`](https://pub.dartlang.org/packages/quick_actions)插件就是一个具体的例子。\n\n### 平台通道数据类型支持\n\n平台通道使用标准消息编/解码器对消息进行编解码，它可以高效的对消息进行二进制序列化与反序列化。由于Dart与原生平台之间数据类型有所差异，下面我们列出数据类型之间的映射关系。\n\n| Dart              | Android              | iOS                                            |\n| ----------------- | -------------------- | ---------------------------------------------- |\n| null              | null                 | nil (NSNull when nested)                       |\n| bool              | java.lang.Boolean    | NSNumber numberWithBool:                       |\n| int               | java.lang.Integer    | NSNumber numberWithInt:                        |\n| int, 如果不足32位 | java.lang.Long       | NSNumber numberWithLong:                       |\n| int, 如果不足64位 | java.math.BigInteger | FlutterStandardBigInteger                      |\n| double            | java.lang.Double     | NSNumber numberWithDouble:                     |\n| String            | java.lang.String     | NSString                                       |\n| Uint8List         | byte[]               | FlutterStandardTypedData typedDataWithBytes:   |\n| Int32List         | int[]                | FlutterStandardTypedData typedDataWithInt32:   |\n| Int64List         | long[]               | FlutterStandardTypedData typedDataWithInt64:   |\n| Float64List       | double[]             | FlutterStandardTypedData typedDataWithFloat64: |\n| List              | java.util.ArrayList  | NSArray                                        |\n| Map               | java.util.HashMap    | NSDictionary                                   |\n\n 当在发送和接收值时，这些值在消息中的序列化和反序列化会自动进行。\n\n### 自定义编解码器\n\n除了上面提到的`MethodChannel`，还可以使用[`BasicMessageChannel`](https://docs.flutter.io/flutter/services/BasicMessageChannel-class.html)，它支持使用自定义消息编解码器进行基本的异步消息传递。 此外，可以使用专门的[`BinaryCodec`](https://docs.flutter.io/flutter/services/BinaryCodec-class.html)、[`StringCodec`](https://docs.flutter.io/flutter/services/StringCodec-class.html)和 [`JSONMessageCodec`](https://docs.flutter.io/flutter/services/JSONMessageCodec-class.html)类，或创建自己的编解码器。\n\n### 如何获取平台信息\n\nFlutter 中提供了一个全局变量`defaultTargetPlatform`来获取当前应用的平台信息，`defaultTargetPlatform`定义在\"platform.dart\"中，它的类型是`TargetPlatform`，这是一个枚举类，定义如下：\n\n```dart\nenum TargetPlatform {\n  android,\n  fuchsia,\n  iOS,\n}\n```\n\n可以看到目前Flutter只支持这三个平台。我们可以通过如下代码判断平台：\n\n```dart\nif(defaultTargetPlatform==TargetPlatform.android){\n  // 是安卓系统，do something\n  ...\n}\n...\n```\n\n由于不同平台有它们各自的交互规范，Flutter Material库中的一些组件都针对相应的平台做了一些适配，比如路由组件`MaterialPageRoute`，它在android和ios中会应用各自平台规范的切换动画。那如果我们想让我们的APP在所有平台都表现一致，比如希望在所有平台路由切换动画都按照ios平台一致的左右滑动切换风格该怎么做？Flutter中提供了一种覆盖默认平台的机制，我们可以通过显式指定`debugDefaultTargetPlatformOverride`全局变量的值来指定应用平台。比如：\n\n```dart\ndebugDefaultTargetPlatformOverride=TargetPlatform.iOS;\nprint(defaultTargetPlatform); // 会输出TargetPlatform.iOS\n```\n\n上面代码即在Android中运行后，Flutter APP就会认为是当前系统是iOS，Material组件库中所有组件交互方式都会和iOS平台对齐，`defaultTargetPlatform`的值也会变为`TargetPlatform.iOS`。"
  },
  {
    "path": "src/chapter12/texture_platformview.md",
    "content": "# 12.6 Texture和PlatformView\n\n本节主要介绍原生和Flutter之间如何共享图像，以及如何在Flutter中嵌套原生组件。\n\n## 12.6.1 Texture（示例：使用摄像头）\n\n前面说过Flutter本身只是一个UI系统，对于一些系统能力的调用我们可以通过消息传送机制与原生交互。但是这种消息传送机制并不能覆盖所有的应用场景，比如我们想调用摄像头来拍照或录视频，但在拍照和录视频的过程中我们需要将预览画面显示到我们的Flutter UI中，如果我们要用Flutter定义的消息通道机制来实现这个功能，就需要将摄像头采集的每一帧图片都要从原生传递到Flutter中，这样做代价将会非常大，因为将图像或视频数据通过消息通道实时传输必然会引起内存和CPU的巨大消耗！为此，Flutter提供了一种基于Texture的图片数据共享机制。\n\nTexture可以理解为GPU内保存将要绘制的图像数据的一个对象，Flutter engine会将Texture的数据在内存中直接进行映射（而无需在原生和Flutter之间再进行数据传递），Flutter会给每一个Texture分配一个id，同时Flutter中提供了一个`Texture`组件，`Texture`构造函数定义如下：\n\n```dart\nconst Texture({\n  Key key,\n  @required this.textureId,\n})\n```\n\n`Texture` 组件正是通过`textureId`与Texture数据关联起来；在`Texture`组件绘制时，Flutter会自动从内存中找到相应id的Texture数据，然后进行绘制。可以总结一下整个流程：图像数据先在原生部分缓存，然后在Flutter部分再通过`textureId`和缓存关联起来，最后绘制由Flutter完成。\n\n如果我们作为一个插件开发者，我们在原生代码中分配了`textureId`，那么在Flutter侧使用`Texture`组件时要如何获取`textureId`呢？这又回到了之前的内容了，`textureId`完全可以通过MethodChannel来传递。\n\n另外，值得注意的是，当原生摄像头捕获的图像发生变化时，`Texture` 组件会自动重绘，这不需要我们写任何Dart 代码去控制。\n\n### Texture用法\n\n如果我们要手动实现一个相机插件，和前面几节介绍的“获取剩余电量”插件的步骤一样，需要分别实现原生部分和Flutter部分。考虑到大多数读者可能并非同时既了解Android开发，又了解iOS开发，如果我们再花大量篇幅来介绍不同端的实现可能会没什么意义，另外，由于Flutter官方提供的相机（camera）插件和视频播放（video_player）插件都是使用Texture来实现的，它们本身就是Texture非常好的示例，所以在本书中将不会再介绍使用Texture的具体流程了，读者有兴趣查看camera和video_player的实现代码。下面我们重点介绍一下如何使用camera和video_player。\n\n### 相机示例\n\n下面我们看一下camera包自带的一个示例，它包含如下功能：\n\n1. 可以拍照，也可以拍视频，拍摄完成后可以保存；排号的视频可以播放预览。\n2. 可以切换摄像头（前置摄像头、后置摄像头、其它）\n3. 可以显示已经拍摄内容的预览图。\n\n下面我们看一下具体代码：\n\n1. 首先，依赖camera插件的最新版，并下载依赖。\n\n   ```yaml\n   dependencies:\n     ...  //省略无关代码\n     camera: ^0.5.2+2\n   ```\n\n2. 在`main`方法中获取可用摄像头列表。\n\n   ```dart\n   void main() async {\n     // 获取可用摄像头列表，cameras为全局变量\n     cameras = await availableCameras();\n     runApp(MyApp());\n   }\n   ```\n\n3. 构建UI。现在我们构建如图12-4的测试界面：\n\n   ![12-4](../imgs/12-4.jpg)\n   线面是完整的代码：\n   \n   ```dart\n   import 'package:camera/camera.dart';\n   import 'package:flutter/material.dart';\n   import '../common.dart';\n   import 'dart:async';\n   import 'dart:io';\n   import 'package:path_provider/path_provider.dart';\n   import 'package:video_player/video_player.dart'; //用于播放录制的视频\n   \n   /// 获取不同摄像头的图标（前置、后置、其它）\n   IconData getCameraLensIcon(CameraLensDirection direction) {\n     switch (direction) {\n       case CameraLensDirection.back:\n         return Icons.camera_rear;\n       case CameraLensDirection.front:\n         return Icons.camera_front;\n       case CameraLensDirection.external:\n         return Icons.camera;\n     }\n     throw ArgumentError('Unknown lens direction');\n   }\n   \n   void logError(String code, String message) =>\n       print('Error: $code\\nError Message: $message');\n   \n   // 示例页面路由\n   class CameraExampleHome extends StatefulWidget {\n     @override\n     _CameraExampleHomeState createState() {\n       return _CameraExampleHomeState();\n     }\n   }\n   \n   class _CameraExampleHomeState extends State<CameraExampleHome>\n       with WidgetsBindingObserver {\n     CameraController controller;\n     String imagePath; // 图片保存路径\n     String videoPath; //视频保存路径\n     VideoPlayerController videoController;\n     VoidCallback videoPlayerListener;\n     bool enableAudio = true;\n   \n     @override\n     void initState() {\n       super.initState();\n       // 监听APP状态改变，是否在前台\n       WidgetsBinding.instance.addObserver(this);\n     }\n   \n     @override\n     void dispose() {\n       WidgetsBinding.instance.removeObserver(this);\n       super.dispose();\n     }\n   \n     @override\n     void didChangeAppLifecycleState(AppLifecycleState state) {\n       // 如果APP不在在前台\n       if (state == AppLifecycleState.inactive) {\n         controller?.dispose();\n       } else if (state == AppLifecycleState.resumed) {\n         // 在前台\n         if (controller != null) {\n           onNewCameraSelected(controller.description);\n         }\n       }\n     }\n   \n     final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();\n   \n     @override\n     Widget build(BuildContext context) {\n       return Scaffold(\n         key: _scaffoldKey,\n         appBar: AppBar(\n           title: const Text('相机示例'),\n         ),\n         body: Column(\n           children: <Widget>[\n             Expanded(\n               child: Container(\n                 child: Padding(\n                   padding: const EdgeInsets.all(1.0),\n                   child: Center(\n                     child: _cameraPreviewWidget(),\n                   ),\n                 ),\n                 decoration: BoxDecoration(\n                   color: Colors.black,\n                   border: Border.all(\n                     color: controller != null && controller.value.isRecordingVideo\n                         ? Colors.redAccent\n                         : Colors.grey,\n                     width: 3.0,\n                   ),\n                 ),\n               ),\n             ),\n             _captureControlRowWidget(),\n             _toggleAudioWidget(),\n             Padding(\n               padding: const EdgeInsets.all(5.0),\n               child: Row(\n                 mainAxisAlignment: MainAxisAlignment.start,\n                 children: <Widget>[\n                   _cameraTogglesRowWidget(),\n                   _thumbnailWidget(),\n                 ],\n               ),\n             ),\n           ],\n         ),\n       );\n     }\n   \n     /// 展示预览窗口\n     Widget _cameraPreviewWidget() {\n       if (controller == null || !controller.value.isInitialized) {\n         return const Text(\n           '选择一个摄像头',\n           style: TextStyle(\n             color: Colors.white,\n             fontSize: 24.0,\n             fontWeight: FontWeight.w900,\n           ),\n         );\n       } else {\n         return AspectRatio(\n           aspectRatio: controller.value.aspectRatio,\n           child: CameraPreview(controller),\n         );\n       }\n     }\n   \n     /// 开启或关闭录音\n     Widget _toggleAudioWidget() {\n       return Padding(\n         padding: const EdgeInsets.only(left: 25),\n         child: Row(\n           children: <Widget>[\n             const Text('开启录音:'),\n             Switch(\n               value: enableAudio,\n               onChanged: (bool value) {\n                 enableAudio = value;\n                 if (controller != null) {\n                   onNewCameraSelected(controller.description);\n                 }\n               },\n             ),\n           ],\n         ),\n       );\n     }\n   \n     /// 显示已拍摄的图片/视频缩略图。\n     Widget _thumbnailWidget() {\n       return Expanded(\n         child: Align(\n           alignment: Alignment.centerRight,\n           child: Row(\n             mainAxisSize: MainAxisSize.min,\n             children: <Widget>[\n               videoController == null && imagePath == null\n                   ? Container()\n                   : SizedBox(\n                 child: (videoController == null)\n                     ? Image.file(File(imagePath))\n                     : Container(\n                   child: Center(\n                     child: AspectRatio(\n                         aspectRatio:\n                         videoController.value.size != null\n                             ? videoController.value.aspectRatio\n                             : 1.0,\n                         child: VideoPlayer(videoController)),\n                   ),\n                   decoration: BoxDecoration(\n                       border: Border.all(color: Colors.pink)),\n                 ),\n                 width: 64.0,\n                 height: 64.0,\n               ),\n             ],\n           ),\n         ),\n       );\n     }\n   \n     /// 相机工具栏\n     Widget _captureControlRowWidget() {\n       return Row(\n         mainAxisAlignment: MainAxisAlignment.spaceEvenly,\n         mainAxisSize: MainAxisSize.max,\n         children: <Widget>[\n           IconButton(\n             icon: const Icon(Icons.camera_alt),\n             color: Colors.blue,\n             onPressed: controller != null &&\n                 controller.value.isInitialized &&\n                 !controller.value.isRecordingVideo\n                 ? onTakePictureButtonPressed\n                 : null,\n           ),\n           IconButton(\n             icon: const Icon(Icons.videocam),\n             color: Colors.blue,\n             onPressed: controller != null &&\n                 controller.value.isInitialized &&\n                 !controller.value.isRecordingVideo\n                 ? onVideoRecordButtonPressed\n                 : null,\n           ),\n           IconButton(\n             icon: const Icon(Icons.stop),\n             color: Colors.red,\n             onPressed: controller != null &&\n                 controller.value.isInitialized &&\n                 controller.value.isRecordingVideo\n                 ? onStopButtonPressed\n                 : null,\n           )\n         ],\n       );\n     }\n   \n     /// 展示所有摄像头\n     Widget _cameraTogglesRowWidget() {\n       final List<Widget> toggles = <Widget>[];\n   \n       if (cameras.isEmpty) {\n         return const Text('没有检测到摄像头');\n       } else {\n         for (CameraDescription cameraDescription in cameras) {\n           toggles.add(\n             SizedBox(\n               width: 90.0,\n               child: RadioListTile<CameraDescription>(\n                 title: Icon(getCameraLensIcon(cameraDescription.lensDirection)),\n                 groupValue: controller?.description,\n                 value: cameraDescription,\n                 onChanged: controller != null && controller.value.isRecordingVideo\n                     ? null\n                     : onNewCameraSelected,\n               ),\n             ),\n           );\n         }\n       }\n   \n       return Row(children: toggles);\n     }\n   \n     String timestamp() => DateTime.now().millisecondsSinceEpoch.toString();\n   \n     void showInSnackBar(String message) {\n       _scaffoldKey.currentState.showSnackBar(SnackBar(content: Text(message)));\n     }\n   \n     // 摄像头选中回调\n     void onNewCameraSelected(CameraDescription cameraDescription) async {\n       if (controller != null) {\n         await controller.dispose();\n       }\n       controller = CameraController(\n         cameraDescription,\n         ResolutionPreset.high,\n         enableAudio: enableAudio,\n       );\n   \n       controller.addListener(() {\n         if (mounted) setState(() {});\n         if (controller.value.hasError) {\n           showInSnackBar('Camera error ${controller.value.errorDescription}');\n         }\n       });\n   \n       try {\n         await controller.initialize();\n       } on CameraException catch (e) {\n         _showCameraException(e);\n       }\n   \n       if (mounted) {\n         setState(() {});\n       }\n     }\n   \n     // 拍照按钮点击回调\n     void onTakePictureButtonPressed() {\n       takePicture().then((String filePath) {\n         if (mounted) {\n           setState(() {\n             imagePath = filePath;\n             videoController?.dispose();\n             videoController = null;\n           });\n           if (filePath != null) showInSnackBar('图片保存在 $filePath');\n         }\n       });\n     }\n   \n     // 开始录制视频\n     void onVideoRecordButtonPressed() {\n       startVideoRecording().then((String filePath) {\n         if (mounted) setState(() {});\n         if (filePath != null) showInSnackBar('正在保存视频于 $filePath');\n       });\n     }\n   \n     // 终止视频录制\n     void onStopButtonPressed() {\n       stopVideoRecording().then((_) {\n         if (mounted) setState(() {});\n         showInSnackBar('视频保存在: $videoPath');\n       });\n     }\n   \n     Future<String> startVideoRecording() async {\n       if (!controller.value.isInitialized) {\n         showInSnackBar('请先选择一个摄像头');\n         return null;\n       }\n   \n       // 确定视频保存的路径\n       final Directory extDir = await getApplicationDocumentsDirectory();\n       final String dirPath = '${extDir.path}/Movies/flutter_test';\n       await Directory(dirPath).create(recursive: true);\n       final String filePath = '$dirPath/${timestamp()}.mp4';\n   \n       if (controller.value.isRecordingVideo) {\n         // 如果正在录制，则直接返回\n         return null;\n       }\n   \n       try {\n         videoPath = filePath;\n         await controller.startVideoRecording(filePath);\n       } on CameraException catch (e) {\n         _showCameraException(e);\n         return null;\n       }\n       return filePath;\n     }\n   \n     Future<void> stopVideoRecording() async {\n       if (!controller.value.isRecordingVideo) {\n         return null;\n       }\n   \n       try {\n         await controller.stopVideoRecording();\n       } on CameraException catch (e) {\n         _showCameraException(e);\n         return null;\n       }\n   \n       await _startVideoPlayer();\n     }\n   \n     Future<void> _startVideoPlayer() async {\n       final VideoPlayerController vcontroller =\n       VideoPlayerController.file(File(videoPath));\n       videoPlayerListener = () {\n         if (videoController != null && videoController.value.size != null) {\n           // Refreshing the state to update video player with the correct ratio.\n           if (mounted) setState(() {});\n           videoController.removeListener(videoPlayerListener);\n         }\n       };\n       vcontroller.addListener(videoPlayerListener);\n       await vcontroller.setLooping(true);\n       await vcontroller.initialize();\n       await videoController?.dispose();\n       if (mounted) {\n         setState(() {\n           imagePath = null;\n           videoController = vcontroller;\n         });\n       }\n       await vcontroller.play();\n     }\n   \n     Future<String> takePicture() async {\n       if (!controller.value.isInitialized) {\n         showInSnackBar('错误: 请先选择一个相机');\n         return null;\n       }\n       final Directory extDir = await getApplicationDocumentsDirectory();\n       final String dirPath = '${extDir.path}/Pictures/flutter_test';\n       await Directory(dirPath).create(recursive: true);\n       final String filePath = '$dirPath/${timestamp()}.jpg';\n   \n       if (controller.value.isTakingPicture) {\n         // A capture is already pending, do nothing.\n         return null;\n       }\n   \n       try {\n         await controller.takePicture(filePath);\n       } on CameraException catch (e) {\n         _showCameraException(e);\n         return null;\n       }\n       return filePath;\n     }\n   \n     void _showCameraException(CameraException e) {\n       logError(e.code, e.description);\n       showInSnackBar('Error: ${e.code}\\n${e.description}');\n     }\n   }\n   ```\n\n> 如果代码运行遇到困难，请直接查看[camera官方文档](https://pub.dev/packages/camera)。\n\n## 12.6.2 PlatformView （示例：WebView）\n\n如果我们在开发过程中需要使用一个原生组件，但这个原生组件在Flutter中很难实现时怎么办（如webview）？这时一个简单的方法就是将需要使用原生组件的页面全部用原生实现，在flutter中需要打开该页面时通过消息通道打开这个原生的页面。但是这种方法有一个最大的缺点，就是原生组件很难和Flutter组件进行组合。\n\n在 Flutter 1.0版本中，Flutter SDK中新增了`AndroidView`和`UIKitView` 两个组件，这两个组件的主要功能就是将原生的Android组件和iOS组件嵌入到Flutter的组件树中，这个功能是非常重要的，尤其是对一些实现非常复杂的组件，比如webview，这些组件原生已经有了，如果Flutter中要用，重新实现的话成本将非常高，所以如果有一种机制能让Flutter共享原生组件，这将会非常有用，也正因如此，Flutter才提供了这两个组件。\n\n由于`AndroidView`和`UIKitView` 是和具体平台相关的，所以称它们为PlatformView。需要说明的是将来Flutter支持的平台可能会增多，则相应的PlatformView也将会变多。那么如何使用Platform View呢？我们以Flutter官方提供的[webview_flutter插件](https://github.com/flutter/plugins/tree/master/packages/webview_flutter)为例：\n\n> 注意，在本书写作之时，webview_flutter仍处于预览阶段，如您想在项目中使用它，请查看一下webview_flutter插件最新版本及动态。\n\n1. 原生代码中注册要被Flutter嵌入的组件工厂，如webview_flutter插件中Android端注册webview插件代码：\n\n   ```java\n   public static void registerWith(Registrar registrar) {\n      registrar.platformViewRegistry().registerViewFactory(\"webview\", \n      WebViewFactory(registrar.messenger()));\n   }\n   ```\n\n   `WebViewFactory`的具体实现请参考webview_flutter插件的实现源码，在此不再赘述。\n\n2. 在Flutter中使用；打开Flutter中文社区首页。\n\n   ```dart\n   class PlatformViewRoute extends StatelessWidget {\n     @override\n     Widget build(BuildContext context) {\n       return WebView(\n         initialUrl: \"https://flutterchina.club\",\n         javascriptMode: JavascriptMode.unrestricted,\n       );\n     }\n   }\n   ```\n\n   运行效果如图12-5所示：\n\n   ![12-5](../imgs/12-5.jpg)\n\n注意，使用PlatformView的开销是非常大的，因此，如果一个原生组件用Flutter实现的难度不大时，我们应该首选Flutter实现。\n\n另外，PlatformView的相关功能在作者写作时还处于预览阶段，可能还会发生变化，因此，读者如果需要在项目中使用的话，应查看一下最新的文档。"
  },
  {
    "path": "src/chapter13/faq.md",
    "content": "# 13.4 国际化常见问题\n\n本节主要解答一下在国际化中常见的问题。\n\n### 默认语言区域不对\n\n在一些非大陆行货渠道买的一些Android和iOS设备，会出现默认的Locale不是中文简体的情况。这属于正常现象，但是为了防止设备获取的Locale与实际的地区不一致，所有的支持多语言的APP都必须提供一个手动选择语言的入口。\n\n### 如何对应用标题进行国际化\n\n`MaterialApp`有一个`title`属性，用于指定APP的标题。在Android系统中，APP的标题会出现在任务管理器中。所以也需要对`title`进行国际化。但是问题是很多国际化的配置都是在`MaterialApp`上设置的，我们无法在构建`MaterialApp`时通过`Localizations.of`来获取本地化资源，如：\n\n```dart\nMaterialApp(\n  title: DemoLocalizations.of(context).title, //不能正常工作！\n  localizationsDelegates: [\n    // 本地化的代理类\n    GlobalMaterialLocalizations.delegate,\n    GlobalWidgetsLocalizations.delegate,\n    DemoLocalizationsDelegate() // 设置Delegate\n  ],\n);\n```\n\n上面代码运行后，`DemoLocalizations.of(context).title` 是会报错的，原因是`Localizations.of`会从当前的context沿着widget树向顶部查找`DemoLocalizations`，但是我们在`MaterialApp`中设置完`DemoLocalizationsDelegate`后，实际上`DemoLocalizations`是在当前context的子树中的，所以`DemoLocalizations.of(context)`会返回null，报错。那么我们该如何处理这种情况呢？其实很简单，我们只需要设置一个`onGenerateTitle`回调即可：\n\n```dart\nMaterialApp(\n  onGenerateTitle: (context){\n    // 此时context在Localizations的子树中\n    return DemoLocalizations.of(context).title;\n  },\n  localizationsDelegates: [\n    DemoLocalizationsDelegate(),\n    ...\n  ],\n);\n```\n\n### 如何为英语系的国家指定同一个locale\n\n英语系的国家非常多，如美国、英国、澳大利亚等，这些英语系国家虽然说的都是英语，但也会有一些区别。如果我们的APP只想提供一种英语（如美国英语）供所有英语系国家使用，我们可以在前面介绍的`localeListResolutionCallback`中来做兼容：\n\n```dart\nlocaleListResolutionCallback:\n    (List<Locale> locales, Iterable<Locale> supportedLocales) {\n  // 判断当前locale是否为英语系国家，如果是直接返回Locale('en', 'US')     \n}\n```\n\n"
  },
  {
    "path": "src/chapter13/index.md",
    "content": "\n## 本章目录\n\n* [13.1：让App支持多语言](multi_languages_support.md)\n* [13.2：实现Localizations](locallization_implement.md) \n* [13.3：使用Intl包](intl.md) \n* [13.4：国际化常见问题](faq.md) \n"
  },
  {
    "path": "src/chapter13/intl.md",
    "content": "# 使用Intl包\n\n使用[Intl](https://pub.dartlang.org/packages/intl)包我们不仅可以非常轻松的实现国际化，而且也可以将字符串文本分离成单独的文件，方便开发人员和翻译人员分工协作。为了使用[Intl](https://pub.dartlang.org/packages/intl)包我们需要添加两个依赖：\n\n```yaml\ndependencies:\n  #...省略无关项\n  intl: ^0.15.7 \ndev_dependencies:\n   #...省略无关项\n  intl_translation: ^0.17.2  \n```\n\n[intl_translation](https://pub.dartlang.org/packages/intl_translation) 包主要包含了一些工具，它在开发阶段主要主要的作用是从代码中提取要国际化的字符串到单独的arb文件和根据arb文件生成对应语言的dart代码，而intl包主要是引用和加载intl_translation生成后的dart代码。下面我们将一步步来说明如何使用：\n\n### 第一步：创建必要目录\n\n首先，在项目根目录下创建一个l10n-arb目录，该目录保存我们接下来通过intl_translation命令生成的arb文件。一个简单的arb文件内容如下：\n\n```json\n{\n  \"@@last_modified\": \"2018-12-10T15:46:20.897228\",\n  \"@@locale\":\"zh_CH\",\n  \"title\": \"Flutter应用\",\n  \"@title\": {\n    \"description\": \"Title for the Demo application\",\n    \"type\": \"text\",\n    \"placeholders\": {}\n  }\n}\n```\n\n我们根据\"@@locale\"字段可以看出这个arb对应的是中文简体的翻译，里面的`title`字段对应的正是我们应用标题的中文简体翻译。`@title`字段是对`title`的一些描述信息。\n\n接下来，我们在lib目录下创建一个l10n的目录，该目录用于保存从arb文件生成的dart代码文件。\n\n### 第二步：实现Localizations和Delegate类\n\n和上一节中的步骤类似，我们仍然要实现`Localizations`和Delegate类，不同的是，现在我们在实现时要使用intl包的一些方法（有些是动态生成的）。\n\n下面我们在`lib/l10n`目录下新建一个“localization_intl.dart”的文件，文件内容如下：\n\n```dart\nimport 'package:flutter/material.dart';\nimport 'package:intl/intl.dart';\nimport 'messages_all.dart'; //1\n\nclass DemoLocalizations {\n  static Future<DemoLocalizations> load(Locale locale) {\n    final String name = locale.countryCode.isEmpty ? locale.languageCode : locale.toString();\n    final String localeName = Intl.canonicalizedLocale(name);\n    //2\n    return initializeMessages(localeName).then((b) {\n      Intl.defaultLocale = localeName;\n      return new DemoLocalizations();\n    });\n  }\n\n  static DemoLocalizations of(BuildContext context) {\n    return Localizations.of<DemoLocalizations>(context, DemoLocalizations);\n  }\n\n  String get title {\n    return Intl.message(\n      'Flutter APP',\n      name: 'title',\n      desc: 'Title for the Demo application',\n    );\n  }\n}\n\n//Locale代理类\nclass DemoLocalizationsDelegate extends LocalizationsDelegate<DemoLocalizations> {\n  const DemoLocalizationsDelegate();\n\n  //是否支持某个Local\n  @override\n  bool isSupported(Locale locale) => ['en', 'zh'].contains(locale.languageCode);\n\n  // Flutter会调用此类加载相应的Locale资源类\n  @override\n  Future<DemoLocalizations> load(Locale locale) {\n    //3\n    return  DemoLocalizations.load(locale);\n  }\n\n  // 当Localizations Widget重新build时，是否调用load重新加载Locale资源.\n  @override\n  bool shouldReload(DemoLocalizationsDelegate old) => false;\n}\n```\n\n注意：\n\n- 注释1的\"messages_all.dart\"文件是通过[intl_translation](https://pub.dartlang.org/packages/intl_translation)工具从arb文件生成的代码，所以在第一次运行生成命令之前，此文件不存在。注释2处的`initializeMessages()`方法和\"messages_all.dart\"文件一样，是同时生成的。\n- 注释3处和上一节示例代码不同，这里我们直接调用`DemoLocalizations.load()`即可。\n\n### 第三步：添加需要国际化的属性\n\n现在我们可以在DemoLocalizations类中添加需要国际化的属性或方法，如上面示例代码中的`title`属性，这时我们就要用到Intl库提供的一些方法，这些方法可以帮我们轻松实现不同语言的一些语法特性，如复数语境，举个例子，比如我们有一个电子邮件列表页，我们需要在顶部显示未读邮件的数量，在未读数量不同事，我们展示的文本可能会不同：\n\n| 未读邮件数 | 提示语                   |\n| ---------- | ------------------------ |\n| 0          | There are no emails left |\n| 1          | There is 1 email left    |\n| n(n>1)     | There are n emails left  |\n\n我们可以通过`Intl.plural(...)`来实现：\n\n```dart\nremainingEmailsMessage(int howMany) => Intl.plural(howMany,\n    zero: 'There are no emails left',\n    one: 'There is $howMany email left',\n    other: 'There are $howMany emails left',\n    name: \"remainingEmailsMessage\",\n    args: [howMany],\n    desc: \"How many emails remain after archiving.\",\n    examples: const {'howMany': 42, 'userName': 'Fred'});\n```\n\n可以看到通过`Intl.plural`方法可以在`howMany`值不同时输出不同的提示信息。\n\n[Intl](https://pub.dartlang.org/packages/intl)包还有一些其他的方法，读者可以自行查看其文档，本书不在赘述。\n\n### 第四步：生成arb文件\n\n现在我们可以通[intl_translation](https://pub.dartlang.org/packages/intl_translation)包的工具来提取代码中的字符串到一个arb文件，运行如下命名：\n\n```shell\nflutter pub pub run intl_translation:extract_to_arb --output-dir=l10n-arb \\ lib/l10n/localization_intl.dart\n```\n\n运行此命令后，会将我们之前通过Intl API标识的属性和字符串提取到“l10n-arb/intl_messages.arb”文件中，我们看看其内容：\n\n```json\n{\n  \"@@last_modified\": \"2018-12-10T17:37:28.505088\",\n  \"title\": \"Flutter APP\",\n  \"@title\": {\n    \"description\": \"Title for the Demo application\",\n    \"type\": \"text\",\n    \"placeholders\": {}\n  },\n  \"remainingEmailsMessage\": \"{howMany,plural, =0{There are no emails left}=1{There is {howMany} email left}other{There are {howMany} emails left}}\",\n  \"@remainingEmailsMessage\": {\n    \"description\": \"How many emails remain after archiving.\",\n    \"type\": \"text\",\n    \"placeholders\": {\n      \"howMany\": {\n        \"example\": 42\n      }\n    }\n  }\n}\n```\n\n这个是默认的Locale资源文件，如果我们现在要支持中文简体，只需要在该文件同级目录创建一个\"intl_zh_CN.arb\"文件，然后将\"intl_messages.arb\"的内容拷贝到\"intl_zh_CN.arb\"文件，接下来将英文翻译为中文即可，翻译后的\"intl_zh_CN.arb\"文件内容如下：\n\n```json\n{\n  \"@@last_modified\": \"2018-12-10T15:46:20.897228\",\n  \"@@locale\":\"zh_CN\",\n  \"title\": \"Flutter应用\",\n  \"@title\": {\n    \"description\": \"Title for the Demo application\",\n    \"type\": \"text\",\n    \"placeholders\": {}\n  },\n  \"remainingEmailsMessage\": \"{howMany,plural, =0{没有未读邮件}=1{有{howMany}封未读邮件}other{有{howMany}封未读邮件}}\",\n  \"@remainingEmailsMessage\": {\n    \"description\": \"How many emails remain after archiving.\",\n    \"type\": \"text\",\n    \"placeholders\": {\n      \"howMany\": {\n        \"example\": 42\n      }\n    }\n  }\n}\n```\n\n我们必须要翻译`title`和`remainingEmailsMessage`字段，`description`是该字段的说明，通常给翻译人员看，代码中不会用到。\n\n有两点需要说明：\n\n1. 如果某个特定的arb中缺失某个属性，那么应用将会加载默认的arb文件(intl_messages.arb)中的相应属性，这是Intl的托底策略。\n2. 每次运行提取命令时，intl_messages.arb都会根据代码重新生成，但其他arb文件不会，所以当要添加新的字段或方法时，其他arb文件是增量的，不用担心会覆盖。\n3. arb文件是标准的，其格式规范可以自行了解。通常会将arb文件交给翻译人员，当他们完成翻译后，我们再通过下面的步骤根据arb文件生成最终的dart代码。\n\n### 第五步：生成dart代码\n\n最后一步就是根据arb生成dart文件：\n\n```shell\nflutter pub pub run intl_translation:generate_from_arb --output-dir=lib/l10n --no-use-deferred-loading lib/l10n/localization_intl.dart l10n-arb/intl_*.arb\n```\n\n这句命令在首次运行时会在\"lib/l10n\"目录下生成多个文件，对应多种Locale，这些代码便是最终要使用的dart代码。\n\n### 总结\n\n至此，我们将使用[Intl](https://pub.dartlang.org/packages/intl)包对APP进行国际化的流程介绍完了，我们可以发现，其中第一步和第二步只在第一次需要，而我们开发时的主要的工作都是在第三步。由于最后两步在第三步完成后每次也都需要，所以我们可以将最后两步放在一个shell脚本里，当我们完成第三步或完成arb文件翻译后只需要分别执行该脚本即可。我们在根目录下创建一个intl.sh的脚本，内容为：\n\n```shell\nflutter pub pub run intl_translation:extract_to_arb --output-dir=l10n-arb lib/l10n/localization_intl.dart\nflutter pub pub run intl_translation:generate_from_arb --output-dir=lib/l10n --no-use-deferred-loading lib/l10n/localization_intl.dart l10n-arb/intl_*.arb\n```\n\n然后授予执行权限：\n\n```shell\nchmod +x intl.sh\n```\n\n执行intl.sh\n\n```shell\n./intl.sh\n```\n\n"
  },
  {
    "path": "src/chapter13/locallization_implement.md",
    "content": "# 13.2 实现Localizations\n\n前面讲了Material组件库如何支持国际化，本节我们将介绍一下我们自己的UI中如何支持多语言。根据上节所述，我们需要实现两个类：一个`Delegate`类一个`Localizations`类，下面我们通过一个实例说明。\n\n### 实现Localizations类\n\n我们已经知道`Localizations`类中主要实现提供了本地化值，如文本：\n\n```dart\n//Locale资源类\nclass DemoLocalizations {\n  DemoLocalizations(this.isZh);\n  //是否为中文\n  bool isZh = false;\n  //为了使用方便，我们定义一个静态方法\n  static DemoLocalizations of(BuildContext context) {\n    return Localizations.of<DemoLocalizations>(context, DemoLocalizations);\n  }\n  //Locale相关值，title为应用标题\n  String get title {\n    return isZh ? \"Flutter应用\" : \"Flutter APP\";\n  }\n  //... 其它的值  \n}\n```\n\n`DemoLocalizations`中会根据当前的语言来返回不同的文本，如`title`，我们可以将所有需要支持多语言的文本都在此类中定义。`DemoLocalizations`的实例将会在Delegate类的`load`方法中创建。\n\n### 实现Delegate类\n\nDelegate类的职责是在Locale改变时加载新的Locale资源，所以它有一个`load`方法，Delegate类需要继承自`LocalizationsDelegate`类，实现相应的接口，示例如下：\n\n```dart\n//Locale代理类\nclass DemoLocalizationsDelegate extends LocalizationsDelegate<DemoLocalizations> {\n  const DemoLocalizationsDelegate();\n\n  //是否支持某个Local\n  @override\n  bool isSupported(Locale locale) => ['en', 'zh'].contains(locale.languageCode);\n\n  // Flutter会调用此类加载相应的Locale资源类\n  @override\n  Future<DemoLocalizations> load(Locale locale) {\n    print(\"$locale\");\n    return SynchronousFuture<DemoLocalizations>(\n        DemoLocalizations(locale.languageCode == \"zh\")\n    );\n  }\n\n  @override\n  bool shouldReload(DemoLocalizationsDelegate old) => false;\n}\n```\n\n`shouldReload`的返回值决定当Localizations组件重新build时，是否调用`load`方法重新加载Locale资源。一般情况下，Locale资源只应该在Locale切换时加载一次，不需要每次在`Localizations`重新build时都加载，所以返回`false`即可。可能有些人会担心返回`false`的话在APP启动后用户再改变系统语言时`load`方法将不会被调用，所以Locale资源将不会被加载。事实上，每当Locale改变时Flutter都会再调用`load`方法加载新的Locale，无论`shouldReload`返回`true`还是`false`。\n\n### 最后一步：添加多语言支持\n\n和上一节中介绍的相同，我们现在需要先注册`DemoLocalizationsDelegate`类，然后再通过`DemoLocalizations.of(context)`来动态获取当前Locale文本。\n\n只需要在MaterialApp或WidgetsApp的`localizationsDelegates`列表中添加我们的Delegate实例即可完成注册：\n\n```dart\nlocalizationsDelegates: [\n // 本地化的代理类\n GlobalMaterialLocalizations.delegate,\n GlobalWidgetsLocalizations.delegate,\n // 注册我们的Delegate\n DemoLocalizationsDelegate()\n],\n```\n\n接下来我们可以在Widget中使用Locale值：\n\n```dart\nreturn Scaffold(\n  appBar: AppBar(\n    //使用Locale title  \n    title: Text(DemoLocalizations.of(context).title),\n  ),\n  ... //省略无关代码\n ） \n```\n\n这样，当在美国英语和中文简体之间切换系统语言时，APP的标题将会分别为“Flutter APP”和“Flutter应用”。\n\n### 总结\n\n本节我们通过一个简单的示例说明了Flutter应用国际化的基本过程及原理。但是上面的实例还有一个严重的不足就是我们需要在DemoLocalizations类中获取`title`时手动的判断当前语言Locale，然后返回合适的文本。试想一下，当我们要支持的语言不是两种而是8种甚至20几种时，如果为每个文本属性都要分别去判断到底是哪种Locale从而获取相应语言的文本将会是一件非常复杂的事。还有，通常情况下翻译人员并不是开发人员，能不能像i18n或l10n标准那样可以将翻译单独保存为一个arb文件交由翻译人员去翻译，翻译好之后开发人员再通过工具将arb文件转为代码。答案是肯定的！我们将在下一节介绍如何通过Dart intl包来实现这些。\n"
  },
  {
    "path": "src/chapter13/multi_languages_support.md",
    "content": "# 13.1 让App支持多语言\n\n如果我们的应用要支持多种语言，那么我们需要“国际化”它。这意味着我们在开发时需要为应用程序支持的每种语言环境设置“本地化”的一些值，如文本和布局。Flutter SDK已经提供了一些组件和类来帮助我们实现国际化，下面我们来介绍一下Flutter中实现国际化的步骤。\n\n接下来我们以`MaterialApp`类为入口的应用来说明如何支持国际化。\n\n> 大多数应用程序都是通过`MaterialApp`为入口，但根据低级别的`WidgetsApp`类为入口编写的应用程序也可以使用相同的类和逻辑进行国际化。`MaterialApp`实际上也是`WidgetsApp`的一个包装。\n\n注意，”本地化的值和资源“是指我们针对不同语言准备的不同资源，这些资源一般是指文案（字符串），当然也会有一些其他的资源会根据不同语言地区而不同，比如我们需要显示一个APP上架地的国旗图片，那么不同Locale区域我们就需要提供不同的的国旗图片。\n\n### 支持国际化\n\n默认情况下，Flutter SDK中的组件仅提供美国英语本地化资源（主要是文本）。要添加对其他语言的支持，应用程序须添加一个名为“flutter_localizations”的包依赖，然后还需要在`MaterialApp`中进行一些配置。 要使用`flutter_localizations`包，首先需要添加依赖到`pubspec.yaml`文件中：\n\n```yaml\ndependencies:\n  flutter:\n    sdk: flutter\n  flutter_localizations:\n    sdk: flutter\n```\n\n接下来，下载`flutter_localizations`库，然后指定`MaterialApp`的`localizationsDelegates`和`supportedLocales`：\n\n```dart\nimport 'package:flutter_localizations/flutter_localizations.dart';\n\nnew MaterialApp(\n localizationsDelegates: [\n   // 本地化的代理类\n   GlobalMaterialLocalizations.delegate,\n   GlobalWidgetsLocalizations.delegate,\n ],\n supportedLocales: [\n    const Locale('en', 'US'), // 美国英语\n    const Locale('zh', 'CN'), // 中文简体\n    //其它Locales\n  ],\n  // ...\n)\n```\n\n> 与`MaterialApp`类为入口的应用不同, 对基于`WidgetsApp`类为入口的应用程序进行国际化时,不需要`GlobalMaterialLocalizations.delegate`。\n\n`localizationsDelegates`列表中的元素是生成本地化值集合的工厂。`GlobalMaterialLocalizations.delegate` 为Material 组件库提供的本地化的字符串和其他值，它可以使Material 组件支持多语言。 `GlobalWidgetsLocalizations.delegate`定义组件默认的文本方向，从左到右或从右到左，这是因为有些语言的阅读习惯并不是从左到右，比如如阿拉伯语就是从右向左的。\n\n`supportedLocales`也接收一个Locale数组，表示我们的应用支持的语言列表，在本例中我们的应用只支持美国英语和中文简体两种语言。\n\n### 获取当前区域Locale\n\n[`Locale`](https://docs.flutter.io/flutter/dart-ui/Locale-class.html)类是用来标识用户的语言环境的，它包括语言和国家两个标志如：\n\n```dart\nconst Locale('zh', 'CN') // 中文简体\n```\n\n我们始终可以通过以下方式来获取应用的当前区域Locale：\n\n```dart\nLocale myLocale = Localizations.localeOf(context);\n```\n\n[`Localizations`](https://docs.flutter.io/flutter/widgets/Localizations-class.html) 组件一般位于widget树中其它业务组件的顶部，它的作用是定义区域Locale以及设置子树依赖的本地化资源。 如果系统的语言环境发生变化，[WidgetsApp](https://docs.flutter.io/flutter/widgets/WidgetsApp-class.html)将创建一个新的Localizations 组件并重建它，这样子树中通过`Localizations.localeOf(context)` 获取的Locale就会更新。\n\n### 监听系统语言切换\n\n当我们更改系统语言设置时，APP中的Localizations组件会重新构建，`Localizations.localeOf(context)` 获取的Locale就会更新，最终界面会重新build达到切换语言的效果。但是这个过程是隐式完成的，我们并没有主动去监听系统语言切换，但是有时我们需要在系统语言发生改变时做一些事，比如系统语言切换为一种我们APP不支持的语言时，我们需要设置一个默认的语言，这时我们就需要监听locale改变事件。\n\n我们可以通过`localeResolutionCallback`或`localeListResolutionCallback`回调来监听locale改变的事件，我们先看看`localeResolutionCallback`的回调函数签名：\n\n```dart\nLocale Function(Locale locale, Iterable<Locale> supportedLocales)\n```\n\n- 参数`locale`的值为当前的当前的系统语言设置，当应用启动时或用户动态改变系统语言设置时此locale即为系统的当前locale。当开发者手动指定APP的locale时，那么此locale参数代表开发者指定的locale，此时将忽略系统locale如：\n\n  ```dart\n  MaterialApp(\n   ...\n   locale: const Locale('en', 'US'), //手动指定locale\n   ...\n  )\n  ```\n\n  上面的例子中手动指定了应用locale为美国英语，指定后即使设备当前语言是中文简体，应用中的locale也依然是美国英语。如果`locale`为`null`，则表示Flutter未能获取到设备的Locale信息，所以我们在使用`locale`之前一定要先判空。\n\n- `supportedLocales` 为当前应用支持的locale列表，是开发者在MaterialApp中通过`supportedLocales`属性注册的。\n\n- 返回值是一个`Locale`，此`Locale`为Flutter APP最终使用的`Locale`。通常在不支持的语言区域时返回一个默认的`Locale`。\n\n`localeListResolutionCallback`和`localeResolutionCallback`唯一的不同就在第一个参数类型，前者接收的是一个`Locale`列表，而后者接收的是单个`Locale`。\n\n```dart\nLocale Function(List<Locale> locales, Iterable<Locale> supportedLocales)\n```\n\n在较新的Android系统中，用户可以设置一个语言列表，这样一来，支持多语言的应用就会得到这个列表，应用通常的处理方式就是按照列表的顺序依次尝试加载相应的Locale，如果某一种语言加载成功则会停止。图13-1是Android系统中设置语言列表的截图：\n\n![设置语言列表](../imgs/13-1.jpeg)\n\n在Flutter中，应该优先使用`localeListResolutionCallback`，当然你不必担心Android系统的差异性，如果在低版本的Android系统中，Flutter会自动处理这种情况，这时Locale列表只会包含一项。\n\n### Localization 组件\n\nLocalizations组件用于加载和查找应用当前语言下的本地化值或资源。应用程序通过[`Localizations.of(context,type)`](https://docs.flutter.io/flutter/widgets/Localizations/of.html)来引用这些对象。 如果设备的Locale区域设置发生更改，则Localizations 组件会自动加载新区域的Locale值，然后重新build使用（依赖）了它们的组件，之所以会这样，是因为`Localizations`内部使用了[InheritedWidget](https://book.flutterchina.club/chapter7/inherited_widget.html) ，我们在介绍该组件时讲过：当子组件的`build`函数引用了`InheritedWidget`时，会创建对`InheritedWidget`的隐式依赖关系。因此，当`InheritedWidget`发生更改时，即`Localizations`的Locale设置发生更改时，将重建所有依赖它的子组件。\n\n本地化值由`Localizations`的 [LocalizationsDelegates](https://docs.flutter.io/flutter/widgets/LocalizationsDelegate-class.html) 列表加载 。 **每个委托必须定义一个异步load() 方法**，以生成封装了一系列本地化值的对象。通常这些对象为每个本地化值定义一个方法。\n\n在大型应用程序中，不同模块或Package可能会与自己的本地化值捆绑在一起。 这就是为什么要用`Localizations` 管理对象表的原因。 要使用由`LocalizationsDelegate `的`load`方法之一产生的对象，可以指定一个`BuildContext`和对象的类型来找到它。例如，Material 组件库的本地化字符串由[MaterialLocalizations](https://docs.flutter.io/flutter/material/MaterialLocalizations-class.html)类定义，此类的实例由[MaterialApp](https://docs.flutter.io/flutter/material/MaterialApp-class.html)类提供的`LocalizationDelegate`创建， 它们可以如下方式获取到：\n\n```dart\nLocalizations.of<MaterialLocalizations>(context, MaterialLocalizations);\n```\n\n这个特殊的`Localizations.of()`表达式会经常使用，所以MaterialLocalizations类提供了一个便捷方法：\n\n```dart\nstatic MaterialLocalizations of(BuildContext context) {\n  return Localizations.of<MaterialLocalizations>(context, MaterialLocalizations);\n}\n\n// 可以直接调用便捷方法\ntooltip: MaterialLocalizations.of(context).backButtonTooltip,\n```\n\n### 使用打包好的LocalizationsDelegates\n\n为了尽可能小而且简单，flutter软件包中仅提供美国英语值的`MaterialLocalizations`和`WidgetsLocalizations`接口的实现。 这些实现类分别称为`DefaultMaterialLocalizations`和`DefaultWidgetsLocalizations`。flutter_localizations 包包含`GlobalMaterialLocalizations`和`GlobalWidgetsLocalizations`的本地化接口的多语言实现， 国际化的应用程序必须按照本节开头说明的那样为这些类指定本地化Delegate。\n\n上述的`GlobalMaterialLocalizations`和`GlobalWidgetsLocalizations`只是Material组件库的本地化实现，如果我们要让自己的布局支持多语言，那么就需要实现在即的`Localizations`，我们将在下一节介绍其具体的实现方式。\n\n\n\n"
  },
  {
    "path": "src/chapter14/element_buildcontext.md",
    "content": "# 14.2 Element与BuildContext\n\n## 14.2.1 Element\n\n在“Widget简介”一节，我们介绍了Widget和Element的关系，我们知道最终的UI树其实是由一个个独立的Element节点构成。我们也说过组件最终的Layout、渲染都是通过`RenderObject`来完成的，从创建到渲染的大体流程是：根据Widget生成Element，然后创建相应的`RenderObject`并关联到`Element.renderObject`属性上，最后再通过`RenderObject`来完成布局排列和绘制。\n\nElement就是Widget在UI树具体位置的一个实例化对象，大多数Element只有唯一的`renderObject`，但还有一些Element会有多个子节点，如继承自`RenderObjectElement`的一些类，比如`MultiChildRenderObjectElement`。最终所有Element的RenderObject构成一棵树，我们称之为”Render Tree“即”渲染树“。总结一下，我们可以认为Flutter的UI系统包含三棵树：Widget树、Element树、渲染树。他们的依赖关系是：Element树根据Widget树生成，而渲染树又依赖于Element树，如图14-0所示。\n\n![图14-0](../imgs/14-0.png)\n\n现在我们重点看一下Element，Element的生命周期如下：\n\n1. Framework 调用`Widget.createElement` 创建一个Element实例，记为`element`\n2. Framework 调用 `element.mount(parentElement,newSlot)` ，mount方法中首先调用`element`所对应Widget的`createRenderObject`方法创建与`element`相关联的RenderObject对象，然后调用`element.attachRenderObject`方法将`element.renderObject`添加到渲染树中插槽指定的位置（这一步不是必须的，一般发生在Element树结构发生变化时才需要重新attach）。插入到渲染树后的`element`就处于“active”状态，处于“active”状态后就可以显示在屏幕上了（可以隐藏）。\n3. 当有父Widget的配置数据改变时，同时其`State.build`返回的Widget结构与之前不同，此时就需要重新构建对应的Element树。为了进行Element复用，在Element重新构建前会先尝试是否可以复用旧树上相同位置的element，element节点在更新前都会调用其对应Widget的`canUpdate`方法，如果返回`true`，则复用旧Element，旧的Element会使用新Widget配置数据更新，反之则会创建一个新的Element。`Widget.canUpdate`主要是判断`newWidget`与`oldWidget`的`runtimeType`和`key`是否同时相等，如果同时相等就返回`true`，否则就会返回`false`。根据这个原理，当我们需要强制更新一个Widget时，可以通过指定不同的Key来避免复用。\n4. 当有祖先Element决定要移除`element ` 时（如Widget树结构发生了变化，导致`element`对应的Widget被移除），这时该祖先Element就会调用`deactivateChild` 方法来移除它，移除后`element.renderObject`也会被从渲染树中移除，然后Framework会调用`element.deactivate ` 方法，这时`element`状态变为“inactive”状态。\n5. “inactive”态的element将不会再显示到屏幕。为了避免在一次动画执行过程中反复创建、移除某个特定element，“inactive”态的element在当前动画最后一帧结束前都会保留，如果在动画执行结束后它还未能重新变成“active”状态，Framework就会调用其`unmount`方法将其彻底移除，这时element的状态为`defunct`,它将永远不会再被插入到树中。\n6. 如果`element`要重新插入到Element树的其它位置，如`element`或`element`的祖先拥有一个GlobalKey（用于全局复用元素），那么Framework会先将element从现有位置移除，然后再调用其`activate`方法，并将其`renderObject`重新attach到渲染树。\n\n看完Element的生命周期，可能有些读者会有疑问，开发者会直接操作Element树吗？其实对于开发者来说，大多数情况下只需要关注Widget树就行，Flutter框架已经将对Widget树的操作映射到了Element树上，这可以极大的降低复杂度，提高开发效率。但是了解Element对理解整个Flutter UI框架是至关重要的，Flutter正是通过Element这个纽带将Widget和RenderObject关联起来，了解Element层不仅会帮助读者对Flutter UI框架有个清晰的认识，而且也会提高自己的抽象能力和设计能力。另外在有些时候，我们必须得直接使用Element对象来完成一些操作，比如获取主题Theme数据，具体细节将在下文介绍。\n\n## 14.2.2 BuildContext\n\n我们已经知道，`StatelessWidget`和`StatefulWidget`的`build`方法都会传一个`BuildContext`对象：\n\n```dart\nWidget build(BuildContext context) {}\n```\n\n我们也知道，在很多时候我们都需要使用这个`context` 做一些事，比如：\n\n```dart\nTheme.of(context) //获取主题\nNavigator.push(context, route) //入栈新路由\nLocalizations.of(context, type) //获取Local\ncontext.size //获取上下文大小\ncontext.findRenderObject() //查找当前或最近的一个祖先RenderObject\n```\n\n那么`BuildContext`到底是什么呢，查看其定义，发现其是一个抽象接口类：\n\n```dart\nabstract class BuildContext {\n    ...\n}\n```\n\n那这个`context`对象对应的实现类到底是谁呢？我们顺藤摸瓜，发现`build`调用是发生在`StatelessWidget`和`StatefulWidget`对应的`StatelessElement`和`StatefulElement`的`build`方法中，以`StatelessElement`为例：\n\n```dart\n\nclass StatelessElement extends ComponentElement {\n  ...\n  @override\n  Widget build() => widget.build(this);\n  ...\n}\n```\n\n发现`build`传递的参数是`this`，很明显！这个`BuildContext`就是`StatelessElement`。同样，我们同样发现`StatefulWidget`的`context`是`StatefulElement`。但`StatelessElement`和`StatefulElement`本身并没有实现`BuildContext`接口，继续跟踪代码，发现它们间接继承自`Element`类，然后查看`Element`类定义，发现`Element`类果然实现了`BuildContext`接口:\n\n```dart\nclass Element extends DiagnosticableTree implements BuildContext {\n    ...\n}\n```\n\n至此真相大白，`BuildContext`就是widget对应的`Element`，所以我们可以通过`context`在`StatelessWidget`和`StatefulWidget`的`build`方法中直接访问`Element`对象。我们获取主题数据的代码`Theme.of(context)`内部正是调用了Element的`dependOnInheritedWidgetOfExactType()`方法。\n\n> 思考题：为什么build方法的参数不定义成Element对象，而要定义成BuildContext ?\n\n### 进阶\n\n我们可以看到Element是Flutter UI框架内部连接widget和`RenderObject`的纽带，大多数时候开发者只需要关注widget层即可，但是widget层有时候并不能完全屏蔽`Element`细节，所以Framework在`StatelessWidget`和`StatefulWidget`中通过`build`方法参数又将`Element`对象也传递给了开发者，这样一来，开发者便可以在需要时直接操作`Element`对象。那么现在笔者提两个问题，请读者先自己思考一下：\n\n1. 如果没有widget层，单靠`Element`层是否可以搭建起一个可用的UI框架？如果可以应该是什么样子？\n2. Flutter UI框架能不做成响应式吗？\n\n对于问题1，答案当然是肯定的，因为我们之前说过widget树只是`Element`树的映射，我们完全可以直接通过Element来搭建一个UI框架。下面举一个例子：\n\n我们通过纯粹的Element来模拟一个`StatefulWidget`的功能，假设有一个页面，该页面有一个按钮，按钮的文本是一个9位数，点击一次按钮，则对9个数随机排一次序，代码如下：\n\n```dart\nclass HomeView extends ComponentElement{\n  HomeView(Widget widget) : super(widget);\n  String text = \"123456789\";\n\n  @override\n  Widget build() {\n    Color primary=Theme.of(this).primaryColor; //1\n    return GestureDetector(\n      child: Center(\n        child: FlatButton(\n          child: Text(text, style: TextStyle(color: primary),),\n          onPressed: () {\n            var t = text.split(\"\")..shuffle();\n            text = t.join();\n            markNeedsBuild(); //点击后将该Element标记为dirty，Element将会rebuild\n          },\n        ),\n      ),\n    );\n  }\n}\n```\n\n- 上面`build`方法不接收参数，这一点和在`StatelessWidget`和`StatefulWidget`中`build(BuildContext)`方法不同。代码中需要用到`BuildContext`的地方直接用`this`代替即可，如代码注释1处`Theme.of(this)`参数直接传`this`即可，因为当前对象本身就是`Element`实例。\n- 当`text`发生改变时，我们调用`markNeedsBuild()`方法将当前Element标记为dirty即可，标记为dirty的Element会在下一帧中重建。实际上，`State.setState()`在内部也是调用的`markNeedsBuild()`方法。\n\n- 上面代码中build方法返回的仍然是一个widget，这是由于Flutter框架中已经有了widget这一层，并且组件库都已经是以widget的形式提供了，如果在Flutter框架中所有组件都像示例的`HomeView`一样以`Element`形式提供，那么就可以用纯`Element`来构建UI了`HomeView`的build方法返回值类型就可以是`Element`了。\n\n如果我们需要将上面代码在现有Flutter框架中跑起来，那么还是得提供一个“适配器”widget将`HomeView`结合到现有框架中，下面`CustomHome`就相当于“适配器”：\n\n```dart\nclass CustomHome extends Widget {\n  @override\n  Element createElement() {\n    return HomeView(this);\n  }\n}\n```\n\n现在就可以将`CustomHome`添加到widget树了，我们在一个新路由页创建它，最终效果如下如图14-1和14-2（点击后）所示：\n\n![图14-1](../imgs/14-1.png) ![图14-2](../imgs/14-2.png)\n\n点击按钮则按钮文本会随机排序。\n\n对于问题2，答案当然也是肯定的，Flutter engine提供的dart API是原始且独立的，这个与操作系统提供的API类似，上层UI框架设计成什么样完全取决于设计者，完全可以将UI框架设计成Android风格或iOS风格，但这些事Google不会再去做，我们也没必要再去搞这一套，这是因为响应式的思想本身是很棒的，之所以提出这个问题，是因为笔者认为做与不做是一回事，但知道能不能做是另一回事，这能反映出我们对知识的理解程度。\n\n\n### 总结\n\n本节详细的介绍了`Element`的生命周期，以及它Widget、BuildContext的关系，也介绍了Element在Flutter UI系统中的角色和作用，我们将在下一节介绍Flutter UI系统中另一个重要的角色RenderObject。\n\n\n\n"
  },
  {
    "path": "src/chapter14/flutter_app_startup.md",
    "content": "# 14.4 Flutter运行机制-从启动到显示\n\n本节我们主要介绍一下Flutter从启动到显示的过程。\n\n### 启动\n\nFlutter的入口在\"lib/main.dart\"的`main()`函数中，它是Dart应用程序的起点。在Flutter应用中，`main()`函数最简单的实现如下：\n\n```dart\nvoid main() {\n  runApp(MyApp());\n}\n```\n\n可以看`main()`函数只调用了一个`runApp()`方法，我们看看`runApp()`方法中都做了什么：\n\n```dart\nvoid runApp(Widget app) {\n  WidgetsFlutterBinding.ensureInitialized()\n    ..attachRootWidget(app)\n    ..scheduleWarmUpFrame();\n}\n```\n\n参数`app`是一个widget，它是Flutter应用启动后要展示的第一个Widget。而`WidgetsFlutterBinding`正是绑定widget 框架和Flutter engine的桥梁，定义如下：\n\n```dart\nclass WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {\n  static WidgetsBinding ensureInitialized() {\n    if (WidgetsBinding.instance == null)\n      WidgetsFlutterBinding();\n    return WidgetsBinding.instance;\n  }\n}\n```\n\n可以看到`WidgetsFlutterBinding`继承自`BindingBase` 并混入了很多`Binding`，在介绍这些`Binding`之前我们先介绍一下`Window`，下面是`Window`的官方解释：\n\n> The most basic interface to the host operating system's user interface.\n\n很明显，`Window`正是Flutter Framework连接宿主操作系统的接口。我们看一下`Window`类的部分定义：\n\n```dart\nclass Window {\n    \n  // 当前设备的DPI，即一个逻辑像素显示多少物理像素，数字越大，显示效果就越精细保真。\n  // DPI是设备屏幕的固件属性，如Nexus 6的屏幕DPI为3.5 \n  double get devicePixelRatio => _devicePixelRatio;\n  \n  // Flutter UI绘制区域的大小\n  Size get physicalSize => _physicalSize;\n\n  // 当前系统默认的语言Locale\n  Locale get locale;\n    \n  // 当前系统字体缩放比例。  \n  double get textScaleFactor => _textScaleFactor;  \n    \n  // 当绘制区域大小改变回调\n  VoidCallback get onMetricsChanged => _onMetricsChanged;  \n  // Locale发生变化回调\n  VoidCallback get onLocaleChanged => _onLocaleChanged;\n  // 系统字体缩放变化回调\n  VoidCallback get onTextScaleFactorChanged => _onTextScaleFactorChanged;\n  // 绘制前回调，一般会受显示器的垂直同步信号VSync驱动，当屏幕刷新时就会被调用\n  FrameCallback get onBeginFrame => _onBeginFrame;\n  // 绘制回调  \n  VoidCallback get onDrawFrame => _onDrawFrame;\n  // 点击或指针事件回调\n  PointerDataPacketCallback get onPointerDataPacket => _onPointerDataPacket;\n  // 调度Frame，该方法执行后，onBeginFrame和onDrawFrame将紧接着会在合适时机被调用，\n  // 此方法会直接调用Flutter engine的Window_scheduleFrame方法\n  void scheduleFrame() native 'Window_scheduleFrame';\n  // 更新应用在GPU上的渲染,此方法会直接调用Flutter engine的Window_render方法\n  void render(Scene scene) native 'Window_render';\n\n  // 发送平台消息\n  void sendPlatformMessage(String name,\n                           ByteData data,\n                           PlatformMessageResponseCallback callback) ;\n  // 平台通道消息处理回调  \n  PlatformMessageCallback get onPlatformMessage => _onPlatformMessage;\n  \n  ... //其它属性及回调\n   \n}\n```\n\n可以看到`Window`类包含了当前设备和系统的一些信息以及Flutter Engine的一些回调。现在我们再回来看看`WidgetsFlutterBinding`混入的各种Binding。通过查看这些 Binding的源码，我们可以发现这些Binding中基本都是监听并处理`Window`对象的一些事件，然后将这些事件按照Framework的模型包装、抽象然后分发。可以看到`WidgetsFlutterBinding`正是粘连Flutter engine与上层Framework的“胶水”。\n\n- `GestureBinding`：提供了`window.onPointerDataPacket` 回调，绑定Framework手势子系统，是Framework事件模型与底层事件的绑定入口。\n- `ServicesBinding`：提供了`window.onPlatformMessage` 回调， 用于绑定平台消息通道（message channel），主要处理原生和Flutter通信。\n- `SchedulerBinding`：提供了`window.onBeginFrame`和`window.onDrawFrame`回调，监听刷新事件，绑定Framework绘制调度子系统。\n- `PaintingBinding`：绑定绘制库，主要用于处理图片缓存。\n- `SemanticsBinding`：语义化层与Flutter engine的桥梁，主要是辅助功能的底层支持。\n- `RendererBinding`: 提供了`window.onMetricsChanged` 、`window.onTextScaleFactorChanged` 等回调。它是渲染树与Flutter engine的桥梁。\n- `WidgetsBinding`：提供了`window.onLocaleChanged`、`onBuildScheduled ` 等回调。它是Flutter widget层与engine的桥梁。\n\n` WidgetsFlutterBinding.ensureInitialized()`负责初始化一个`WidgetsBinding`的全局单例，紧接着会调用`WidgetsBinding`的`attachRootWidget`方法，该方法负责将根Widget添加到`RenderView`上，代码如下：\n\n```dart\nvoid attachRootWidget(Widget rootWidget) {\n  _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(\n    container: renderView, \n    debugShortDescription: '[root]',\n    child: rootWidget\n  ).attachToRenderTree(buildOwner, renderViewElement);\n}\n```\n\n注意，代码中的有`renderView`和`renderViewElement`两个变量，`renderView`是一个`RenderObject`，它是渲染树的根，而`renderViewElement`是`renderView`对应的`Element`对象，可见该方法主要完成了根widget到根 `RenderObject`再到根`Element`的整个关联过程。我们看看`attachToRenderTree`的源码实现：\n\n```dart\nRenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [RenderObjectToWidgetElement<T> element]) {\n  if (element == null) {\n    owner.lockState(() {\n      element = createElement();\n      assert(element != null);\n      element.assignOwner(owner);\n    });\n    owner.buildScope(element, () {\n      element.mount(null, null);\n    });\n  } else {\n    element._newWidget = this;\n    element.markNeedsBuild();\n  }\n  return element;\n}\n```\n\n该方法负责创建根element，即` RenderObjectToWidgetElement`，并且将element与widget 进行关联，即创建出 widget树对应的element树。如果element 已经创建过了，则将根element 中关联的widget 设为新的，由此可以看出element 只会创建一次，后面会进行复用。那么`BuildOwner`是什么呢？其实他就是widget framework的管理类，它跟踪哪些widget需要重新构建。\n\n### 渲染\n\n回到`runApp`的实现中，当调用完`attachRootWidget`后，最后一行会调用 `WidgetsFlutterBinding` 实例的 `scheduleWarmUpFrame()` 方法，该方法的实现在`SchedulerBinding` 中，它被调用后会立即进行一次绘制（而不是等待\"vsync\" 信号），在此次绘制结束前，该方法会锁定事件分发，也就是说在本次绘制结束完成之前Flutter将不会响应各种事件，这可以保证在绘制过程中不会再触发新的重绘。下面是`scheduleWarmUpFrame()` 方法的部分实现(省略了无关代码)：\n\n```dart\nvoid scheduleWarmUpFrame() {\n  ...\n  Timer.run(() {\n    handleBeginFrame(null); \n  });\n  Timer.run(() {\n    handleDrawFrame();  \n    resetEpoch();\n  });\n  // 锁定事件\n  lockEvents(() async {\n    await endOfFrame;\n    Timeline.finishSync();\n  });\n ...\n}\n```\n\n可以看到该方法中主要调用了`handleBeginFrame()` 和 `handleDrawFrame()` 两个方法，在看这两个方法之前我们首先了解一下Frame 和 FrameCallback 的概念：\n\n- Frame: 一次绘制过程，我们称其为一帧。Flutter engine受显示器垂直同步信号\"VSync\"的驱使不断的触发绘制。我们之前说的Flutter可以实现60fps（Frame Per-Second），就是指一秒钟可以触发60次重绘，FPS值越大，界面就越流畅。\n\n- FrameCallback：`SchedulerBinding` 类中有三个FrameCallback回调队列， 在一次绘制过程中，这三个回调队列会放在不同时机被执行：\n\n  1. `transientCallbacks`：用于存放一些临时回调，一般存放动画回调。可以通过`SchedulerBinding.instance.scheduleFrameCallback` 添加回调。\n  2. `persistentCallbacks`：用于存放一些持久的回调，不能在此类回调中再请求新的绘制帧，持久回调一经注册则不能移除。`SchedulerBinding.instance.addPersitentFrameCallback()`，这个回调中处理了布局与绘制工作。\n  3. `postFrameCallbacks`：在Frame结束时只会被调用一次，调用后会被系统移除，可由 `SchedulerBinding.instance.addPostFrameCallback()` 注册，注意，不要在此类回调中再触发新的Frame，这可以会导致循环刷新。\n\n现在请读者自行查看`handleBeginFrame()` 和 `handleDrawFrame()` 两个方法的源码，可以发现前者主要是执行了`transientCallbacks`队列，而后者执行了 `persistentCallbacks` 和 `postFrameCallbacks` 队列。\n\n### 绘制\n\n渲染和绘制逻辑在`RendererBinding`中实现，查看其源码，发现在其`initInstances()`方法中有如下代码：\n\n```dart\nvoid initInstances() {\n  ... //省略无关代码\n      \n  //监听Window对象的事件  \n  ui.window\n    ..onMetricsChanged = handleMetricsChanged\n    ..onTextScaleFactorChanged = handleTextScaleFactorChanged\n    ..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged\n    ..onSemanticsAction = _handleSemanticsAction;\n   \n  //添加PersistentFrameCallback    \n  addPersistentFrameCallback(_handlePersistentFrameCallback);\n}\n```\n\n我们看最后一行，通过`addPersistentFrameCallback` 向`persistentCallbacks`队列添加了一个回调 `_handlePersistentFrameCallback`:\n\n```dart\nvoid _handlePersistentFrameCallback(Duration timeStamp) {\n  drawFrame();\n}\n```\n\n该方法直接调用了`RendererBinding`的`drawFrame()`方法：\n\n```dart\nvoid drawFrame() {\n  assert(renderView != null);\n  pipelineOwner.flushLayout(); //布局\n  pipelineOwner.flushCompositingBits(); //重绘之前的预处理操作，检查RenderObject是否需要重绘\n  pipelineOwner.flushPaint(); // 重绘\n  renderView.compositeFrame(); // 将需要绘制的比特数据发给GPU\n  pipelineOwner.flushSemantics(); // this also sends the semantics to the OS.\n}\n```\n\n我们看看这些方法分别做了什么：\n\n#### flushLayout()\n\n```dart\nvoid flushLayout() {\n   ...\n    while (_nodesNeedingLayout.isNotEmpty) {\n      final List<RenderObject> dirtyNodes = _nodesNeedingLayout;\n      _nodesNeedingLayout = <RenderObject>[];\n      for (RenderObject node in \n           dirtyNodes..sort((RenderObject a, RenderObject b) => a.depth - b.depth)) {\n        if (node._needsLayout && node.owner == this)\n          node._layoutWithoutResize();\n      }\n    }\n  } \n}\n```\n\n源码很简单，该方法主要任务是更新了所有被标记为“dirty”的`RenderObject`的布局信息。主要的动作发生在`node._layoutWithoutResize()`方法中，该方法中会调用`performLayout()`进行重新布局。\n\n#### flushCompositingBits()\n\n```dart\nvoid flushCompositingBits() {\n  _nodesNeedingCompositingBitsUpdate.sort(\n      (RenderObject a, RenderObject b) => a.depth - b.depth\n  );\n  for (RenderObject node in _nodesNeedingCompositingBitsUpdate) {\n    if (node._needsCompositingBitsUpdate && node.owner == this)\n      node._updateCompositingBits(); //更新RenderObject.needsCompositing属性值\n  }\n  _nodesNeedingCompositingBitsUpdate.clear();\n}\n```\n\n检查`RenderObject`是否需要重绘，然后更新`RenderObject.needsCompositing`属性，如果该属性值被标记为`true`则需要重绘。\n\n#### flushPaint()\n\n```dart\nvoid flushPaint() {\n ...\n  try {\n    final List<RenderObject> dirtyNodes = _nodesNeedingPaint; \n    _nodesNeedingPaint = <RenderObject>[];\n    // 反向遍历需要重绘的RenderObject\n    for (RenderObject node in \n         dirtyNodes..sort((RenderObject a, RenderObject b) => b.depth - a.depth)) {\n      if (node._needsPaint && node.owner == this) {\n        if (node._layer.attached) {\n          // 真正的绘制逻辑  \n          PaintingContext.repaintCompositedChild(node);\n        } else {\n          node._skippedPaintingOnLayer();\n        }\n      }\n    }\n  } \n}\n```\n\n该方法进行了最终的绘制，可以看出它不是重绘了所有 `RenderObject`，而是只重绘了需要重绘的 `RenderObject`。真正的绘制是通过`PaintingContext.repaintCompositedChild()`来绘制的，该方法最终会调用Flutter engine提供的Canvas API来完成绘制。\n\n#### compositeFrame()\n\n```dart\nvoid compositeFrame() {\n  ...\n  try {\n    final ui.SceneBuilder builder = ui.SceneBuilder();\n    final ui.Scene scene = layer.buildScene(builder);\n    if (automaticSystemUiAdjustment)\n      _updateSystemChrome();\n    ui.window.render(scene); //调用Flutter engine的渲染API\n    scene.dispose(); \n  } finally {\n    Timeline.finishSync();\n  }\n}\n```\n\n这个方法中有一个`Scene`对象，Scene对象是一个数据结构，保存最终渲染后的像素信息。这个方法将Canvas画好的`Scene`传给`window.render()`方法，该方法会直接将scene信息发送给Flutter engine，最终由engine将图像画在设备屏幕上。\n\n#### 最后\n\n需要注意的是：由于`RendererBinding`只是一个mixin，而with它的是`WidgetsBinding`，所以我们需要看看`WidgetsBinding`中是否重写该方法，查看`WidgetsBinding`的`drawFrame()`方法源码：\n\n```dart\n@override\nvoid drawFrame() {\n ...//省略无关代码\n  try {\n    if (renderViewElement != null)\n      buildOwner.buildScope(renderViewElement); \n    super.drawFrame(); //调用RendererBinding的drawFrame()方法\n    buildOwner.finalizeTree();\n  } \n}\n```\n\n我们发现在调用`RendererBinding.drawFrame()`方法前会调用 `buildOwner.buildScope()` （非首次绘制），该方法会将被标记为“dirty” 的 element 进行 `rebuild()` 。\n\n### 总结\n\n本节介绍了Flutter APP从启动到显示到屏幕上的主流程，读者可以结合前面章节对Widget、Element以及RenderObject的介绍来加强细节理解。\n\n"
  },
  {
    "path": "src/chapter14/flutter_ui_system.md",
    "content": "# 14.1 Flutter UI系统\n\n在本书的前面章节中，我们多次提到\"UI系统\"这个概念，本书中所指的UI系统特指：基于一个平台，在此平台上实现GUI的一个系统，这里的平台特指操作系统，如Android、iOS或者Windows、macOS。我们说过各个平台UI系统的原理是相通的，也就是说无论是Android还是iOS，他们将一个用户界面展示到屏幕的流程是相似的，所以，在介绍Flutter UI系统之前，我们先看看UI系统的基本原理，这样可以帮助读者对操作系统和系统底层UI逻辑有一个清晰的认识。\n\n### 硬件绘图基本原理\n\n提到原理，我们要从屏幕显示图像的基本原理谈起。我们知道显示器（屏幕）是由一个个物理显示单元组成，每一个单元我们可以称之为一个物理像素点，而每一个像素点可以发出多种颜色，显示器成相的原理就是在不同的物理像素点上显示不同的颜色，最终构成完整的图像。\n\n一个像素点能发出的所有颜色总数是显示器的一个重要指标，比如我们所说的1600万色的屏幕就是指一个像素点可以显示出1600万种颜色，而显示器颜色是有RGB三基色组成，所以1600万即2的24次方，即每个基本色（R、G、B）深度扩展至8 bit(位)，颜色深度越深，所能显示的色彩更加丰富靓丽。\n\n为了更新显示画面，显示器是以固定的频率刷新（从GPU取数据），比如有一部手机屏幕的刷新频率是 60Hz。当一帧图像绘制完毕后准备绘制下一帧时，显示器会发出一个垂直同步信号（如VSync）， 60Hz的屏幕就会一秒内发出 60次这样的信号。而这个信号主要是用于同步CPU、GPU和显示器的。一般地来说，计算机系统中，CPU、GPU和显示器以一种特定的方式协作：CPU将计算好的显示内容提交给 GPU，GPU渲染后放入帧缓冲区，然后视频控制器按照同步信号从帧缓冲区取帧数据传递给显示器显示。\n\nCPU和GPU的任务是各有偏重的，CPU主要用于基本数学和逻辑计算，而GPU主要执行和图形处理相关的复杂的数学，如矩阵变化和几何计算，GPU的主要作用就是确定最终输送给显示器的各个像素点的色值。\n\n### 操作系统绘制API的封装\n\n由于最终的图形计算和绘制都是由相应的硬件来完成，而直接操作硬件的指令通常都会有操作系统屏蔽，应用开发者通常不会直接面对硬件，操作系统屏蔽了这些底层硬件操作后会提供一些封装后的API供操作系统之上的应用调用，但是对于应用开发者来说，直接调用这些操作系统提供的API是比较复杂和低效的，因为操作系统提供的API往往比较基础，直接调用需要了解API的很多细节。正是因为这个原因，几乎所有用于开发GUI程序的编程语言都会在操作系统之上再封装一层，将操作系统原生API封装在一个编程框架和模型中，然后定义一种简单的开发规则来开发GUI应用程序，而这一层抽象，正是我们所说的“UI”系统，如Android SDK正是封装了Android操作系统API，提供了一个“UI描述文件XML+Java操作DOM”的UI系统，而iOS的UIKit 对View的抽象也是一样的，他们都将操作系统API抽象成一个基础对象（如用于2D图形绘制的Canvas），然后再定义一套规则来描述UI，如UI树结构，UI操作的单线程原则等。\n\n### Flutter UI系统\n\n我们可以看到，无论是Android SDK还是iOS的UIKit 的职责都是相同的，它们只是语言载体和底层的系统不同而已。那么可不可以实现这么一个UI系统：可以使用同一种编程语言开发，然后针对不同操作系统API抽象一个对上接口一致，对下适配不同操作系统的的中间层，然后在打包编译时再使用相应的中间层代码？如果可以做到，那么我们就可以使用同一套代码编写跨平台的应用了。而Flutter的原理正是如此，它提供了一套Dart API，然后在底层通过OpenGL这种跨平台的绘制库（内部会调用操作系统API）实现了一套代码跨多端。由于Dart API也是调用操作系统API，所以它的性能接近原生。\n\n> 注意，虽然Dart是先调用了OpenGL，OpenGL才会调用操作系统API，但是这仍然是原生渲染，因为OpenGL只是操作系统API的一个封装库，它并不像WebView渲染那样需要JavaScript运行环境和CSS渲染器，所以不会有性能损失。\n\n至此，我们已经介绍了Flutter UI系统和操作系统交互的这一部分原理，现在需要说一些它对应用开发者定义的开发标准。其实在前面的章节中，我们已经对这个标准非常熟悉了, 简单概括就是：组合和响应式。我们要开发一个UI界面，需要通过组合其它Widget来实现，Flutter中，一切都是Widget，当UI要发生变化时，我们不去直接修改DOM，而是通过更新状态，让Flutter UI系统来根据新的状态来重新构建UI。\n\n讲到这里，读者可能发现Flutter UI系统和Flutter Framework的概念是差不多的，的确如此，之所以用“UI系统”，是因为其他平台中可能不这么叫，我们只是为了概念统一，便于描述，读者不必纠结于概念本身。\n\n在接下来的小节中，我们先详细介绍一下`Element`、`RenderObject`，它们是组成Flutter UI系统的基石。最后我们再分析一下Flutter应用启动和运行的整体过程。\n\n\n\n\n\n\n\n"
  },
  {
    "path": "src/chapter14/image_and_cache.md",
    "content": "# 14.5 图片加载原理与缓存\n\n在本书前面章节已经介绍过`Image` 组件，并提到Flutter框架对加载过的图片是有缓存的（内存），默认最大缓存数量是1000，最大缓存空间为100M。本节便详细介绍Image的原理及图片缓存机制，下面我们先看看`ImageProvider` 类。\n\n## 14.5.1 ImageProvider\n\n我们已经知道`Image` 组件的`image` 参数是一个必选参数，它是`ImageProvider`类型。下面我们便详细介绍一下`ImageProvider`，`ImageProvider`是一个抽象类，定义了图片数据获取和加载的相关接口。它的主要职责有两个：\n\n1. 提供图片数据源\n2. 缓存图片\n\n我们看看`ImageProvider`抽象类的详细定义：\n\n```dart\nabstract class ImageProvider<T> {\n\n  ImageStream resolve(ImageConfiguration configuration) {\n    // 实现代码省略\n  }\n  Future<bool> evict({ ImageCache cache,\n                      ImageConfiguration configuration = ImageConfiguration.empty }) async {\n    // 实现代码省略\n  }\n\n  Future<T> obtainKey(ImageConfiguration configuration); \n  @protected\n  ImageStreamCompleter load(T key); // 需子类实现\n}\n```\n\n#### `load(T key)`方法\n\n加载图片数据源的接口，不同的数据源的加载方法不同，每个`ImageProvider`的子类必须实现它。比如`NetworkImage`类和`AssetImage`类，它们都是`ImageProvider`的子类，但它们需要从不同的数据源来加载图片数据：`NetworkImage`是从网络来加载图片数据，而`AssetImage`则是从最终的应用包里来加载（加载打到应用安装包里的资源图片）。 我们以`NetworkImage`为例，看看其load方法的实现：\n\n```dart\n\n@override\nImageStreamCompleter load(image_provider.NetworkImage key) {\n\n  final StreamController<ImageChunkEvent> chunkEvents = StreamController<ImageChunkEvent>();\n  \n  return MultiFrameImageStreamCompleter(\n    codec: _loadAsync(key, chunkEvents), //调用\n    chunkEvents: chunkEvents.stream,\n    scale: key.scale,\n    ... //省略无关代码\n  );\n}\n```\n\n我们看到，`load`方法的返回值类型是`ImageStreamCompleter` ，它是一个抽象类，定义了管理图片加载过程的一些接口，`Image` Widget中正是通过它来监听图片加载状态的（我们将在下面介绍`Image` 原理时详细介绍）。\n\n`MultiFrameImageStreamCompleter` 是 `ImageStreamCompleter`的一个子类，是flutter sdk预置的类，通过该类，我们以方便、轻松地创建出一个`ImageStreamCompleter`实例来做为`load`方法的返回值。\n\n我们可以看到，`MultiFrameImageStreamCompleter` 需要一个`codec`参数，该参数类型为`Future<ui.Codec> `。`Codec ` 是处理图片编解码的类的一个handler，实际上，它只是一个flutter engine API 的包装类，也就是说图片的编解码逻辑不是在Dart 代码部分实现，而是在flutter engine中实现的。`Codec`类部分定义如下：\n\n```dart\n@pragma('vm:entry-point')\nclass Codec extends NativeFieldWrapperClass2 {\n  // 此类由flutter engine创建，不应该手动实例化此类或直接继承此类。\n  @pragma('vm:entry-point')\n  Codec._();\n\n  /// 图片中的帧数(动态图会有多帧)\n  int get frameCount native 'Codec_frameCount';\n\n  /// 动画重复的次数\n  /// * 0 表示只执行一次\n  /// * -1 表示循环执行\n  int get repetitionCount native 'Codec_repetitionCount';\n\n  /// 获取下一个动画帧\n  Future<FrameInfo> getNextFrame() {\n    return _futurize(_getNextFrame);\n  }\n\n  String _getNextFrame(_Callback<FrameInfo> callback) native 'Codec_getNextFrame';\n```\n\n我们可以看到`Codec`最终的结果是一个或多个（动图）帧，而这些帧最终会绘制到屏幕上。\n\n`MultiFrameImageStreamCompleter 的` `codec`参数值为`_loadAsync`方法的返回值，我们继续看`_loadAsync`方法的实现：\n\n```dart\n\n Future<ui.Codec> _loadAsync(\n    NetworkImage key,\n    StreamController<ImageChunkEvent> chunkEvents,\n  ) async {\n    try {\n      //下载图片\n      final Uri resolved = Uri.base.resolve(key.url);\n      final HttpClientRequest request = await _httpClient.getUrl(resolved);\n      headers?.forEach((String name, String value) {\n        request.headers.add(name, value);\n      });\n      final HttpClientResponse response = await request.close();\n      if (response.statusCode != HttpStatus.ok)\n        throw Exception(...);\n      // 接收图片数据 \n      final Uint8List bytes = await consolidateHttpClientResponseBytes(\n        response,\n        onBytesReceived: (int cumulative, int total) {\n          chunkEvents.add(ImageChunkEvent(\n            cumulativeBytesLoaded: cumulative,\n            expectedTotalBytes: total,\n          ));\n        },\n      );\n      if (bytes.lengthInBytes == 0)\n        throw Exception('NetworkImage is an empty file: $resolved');\n      // 对图片数据进行解码\n      return PaintingBinding.instance.instantiateImageCodec(bytes);\n    } finally {\n      chunkEvents.close();\n    }\n  }\n```\n\n可以看到`_loadAsync`方法主要做了两件事：\n\n1. 下载图片。\n2. 对下载的图片数据进行解码。\n\n下载逻辑比较简单：通过`HttpClient`从网上下载图片，另外下载请求会设置一些自定义的header，开发者可以通过`NetworkImage`的`headers`命名参数来传递。\n\n在图片下载完成后调用了`PaintingBinding.instance.instantiateImageCodec(bytes)`对图片进行解码，值得注意的是`instantiateImageCodec(...)`也是一个Native API的包装，实际上会调用Flutter engine的`instantiateImageCodec`方法，源码如下：\n\n```dart\nString _instantiateImageCodec(Uint8List list, _Callback<Codec> callback, _ImageInfo imageInfo, int targetWidth, int targetHeight)\n  native 'instantiateImageCodec';\n```\n\n#### `obtainKey(ImageConfiguration)`方法\n\n该接口主要是为了配合实现图片缓存，`ImageProvider`从数据源加载完数据后，会在全局的`ImageCache`中缓存图片数据，而图片数据缓存是一个Map，而Map的key便是调用此方法的返回值，不同的key代表不同的图片数据缓存。\n\n#### `resolve(ImageConfiguration)` 方法\n\n`resolve`方法是`ImageProvider`的暴露的给`Image`的主入口方法，它接受一个`ImageConfiguration`参数，返回`ImageStream`，即图片数据流。我们重点看一下`resolve`执行流程：\n\n```dart\nImageStream resolve(ImageConfiguration configuration) {\n  ... //省略无关代码\n  final ImageStream stream = ImageStream();\n  T obtainedKey; //\n  //定义错误处理函数\n  Future<void> handleError(dynamic exception, StackTrace stack) async {\n    ... //省略无关代码\n    stream.setCompleter(imageCompleter);\n    imageCompleter.setError(...);\n  }\n\n  // 创建一个新Zone，主要是为了当发生错误时不会干扰MainZone\n  final Zone dangerZone = Zone.current.fork(...);\n  \n  dangerZone.runGuarded(() {\n    Future<T> key;\n    // 先验证是否已经有缓存\n    try {\n      // 生成缓存key，后面会根据此key来检测是否有缓存\n      key = obtainKey(configuration);\n    } catch (error, stackTrace) {\n      handleError(error, stackTrace);\n      return;\n    }\n    key.then<void>((T key) {\n      obtainedKey = key;\n      // 缓存的处理逻辑在这里，记为A，下面详细介绍\n      final ImageStreamCompleter completer = PaintingBinding.instance\n          .imageCache.putIfAbsent(key, () => load(key), onError: handleError);\n      if (completer != null) {\n        stream.setCompleter(completer);\n      }\n    }).catchError(handleError);\n  });\n  return stream;\n}\n```\n\n`ImageConfiguration`  包含图片和设备的相关信息，如图片的大小、所在的`AssetBundle `(只有打到安装包的图片存在)以及当前的设备平台、devicePixelRatio（设备像素比等）。Flutter SDK提供了一个便捷函数`createLocalImageConfiguration`来创建`ImageConfiguration`  对象：\n\n```dart\nImageConfiguration createLocalImageConfiguration(BuildContext context, { Size size }) {\n  return ImageConfiguration(\n    bundle: DefaultAssetBundle.of(context),\n    devicePixelRatio: MediaQuery.of(context, nullOk: true)?.devicePixelRatio ?? 1.0,\n    locale: Localizations.localeOf(context, nullOk: true),\n    textDirection: Directionality.of(context),\n    size: size,\n    platform: defaultTargetPlatform,\n  );\n}\n```\n\n我们可以发现这些信息基本都是通过`Context`来获取。\n\n上面代码A处就是处理缓存的主要代码，这里的`PaintingBinding.instance.imageCache` 是 `ImageCache`的一个实例，它是`PaintingBinding`的一个属性，而Flutter框架中的`PaintingBinding.instance`是一个单例，`imageCache`事实上也是一个单例，也就是说图片缓存是全局的，统一由`PaintingBinding.instance.imageCache` 来管理。\n\n下面我们看看`ImageCache`类定义：\n\n```dart\nconst int _kDefaultSize = 1000;\nconst int _kDefaultSizeBytes = 100 << 20; // 100 MiB\n\nclass ImageCache {\n  // 正在加载中的图片队列\n  final Map<Object, _PendingImage> _pendingImages = <Object, _PendingImage>{};\n  // 缓存队列\n  final Map<Object, _CachedImage> _cache = <Object, _CachedImage>{};\n\n  // 缓存数量上限(1000)\n  int _maximumSize = _kDefaultSize;\n  // 缓存容量上限 (100 MB)\n  int _maximumSizeBytes = _kDefaultSizeBytes;\n  \n  // 缓存上限设置的setter\n  set maximumSize(int value) {...}\n  set maximumSizeBytes(int value) {...}\n \n  ... // 省略部分定义\n\n  // 清除所有缓存\n  void clear() {\n    // ...省略具体实现代码\n  }\n\n  // 清除指定key对应的图片缓存\n  bool evict(Object key) {\n   // ...省略具体实现代码\n  }\n\n \n  ImageStreamCompleter putIfAbsent(Object key, ImageStreamCompleter loader(), { ImageErrorListener onError }) {\n    assert(key != null);\n    assert(loader != null);\n    ImageStreamCompleter result = _pendingImages[key]?.completer;\n    // 图片还未加载成功，直接返回\n    if (result != null)\n      return result;\n \n    // 有缓存，继续往下走\n    // 先移除缓存，后再添加，可以让最新使用过的缓存在_map中的位置更近一些，清理时会LRU来清除\n    final _CachedImage image = _cache.remove(key);\n    if (image != null) {\n      _cache[key] = image;\n      return image.completer;\n    }\n    try {\n      result = loader();\n    } catch (error, stackTrace) {\n      if (onError != null) {\n        onError(error, stackTrace);\n        return null;\n      } else {\n        rethrow;\n      }\n    }\n    void listener(ImageInfo info, bool syncCall) {\n      final int imageSize = info?.image == null ? 0 : info.image.height * info.image.width * 4;\n      final _CachedImage image = _CachedImage(result, imageSize);\n      // 下面是缓存处理的逻辑\n      if (maximumSizeBytes > 0 && imageSize > maximumSizeBytes) {\n        _maximumSizeBytes = imageSize + 1000;\n      }\n      _currentSizeBytes += imageSize;\n      final _PendingImage pendingImage = _pendingImages.remove(key);\n      if (pendingImage != null) {\n        pendingImage.removeListener();\n      }\n\n      _cache[key] = image;\n      _checkCacheSize();\n    }\n    if (maximumSize > 0 && maximumSizeBytes > 0) {\n      final ImageStreamListener streamListener = ImageStreamListener(listener);\n      _pendingImages[key] = _PendingImage(result, streamListener);\n      // Listener is removed in [_PendingImage.removeListener].\n      result.addListener(streamListener);\n    }\n    return result;\n  }\n\n  // 当缓存数量超过最大值或缓存的大小超过最大缓存容量，会调用此方法清理到缓存上限以内\n  void _checkCacheSize() {\n   while (_currentSizeBytes > _maximumSizeBytes || _cache.length > _maximumSize) {\n      final Object key = _cache.keys.first;\n      final _CachedImage image = _cache[key];\n      _currentSizeBytes -= image.sizeBytes;\n      _cache.remove(key);\n    }\n    ... //省略无关代码\n  }\n}\n```\n\n有缓存则使用缓存，没有缓存则调用load方法加载图片，加载成功后:\n\n1. 先判断图片数据有没有缓存，如果有，则直接返回`ImageStream`。\n2. 如果没有缓存，则调用`load(T key)`方法从数据源加载图片数据，加载成功后先缓存，然后返回ImageStream。\n\n另外，我们可以看到`ImageCache`类中有设置缓存上限的setter，所以，如果我们可以自定义缓存上限：\n\n```dart\n PaintingBinding.instance.imageCache.maximumSize=2000; //最多2000张\n PaintingBinding.instance.imageCache.maximumSizeBytes = 200 << 20; //最大200M\n```\n\n现在我们看一下缓存的key，因为Map中相同key的值会被覆盖，也就是说key是图片缓存的一个唯一标识，只要是不同key，那么图片数据就会分别缓存（即使事实上是同一张图片）。那么图片的唯一标识是什么呢？跟踪源码，很容易发现key正是`ImageProvider.obtainKey()`方法的返回值，而此方法需要`ImageProvider`子类去重写，这也就意味着不同的`ImageProvider`对key的定义逻辑会不同。其实也很好理解，比如对于`NetworkImage`，将图片的url作为key会很合适，而对于`AssetImage`，则应该将“包名+路径”作为唯一的key。下面我们以`NetworkImage`为例，看一下它的`obtainKey()`实现：\n\n```dart\n@override\nFuture<NetworkImage> obtainKey(image_provider.ImageConfiguration configuration) {\n  return SynchronousFuture<NetworkImage>(this);\n}\n```\n\n代码很简单，创建了一个同步的future，然后直接将自身做为key返回。因为Map中在判断key（此时是`NetworkImage`对象）是否相等时会使用“==”运算符，那么定义key的逻辑就是`NetworkImage`的“==”运算符：\n\n```dart\n@override\nbool operator ==(dynamic other) {\n  ... //省略无关代码\n  final NetworkImage typedOther = other;\n  return url == typedOther.url\n      && scale == typedOther.scale;\n}\n```\n\n很清晰，对于网络图片来说，会将其“url+缩放比例”作为缓存的key。也就是说**如果两张图片的url或scale只要有一个不同，便会重新下载并分别缓存**。\n\n另外，我们需要注意的是，图片缓存是在内存中，并没有进行本地文件持久化存储，这也是为什么网络图片在应用重启后需要重新联网下载的原因。\n\n同时也意味着在应用生命周期内，如果缓存没有超过上限，相同的图片只会被下载一次。\n\n### 总结\n\n上面主要结合源码，探索了`ImageProvider`的主要功能和原理，如果要用一句话来总结`ImageProvider`功能，那么应该是：加载图片数据并进行缓存、解码。在此再次提醒读者，Flutter的源码是非常好的第一手资料，建议读者多多探索，另外，在阅读源码学习的同时一定要有总结，这样才不至于在源码中迷失。\n\n## 14.5.2 Image组件原理\n\n前面章节中我们介绍过`Image`的基础用法，现在我们更深入一些，研究一下`Image`是如何和`ImageProvider`配合来获取最终解码后的数据，然后又如何将图片绘制到屏幕上的。\n\n本节换一个思路，我们先不去直接看`Image`的源码，而根据已经掌握的知识来实现一个简版的“`Image`组件” `MyImage`，代码大致如下：\n\n```dart\nclass MyImage extends StatefulWidget {\n  const MyImage({\n    Key key,\n    @required this.imageProvider,\n  })\n      : assert(imageProvider != null),\n        super(key: key);\n\n  final ImageProvider imageProvider;\n\n  @override\n  _MyImageState createState() => _MyImageState();\n}\n\nclass _MyImageState extends State<MyImage> {\n  ImageStream _imageStream;\n  ImageInfo _imageInfo;\n\n  @override\n  void didChangeDependencies() {\n    super.didChangeDependencies();\n    // 依赖改变时，图片的配置信息可能会发生改变\n    _getImage();\n  }\n\n  @override\n  void didUpdateWidget(MyImage oldWidget) {\n    super.didUpdateWidget(oldWidget);\n    if (widget.imageProvider != oldWidget.imageProvider)\n      _getImage();\n  }\n\n  void _getImage() {\n    final ImageStream oldImageStream = _imageStream;\n    // 调用imageProvider.resolve方法，获得ImageStream。\n    _imageStream =\n        widget.imageProvider.resolve(createLocalImageConfiguration(context));\n    //判断新旧ImageStream是否相同，如果不同，则需要调整流的监听器\n    if (_imageStream.key != oldImageStream?.key) {\n      final ImageStreamListener listener = ImageStreamListener(_updateImage);\n      oldImageStream?.removeListener(listener);\n      _imageStream.addListener(listener);\n    }\n  }\n\n  void _updateImage(ImageInfo imageInfo, bool synchronousCall) {\n    setState(() {\n      // Trigger a build whenever the image changes.\n      _imageInfo = imageInfo;\n    });\n  }\n\n  @override\n  void dispose() {\n    _imageStream.removeListener(ImageStreamListener(_updateImage));\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return RawImage(\n      image: _imageInfo?.image, // this is a dart:ui Image object\n      scale: _imageInfo?.scale ?? 1.0,\n    );\n  }\n}\n```\n\n\n\n上面代码流程如下：\n\n1. 通过`imageProvider.resolve`方法可以得到一个`ImageStream`（图片数据流），然后监听`ImageStream`的变化。当图片数据源发生变化时，`ImageStream`会触发相应的事件，而本例中我们只设置了图片成功的监听器`_updateImage`，而`_updateImage`中只更新了`_imageInfo`。值得注意的是，如果是静态图，`ImageStream`只会触发一次时间，如果是动态图，则会触发多次事件，每一次都会有一个解码后的图片帧。\n2. `_imageInfo` 更新后会rebuild，此时会创建一个`RawImage` Widget。`RawImage`最终会通过`RenderImage`来将图片绘制在屏幕上。如果继续跟进`RenderImage`类，我们会发现`RenderImage`的`paint` 方法中调用了`paintImage`方法，而`paintImage`方法中通过`Canvas`的`drawImageRect(…)`、`drawImageNine(...)`等方法来完成最终的绘制。\n3. 最终的绘制由`RawImage`来完成。\n\n下面测试一下`MyImage`：\n\n```dart\nclass ImageInternalTestRoute extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return Column(\n      children: <Widget>[\n        MyImage(\n          imageProvider: NetworkImage(\n            \"https://avatars2.githubusercontent.com/u/20411648?s=460&v=4\",\n          ),\n        )\n      ],\n    );\n  }\n}\n```\n\n运行效果如图14-4所示：\n\n![图14-4](../imgs/14-4.png)\n\n成功了！ 现在，想必`Image` Widget的源码已经没必要在花费篇章去介绍了，读者有兴趣可以自行去阅读。\n\n\n\n## 总结\n\n本节主要介绍了Flutter 图片的加载、缓存和绘制流程。其中`ImageProvider`主要负责图片数据的加载和缓存，而绘制部分逻辑主要是由`RawImage`来完成。 而`Image`正是连接起`ImageProvider`和`RawImage` 的桥梁。\n\n\n\n\n\n\n\n"
  },
  {
    "path": "src/chapter14/index.md",
    "content": "# 本章目录\n* [Flutter UI系统](flutter_ui_system.md)\n* [Element和BuildContext](element_buildcontext.md)\n* [RenderObject和RenderBox](render_object.md)\n* [Flutter从启动到显示](flutter_app_startup.md)\n"
  },
  {
    "path": "src/chapter14/render_object.md",
    "content": "# 14.3 RenderObject和RenderBox\n\n在上一节我们说过每个`Element`都对应一个`RenderObject`，我们可以通过`Element.renderObject` 来获取。并且我们也说过`RenderObject`的主要职责是Layout和绘制，所有的`RenderObject`会组成一棵渲染树Render Tree。本节我们将重点介绍一下`RenderObject`的作用。\n\n`RenderObject`就是渲染树中的一个对象，它拥有一个`parent`和一个`parentData` 插槽（slot），所谓插槽，就是指预留的一个接口或位置，这个接口和位置是由其它对象来接入或占据的，这个接口或位置在软件中通常用预留变量来表示，而`parentData`正是一个预留变量，它正是由`parent` 来赋值的，`parent`通常会通过子`RenderObject`的`parentData`存储一些和子元素相关的数据，如在Stack布局中，`RenderStack`就会将子元素的偏移数据存储在子元素的`parentData`中（具体可以查看`Positioned`实现）。\n\n`RenderObject`类本身实现了一套基础的layout和绘制协议，但是并没有定义子节点模型（如一个节点可以有几个子节点，没有子节点？一个？两个？或者更多？）。 它也没有定义坐标系统（如子节点定位是在笛卡尔坐标中还是极坐标？）和具体的布局协议（是通过宽高还是通过constraint和size?，或者是否由父节点在子节点布局之前或之后设置子节点的大小和位置等）。为此，Flutter提供了一个`RenderBox`类，它继承自``RenderObject`，布局坐标系统采用笛卡尔坐标系，这和Android和iOS原生坐标系是一致的，都是屏幕的top、left是原点，然后分宽高两个轴，大多数情况下，我们直接使用`RenderBox`就可以了，除非遇到要自定义布局模型或坐标系统的情况，下面我们重点介绍一下`RenderBox`。\n\n## 14.3.1 布局过程\n\n### Constraints\n\n在`RenderBox` 中，有个`size`属性用来保存控件的宽和高。`RenderBox`的layout是通过在组件树中从上往下传递`BoxConstraints`对象的实现的。`BoxConstraints`对象可以限制子节点的最大和最小宽高，子节点必须遵守父节点给定的限制条件。\n\n在布局阶段，父节点会调用子节点的`layout()`方法，下面我们看看`RenderObject`中`layout()`方法的大致实现（删掉了一些无关代码和异常捕获）:\n\n```dart\nvoid layout(Constraints constraints, { bool parentUsesSize = false }) {\n   ...\n   RenderObject relayoutBoundary; \n    if (!parentUsesSize || sizedByParent || constraints.isTight \n    \t|| parent is! RenderObject) {\n      relayoutBoundary = this;\n    } else {\n      final RenderObject parent = this.parent;\n      relayoutBoundary = parent._relayoutBoundary;\n    }\n    ...\n    if (sizedByParent) {\n        performResize();\n    }\n    performLayout();\n    ...\n}\n```\n\n可以看到`layout`方法需要传入两个参数，第一个为`constraints`，即 父节点对子节点大小的限制，该值根据父节点的布局逻辑确定。另外一个参数是 `parentUsesSize`，该值用于确定 `relayoutBoundary`，该参数表示子节点布局变化是否影响父节点，如果为`true`，当子节点布局发生变化时父节点都会标记为需要重新布局，如果为`false`，则子节点布局发生变化后不会影响父节点。\n\n#### relayoutBoundary\n\n上面`layout()`源码中定义了一个`relayoutBoundary`变量，什么是 `relayoutBoundary`？在前面介绍`Element`时，我们讲过当一个`Element`标记为 dirty 时便会重新build，这时`RenderObject`便会重新布局，我们是通过调用 `markNeedsBuild()` 来标记`Element`为dirty的。在`RenderObject`中有一个类似的`markNeedsLayout()`方法，它会将`RenderObject`的布局状态标记为 dirty，这样在下一个frame中便会重新layout，我们看看`RenderObject`的`markNeedsLayout()`的部分源码：\n\n```dart\nvoid markNeedsLayout() {\n  ...\n  assert(_relayoutBoundary != null);\n  if (_relayoutBoundary != this) {\n    markParentNeedsLayout();\n  } else {\n    _needsLayout = true;\n    if (owner != null) {\n      ...\n      owner._nodesNeedingLayout.add(this);\n      owner.requestVisualUpdate();\n    }\n  }\n}\n```\n\n代码大致逻辑是先判断自身是不是`relayoutBoundary`，如果不是就继续向parent 查找，一直向上查找到是 `relayoutBoundary` 的 `RenderObject`为止，然后再将其标记为 dirty 的。这样来看它的作用就比较明显了，意思就是当一个控件的大小被改变时可能会影响到它的 parent，因此 parent 也需要被重新布局，那么到什么时候是个头呢？答案就是 `relayoutBoundary`，如果一个 `RenderObject` 是 `relayoutBoundary`，就表示它的大小变化不会再影响到 parent 的大小了，于是 parent 也就不用重新布局了。\n\n#### performResize 和 performLayout\n\n`RenderBox`实际的测量和布局逻辑是在`performResize()` 和 `performLayout()`两个方法中，RenderBox子类需要实现这两个方法来定制自身的布局逻辑。根据`layout()` 源码可以看出只有 `sizedByParent` 为 `true` 时，`performResize()` 才会被调用，而 `performLayout()` 是每次布局都会被调用的。`sizedByParent` 意为该节点的大小是否仅通过 parent 传给它的 constraints 就可以确定了，即该节点的大小与它自身的属性和其子节点无关，比如如果一个控件永远充满 parent 的大小，那么 `sizedByParent `就应该返回` true`，此时其大小在 `performResize()` 中就确定了，在后面的 `performLayout()` 方法中将不会再被修改了，这种情况下 `performLayout()` 只负责布局子节点。\n\n在 `performLayout()` 方法中除了完成自身布局，也必须完成子节点的布局，这是因为只有父子节点全部完成后布局流程才算真正完成。所以最终的调用栈将会变成：*layout() > performResize()/performLayout() > child.layout() > ...*  ，如此递归完成整个UI的布局。\n\n`RenderBox`子类要定制布局算法不应该重写`layout()`方法，因为对于任何RenderBox的子类来说，它的layout流程基本是相同的，不同之处只在具体的布局算法，而具体的布局算法子类应该通过重写`performResize()` 和 `performLayout()`两个方法来实现，他们会在`layout()`中被调用。\n\n#### ParentData\n\n当layout结束后，每个节点的位置（相对于父节点的偏移）就已经确定了，`RenderObject`就可以根据位置信息来进行最终的绘制。但是在layout过程中，节点的位置信息怎么保存？对于大多数`RenderBox`子类来说如果子类只有一个子节点，那么子节点偏移一般都是`Offset.zero` ，如果有多个子节点，则每个子节点的偏移就可能不同。而子节点在父节点的偏移数据正是通过`RenderObject`的`parentData`属性来保存的。在`RenderBox`中，其`parentData`属性默认是一个`BoxParentData`对象，该属性只能通过父节点的`setupParentData()`方法来设置：\n\n```dart\nabstract class RenderBox extends RenderObject {\n  @override\n  void setupParentData(covariant RenderObject child) {\n    if (child.parentData is! BoxParentData)\n      child.parentData = BoxParentData();\n  }\n  ...\n}\n```\n\n`BoxParentData`定义如下：\n\n```dart\n/// Parentdata 会被RenderBox和它的子类使用.\nclass BoxParentData extends ParentData {\n  /// offset表示在子节点在父节点坐标系中的绘制偏移  \n  Offset offset = Offset.zero;\n\n  @override\n  String toString() => 'offset=$offset';\n}\n```\n\n> 一定要注意，`RenderObject`的`parentData` 只能通过父元素设置.\n\n当然，`ParentData`并不仅仅可以用来存储偏移信息，通常所有和子节点特定的数据都可以存储到子节点的`ParentData`中，如`ContainerBox`的`ParentData`就保存了指向兄弟节点的`previousSibling`和`nextSibling`，`Element.visitChildren()`方法也正是通过它们来实现对子节点的遍历。再比如`KeepAlive` 组件，它使用`KeepAliveParentDataMixin`（继承自`ParentData`） 来保存子节的`keepAlive`状态。\n\n## 14.3.2 绘制过程\n\n`RenderObject`可以通过`paint()`方法来完成具体绘制逻辑，流程和布局流程相似，子类可以实现`paint()`方法来完成自身的绘制逻辑，`paint()`签名如下：\n\n```dart\nvoid paint(PaintingContext context, Offset offset) { }\n```\n\n通过`context.canvas`可以取到`Canvas`对象，接下来就可以调用`Canvas` API来实现具体的绘制逻辑。\n\n如果节点有子节点，它除了完成自身绘制逻辑之外，还要调用子节点的绘制方法。我们以`RenderFlex`对象为例说明：\n\n```dart\n@override\nvoid paint(PaintingContext context, Offset offset) {\n\n  // 如果子元素未超出当前边界，则绘制子元素  \n  if (_overflow <= 0.0) {\n    defaultPaint(context, offset);\n    return;\n  }\n\n  // 如果size为空，则无需绘制\n  if (size.isEmpty)\n    return;\n\n  // 剪裁掉溢出边界的部分\n  context.pushClipRect(needsCompositing, offset, Offset.zero & size, defaultPaint);\n\n  assert(() {\n    final String debugOverflowHints = '...'; //溢出提示内容，省略\n    // 绘制溢出部分的错误提示样式\n    Rect overflowChildRect;\n    switch (_direction) {\n      case Axis.horizontal:\n        overflowChildRect = Rect.fromLTWH(0.0, 0.0, size.width + _overflow, 0.0);\n        break;\n      case Axis.vertical:\n        overflowChildRect = Rect.fromLTWH(0.0, 0.0, 0.0, size.height + _overflow);\n        break;\n    }  \n    paintOverflowIndicator(context, offset, Offset.zero & size,\n                           overflowChildRect, overflowHints: debugOverflowHints);\n    return true;\n  }());\n}\n```\n\n代码很简单，首先判断有无溢出，如果没有则调用`defaultPaint(context, offset)`来完成绘制，该方法源码如下：\n\n```dart\nvoid defaultPaint(PaintingContext context, Offset offset) {\n  ChildType child = firstChild;\n  while (child != null) {\n    final ParentDataType childParentData = child.parentData;\n    //绘制子节点， \n    context.paintChild(child, childParentData.offset + offset);\n    child = childParentData.nextSibling;\n  }\n}\n```\n\n很明显，由于Flex本身没有需要绘制的东西，所以直接遍历其子节点，然后调用`paintChild()`来绘制子节点，同时将子节点`ParentData`中在layout阶段保存的offset加上自身偏移作为第二个参数传递给`paintChild()`。而如果子节点还有子节点时，`paintChild()`方法还会调用子节点的`paint()`方法，如此递归完成整个节点树的绘制，最终调用栈为： *paint() > paintChild() > paint() ...* 。\n\n当需要绘制的内容大小溢出当前空间时，将会执行`paintOverflowIndicator()` 来绘制溢出部分提示，这个就是我们经常看到的溢出提示，如图14-3所示：\n\n![overflow](../imgs/14-3.png)\n\n### RepaintBoundary\n\n我们已经在`CustomPaint`一节中介绍过`RepaintBoundary`，现在我们深入的了解一些。与 `RelayoutBoundary` 相似，`RepaintBoundary`是用于在确定重绘边界的，与`RelayoutBoundary`不同的是，这个绘制边界需要由开发者通过`RepaintBoundary` 组件自己指定，如：\n\n```dart\nCustomPaint(\n  size: Size(300, 300), //指定画布大小\n  painter: MyPainter(),\n  child: RepaintBoundary(\n    child: Container(...),\n  ),\n),\n```\n\n下面我们看看`RepaintBoundary`的原理，`RenderObject`有一个`isRepaintBoundary `属性，该属性决定这个`RenderObject`重绘时是否独立于其父元素，如果该属性值为`true` ，则独立绘制，反之则一起绘制。那独立绘制是怎么实现的呢？ 答案就在`paintChild()`源码中：\n\n```dart\nvoid paintChild(RenderObject child, Offset offset) {\n  ...\n  if (child.isRepaintBoundary) {\n    stopRecordingIfNeeded();\n    _compositeChild(child, offset);\n  } else {\n    child._paintWithContext(this, offset);\n  }\n  ...\n}\n```\n\n我们可以看到，在绘制子节点时，如果`child.isRepaintBoundary` 为 `true`则会调用`_compositeChild()`方法，`_compositeChild()`源码如下：\n\n```dart\nvoid _compositeChild(RenderObject child, Offset offset) {\n  // 给子节点创建一个layer ，然后再上面绘制子节点 \n  if (child._needsPaint) {\n    repaintCompositedChild(child, debugAlsoPaintedParent: true);\n  } else {\n    ...\n  }\n  assert(child._layer != null);\n  child._layer.offset = offset;\n  appendLayer(child._layer);\n}\n```\n\n很明显了，独立绘制是通过在不同的layer（层）上绘制的。所以，很明显，正确使用`isRepaintBoundary`属性可以提高绘制效率，避免不必要的重绘。具体原理是：和触发重新build和layout类似，`RenderObject`也提供了一个`markNeedsPaint()`方法，其源码如下：\n\n```dart\nvoid markNeedsPaint() {\n ...\n  //如果RenderObject.isRepaintBoundary 为true,则该RenderObject拥有layer，直接绘制  \n  if (isRepaintBoundary) {\n    ...\n    if (owner != null) {\n      //找到最近的layer，绘制  \n      owner._nodesNeedingPaint.add(this);\n      owner.requestVisualUpdate();\n    }\n  } else if (parent is RenderObject) {\n    // 没有自己的layer, 会和一个祖先节点共用一个layer  \n    assert(_layer == null);\n    final RenderObject parent = this.parent;\n    // 向父级递归查找  \n    parent.markNeedsPaint();\n    assert(parent == this.parent);\n  } else {\n    // 如果直到根节点也没找到一个Layer，那么便需要绘制自身，因为没有其它节点可以绘制根节点。  \n    if (owner != null)\n      owner.requestVisualUpdate();\n  }\n}\n```\n\n可以看出，当调用 `markNeedsPaint()` 方法时，会从当前 `RenderObject` 开始一直向父节点查找，直到找到 一个`isRepaintBoundary` 为 `true`的`RenderObject` 时，才会触发重绘，这样便可以实现局部重绘。当 有`RenderObject` 绘制的很频繁或很复杂时，可以通过RepaintBoundary Widget来指定`isRepaintBoundary` 为 `true`，这样在绘制时仅会重绘自身而无需重绘它的 parent，如此便可提高性能。\n\n还有一个问题，通过`RepaintBoundary` 如何设置`isRepaintBoundary`属性呢？其实，如果使用了`RepaintBoundary`，其对应的`RenderRepaintBoundary`会自动将`isRepaintBoundary`设为`true`的：\n\n```dart\nclass RenderRepaintBoundary extends RenderProxyBox {\n  /// Creates a repaint boundary around [child].\n  RenderRepaintBoundary({ RenderBox child }) : super(child);\n\n  @override\n  bool get isRepaintBoundary => true;\n}\n```\n\n\n\n## 14.3.3 命中测试\n\n我们在“事件处理与通知”一章中已经讲过Flutter事件机制和命中测试流程，本节我们看一下其内部实现原理。\n\n一个对象是否可以响应事件，取决于其对命中测试的返回，当发生用户事件时，会从根节点（`RenderView`）开始进行命中测试，下面是`RenderView`的`hitTest()`源码：\n\n```dart\nbool hitTest(HitTestResult result, { Offset position }) {\n  if (child != null)\n    child.hitTest(result, position: position); //递归子RenderBox进行命中测试\n  result.add(HitTestEntry(this)); //将测试结果添加到result中\n  return true;\n}\n```\n\n我们再看看`RenderBox`默认的`hitTest()`实现：\n\n```dart\nbool hitTest(HitTestResult result, { @required Offset position }) {\n  ...  \n  if (_size.contains(position)) {\n    if (hitTestChildren(result, position: position) || hitTestSelf(position)) {\n      result.add(BoxHitTestEntry(this, position));\n      return true;\n    }\n  }\n  return false;\n}\n```\n\n我们看到默认的实现里调用了`hitTestSelf()`和`hitTestChildren()`两个方法，这两个方法默认实现如下：\n\n```dart\n \n@protected\nbool hitTestSelf(Offset position) => false;\n \n@protected\nbool hitTestChildren(HitTestResult result, { Offset position }) => false;\n```\n\n`hitTest` 方法用来判断该` RenderObject` 是否在被点击的范围内，同时负责将被点击的 `RenderBox` 添加到 `HitTestResult` 列表中，参数 `position` 为事件触发的坐标（如果有的话），返回 true 则表示有` RenderBox` 通过了命中测试，需要响应事件，反之则认为当前`RenderBox`没有命中。在继承`RenderBox`时，可以直接重写`hitTest()`方法，也可以重写 `hitTestSelf()` 或 `hitTestChildren()`, 唯一不同的是 `hitTest()`中需要将通过命中测试的节点信息添加到命中测试结果列表中，而 `hitTestSelf()` 和 `hitTestChildren()`则只需要简单的返回`true`或`false`。\n\n## 14.3.4 语义化\n\n语义化即Semantics，主要是提供给读屏软件的接口，也是实现辅助功能的基础，通过语义化接口可以让机器理解页面上的内容，对于有视力障碍用户可以使用读屏软件来理解UI内容。如果一个`RenderObject`要支持语义化接口，可以实现 `describeApproximatePaintClip`和 `visitChildrenForSemantics`方法和`semanticsAnnotator` getter。更多关于语义化的信息可以查看API文档。\n\n## 14.3.5 总结\n\n本节我们介绍了`RenderObject`主要的功能和方法，理解这些内容可以帮助我们更好的理解Flutter UI底层原理。我们也可以看到，如果要从头到尾实现一个`RenderObject`是比较麻烦的，我们必须去实现layout、绘制和命中测试逻辑，但是值得庆幸的是，大多数时候我们可以直接在Widget层通过组合或者`CustomPaint`完成自定义UI。如果遇到只能定义一个新`RenderObject`的场景时（如要实现一个新的layout算法的布局容器），可以直接继承自`RenderBox`，这样可以帮我们减少一部分工作。\n\n"
  },
  {
    "path": "src/chapter15/code_structure.md",
    "content": "# 15.2 Flutter APP代码结构\n\n我们先来创建一个全新的Flutter工程，命名为\"github_client_app\"；创建新工程的步骤视读者使用的编辑器而定，都比较简单，在此不再赘述。创建完成后，工程结构如下：\n\n```shell\ngithub_client_app\n├── android\n├── ios\n├── lib\n└── test\n```\n\n由于我们需要使用外部图片和Icon资源，所以我们在项目根目录下分别创建“imgs”和“fonts”文件夹，前者用于保存图片，后者用于保存Icon文件。关于图片和Icon，读者可以参考第三章中相应的内容。\n\n由于在网络数据传输和持久化时，我们需要通过Json来传输、保存数据；但是在应用开发时我们又需要将Json转成Dart Model类，现在我们使用在第十一章中“Json转Model”小节中介绍的方案，所以，我们需要在根目录下再创建一个用于保存Json文件的“jsons”文件夹。\n\n多语言支持我们使用第十三章“国际化”中介绍的方案，所以还需要在根目录下创建一个“l10n”文件夹，用于保存各国语言对应的arb文件。\n\n现在工程目录变为：\n\n```shell\ngithub_client_app\n├── android\n├── fonts\n├── l10n-arb\n├── imgs\n├── ios\n├── jsons\n├── lib\n└── test\n```\n\n由于我们的Dart代码都在“lib”文件夹下，笔者根据技术选型和经验在lib文件下创建了如下目录：\n\n```shell\nlib\n├── common\n├── l10n\n├── models\n├── states\n├── routes\n└── widgets \n```\n\n| 文件夹  | 作用                                                         |\n| ------- | ------------------------------------------------------------ |\n| common  | 一些工具类，如通用方法类、网络接口类、保存全局变量的静态类等 |\n| l10n    | 国际化相关的类都在此目录下                                   |\n| models  | Json文件对应的Dart Model类会在此目录下                       |\n| states  | 保存APP中需要跨组件共享的状态类                              |\n| routes  | 存放所有路由页面类                                           |\n| widgets | APP内封装的一些Widget组件都在该目录下                        |\n\n注意，使用不同的框架或技术选型会对代码有不同的组织方式，因此，本节介绍的代码组织结构并不是固定或者“最佳”的，在实战中，读者可以自己根据情况调整源码结构。但是无论采取何种源码组织结构，清晰和解耦都是一个通用原则，我们应该让自己的代码结构清晰，以便交流和维护。\n\n\n\n\n\n\n\n"
  },
  {
    "path": "src/chapter15/entry.md",
    "content": "# 15.6 APP入口及主页\n\n本节来介绍一下APP入口及首页。\n\n## 15.6.1 APP入口\n\n`main`函数为APP入口函数，实现如下：\n\n```dart\nvoid main() => Global.init().then((e) => runApp(MyApp()));\n```\n\n初始化完成后才会加载UI(`MyApp`)，`MyApp` 是应用的入口Widget，实现如下：\n\n```dart\nclass MyApp extends StatelessWidget {\n  // This widget is the root of your application.\n  @override\n  Widget build(BuildContext context) {\n    return MultiProvider(\n      providers: <SingleChildCloneableWidget>[\n        ChangeNotifierProvider.value(value: ThemeModel()),\n        ChangeNotifierProvider.value(value: UserModel()),\n        ChangeNotifierProvider.value(value: LocaleModel()),\n      ],\n      child: Consumer2<ThemeModel, LocaleModel>(\n        builder: (BuildContext context, themeModel, localeModel, Widget child) {\n          return MaterialApp(\n            theme: ThemeData(\n              primarySwatch: themeModel.theme,\n            ),\n            onGenerateTitle: (context){\n              return GmLocalizations.of(context).title;\n            },\n            home: HomeRoute(), //应用主页\n            locale: localeModel.getLocale(),\n            //我们只支持美国英语和中文简体\n            supportedLocales: [\n              const Locale('en', 'US'), // 美国英语\n              const Locale('zh', 'CN'), // 中文简体\n              //其它Locales\n            ],\n            localizationsDelegates: [\n              // 本地化的代理类\n              GlobalMaterialLocalizations.delegate,\n              GlobalWidgetsLocalizations.delegate,\n              GmLocalizationsDelegate()\n            ],\n            localeResolutionCallback:\n                (Locale _locale, Iterable<Locale> supportedLocales) {\n              if (localeModel.getLocale() != null) {\n                //如果已经选定语言，则不跟随系统\n                return localeModel.getLocale();\n              } else {\n         \n                Locale locale;\n                //APP语言跟随系统语言，如果系统语言不是中文简体或美国英语，\n                //则默认使用美国英语\n                if (supportedLocales.contains(_locale)) {\n                  locale= _locale;\n                } else {\n                  locale= Locale('en', 'US');\n                }\n                return locale;\n              }\n            },\n            // 注册命名路由表\n            routes: <String, WidgetBuilder>{\n              \"login\": (context) => LoginRoute(),\n              \"themes\": (context) => ThemeChangeRoute(),\n              \"language\": (context) => LanguageRoute(),\n            },\n          );\n        },\n      ),\n    );\n  }\n}\n```\n\n在上面的代码中：\n\n1. 我们的根widget是`MultiProvider`，它将主题、用户、语言三种状态绑定到了应用的根上，如此一来，任何路由中都可以通过`Provider.of()`来获取这些状态，也就是说这三种状态是全局共享的！\n2. `HomeRoute`是应用的主页。\n3. 在构建`MaterialApp`时，我们配置了APP支持的语言列表，以及监听了系统语言改变事件；另外`MaterialApp`消费（依赖）了`ThemeModel`和`LocaleModel`，所以当APP主题或语言改变时`MaterialApp`会重新构建\n4. 我们注册了命名路由表，以便在APP中可以直接通过路由名跳转。\n5. 为了支持多语言（本APP中我们支持美国英语和中文简体两种语言）我们实现了一个`GmLocalizationsDelegate`，子Widget中都可以通过`GmLocalizations`来动态获取APP当前语言对应的文案。关于`GmLocalizationsDelegate`和`GmLocalizations`的实现方式读者可以参考“国际化”一章中的介绍，此处不再赘述。\n\n## 15.6.2 主页\n\n为了简单起见，当APP启动后，如果之前已登录了APP，则显示该用户项目列表；如果之前未登录，则显示一个登录按钮，点击后跳转到登录页。另外，我们实现一个抽屉菜单，里面包含当前用户头像及APP的菜单。下面我们先看看要实现的效果，如图15-1、15-2所示：\n\n![15-1](../imgs/15-1.png)![15-2](../imgs/15-2.png)\n\n我们在“lib/routes”下创建一个“home_page.dart”文件，实现如下：\n\n```dart\nclass HomeRoute extends StatefulWidget {\n  @override\n  _HomeRouteState createState() => _HomeRouteState();\n}\n\nclass _HomeRouteState extends State<HomeRoute> {\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        title: Text(GmLocalizations.of(context).home),\n      ),\n      body: _buildBody(), // 构建主页面\n      drawer: MyDrawer(), //抽屉菜单\n    );\n  }\n  ...// 省略\n}\n```\n\n上面代码中，主页的标题（title）我们是通过`GmLocalizations.of(context).home`来获得，`GmLocalizations`是我们提供的一个`Localizations`类，用于支持多语言，因此当APP语言改变时，凡是使用`GmLocalizations`动态获取的文案都会是相应语言的文案，这在前面“国际化”一章中已经介绍过，读者可以前翻查阅。\n\n我们通过 `_buildBody()`方法来构建主页内容，`_buildBody()`方法实现代码如下：\n\n```dart\n  Widget _buildBody() {\n    UserModel userModel = Provider.of<UserModel>(context);\n    if (!userModel.isLogin) {\n      //用户未登录，显示登录按钮\n      return Center(\n        child: RaisedButton(\n          child: Text(GmLocalizations.of(context).login),\n          onPressed: () => Navigator.of(context).pushNamed(\"login\"),\n        ),\n      );\n    } else {\n      //已登录，则展示项目列表\n      return InfiniteListView<Repo>(\n        onRetrieveData: (int page, List<Repo> items, bool refresh) async {\n          var data = await Git(context).getRepos(\n            refresh: refresh,\n            queryParameters: {\n              'page': page,\n              'page_size': 20,\n            },\n          );\n          //把请求到的新数据添加到items中\n          items.addAll(data); \n          // 如果接口返回的数量等于'page_size'，则认为还有数据，反之则认为最后一页\n          return data.length==20;\n        },\n        itemBuilder: (List list, int index, BuildContext ctx) {\n          // 项目信息列表项\n          return RepoItem(list[index]);\n        },\n      );\n    }\n  }\n}\n```\n\n上面代码注释很清楚：如果用户未登录，显示登录按钮；如果用户已登录，则展示项目列表。这里项目列表使用了`InfiniteListView` Widget，它是flukit package中提供的。`InfiniteListView`同时支持了下拉刷新和上拉加载更多两种功能。`onRetrieveData` 为数据获取回调，该回调函数接收三个参数：\n\n| 参数名  | 类型    | 解释                   |\n| ------- | ------- | ---------------------- |\n| page    | int     | 当前页号               |\n| items   | List<T> | 保存当前列表数据的List |\n| refresh | bool    | 是否是下拉刷新触发     |\n\n返回值类型为`bool`，为`true`时表示还有数据，为`false`时则表示后续没有数据了。`onRetrieveData` 回调中我们调用`Git(context).getRepos(...)`来获取用户项目列表，同时指定每次请求获取20条。当获取成功时，首先要将新获取的项目数据添加到`items`中，然后根据本次请求的项目条数是否等于期望的20条来判断还有没有更多的数据。在此需要注意，`Git(context).getRepos(…)`方法中需要`refresh`参数来判断是否使用缓存。\n\n`itemBuilder`为列表项的builder，我们需要在该回调中构建每一个列表项Widget。由于列表项构建逻辑较复杂，我们单独封装一个`RepoItem` Widget 专门用于构建列表项UI。`RepoItem` 实现如下：\n\n```dart\nimport '../index.dart';\n\nclass RepoItem extends StatefulWidget {\n  // 将`repo.id`作为RepoItem的默认key\n  RepoItem(this.repo) : super(key: ValueKey(repo.id));\n\n  final Repo repo;\n\n  @override\n  _RepoItemState createState() => _RepoItemState();\n}\n\nclass _RepoItemState extends State<RepoItem> {\n  @override\n  Widget build(BuildContext context) {\n    var subtitle;\n    return Padding(\n      padding: const EdgeInsets.only(top: 8.0),\n      child: Material(\n        color: Colors.white,\n        shape: BorderDirectional(\n          bottom: BorderSide(\n            color: Theme.of(context).dividerColor,\n            width: .5,\n          ),\n        ),\n        child: Padding(\n          padding: const EdgeInsets.only(top: 0.0, bottom: 16),\n          child: Column(\n            crossAxisAlignment: CrossAxisAlignment.start,\n            children: <Widget>[\n              ListTile(\n                dense: true,\n                leading: gmAvatar(\n                  //项目owner头像\n                  widget.repo.owner.avatar_url,\n                  width: 24.0,\n                  borderRadius: BorderRadius.circular(12),\n                ),\n                title: Text(\n                  widget.repo.owner.login,\n                  textScaleFactor: .9,\n                ),\n                subtitle: subtitle,\n                trailing: Text(widget.repo.language ?? \"\"),\n              ),\n              // 构建项目标题和简介\n              Padding(\n                padding: const EdgeInsets.symmetric(horizontal: 16.0),\n                child: Column(\n                  crossAxisAlignment: CrossAxisAlignment.start,\n                  children: <Widget>[\n                    Text(\n                      widget.repo.fork\n                          ? widget.repo.full_name\n                          : widget.repo.name,\n                      style: TextStyle(\n                        fontSize: 15,\n                        fontWeight: FontWeight.bold,\n                        fontStyle: widget.repo.fork\n                            ? FontStyle.italic\n                            : FontStyle.normal,\n                      ),\n                    ),\n                    Padding(\n                      padding: const EdgeInsets.only(top: 8, bottom: 12),\n                      child: widget.repo.description == null\n                          ? Text(\n                              GmLocalizations.of(context).noDescription,\n                              style: TextStyle(\n                                  fontStyle: FontStyle.italic,\n                                  color: Colors.grey[700]),\n                            )\n                          : Text(\n                              widget.repo.description,\n                              maxLines: 3,\n                              style: TextStyle(\n                                height: 1.15,\n                                color: Colors.blueGrey[700],\n                                fontSize: 13,\n                              ),\n                            ),\n                    ),\n                  ],\n                ),\n              ),\n              // 构建卡片底部信息\n              _buildBottom()\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n\n  // 构建卡片底部信息\n  Widget _buildBottom() {\n    const paddingWidth = 10;\n    return IconTheme(\n      data: IconThemeData(\n        color: Colors.grey,\n        size: 15,\n      ),\n      child: DefaultTextStyle(\n        style: TextStyle(color: Colors.grey, fontSize: 12),\n        child: Padding(\n          padding: const EdgeInsets.symmetric(horizontal: 16),\n          child: Builder(builder: (context) {\n            var children = <Widget>[\n              Icon(Icons.star),\n              Text(\" \" +\n                  widget.repo.stargazers_count\n                      .toString()\n                      .padRight(paddingWidth)),\n              Icon(Icons.info_outline),\n              Text(\" \" +\n                  widget.repo.open_issues_count\n                      .toString()\n                      .padRight(paddingWidth)),\n\n              Icon(MyIcons.fork), //我们的自定义图标\n              Text(widget.repo.forks_count.toString().padRight(paddingWidth)),\n            ];\n\n            if (widget.repo.fork) {\n              children.add(Text(\"Forked\".padRight(paddingWidth)));\n            }\n\n            if (widget.repo.private == true) {\n              children.addAll(<Widget>[\n                Icon(Icons.lock),\n                Text(\" private\".padRight(paddingWidth))\n              ]);\n            }\n            return Row(children: children);\n          }),\n        ),\n      ),\n    );\n  }\n}\n```\n\n上面代码有两点需要注意：\n\n1. 在构建项目拥有者头像时调用了`gmAvatar(…)`方法，该方法是是一个全局工具函数，专门用于获取头像图片，实现如下：\n\n   ```dart\n   Widget gmAvatar(String url, {\n     double width = 30,\n     double height,\n     BoxFit fit,\n     BorderRadius borderRadius,\n   }) {\n     var placeholder = Image.asset(\n         \"imgs/avatar-default.png\", //头像占位图，加载过程中显示\n         width: width,\n         height: height\n     );\n     return ClipRRect(\n       borderRadius: borderRadius ?? BorderRadius.circular(2),\n       child: CachedNetworkImage( \n         imageUrl: url,\n         width: width,\n         height: height,\n         fit: fit,\n         placeholder: (context, url) =>placeholder,\n         errorWidget: (context, url, error) =>placeholder,\n       ),\n     );\n   }\n   ```\n\n   代码中调用了`CachedNetworkImage` 是cached_network_image包中提供的一个Widget，它不仅可以在图片加载过程中指定一个占位图，而且还可以对网络请求的图片进行缓存，更多详情读者可以自行查阅其文档。\n\n2. 由于Flutter 的Material 图标库中没有fork图标，所以我们在iconfont.cn上找了一个fork图标，然后根据“图片和Icon”一节中介绍的使用自定义字体图标的方法集成到了我们的项目中。\n\n## 15.6.3 抽屉菜单\n\n抽屉菜单分为两部分：顶部头像和底部功能菜单项。当用户未登录，则抽屉菜单顶部会显示一个默认的灰色占位图，若用户已登录，则会显示用户的头像。抽屉菜单底部有“换肤”和“语言”两个固定菜单，若用户已登录，则会多一个“注销”菜单。用户点击“换肤”和“语言”两个菜单项，会进入相应的设置页面。我们的抽屉菜单效果如图15-3、15-4所示：\n\n![15-3](../imgs/15-3.png)![15-4](../imgs/15-4.png)\n\n实现代码如下：\n\n```dart\nclass MyDrawer extends StatelessWidget {\n  const MyDrawer({\n    Key key,\n  }) : super(key: key);\n\n  @override\n  Widget build(BuildContext context) {\n    return Drawer(\n      //移除顶部padding\n      child: MediaQuery.removePadding(\n        context: context,\n        removeTop: true,\n        child: Column(\n          crossAxisAlignment: CrossAxisAlignment.start,\n          children: <Widget>[\n            _buildHeader(), //构建抽屉菜单头部\n            Expanded(child: _buildMenus()), //构建功能菜单\n          ],\n        ),\n      ),\n    );\n  }\n\n  Widget _buildHeader() {\n    return Consumer<UserModel>(\n      builder: (BuildContext context, UserModel value, Widget child) {\n        return GestureDetector(\n          child: Container(\n            color: Theme.of(context).primaryColor,\n            padding: EdgeInsets.only(top: 40, bottom: 20),\n            child: Row(\n              children: <Widget>[\n                Padding(\n                  padding: const EdgeInsets.symmetric(horizontal: 16.0),\n                  child: ClipOval(\n                    // 如果已登录，则显示用户头像；若未登录，则显示默认头像\n                    child: value.isLogin\n                        ? gmAvatar(value.user.avatar_url, width: 80)\n                        : Image.asset(\n                            \"imgs/avatar-default.png\",\n                            width: 80,\n                          ),\n                  ),\n                ),\n                Text(\n                  value.isLogin\n                      ? value.user.login\n                      : GmLocalizations.of(context).login,\n                  style: TextStyle(\n                    fontWeight: FontWeight.bold,\n                    color: Colors.white,\n                  ),\n                )\n              ],\n            ),\n          ),\n          onTap: () {\n            if (!value.isLogin) Navigator.of(context).pushNamed(\"login\");\n          },\n        );\n      },\n    );\n  }\n\n  // 构建菜单项\n  Widget _buildMenus() {\n    return Consumer<UserModel>(\n      builder: (BuildContext context, UserModel userModel, Widget child) {\n        var gm = GmLocalizations.of(context);\n        return ListView(\n          children: <Widget>[\n            ListTile(\n              leading: const Icon(Icons.color_lens),\n              title: Text(gm.theme),\n              onTap: () => Navigator.pushNamed(context, \"themes\"),\n            ),\n            ListTile(\n              leading: const Icon(Icons.language),\n              title: Text(gm.language),\n              onTap: () => Navigator.pushNamed(context, \"language\"),\n            ),\n            if(userModel.isLogin) ListTile(\n              leading: const Icon(Icons.power_settings_new),\n              title: Text(gm.logout),\n              onTap: () {\n                showDialog(\n                  context: context,\n                  builder: (ctx) {\n                    //退出账号前先弹二次确认窗\n                    return AlertDialog(\n                      content: Text(gm.logoutTip),\n                      actions: <Widget>[\n                        FlatButton(\n                          child: Text(gm.cancel),\n                          onPressed: () => Navigator.pop(context),\n                        ),\n                        FlatButton(\n                          child: Text(gm.yes),\n                          onPressed: () {\n                            //该赋值语句会触发MaterialApp rebuild\n                            userModel.user = null;\n                            Navigator.pop(context);\n                          },\n                        ),\n                      ],\n                    );\n                  },\n                );\n              },\n            ),\n          ],\n        );\n      },\n    );\n  }\n}\n```\n\n用户点击“注销”，`userModel.user` 会被置空，此时所有依赖`userModel`的组件都会被`rebuild`，如主页会恢复成未登录的状态。\n\n本小节我们介绍了APP入口`MaterialApp`的一些配置，然后实现了APP的首页。后面我们将展示登录页、换肤页、语言切换页。\n"
  },
  {
    "path": "src/chapter15/globals.md",
    "content": "# 15.4 全局变量及共享状态\n\n应用程序中通常会包含一些贯穿APP生命周期的变量信息，这些信息在APP大多数地方可能都会被用到，比如当前用户信息、Local信息等。在Flutter中我们把需要全局共享的信息分为两类：全局变量和共享状态。全局变量就是单纯指会贯穿整个APP生命周期的变量，用于单纯的保存一些信息，或者封装一些全局工具和方法的对象。而共享状态则是指哪些需要跨组件或跨路由共享的信息，这些信息通常也是全局变量，而共享状态和全局变量的不同在于前者发生改变时需要通知所有使用该状态的组件，而后者不需要。为此，我们将全局变量和共享状态分开单独管理。\n\n## 15.4.1 全局变量-Global类\n\n我们在“lib/common”目录下创建一个`Global`类，它主要管理APP的全局变量，定义如下：\n\n```dart\n// 提供五套可选主题色\nconst _themes = <MaterialColor>[\n  Colors.blue,\n  Colors.cyan,\n  Colors.teal,\n  Colors.green,\n  Colors.red,\n];\n\nclass Global {\n  static SharedPreferences _prefs;\n  static Profile profile = Profile();\n  // 网络缓存对象\n  static NetCache netCache = NetCache();\n\n  // 可选的主题列表\n  static List<MaterialColor> get themes => _themes;\n\n  // 是否为release版\n  static bool get isRelease => bool.fromEnvironment(\"dart.vm.product\");\n\n  //初始化全局信息，会在APP启动时执行\n  static Future init() async {\n    _prefs = await SharedPreferences.getInstance();\n    var _profile = _prefs.getString(\"profile\");\n    if (_profile != null) {\n      try {\n        profile = Profile.fromJson(jsonDecode(_profile));\n      } catch (e) {\n        print(e);\n      }\n    }\n\n    // 如果没有缓存策略，设置默认缓存策略\n    profile.cache = profile.cache ?? CacheConfig()\n      ..enable = true\n      ..maxAge = 3600\n      ..maxCount = 100;\n\n    //初始化网络请求相关配置\n    Git.init();\n  }\n\n  // 持久化Profile信息\n  static saveProfile() =>\n      _prefs.setString(\"profile\", jsonEncode(profile.toJson()));\n}\n```\n\nGlobal类的各个字段的意义都有注释，在此不再赘述，需要注意的是`init()`需要在App启动时就要执行，所以应用的`main`方法如下：\n\n```dart\nvoid main() => Global.init().then((e) => runApp(MyApp()));\n```\n\n在此，一定要确保`Global.init()`方法不能抛出异常，否则 `runApp(MyApp())`根本执行不到。\n\n## 15.4.2 共享状态\n\n有了全局变量，我们还需要考虑如何跨组件共享状态。当然，如果我们将要共享的状态全部用全局变量替代也是可以的，但是这在Flutter开发中并不是一个好主意，因为组件的状态是和UI相关，而在状态改变时我们会期望依赖该状态的UI组件会自动更新，如果使用全局变量，那么我们必须得去手动处理状态变动通知、接收机制以及变量和组件依赖关系。因此，本实例中，我们使用前面介绍过的Provider包来实现跨组件状态共享，因此我们需要定义相关的Provider。在本实例中，需要共享的状态有登录用户信息、APP主题信息、APP语言信息。由于这些信息改变后都要立即通知其它依赖的该信息的Widget更新，所以我们应该使用`ChangeNotifierProvider`，另外，这些信息改变后都是需要更新Profile信息并进行持久化的。综上所述，我们可以定义一个`ProfileChangeNotifier`基类，然后让需要共享的Model继承自该类即可，`ProfileChangeNotifier`定义如下：\n\n```dart\nclass ProfileChangeNotifier extends ChangeNotifier {\n  Profile get _profile => Global.profile;\n\n  @override\n  void notifyListeners() {\n    Global.saveProfile(); //保存Profile变更\n    super.notifyListeners(); //通知依赖的Widget更新\n  }\n}\n```\n\n### 用户状态\n\n用户状态在登录状态发生变化时更新、通知其依赖项，我们定义如下：\n\n```dart\nclass UserModel extends ProfileChangeNotifier {\n  User get user => _profile.user;\n\n  // APP是否登录(如果有用户信息，则证明登录过)\n  bool get isLogin => user != null;\n\n  //用户信息发生变化，更新用户信息并通知依赖它的子孙Widgets更新\n  set user(User user) {\n    if (user?.login != _profile.user?.login) {\n      _profile.lastLogin = _profile.user?.login;\n      _profile.user = user;\n      notifyListeners();\n    }\n  }\n}\n```\n\n### APP主题状态\n\n主题状态在用户更换APP主题时更新、通知其依赖项，定义如下：\n\n```dart\nclass ThemeModel extends ProfileChangeNotifier {\n  // 获取当前主题，如果为设置主题，则默认使用蓝色主题\n  ColorSwatch get theme => Global.themes\n      .firstWhere((e) => e.value == _profile.theme, orElse: () => Colors.blue);\n\n  // 主题改变后，通知其依赖项，新主题会立即生效\n  set theme(ColorSwatch color) {\n    if (color != theme) {\n      _profile.theme = color[500].value;\n      notifyListeners();\n    }\n  }\n}\n```\n\n### APP语言状态\n\n当APP语言选为跟随系统（Auto）时，在系通语言改变时，APP语言会更新；当用户在APP中选定了具体语言时（美国英语或中文简体），则APP便会一直使用用户选定的语言，不会再随系统语言而变。语言状态类定义如下：\n\n```dart\nclass LocaleModel extends ProfileChangeNotifier {\n  // 获取当前用户的APP语言配置Locale类，如果为null，则语言跟随系统语言\n  Locale getLocale() {\n    if (_profile.locale == null) return null;\n    var t = _profile.locale.split(\"_\");\n    return Locale(t[0], t[1]);\n  }\n\n  // 获取当前Locale的字符串表示\n  String get locale => _profile.locale;\n\n  // 用户改变APP语言后，通知依赖项更新，新语言会立即生效\n  set locale(String locale) {\n    if (locale != _profile.locale) {\n      _profile.locale = locale;\n      notifyListeners();\n    }\n  }\n}\n```\n\n"
  },
  {
    "path": "src/chapter15/intro.md",
    "content": "# 15.1 Github客户端示例\n\n本章新建一个Flutter工程，实现一个简单的Github客户端。这个实例的主要目标有两个：\n\n1. 带领读者了解如何使用Flutter来开发一个完整APP，了解Flutter应用开发流程及工程结构等。\n2. 对前面章节所学内容的一个应用及总结。\n\n需要注意的是，由于Github本身功能非常多，我们的焦点并不是去实现Github的所有业务功能。因此，我们只需要实现一个APP的骨架，能达到上面这两点即可。下面对我们要实现的功能如下：\n\n1. 实现Github账号登录、退出登录功能\n2. 登录后可以查看自己的项目主页\n3. 支持换肤\n4. 支持多语言\n5. 登录状态可以持久化；\n\n要实现上面这些功能会涉及到如下技术点：\n\n1. 网络请求；需要请求Github API。\n2. Json转Dart Model类；\n3. 全局状态管理；语言、主题、登录态等都需要全局共享。\n4. 持久化存储；保存登录信息，用户信息等。\n5. 支持国际化、Intl包的使用\n\n现在，目标已经确定，在接下来章节中，我们将分模块一步一步实现上述功能。\n\n"
  },
  {
    "path": "src/chapter15/language_and_theme_setting.md",
    "content": "# 15.8 多语言和多主题\n\n本实例APP中语言和主题都是可以设置的，而两者都是通过`ChangeNotifierProvider`来实现的：我们在`main`函数中使用了`Consumer2`，依赖了`ThemeModel`和`LocaleModel`，因此，当我们在语言和主题设置页更该当前的配置后，`Consumer2`的`builder`都会重新执行，构建一个新的`MaterialApp`，所以修改会立即生效。下面看一下语言和主题设置页的实现。\n\n## 15.8.1 语言选择页\n\nAPP语言选择页提供三个选项：中文简体、美国英语、跟随系统。我们将当前APP使用的语言高亮显示，并且在后面添加一个“对号”图标，实现如下：\n\n```dart\nclass LanguageRoute extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    var color = Theme.of(context).primaryColor;\n    var localeModel = Provider.of<LocaleModel>(context);\n    var gm = GmLocalizations.of(context);\n    //构建语言选择项\n    Widget _buildLanguageItem(String lan, value) {\n      return ListTile(\n        title: Text(\n          lan,\n          // 对APP当前语言进行高亮显示\n          style: TextStyle(color: localeModel.locale == value ? color : null),\n        ),\n        trailing:\n            localeModel.locale == value ? Icon(Icons.done, color: color) : null,\n        onTap: () {\n          // 更新locale后MaterialApp会重新build\n          localeModel.locale = value;\n        },\n      );\n    }\n\n    return Scaffold(\n      appBar: AppBar(\n        title: Text(gm.language),\n      ),\n      body: ListView(\n        children: <Widget>[\n          _buildLanguageItem(\"中文简体\", \"zh_CN\"),\n          _buildLanguageItem(\"English\", \"en_US\"),\n          _buildLanguageItem(gm.auto, null),\n        ],\n      ),\n    );\n  }\n}\n```\n\n上面代码逻辑很简单，唯一需要注意的是我们在`build(…)`方法里面定义了`_buildLanguageItem(…)`方法，它和在`LanguageRoute`类中定义该方法的区别就在于：在`build(…)`内定义的方法可以共享`build(...)`方法上下文中的变量，本例中是共享了`localeModel`。当然，如果`_buildLanguageItem(…)`的实现复杂一些的话不建议这样做，此时最好是将其作为`LanguageRoute`类的方法。该页面运行效果如图15-6、15-7所示：\n\n![15-6](../imgs/15-6.png)![15-7](../imgs/15-7.png)\n\n切换语言后立即生效。\n\n## 15.8.2 主题选择页\n\n一个完整的主题`Theme`包括很多选项，这些选项在`ThemeData`中定义。本实例为了简单起见，我们只配置主题颜色。我们提供几种默认预定义的主题色供用户选择，用户点击一种色块后则更新主题。主题选择页的实现代码如下：\n\n```dart\nclass ThemeChangeRoute extends StatelessWidget{\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        title: Text(GmLocalizations.of(context).theme),\n      ),\n      body: ListView( //显示主题色块\n        children: Global.themes.map<Widget>((e) {\n          return GestureDetector(\n            child: Padding(\n              padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 16),\n              child: Container(\n                color: e,\n                height: 40,\n              ),\n            ),\n            onTap: () {\n              //主题更新后，MaterialApp会重新build\n              Provider.of<ThemeModel>(context).theme = e;\n            },\n          );\n        }).toList(),\n      ),\n    );\n  }\n}\n```\n\n运行效果如图15-8所示：\n\n![15-8](../imgs/15-8.png)\n\n点击其它主题色块后，APP主题色立马切换生效。\n"
  },
  {
    "path": "src/chapter15/login_page.md",
    "content": "# 15.7 登录页\n\n我们说过Github有多种登录方式，为了简单起见，我们只实现通过用户名和密码登录。在实现登录页时有四点需要注意：\n\n1. 可以自动填充上次登录的用户名（如果有）。\n2. 为了防止密码输入错误，密码框应该有开关可以看明文。\n3. 用户名或密码字段在调用登录接口前有本地合法性校验（比如不能为空）。\n4. 登录成功后需更新用户信息。\n\n实现代码如下：\n\n```dart\nimport '../index.dart';\n\nclass LoginRoute extends StatefulWidget {\n  @override\n  _LoginRouteState createState() => _LoginRouteState();\n}\n\nclass _LoginRouteState extends State<LoginRoute> {\n  TextEditingController _unameController = new TextEditingController();\n  TextEditingController _pwdController = new TextEditingController();\n  bool pwdShow = false; //密码是否显示明文\n  GlobalKey _formKey = new GlobalKey<FormState>();\n  bool _nameAutoFocus = true;\n\n  @override\n  void initState() {\n    // 自动填充上次登录的用户名，填充后将焦点定位到密码输入框\n    _unameController.text = Global.profile.lastLogin;\n    if (_unameController.text != null) {\n      _nameAutoFocus = false;\n    }\n    super.initState();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    var gm = GmLocalizations.of(context);\n    return Scaffold(\n      appBar: AppBar(title: Text(gm.login)),\n      body: Padding(\n        padding: const EdgeInsets.all(16.0),\n        child: Form(\n          key: _formKey,\n          autovalidate: true,\n          child: Column(\n            children: <Widget>[\n              TextFormField(\n                  autofocus: _nameAutoFocus,\n                  controller: _unameController,\n                  decoration: InputDecoration(\n                    labelText: gm.userName,\n                    hintText: gm.userNameOrEmail,\n                    prefixIcon: Icon(Icons.person),\n                  ),\n                  // 校验用户名（不能为空）\n                  validator: (v) {\n                    return v.trim().isNotEmpty ? null : gm.userNameRequired;\n                  }),\n              TextFormField(\n                controller: _pwdController,\n                autofocus: !_nameAutoFocus,\n                decoration: InputDecoration(\n                    labelText: gm.password,\n                    hintText: gm.password,\n                    prefixIcon: Icon(Icons.lock),\n                    suffixIcon: IconButton(\n                      icon: Icon(\n                          pwdShow ? Icons.visibility_off : Icons.visibility),\n                      onPressed: () {\n                        setState(() {\n                          pwdShow = !pwdShow;\n                        });\n                      },\n                    )),\n                obscureText: !pwdShow,\n                //校验密码（不能为空）\n                validator: (v) {\n                  return v.trim().isNotEmpty ? null : gm.passwordRequired;\n                },\n              ),\n              Padding(\n                padding: const EdgeInsets.only(top: 25),\n                child: ConstrainedBox(\n                  constraints: BoxConstraints.expand(height: 55.0),\n                  child: RaisedButton(\n                    color: Theme.of(context).primaryColor,\n                    onPressed: _onLogin,\n                    textColor: Colors.white,\n                    child: Text(gm.login),\n                  ),\n                ),\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n\n  void _onLogin() async {\n    // 提交前，先验证各个表单字段是否合法\n    if ((_formKey.currentState as FormState).validate()) {\n      showLoading(context);\n      User user;\n      try {\n        user = await Git(context).login(_unameController.text, _pwdController.text);\n        // 因为登录页返回后，首页会build，所以我们传false，更新user后不触发更新\n        Provider.of<UserModel>(context, listen: false).user = user;\n      } catch (e) {\n        //登录失败则提示\n        if (e.response?.statusCode == 401) {\n          showToast(GmLocalizations.of(context).userNameOrPasswordWrong);\n        } else {\n          showToast(e.toString());\n        }\n      } finally {\n        // 隐藏loading框\n        Navigator.of(context).pop();\n      }\n      if (user != null) {\n        // 返回\n        Navigator.of(context).pop();\n      }\n    }\n  }\n}\n```\n\n代码很简单，关键地方都有注释，不再赘述，下面我们看一下运行效果，如图15-5所示。\n\n![图15-5](../imgs/15-5.png)"
  },
  {
    "path": "src/chapter15/models.md",
    "content": "# 15.3 Model类定义\n\n本节我们先梳理一下APP中将用到的数据，然后生成相应的Dart Model类。Json文件转Dart Model的方案采用前面介绍过的 json_model 包方案\n\n### Github账号信息\n\n登录Github后，我们需要获取当前登录者的Github账号信息，Github API接口返回Json结构如下：\n\n```json\n{\n  \"login\": \"octocat\", //用户登录名\n  \"avatar_url\": \"https://github.com/images/error/octocat_happy.gif\", //用户头像地址\n  \"type\": \"User\", //用户类型，可能是组织\n  \"name\": \"monalisa octocat\", //用户名字\n  \"company\": \"GitHub\", //公司\n  \"blog\": \"https://github.com/blog\", //博客地址\n  \"location\": \"San Francisco\", // 用户所处地理位置\n  \"email\": \"octocat@github.com\", // 邮箱\n  \"hireable\": false,\n  \"bio\": \"There once was...\", // 用户简介\n  \"public_repos\": 2, // 公开项目数\n  \"followers\": 20, //关注该用户的人数\n  \"following\": 0, // 该用户关注的人数\n  \"created_at\": \"2008-01-14T04:33:35Z\", // 账号创建时间\n  \"updated_at\": \"2008-01-14T04:33:35Z\", // 账号信息更新时间\n  \"total_private_repos\": 100, //该用户总的私有项目数(包括参与的其它组织的私有项目)\n  \"owned_private_repos\": 100 //该用户自己的私有项目数\n  ... //省略其它字段\n}\n```\n\n我们在“jsons”目录下创建一个“user.json”文件保存上述信息。\n\n### API缓存策略信息\n\n由于Github服务器在国内访问速度较慢，我们对Github API应用一些简单的缓存策略。我们在“jsons”目录下创建一个“cacheConfig.json”文件缓存策略信息，定义如下：\n\n```json\n{\n  \"enable\":true, // 是否启用缓存\n  \"maxAge\":1000, // 缓存的最长时间，单位（秒）\n  \"maxCount\":100 // 最大缓存数\n}\n```\n\n### 用户信息\n\n用户信息(Profile)应包括如下信息：\n\n1. Github账号信息；由于我们的APP可以切换账号登录，且登录后再次打开则不需要登录，所以我们需要对用户账号信息和登录状态进行持久化。\n2. 应用使用配置信息；每一个用户都应有自己的APP配置信息，如主题、语言、以及数据缓存策略等。\n3. 用户注销登录后，为了便于用户在退出APP前再次登录，我们需要记住上次登录的用户名。\n\n需要注意的是，目前Github有三种登录方式，分别是账号密码登录、oauth授权登录、二次认证登录；这三种登录方式的安全性依次加强，但是在本示例中，为了简单起见，我们使用账号密码登录，因此我们需要保存用户的密码。\n\n> 注意：在这里需要提醒读者，在登录场景中，保护用户账号安全是一个非常重要且永恒的话题，在实际开发中应严格杜绝直接明文存储用户账密的行为。\n\n我们在“jsons”目录下创建一个“profile.json”文件，结构如下：\n\n```json\n{\n  \"user\":\"$user\", //Github账号信息，结构见\"user.json\"\n  \"token\":\"\", // 登录用户的token(oauth)或密码\n  \"theme\":5678, //主题色值\n  \"cache\":\"$cacheConfig\", // 缓存策略信息，结构见\"cacheConfig.json\"\n  \"lastLogin\":\"\", //最近一次的注销登录的用户名\n  \"locale\":\"\" // APP语言信息\n}\n```\n\n### 项目信息\n\n由于APP主页要显示其所有项目信息，我们在“jsons”目录下创建一个“repo.json”文件保存项目信息。通过参考Github 获取项目信息的API文档，定义出最终的“repo.json”文件结构，如下：\n\n```json\n{\n  \"id\": 1296269,\n  \"name\": \"Hello-World\", //项目名称\n  \"full_name\": \"octocat/Hello-World\", //项目完整名称\n  \"owner\": \"$user\", // 项目拥有者，结构见\"user.json\"\n  \"parent\":\"$repo\", // 如果是fork的项目，则此字段表示fork的父项目信息\n  \"private\": false, // 是否私有项目\n  \"description\": \"This your first repo!\", //项目描述\n  \"fork\": false, // 该项目是否为fork的项目\n  \"language\": \"JavaScript\",//该项目的主要编程语言\n  \"forks_count\": 9, // fork了该项目的数量\n  \"stargazers_count\": 80, //该项目的star数量\n  \"size\": 108, // 项目占用的存储大小\n  \"default_branch\": \"master\", //项目的默认分支\n  \"open_issues_count\": 2, //该项目当前打开的issue数量\n  \"pushed_at\": \"2011-01-26T19:06:43Z\",\n  \"created_at\": \"2011-01-26T19:01:12Z\",\n  \"updated_at\": \"2011-01-26T19:14:43Z\",\n  \"subscribers_count\": 42, //订阅（关注）该项目的人数\n  \"license\": { // 该项目的开源许可证\n    \"key\": \"mit\",\n    \"name\": \"MIT License\",\n    \"spdx_id\": \"MIT\",\n    \"url\": \"https://api.github.com/licenses/mit\",\n    \"node_id\": \"MDc6TGljZW5zZW1pdA==\"\n  }\n  ...//省略其它字段\n}\n```\n\n### 生成Dart Model类\n\n现在，我们需要的Json数据已经定义完毕，现在只需要运行json_model package提供的命令来通过json文件生成相应的Dart类：\n\n```shell\nflutter packages pub run json_model\n```\n\n命令执行成功后，可以看到lib/models文件夹下会生成相应的Dart Model类：\n\n```\n├── models\n│   ├── cacheConfig.dart\n│   ├── cacheConfig.g.dart\n│   ├── index.dart\n│   ├── profile.dart\n│   ├── profile.g.dart\n│   ├── repo.dart\n│   ├── repo.g.dart\n│   ├── user.dart\n│   └── user.g.dart\n\n```\n\n### 数据持久化\n\n我们使用shared_preferences包来对登录用户的Profile信息进行持久化。shared_preferences是一个Flutter插件，它通过Android和iOS平台提供的机制来实现数据持久化。由于shared_preferences的使用非常简单，读者可以自行查看其文档，在此不再赘述。\n"
  },
  {
    "path": "src/chapter15/network.md",
    "content": "# 15.5 网络请求封装\n\n本节我们会基于前面介绍过的dio网络库封装APP中用到的网络请求接口，并同时应用一个简单的缓存策略。下面我们先介绍一下网络接口缓存原理，然后再封装APP的业务请求接口。\n\n## 15.5.1 网络接口缓存\n\n由于在国内访问Github服务器速度较慢，所以我们应用一些简单的缓存策略：将请求的url作为key，对请求的返回值在一个指定时间段类进行缓存，另外设置一个最大缓存数，当超过最大缓存数后移除最早的一条缓存。但是也得提供一种针对特定接口或请求决定是否启用缓存的机制，这种机制可以指定哪些接口或那次请求不应用缓存，这种机制是很有必要的，比如登录接口就不应该缓存，又比如用户在下拉刷新时就不应该再应用缓存。在实现缓存之前我们先定义保存缓存信息的`CacheObject`类：\n\n```dart\nclass CacheObject {\n  CacheObject(this.response)\n      : timeStamp = DateTime.now().millisecondsSinceEpoch;\n  Response response;\n  int timeStamp; // 缓存创建时间\n\n  @override\n  bool operator ==(other) {\n    return response.hashCode == other.hashCode;\n  }\n\n  //将请求uri作为缓存的key\n  @override\n  int get hashCode => response.realUri.hashCode;\n}\n```\n\n接下来我们需要实现具体的缓存策略，由于我们使用的是dio package，所以我们可以直接通过拦截器来实现缓存策略：\n\n```dart\nimport 'dart:collection';\nimport 'package:dio/dio.dart';\nimport '../index.dart';\n\nclass CacheObject {\n  CacheObject(this.response)\n      : timeStamp = DateTime.now().millisecondsSinceEpoch;\n  Response response;\n  int timeStamp;\n\n  @override\n  bool operator ==(other) {\n    return response.hashCode == other.hashCode;\n  }\n\n  @override\n  int get hashCode => response.realUri.hashCode;\n}\n\nclass NetCache extends Interceptor {\n  // 为确保迭代器顺序和对象插入时间一致顺序一致，我们使用LinkedHashMap\n  var cache = LinkedHashMap<String, CacheObject>();\n\n  @override\n  onRequest(RequestOptions options) async {\n    if (!Global.profile.cache.enable) return options;\n    // refresh标记是否是\"下拉刷新\"\n    bool refresh = options.extra[\"refresh\"] == true;\n    //如果是下拉刷新，先删除相关缓存\n    if (refresh) {\n      if (options.extra[\"list\"] == true) {\n        //若是列表，则只要url中包含当前path的缓存全部删除（简单实现，并不精准）\n        cache.removeWhere((key, v) => key.contains(options.path));\n      } else {\n        // 如果不是列表，则只删除uri相同的缓存\n        delete(options.uri.toString());\n      }\n      return options;\n    }\n    if (options.extra[\"noCache\"] != true &&\n        options.method.toLowerCase() == 'get') {\n      String key = options.extra[\"cacheKey\"] ?? options.uri.toString();\n      var ob = cache[key];\n      if (ob != null) {\n        //若缓存未过期，则返回缓存内容\n        if ((DateTime.now().millisecondsSinceEpoch - ob.timeStamp) / 1000 <\n            Global.profile.cache.maxAge) {\n          return cache[key].response;\n        } else {\n          //若已过期则删除缓存，继续向服务器请求\n          cache.remove(key);\n        }\n      }\n    }\n  }\n\n  @override\n  onError(DioError err) async {\n    // 错误状态不缓存\n  }\n\n  @override\n  onResponse(Response response) async {\n    // 如果启用缓存，将返回结果保存到缓存\n    if (Global.profile.cache.enable) {\n      _saveCache(response);\n    }\n  }\n\n  _saveCache(Response object) {\n    RequestOptions options = object.request;\n    if (options.extra[\"noCache\"] != true &&\n        options.method.toLowerCase() == \"get\") {\n      // 如果缓存数量超过最大数量限制，则先移除最早的一条记录\n      if (cache.length == Global.profile.cache.maxCount) {\n        cache.remove(cache[cache.keys.first]);\n      }\n      String key = options.extra[\"cacheKey\"] ?? options.uri.toString();\n      cache[key] = CacheObject(object);\n    }\n  }\n\n  void delete(String key) {\n    cache.remove(key);\n  }\n}\n```\n\n关于代码的解释都在注释中了，在此需要说明的是dio包的`option.extra`是专门用于扩展请求参数的，我们通过定义了“refresh”和“noCache”两个参数实现了“针对特定接口或请求决定是否启用缓存的机制”，这两个参数含义如下：\n\n| 参数名  | 类型 | 解释                                                         |\n| ------- | ---- | ------------------------------------------------------------ |\n| refresh | bool | 如果为true，则本次请求不使用缓存，但新的请求结果依然会被缓存 |\n| noCache | bool | 本次请求禁用缓存，请求结果也不会被缓存。                     |\n\n## 15.5.2 封装网络请求\n\n一个完整的APP，可能会涉及很多网络请求，为了便于管理、收敛请求入口，工程上最好的作法就是将所有网络请求放到同一个源码文件中。由于我们的接口都是请求的Github 开发平台提供的API，所以我们定义一个Git类，专门用于Github API接口调用。另外，在调试过程中，我们通常需要一些工具来查看网络请求、响应报文，使用网络代理工具来调试网络数据问题是主流方式。配置代理需要在应用中指定代理服务器的地址和端口，另外Github API是HTTPS协议，所以在配置完代理后还应该禁用证书校验，这些配置我们在Git类初始化时执行（`init()方法`）。下面是Git类的源码：\n\n```dart\nimport 'dart:async';\nimport 'dart:convert';\nimport 'dart:io';\nimport 'package:dio/dio.dart';\nimport 'package:dio/adapter.dart';\nimport 'package:flutter/material.dart';\nimport '../index.dart';\n\nclass Git {\n  // 在网络请求过程中可能会需要使用当前的context信息，比如在请求失败时\n  // 打开一个新路由，而打开新路由需要context信息。\n  Git([this.context]) {\n    _options = Options(extra: {\"context\": context});\n  }\n\n  BuildContext context;\n  Options _options;\n  static Dio dio = new Dio(BaseOptions(\n    baseUrl: 'https://api.github.com/',\n    headers: {\n      HttpHeaders.acceptHeader: \"application/vnd.github.squirrel-girl-preview,\"\n          \"application/vnd.github.symmetra-preview+json\",\n    },\n  ));\n\n  static void init() {\n    // 添加缓存插件\n    dio.interceptors.add(Global.netCache);\n    // 设置用户token（可能为null，代表未登录）\n    dio.options.headers[HttpHeaders.authorizationHeader] = Global.profile.token;\n\n    // 在调试模式下需要抓包调试，所以我们使用代理，并禁用HTTPS证书校验\n    if (!Global.isRelease) {\n      (dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =\n          (client) {\n        client.findProxy = (uri) {\n          return \"PROXY 10.1.10.250:8888\";\n        };\n        //代理工具会提供一个抓包的自签名证书，会通不过证书校验，所以我们禁用证书校验\n        client.badCertificateCallback =\n            (X509Certificate cert, String host, int port) => true;\n      };\n    }\n  }\n\n  // 登录接口，登录成功后返回用户信息\n  Future<User> login(String login, String pwd) async {\n    String basic = 'Basic ' + base64.encode(utf8.encode('$login:$pwd'));\n    var r = await dio.get(\n      \"/users/$login\",\n      options: _options.merge(headers: {\n        HttpHeaders.authorizationHeader: basic\n      }, extra: {\n        \"noCache\": true, //本接口禁用缓存\n      }),\n    );\n    //登录成功后更新公共头（authorization），此后的所有请求都会带上用户身份信息\n    dio.options.headers[HttpHeaders.authorizationHeader] = basic;\n    //清空所有缓存\n    Global.netCache.cache.clear();\n    //更新profile中的token信息\n    Global.profile.token = basic;\n    return User.fromJson(r.data);\n  }\n\n  //获取用户项目列表\n  Future<List<Repo>> getRepos(\n      {Map<String, dynamic> queryParameters, //query参数，用于接收分页信息\n      refresh = false}) async {\n    if (refresh) {\n      // 列表下拉刷新，需要删除缓存（拦截器中会读取这些信息）\n      _options.extra.addAll({\"refresh\": true, \"list\": true});\n    }\n    var r = await dio.get<List>(\n      \"user/repos\",\n      queryParameters: queryParameters,\n      options: _options,\n    );\n    return r.data.map((e) => Repo.fromJson(e)).toList();\n  }\n}\n```\n\n可以看到我们在`init()`方法中，我们判断了是否是调试环境，然后做了一些针对调试环境的网络配置（设置代理和禁用证书校验）。而`Git.init()`方法是应用启动时被调用的（`Global.init()`方法中会调用`Git.init()`）。\n\n另外需要注意，我们所有的网络请求是通过同一个`dio`实例（静态变量）发出的，在创建该`dio`实例时我们将Github API的基地址和API支持的Header进行了全局配置，这样所有通过该`dio`实例发出的请求都会默认使用者些配置。\n\n在本实例中，我们只用到了登录接口和获取用户项目的接口，所以在`Git`类中只定义了`login(…)`和`getRepos(…)`方法，如果读者要在本实例的基础上扩充功能，读者可以将其它的接口请求方法添加到`Git`类中，这样便实现了网络请求接口在代码层面的集中管理和维护。\n\n"
  },
  {
    "path": "src/chapter2/first_flutter_app.md",
    "content": "\n# 2.1 计数器应用示例\n\n用Android Studio和VS Code创建的Flutter应用模板默认是一个简单的计数器示例。本节先仔细讲解一下这个计数器Demo的源码，让读者对Flutter应用程序结构有个基本了解，然后在随后的小节中将会基于此示例，一步一步添加一些新的功能来介绍Flutter应用的其它概念与技术。\n\n对于接下来的示例，希望读者可以跟着笔者一起亲自动手来写一下，这样不仅可以加深印象，而且也会对介绍的概念与技术有一个真切的体会。如果你还不是很熟悉Dart语言或者没有移动开发经验，不用担心，只要你熟悉面向对象和基本编程概念（如变量、循环和条件控制），则可以完成本示例。\n\n## 2.1.1 创建Flutter应用模板\n\n通过Android Studio或VS Code创建一个新的Flutter工程，命名为\"first_flutter_app\"。创建好后，就会得到一个计数器应用的Demo。\n\n> 注意，默认Demo示例可能随着编辑器Flutter插件的版本变化而变化，本例中会介绍计数器示例的全部代码，所以不会对本示例产生影响。\n\n我们先运行创建的工程，效果如图2-1所示：\n\n![图2-1](../imgs/2-1.png)\n\n\n\n该计数器示例中，每点击一次右下角带“+”号的悬浮按钮，屏幕中央的数字就会加1。\n\n在这个示例中，主要Dart代码是在 **lib/main.dart** 文件中，下面是它的源码：\n\n```dart\nimport 'package:flutter/material.dart';\n\nvoid main() => runApp(new MyApp());\n\nclass MyApp extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return new MaterialApp(\n      title: 'Flutter Demo',\n      theme: new ThemeData(\n        primarySwatch: Colors.blue,\n      ),\n      home: new MyHomePage(title: 'Flutter Demo Home Page'),\n    );\n  }\n}\n\nclass MyHomePage extends StatefulWidget {\n  MyHomePage({Key key, this.title}) : super(key: key);\n  final String title;\n\n  @override\n  _MyHomePageState createState() => new _MyHomePageState();\n}\n\nclass _MyHomePageState extends State<MyHomePage> {\n  int _counter = 0;\n\n  void _incrementCounter() {\n    setState(() {\n      _counter++;\n    });\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return new Scaffold(\n      appBar: new AppBar(\n        title: new Text(widget.title),\n      ),\n      body: new Center(\n        child: new Column(\n          mainAxisAlignment: MainAxisAlignment.center,\n          children: <Widget>[\n            new Text(\n              'You have pushed the button this many times:',\n            ),\n            new Text(\n              '$_counter',\n              style: Theme.of(context).textTheme.headline4,\n            ),\n          ],\n        ),\n      ),\n      floatingActionButton: new FloatingActionButton(\n        onPressed: _incrementCounter,\n        tooltip: 'Increment',\n        child: new Icon(Icons.add),\n      ), // This trailing comma makes auto-formatting nicer for build methods.\n    );\n  }\n}\n\n```\n\n### 分析\n\n1. 导入包。\n\n   ```dart\n   import 'package:flutter/material.dart';\n   ```\n\n   此行代码作用是导入了Material UI组件库。[Material](https://material.io/guidelines/)是一种标准的移动端和web端的视觉设计语言， Flutter默认提供了一套丰富的Material风格的UI组件。\n\n2. 应用入口。\n\n   ```dart\n   void main() => runApp(MyApp());\n   ```\n\n   - 与C/C++、Java类似，Flutter 应用中`main`函数为应用程序的入口。`main`函数中调用了`runApp` 方法，它的功能是启动Flutter应用。`runApp`它接受一个`Widget`参数，在本示例中它是一个`MyApp`对象，`MyApp()`是Flutter应用的根组件。\n   - `main`函数使用了(`=>`)符号，这是Dart中单行函数或方法的简写。\n\n3. 应用结构。\n\n   ```dart\n   class MyApp extends StatelessWidget {\n     @override\n     Widget build(BuildContext context) {\n       return new MaterialApp(\n         //应用名称  \n         title: 'Flutter Demo', \n         theme: new ThemeData(\n           //蓝色主题  \n           primarySwatch: Colors.blue,\n         ),\n         //应用首页路由  \n         home: new MyHomePage(title: 'Flutter Demo Home Page'),\n       );\n     }\n   }\n   ```\n\n   - `MyApp`类代表Flutter应用，它继承了 `StatelessWidget `类，这也就意味着应用本身也是一个widget。\n\n   - 在Flutter中，大多数东西都是widget（后同“组件”或“部件”），包括对齐(alignment)、填充(padding)和布局(layout)等，它们都是以widget的形式提供。\n\n   - Flutter在构建页面时，会调用组件的`build`方法，widget的主要工作是提供一个build()方法来描述如何构建UI界面（通常是通过组合、拼装其它基础widget）。\n\n   - `MaterialApp` 是Material库中提供的Flutter APP框架，通过它可以设置应用的名称、主题、语言、首页及路由列表等。`MaterialApp`也是一个widget。\n\n   - `home` 为Flutter应用的首页，它也是一个widget。\n\n## 2.1.2 首页\n\n   ```dart\n   class MyHomePage extends StatefulWidget {\n     MyHomePage({Key key, this.title}) : super(key: key);\n     final String title;\n     @override\n     _MyHomePageState createState() => new _MyHomePageState();\n   }\n   \n   class _MyHomePageState extends State<MyHomePage> {\n    ...\n   }\n   ```\n\n`MyHomePage` 是Flutter应用的首页，它继承自`StatefulWidget`类，表示它是一个有状态的组件（Stateful widget）。关于Stateful widget我们将在第三章“Widget简介”一节仔细介绍，现在我们只需简单认为有状态的组件（Stateful widget） 和无状态的组件（Stateless widget）有两点不同：\n\n1. Stateful widget可以拥有状态，这些状态在widget生命周期中是可以变的，而Stateless widget是不可变的。\n\n2. Stateful widget至少由两个类组成：\n   - 一个` StatefulWidget`类。\n   - 一个 `State`类； `StatefulWidget`类本身是不变的，但是`State`类中持有的状态在widget生命周期中可能会发生变化。\n\n   `_MyHomePageState`类是`MyHomePage`类对应的状态类。看到这里，读者可能已经发现：和`MyApp` 类不同， `MyHomePage`类中并没有`build`方法，取而代之的是，`build`方法被挪到了`_MyHomePageState`方法中，至于为什么这么做，先留个疑问，在分析完完整代码后再来解答。\n   \n### State类\n\n接下来，我们看看`_MyHomePageState`中都包含哪些东西：\n\n1. 该组件的状态。由于我们只需要维护一个点击次数计数器，所以定义一个`_counter`状态：\n\n   ```dart\n   int _counter = 0; //用于记录按钮点击的总次数\n   ```\n\n   `_counter` 为保存屏幕右下角带“+”号按钮点击次数的状态。\n\n2. 设置状态的自增函数。\n\n   ```dart\n   void _incrementCounter() {\n     setState(() {\n        _counter++;\n     });\n   }\n   ```\n\n   当按钮点击时，会调用此函数，该函数的作用是先自增`_counter`，然后调用`setState` 方法。`setState`方法的作用是通知Flutter框架，有状态发生了改变，Flutter框架收到通知后，会执行`build`方法来根据新的状态重新构建界面， Flutter 对此方法做了优化，使重新执行变的很快，所以你可以重新构建任何需要更新的东西，而无需分别去修改各个widget。\n\n3. 构建UI界面\n\n   构建UI界面的逻辑在`build`方法中，当`MyHomePage`第一次创建时，`_MyHomePageState`类会被创建，当初始化完成后，Flutter框架会调用Widget的`build`方法来构建widget树，最终将widget树渲染到设备屏幕上。所以，我们看看`_MyHomePageState`的`build`方法中都干了什么事：\n\n   ```dart\n     Widget build(BuildContext context) {\n       return new Scaffold(\n         appBar: new AppBar(\n           title: new Text(widget.title),\n         ),\n         body: new Center(\n           child: new Column(\n             mainAxisAlignment: MainAxisAlignment.center,\n             children: <Widget>[\n               new Text(\n                 'You have pushed the button this many times:',\n               ),\n               new Text(\n                 '$_counter',\n                 style: Theme.of(context).textTheme.headline4,\n               ),\n             ],\n           ),\n         ),\n         floatingActionButton: new FloatingActionButton(\n           onPressed: _incrementCounter,\n           tooltip: 'Increment',\n           child: new Icon(Icons.add),\n         ),\n       );\n     }\n   ```\n\n   - `Scaffold` 是 Material 库中提供的页面脚手架，它提供了默认的导航栏、标题和包含主屏幕widget树（后同“组件树”或“部件树”）的`body`属性，组件树可以很复杂。本书后面示例中，路由默认都是通过`Scaffold`创建。\n   - `body`的组件树中包含了一个`Center` 组件，`Center` 可以将其子组件树对齐到屏幕中心。此例中， `Center` 子组件是一个`Column` 组件，`Column`的作用是将其所有子组件沿屏幕垂直方向依次排列； 此例中`Column`子组件是两个 `Text `，第一个`Text` 显示固定文本 “You have pushed the button this many times:”，第二个`Text` 显示`_counter`状态的数值。\n   - `floatingActionButton`是页面右下角的带“+”的悬浮按钮，它的`onPressed`属性接受一个回调函数，代表它被点击后的处理器，本例中直接将`_incrementCounter`方法作为其处理函数。\n\n\n\n现在，我们将整个计数器执行流程串起来：当右下角的`floatingActionButton`按钮被点击之后，会调用`_incrementCounter`方法。在`_incrementCounter`方法中，首先会自增`_counter`计数器（状态），然后`setState`会通知Flutter框架状态发生变化，接着，Flutter框架会调用`build`方法以新的状态重新构建UI，最终显示在设备屏幕上。\n\n\n#### 为什么要将build方法放在State中，而不是放在StatefulWidget中？\n\n现在，我们回答之前提出的问题，为什么`build()`方法放在State（而不是`StatefulWidget`）中 ？这主要是为了提高开发的灵活性。如果将`build()`方法放在`StatefulWidget`中则会有两个问题：\n\n- 状态访问不便\n\n  试想一下，如果我们的`StatefulWidget`有很多状态，而每次状态改变都要调用`build`方法，由于状态是保存在State中的，如果`build`方法在`StatefulWidget`中，那么`build`方法和状态分别在两个类中，那么构建时读取状态将会很不方便！试想一下，如果真的将`build`方法放在StatefulWidget中的话，由于构建用户界面过程需要依赖State，所以`build`方法将必须加一个`State`参数，大概是下面这样：\n\n  ```dart\n    Widget build(BuildContext context, State state){\n        //state.counter\n        ...\n    }\n  ```\n\n  这样的话就只能将State的所有状态声明为公开的状态，这样才能在State类外部访问状态！但是，将状态设置为公开后，状态将不再具有私密性，这就会导致对状态的修改将会变的不可控。但如果将`build()`方法放在State中的话，构建过程不仅可以直接访问状态，而且也无需公开私有状态，这会非常方便。\n\n- 继承`StatefulWidget`不便\n\n  例如，Flutter中有一个动画widget的基类`AnimatedWidget`，它继承自`StatefulWidget`类。`AnimatedWidget`中引入了一个抽象方法`build(BuildContext context)`，继承自`AnimatedWidget`的动画widget都要实现这个`build`方法。现在设想一下，如果`StatefulWidget` 类中已经有了一个`build`方法，正如上面所述，此时`build`方法需要接收一个state对象，这就意味着`AnimatedWidget`必须将自己的State对象(记为_animatedWidgetState)提供给其子类，因为子类需要在其`build`方法中调用父类的`build`方法，代码可能如下：\n\n  ```dart\n  class MyAnimationWidget extends AnimatedWidget{\n      @override\n      Widget build(BuildContext context, State state){\n        //由于子类要用到AnimatedWidget的状态对象_animatedWidgetState，\n        //所以AnimatedWidget必须通过某种方式将其状态对象_animatedWidgetState\n        //暴露给其子类   \n        super.build(context, _animatedWidgetState)\n      }\n  }\n  ```\n\n  这样很显然是不合理的，因为\n\n  1. `AnimatedWidget`的状态对象是`AnimatedWidget`内部实现细节，不应该暴露给外部。\n  2. 如果要将父类状态暴露给子类，那么必须得有一种传递机制，而做这一套传递机制是无意义的，因为父子类之间状态的传递和子类本身逻辑是无关的。\n\n综上所述，可以发现，对于`StatefulWidget`，将`build`方法放在State中，可以给开发带来很大的灵活性。\n\n"
  },
  {
    "path": "src/chapter2/flutter_app_debug.md",
    "content": "\n# 2.5 调试Flutter应用\n\n有各种各样的工具和功能来帮助调试Flutter应用程序。\n\n### Dart 分析器\n\n在运行应用程序前，请运行`flutter analyze`测试你的代码。这个工具是一个静态代码检查工具，它是`dartanalyzer`工具的一个包装，主要用于分析代码并帮助开发者发现可能的错误，比如，Dart分析器大量使用了代码中的类型注释来帮助追踪问题，避免`var`、无类型的参数、无类型的列表文字等。\n\n 如果你使用IntelliJ的Flutter插件，那么分析器在打开IDE时就已经自动启用了，如果读者使用的是其它IDE，强烈建议读者启用Dart 分析器，因为在大多数时候，Dart 分析器可以在代码运行前发现大多数问题。\n\n### Dart Observatory (语句级的单步调试和分析器)\n\n如果我们使用`flutter run`启动应用程序，那么当它运行时，我们可以打开Observatory工具的Web页面，例如Observatory默认监听[http://127.0.0.1:8100/](http://127.0.0.1:8100/)，可以在浏览器中直接打开该链接。直接使用语句级单步调试器连接到您的应用程序。如果您使用的是IntelliJ，则还可以使用其内置的调试器来调试您的应用程序。\n\nObservatory 同时支持分析、检查堆等。有关Observatory的更多信息请参考[Observatory 文档](https://dart-lang.github.io/observatory/)。\n\n如果您使用Observatory进行分析，请确保通过`--profile`选项来运行`flutter run`命令来运行应用程序。 否则，配置文件中将出现的主要问题将是调试断言，以验证框架的各种不变量（请参阅下面的“调试模式断言”）。\n\n### `debugger()` 声明\n\n当使用Dart Observatory（或另一个Dart调试器，例如IntelliJ IDE中的调试器）时，可以使用该`debugger()`语句插入编程式断点。要使用这个，你必须添加`import 'dart:developer';`到相关文件顶部。\n\n`debugger()`语句采用一个可选`when`参数，您可以指定该参数仅在特定条件为真时中断，如下所示：\n\n```dart\nvoid someFunction(double offset) {\n  debugger(when: offset > 30.0);\n  // ...\n}\n```\n\n### `print`、`debugPrint`、`flutter logs`\n\nDart `print()`功能将输出到系统控制台，您可以使用`flutter logs`来查看它（基本上是一个包装`adb logcat`）。\n\n如果你一次输出太多，那么Android有时会丢弃一些日志行。为了避免这种情况，您可以使用Flutter的`foundation`库中的[`debugPrint()`](https://docs.flutter.io/flutter/foundation/debugPrint.html)。 这是一个封装print，它将输出限制在一个级别，避免被Android内核丢弃。\n\nFlutter框架中的许多类都有`toString`实现。按照惯例，这些输出通常包括对象的`runtimeType`单行输出，通常在表单中ClassName(more information about this instance…)。 树中使用的一些类也具有`toStringDeep`，从该点返回整个子树的多行描述。已一些具有详细信息`toString`的类会实现一个`toStringShort`，它只返回对象的类型或其他非常简短的（一个或两个单词）描述。\n\n### 调试模式断言\n\n在Flutter应用调试过程中，Dart `assert`语句被启用，并且Flutter框架使用它来执行许多运行时检查来验证是否违反一些不可变的规则。\n\n当一个不可变的规则被违反时，它被报告给控制台，并带有一些上下文信息来帮助追踪问题的根源。\n\n要关闭调试模式并使用发布模式，请使用`flutter run --release`运行您的应用程序。 这也关闭了Observatory调试器。一个中间模式可以关闭除Observatory之外所有调试辅助工具的，称为“profile mode”，用`--profile`替代`--release`即可。\n\n### 调试应用程序层\n\nFlutter框架的每一层都提供了将其当前状态或事件转储(dump)到控制台（使用`debugPrint`）的功能。\n\n#### Widget 树\n\n要转储Widgets树的状态，请调用[`debugDumpApp()`](https://docs.flutter.io/flutter/widgets/debugDumpApp.html)。 只要应用程序已经构建了至少一次（即在调用`build()`之后的任何时间），您可以在应用程序未处于构建阶段（即，不在`build()`方法内调用 ）的任何时间调用此方法（在调用`runApp()`之后）。\n\n如, 这个应用程序:\n\n```dart\nimport 'package:flutter/material.dart';\n\nvoid main() {\n  runApp(\n    new MaterialApp(\n      home: new AppHome(),\n    ),\n  );\n}\n\nclass AppHome extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return new Material(\n      child: new Center(\n        child: new FlatButton(\n          onPressed: () {\n            debugDumpApp();\n          },\n          child: new Text('Dump App'),\n        ),\n      ),\n    );\n  }\n}\n```\n\n…会输出这样的内容（精确的细节会根据框架的版本、设备的大小等等而变化）：\n\n```shell\nI/flutter ( 6559): WidgetsFlutterBinding - CHECKED MODE\nI/flutter ( 6559): RenderObjectToWidgetAdapter<RenderBox>([GlobalObjectKey RenderView(497039273)]; renderObject: RenderView)\nI/flutter ( 6559): └MaterialApp(state: _MaterialAppState(1009803148))\nI/flutter ( 6559):  └ScrollConfiguration()\nI/flutter ( 6559):   └AnimatedTheme(duration: 200ms; state: _AnimatedThemeState(543295893; ticker inactive; ThemeDataTween(ThemeData(Brightness.light Color(0xff2196f3) etc...) → null)))\nI/flutter ( 6559):    └Theme(ThemeData(Brightness.light Color(0xff2196f3) etc...))\nI/flutter ( 6559):     └WidgetsApp([GlobalObjectKey _MaterialAppState(1009803148)]; state: _WidgetsAppState(552902158))\nI/flutter ( 6559):      └CheckedModeBanner()\nI/flutter ( 6559):       └Banner()\nI/flutter ( 6559):        └CustomPaint(renderObject: RenderCustomPaint)\nI/flutter ( 6559):         └DefaultTextStyle(inherit: true; color: Color(0xd0ff0000); family: \"monospace\"; size: 48.0; weight: 900; decoration: double Color(0xffffff00) TextDecoration.underline)\nI/flutter ( 6559):          └MediaQuery(MediaQueryData(size: Size(411.4, 683.4), devicePixelRatio: 2.625, textScaleFactor: 1.0, padding: EdgeInsets(0.0, 24.0, 0.0, 0.0)))\nI/flutter ( 6559):           └LocaleQuery(null)\nI/flutter ( 6559):            └Title(color: Color(0xff2196f3))\n... #省略剩余内容\n```\n\n这是一个“扁平化”的树，显示了通过各种构建函数投影的所有widget（如果你在widget树的根中调用`toStringDeepwidget`，这是你获得的树）。 你会看到很多在你的应用源代码中没有出现的widget，因为它们是被框架中widget的`build()`函数插入的。例如，[`InkFeature`](https://docs.flutter.io/flutter/material/InkFeature-class.html)是Material widget的一个实现细节 。\n\n当按钮从被按下变为被释放时debugDumpApp()被调用，FlatButton对象同时调用`setState()`，并将自己标记为\"dirty\"。 这就是为什么如果你看转储，你会看到特定的对象标记为“dirty”。您还可以查看已注册了哪些手势监听器; 在这种情况下，一个单一的GestureDetector被列出，并且监听“tap”手势（“tap”是`TapGestureDetector`的`toStringShort`函数输出的）\n\n如果您编写自己的widget，则可以通过覆盖[`debugFillProperties()`](https://docs.flutter.io/flutter/widgets/Widget/debugFillProperties.html)来添加信息。 将[DiagnosticsProperty](https://docs.flutter.io/flutter/foundation/DiagnosticsProperty-class.html)对象作为方法参数，并调用父类方法。 该函数是该`toString`方法用来填充小部件描述信息的。\n\n#### 渲染树\n\n如果您尝试调试布局问题，那么Widget树可能不够详细。在这种情况下，您可以通过调用`debugDumpRenderTree()`转储渲染树。 正如`debugDumpApp()`，除布局或绘制阶段外，您可以随时调用此函数。作为一般规则，从[frame 回调](https://docs.flutter.io/flutter/scheduler/SchedulerBinding/addPersistentFrameCallback.html) 或事件处理器中调用它是最佳解决方案。\n\n要调用`debugDumpRenderTree()`，您需要添加`import'package:flutter/rendering.dart';`到您的源文件。\n\n上面这个小例子的输出结果如下所示：\n\n```shell\nI/flutter ( 6559): RenderView\nI/flutter ( 6559):  │ debug mode enabled - android\nI/flutter ( 6559):  │ window size: Size(1080.0, 1794.0) (in physical pixels)\nI/flutter ( 6559):  │ device pixel ratio: 2.625 (physical pixels per logical pixel)\nI/flutter ( 6559):  │ configuration: Size(411.4, 683.4) at 2.625x (in logical pixels)\nI/flutter ( 6559):  │\nI/flutter ( 6559):  └─child: RenderCustomPaint\nI/flutter ( 6559):    │ creator: CustomPaint ← Banner ← CheckedModeBanner ←\nI/flutter ( 6559):    │   WidgetsApp-[GlobalObjectKey _MaterialAppState(1009803148)] ←\nI/flutter ( 6559):    │   Theme ← AnimatedTheme ← ScrollConfiguration ← MaterialApp ←\nI/flutter ( 6559):    │   [root]\nI/flutter ( 6559):    │ parentData: <none>\nI/flutter ( 6559):    │ constraints: BoxConstraints(w=411.4, h=683.4)\nI/flutter ( 6559):    │ size: Size(411.4, 683.4)\n... # 省略\n```\n\n这是根`RenderObject`对象的`toStringDeep`函数的输出。\n\n当调试布局问题时，关键要看的是`size`和`constraints`字段。约束沿着树向下传递，尺寸向上传递。\n\n如果您编写自己的渲染对象，则可以通过覆盖[`debugFillProperties()`](https://docs.flutter.io/flutter/rendering/Layer/debugFillProperties.html)将信息添加到转储。 将[DiagnosticsProperty](https://docs.flutter.io/flutter/foundation/DiagnosticsProperty-class.html)对象作为方法的参数，并调用父类方法。\n\n#### Layer树\n\n读者可以理解为渲染树是可以分层的，而最终绘制需要将不同的层合成起来，而Layer则是绘制时需要合成的层，如果您尝试调试合成问题，则可以使用[`debugDumpLayerTree()`](https://docs.flutter.io/flutter/rendering/debugDumpLayerTree.html)。对于上面的例子，它会输出：\n\n```\nI/flutter : TransformLayer\nI/flutter :  │ creator: [root]\nI/flutter :  │ offset: Offset(0.0, 0.0)\nI/flutter :  │ transform:\nI/flutter :  │   [0] 3.5,0.0,0.0,0.0\nI/flutter :  │   [1] 0.0,3.5,0.0,0.0\nI/flutter :  │   [2] 0.0,0.0,1.0,0.0\nI/flutter :  │   [3] 0.0,0.0,0.0,1.0\nI/flutter :  │\nI/flutter :  ├─child 1: OffsetLayer\nI/flutter :  │ │ creator: RepaintBoundary ← _FocusScope ← Semantics ← Focus-[GlobalObjectKey MaterialPageRoute(560156430)] ← _ModalScope-[GlobalKey 328026813] ← _OverlayEntry-[GlobalKey 388965355] ← Stack ← Overlay-[GlobalKey 625702218] ← Navigator-[GlobalObjectKey _MaterialAppState(859106034)] ← Title ← ⋯\nI/flutter :  │ │ offset: Offset(0.0, 0.0)\nI/flutter :  │ │\nI/flutter :  │ └─child 1: PictureLayer\nI/flutter :  │\nI/flutter :  └─child 2: PictureLayer\n```\n\n这是根`Layer`的`toStringDeep`输出的。\n\n根部的变换是应用设备像素比的变换; 在这种情况下，每个逻辑像素代表3.5个设备像素。\n\n`RepaintBoundary` widget在渲染树的层中创建了一个`RenderRepaintBoundary`。这用于减少需要重绘的需求量。\n\n### 语义\n\n您还可以调用[`debugDumpSemanticsTree()`](https://docs.flutter.io/flutter/rendering/debugDumpSemanticsTree.html)获取语义树（呈现给系统可访问性API的树）的转储。 要使用此功能，必须首先启用辅助功能，例如启用系统辅助工具或`SemanticsDebugger` （下面讨论）。\n\n对于上面的例子，它会输出:\n\n```\nI/flutter : SemanticsNode(0; Rect.fromLTRB(0.0, 0.0, 411.4, 683.4))\nI/flutter :  ├SemanticsNode(1; Rect.fromLTRB(0.0, 0.0, 411.4, 683.4))\nI/flutter :  │ └SemanticsNode(2; Rect.fromLTRB(0.0, 0.0, 411.4, 683.4); canBeTapped)\nI/flutter :  └SemanticsNode(3; Rect.fromLTRB(0.0, 0.0, 411.4, 683.4))\nI/flutter :    └SemanticsNode(4; Rect.fromLTRB(0.0, 0.0, 82.0, 36.0); canBeTapped; \"Dump App\")\n```\n\n### 调度\n\n要找出相对于帧的开始/结束事件发生的位置，可以切换[`debugPrintBeginFrameBanner`](https://docs.flutter.io/flutter/scheduler/debugPrintBeginFrameBanner.html)和[`debugPrintEndFrameBanner`](https://docs.flutter.io/flutter/scheduler/debugPrintEndFrameBanner.html)布尔值以将帧的开始和结束打印到控制台。\n\n例如:\n\n```\nI/flutter : ▄▄▄▄▄▄▄▄ Frame 12         30s 437.086ms ▄▄▄▄▄▄▄▄\nI/flutter : Debug print: Am I performing this work more than once per frame?\nI/flutter : Debug print: Am I performing this work more than once per frame?\nI/flutter : ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀\n```\n\n[`debugPrintScheduleFrameStacks`](https://docs.flutter.io/flutter/scheduler/debugPrintScheduleFrameStacks.html)还可以用来打印导致当前帧被调度的调用堆栈。\n\n### 可视化调试\n\n您也可以通过设置`debugPaintSizeEnabled`为`true`以可视方式调试布局问题。 这是来自`rendering`库的布尔值。它可以在任何时候启用，并在为true时影响绘制。 设置它的最简单方法是在`void main()`的顶部设置。\n\n当它被启用时，所有的盒子都会得到一个明亮的深青色边框，padding（来自widget如Padding）显示为浅蓝色，子widget周围有一个深蓝色框， 对齐方式（来自widget如Center和Align）显示为黄色箭头. 空白（如没有任何子节点的Container）以灰色显示。\n\n[`debugPaintBaselinesEnabled`](https://docs.flutter.io/flutter/rendering/debugPaintBaselinesEnabled.html)做了类似的事情，但对于具有基线的对象，文字基线以绿色显示，表意(ideographic)基线以橙色显示。\n\n[`debugPaintPointersEnabled`](https://docs.flutter.io/flutter/rendering/debugPaintPointersEnabled.html)标志打开一个特殊模式，任何正在点击的对象都会以深青色突出显示。 这可以帮助您确定某个对象是否以某种不正确的方式进行hit测试（Flutter检测点击的位置是否有能响应用户操作的widget）,例如，如果它实际上超出了其父项的范围，首先不会考虑通过hit测试。\n\n如果您尝试调试合成图层，例如以确定是否以及在何处添加`RepaintBoundary` widget，则可以使用[`debugPaintLayerBordersEnabled`](https://docs.flutter.io/flutter/rendering/debugPaintLayerBordersEnabled.html) 标志， 该标志用橙色或轮廓线标出每个层的边界，或者使用[`debugRepaintRainbowEnabled`](https://docs.flutter.io/flutter/rendering/debugRepaintRainbowEnabled.html)标志， 只要他们重绘时，这会使该层被一组旋转色所覆盖。\n\n所有这些标志只能在调试模式下工作。通常，Flutter框架中以“`debug...`” 开头的任何内容都只能在调试模式下工作。\n\n### 调试动画\n\n调试动画最简单的方法是减慢它们的速度。为此，请将[`timeDilation`](https://docs.flutter.io/flutter/scheduler/timeDilation.html)变量（在scheduler库中）设置为大于1.0的数字，例如50.0。 最好在应用程序启动时只设置一次。如果您在运行中更改它，尤其是在动画运行时将其值改小，则在观察时可能会出现倒退，这可能会导致断言命中，并且这通常会干扰我们的开发工作。\n\n### 调试性能问题\n\n要了解您的应用程序导致重新布局或重新绘制的原因，您可以分别设置[`debugPrintMarkNeedsLayoutStacks`](https://docs.flutter.io/flutter/rendering/debugPrintMarkNeedsLayoutStacks.html)和 [`debugPrintMarkNeedsPaintStacks`](https://docs.flutter.io/flutter/rendering/debugPrintMarkNeedsPaintStacks.html)标志。 每当渲染盒被要求重新布局和重新绘制时，这些都会将堆栈跟踪记录到控制台。如果这种方法对您有用，您可以使用`services`库中的`debugPrintStack()`方法按需打印堆栈痕迹。\n\n### 统计应用启动时间\n\n要收集有关Flutter应用程序启动所需时间的详细信息，可以在运行`flutter run`时使用`trace-startup`和`profile`选项。\n\n```shell\n$ flutter run --trace-startup --profile\n```\n\n跟踪输出保存为`start_up_info.json`，在Flutter工程目录在build目录下。输出列出了从应用程序启动到这些跟踪事件（以微秒捕获）所用的时间：\n\n- 进入Flutter引擎时.\n- 展示应用第一帧时.\n- 初始化Flutter框架时.\n- 完成Flutter框架初始化时.\n\n如 :\n\n```json\n{\n  \"engineEnterTimestampMicros\": 96025565262,\n  \"timeToFirstFrameMicros\": 2171978,\n  \"timeToFrameworkInitMicros\": 514585,\n  \"timeAfterFrameworkInitMicros\": 1657393\n}\n```\n\n### 跟踪Dart代码性能\n\n要执行自定义性能跟踪和测量Dart任意代码段的wall/CPU时间（类似于在Android上使用[systrace](https://developer.android.com/studio/profile/systrace.html)）。 使用`dart:developer`的[Timeline](https://api.dartlang.org/stable/dart-developer/Timeline-class.html)工具来包含你想测试的代码块，例如：\n\n```dart\nTimeline.startSync('interesting function');\n// iWonderHowLongThisTakes();\nTimeline.finishSync();\n```\n\n然后打开你应用程序的Observatory timeline页面，在“Recorded Streams”中选择‘Dart’复选框，并执行你想测量的功能。\n\n刷新页面将在Chrome的[跟踪工具](https://www.chromium.org/developers/how-tos/trace-event-profiling-tool)中显示应用按时间顺序排列的timeline记录。\n\n请确保运行`flutter run`时带有`--profile`标志，以确保运行时性能特征与您的最终产品差异最小。\n\n\n"
  },
  {
    "path": "src/chapter2/flutter_assets_mgr.md",
    "content": "# 2.4 资源管理\n\nFlutter APP安装包中会包含代码和 assets（资源）两部分。Assets是会打包到程序安装包中的，可在运行时访问。常见类型的assets包括静态数据（例如JSON文件）、配置文件、图标和图片（JPEG，WebP，GIF，动画WebP / GIF，PNG，BMP和WBMP）等。\n\n## 指定 assets\n\n和包管理一样，Flutter也使用[`pubspec.yaml`](https://www.dartlang.org/tools/pub/pubspec)文件来管理应用程序所需的资源，举个例子:\n\n```yaml\nflutter:\n  assets:\n    - assets/my_icon.png\n    - assets/background.png\n```\n\n`assets`指定应包含在应用程序中的文件， 每个asset都通过相对于`pubspec.yaml`文件所在的文件系统路径来标识自身的路径。asset的声明顺序是无关紧要的，asset的实际目录可以是任意文件夹（在本示例中是assets文件夹）。\n\n在构建期间，Flutter将asset放置到称为 *asset bundle* 的特殊存档中，应用程序可以在运行时读取它们（但不能修改）。\n\n## Asset 变体（variant）\n\n构建过程支持“asset变体”的概念：不同版本的asset可能会显示在不同的上下文中。 在`pubspec.yaml`的assets部分中指定asset路径时，构建过程中，会在相邻子目录中查找具有相同名称的任何文件。这些文件随后会与指定的asset一起被包含在asset bundle中。\n\n例如，如果应用程序目录中有以下文件:\n\n- …/pubspec.yaml\n- …/graphics/my_icon.png\n- …/graphics/background.png\n- …/graphics/dark/background.png\n- …etc.\n\n然后`pubspec.yaml`文件中只需包含:\n\n```\nflutter:\n  assets:\n    - graphics/background.png\n```\n\n那么这两个`graphics/background.png`和`graphics/dark/background.png` 都将包含在您的asset bundle中。前者被认为是_main asset_ （主资源），后者被认为是一种变体（variant）。\n\n在选择匹配当前设备分辨率的图片时，Flutter会使用到asset变体（见下文），将来，Flutter可能会将这种机制扩展到本地化、阅读提示等方面。\n\n## 加载 assets\n\n您的应用可以通过[`AssetBundle`](https://docs.flutter.io/flutter/services/AssetBundle-class.html)对象访问其asset 。有两种主要方法允许从Asset bundle中加载字符串或图片（二进制）文件。\n\n### 加载文本assets\n\n- 通过[`rootBundle`](https://docs.flutter.io/flutter/services/rootBundle.html) 对象加载：每个Flutter应用程序都有一个[`rootBundle`](https://docs.flutter.io/flutter/services/rootBundle.html)对象， 通过它可以轻松访问主资源包，直接使用`package:flutter/services.dart`中全局静态的`rootBundle`对象来加载asset即可。\n- 通过 [`DefaultAssetBundle`](https://docs.flutter.io/flutter/widgets/DefaultAssetBundle-class.html) 加载：建议使用 [`DefaultAssetBundle`](https://docs.flutter.io/flutter/widgets/DefaultAssetBundle-class.html) 来获取当前BuildContext的AssetBundle。 这种方法不是使用应用程序构建的默认asset bundle，而是使父级widget在运行时动态替换的不同的AssetBundle，这对于本地化或测试场景很有用。\n\n通常，可以使用`DefaultAssetBundle.of()`在应用运行时来间接加载asset（例如JSON文件），而在widget上下文之外，或其它`AssetBundle`句柄不可用时，可以使用`rootBundle`直接加载这些asset，例如：\n\n```dart\nimport 'dart:async' show Future;\nimport 'package:flutter/services.dart' show rootBundle;\n\nFuture<String> loadAsset() async {\n  return await rootBundle.loadString('assets/config.json');\n}\n```\n\n### 加载图片\n\n类似于原生开发，Flutter也可以为当前设备加载适合其分辨率的图像。\n\n#### 声明分辨率相关的图片 assets\n\n[`AssetImage`](https://docs.flutter.io/flutter/painting/AssetImage-class.html) 可以将asset的请求逻辑映射到最接近当前设备像素比例（dpi）的asset。为了使这种映射起作用，必须根据特定的目录结构来保存asset：\n\n- …/image.png\n- …/**M**x/image.png\n- …/**N**x/image.png\n- …etc.\n\n其中M和N是数字标识符，对应于其中包含的图像的分辨率，也就是说，它们指定不同设备像素比例的图片。\n\n主资源默认对应于1.0倍的分辨率图片。看一个例子：\n\n- …/my_icon.png\n- …/2.0x/my_icon.png\n- …/3.0x/my_icon.png\n\n在设备像素比率为1.8的设备上，`.../2.0x/my_icon.png` 将被选择。对于2.7的设备像素比率，`.../3.0x/my_icon.png`将被选择。\n\n如果未在`Image` widget上指定渲染图像的宽度和高度，那么`Image` widget将占用与主资源相同的屏幕空间大小。 也就是说，如果`.../my_icon.png`是72px乘72px，那么`.../3.0x/my_icon.png`应该是216px乘216px; 但如果未指定宽度和高度，它们都将渲染为72像素×72像素（以逻辑像素为单位）。\n\n`pubspec.yaml`中asset部分中的每一项都应与实际文件相对应，但主资源项除外。当主资源缺少某个资源时，会按分辨率从低到高的顺序去选择 ，也就是说1x中没有的话会在2x中找，2x中还没有的话就在3x中找。\n\n#### 加载图片\n\n要加载图片，可以使用 [`AssetImage`](https://docs.flutter.io/flutter/painting/AssetImage-class.html)类。例如，我们可以从上面的asset声明中加载背景图片：\n\n```dart\nWidget build(BuildContext context) {\n  return new DecoratedBox(\n    decoration: new BoxDecoration(\n      image: new DecorationImage(\n        image: new AssetImage('graphics/background.png'),\n      ),\n    ),\n  );\n}\n```\n\n注意，`AssetImage` 并非是一个widget， 它实际上是一个`ImageProvider`，有些时候你可能期望直接得到一个显示图片的widget，那么你可以使用`Image.asset()`方法，如：\n\n```dart\nWidget build(BuildContext context) {\n  return Image.asset('graphics/background.png');\n}\n```\n\n使用默认的 asset bundle 加载资源时，内部会自动处理分辨率等，这些处理对开发者来说是无感知的。 (如果使用一些更低级别的类，如 [`ImageStream`](https://docs.flutter.io/flutter/painting/ImageStream-class.html)或 [`ImageCache`](https://docs.flutter.io/flutter/painting/ImageCache-class.html) 时你会注意到有与缩放相关的参数)\n\n#### 依赖包中的资源图片\n\n要加载依赖包中的图像，必须给`AssetImage`提供`package`参数。\n\n例如，假设您的应用程序依赖于一个名为“my_icons”的包，它具有如下目录结构：\n\n- …/pubspec.yaml\n- …/icons/heart.png\n- …/icons/1.5x/heart.png\n- …/icons/2.0x/heart.png\n- …etc.\n\n然后加载图像，使用:\n\n```dart\n new AssetImage('icons/heart.png', package: 'my_icons')\n```\n\n或\n\n```dart\nnew Image.asset('icons/heart.png', package: 'my_icons')\n```\n\n**注意：包在使用本身的资源时也应该加上`package`参数来获取**。\n\n\n\n##### 打包包中的 assets\n\n如果在`pubspec.yaml`文件中声明了期望的资源，它将会打包到相应的package中。特别是，包本身使用的资源必须在`pubspec.yaml`中指定。\n\n包也可以选择在其`lib/`文件夹中包含未在其`pubspec.yaml`文件中声明的资源。在这种情况下，对于要打包的图片，应用程序必须在`pubspec.yaml`中指定包含哪些图像。 例如，一个名为“fancy_backgrounds”的包，可能包含以下文件：\n\n- …/lib/backgrounds/background1.png\n- …/lib/backgrounds/background2.png\n- …/lib/backgrounds/background3.png\n\n要包含第一张图像，必须在`pubspec.yaml`的assets部分中声明它：\n\n```\nflutter:\n  assets:\n    - packages/fancy_backgrounds/backgrounds/background1.png\n```\n\n`lib/`是隐含的，所以它不应该包含在资产路径中。\n\n### 特定平台 assets\n\n上面的资源都是flutter应用中的，这些资源只有在Flutter框架运行之后才能使用，如果要给我们的应用设置APP图标或者添加启动图，那我们必须使用特定平台的assets。\n\n#### 设置APP图标\n\n更新Flutter应用程序启动图标的方式与在本机Android或iOS应用程序中更新启动图标的方式相同。\n\n- Android\n\n  在Flutter项目的根目录中，导航到`.../android/app/src/main/res`目录，里面包含了各种资源文件夹（如`mipmap-hdpi`已包含占位符图像“ic_launcher.png”，见图2-8）。 只需按照[Android开发人员指南](https://developer.android.com/guide/practices/ui_guidelines/icon_design_launcher.html#size)中的说明， 将其替换为所需的资源，并遵守每种屏幕密度（dpi）的建议图标大小标准。\n\n  ![图2-8](../imgs/2-8.png)\n\n  > **注意:** 如果您重命名.png文件，则还必须在您`AndroidManifest.xml`的`<application>`标签的`android:icon`属性中更新名称。\n\n- iOS\n\n  在Flutter项目的根目录中，导航到`.../ios/Runner`。该目录中`Assets.xcassets/AppIcon.appiconset`已经包含占位符图片（见图2-9）， 只需将它们替换为适当大小的图片，保留原始文件名称。\n  \n  ![图2-9](../imgs/2-9.png)\n\n   \n\n#### 更新启动页\n\n![图2-10](../imgs/2-10.png)\n\n在Flutter框架加载时，Flutter会使用本地平台机制绘制启动页。此启动页将持续到Flutter渲染应用程序的第一帧时。\n\n> **注意:** 这意味着如果您不在应用程序的`main()`方法中调用[runApp](https://docs.flutter.io/flutter/widgets/runApp.html) 函数 （或者更具体地说，如果您不调用[`window.render`](https://docs.flutter.io/flutter/dart-ui/Window/render.html)去响应[`window.onDrawFrame`](https://docs.flutter.io/flutter/dart-ui/Window/onDrawFrame.html)）的话， 启动屏幕将永远持续显示。\n\n##### Android\n\n要将启动屏幕（splash screen）添加到您的Flutter应用程序， 请导航至`.../android/app/src/main`。在`res/drawable/launch_background.xml`，通过自定义drawable来实现自定义启动界面（你也可以直接换一张图片）。\n\n##### iOS\n\n要将图片添加到启动屏幕（splash screen）的中心，请导航至`.../ios/Runner`。在`Assets.xcassets/LaunchImage.imageset`， 拖入图片，并命名为`LaunchImage.png`、`LaunchImage@2x.png`、`LaunchImage@3x.png`。 如果你使用不同的文件名，那您还必须更新同一目录中的`Contents.json`文件，图片的具体尺寸可以查看苹果官方的标准。\n\n您也可以通过打开Xcode完全自定义storyboard。在Project Navigator中导航到`Runner/Runner`然后通过打开`Assets.xcassets`拖入图片，或者通过在LaunchScreen.storyboard中使用Interface Builder进行自定义，如图2-11所示。\n\n![图2-11](../imgs/2-11.png)\n\n\n"
  },
  {
    "path": "src/chapter2/flutter_package_mgr.md",
    "content": "# 2.3 包管理\n\n在软件开发中，很多时候有一些公共的库或SDK可能会被很多项目用到，因此，将这些代码单独抽到一个独立模块，然后哪个项目需要使用时再直接集成这个模块，便可大大提高开发效率。很多编程语言或开发工具都支持这种“模块共享”机制，如Java语言中这种独立模块会被打成一个jar包，Android中的aar包，Web开发中的npm包等。为了方便表述，我们将这种可共享的独立模块统一称为“包”（ Package）。\n\n一个APP在实际开发中往往会依赖很多包，而这些包通常都有交叉依赖关系、版本依赖等，如果由开发者手动来管理应用中的依赖包将会非常麻烦。因此，各种开发生态或编程语言官方通常都会提供一些包管理工具，比如在Android提供了Gradle来管理依赖，iOS用Cocoapods或Carthage来管理依赖，Node中通过npm等。而在Flutter开发中也有自己的包管理工具。本节我们主要介绍一下flutter如何使用配置文件`pubspec.yaml`（位于项目根目录）来管理第三方依赖包。\n\nYAML是一种直观、可读性高并且容易被人类阅读的文件格式，它和xml或Json相比，它语法简单并非常容易解析，所以YAML常用于配置文件，Flutter也是用yaml文件作为其配置文件。Flutter项目默认的配置文件是`pubspec.yaml`，我们看一个简单的示例：\n\n```yaml\nname: flutter_in_action\ndescription: First Flutter application.\n\nversion: 1.0.0+1\n\ndependencies:\n  flutter:\n    sdk: flutter\n  cupertino_icons: ^0.1.2\n\ndev_dependencies:\n  flutter_test:\n    sdk: flutter\n    \nflutter:\n  uses-material-design: true\n```\n\n下面，我们逐一解释一下各个字段的意义：\n\n- `name`：应用或包名称。\n- `description`: 应用或包的描述、简介。\n- `version`：应用或包的版本号。\n- `dependencies`：应用或包依赖的其它包或插件。\n- `dev_dependencies`：开发环境依赖的工具包（而不是flutter应用本身依赖的包）。\n- `flutter`：flutter相关的配置选项。\n\n如果我们的Flutter应用本身依赖某个包，我们需要将所依赖的包添加到`dependencies` 下，接下来我们通过一个例子来演示一下如何添加、下载并使用第三方包。\n\n## Pub仓库\n\nPub（https://pub.dev/ ）是Google官方的Dart Packages仓库，类似于node中的npm仓库，android中的jcenter。我们可以在Pub上面查找我们需要的包和插件，也可以向Pub发布我们的包和插件。我们将在后面的章节中介绍如何向Pub发布我们的包和插件。\n\n## 示例\n\n接下来，我们实现一个显示随机字符串的widget。有一个名为“english_words”的开源软件包，其中包含数千个常用的英文单词以及一些实用功能。我们首先在pub上找到english_words这个包（如图2-5所示），确定其最新的版本号和是否支持Flutter。\n\n![图2-5](../imgs/2-5.png)\n\n我们看到“english_words”包最新的版本是3.1.3，并且支持flutter，接下来：\n\n1. 将“english_words”（3.1.3版本）添加到依赖项列表，如下：\n\n   ```yaml\n   dependencies:\n     flutter:\n       sdk: flutter\n   \n     cupertino_icons: ^0.1.0\n     # 新添加的依赖\n     english_words: ^3.1.3\n   ```\n\n2. 下载包。在Android Studio的编辑器视图中查看pubspec.yaml时（图2-6），单击右上角的 **Packages get** 。\n\n   ![图2-6](../imgs/2-6.png)\n\n   这会将依赖包安装到您的项目。我们可以在控制台中看到以下内容：\n   \n   ```shell\n   flutter packages get\n   Running \"flutter packages get\" in flutter_in_action...\n   Process finished with exit code 0\n   ```\n   \n   我们也可以在控制台，定位到当前工程目录，然后手动运行`flutter packages get` 命令来下载依赖包。另外，需要注意`dependencies`和`dev_dependencies`的区别，前者的依赖包将作为APP的源码的一部分参与编译，生成最终的安装包。而后者的依赖包只是作为开发阶段的一些工具包，主要是用于帮助我们提高开发、测试效率，比如flutter的自动化测试包等。\n\n3. 引入`english_words`包。\n\n   ```dart\n   import 'package:english_words/english_words.dart';\n   ```\n\n   在输入时，Android Studio会自动提供有关库导入的建议选项。导入后该行代码将会显示为灰色，表示导入的库尚未使用。\n\n4. 使用`english_words`包来生成随机字符串。\n\n   ```dart\n   class RandomWordsWidget extends StatelessWidget {\n     @override\n     Widget build(BuildContext context) {\n      // 生成随机字符串\n       final wordPair = new WordPair.random();\n       return Padding(\n         padding: const EdgeInsets.all(8.0),\n         child: new Text(wordPair.toString()),\n       );\n     }\n   }\n   ```\n\n   我们将`RandomWordsWidget` 添加到 `_MyHomePageState.build` 的`Column`的子widget中。\n\n   ```dart\n   Column(\n     mainAxisAlignment: MainAxisAlignment.center,\n     children: <Widget>[\n       ... //省略无关代码\n       RandomWordsWidget(),\n     ],\n   )\n   ```\n\n5. 如果应用程序正在运行，请使用热重载按钮（⚡️图标） 更新正在运行的应用程序。每次单击热重载或保存项目时，都会在正在运行的应用程序中随机选择不同的单词对。 这是因为单词对是在 `build` 方法内部生成的。每次热更新时，`build`方法都会被执行，运行效果如图2-7所示。\n\n   ![图2-7](../imgs/2-7.png)\n\n\n## 其它依赖方式\n\n上文所述的依赖方式是依赖Pub仓库的。但我们还可以依赖本地包和git仓库。\n\n- 依赖本地包\n\n  如果我们正在本地开发一个包，包名为pkg1，我们可以通过下面方式依赖：\n\n  ```yaml\n  dependencies:\n  \tpkg1:\n          path: ../../code/pkg1\n  ```\n\n  路径可以是相对的，也可以是绝对的。\n\n- 依赖Git：你也可以依赖存储在Git仓库中的包。如果软件包位于仓库的根目录中，请使用以下语法\n\n  ```yaml\n  dependencies:\n    pkg1:\n      git:\n        url: git://github.com/xxx/pkg1.git\n  ```\n\n  上面假定包位于Git存储库的根目录中。如果不是这种情况，可以使用path参数指定相对位置，例如：\n\n  ```yaml\n  dependencies:\n    package1:\n      git:\n        url: git://github.com/flutter/packages.git\n        path: packages/package1        \n  ```\n\n上面介绍的这些依赖方式是Flutter开发中常用的，但还有一些其它依赖方式，完整的内容读者可以自行查看：https://www.dartlang.org/tools/pub/dependencies 。\n\n## 总结\n\n本节介绍了Flutter中包管理、引用、下载的整体流程，我们将在后面的章节中介绍如何开发并发布我们自己的包。\n\n\n"
  },
  {
    "path": "src/chapter2/flutter_router.md",
    "content": "# 2.2 路由管理\n\n路由(Route)在移动开发中通常指页面（Page），这跟web开发中单页应用的Route概念意义是相同的，Route在Android中通常指一个Activity，在iOS中指一个ViewController。所谓路由管理，就是管理页面之间如何跳转，通常也可被称为导航管理。Flutter中的路由管理和原生开发类似，无论是Android还是iOS，导航管理都会维护一个路由栈，路由入栈(push)操作对应打开一个新页面，路由出栈(pop)操作对应页面关闭操作，而路由管理主要是指如何来管理路由栈。\n\n## 2.2.1 一个简单示例\n\n我们在上一节“计数器”示例的基础上，做如下修改：\n\n1. 创建一个新路由，命名“NewRoute”\n\n   ```dart\n   class NewRoute extends StatelessWidget {\n     @override\n     Widget build(BuildContext context) {\n       return Scaffold(\n         appBar: AppBar(\n           title: Text(\"New route\"),\n         ),\n         body: Center(\n           child: Text(\"This is new route\"),\n         ),\n       );\n     }\n   }\n   ```\n\n   新路由继承自`StatelessWidget`，界面很简单，在页面中间显示一句\"This is new route\"。\n\n2. 在`_MyHomePageState.build`方法中的`Column`的子widget中添加一个按钮（`FlatButton`） :\n\n   ```dart\n   Column(\n         mainAxisAlignment: MainAxisAlignment.center,\n         children: <Widget>[\n         ... //省略无关代码\n         FlatButton(\n            child: Text(\"open new route\"),\n            textColor: Colors.blue,\n            onPressed: () {\n             //导航到新路由   \n             Navigator.push( context,\n              MaterialPageRoute(builder: (context) {\n                 return NewRoute();\n              }));\n             },\n            ),\n          ],\n    )\n   ```\n\n   我们添加了一个打开新路由的按钮，并将按钮文字颜色设置为蓝色，点击该按钮后就会打开新的路由页面，效果如图2-2和2-3所示。\n\n   ![图2-2](../imgs/2-2.png) ![图2-3](../imgs/2-3.png)\n\n\n\n\n\n## 2.2.2 MaterialPageRoute\n\n`MaterialPageRoute`继承自`PageRoute`类，`PageRoute`类是一个抽象类，表示占有整个屏幕空间的一个模态路由页面，它还定义了路由构建及切换时过渡动画的相关接口及属性。`MaterialPageRoute` 是Material组件库提供的组件，它可以针对不同平台，实现与平台页面切换动画风格一致的路由切换动画：\n\n- 对于Android，当打开新页面时，新的页面会从屏幕底部滑动到屏幕顶部；当关闭页面时，当前页面会从屏幕顶部滑动到屏幕底部后消失，同时上一个页面会显示到屏幕上。\n- 对于iOS，当打开页面时，新的页面会从屏幕右侧边缘一致滑动到屏幕左边，直到新页面全部显示到屏幕上，而上一个页面则会从当前屏幕滑动到屏幕左侧而消失；当关闭页面时，正好相反，当前页面会从屏幕右侧滑出，同时上一个页面会从屏幕左侧滑入。\n\n下面我们介绍一下`MaterialPageRoute` 构造函数的各个参数的意义：\n\n```dart\n  MaterialPageRoute({\n    WidgetBuilder builder,\n    RouteSettings settings,\n    bool maintainState = true,\n    bool fullscreenDialog = false,\n  })\n```\n\n- `builder` 是一个WidgetBuilder类型的回调函数，它的作用是构建路由页面的具体内容，返回值是一个widget。我们通常要实现此回调，返回新路由的实例。\n- `settings` 包含路由的配置信息，如路由名称、是否初始路由（首页）。\n- `maintainState`：默认情况下，当入栈一个新路由时，原来的路由仍然会被保存在内存中，如果想在路由没用的时候释放其所占用的所有资源，可以设置`maintainState`为false。\n- `fullscreenDialog`表示新的路由页面是否是一个全屏的模态对话框，在iOS中，如果`fullscreenDialog`为`true`，新页面将会从屏幕底部滑入（而不是水平方向）。\n\n> 如果想自定义路由切换动画，可以自己继承PageRoute来实现，我们将在后面介绍动画时，实现一个自定义的路由组件。\n\n\n\n## 2.2.3 Navigator\n\n`Navigator`是一个路由管理的组件，它提供了打开和退出路由页方法。`Navigator`通过一个栈来管理活动路由集合。通常当前屏幕显示的页面就是栈顶的路由。`Navigator`提供了一系列方法来管理路由栈，在此我们只介绍其最常用的两个方法：\n\n### Future  push(BuildContext context, Route route)\n\n将给定的路由入栈（即打开新的页面），返回值是一个`Future`对象，用以接收新路由出栈（即关闭）时的返回数据。\n\n### bool  pop(BuildContext context, [ result ])\n\n将栈顶路由出栈，`result`为页面关闭时返回给上一个页面的数据。\n\n`Navigator` 还有很多其它方法，如`Navigator.replace`、`Navigator.popUntil`等，详情请参考API文档或SDK源码注释，在此不再赘述。下面我们还需要介绍一下路由相关的另一个概念“命名路由”。\n\n### 实例方法\n\nNavigator类中第一个参数为context的**静态方法**都对应一个Navigator的**实例方法**， 比如`Navigator.push(BuildContext context, Route route) `等价于`Navigator.of(context).push(Route route)` ，下面命名路由相关的方法也是一样的。\n\n\n\n## 2.2.4 路由传值\n\n很多时候，在路由跳转时我们需要带一些参数，比如打开商品详情页时，我们需要带一个商品id，这样商品详情页才知道展示哪个商品信息；又比如我们在填写订单时需要选择收货地址，打开地址选择页并选择地址后，可以将用户选择的地址返回到订单页等等。下面我们通过一个简单的示例来演示新旧路由如何传参。\n\n### 示例\n\n我们创建一个`TipRoute`路由，它接受一个提示文本参数，负责将传入它的文本显示在页面上，另外`TipRoute`中我们添加一个“返回”按钮，点击后在返回上一个路由的同时会带上一个返回参数，下面我们看一下实现代码。\n\n`TipRoute`实现代码：\n\n```dart\nclass TipRoute extends StatelessWidget {\n  TipRoute({\n    Key key,\n    @required this.text,  // 接收一个text参数\n  }) : super(key: key);\n  final String text;\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        title: Text(\"提示\"),\n      ),\n      body: Padding(\n        padding: EdgeInsets.all(18),\n        child: Center(\n          child: Column(\n            children: <Widget>[\n              Text(text),\n              RaisedButton(\n                onPressed: () => Navigator.pop(context, \"我是返回值\"),\n                child: Text(\"返回\"),\n              )\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n```\n\n下面是打开新路由`TipRoute`的代码：\n\n```dart\nclass RouterTestRoute extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return Center(\n      child: RaisedButton(\n        onPressed: () async {\n          // 打开`TipRoute`，并等待返回结果\n          var result = await Navigator.push(\n            context,\n            MaterialPageRoute(\n              builder: (context) {\n                return TipRoute(\n                  // 路由参数\n                  text: \"我是提示xxxx\",\n                );\n              },\n            ),\n          );\n          //输出`TipRoute`路由返回结果\n          print(\"路由返回值: $result\");\n        },\n        child: Text(\"打开提示页\"),\n      ),\n    );\n  }\n}\n```\n\n运行上面代码，点击`RouterTestRoute`页的“打开提示页”按钮，会打开`TipRoute`页，运行效果如图2-4所示下：\n\n![图2-4](../imgs/2-4.png)\n\n需要说明：\n\n1. 提示文案“我是提示xxxx”是通过`TipRoute`的`text`参数传递给新路由页的。我们可以通过等待`Navigator.push(…)`返回的`Future`来获取新路由的返回数据。\n\n2. 在`TipRoute`页中有两种方式可以返回到上一页；第一种方式时直接点击导航栏返回箭头，第二种方式是点击页面中的“返回”按钮。这两种返回方式的区别是前者不会返回数据给上一个路由，而后者会。下面是分别点击页面中的返回按钮和导航栏返回箭头后，`RouterTestRoute`页中`print`方法在控制台输出的内容：\n\n   ```\n   I/flutter (27896): 路由返回值: 我是返回值\n   I/flutter (27896): 路由返回值: null\n   ```\n\n上面介绍的是非命名路由的传值方式，命名路由的传值方式会有所不同，我们会在下面介绍命名路由时介绍。\n\n## 2.2.5 命名路由\n\n所谓“命名路由”（Named Route）即有名字的路由，我们可以先给路由起一个名字，然后就可以通过路由名字直接打开新的路由了，这为路由管理带来了一种直观、简单的方式。\n\n### 路由表\n\n要想使用命名路由，我们必须先提供并注册一个路由表（routing table），这样应用程序才知道哪个名字与哪个路由组件相对应。其实注册路由表就是给路由起名字，路由表的定义如下：\n\n```dart\nMap<String, WidgetBuilder> routes;\n```\n\n它是一个`Map`，key为路由的名字，是个字符串；value是个`builder`回调函数，用于生成相应的路由widget。我们在通过路由名字打开新路由时，应用会根据路由名字在路由表中查找到对应的`WidgetBuilder`回调函数，然后调用该回调函数生成路由widget并返回。\n\n### 注册路由表\n\n路由表的注册方式很简单，我们回到之前“计数器”的示例，然后在`MyApp`类的`build`方法中找到`MaterialApp`，添加`routes`属性，代码如下：\n\n```dart\nMaterialApp(\n  title: 'Flutter Demo',\n  theme: ThemeData(\n    primarySwatch: Colors.blue,\n  ),\n  //注册路由表\n  routes:{\n   \"new_page\":(context) => NewRoute(),\n    ... // 省略其它路由注册信息\n  } ,\n  home: MyHomePage(title: 'Flutter Demo Home Page'),\n);\n```\n\n现在我们就完成了路由表的注册。上面的代码中`home`路由并没有使用命名路由，如果我们也想将`home`注册为命名路由应该怎么做呢？其实很简单，直接看代码：\n\n```dart\nMaterialApp(\n  title: 'Flutter Demo',\n  initialRoute:\"/\", //名为\"/\"的路由作为应用的home(首页)\n  theme: ThemeData(\n    primarySwatch: Colors.blue,\n  ),\n  //注册路由表\n  routes:{\n   \"new_page\":(context) => NewRoute(),\n   \"/\":(context) => MyHomePage(title: 'Flutter Demo Home Page'), //注册首页路由\n  } \n);\n```\n\n可以看到，我们只需在路由表中注册一下`MyHomePage`路由，然后将其名字作为`MaterialApp`的`initialRoute`属性值即可，该属性决定应用的初始路由页是哪一个命名路由。\n\n### 通过路由名打开新路由页\n\n要通过路由名称来打开新路由，可以使用`Navigator` 的`pushNamed`方法：\n\n```dart\nFuture pushNamed(BuildContext context, String routeName,{Object arguments})\n```\n\n`Navigator` 除了`pushNamed`方法，还有`pushReplacementNamed`等其他管理命名路由的方法，读者可以自行查看API文档。接下来我们通过路由名来打开新的路由页，修改`FlatButton`的`onPressed`回调代码，改为：\n\n```dart\nonPressed: () {\n  Navigator.pushNamed(context, \"new_page\");\n  //Navigator.push(context,\n  //  MaterialPageRoute(builder: (context) {\n  //  return NewRoute();\n  //}));  \n},\n```\n\n热重载应用，再次点击“open new route”按钮，依然可以打开新的路由页。\n\n### 命名路由参数传递\n\n在Flutter最初的版本中，命名路由是不能传递参数的，后来才支持了参数；下面展示命名路由如何传递并获取路由参数：\n\n我们先注册一个路由：\n\n```dart\n routes:{\n   \"new_page\":(context) => EchoRoute(),\n  } ,\n```\n\n在路由页通过`RouteSetting`对象获取路由参数：\n\n```dart\nclass EchoRoute extends StatelessWidget {\n\n  @override\n  Widget build(BuildContext context) {\n    //获取路由参数  \n    var args=ModalRoute.of(context).settings.arguments;\n    //...省略无关代码\n  }\n}\n```\n\n在打开路由时传递参数\n\n```dart\nNavigator.of(context).pushNamed(\"new_page\", arguments: \"hi\");\n```\n\n### 适配\n\n假设我们也想将上面路由传参示例中的`TipRoute`路由页注册到路由表中，以便也可以通过路由名来打开它。但是，由于`TipRoute`接受一个`text` 参数，我们如何在不改变`TipRoute`源码的前提下适配这种情况？其实很简单：\n\n```dart\nMaterialApp(\n  ... //省略无关代码\n  routes: {\n   \"tip2\": (context){\n     return TipRoute(text: ModalRoute.of(context).settings.arguments);\n   },\n }, \n);\n```\n\n## 2.2.6 路由生成钩子\n\n假设我们要开发一个电商APP，当用户没有登录时可以看店铺、商品等信息，但交易记录、购物车、用户个人信息等页面需要登录后才能看。为了实现上述功能，我们需要在打开每一个路由页前判断用户登录状态！如果每次打开路由前我们都需要去判断一下将会非常麻烦，那有什么更好的办法吗？答案是有！\n\n`MaterialApp`有一个`onGenerateRoute`属性，它在打开命名路由时可能会被调用，之所以说可能，是因为当调用`Navigator.pushNamed(...)`打开命名路由时，如果指定的路由名在路由表中已注册，则会调用路由表中的`builder`函数来生成路由组件；如果路由表中没有注册，才会调用`onGenerateRoute`来生成路由。`onGenerateRoute`回调签名如下：\n\n```dart\nRoute<dynamic> Function(RouteSettings settings)\n```\n\n有了`onGenerateRoute`回调，要实现上面控制页面权限的功能就非常容易：我们放弃使用路由表，取而代之的是提供一个`onGenerateRoute`回调，然后在该回调中进行统一的权限控制，如：\n\n```dart\nMaterialApp(\n  ... //省略无关代码\n  onGenerateRoute:(RouteSettings settings){\n\t  return MaterialPageRoute(builder: (context){\n\t\t   String routeName = settings.name;\n       // 如果访问的路由页需要登录，但当前未登录，则直接返回登录页路由，\n       // 引导用户登录；其它情况则正常打开路由。\n     }\n   );\n  }\n);\n```\n\n> 注意，`onGenerateRoute`只会对命名路由生效。\n\n## 2.2.7 总结\n\n本章先介绍了Flutter中路由管理、传参的方式，然后又着重介绍了命名路由相关内容。在此需要说明一点，由于命名路由只是一种可选的路由管理方式，在实际开发中，读者可能心中会犹豫到底使用哪种路由管理方式。在此，根据笔者经验，建议读者最好统一使用命名路由的管理方式，这将会带来如下好处：\n\n1. 语义化更明确。\n2. 代码更好维护；如果使用匿名路由，则必须在调用`Navigator.push`的地方创建新路由页，这样不仅需要import新路由页的dart文件，而且这样的代码将会非常分散。\n3. 可以通过`onGenerateRoute`做一些全局的路由跳转前置处理逻辑。\n\n综上所述，笔者比较建议使用命名路由，当然这并不是什么金科玉律，读者可以根据自己偏好或实际情况来决定。\n\n另外，还有一些关于路由管理的内容我们没有介绍，比如路由MaterialApp中还有`navigatorObservers`和`onUnknownRoute`两个回调属性，前者可以监听所有路由跳转动作，后者在打开一个不存在的命名路由时会被调用，由于这些功能并不常用，而且也比较简单，我们便不再花费篇幅来介绍了，读者可以自行查看API文档。"
  },
  {
    "path": "src/chapter2/index.md",
    "content": "## 简介\n\n本章将通过一些简单的示例来一步步介绍Flutter的开发流程.\n\n## 本章目录\n\n* [2.1：计数器示例](first_flutter_app.md)\n* [2.2：路由管理](flutter_router.md)  \n* [2.3：包管理](flutter_package_mgr.md)        \n* [2.4：资源管理](flutter_assets_mgr.md)    \n* [2.5：调试Flutter APP](flutter_app_debug.md)\n* [2.6：Dart线程模型及异常捕获](thread_model_and_error_report.md)\n"
  },
  {
    "path": "src/chapter2/thread_model_and_error_report.md",
    "content": "# 2.6 Flutter异常捕获\n\n在介绍Flutter异常捕获之前必须先了解一下Dart单线程模型，只有了解了Dart的代码执行流程，我们才能知道该在什么地方去捕获异常。\n\n## 2.6.1 Dart单线程模型\n\n在Java和Objective-C（以下简称“OC”）中，如果程序发生异常且没有被捕获，那么程序将会终止，但是这在Dart或JavaScript中则不会！究其原因，这和它们的运行机制有关系。Java和OC都是多线程模型的编程语言，任意一个线程触发异常且该异常未被捕获时，就会导致整个进程退出。但Dart和JavaScript不会，它们都是单线程模型，运行机制很相似(但有区别)，下面我们通过Dart官方提供的一张图来看看Dart大致运行原理：\n\n\n\n![图2-12](../imgs/2-12.png)\n\nDart 在单线程中是以消息循环机制来运行的，其中包含两个任务队列，一个是“微任务队列”  **microtask queue**，另一个叫做“事件队列” **event queue**。从图中可以发现，微任务队列的执行优先级高于事件队列。\n\n现在我们来介绍一下Dart线程运行过程，如上图中所示，入口函数 main() 执行完后，消息循环机制便启动了。首先会按照先进先出的顺序逐个执行微任务队列中的任务，事件任务执行完毕后程序便会退出，但是，在事件任务执行的过程中也可以插入新的微任务和事件任务，在这种情况下，整个线程的执行过程便是一直在循环，不会退出，而Flutter中，主线程的执行过程正是如此，永不终止。\n\n在Dart中，所有的外部事件任务都在事件队列中，如IO、计时器、点击、以及绘制事件等，而微任务通常来源于Dart内部，并且微任务非常少，之所以如此，是因为微任务队列优先级高，如果微任务太多，执行时间总和就越久，事件队列任务的延迟也就越久，对于GUI应用来说最直观的表现就是比较卡，所以必须得保证微任务队列不会太长。值得注意的是，我们可以通过`Future.microtask(…)`方法向微任务队列插入一个任务。\n\n在事件循环中，当某个任务发生异常并没有被捕获时，程序并不会退出，而直接导致的结果是**当前任务**的后续代码就不会被执行了，也就是说一个任务中的异常是不会影响其它任务执行的。\n\n\n\n## 2.6.2 Flutter异常捕获\n\nDart中可以通过`try/catch/finally`来捕获代码块异常，这个和其它编程语言类似，如果读者不清楚，可以查看Dart语言文档，不再赘述，下面我们看看Flutter中的异常捕获。\n\n### Flutter框架异常捕获\n\nFlutter 框架为我们在很多关键的方法进行了异常捕获。这里举一个例子，当我们布局发生越界或不合规范时，Flutter就会自动弹出一个错误界面，这是因为Flutter已经在执行build方法时添加了异常捕获，最终的源码如下：\n\n```dart\n@override\nvoid performRebuild() {\n ...\n  try {\n    //执行build方法  \n    built = build();\n  } catch (e, stack) {\n    // 有异常时则弹出错误提示  \n    built = ErrorWidget.builder(_debugReportException('building $this', e, stack));\n  } \n  ...\n}      \n```\n\n可以看到，在发生异常时，Flutter默认的处理方式是弹一个ErrorWidget，但如果我们想自己捕获异常并上报到报警平台的话应该怎么做？我们进入`_debugReportException()`方法看看：\n\n```dart\nFlutterErrorDetails _debugReportException(\n  String context,\n  dynamic exception,\n  StackTrace stack, {\n  InformationCollector informationCollector\n}) {\n  //构建错误详情对象  \n  final FlutterErrorDetails details = FlutterErrorDetails(\n    exception: exception,\n    stack: stack,\n    library: 'widgets library',\n    context: context,\n    informationCollector: informationCollector,\n  );\n  //报告错误 \n  FlutterError.reportError(details);\n  return details;\n}\n```\n\n我们发现，错误是通过`FlutterError.reportError`方法上报的，继续跟踪：\n\n```dart\n\nstatic void reportError(FlutterErrorDetails details) {\n  ...\n  if (onError != null)\n    onError(details); //调用了onError回调\n}\n```\n\n我们发现`onError`是`FlutterError`的一个静态属性，它有一个默认的处理方法 `dumpErrorToConsole`，到这里就清晰了，如果我们想自己上报异常，只需要提供一个自定义的错误处理回调即可，如：\n\n```dart\nvoid main() {\n  FlutterError.onError = (FlutterErrorDetails details) {\n    reportError(details);\n  };\n ...\n}\n```\n\n这样我们就可以处理那些Flutter为我们捕获的异常了，接下来我们看看如何捕获其它异常。\n\n### 其它异常捕获与日志收集\n\n在Flutter中，还有一些Flutter没有为我们捕获的异常，如调用空对象方法异常、Future中的异常。在Dart中，异常分两类：同步异常和异步异常，同步异常可以通过`try/catch`捕获，而异步异常则比较麻烦，如下面的代码是捕获不了`Future`的异常的：\n\n```dart\ntry{\n    Future.delayed(Duration(seconds: 1)).then((e) => Future.error(\"xxx\"));\n}catch (e){\n    print(e)\n}\n```\n\nDart中有一个`runZoned(...)` 方法，可以给执行对象指定一个Zone。Zone表示一个代码执行的环境范围，为了方便理解，读者可以将Zone类比为一个代码执行沙箱，不同沙箱的之间是隔离的，沙箱可以捕获、拦截或修改一些代码行为，如Zone中可以捕获日志输出、Timer创建、微任务调度的行为，同时Zone也可以捕获所有未处理的异常。下面我们看看`runZoned(...)`方法定义：\n\n```dart\nR runZoned<R>(R body(), {\n    Map zoneValues, \n    ZoneSpecification zoneSpecification,\n    Function onError,\n}) \n```\n\n- `zoneValues`: Zone 的私有数据，可以通过实例`zone[key]`获取，可以理解为每个“沙箱”的私有数据。\n\n- `zoneSpecification`：Zone的一些配置，可以自定义一些代码行为，比如拦截日志输出行为等，举个例子：\n\n  下面是拦截应用中所有调用`print`输出日志的行为。\n\n  ```dart\n  main() {\n    runZoned(() => runApp(MyApp()), zoneSpecification: new ZoneSpecification(\n        print: (Zone self, ZoneDelegate parent, Zone zone, String line) {\n          parent.print(zone, \"Intercepted: $line\");\n        }),\n    );\n  }\n  ```\n\n  这样一来，我们APP中所有调用`print`方法输出日志的行为都会被拦截，通过这种方式，我们也可以在应用中记录日志，等到应用触发未捕获的异常时，将异常信息和日志统一上报。ZoneSpecification还可以自定义一些其他行为，读者可以查看API文档。\n\n- `onError`：Zone中未捕获异常处理回调，如果开发者提供了onError回调或者通过`ZoneSpecification.handleUncaughtError`指定了错误处理回调，那么这个zone将会变成一个error-zone，该error-zone中发生未捕获异常(无论同步还是异步)时都会调用开发者提供的回调，如：\n\n  ```dart\n  runZoned(() {\n      runApp(MyApp());\n  }, onError: (Object obj, StackTrace stack) {\n      var details=makeDetails(obj,stack);\n      reportError(details);\n  });\n  ```\n\n  这样一来，结合上面的`FlutterError.onError`我们就可以捕获我们Flutter应用中全部错误了！需要注意的是，error-zone内部发生的错误是不会跨越当前error-zone的边界的，如果想跨越error-zone边界去捕获异常，可以通过共同的“源”zone来捕获，如：\n\n  ```dart\n  var future = new Future.value(499);\n  runZoned(() {\n  \tvar future2 = future.then((_) { throw \"error in first error-zone\"; });\n  \trunZoned(() {\n  \t\tvar future3 = future2.catchError((e) { print(\"Never reached!\"); });\n  \t}, onError: (e) { print(\"unused error handler\"); });\n  }, onError: (e) { print(\"catches error of first error-zone.\"); });\n  \n  ```\n\n### 总结\n我们最终的异常捕获和上报代码大致如下：\n\n```dart\nvoid collectLog(String line){\n    ... //收集日志\n}\nvoid reportErrorAndLog(FlutterErrorDetails details){\n    ... //上报错误和日志逻辑\n}\n\nFlutterErrorDetails makeDetails(Object obj, StackTrace stack){\n    ...// 构建错误信息\n}\n\nvoid main() {\n  FlutterError.onError = (FlutterErrorDetails details) {\n    reportErrorAndLog(details);\n  };\n  runZoned(\n    () => runApp(MyApp()),\n    zoneSpecification: ZoneSpecification(\n      print: (Zone self, ZoneDelegate parent, Zone zone, String line) {\n        collectLog(line); // 收集日志\n      },\n    ),\n    onError: (Object obj, StackTrace stack) {\n      var details = makeDetails(obj, stack);\n      reportErrorAndLog(details);\n    },\n  );\n}\n```\n\n\n\n\n\n"
  },
  {
    "path": "src/chapter3/buttons.md",
    "content": "# 3.4 按钮\n\n## 3.4.1 Material组件库中的按钮\n\nMaterial 组件库中提供了多种按钮组件如`RaisedButton`、`FlatButton`、`OutlineButton`等，它们都是直接或间接对`RawMaterialButton`组件的包装定制，所以他们大多数属性都和`RawMaterialButton`一样。在介绍各个按钮时我们先介绍其默认外观，而按钮的外观大都可以通过属性来自定义，我们在后面统一介绍这些属性。另外，所有Material 库中的按钮都有如下相同点：\n\n1. 按下时都会有“水波动画”（又称“涟漪动画”，就是点击时按钮上会出现水波荡漾的动画）。\n2. 有一个`onPressed`属性来设置点击回调，当按钮按下时会执行该回调，如果不提供该回调则按钮会处于禁用状态，禁用状态不响应用户点击。\n\n### RaisedButton\n\n`RaisedButton` 即\"漂浮\"按钮，它默认带有阴影和灰色背景。按下后，阴影会变大，如图3-10所示：\n\n![图3-10](../imgs/3-10.png)\n\n使用`RaisedButton`非常简单，如：\n\n```dart\nRaisedButton(\n  child: Text(\"normal\"),\n  onPressed: () {},\n);\n```\n\n### FlatButton\n\n`FlatButton`即扁平按钮，默认背景透明并不带阴影。按下后，会有背景色，如图3-11所示：\n\n![图3-11](../imgs/3-11.png)\n\n使用FlatButton也很简单，代码如下：\n\n```dart\nFlatButton(\n  child: Text(\"normal\"),\n  onPressed: () {},\n)\n```\n\n### OutlineButton\n\n`OutlineButton`默认有一个边框，不带阴影且背景透明。按下后，边框颜色会变亮、同时出现背景和阴影(较弱)，如图3-12所示：\n\n![图3-12](../imgs/3-12.png)\n\n使用`OutlineButton`也很简单，代码如下：\n\n```dart\nOutlineButton(\n  child: Text(\"normal\"),\n  onPressed: () {},\n)\n```\n\n### IconButton\n\n`IconButton`是一个可点击的Icon，不包括文字，默认没有背景，点击后会出现背景，如图3-13所示：\n\n![图3-13](../imgs/3-13.png)\n\n代码如下：\n\n```dart\nIconButton(\n  icon: Icon(Icons.thumb_up),\n  onPressed: () {},\n)\n```\n\n\n\n### 带图标的按钮\n\n`RaisedButton`、`FlatButton`、`OutlineButton`都有一个`icon` 构造函数，通过它可以轻松创建带图标的按钮，如图3-14所示：\n\n![图3-14](../imgs/3-14.png)\n\n代码如下：\n\n```dart\nRaisedButton.icon(\n  icon: Icon(Icons.send),\n  label: Text(\"发送\"),\n  onPressed: _onPressed,\n),\nOutlineButton.icon(\n  icon: Icon(Icons.add),\n  label: Text(\"添加\"),\n  onPressed: _onPressed,\n),\nFlatButton.icon(\n  icon: Icon(Icons.info),\n  label: Text(\"详情\"),\n  onPressed: _onPressed,\n),\n```\n\n\n\n## 3.4.2 自定义按钮外观\n\n按钮外观可以通过其属性来定义，不同按钮属性大同小异，我们以FlatButton为例，介绍一下常见的按钮属性，详细的信息可以查看API文档。\n\n```dart\nconst FlatButton({\n  ...  \n  @required this.onPressed, //按钮点击回调\n  this.textColor, //按钮文字颜色\n  this.disabledTextColor, //按钮禁用时的文字颜色\n  this.color, //按钮背景颜色\n  this.disabledColor,//按钮禁用时的背景颜色\n  this.highlightColor, //按钮按下时的背景颜色\n  this.splashColor, //点击时，水波动画中水波的颜色\n  this.colorBrightness,//按钮主题，默认是浅色主题 \n  this.padding, //按钮的填充\n  this.shape, //外形\n  @required this.child, //按钮的内容\n})\n```\n\n其中大多数属性名都是自解释的，我们不赘述。下面我们通过一个示例来看看如何自定义按钮。\n\n#### 示例\n\n定义一个背景蓝色，两边圆角的按钮。效果如图3-15所示：\n\n![图3-15](../imgs/3-15.png)\n\n代码如下：\n\n```dart\nFlatButton(\n  color: Colors.blue,\n  highlightColor: Colors.blue[700],\n  colorBrightness: Brightness.dark,\n  splashColor: Colors.grey,\n  child: Text(\"Submit\"),\n  shape:RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)),\n  onPressed: () {},\n)\n```\n\n很简单吧，在上面的代码中，我们主要通过`shape`来指定其外形为一个圆角矩形。因为按钮背景是蓝色(深色)，我们需要指定按钮主题`colorBrightness`为`Brightness.dark`，这是为了保证按钮文字颜色为浅色。\n\nFlutter 中没有提供去除背景的设置，假若我们需要去除背景，则可以通过将背景颜色设置为全透明来实现。对应上面的代码，便是将 `color: Colors.blue` 替换为 `color: Color(0x000000)`。\n\n细心的读者可能会发现这个按钮没有阴影(点击之后也没有)，这样会显得没有质感。其实这也很容易，将上面的`FlatButton`换成`RaisedButton`就行，其它代码不用改（这里 color 也不做更改），换了之后的效果如图3-16所示：\n\n![图3-16](../imgs/3-16.png)\n\n是不是有质感了！之所以会这样，是因为`RaisedButton`默认有配置阴影：\n\n```dart\nconst RaisedButton({\n  ...\n  this.elevation = 2.0, //正常状态下的阴影\n  this.highlightElevation = 8.0,//按下时的阴影\n  this.disabledElevation = 0.0,// 禁用时的阴影\n  ...\n}\n```\n\n值得注意的是，在Material 组件库中，我们会在很多组件中见到elevation相关的属性，它们都是用来控制阴影的，这是因为阴影在Material设计风格中是一种很重要的表现形式，以后在介绍其它组件时，便不再赘述。\n\n如果我们想实现一个背景渐变的圆角按钮，按钮有没有相应的属性呢？答案是否定的，但是，我们可以通过其它方式来实现，我们将在后面\"自定义组件\"一章中实现。\n\n\n"
  },
  {
    "path": "src/chapter3/flutter_widget_intro.md",
    "content": "# 3.1 Widget简介\n\n## 3.1.1 概念\n\n在前面的介绍中，我们知道在Flutter中几乎所有的对象都是一个Widget。与原生开发中“控件”不同的是，Flutter中的Widget的概念更广泛，它不仅可以表示UI元素，也可以表示一些功能性的组件如：用于手势检测的 `GestureDetector` widget、用于APP主题数据传递的`Theme`等等，而原生开发中的控件通常只是指UI元素。在后面的内容中，我们在描述UI元素时可能会用到“控件”、“组件”这样的概念，读者心里需要知道他们就是widget，只是在不同场景的不同表述而已。由于Flutter主要就是用于构建用户界面的，所以，在大多数时候，读者可以认为widget就是一个控件，不必纠结于概念。\n\n## 3.1.2 Widget与Element\n\n在Flutter中，Widget的功能是“描述一个UI元素的配置数据”，它就是说，Widget其实并不是表示最终绘制在设备屏幕上的显示元素，而它只是描述显示元素的一个配置数据。\n\n实际上，Flutter中真正代表屏幕上显示元素的类是`Element`，也就是说Widget只是描述`Element`的配置数据！有关`Element`的详细介绍我们将在本书后面的高级部分深入介绍，现在，读者只需要知道：**Widget只是UI元素的一个配置数据，并且一个Widget可以对应多个`Element`**。这是因为同一个Widget对象可以被添加到UI树的不同部分，而真正渲染时，UI树的每一个`Element`节点都会对应一个Widget对象。总结一下：\n\n- Widget实际上就是`Element`的配置数据，Widget树实际上是一个配置树，而真正的UI渲染树是由`Element`构成；不过，由于`Element`是通过Widget生成的，所以它们之间有对应关系，在大多数场景，我们可以宽泛地认为Widget树就是指UI控件树或UI渲染树。\n- 一个Widget对象可以对应多个`Element`对象。这很好理解，根据同一份配置（Widget），可以创建多个实例（Element）。\n\n读者应该将这两点牢记在心中。\n\n## 3.1.3 Widget主要接口\n\n\n我们先来看一下Widget类的声明：\n\n```dart\n@immutable\nabstract class Widget extends DiagnosticableTree {\n  const Widget({ this.key });\n  final Key key;\n    \n  @protected\n  Element createElement();\n\n  @override\n  String toStringShort() {\n    return key == null ? '$runtimeType' : '$runtimeType-$key';\n  }\n\n  @override\n  void debugFillProperties(DiagnosticPropertiesBuilder properties) {\n    super.debugFillProperties(properties);\n    properties.defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.dense;\n  }\n  \n  static bool canUpdate(Widget oldWidget, Widget newWidget) {\n    return oldWidget.runtimeType == newWidget.runtimeType\n        && oldWidget.key == newWidget.key;\n  }\n}\n```\n\n- `Widget`类继承自`DiagnosticableTree`，`DiagnosticableTree`即“诊断树”，主要作用是提供调试信息。\n- `Key`: 这个`key`属性类似于React/Vue中的`key`，主要的作用是决定是否在下一次`build`时复用旧的widget，决定的条件在`canUpdate()`方法中。\n- `createElement()`：正如前文所述“一个Widget可以对应多个`Element`”；Flutter Framework在构建UI树时，会先调用此方法生成对应节点的`Element`对象。此方法是Flutter Framework隐式调用的，在我们开发过程中基本不会调用到。\n- `debugFillProperties(...)` 复写父类的方法，主要是设置诊断树的一些特性。\n- `canUpdate(...)`是一个静态方法，它主要用于在Widget树重新`build`时复用旧的widget，其实具体来说，应该是：是否用新的Widget对象去更新旧UI树上所对应的`Element`对象的配置；通过其源码我们可以看到，只要`newWidget`与`oldWidget`的`runtimeType`和`key`同时相等时就会用`newWidget`去更新`Element`对象的配置，否则就会创建新的`Element`。\n\n有关Key和Widget复用的细节将会在本书后面高级部分深入讨论，读者现在只需知道，为Widget显式添加key的话可能（但不一定）会使UI在重新构建时变的高效，读者目前可以先忽略此参数。本书后面的示例中，只会在构建列表项UI时会显式指定Key。\n\n另外`Widget`类本身是一个抽象类，其中最核心的就是定义了`createElement()`接口，在Flutter开发中，我们一般都不用直接继承`Widget`类来实现一个新组件，相反，我们通常会通过继承`StatelessWidget`或`StatefulWidget`来间接继承`Widget`类来实现。`StatelessWidget`和`StatefulWidget`都是直接继承自`Widget`类，而这两个类也正是Flutter中非常重要的两个抽象类，它们引入了两种Widget模型，接下来我们将重点介绍一下这两个类。\n\n## 3.1.4 StatelessWidget\n\n在之前的章节中，我们已经简单介绍过`StatelessWidget`，`StatelessWidget`相对比较简单，它继承自`Widget`类，重写了`createElement() `方法：\n\n```dart\n@override\nStatelessElement createElement() => new StatelessElement(this);\n```\n\n`StatelessElement` 间接继承自`Element`类，与`StatelessWidget`相对应（作为其配置数据）。\n\n`StatelessWidget`用于不需要维护状态的场景，它通常在`build`方法中通过嵌套其它Widget来构建UI，在构建过程中会递归的构建其嵌套的Widget。我们看一个简单的例子：\n\n```dart\nclass Echo extends StatelessWidget {\n  const Echo({\n    Key key,  \n    @required this.text,\n    this.backgroundColor:Colors.grey,\n  }):super(key:key);\n    \n  final String text;\n  final Color backgroundColor;\n\n  @override\n  Widget build(BuildContext context) {\n    return Center(\n      child: Container(\n        color: backgroundColor,\n        child: Text(text),\n      ),\n    );\n  }\n}\n```\n\n上面的代码，实现了一个回显字符串的`Echo` widget。\n\n> 按照惯例，`widget`的构造函数参数应使用命名参数，命名参数中的必要参数要添加`@required`标注，这样有利于静态代码分析器进行检查。另外，在继承`widget`时，第一个参数通常应该是`Key`，另外，如果Widget需要接收子Widget，那么`child`或`children`参数通常应被放在参数列表的最后。同样是按照惯例，Widget的属性应尽可能的被声明为`final`，防止被意外改变。\n\n然后我们可以通过如下方式使用它：\n\n```dart\nWidget build(BuildContext context) {\n  return Echo(text: \"hello world\");\n}\n```\n\n运行后效果如图3-1所示：\n\n![图3-1](../imgs/3-1.png)\n\n### Context\n\n`build`方法有一个`context`参数，它是`BuildContext`类的一个实例，表示当前widget在widget树中的上下文，每一个widget都会对应一个context对象（因为每一个widget都是widget树上的一个节点）。实际上，`context`是当前widget在widget树中位置中执行”相关操作“的一个句柄，比如它提供了从当前widget开始向上遍历widget树以及按照widget类型查找父级widget的方法。下面是在子树中获取父级widget的一个示例：\n\n```dart\nclass ContextRoute extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        title: Text(\"Context测试\"),\n      ),\n      body: Container(\n        child: Builder(builder: (context) {\n          // 在Widget树中向上查找最近的父级`Scaffold` widget\n          Scaffold scaffold = context.findAncestorWidgetOfExactType<Scaffold>();\n          // 直接返回 AppBar的title， 此处实际上是Text(\"Context测试\")\n          return (scaffold.appBar as AppBar).title;\n        }),\n      ),\n    );\n  }\n}\n```\n\n运行后效果如图3-1-1所示：\n\n![图3-1-1](../imgs/3-1-1.png)\n\n>**注意**：对于`BuildContext`读者现在可以先作了解，随着本书后面内容的展开，也会用到Context的一些方法，读者可以通过具体的场景对其有个直观的认识。关于`BuildContext`更多的内容，我们也将在后面高级部分再深入介绍。\n\n## 3.1.5 StatefulWidget\n\n和`StatelessWidget`一样，`StatefulWidget`也是继承自`Widget`类，并重写了`createElement() `方法，不同的是返回的`Element` 对象并不相同；另外`StatefulWidget`类中添加了一个新的接口`createState()`。\n\n下面我们看看`StatefulWidget`的类定义：\n\n```dart\nabstract class StatefulWidget extends Widget {\n  const StatefulWidget({ Key key }) : super(key: key);\n    \n  @override\n  StatefulElement createElement() => new StatefulElement(this);\n    \n  @protected\n  State createState();\n}\n```\n\n- `StatefulElement ` 间接继承自`Element`类，与StatefulWidget相对应（作为其配置数据）。`StatefulElement `中可能会多次调用`createState()`来创建状态(State)对象。\n\n- `createState()` 用于创建和Stateful widget相关的状态，它在Stateful widget的生命周期中可能会被多次调用。例如，当一个Stateful widget同时插入到widget树的多个位置时，Flutter framework就会调用该方法为每一个位置生成一个独立的State实例，其实，本质上就是一个`StatefulElement`对应一个State实例。\n\n  > 在本书中经常会出现“树”的概念，在不同的场景可能指不同的意思，在说“widget树”时它可以指widget结构树，但由于widget与Element有对应关系（一可能对多），在有些场景（Flutter的SDK文档中）也代指“UI树”的意思。而在stateful widget中，State对象也和`StatefulElement`具有对应关系（一对一），所以在Flutter的SDK文档中，可以经常看到“从树中移除State对象”或“插入State对象到树中”这样的描述。其实，无论哪种描述，其意思都是在描述“一棵构成用户界面的节点元素的树”，读者不必纠结于这些概念，还是那句话“得其神，忘其形”，因此，本书中出现的各种“树”，如果没有特别说明，读者都可抽象的认为它是“一棵构成用户界面的节点元素的树”。\n\n## 3.1.6 State\n\n一个StatefulWidget类会对应一个State类，State表示与其对应的StatefulWidget要维护的状态，State中的保存的状态信息可以：\n\n1. 在widget 构建时可以被同步读取。\n2. 在widget生命周期中可以被改变，当State被改变时，可以手动调用其`setState()`方法通知Flutter framework状态发生改变，Flutter framework在收到消息后，会重新调用其`build`方法重新构建widget树，从而达到更新UI的目的。\n\nState中有两个常用属性：\n\n1. `widget`，它表示与该State实例关联的widget实例，由Flutter framework动态设置。注意，这种关联并非永久的，因为在应用生命周期中，UI树上的某一个节点的widget实例在重新构建时可能会变化，但State实例只会在第一次插入到树中时被创建，当在重新构建时，如果widget被修改了，Flutter framework会动态设置State.widget为新的widget实例。\n\n2. `context`。StatefulWidget对应的BuildContext，作用同StatelessWidget的BuildContext。\n\n#### State生命周期\n\n理解State的生命周期对flutter开发非常重要，为了加深读者印象，本节我们通过一个实例来演示一下State的生命周期。在接下来的示例中，我们实现一个计数器widget，点击它可以使计数器加1，由于要保存计数器的数值状态，所以我们应继承StatefulWidget，代码如下：\n\n```dart\nclass CounterWidget extends StatefulWidget {\n  const CounterWidget({\n    Key key,\n    this.initValue: 0\n  });\n\n  final int initValue;\n\n  @override\n  _CounterWidgetState createState() => new _CounterWidgetState();\n}\n```\n\n`CounterWidget`接收一个`initValue`整型参数，它表示计数器的初始值。下面我们看一下State的代码：\n\n```dart\nclass _CounterWidgetState extends State<CounterWidget> {  \n  int _counter;\n\n  @override\n  void initState() {\n    super.initState();\n    //初始化状态  \n    _counter=widget.initValue;\n    print(\"initState\");\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    print(\"build\");\n    return Scaffold(\n      body: Center(\n        child: FlatButton(\n          child: Text('$_counter'),\n          //点击后计数器自增\n          onPressed:()=>setState(()=> ++_counter,\n          ),\n        ),\n      ),\n    );\n  }\n\n  @override\n  void didUpdateWidget(CounterWidget oldWidget) {\n    super.didUpdateWidget(oldWidget);\n    print(\"didUpdateWidget\");\n  }\n\n  @override\n  void deactivate() {\n    super.deactivate();\n    print(\"deactive\");\n  }\n\n  @override\n  void dispose() {\n    super.dispose();\n    print(\"dispose\");\n  }\n\n  @override\n  void reassemble() {\n    super.reassemble();\n    print(\"reassemble\");\n  }\n\n  @override\n  void didChangeDependencies() {\n    super.didChangeDependencies();\n    print(\"didChangeDependencies\");\n  }\n\n}\n```\n\n接下来，我们创建一个新路由，在新路由中，我们只显示一个`CounterWidget`：\n\n```dart\nWidget build(BuildContext context) {\n  return CounterWidget();\n}\n```\n\n我们运行应用并打开该路由页面，在新路由页打开后，屏幕中央就会出现一个数字0，然后控制台日志输出：\n\n```\nI/flutter ( 5436): initState\nI/flutter ( 5436): didChangeDependencies\nI/flutter ( 5436): build\n```\n\n可以看到，在StatefulWidget插入到Widget树时首先`initState`方法会被调用。\n\n然后我们点击⚡️按钮热重载，控制台输出日志如下：\n\n```\nI/flutter ( 5436): reassemble\nI/flutter ( 5436): didUpdateWidget\nI/flutter ( 5436): build\n```\n\n可以看到此时` initState` 和`didChangeDependencies`都没有被调用，而此时`didUpdateWidget`被调用。\n\n接下来，我们在widget树中移除`CounterWidget`，将路由`build`方法改为：\n\n```dart\nWidget build(BuildContext context) {\n  //移除计数器 \n  //return CounterWidget();\n  //随便返回一个Text()\n  return Text(\"xxx\");\n}\n```\n\n然后热重载，日志如下：\n\n```\nI/flutter ( 5436): reassemble\nI/flutter ( 5436): deactive\nI/flutter ( 5436): dispose\n```\n\n我们可以看到，在`CounterWidget`从widget树中移除时，`deactive`和`dispose`会依次被调用。\n\n下面我们来看看各个回调函数：\n\n- `initState`：当Widget第一次插入到Widget树时会被调用，对于每一个State对象，Flutter framework只会调用一次该回调，所以，通常在该回调中做一些一次性的操作，如状态初始化、订阅子树的事件通知等。不能在该回调中调用`BuildContext.dependOnInheritedWidgetOfExactType`（该方法用于在Widget树上获取离当前widget最近的一个父级`InheritFromWidget`，关于`InheritedWidget`我们将在后面章节介绍），原因是在初始化完成后，Widget树中的`InheritFromWidget`也可能会发生变化，所以正确的做法应该在在`build（）`方法或`didChangeDependencies()`中调用它。\n- `didChangeDependencies()`：当State对象的依赖发生变化时会被调用；例如：在之前`build()` 中包含了一个`InheritedWidget`，然后在之后的`build()` 中`InheritedWidget`发生了变化，那么此时`InheritedWidget`的子widget的`didChangeDependencies()`回调都会被调用。典型的场景是当系统语言Locale或应用主题改变时，Flutter framework会通知widget调用此回调。\n- `build()`：此回调读者现在应该已经相当熟悉了，它主要是用于构建Widget子树的，会在如下场景被调用：\n\n  1. 在调用`initState()`之后。\n  2. 在调用`didUpdateWidget()`之后。\n  3. 在调用`setState()`之后。\n  4. 在调用`didChangeDependencies()`之后。\n  5. 在State对象从树中一个位置移除后（会调用deactivate）又重新插入到树的其它位置之后。\n- `reassemble()`：此回调是专门为了开发调试而提供的，在热重载(hot reload)时会被调用，此回调在Release模式下永远不会被调用。\n- `didUpdateWidget()`：在widget重新构建时，Flutter framework会调用`Widget.canUpdate`来检测Widget树中同一位置的新旧节点，然后决定是否需要更新，如果`Widget.canUpdate`返回`true`则会调用此回调。正如之前所述，`Widget.canUpdate`会在新旧widget的key和runtimeType同时相等时会返回true，也就是说在在新旧widget的key和runtimeType同时相等时`didUpdateWidget()`就会被调用。\n- `deactivate()`：当State对象从树中被移除时，会调用此回调。在一些场景下，Flutter framework会将State对象重新插到树中，如包含此State对象的子树在树的一个位置移动到另一个位置时（可以通过GlobalKey来实现）。如果移除后没有重新插入到树中则紧接着会调用`dispose()`方法。\n- `dispose()`：当State对象从树中被永久移除时调用；通常在此回调中释放资源。\n\nStatefulWidget生命周期如图3-2所示：\n\n![图3-2](../imgs/3-2.jpg)\n\n\n\n> **注意**：在继承`StatefulWidget`重写其方法时，对于包含`@mustCallSuper`标注的父类方法，都要在子类方法中先调用父类方法。\n\n\n### 为什么要将build方法放在State中，而不是放在StatefulWidget中？\n\n现在，我们回答之前提出的问题，为什么`build()`方法放在State（而不是`StatefulWidget`）中 ？这主要是为了提高开发的灵活性。如果将`build()`方法在`StatefulWidget`中则会有两个问题：\n\n- 状态访问不便。\n\n  试想一下，如果我们的`StatefulWidget`有很多状态，而每次状态改变都要调用`build`方法，由于状态是保存在State中的，如果`build`方法在`StatefulWidget`中，那么`build`方法和状态分别在两个类中，那么构建时读取状态将会很不方便！试想一下，如果真的将`build`方法放在StatefulWidget中的话，由于构建用户界面过程需要依赖State，所以`build`方法将必须加一个`State`参数，大概是下面这样：\n\n  ```dart\n    Widget build(BuildContext context, State state){\n        //state.counter\n        ...\n    }\n  ```\n\n  这样的话就只能将State的所有状态声明为公开的状态，这样才能在State类外部访问状态！但是，将状态设置为公开后，状态将不再具有私密性，这就会导致对状态的修改将会变的不可控。但如果将`build()`方法放在State中的话，构建过程不仅可以直接访问状态，而且也无需公开私有状态，这会非常方便。\n\n- 继承`StatefulWidget`不便。\n\n  例如，Flutter中有一个动画widget的基类`AnimatedWidget`，它继承自`StatefulWidget`类。`AnimatedWidget`中引入了一个抽象方法`build(BuildContext context)`，继承自`AnimatedWidget`的动画widget都要实现这个`build`方法。现在设想一下，如果`StatefulWidget` 类中已经有了一个`build`方法，正如上面所述，此时`build`方法需要接收一个state对象，这就意味着`AnimatedWidget`必须将自己的State对象(记为_animatedWidgetState)提供给其子类，因为子类需要在其`build`方法中调用父类的`build`方法，代码可能如下：\n\n  ```dart\n  class MyAnimationWidget extends AnimatedWidget{\n      @override\n      Widget build(BuildContext context, State state){\n        //由于子类要用到AnimatedWidget的状态对象_animatedWidgetState，\n        //所以AnimatedWidget必须通过某种方式将其状态对象_animatedWidgetState\n        //暴露给其子类   \n        super.build(context, _animatedWidgetState)\n      }\n  }\n  ```\n\n  这样很显然是不合理的，因为\n\n  1. `AnimatedWidget`的状态对象是`AnimatedWidget`内部实现细节，不应该暴露给外部。\n  2. 如果要将父类状态暴露给子类，那么必须得有一种传递机制，而做这一套传递机制是无意义的，因为父子类之间状态的传递和子类本身逻辑是无关的。\n\n综上所述，可以发现，对于`StatefulWidget`，将`build`方法放在State中，可以给开发带来很大的灵活性。\n\n\n\n## 3.1.7 在Widget树中获取State对象\n\n由于StatefulWidget的的具体逻辑都在其State中，所以很多时候，我们需要获取StatefulWidget对应的State对象来调用一些方法，比如`Scaffold`组件对应的状态类`ScaffoldState`中就定义了打开SnackBar(路由页底部提示条)的方法。我们有两种方法在子widget树中获取父级StatefulWidget的State对象。\n\n### 通过Context获取\n\n`context`对象有一个`findAncestorStateOfType()`方法，该方法可以从当前节点沿着widget树向上查找指定类型的StatefulWidget对应的State对象。下面是实现打开SnackBar的示例：\n\n```dart\nScaffold(\n  appBar: AppBar(\n    title: Text(\"子树中获取State对象\"),\n  ),\n  body: Center(\n    child: Builder(builder: (context) {\n      return RaisedButton(\n        onPressed: () {\n          // 查找父级最近的Scaffold对应的ScaffoldState对象\n          ScaffoldState _state = context.findAncestorStateOfType<ScaffoldState>();\n          //调用ScaffoldState的showSnackBar来弹出SnackBar\n          _state.showSnackBar(\n            SnackBar(\n              content: Text(\"我是SnackBar\"),\n            ),\n          );\n        },\n        child: Text(\"显示SnackBar\"),\n      );\n    }),\n  ),\n);\n```\n\n上面示例运行后，点击”显示SnackBar“，效果如图3-1-2所示：\n\n![图3-1-2](../imgs/3-1-2.png)\n\n一般来说，如果StatefulWidget的状态是私有的（不应该向外部暴露），那么我们代码中就不应该去直接获取其State对象；如果StatefulWidget的状态是希望暴露出的（通常还有一些组件的操作方法），我们则可以去直接获取其State对象。但是通过`context.findAncestorStateOfType`获取StatefulWidget的状态的方法是通用的，我们并不能在语法层面指定StatefulWidget的状态是否私有，所以在Flutter开发中便有了一个默认的约定：如果StatefulWidget的状态是希望暴露出的，应当在StatefulWidget中提供一个`of`静态方法来获取其State对象，开发者便可直接通过该方法来获取；如果State不希望暴露，则不提供`of`方法。这个约定在Flutter SDK里随处可见。所以，上面示例中的`Scaffold`也提供了一个`of`方法，我们其实是可以直接调用它的：\n\n```dart\n...//省略无关代码\n// 直接通过of静态方法来获取ScaffoldState \nScaffoldState _state=Scaffold.of(context); \n_state.showSnackBar(\n  SnackBar(\n    content: Text(\"我是SnackBar\"),\n  ),\n);\n```\n\n### 通过GlobalKey\n\nFlutter还有一种通用的获取`State`对象的方法——通过GlobalKey来获取！ 步骤分两步：\n\n1. 给目标`StatefulWidget`添加`GlobalKey`。\n\n   ```dart\n   //定义一个globalKey, 由于GlobalKey要保持全局唯一性，我们使用静态变量存储\n   static GlobalKey<ScaffoldState> _globalKey= GlobalKey();\n   ...\n   Scaffold(\n       key: _globalKey , //设置key\n       ...  \n   )\n   ```\n\n2. 通过`GlobalKey`来获取`State`对象\n\n   ```dart\n   _globalKey.currentState.openDrawer()\n   ```\n\nGlobalKey是Flutter提供的一种在整个APP中引用element的机制。如果一个widget设置了`GlobalKey`，那么我们便可以通过`globalKey.currentWidget`获得该widget对象、`globalKey.currentElement`来获得widget对应的element对象，如果当前widget是`StatefulWidget`，则可以通过`globalKey.currentState`来获得该widget对应的state对象。\n\n> 注意：使用GlobalKey开销较大，如果有其他可选方案，应尽量避免使用它。另外同一个GlobalKey在整个widget树中必须是唯一的，不能重复。\n\n## 3.1.8 Flutter SDK内置组件库介绍\n\nFlutter提供了一套丰富、强大的基础组件，在基础组件库之上Flutter又提供了一套Material风格（Android默认的视觉风格）和一套Cupertino风格（iOS视觉风格）的组件库。要使用基础组件库，需要先导入：\n\n```dart\nimport 'package:flutter/widgets.dart';\n```\n\n下面我们介绍一下常用的组件。\n\n#### 基础组件\n\n- [`Text`](https://docs.flutter.io/flutter/widgets/Text-class.html)：该组件可让您创建一个带格式的文本。\n- [`Row`](https://docs.flutter.io/flutter/widgets/Row-class.html)、 [`Column`](https://docs.flutter.io/flutter/widgets/Column-class.html)： 这些具有弹性空间的布局类Widget可让您在水平（Row）和垂直（Column）方向上创建灵活的布局。其设计是基于Web开发中的Flexbox布局模型。\n- [`Stack`](https://docs.flutter.io/flutter/widgets/Stack-class.html)： 取代线性布局 (译者语：和Android中的`FrameLayout`相似)，[`Stack`](https://docs.flutter.io/flutter/widgets/Stack-class.html)允许子 widget 堆叠， 你可以使用 [`Positioned`](https://docs.flutter.io/flutter/widgets/Positioned-class.html) 来定位他们相对于`Stack`的上下左右四条边的位置。Stacks是基于Web开发中的绝对定位（absolute positioning )布局模型设计的。\n- [`Container`](https://docs.flutter.io/flutter/widgets/Container-class.html)： [`Container`](https://docs.flutter.io/flutter/widgets/Container-class.html) 可让您创建矩形视觉元素。container 可以装饰一个[`BoxDecoration`](https://docs.flutter.io/flutter/painting/BoxDecoration-class.html), 如 background、一个边框、或者一个阴影。 [`Container`](https://docs.flutter.io/flutter/widgets/Container-class.html) 也可以具有边距（margins）、填充(padding)和应用于其大小的约束(constraints)。另外， [`Container`](https://docs.flutter.io/flutter/widgets/Container-class.html)可以使用矩阵在三维空间中对其进行变换。\n\n\n#### Material组件\n\nFlutter提供了一套丰富的Material组件，它可以帮助我们构建遵循Material Design设计规范的应用程序。Material应用程序以[`MaterialApp`](https://docs.flutter.io/flutter/material/MaterialApp-class.html) 组件开始， 该组件在应用程序的根部创建了一些必要的组件，比如`Theme`组件，它用于配置应用的主题。 是否使用[`MaterialApp`](https://docs.flutter.io/flutter/material/MaterialApp-class.html)完全是可选的，但是使用它是一个很好的做法。在之前的示例中，我们已经使用过多个Material 组件了，如：`Scaffold`、`AppBar`、`FlatButton`等。要使用Material 组件，需要先引入它：\n\n```dart\nimport 'package:flutter/material.dart';\n```\n\n#### Cupertino组件\n\nFlutter也提供了一套丰富的Cupertino风格的组件，尽管目前还没有Material 组件那么丰富，但是它仍在不断的完善中。值得一提的是在Material 组件库中有一些组件可以根据实际运行平台来切换表现风格，比如`MaterialPageRoute`，在路由切换时，如果是Android系统，它将会使用Android系统默认的页面切换动画(从底向上)；如果是iOS系统，它会使用iOS系统默认的页面切换动画（从右向左）。由于在前面的示例中还没有Cupertino组件的示例，下面我们实现一个简单的Cupertino组件风格的页面：\n\n```dart\n//导入cupertino widget库\nimport 'package:flutter/cupertino.dart';\n\nclass CupertinoTestRoute extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return CupertinoPageScaffold(\n      navigationBar: CupertinoNavigationBar(\n        middle: Text(\"Cupertino Demo\"),\n      ),\n      child: Center(\n        child: CupertinoButton(\n            color: CupertinoColors.activeBlue,\n            child: Text(\"Press\"),\n            onPressed: () {}\n        ),\n      ),\n    );\n  }\n}\n```\n\n下面（图3-3）是在iPhoneX上页面效果截图：\n\n![图3-3](../imgs/3-3.png)\n\n\n\n### 关于示例\n\n本章后面章节的示例中会使用一些布局类组件，如`Scaffold`、`Row`、`Column`等，这些组件将在后面“布局类组件”一章中详细介绍，读者可以先不用关注。\n\n### 总结\n\nFlutter提供了丰富的组件，在实际的开发中你可以根据需要随意使用它们，而不必担心引入过多组件库会让你的应用安装包变大，这不是web开发，dart在编译时只会编译你使用了的代码。由于Material和Cupertino都是在基础组件库之上的，所以如果我们的应用中引入了这两者之一，则不需要再引入`flutter/widgets.dart`了，因为它们内部已经引入过了。\n\n\n\n\n\n"
  },
  {
    "path": "src/chapter3/img_and_icon.md",
    "content": "## 3.5 图片及ICON\n\n## 3.5.1 图片\n\nFlutter中，我们可以通过`Image`组件来加载并显示图片，`Image`的数据源可以是asset、文件、内存以及网络。\n\n### ImageProvider\n\n`ImageProvider` 是一个抽象类，主要定义了图片数据获取的接口`load()`，从不同的数据源获取图片需要实现不同的`ImageProvider` ，如`AssetImage`是实现了从Asset中加载图片的ImageProvider，而`NetworkImage`实现了从网络加载图片的ImageProvider。\n\n### Image\n\n`Image` widget有一个必选的`image`参数，它对应一个ImageProvider。下面我们分别演示一下如何从asset和网络加载图片。\n\n#### 从asset中加载图片\n\n1. 在工程根目录下创建一个`images目录`，并将图片avatar.png拷贝到该目录。\n\n2. 在`pubspec.yaml`中的`flutter`部分添加如下内容：\n\n   ```yaml\n     assets:\n       - images/avatar.png\n   ```\n  > 注意: 由于 yaml 文件对缩进严格，所以必须严格按照每一层两个空格的方式进行缩进，此处assets前面应有两个空格。\n\n3. 加载该图片\n\n   ```dart\n   Image(\n     image: AssetImage(\"images/avatar.png\"),\n     width: 100.0\n   );\n   ```\n   Image也提供了一个快捷的构造函数`Image.asset`用于从asset中加载、显示图片：\n\n   ```dart\n   Image.asset(\"images/avatar.png\",\n     width: 100.0,\n   )\n   ```\n\n\n\n#### 从网络加载图片\n\n```dart\nImage(\n  image: NetworkImage(\n      \"https://avatars2.githubusercontent.com/u/20411648?s=460&v=4\"),\n  width: 100.0,\n)\n```\n\nImage也提供了一个快捷的构造函数`Image.network`用于从网络加载、显示图片：\n\n```dart\nImage.network(\n  \"https://avatars2.githubusercontent.com/u/20411648?s=460&v=4\",\n  width: 100.0,\n)\n```\n\n运行上面两个示例，图片加载成功后如图3-17所示：\n\n![图3-17](../imgs/3-17.png)\n\n\n\n#### 参数\n\n`Image`在显示图片时定义了一系列参数，通过这些参数我们可以控制图片的显示外观、大小、混合效果等。我们看一下Image的主要参数：\n\n```dart\nconst Image({\n  ...\n  this.width, //图片的宽\n  this.height, //图片高度\n  this.color, //图片的混合色值\n  this.colorBlendMode, //混合模式\n  this.fit,//缩放模式\n  this.alignment = Alignment.center, //对齐方式\n  this.repeat = ImageRepeat.noRepeat, //重复方式\n  ...\n})\n```\n\n- `width`、`height`：用于设置图片的宽、高，当不指定宽高时，图片会根据当前父容器的限制，尽可能的显示其原始大小，如果只设置`width`、`height`的其中一个，那么另一个属性默认会按比例缩放，但可以通过下面介绍的`fit`属性来指定适应规则。\n\n- `fit`：该属性用于在图片的显示空间和图片本身大小不同时指定图片的适应模式。适应模式是在`BoxFit`中定义，它是一个枚举类型，有如下值：\n\n  - `fill`：会拉伸填充满显示空间，图片本身长宽比会发生变化，图片会变形。\n  - `cover`：会按图片的长宽比放大后居中填满显示空间，图片不会变形，超出显示空间部分会被剪裁。\n  - `contain`：这是图片的默认适应规则，图片会在保证图片本身长宽比不变的情况下缩放以适应当前显示空间，图片不会变形。\n  - `fitWidth`：图片的宽度会缩放到显示空间的宽度，高度会按比例缩放，然后居中显示，图片不会变形，超出显示空间部分会被剪裁。\n  - `fitHeight`：图片的高度会缩放到显示空间的高度，宽度会按比例缩放，然后居中显示，图片不会变形，超出显示空间部分会被剪裁。\n  - `none`：图片没有适应策略，会在显示空间内显示图片，如果图片比显示空间大，则显示空间只会显示图片中间部分。\n\n  一图胜万言！ 我们对一个宽高相同的头像图片应用不同的`fit`值，效果如图3-18所示：\n\n  ![图3-18](../imgs/3-18.png)\n  \n\n\n- `color`和 `colorBlendMode`：在图片绘制时可以对每一个像素进行颜色混合处理，`color`指定混合色，而`colorBlendMode`指定混合模式，下面是一个简单的示例：\n\n  ```dart\n  Image(\n    image: AssetImage(\"images/avatar.png\"),\n    width: 100.0,\n    color: Colors.blue,\n    colorBlendMode: BlendMode.difference,\n  );\n  ```\n\n运行效果如图3-19所示（彩色）:\n\n![图3-19](../imgs/3-19.png)\n\n- `repeat`：当图片本身大小小于显示空间时，指定图片的重复规则。简单示例如下：\n\n  ```dart\n  Image(\n    image: AssetImage(\"images/avatar.png\"),\n    width: 100.0,\n    height: 200.0,\n    repeat: ImageRepeat.repeatY ,\n  )\n  ```\n  运行后效果如图3-20所示：\n\n  ![图3-20](../imgs/3-20.png)\n\n完整的示例代码如下：\n\n```dart\nimport 'package:flutter/material.dart';\n\nclass ImageAndIconRoute extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    var img=AssetImage(\"imgs/avatar.png\");\n    return SingleChildScrollView(\n      child: Column(\n        children: <Image>[\n          Image(\n            image: img,\n            height: 50.0,\n            width: 100.0,\n            fit: BoxFit.fill,\n          ),\n          Image(\n            image: img,\n            height: 50,\n            width: 50.0,\n            fit: BoxFit.contain,\n          ),\n          Image(\n            image: img,\n            width: 100.0,\n            height: 50.0,\n            fit: BoxFit.cover,\n          ),\n          Image(\n            image: img,\n            width: 100.0,\n            height: 50.0,\n            fit: BoxFit.fitWidth,\n          ),\n          Image(\n            image: img,\n            width: 100.0,\n            height: 50.0,\n            fit: BoxFit.fitHeight,\n          ),\n          Image(\n            image: img,\n            width: 100.0,\n            height: 50.0,\n            fit: BoxFit.scaleDown,\n          ),\n          Image(\n            image: img,\n            height: 50.0,\n            width: 100.0,\n            fit: BoxFit.none,\n          ),\n          Image(\n            image: img,\n            width: 100.0,\n            color: Colors.blue,\n            colorBlendMode: BlendMode.difference,\n            fit: BoxFit.fill,\n          ),\n          Image(\n            image: img,\n            width: 100.0,\n            height: 200.0,\n            repeat: ImageRepeat.repeatY ,\n          )\n        ].map((e){\n          return Row(\n            children: <Widget>[\n              Padding(\n                padding: EdgeInsets.all(16.0),\n                child: SizedBox(\n                  width: 100,\n                  child: e,\n                ),\n              ),\n              Text(e.fit.toString())\n            ],\n          );\n        }).toList()\n      ),\n    );\n  }\n}\n```\n\n### Image缓存\n\nFlutter框架对加载过的图片是有缓存的（内存），默认最大缓存数量是1000，最大缓存空间为100M。关于Image的详细内容及原理我们将会在后面进阶部分深入介绍。\n\n## 3.5.2 ICON\n\nFlutter中，可以像Web开发一样使用iconfont，iconfont即“字体图标”，它是将图标做成字体文件，然后通过指定不同的字符而显示不同的图片。\n\n> 在字体文件中，每一个字符都对应一个位码，而每一个位码对应一个显示字形，不同的字体就是指字形不同，即字符对应的字形是不同的。而在iconfont中，只是将位码对应的字形做成了图标，所以不同的字符最终就会渲染成不同的图标。\n\n在Flutter开发中，iconfont和图片相比有如下优势：\n\n1. 体积小：可以减小安装包大小。\n2. 矢量的：iconfont都是矢量图标，放大不会影响其清晰度。\n3. 可以应用文本样式：可以像文本一样改变字体图标的颜色、大小对齐等。\n4. 可以通过TextSpan和文本混用。\n\n##### 使用Material Design字体图标\n\nFlutter默认包含了一套Material Design的字体图标，在`pubspec.yaml`文件中的配置如下\n\n```yaml\nflutter:\n  uses-material-design: true\n```\n\nMaterial Design所有图标可以在其官网查看：https://material.io/tools/icons/\n\n我们看一个简单的例子：\n\n```dart\nString icons = \"\";\n// accessible: &#xE914; or 0xE914 or E914\nicons += \"\\uE914\";\n// error: &#xE000; or 0xE000 or E000\nicons += \" \\uE000\";\n// fingerprint: &#xE90D; or 0xE90D or E90D\nicons += \" \\uE90D\";\n\nText(icons,\n  style: TextStyle(\n      fontFamily: \"MaterialIcons\",\n      fontSize: 24.0,\n      color: Colors.green\n  ),\n);\n```\n\n运行效果如图3-21所示：\n\n![图3-21](../imgs/3-21.png)\n\n通过这个示例可以看到，使用图标就像使用文本一样，但是这种方式需要我们提供每个图标的码点，这并对开发者不友好，所以，Flutter封装了`IconData`和`Icon`来专门显示字体图标，上面的例子也可以用如下方式实现：\n\n```dart\nRow(\n  mainAxisAlignment: MainAxisAlignment.center,\n  children: <Widget>[\n    Icon(Icons.accessible,color: Colors.green,),\n    Icon(Icons.error,color: Colors.green,),\n    Icon(Icons.fingerprint,color: Colors.green,),\n  ],\n)\n```\n\n`Icons`类中包含了所有Material Design图标的`IconData`静态变量定义。\n\n\n\n#### 使用自定义字体图标\n\n我们也可以使用自定义字体图标。iconfont.cn上有很多字体图标素材，我们可以选择自己需要的图标打包下载后，会生成一些不同格式的字体文件，在Flutter中，我们使用ttf格式即可。\n\n假设我们项目中需要使用一个书籍图标和微信图标，我们打包下载后导入：\n\n1. 导入字体图标文件；这一步和导入字体文件相同，假设我们的字体图标文件保存在项目根目录下，路径为\"fonts/iconfont.ttf\"：\n\n   ```yaml\n   fonts:\n     - family: myIcon  #指定一个字体名\n       fonts:\n         - asset: fonts/iconfont.ttf\n   ```\n\n2. 为了使用方便，我们定义一个`MyIcons`类，功能和`Icons`类一样：将字体文件中的所有图标都定义成静态变量：\n\n   ```dart\n   class MyIcons{\n     // book 图标\n     static const IconData book = const IconData(\n         0xe614, \n         fontFamily: 'myIcon', \n         matchTextDirection: true\n     );\n     // 微信图标\n     static const IconData wechat = const IconData(\n         0xec7d,  \n         fontFamily: 'myIcon', \n         matchTextDirection: true\n     );\n   }\n   ```\n\n3. 使用\n\n   ```dart\n   Row(\n     mainAxisAlignment: MainAxisAlignment.center,\n     children: <Widget>[\n       Icon(MyIcons.book,color: Colors.purple,),\n       Icon(MyIcons.wechat,color: Colors.green,),\n     ],\n   )\n   ```\n\n   运行后效果如图3-22所示：\n\n   ![图3-22](../imgs/3-22.png)\n\n"
  },
  {
    "path": "src/chapter3/index.md",
    "content": "# 基础Widget\n\n本节介绍一下Flutter中常用的一些基础widget，由于大多数widget的属性都比较多，我们在介绍widget时会着重介绍常用的属性，而不会像API文档一样所有属性都介绍，关于属性详细的信息请参考Flutter SDK文档。\n\n## 本章目录\n\n* [3.1：Widget简介](flutter_widget_intro.md)\n* [3.2：状态管理](state_manage.md)\n* [3.3：文本、字体样式](text.md)\n* [3.4：按钮](buttons.md)      \n* [3.5：图片和Icon](img_and_icon.md)   \n* [3.6：单选框和复选框](radio_and_checkbox.md)   \n* [3.7：输入框和表单](input_and_form.md) \n* [3.8：进度指示器](progress.md)  \n"
  },
  {
    "path": "src/chapter3/input_and_form.md",
    "content": "\n# 3.7 输入框及表单\n\nMaterial组件库中提供了输入框组件`TextField`和表单组件`Form`。下面我们分别介绍一下。\n\n## 3.7.1 TextField\n\n`TextField`用于文本输入，它提供了很多属性，我们先简单介绍一下主要属性的作用，然后通过几个示例来演示一下关键属性的用法。\n\n```dart\nconst TextField({\n  ...\n  TextEditingController controller, \n  FocusNode focusNode,\n  InputDecoration decoration = const InputDecoration(),\n  TextInputType keyboardType,\n  TextInputAction textInputAction,\n  TextStyle style,\n  TextAlign textAlign = TextAlign.start,\n  bool autofocus = false,\n  bool obscureText = false,\n  int maxLines = 1,\n  int maxLength,\n  bool maxLengthEnforced = true,\n  ValueChanged<String> onChanged,\n  VoidCallback onEditingComplete,\n  ValueChanged<String> onSubmitted,\n  List<TextInputFormatter> inputFormatters,\n  bool enabled,\n  this.cursorWidth = 2.0,\n  this.cursorRadius,\n  this.cursorColor,\n  ...\n})\n```\n\n\n\n- `controller`：编辑框的控制器，通过它可以设置/获取编辑框的内容、选择编辑内容、监听编辑文本改变事件。大多数情况下我们都需要显式提供一个`controller`来与文本框交互。如果没有提供`controller`，则`TextField`内部会自动创建一个。\n\n- `focusNode`：用于控制`TextField`是否占有当前键盘的输入焦点。它是我们和键盘交互的一个句柄（handle）。\n\n- `InputDecoration`：用于控制`TextField`的外观显示，如提示文本、背景颜色、边框等。\n\n- `keyboardType`：用于设置该输入框默认的键盘输入类型，取值如下：\n\n  \n\n  | TextInputType枚举值 | 含义                                                |\n  | ------------------- | --------------------------------------------------- |\n  | text                | 文本输入键盘                                        |\n  | multiline           | 多行文本，需和maxLines配合使用(设为null或大于1)     |\n  | number              | 数字；会弹出数字键盘                                |\n  | phone               | 优化后的电话号码输入键盘；会弹出数字键盘并显示“* #” |\n  | datetime            | 优化后的日期输入键盘；Android上会显示“: -”          |\n  | emailAddress        | 优化后的电子邮件地址；会显示“@ .”                   |\n  | url                 | 优化后的url输入键盘； 会显示“/ .”                   |\n\n  \n\n- `textInputAction`：键盘动作按钮图标(即回车键位图标)，它是一个枚举值，有多个可选值，全部的取值列表读者可以查看API文档，下面是当值为`TextInputAction.search`时，原生Android系统下键盘样式如图3-24所示：\n\n  ![图3-24](../imgs/3-24.png)\n\n- `style`：正在编辑的文本样式。\n\n- `textAlign`: 输入框内编辑文本在水平方向的对齐方式。\n\n- `autofocus`: 是否自动获取焦点。\n\n- `obscureText`：是否隐藏正在编辑的文本，如用于输入密码的场景等，文本内容会用“•”替换。\n\n- `maxLines`：输入框的最大行数，默认为1；如果为`null`，则无行数限制。\n\n- `maxLength`和`maxLengthEnforced` ：`maxLength`代表输入框文本的最大长度，设置后输入框右下角会显示输入的文本计数。`maxLengthEnforced`决定当输入文本长度超过`maxLength`时是否阻止输入，为`true`时会阻止输入，为`false`时不会阻止输入但输入框会变红。\n\n- `onChange`：输入框内容改变时的回调函数；注：内容改变事件也可以通过`controller`来监听。\n\n- `onEditingComplete`和`onSubmitted`：这两个回调都是在输入框输入完成时触发，比如按了键盘的完成键（对号图标）或搜索键（🔍图标）。不同的是两个回调签名不同，`onSubmitted`回调是`ValueChanged<String>`类型，它接收当前输入内容做为参数，而`onEditingComplete`不接收参数。\n\n- `inputFormatters`：用于指定输入格式；当用户输入内容改变时，会根据指定的格式来校验。\n\n- `enable`：如果为`false`，则输入框会被禁用，禁用状态不接收输入和事件，同时显示禁用态样式（在其`decoration`中定义）。\n\n- `cursorWidth`、`cursorRadius`和`cursorColor`：这三个属性是用于自定义输入框光标宽度、圆角和颜色的。\n\n#### 示例：登录输入框\n\n##### 布局\n\n```dart\nColumn(\n        children: <Widget>[\n          TextField(\n            autofocus: true,\n            decoration: InputDecoration(\n                labelText: \"用户名\",\n                hintText: \"用户名或邮箱\",\n                prefixIcon: Icon(Icons.person)\n            ),\n          ),\n          TextField(\n            decoration: InputDecoration(\n                labelText: \"密码\",\n                hintText: \"您的登录密码\",\n                prefixIcon: Icon(Icons.lock)\n            ),\n            obscureText: true,\n          ),\n        ],\n);\n```\n\n运行后，效果如图3-25所示：\n\n![图3-25](../imgs/3-25.png)\n\n##### 获取输入内容\n\n获取输入内容有两种方式：\n\n1. 定义两个变量，用于保存用户名和密码，然后在`onChange`触发时，各自保存一下输入内容。\n2. 通过`controller`直接获取。\n\n第一种方式比较简单，不在举例，我们来重点看一下第二种方式，我们以用户名输入框举例：\n\n定义一个`controller`：\n\n```dart\n//定义一个controller\nTextEditingController _unameController = TextEditingController();\n```\n\n然后设置输入框controller：\n\n```dart\nTextField(\n    autofocus: true,\n    controller: _unameController, //设置controller\n    ...\n)\n```\n\n通过controller获取输入框内容\n\n```dart\nprint(_unameController.text)\n```\n\n##### 监听文本变化\n\n监听文本变化也有两种方式：\n\n1. 设置`onChange`回调，如：\n\n   ```dart\n   TextField(\n       autofocus: true,\n       onChanged: (v) {\n         print(\"onChange: $v\");\n       }\n   )\n   ```\n\n2. 通过`controller`监听，如：\n\n   ```dart\n   @override\n   void initState() {\n     //监听输入改变  \n     _unameController.addListener((){\n       print(_unameController.text);\n     });\n   }\n   ```\n\n两种方式相比，`onChanged`是专门用于监听文本变化，而`controller`的功能却多一些，除了能监听文本变化外，它还可以设置默认值、选择文本，下面我们看一个例子：\n\n创建一个`controller`:\n\n```dart\nTextEditingController _selectionController =  TextEditingController();\n```\n\n设置默认值，并从第三个字符开始选中后面的字符\n\n```dart\n_selectionController.text=\"hello world!\";\n_selectionController.selection=TextSelection(\n    baseOffset: 2,\n    extentOffset: _selectionController.text.length\n);\n```\n\n设置`controlle`r:\n\n```dart\nTextField(\n  controller: _selectionController,\n)\n```\n\n运行效果如图3-26所示：\n\n![图3-26](../imgs/3-26.png)\n\n##### 控制焦点\n\n焦点可以通过`FocusNode`和`FocusScopeNode`来控制，默认情况下，焦点由`FocusScope`来管理，它代表焦点控制范围，可以在这个范围内可以通过`FocusScopeNode`在输入框之间移动焦点、设置默认焦点等。我们可以通过`FocusScope.of(context)` 来获取Widget树中默认的`FocusScopeNode`。下面看一个示例，在此示例中创建两个`TextField`，第一个自动获取焦点，然后创建两个按钮：\n\n- 点击第一个按钮可以将焦点从第一个`TextField`挪到第二个`TextField`。\n- 点击第二个按钮可以关闭键盘。\n\n我们要实现的效果如图3-27所示：\n\n![图3-27](../imgs/3-27.png)\n\n代码如下：\n\n```dart\nclass FocusTestRoute extends StatefulWidget {\n  @override\n  _FocusTestRouteState createState() => new _FocusTestRouteState();\n}\n\nclass _FocusTestRouteState extends State<FocusTestRoute> {\n  FocusNode focusNode1 = new FocusNode();\n  FocusNode focusNode2 = new FocusNode();\n  FocusScopeNode focusScopeNode;\n\n  @override\n  Widget build(BuildContext context) {\n    return Padding(\n      padding: EdgeInsets.all(16.0),\n      child: Column(\n        children: <Widget>[\n          TextField(\n            autofocus: true, \n            focusNode: focusNode1,//关联focusNode1\n            decoration: InputDecoration(\n                labelText: \"input1\"\n            ),\n          ),\n          TextField(\n            focusNode: focusNode2,//关联focusNode2\n            decoration: InputDecoration(\n                labelText: \"input2\"\n            ),\n          ),\n          Builder(builder: (ctx) {\n            return Column(\n              children: <Widget>[\n                RaisedButton(\n                  child: Text(\"移动焦点\"),\n                  onPressed: () {\n                    //将焦点从第一个TextField移到第二个TextField\n                    // 这是一种写法 FocusScope.of(context).requestFocus(focusNode2);\n                    // 这是第二种写法\n                    if(null == focusScopeNode){\n                      focusScopeNode = FocusScope.of(context);\n                    }\n                    focusScopeNode.requestFocus(focusNode2);\n                  },\n                ),\n                RaisedButton(\n                  child: Text(\"隐藏键盘\"),\n                  onPressed: () {\n                    // 当所有编辑框都失去焦点时键盘就会收起  \n                    focusNode1.unfocus();\n                    focusNode2.unfocus();\n                  },\n                ),\n              ],\n            );\n          },\n          ),\n        ],\n      ),\n    );\n  }\n\n}\n```\n\n`FocusNode`和`FocusScopeNode`还有一些其它的方法，详情可以查看API文档。\n\n##### 监听焦点状态改变事件\n\n`FocusNode`继承自`ChangeNotifier`，通过`FocusNode`可以监听焦点的改变事件，如：\n\n```dart\n...\n// 创建 focusNode   \nFocusNode focusNode = new FocusNode();\n...\n// focusNode绑定输入框   \nTextField(focusNode: focusNode);\n...\n// 监听焦点变化    \nfocusNode.addListener((){\n   print(focusNode.hasFocus);\n});\n```\n\n获得焦点时`focusNode.hasFocus`值为`true`，失去焦点时为`false`。\n\n##### 自定义样式\n\n虽然我们可以通过`decoration`属性来定义输入框样式，下面以自定义输入框下划线颜色为例来介绍一下：\n\n```dart\nTextField(\n  decoration: InputDecoration(\n    labelText: \"请输入用户名\",\n    prefixIcon: Icon(Icons.person),\n    // 未获得焦点下划线设为灰色\n    enabledBorder: UnderlineInputBorder(\n      borderSide: BorderSide(color: Colors.grey),\n    ),\n    //获得焦点下划线设为蓝色\n    focusedBorder: UnderlineInputBorder(\n      borderSide: BorderSide(color: Colors.blue),\n    ),\n  ),\n),\n```\n\n上面代码我们直接通过InputDecoration的enabledBorder和focusedBorder来分别设置了输入框在未获取焦点和获得焦点后的下划线颜色。另外，我们也可以通过主题来自定义输入框的样式，下面我们探索一下如何在不使用enabledBorder和focusedBorder的情况下来自定义下滑线颜色。\n\n由于`TextField`在绘制下划线时使用的颜色是主题色里面的`hintColor`，但提示文本颜色也是用的`hintColor`， 如果我们直接修改`hintColor`，那么下划线和提示文本的颜色都会变。值得高兴的是`decoration`中可以设置`hintStyle`，它可以覆盖`hintColor`，并且主题中可以通过`inputDecorationTheme`来设置输入框默认的`decoration`。所以我们可以通过主题来自定义，代码如下：\n\n```dart\nTheme(\n  data: Theme.of(context).copyWith(\n      hintColor: Colors.grey[200], //定义下划线颜色\n      inputDecorationTheme: InputDecorationTheme(\n          labelStyle: TextStyle(color: Colors.grey),//定义label字体样式\n          hintStyle: TextStyle(color: Colors.grey, fontSize: 14.0)//定义提示文本样式\n      )\n  ),\n  child: Column(\n    children: <Widget>[\n      TextField(\n        decoration: InputDecoration(\n            labelText: \"用户名\",\n            hintText: \"用户名或邮箱\",\n            prefixIcon: Icon(Icons.person)\n        ),\n      ),\n      TextField(\n        decoration: InputDecoration(\n            prefixIcon: Icon(Icons.lock),\n            labelText: \"密码\",\n            hintText: \"您的登录密码\",\n            hintStyle: TextStyle(color: Colors.grey, fontSize: 13.0)\n        ),\n        obscureText: true,\n      )\n    ],\n  )\n)\n```\n\n运行效果如图3-28所示：\n\n![图3-28](../imgs/3-28.png)\n\n我们成功的自定义了下划线颜色和提问文字样式，细心的读者可能已经发现，通过这种方式自定义后，输入框在获取焦点时，`labelText`不会高亮显示了，正如上图中的\"用户名\"本应该显示蓝色，但现在却显示为灰色，并且我们还是无法定义下划线宽度。另一种灵活的方式是直接隐藏掉`TextField`本身的下划线，然后通过`Container`去嵌套定义样式，如:\n\n```dart\nContainer(\n  child: TextField(\n    keyboardType: TextInputType.emailAddress,\n    decoration: InputDecoration(\n        labelText: \"Email\",\n        hintText: \"电子邮件地址\",\n        prefixIcon: Icon(Icons.email),\n        border: InputBorder.none //隐藏下划线\n    )\n  ),\n  decoration: BoxDecoration(\n      // 下滑线浅灰色，宽度1像素\n      border: Border(bottom: BorderSide(color: Colors.grey[200], width: 1.0))\n  ),\n)\n```\n\n运行效果：\n\n![image-20180904150511545](https://cdn.jsdelivr.net/gh/flutterchina/flutter-in-action@1.0/docs/imgs/image-20180904150511545.png)\n\n通过这种组件组合的方式，也可以定义背景圆角等。一般来说，优先通过`decoration`来自定义样式，如果`decoration`实现不了，再用widget组合的方式。\n\n> 思考题：在这个示例中，下划线颜色是固定的，所以获得焦点后颜色仍然为灰色，如何实现点击后下滑线也变色呢？\n\n## 3.7.2 表单Form\n\n实际业务中，在正式向服务器提交数据前，都会对各个输入框数据进行合法性校验，但是对每一个`TextField`都分别进行校验将会是一件很麻烦的事。还有，如果用户想清除一组`TextField`的内容，除了一个一个清除有没有什么更好的办法呢？为此，Flutter提供了一个`Form` 组件，它可以对输入框进行分组，然后进行一些统一操作，如输入内容校验、输入框重置以及输入内容保存。\n\n#### Form\n\n`Form`继承自`StatefulWidget`对象，它对应的状态类为`FormState`。我们先看看`Form`类的定义：\n\n```dart\nForm({\n  @required Widget child,\n  bool autovalidate = false,\n  WillPopCallback onWillPop,\n  VoidCallback onChanged,\n})\n```\n\n- `autovalidate`：是否自动校验输入内容；当为`true`时，每一个子FormField内容发生变化时都会自动校验合法性，并直接显示错误信息。否则，需要通过调用`FormState.validate()`来手动校验。\n- `onWillPop`：决定`Form`所在的路由是否可以直接返回（如点击返回按钮），该回调返回一个`Future`对象，如果Future的最终结果是`false`，则当前路由不会返回；如果为`true`，则会返回到上一个路由。此属性通常用于拦截返回按钮。\n- `onChanged`：`Form`的任意一个子`FormField`内容发生变化时会触发此回调。\n\n\n\n#### FormField\n\n`Form`的子孙元素必须是`FormField`类型，`FormField`是一个抽象类，定义几个属性，`FormState`内部通过它们来完成操作，`FormField`部分定义如下：\n\n```dart\nconst FormField({\n  ...\n  FormFieldSetter<T> onSaved, //保存回调\n  FormFieldValidator<T>  validator, //验证回调\n  T initialValue, //初始值\n  bool autovalidate = false, //是否自动校验。\n})\n```\n\n为了方便使用，Flutter提供了一个`TextFormField`组件，它继承自`FormField`类，也是`TextField`的一个包装类，所以除了`FormField`定义的属性之外，它还包括`TextField`的属性。\n\n#### FormState \n\n`FormState`为`Form`的`State`类，可以通过`Form.of()`或`GlobalKey`获得。我们可以通过它来对`Form`的子孙`FormField`进行统一操作。我们看看其常用的三个方法：\n\n- `FormState.validate()`：调用此方法后，会调用`Form`子孙`FormField的validate`回调，如果有一个校验失败，则返回false，所有校验失败项都会返回用户返回的错误提示。\n- `FormState.save()`：调用此方法后，会调用`Form`子孙`FormField`的`save`回调，用于保存表单内容\n- `FormState.reset()`：调用此方法后，会将子孙`FormField`的内容清空。\n\n#### 示例\n\n我们修改一下上面用户登录的示例，在提交之前校验：\n\n1. 用户名不能为空，如果为空则提示“用户名不能为空”。\n2. 密码不能小于6位，如果小于6为则提示“密码不能少于6位”。\n\n完整代码：\n\n```dart\nclass FormTestRoute extends StatefulWidget {\n  @override\n  _FormTestRouteState createState() => new _FormTestRouteState();\n}\n\nclass _FormTestRouteState extends State<FormTestRoute> {\n  TextEditingController _unameController = new TextEditingController();\n  TextEditingController _pwdController = new TextEditingController();\n  GlobalKey _formKey= new GlobalKey<FormState>();\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        title:Text(\"Form Test\"),\n      ),\n      body: Padding(\n        padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 24.0),\n        child: Form(\n          key: _formKey, //设置globalKey，用于后面获取FormState\n          autovalidate: true, //开启自动校验\n          child: Column(\n            children: <Widget>[\n              TextFormField(\n                  autofocus: true,\n                  controller: _unameController,\n                  decoration: InputDecoration(\n                      labelText: \"用户名\",\n                      hintText: \"用户名或邮箱\",\n                      icon: Icon(Icons.person)\n                  ),\n                  // 校验用户名\n                  validator: (v) {\n                    return v\n                        .trim()\n                        .length > 0 ? null : \"用户名不能为空\";\n                  }\n\n              ),\n              TextFormField(\n                  controller: _pwdController,\n                  decoration: InputDecoration(\n                      labelText: \"密码\",\n                      hintText: \"您的登录密码\",\n                      icon: Icon(Icons.lock)\n                  ),\n                  obscureText: true,\n                  //校验密码\n                  validator: (v) {\n                    return v\n                        .trim()\n                        .length > 5 ? null : \"密码不能少于6位\";\n                  }\n              ),\n              // 登录按钮\n              Padding(\n                padding: const EdgeInsets.only(top: 28.0),\n                child: Row(\n                  children: <Widget>[\n                    Expanded(\n                      child: RaisedButton(\n                        padding: EdgeInsets.all(15.0),\n                        child: Text(\"登录\"),\n                        color: Theme\n                            .of(context)\n                            .primaryColor,\n                        textColor: Colors.white,\n                        onPressed: () {\n                          //在这里不能通过此方式获取FormState，context不对\n                          //print(Form.of(context));\n                            \n                          // 通过_formKey.currentState 获取FormState后，\n                          // 调用validate()方法校验用户名密码是否合法，校验\n                          // 通过后再提交数据。 \n                          if((_formKey.currentState as FormState).validate()){\n                            //验证通过提交数据\n                          }\n                        },\n                      ),\n                    ),\n                  ],\n                ),\n              )\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n```\n\n\n\n运行后效果如图3-29所示：\n\n![图3-29](../imgs/3-29.png)\n\n\n\n注意，登录按钮的`onPressed`方法中不能通过`Form.of(context)`来获取，原因是，此处的`context`为`FormTestRoute`的context，而`Form.of(context)`是根据所指定`context`向根去查找，而`FormState`是在`FormTestRoute`的子树中，所以不行。正确的做法是通过`Builder`来构建登录按钮，`Builder`会将`widget`节点的`context`作为回调参数：\n\n```dart\nExpanded(\n // 通过Builder来获取RaisedButton所在widget树的真正context(Element) \n  child:Builder(builder: (context){\n    return RaisedButton(\n      ...\n      onPressed: () {\n        //由于本widget也是Form的子代widget，所以可以通过下面方式获取FormState  \n        if(Form.of(context).validate()){\n          //验证通过提交数据\n        }\n      },\n    );\n  })\n)\n```\n\n其实`context`正是操作Widget所对应的`Element`的一个接口，由于Widget树对应的`Element`都是不同的，所以`context`也都是不同的，有关`context`的更多内容会在后面高级部分详细讨论。Flutter中有很多“of(context)”这种方法，读者在使用时一定要注意`context`是否正确。\n\n\n\n\n\n\n"
  },
  {
    "path": "src/chapter3/progress.md",
    "content": "# 3.8 进度指示器\n\nMaterial 组件库中提供了两种进度指示器：`LinearProgressIndicator`和`CircularProgressIndicator`，它们都可以同时用于精确的进度指示和模糊的进度指示。精确进度通常用于任务进度可以计算和预估的情况，比如文件下载；而模糊进度则用户任务进度无法准确获得的情况，如下拉刷新，数据提交等。\n\n### LinearProgressIndicator\n\n`LinearProgressIndicator`是一个线性、条状的进度条，定义如下：\n\n```dart\nLinearProgressIndicator({\n  double value,\n  Color backgroundColor,\n  Animation<Color> valueColor,\n  ...\n})\n```\n\n- `value`：`value`表示当前的进度，取值范围为[0,1]；如果`value`为`null`时则指示器会执行一个循环动画（模糊进度）；当`value`不为`null`时，指示器为一个具体进度的进度条。\n- `backgroundColor`：指示器的背景色。\n- `valueColor`: 指示器的进度条颜色；值得注意的是，该值类型是`Animation<Color> `，这允许我们对进度条的颜色也可以指定动画。如果我们不需要对进度条颜色执行动画，换言之，我们想对进度条应用一种固定的颜色，此时我们可以通过`AlwaysStoppedAnimation`来指定。\n\n### 示例\n\n```dart\n// 模糊进度条(会执行一个动画)\nLinearProgressIndicator(\n  backgroundColor: Colors.grey[200],\n  valueColor: AlwaysStoppedAnimation(Colors.blue),\n),\n//进度条显示50%\nLinearProgressIndicator(\n  backgroundColor: Colors.grey[200],\n  valueColor: AlwaysStoppedAnimation(Colors.blue),\n  value: .5, \n)\n```\n\n运行效果如图3-30所示：\n\n![图3-30](../imgs/3-30.png)\n\n第一个进度条在执行循环动画：蓝色条一直在移动，而第二个进度条是静止的，停在50%的位置。\n\n### CircularProgressIndicator\n\n`CircularProgressIndicator`是一个圆形进度条，定义如下：\n\n```dart\n CircularProgressIndicator({\n  double value,\n  Color backgroundColor,\n  Animation<Color> valueColor,\n  this.strokeWidth = 4.0,\n  ...   \n}) \n```\n\n前三个参数和`LinearProgressIndicator`相同，不再赘述。`strokeWidth` 表示圆形进度条的粗细。示例如下：\n\n```dart\n// 模糊进度条(会执行一个旋转动画)\nCircularProgressIndicator(\n  backgroundColor: Colors.grey[200],\n  valueColor: AlwaysStoppedAnimation(Colors.blue),\n),\n//进度条显示50%，会显示一个半圆\nCircularProgressIndicator(\n  backgroundColor: Colors.grey[200],\n  valueColor: AlwaysStoppedAnimation(Colors.blue),\n  value: .5,\n),\n```\n\n运行效果如图3-31所示：\n\n![图3-31](../imgs/3-31.png)\n\n第一个进度条会执行旋转动画，而第二个进度条是静止的，它停在50%的位置。\n\n### 自定义尺寸\n\n我们可以发现`LinearProgressIndicator`和`CircularProgressIndicator`，并没有提供设置圆形进度条尺寸的参数；如果我们希望`LinearProgressIndicator`的线细一些，或者希望`CircularProgressIndicator`的圆大一些该怎么做？\n\n其实`LinearProgressIndicator`和`CircularProgressIndicator`都是取父容器的尺寸作为绘制的边界的。知道了这点，我们便可以通过尺寸限制类Widget，如`ConstrainedBox`、`SizedBox` （我们将在后面容器类组件一章中介绍）来指定尺寸，如：\n\n```dart\n// 线性进度条高度指定为3\nSizedBox(\n  height: 3,\n  child: LinearProgressIndicator(\n    backgroundColor: Colors.grey[200],\n    valueColor: AlwaysStoppedAnimation(Colors.blue),\n    value: .5,\n  ),\n),\n// 圆形进度条直径指定为100\nSizedBox(\n  height: 100,\n  width: 100,\n  child: CircularProgressIndicator(\n    backgroundColor: Colors.grey[200],\n    valueColor: AlwaysStoppedAnimation(Colors.blue),\n    value: .7,\n  ),\n),\n```\n\n运行效果如图3-32所示：\n\n![图3-32](../imgs/3-32.png)\n\n注意，如果`CircularProgressIndicator`显示空间的宽高不同，则会显示为椭圆。如：\n\n```dart\n// 宽高不等\nSizedBox(\n  height: 100,\n  width: 130,\n  child: CircularProgressIndicator(\n    backgroundColor: Colors.grey[200],\n    valueColor: AlwaysStoppedAnimation(Colors.blue),\n    value: .7,\n  ),\n),\n```\n\n运行效果如图3-33所示：\n\n![progress_oval](../imgs/progress_oval.png)\n\n### 进度色动画\n\n前面说过可以通过`valueColor`对进度条颜色做动画，关于动画我们将在后面专门的章节详细介绍，这里先给出一个例子，读者在了解了Flutter动画一章后再回过头来看。\n\n我们实现一个进度条在3秒内从灰色变成蓝色的动画：\n\n```dart\nimport 'package:flutter/material.dart';\n\nclass ProgressRoute extends StatefulWidget {\n  @override\n  _ProgressRouteState createState() => _ProgressRouteState();\n}\n\nclass _ProgressRouteState extends State<ProgressRoute>\n    with SingleTickerProviderStateMixin {\n  AnimationController _animationController;\n\n  @override\n  void initState() {\n    //动画执行时间3秒  \n    _animationController =\n        new AnimationController(vsync: this, duration: Duration(seconds: 3));\n    _animationController.forward();\n    _animationController.addListener(() => setState(() => {}));\n    super.initState();\n  }\n\n  @override\n  void dispose() {\n    _animationController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return SingleChildScrollView(\n      child: Column(\n        children: <Widget>[\n            Padding(\n            padding: EdgeInsets.all(16),\n            child: LinearProgressIndicator(\n              backgroundColor: Colors.grey[200],\n              valueColor: ColorTween(begin: Colors.grey, end: Colors.blue)\n                .animate(_animationController), // 从灰色变成蓝色\n              value: _animationController.value,\n            ),\n          );\n        ],\n      ),\n    );\n  }\n}\n```\n\n### 自定义进度指示器样式\n\n定制进度指示器风格样式，可以通过`CustomPainter` Widget 来自定义绘制逻辑，实际上`LinearProgressIndicator`和`CircularProgressIndicator`也正是通过`CustomPainter`来实现外观绘制的。关于`CustomPainter`，我们将在后面“自定义Widget”一章中详细介绍。\n\n> [flutter_spinkit](https://pub.flutter-io.cn/packages/flutter_spinkit) 包提供了多种风格的模糊进度指示器，读者若是感兴趣，可以参考。\n"
  },
  {
    "path": "src/chapter3/radio_and_checkbox.md",
    "content": "# 3.6 单选开关和复选框\n\nMaterial 组件库中提供了Material风格的单选开关`Switch`和复选框`Checkbox`，虽然它们都是继承自`StatefulWidget`，但它们本身不会保存当前选中状态，选中状态都是由父组件来管理的。当`Switch`或`Checkbox`被点击时，会触发它们的`onChanged`回调，我们可以在此回调中处理选中状态改变逻辑。下面看一个简单的例子：\n\n```dart\nclass SwitchAndCheckBoxTestRoute extends StatefulWidget {\n  @override\n  _SwitchAndCheckBoxTestRouteState createState() => new _SwitchAndCheckBoxTestRouteState();\n}\n\nclass _SwitchAndCheckBoxTestRouteState extends State<SwitchAndCheckBoxTestRoute> {\n  bool _switchSelected=true; //维护单选开关状态\n  bool _checkboxSelected=true;//维护复选框状态\n  @override\n  Widget build(BuildContext context) {\n    return Column(\n      children: <Widget>[\n        Switch(\n          value: _switchSelected,//当前状态\n          onChanged:(value){\n            //重新构建页面  \n            setState(() {\n              _switchSelected=value;\n            });\n          },\n        ),\n        Checkbox(\n          value: _checkboxSelected,\n          activeColor: Colors.red, //选中时的颜色\n          onChanged:(value){\n            setState(() {\n              _checkboxSelected=value;\n            });\n          } ,\n        )\n      ],\n    );\n  }\n}\n```\n\n\n\n上面代码中，由于需要维护`Switch`和`Checkbox`的选中状态，所以`SwitchAndCheckBoxTestRoute`继承自`StatefulWidget` 。在其`build`方法中分别构建了一个`Switch`和`Checkbox`，初始状态都为选中状态，当用户点击时，会将状态置反，然后回调用`setState()`通知Flutter framework重新构建UI。\n\n![图3-23](../imgs/3-23.png)\n\n\n\n### 属性及外观\n\n`Switch`和`Checkbox`属性比较简单，读者可以查看API文档，它们都有一个`activeColor`属性，用于设置激活态的颜色。至于大小，到目前为止，`Checkbox`的大小是固定的，无法自定义，而`Switch`只能定义宽度，高度也是固定的。值得一提的是`Checkbox`有一个属性`tristate` ，表示是否为三态，其默认值为`false` ，这时Checkbox有两种状态即“选中”和“不选中”，对应的value值为`true`和`false` 。如果`tristate`值为`true`时，value的值会增加一个状态`null`，读者可以自行了解。\n\n\n\n### 总结\n\n通过`Switch`和`Checkbox`我们可以看到，虽然它们本身是与状态（是否选中）关联的，但它们却不是自己来维护状态，而是需要父组件来管理状态，然后当用户点击时，再通过事件通知给父组件，这样是合理的，因为`Switch`和`Checkbox`是否选中本就和用户数据关联，而这些用户数据也不可能是它们的私有状态。我们在自定义组件时也应该思考一下哪种状态的管理方式最为合理。\n\n"
  },
  {
    "path": "src/chapter3/state_manage.md",
    "content": "# 3.2 状态管理\n\n响应式的编程框架中都会有一个永恒的主题——“状态(State)管理”，无论是在React/Vue（两者都是支持响应式编程的Web开发框架）还是Flutter中，他们讨论的问题和解决的思想都是一致的。所以，如果你对React/Vue的状态管理有了解，可以跳过本节。言归正传，我们想一个问题，`StatefulWidget`的状态应该被谁管理？Widget本身？父Widget？都会？还是另一个对象？答案是取决于实际情况！以下是管理状态的最常见的方法：\n\n- Widget管理自己的状态。\n- Widget管理子Widget状态。\n- 混合管理（父Widget和子Widget都管理状态）。\n\n如何决定使用哪种管理方法？下面是官方给出的一些原则可以帮助你做决定：\n\n- 如果状态是用户数据，如复选框的选中状态、滑块的位置，则该状态最好由父Widget管理。\n- 如果状态是有关界面外观效果的，例如颜色、动画，那么状态最好由Widget本身来管理。\n- 如果某一个状态是不同Widget共享的则最好由它们共同的父Widget管理。\n\n在Widget内部管理状态封装性会好一些，而在父Widget中管理会比较灵活。有些时候，如果不确定到底该怎么管理状态，那么推荐的首选是在父widget中管理（灵活会显得更重要一些）。\n\n接下来，我们将通过创建三个简单示例TapboxA、TapboxB和TapboxC来说明管理状态的不同方式。 这些例子功能是相似的 ——创建一个盒子，当点击它时，盒子背景会在绿色与灰色之间切换。状态 `_active`确定颜色：绿色为`true` ，灰色为`false`，如图3-4所示。![a large grey box with the text, 'Inactive'](../imgs/3-4.png)\n\n\n\n下面的例子将使用`GestureDetector`来识别点击事件，关于该`GestureDetector`的详细内容我们将在后面“事件处理”一章中介绍。\n\n### 3.2.1 Widget管理自身状态\n\n_TapboxAState 类:\n\n- 管理TapboxA的状态。\n- 定义`_active`：确定盒子的当前颜色的布尔值。\n- 定义`_handleTap()`函数，该函数在点击该盒子时更新`_active`，并调用`setState()`更新UI。\n- 实现widget的所有交互式行为。\n\n```dart\n// TapboxA 管理自身状态.\n\n//------------------------- TapboxA ----------------------------------\n\nclass TapboxA extends StatefulWidget {\n  TapboxA({Key key}) : super(key: key);\n\n  @override\n  _TapboxAState createState() => new _TapboxAState();\n}\n\nclass _TapboxAState extends State<TapboxA> {\n  bool _active = false;\n\n  void _handleTap() {\n    setState(() {\n      _active = !_active;\n    });\n  }\n\n  Widget build(BuildContext context) {\n    return new GestureDetector(\n      onTap: _handleTap,\n      child: new Container(\n        child: new Center(\n          child: new Text(\n            _active ? 'Active' : 'Inactive',\n            style: new TextStyle(fontSize: 32.0, color: Colors.white),\n          ),\n        ),\n        width: 200.0,\n        height: 200.0,\n        decoration: new BoxDecoration(\n          color: _active ? Colors.lightGreen[700] : Colors.grey[600],\n        ),\n      ),\n    );\n  }\n}\n\n```\n\n\n\n### 3.2.2 父Widget管理子Widget的状态\n\n对于父Widget来说，管理状态并告诉其子Widget何时更新通常是比较好的方式。 例如，`IconButton`是一个图标按钮，但它是一个无状态的Widget，因为我们认为父Widget需要知道该按钮是否被点击来采取相应的处理。\n\n在以下示例中，TapboxB通过回调将其状态导出到其父组件，状态由父组件管理，因此它的父组件为`StatefulWidget`。但是由于TapboxB不管理任何状态，所以`TapboxB`为`StatelessWidget`。\n\n`ParentWidgetState` 类:\n\n- 为TapboxB 管理`_active`状态。\n- 实现`_handleTapboxChanged()`，当盒子被点击时调用的方法。\n- 当状态改变时，调用`setState()`更新UI。\n\nTapboxB 类:\n\n- 继承`StatelessWidget`类，因为所有状态都由其父组件处理。\n- 当检测到点击时，它会通知父组件。\n\n```dart\n// ParentWidget 为 TapboxB 管理状态.\n\n//------------------------ ParentWidget --------------------------------\n\nclass ParentWidget extends StatefulWidget {\n  @override\n  _ParentWidgetState createState() => new _ParentWidgetState();\n}\n\nclass _ParentWidgetState extends State<ParentWidget> {\n  bool _active = false;\n\n  void _handleTapboxChanged(bool newValue) {\n    setState(() {\n      _active = newValue;\n    });\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return new Container(\n      child: new TapboxB(\n        active: _active,\n        onChanged: _handleTapboxChanged,\n      ),\n    );\n  }\n}\n\n//------------------------- TapboxB ----------------------------------\n\nclass TapboxB extends StatelessWidget {\n  TapboxB({Key key, this.active: false, @required this.onChanged})\n      : super(key: key);\n\n  final bool active;\n  final ValueChanged<bool> onChanged;\n\n  void _handleTap() {\n    onChanged(!active);\n  }\n\n  Widget build(BuildContext context) {\n    return new GestureDetector(\n      onTap: _handleTap,\n      child: new Container(\n        child: new Center(\n          child: new Text(\n            active ? 'Active' : 'Inactive',\n            style: new TextStyle(fontSize: 32.0, color: Colors.white),\n          ),\n        ),\n        width: 200.0,\n        height: 200.0,\n        decoration: new BoxDecoration(\n          color: active ? Colors.lightGreen[700] : Colors.grey[600],\n        ),\n      ),\n    );\n  }\n}\n```\n\n \n\n### 3.2.3 混合状态管理\n\n对于一些组件来说，混合管理的方式会非常有用。在这种情况下，组件自身管理一些内部状态，而父组件管理一些其他外部状态。\n\n在下面TapboxC示例中，手指按下时，盒子的周围会出现一个深绿色的边框，抬起时，边框消失。点击完成后，盒子的颜色改变。 TapboxC将其`_active`状态导出到其父组件中，但在内部管理其`_highlight`状态。这个例子有两个状态对象`_ParentWidgetState`和`_TapboxCState`。\n\n`_ParentWidgetStateC `类:\n\n- 管理`_active` 状态。\n- 实现 `_handleTapboxChanged()` ，当盒子被点击时调用。\n- 当点击盒子并且`_active`状态改变时调用`setState()`更新UI。\n\n`_TapboxCState` 对象:\n\n- 管理`_highlight` 状态。\n- `GestureDetector`监听所有tap事件。当用户点下时，它添加高亮（深绿色边框）；当用户释放时，会移除高亮。\n- 当按下、抬起、或者取消点击时更新`_highlight`状态，调用`setState()`更新UI。\n- 当点击时，将状态的改变传递给父组件。\n\n```dart\n//---------------------------- ParentWidget ----------------------------\n\nclass ParentWidgetC extends StatefulWidget {\n  @override\n  _ParentWidgetCState createState() => new _ParentWidgetCState();\n}\n\nclass _ParentWidgetCState extends State<ParentWidgetC> {\n  bool _active = false;\n\n  void _handleTapboxChanged(bool newValue) {\n    setState(() {\n      _active = newValue;\n    });\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return new Container(\n      child: new TapboxC(\n        active: _active,\n        onChanged: _handleTapboxChanged,\n      ),\n    );\n  }\n}\n\n//----------------------------- TapboxC ------------------------------\n\nclass TapboxC extends StatefulWidget {\n  TapboxC({Key key, this.active: false, @required this.onChanged})\n      : super(key: key);\n\n  final bool active;\n  final ValueChanged<bool> onChanged;\n  \n  @override\n  _TapboxCState createState() => new _TapboxCState();\n}\n\nclass _TapboxCState extends State<TapboxC> {\n  bool _highlight = false;\n\n  void _handleTapDown(TapDownDetails details) {\n    setState(() {\n      _highlight = true;\n    });\n  }\n\n  void _handleTapUp(TapUpDetails details) {\n    setState(() {\n      _highlight = false;\n    });\n  }\n\n  void _handleTapCancel() {\n    setState(() {\n      _highlight = false;\n    });\n  }\n\n  void _handleTap() {\n    widget.onChanged(!widget.active);\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    // 在按下时添加绿色边框，当抬起时，取消高亮  \n    return new GestureDetector(\n      onTapDown: _handleTapDown, // 处理按下事件\n      onTapUp: _handleTapUp, // 处理抬起事件\n      onTap: _handleTap,\n      onTapCancel: _handleTapCancel,\n      child: new Container(\n        child: new Center(\n          child: new Text(widget.active ? 'Active' : 'Inactive',\n              style: new TextStyle(fontSize: 32.0, color: Colors.white)),\n        ),\n        width: 200.0,\n        height: 200.0,\n        decoration: new BoxDecoration(\n          color: widget.active ? Colors.lightGreen[700] : Colors.grey[600],\n          border: _highlight\n              ? new Border.all(\n                  color: Colors.teal[700],\n                  width: 10.0,\n                )\n              : null,\n        ),\n      ),\n    );\n  }\n}\n```\n\n另一种实现可能会将高亮状态导出到父组件，但同时保持`_active`状态为内部状态，但如果你要将该TapBox给其它人使用，可能没有什么意义。 开发人员只会关心该框是否处于Active状态，而不在乎高亮显示是如何管理的，所以应该让TapBox内部处理这些细节。\n\n### 3.2.4 全局状态管理\n\n当应用中需要一些跨组件（包括跨路由）的状态需要同步时，上面介绍的方法便很难胜任了。比如，我们有一个设置页，里面可以设置应用的语言，我们为了让设置实时生效，我们期望在语言状态发生改变时，APP中依赖应用语言的组件能够重新build一下，但这些依赖应用语言的组件和设置页并不在一起，所以这种情况用上面的方法很难管理。这时，正确的做法是通过一个全局状态管理器来处理这种相距较远的组件之间的通信。目前主要有两种办法：\n\n1. 实现一个全局的事件总线，将语言状态改变对应为一个事件，然后在APP中依赖应用语言的组件的`initState` 方法中订阅语言改变的事件。当用户在设置页切换语言后，我们发布语言改变事件，而订阅了此事件的组件就会收到通知，收到通知后调用`setState(...)`方法重新`build`一下自身即可。\n2. 使用一些专门用于状态管理的包，如Provider、Redux，读者可以在pub上查看其详细信息。\n\n本书将在\"功能型组件\"一章中介绍Provider包的实现原理及用法，同时也将会在\"事件处理与通知\"一章中实现一个全局事件总线，读者有需要可以直接翻看。\n\n"
  },
  {
    "path": "src/chapter3/text.md",
    "content": "## 3.3 文本及样式\n\n### 3.3.1 Text\n\n`Text`用于显示简单样式文本，它包含一些控制文本显示样式的一些属性，一个简单的例子如下：\n\n```dart\nText(\"Hello world\",\n  textAlign: TextAlign.left,\n);\n\nText(\"Hello world! I'm Jack. \"*4,\n  maxLines: 1,\n  overflow: TextOverflow.ellipsis,\n);\n\nText(\"Hello world\",\n  textScaleFactor: 1.5,\n);\n```\n运行效果如图3-5所示：\n\n![image-20180829103242552](../imgs/3-5.png)\n\n- `textAlign`：文本的对齐方式；可以选择左对齐、右对齐还是居中。注意，对齐的参考系是Text widget本身。本例中虽然是指定了居中对齐，但因为Text文本内容宽度不足一行，Text的宽度和文本内容长度相等，那么这时指定对齐方式是没有意义的，只有Text宽度大于文本内容长度时指定此属性才有意义。下面我们指定一个较长的字符串：\n\n  ```dart\n  Text(\"Hello world \"*6,  //字符串重复六次\n    textAlign: TextAlign.center,\n  )；\n  ```\n\n  运行效果如图3-6所示：\n\n  ![image-20180829104807535](../imgs/3-6.png)\n\n​      字符串内容超过一行，Text宽度等于屏幕宽度，第二行文本便会居中显示。\n\n- `maxLines`、`overflow`：指定文本显示的最大行数，默认情况下，文本是自动折行的，如果指定此参数，则文本最多不会超过指定的行。如果有多余的文本，可以通过`overflow`来指定截断方式，默认是直接截断，本例中指定的截断方式`TextOverflow.ellipsis`，它会将多余文本截断后以省略符“...”表示；TextOverflow的其它截断方式请参考SDK文档。\n- `textScaleFactor`：代表文本相对于当前字体大小的缩放因子，相对于去设置文本的样式`style`属性的`fontSize`，它是调整字体大小的一个快捷方式。该属性的默认值可以通过`MediaQueryData.textScaleFactor`获得，如果没有`MediaQuery`，那么会默认值将为1.0。\n\n### 3.3.2 TextStyle\n\n`TextStyle`用于指定文本显示的样式如颜色、字体、粗细、背景等。我们看一个示例：\n\n```dart\nText(\"Hello world\",\n  style: TextStyle(\n    color: Colors.blue,\n    fontSize: 18.0,\n    height: 1.2,  \n    fontFamily: \"Courier\",\n    background: new Paint()..color=Colors.yellow,\n    decoration:TextDecoration.underline,\n    decorationStyle: TextDecorationStyle.dashed\n  ),\n);\n```\n\n效果如图3-7所示：\n\n![3-7](../imgs/3-7.png)\n\n此示例只展示了TextStyle的部分属性，它还有一些其它属性，属性名基本都是自解释的，在此不再赘述，读者可以查阅SDK文档。值得注意的是：\n\n- `height`：该属性用于指定行高，但它并不是一个绝对值，而是一个因子，具体的行高等于`fontSize`*`height`。\n\n- `fontFamily` ：由于不同平台默认支持的字体集不同，所以在手动指定字体时一定要先在不同平台测试一下。\n\n- `fontSize`：该属性和Text的`textScaleFactor`都用于控制字体大小。但是有两个主要区别：\n\n  - `fontSize`可以精确指定字体大小，而`textScaleFactor`只能通过缩放比例来控制。\n  - `textScaleFactor`主要是用于系统字体大小设置改变时对Flutter应用字体进行全局调整，而`fontSize`通常用于单个文本，字体大小不会跟随系统字体大小变化。\n\n### 3.3.3 TextSpan\n\n在上面的例子中，Text的所有文本内容只能按同一种样式，如果我们需要对一个Text内容的不同部分按照不同的样式显示，这时就可以使用`TextSpan`，它代表文本的一个“片段”。我们看看TextSpan的定义:\n\n```dart\nconst TextSpan({\n  TextStyle style, \n  Sting text,\n  List<TextSpan> children,\n  GestureRecognizer recognizer,\n});\n```\n\n其中`style` 和 `text`属性代表该文本片段的样式和内容。  `children`是一个`TextSpan`的数组，也就是说`TextSpan`可以包括其他`TextSpan`。而`recognizer`用于对该文本片段上用于手势进行识别处理。下面我们看一个效果（图3-8），然后用`TextSpan`实现它。\n\n![3-8](../imgs/3-8.png)\n\n源码：\n\n```dart\nText.rich(TextSpan(\n    children: [\n     TextSpan(\n       text: \"Home: \"\n     ),\n     TextSpan(\n       text: \"https://flutterchina.club\",\n       style: TextStyle(\n         color: Colors.blue\n       ),  \n       recognizer: _tapRecognizer\n     ),\n    ]\n))\n```\n\n- 上面代码中，我们通过TextSpan实现了一个基础文本片段和一个链接片段，然后通过`Text.rich ` 方法将`TextSpan` 添加到Text中，之所以可以这样做，是因为Text其实就是RichText的一个包装，而RichText是可以显示多种样式(富文本)的widget。\n- `_tapRecognizer`，它是点击链接后的一个处理器（代码已省略），关于手势识别的更多内容我们将在后面单独介绍。\n\n### 3.3.4 DefaultTextStyle\n\n在Widget树中，文本的样式默认是可以被继承的（子类文本类组件未指定具体样式时可以使用Widget树中父级设置的默认样式），因此，如果在Widget树的某一个节点处设置一个默认的文本样式，那么该节点的子树中所有文本都会默认使用这个样式，而`DefaultTextStyle`正是用于设置默认文本样式的。下面我们看一个例子：\n\n```dart\nDefaultTextStyle(\n  //1.设置文本默认样式  \n  style: TextStyle(\n    color:Colors.red,\n    fontSize: 20.0,\n  ),\n  textAlign: TextAlign.start,\n  child: Column(\n    crossAxisAlignment: CrossAxisAlignment.start,\n    children: <Widget>[\n      Text(\"hello world\"),\n      Text(\"I am Jack\"),\n      Text(\"I am Jack\",\n        style: TextStyle(\n          inherit: false, //2.不继承默认样式\n          color: Colors.grey\n        ),\n      ),\n    ],\n  ),\n);\n```\n\n上面代码中，我们首先设置了一个默认的文本样式，即字体为20像素(逻辑像素)、颜色为红色。然后通过`DefaultTextStyle` 设置给了子树Column节点处，这样一来Column的所有子孙Text默认都会继承该样式，除非Text显示指定不继承样式，如代码中注释2。示例运行效果如图3-9：\n\n![3-9](../imgs/3-9.png)\n\n### 3.3.5 字体\n\n可以在Flutter应用程序中使用不同的字体。例如，我们可能会使用设计人员创建的自定义字体，或者其它第三方的字体，如[Google Fonts](https://fonts.google.com/)中的字体。本节将介绍如何为Flutter应用配置字体，并在渲染文本时使用它们。\n\n在Flutter中使用字体分两步完成。首先在`pubspec.yaml`中声明它们，以确保它们会打包到应用程序中。然后通过[`TextStyle`](https://docs.flutter.io/flutter/painting/TextStyle-class.html)属性使用字体。\n\n#### 在asset中声明\n\n要将字体文件打包到应用中，和使用其它资源一样，要先在`pubspec.yaml`中声明它。然后将字体文件复制到在`pubspec.yaml`中指定的位置。如：\n\n```yaml\nflutter:\n  fonts:\n    - family: Raleway\n      fonts:\n        - asset: assets/fonts/Raleway-Regular.ttf\n        - asset: assets/fonts/Raleway-Medium.ttf\n          weight: 500\n        - asset: assets/fonts/Raleway-SemiBold.ttf\n          weight: 600\n    - family: AbrilFatface\n      fonts:\n        - asset: assets/fonts/abrilfatface/AbrilFatface-Regular.ttf\n```\n\n#### 使用字体\n\n```dart\n// 声明文本样式\nconst textStyle = const TextStyle(\n  fontFamily: 'Raleway',\n);\n\n// 使用文本样式\nvar buttonText = const Text(\n  \"Use the font for this text\",\n  style: textStyle,\n);\n```\n\n#### Package中的字体\n\n要使用Package中定义的字体，**必须提供`package`参数**。例如，假设上面的字体声明位于`my_package`包中。然后创建TextStyle的过程如下：\n\n```dart\nconst textStyle = const TextStyle(\n  fontFamily: 'Raleway',\n  package: 'my_package', //指定包名\n);\n```\n\n如果在package包内部使用它自己定义的字体，也应该在创建文本样式时指定`package`参数，如上例所示。\n\n一个包也可以只提供字体文件而不需要在pubspec.yaml中声明。 这些文件应该存放在包的`lib/`文件夹中。字体文件不会自动绑定到应用程序中，应用程序可以在声明字体时有选择地使用这些字体。假设一个名为my_package的包中有一个字体文件：\n\n```\nlib/fonts/Raleway-Medium.ttf\n```\n\n然后，应用程序可以声明一个字体，如下面的示例所示：\n\n```yaml\n flutter:\n   fonts:\n     - family: Raleway\n       fonts:\n         - asset: assets/fonts/Raleway-Regular.ttf\n         - asset: packages/my_package/fonts/Raleway-Medium.ttf\n           weight: 500\n```\n\n`lib/`是隐含的，所以它不应该包含在asset路径中。\n\n在这种情况下，由于应用程序本地定义了字体，所以在创建TextStyle时可以不指定`package`参数：\n\n```dart\nconst textStyle = const TextStyle(\n  fontFamily: 'Raleway',\n);\n```\n\n"
  },
  {
    "path": "src/chapter4/alignment.md",
    "content": "# 4.6 对齐与相对定位（Align）\n\n在上一节中我们讲过通过`Stack`和`Positioned`，我们可以指定一个或多个子元素相对于父元素各个边的精确偏移，并且可以重叠。但如果我们只想简单的调整**一个**子元素在父元素中的位置的话，使用`Align`组件会更简单一些。\n\n## 4.6.1 Align\n\n`Align` 组件可以调整子组件的位置，并且可以根据子组件的宽高来确定自身的的宽高，定义如下：\n\n```dart\nAlign({\n  Key key,\n  this.alignment = Alignment.center,\n  this.widthFactor,\n  this.heightFactor,\n  Widget child,\n})\n```\n\n- `alignment` : 需要一个`AlignmentGeometry`类型的值，表示子组件在父组件中的起始位置。`AlignmentGeometry` 是一个抽象类，它有两个常用的子类：`Alignment`和 `FractionalOffset`，我们将在下面的示例中详细介绍。\n- `widthFactor`和`heightFactor`是用于确定`Align` 组件本身宽高的属性；它们是两个缩放因子，会分别乘以子元素的宽、高，最终的结果就是`Align` 组件的宽高。如果值为`null`，则组件的宽高将会占用尽可能多的空间。\n\n### 示例\n\n我们先来看一个简单的例子：\n\n```dart\nContainer(\n  height: 120.0,\n  width: 120.0,\n  color: Colors.blue[50],\n  child: Align(\n    alignment: Alignment.topRight,\n    child: FlutterLogo(\n      size: 60,\n    ),\n  ),\n)\n```\n\n运行效果如图4-11所示：\n\n![图4-11](../imgs/4-11.png)\n\n`FlutterLogo` 是Flutter SDK提供的一个组件，内容就是Flutter的商标。在上面的例子中，我们显式指定了`Container`的宽、高都为120。如果我们不显式指定宽高，而通过同时指定`widthFactor`和`heightFactor` 为2也是可以达到同样的效果：\n\n```dart\nAlign(\n  widthFactor: 2,\n  heightFactor: 2,\n  alignment: Alignment.topRight,\n  child: FlutterLogo(\n    size: 60,\n  ),\n),\n```\n\n因为`FlutterLogo`的宽高为60，则`Align`的最终宽高都为`2*60=120`。\n\n另外，我们通过`Alignment.topRight`将`FlutterLogo`定位在`Container`的右上角。那`Alignment.topRight`是什么呢？通过源码我们可以看到其定义如下：\n\n```dart\n//右上角\nstatic const Alignment topRight = Alignment(1.0, -1.0);\n```\n\n可以看到它只是`Alignment`的一个实例，下面我们介绍一下`Alignment`。\n\n### Alignment\n\n`Alignment`继承自`AlignmentGeometry`，表示矩形内的一个点，他有两个属性`x`、`y`，分别表示在水平和垂直方向的偏移，`Alignment`定义如下：\n\n```dart\nAlignment(this.x, this.y)\n```\n\n`Alignment` Widget会以**矩形的中心点作为坐标原点**，即`Alignment(0.0, 0.0)` 。`x`、`y`的值从-1到1分别代表矩形左边到右边的距离和顶部到底边的距离，因此2个水平（或垂直）单位则等于矩形的宽（或高），如`Alignment(-1.0, -1.0)` 代表矩形的左侧顶点，而`Alignment(1.0, 1.0)`代表右侧底部终点，而`Alignment(1.0, -1.0)` 则正是右侧顶点，即`Alignment.topRight`。为了使用方便，矩形的原点、四个顶点，以及四条边的终点在`Alignment`类中都已经定义为了静态常量。\n\n`Alignment`可以通过其**坐标转换公式**将其坐标转为子元素的具体偏移坐标：\n\n```\n(Alignment.x*childWidth/2+childWidth/2, Alignment.y*childHeight/2+childHeight/2)\n```\n\n其中`childWidth`为子元素的宽度，`childHeight`为子元素高度。\n\n现在我们再看看上面的示例，我们将`Alignment(1.0, -1.0)`带入上面公式，可得`FlutterLogo`的实际偏移坐标正是（60，0）。下面再看一个例子：\n\n```dart\n Align(\n  widthFactor: 2,\n  heightFactor: 2,\n  alignment: Alignment(2,0.0),\n  child: FlutterLogo(\n    size: 60,\n  ),\n)\n```\n\n我们可以先想象一下运行效果：将`Alignment(2,0.0)`带入上述坐标转换公式，可以得到`FlutterLogo`的实际偏移坐标为（90，30）。实际运行如图4-12所示：\n\n![图4-12](../imgs/4-12.png)\n\n### FractionalOffset\n\n`FractionalOffset` 继承自 `Alignment `，它和 `Alignment `唯一的区别就是坐标原点不同！`FractionalOffset` 的坐标原点为矩形的左侧顶点，这和布局系统的一致，所以理解起来会比较容易。`FractionalOffset`的坐标转换公式为：\n\n```\n实际偏移 = (FractionalOffse.x * childWidth, FractionalOffse.y * childHeight)\n```\n\n下面看一个例子：\n\n```dart\nContainer(\n  height: 120.0,\n  width: 120.0,\n  color: Colors.blue[50],\n  child: Align(\n    alignment: FractionalOffset(0.2, 0.6),\n    child: FlutterLogo(\n      size: 60,\n    ),\n  ),\n)\n```\n\n实际运行效果如图4-13所示下：\n\n![图4-13](../imgs/4-13.png)\n\n我们将`FractionalOffset(0.2, 0.6)`带入坐标转换公式得`FlutterLogo`实际偏移为（12，36），和实际运行效果吻合。\n\n## 4.6.2 Align和Stack对比\n\n可以看到，`Align`和`Stack`/`Positioned`都可以用于指定子元素相对于父元素的偏移，但它们还是有两个主要区别：\n\n1. 定位参考系统不同；`Stack`/`Positioned`定位的的参考系可以是父容器矩形的四个顶点；而`Align`则需要先通过`alignment` 参数来确定坐标原点，不同的`alignment`会对应不同原点，最终的偏移是需要通过`alignment`的转换公式来计算出。\n2. `Stack`可以有多个子元素，并且子元素可以堆叠，而`Align`只能有一个子元素，不存在堆叠。\n\n\n\n## 4.6.3 Center组件\n\n我们在前面章节的例子中已经使用过`Center`组件来居中子元素了，现在我们正式来介绍一下它。通过查找SDK源码，我们看到`Center`组件定义如下：\n\n```dart\nclass Center extends Align {\n  const Center({ Key key, double widthFactor, double heightFactor, Widget child })\n    : super(key: key, widthFactor: widthFactor, heightFactor: heightFactor, child: child);\n}\n```\n\n可以看到`Center`继承自`Align`，它比`Align`只少了一个`alignment` 参数；由于`Align`的构造函数中`alignment` 值为`Alignment.center`，所以，我们可以认为`Center`组件其实是对齐方式确定（`Alignment.center`）了的`Align`。\n\n上面我们讲过当`widthFactor`或`heightFactor`为`null`时组件的宽高将会占用尽可能多的空间，这一点需要特别注意，我们通过一个示例说明：\n\n```dart\n...//省略无关代码\nDecoratedBox(\n  decoration: BoxDecoration(color: Colors.red),\n  child: Center(\n    child: Text(\"xxx\"),\n  ),\n),\nDecoratedBox(\n  decoration: BoxDecoration(color: Colors.red),\n  child: Center(\n    widthFactor: 1,\n    heightFactor: 1,\n    child: Text(\"xxx\"),\n  ),\n)\n```\n\n运行效果如图4-14所示：\n\n![图4-14](../imgs/4-14.png)\n\n## 总结\n\n本节重点介绍了`Align`组件及两种偏移类`Alignment` 和`FractionalOffset`，读者需要理解这两种偏移类的区别及各自的坐标转化公式。另外，在此建议读者在需要制定一些精确的偏移时应优先使用`FractionalOffset`，因为它的坐标原点和布局系统相同，能更容易算出实际偏移。\n\n在后面，我们又介绍了`Align`组件和`Stack`/`Positioned`、`Center`的关系，读者可以对比理解。\n\n还有，熟悉Web开发的同学可能会发现`Align`组件的特性和Web开发中相对定位（`position: relative`）非常像，是的！在大多数时候，我们可以直接使用`Align`组件来实现Web中相对定位的效果，读者可以类比记忆。\n"
  },
  {
    "path": "src/chapter4/flex.md",
    "content": "\n\n# 4.3 弹性布局（Flex）\n\n弹性布局允许子组件按照一定比例来分配父容器空间。弹性布局的概念在其它UI系统中也都存在，如H5中的弹性盒子布局，Android中的`FlexboxLayout`等。Flutter中的弹性布局主要通过`Flex`和`Expanded`来配合实现。\n\n### Flex\n\n`Flex`组件可以沿着水平或垂直方向排列子组件，如果你知道主轴方向，使用`Row`或`Column`会方便一些，因为`Row`和`Column`都继承自`Flex`，参数基本相同，所以能使用Flex的地方基本上都可以使用`Row`或`Column`。`Flex`本身功能是很强大的，它也可以和`Expanded`组件配合实现弹性布局。接下来我们只讨论`Flex`和弹性布局相关的属性(其它属性已经在介绍`Row`和`Column`时介绍过了)。\n\n```dart\nFlex({\n  ...\n  @required this.direction, //弹性布局的方向, Row默认为水平方向，Column默认为垂直方向\n  List<Widget> children = const <Widget>[],\n})\n```\n\n`Flex`继承自`MultiChildRenderObjectWidget`，对应的`RenderObject`为`RenderFlex`，`RenderFlex`中实现了其布局算法。\n\n### Expanded\n\n可以按比例“扩伸” `Row`、`Column`和`Flex`子组件所占用的空间。\n\n```dart\nconst Expanded({\n  int flex = 1, \n  @required Widget child,\n})\n```\n\n`flex`参数为弹性系数，如果为0或`null`，则`child`是没有弹性的，即不会被扩伸占用的空间。如果大于0，所有的`Expanded`按照其flex的比例来分割主轴的全部空闲空间。下面我们看一个例子：\n\n```dart\nclass FlexLayoutTestRoute extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return Column(\n      children: <Widget>[\n        //Flex的两个子widget按1：2来占据水平空间  \n        Flex(\n          direction: Axis.horizontal,\n          children: <Widget>[\n            Expanded(\n              flex: 1,\n              child: Container(\n                height: 30.0,\n                color: Colors.red,\n              ),\n            ),\n            Expanded(\n              flex: 2,\n              child: Container(\n                height: 30.0,\n                color: Colors.green,\n              ),\n            ),\n          ],\n        ),\n        Padding(\n          padding: const EdgeInsets.only(top: 20.0),\n          child: SizedBox(\n            height: 100.0,\n            //Flex的三个子widget，在垂直方向按2：1：1来占用100像素的空间  \n            child: Flex(\n              direction: Axis.vertical,\n              children: <Widget>[\n                Expanded(\n                  flex: 2,\n                  child: Container(\n                    height: 30.0,\n                    color: Colors.red,\n                  ),\n                ),\n                Spacer(\n                  flex: 1,\n                ),\n                Expanded(\n                  flex: 1,\n                  child: Container(\n                    height: 30.0,\n                    color: Colors.green,\n                  ),\n                ),\n              ],\n            ),\n          ),\n        ),\n      ],\n    );\n  }\n}\n```\n\n运行效果如图4-5所示：\n\n![弹性布局](../imgs/4-5.png)\n\n示例中的`Spacer`的功能是占用指定比例的空间，实际上它只是`Expanded`的一个包装类，`Spacer`的源码如下：\n\n```dart\nclass Spacer extends StatelessWidget {\n  const Spacer({Key key, this.flex = 1})\n    : assert(flex != null),\n      assert(flex > 0),\n      super(key: key);\n  \n  final int flex;\n\n  @override\n  Widget build(BuildContext context) {\n    return Expanded(\n      flex: flex,\n      child: const SizedBox.shrink(),\n    );\n  }\n}\n```\n\n### 小结\n\n弹性布局比较简单，唯一需要注意的就是`Row`、`Column`以及`Flex`的关系。"
  },
  {
    "path": "src/chapter4/index.md",
    "content": "# 本章目录\n\n* [4.1：布局类组件简介](intro.md)\n* [4.2：线性布局（Row、Column）](row_and_column.md)\n* [4.3：弹性布局（Flex）](flex.md)\n* [4.4：流式布局（Wrap、Flow）](wrap_and_flow.md)      \n* [4.5：层叠布局（Stack、Positioned）](stack.md)\n* [4.6：对齐与相对定位（Align）](alignment.md)  \n\n"
  },
  {
    "path": "src/chapter4/intro.md",
    "content": "# 4.1 布局类组件简介\n\n布局类组件都会包含一个或多个子组件，不同的布局类组件对子组件排版(layout)方式不同。我们在前面说过`Element`树才是最终的绘制树，`Element`树是通过Widget树来创建的（通过`Widget.createElement()`），Widget其实就是Element的配置数据。在Flutter中，根据Widget是否需要包含子节点将Widget分为了三类，分别对应三种Element，如下表：\n\n| Widget                        | 对应的Element                  | 用途                                                         |\n| ----------------------------- | ------------------------------ | ------------------------------------------------------------ |\n| LeafRenderObjectWidget        | LeafRenderObjectElement        | Widget树的叶子节点，用于没有子节点的widget，通常基础组件都属于这一类，如Image。 |\n| SingleChildRenderObjectWidget | SingleChildRenderObjectElement | 包含一个子Widget，如：ConstrainedBox、DecoratedBox等         |\n| MultiChildRenderObjectWidget  | MultiChildRenderObjectElement  | 包含多个子Widget，一般都有一个children参数，接受一个Widget数组。如Row、Column、Stack等 |\n\n> 注意，Flutter中的很多Widget是直接继承自StatelessWidget或StatefulWidget，然后在`build()`方法中构建真正的RenderObjectWidget，如Text，它其实是继承自StatelessWidget，然后在`build()`方法中通过RichText来构建其子树，而RichText才是继承自MultiChildRenderObjectWidget。所以为了方便叙述，我们也可以直接说Text属于MultiChildRenderObjectWidget（其它widget也可以这么描述），这才是本质。读到这里我们也会发现，其实**StatelessWidget和StatefulWidget就是两个用于组合Widget的基类，它们本身并不关联最终的渲染对象（RenderObjectWidget）**。\n\n布局类组件就是指直接或间接继承(包含)`MultiChildRenderObjectWidget`的Widget，它们一般都会有一个`children`属性用于接收子Widget。我们看一下继承关系 Widget > RenderObjectWidget > (Leaf/SingleChild/MultiChild)RenderObjectWidget 。\n\n`RenderObjectWidget`类中定义了创建、更新`RenderObject`的方法，子类必须实现他们，关于`RenderObject`我们现在只需要知道它是最终布局、渲染UI界面的对象即可，也就是说，对于布局类组件来说，其布局算法都是通过对应的`RenderObject`对象来实现的，所以读者如果对接下来介绍的某个布局类组件的原理感兴趣，可以查看其对应的`RenderObject`的实现，比如`Stack`（层叠布局）对应的`RenderObject`对象就是`RenderStack`，而层叠布局的实现就在`RenderStack`中。\n\n在本章中，为了让读者对布局类Widget有个快速的认识，所以我们并不会深入到`RenderObject`的细节中去。在学习本章时，读者的重点是掌握不同布局组件的布局特点，具体原理和细节等我们对Flutter整体入门后，感兴趣的话再去研究。\n\n"
  },
  {
    "path": "src/chapter4/row_and_column.md",
    "content": "\n\n# 4.2 线性布局（Row和Column）\n\n所谓线性布局，即指沿水平或垂直方向排布子组件。Flutter中通过`Row`和`Column`来实现线性布局，类似于Android中的`LinearLayout`控件。`Row`和`Column`都继承自`Flex`，我们将在弹性布局一节中详细介绍`Flex`。\n\n### 主轴和纵轴\n\n对于线性布局，有主轴和纵轴之分，如果布局是沿水平方向，那么主轴就是指水平方向，而纵轴即垂直方向；如果布局沿垂直方向，那么主轴就是指垂直方向，而纵轴就是水平方向。在线性布局中，有两个定义对齐方式的枚举类`MainAxisAlignment`和`CrossAxisAlignment`，分别代表主轴对齐和纵轴对齐。\n\n### Row\n\nRow可以在水平方向排列其子widget。定义如下：\n\n```dart\nRow({\n  ...  \n  TextDirection textDirection,    \n  MainAxisSize mainAxisSize = MainAxisSize.max,    \n  MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,\n  VerticalDirection verticalDirection = VerticalDirection.down,  \n  CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,\n  List<Widget> children = const <Widget>[],\n})\n```\n\n\n- `textDirection`：表示水平方向子组件的布局顺序(是从左往右还是从右往左)，默认为系统当前Locale环境的文本方向(如中文、英语都是从左往右，而阿拉伯语是从右往左)。\n- `mainAxisSize`：表示`Row`在主轴(水平)方向占用的空间，默认是`MainAxisSize.max`，表示尽可能多的占用水平方向的空间，此时无论子widgets实际占用多少水平空间，`Row`的宽度始终等于水平方向的最大宽度；而`MainAxisSize.min`表示尽可能少的占用水平空间，当子组件没有占满水平剩余空间，则`Row`的实际宽度等于所有子组件占用的的水平空间；\n- `mainAxisAlignment`：表示子组件在`Row`所占用的水平空间内对齐方式，如果`mainAxisSize`值为`MainAxisSize.min`，则此属性无意义，因为子组件的宽度等于`Row`的宽度。只有当`mainAxisSize`的值为`MainAxisSize.max`时，此属性才有意义，`MainAxisAlignment.start`表示沿`textDirection`的初始方向对齐，如`textDirection`取值为`TextDirection.ltr`时，则`MainAxisAlignment.start`表示左对齐，`textDirection`取值为`TextDirection.rtl`时表示从右对齐。而`MainAxisAlignment.end`和`MainAxisAlignment.start`正好相反；`MainAxisAlignment.center`表示居中对齐。读者可以这么理解：`textDirection`是`mainAxisAlignment`的参考系。\n- `verticalDirection`：表示`Row`纵轴（垂直）的对齐方向，默认是`VerticalDirection.down`，表示从上到下。\n- `crossAxisAlignment`：表示子组件在纵轴方向的对齐方式，`Row`的高度等于子组件中最高的子元素高度，它的取值和`MainAxisAlignment`一样(包含`start`、`end`、 `center`三个值)，不同的是`crossAxisAlignment`的参考系是`verticalDirection`，即`verticalDirection`值为`VerticalDirection.down`时`crossAxisAlignment.start`指顶部对齐，`verticalDirection`值为`VerticalDirection.up`时，`crossAxisAlignment.start`指底部对齐；而`crossAxisAlignment.end`和`crossAxisAlignment.start`正好相反；\n- `children` ：子组件数组。\n\n### 示例\n\n请阅读下面代码，先想象一下运行的结果：\n\n```dart\nColumn(\n  //测试Row对齐方式，排除Column默认居中对齐的干扰\n  crossAxisAlignment: CrossAxisAlignment.start,\n  children: <Widget>[\n    Row(\n      mainAxisAlignment: MainAxisAlignment.center,\n      children: <Widget>[\n        Text(\" hello world \"),\n        Text(\" I am Jack \"),\n      ],\n    ),\n    Row(\n      mainAxisSize: MainAxisSize.min,\n      mainAxisAlignment: MainAxisAlignment.center,\n      children: <Widget>[\n        Text(\" hello world \"),\n        Text(\" I am Jack \"),\n      ],\n    ),\n    Row(\n      mainAxisAlignment: MainAxisAlignment.end,\n      textDirection: TextDirection.rtl,\n      children: <Widget>[\n        Text(\" hello world \"),\n        Text(\" I am Jack \"),\n      ],\n    ),\n    Row(\n      crossAxisAlignment: CrossAxisAlignment.start,  \n      verticalDirection: VerticalDirection.up,\n      children: <Widget>[\n        Text(\" hello world \", style: TextStyle(fontSize: 30.0),),\n        Text(\" I am Jack \"),\n      ],\n    ),\n  ],\n);\n```\n\n实际运行结果如图4-1所示：\n\n![图4-1](../imgs/4-1.png)\n\n解释：第一个`Row`很简单，默认为居中对齐；第二个`Row`，由于`mainAxisSize`值为`MainAxisSize.min`，`Row`的宽度等于两个`Text`的宽度和，所以对齐是无意义的，所以会从左往右显示；第三个`Row`设置`textDirection`值为`TextDirection.rtl`，所以子组件会从右向左的顺序排列，而此时`MainAxisAlignment.end`表示左对齐，所以最终显示结果就是图中第三行的样子；第四个Row测试的是纵轴的对齐方式，由于两个子Text字体不一样，所以其高度也不同，我们指定了`verticalDirection`值为`VerticalDirection.up`，即从低向顶排列，而此时`crossAxisAlignment`值为`CrossAxisAlignment.start`表示底对齐。\n\n### Column\n\n`Column`可以在垂直方向排列其子组件。参数和`Row`一样，不同的是布局方向为垂直，主轴纵轴正好相反，读者可类比`Row`来理解，下面看一个例子：\n\n```dart\nimport 'package:flutter/material.dart';\n\nclass CenterColumnRoute extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return Column(\n      crossAxisAlignment: CrossAxisAlignment.center,\n      children: <Widget>[\n        Text(\"hi\"),\n        Text(\"world\"),\n      ],\n    );\n  }\n}\n```\n\n运行效果如图4-2所示：\n\n![图4-2示例](../imgs/4-2.png)\n\n\n\n解释：\n\n- 由于我们没有指定`Column`的`mainAxisSize`，所以使用默认值`MainAxisSize.max`，则`Column`会在垂直方向占用尽可能多的空间，此例中为屏幕高度。\n- 由于我们指定了 `crossAxisAlignment` 属性为`CrossAxisAlignment.center`，那么子项在`Column`纵轴方向（此时为水平方向）会居中对齐。注意，在水平方向对齐是有边界的，总宽度为`Column`占用空间的实际宽度，而实际的宽度取决于子项中宽度最大的Widget。在本例中，`Column`有两个子Widget，而显示“world”的`Text`宽度最大，所以`Column`的实际宽度则为`Text(\"world\")` 的宽度，所以居中对齐后`Text(\"hi\")`会显示在`Text(\"world\")`的中间部分。\n\n**实际上，`Row`和`Column`都只会在主轴方向占用尽可能大的空间，而纵轴的长度则取决于他们最大子元素的长度**。如果我们想让本例中的两个文本控件在整个手机屏幕中间对齐，我们有两种方法：\n\n- 将`Column`的宽度指定为屏幕宽度；这很简单，我们可以通过`ConstrainedBox`或`SizedBox`（我们将在后面章节中专门介绍这两个Widget）来强制更改宽度限制，例如：\n\n  ```dart\n  ConstrainedBox(\n    constraints: BoxConstraints(minWidth: double.infinity), \n    child: Column(\n      crossAxisAlignment: CrossAxisAlignment.center,\n      children: <Widget>[\n        Text(\"hi\"),\n        Text(\"world\"),\n      ],\n    ),\n  );\n  ```\n\n  将`minWidth`设为`double.infinity`，可以使宽度占用尽可能多的空间。\n\n- 使用`Center` Widget；我们将在后面章节中介绍。\n\n\n\n### 特殊情况\n\n如果`Row`里面嵌套`Row`，或者`Column`里面再嵌套`Column`，那么只有最外面的`Row`或`Column`会占用尽可能大的空间，里面`Row`或`Column`所占用的空间为实际大小，下面以`Column`为例说明：\n\n```dart\nContainer(\n  color: Colors.green,\n  child: Padding(\n    padding: const EdgeInsets.all(16.0),\n    child: Column(\n      crossAxisAlignment: CrossAxisAlignment.start,\n      mainAxisSize: MainAxisSize.max, //有效，外层Colum高度为整个屏幕\n      children: <Widget>[\n        Container(\n          color: Colors.red,\n          child: Column(\n            mainAxisSize: MainAxisSize.max,//无效，内层Colum高度为实际高度  \n            children: <Widget>[\n              Text(\"hello world \"),\n              Text(\"I am Jack \"),\n            ],\n          ),\n        )\n      ],\n    ),\n  ),\n);\n```\n\n运行效果如图4-3所示：\n\n![图4-3](../imgs/4-3.png)\n\n如果要让里面的`Column`占满外部`Column`，可以使用`Expanded` 组件：\n\n```dart\nExpanded( \n  child: Container(\n    color: Colors.red,\n    child: Column(\n      mainAxisAlignment: MainAxisAlignment.center, //垂直方向居中对齐\n      children: <Widget>[\n        Text(\"hello world \"),\n        Text(\"I am Jack \"),\n      ],\n    ),\n  ),\n)\n```\n\n运行效果如图4-4所示：\n\n![图4-4](../imgs/4-4.png)\n\n我们将在介绍弹性布局时详细介绍Expanded。\n\n"
  },
  {
    "path": "src/chapter4/stack.md",
    "content": "# 4.5 层叠布局 Stack、Positioned\n\n层叠布局和Web中的绝对定位、Android中的Frame布局是相似的，子组件可以根据距父容器四个角的位置来确定自身的位置。绝对定位允许子组件堆叠起来（按照代码中声明的顺序）。Flutter中使用`Stack`和`Positioned`这两个组件来配合实现绝对定位。`Stack`允许子组件堆叠，而`Positioned`用于根据`Stack`的四个角来确定子组件的位置。\n\n### Stack\n\n```dart\nStack({\n  this.alignment = AlignmentDirectional.topStart,\n  this.textDirection,\n  this.fit = StackFit.loose,\n  this.overflow = Overflow.clip,\n  List<Widget> children = const <Widget>[],\n})\n```\n\n- `alignment`：此参数决定如何去对齐没有定位（没有使用`Positioned`）或部分定位的子组件。所谓部分定位，在这里**特指没有在某一个轴上定位：**`left`、`right`为横轴，`top`、`bottom`为纵轴，只要包含某个轴上的一个定位属性就算在该轴上有定位。\n- `textDirection`：和`Row`、`Wrap`的`textDirection`功能一样，都用于确定`alignment`对齐的参考系，即：`textDirection`的值为`TextDirection.ltr`，则`alignment`的`start`代表左，`end`代表右，即`从左往右`的顺序；`textDirection`的值为`TextDirection.rtl`，则alignment的`start`代表右，`end`代表左，即`从右往左`的顺序。\n- `fit`：此参数用于确定**没有定位**的子组件如何去适应`Stack`的大小。`StackFit.loose`表示使用子组件的大小，`StackFit.expand`表示扩伸到`Stack`的大小。\n- `overflow`：此属性决定如何显示超出`Stack`显示空间的子组件；值为`Overflow.clip`时，超出部分会被剪裁（隐藏），值为`Overflow.visible` 时则不会。\n\n### Positioned\n\n```dart\nconst Positioned({\n  Key key,\n  this.left, \n  this.top,\n  this.right,\n  this.bottom,\n  this.width,\n  this.height,\n  @required Widget child,\n})\n```\n\n`left`、`top` 、`right`、 `bottom`分别代表离`Stack`左、上、右、底四边的距离。`width`和`height`用于指定需要定位元素的宽度和高度。注意，`Positioned`的`width`、`height` 和其它地方的意义稍微有点区别，此处用于配合`left`、`top` 、`right`、 `bottom`来定位组件，举个例子，在水平方向时，你只能指定`left`、`right`、`width`三个属性中的两个，如指定`left`和`width`后，`right`会自动算出(`left`+`width`)，如果同时指定三个属性则会报错，垂直方向同理。\n\n### 示例\n\n在下面的例子中，我们通过对几个`Text`组件的定位来演示`Stack`和`Positioned`的特性：\n\n```dart\n//通过ConstrainedBox来确保Stack占满屏幕\nConstrainedBox(\n  constraints: BoxConstraints.expand(),\n  child: Stack(\n    alignment:Alignment.center , //指定未定位或部分定位widget的对齐方式\n    children: <Widget>[\n      Container(child: Text(\"Hello world\",style: TextStyle(color: Colors.white)),\n        color: Colors.red,\n      ),\n      Positioned(\n        left: 18.0,\n        child: Text(\"I am Jack\"),\n      ),\n      Positioned(\n        top: 18.0,\n        child: Text(\"Your friend\"),\n      )        \n    ],\n  ),\n);\n```\n\n运行效果见图4-9：\n\n![图4-9](../imgs/4-9.png)\n\n\n\n由于第一个子文本组件`Text(\"Hello world\")`没有指定定位，并且`alignment`值为`Alignment.center`，所以它会居中显示。第二个子文本组件`Text(\"I am Jack\")`只指定了水平方向的定位(`left`)，所以属于部分定位，即垂直方向上没有定位，那么它在垂直方向的对齐方式则会按照`alignment`指定的对齐方式对齐，即垂直方向居中。对于第三个子文本组件`Text(\"Your friend\")`，和第二个`Text`原理一样，只不过是水平方向没有定位，则水平方向居中。\n\n我们给上例中的`Stack`指定一个`fit`属性，然后将三个子文本组件的顺序调整一下：\n\n```dart\nStack(\n  alignment:Alignment.center ,\n  fit: StackFit.expand, //未定位widget占满Stack整个空间\n  children: <Widget>[\n    Positioned(\n      left: 18.0,\n      child: Text(\"I am Jack\"),\n    ),\n    Container(child: Text(\"Hello world\",style: TextStyle(color: Colors.white)),\n      color: Colors.red,\n    ),\n    Positioned(\n      top: 18.0,\n      child: Text(\"Your friend\"),\n    )\n  ],\n),\n```\n\n显示效果如图4-10所示：\n\n![图4-10](../imgs/4-10.png)\n\n可以看到，由于第二个子文本组件没有定位，所以`fit`属性会对它起作用，就会占满`Stack`。由于`Stack`子元素是堆叠的，所以第一个子文本组件被第二个遮住了，而第三个在最上层，所以可以正常显示。\n\n"
  },
  {
    "path": "src/chapter4/wrap_and_flow.md",
    "content": "# 4.4 流式布局\n\n在介绍Row和Colum时，如果子widget超出屏幕范围，则会报溢出错误，如：\n\n```dart\nRow(\n  children: <Widget>[\n    Text(\"xxx\"*100)\n  ],\n);\n```\n\n运行效果如图4-6所示：\n\n![图4-6](../imgs/4-6.png)\n\n可以看到，右边溢出部分报错。这是因为Row默认只有一行，如果超出屏幕不会折行。我们把超出屏幕显示范围会自动折行的布局称为流式布局。Flutter中通过`Wrap`和`Flow`来支持流式布局，将上例中的Row换成Wrap后溢出部分则会自动折行，下面我们分别介绍`Wrap`和`Flow`.\n\n## 4.4.1 Wrap\n\n下面是Wrap的定义:\n\n```dart\nWrap({\n  ...\n  this.direction = Axis.horizontal,\n  this.alignment = WrapAlignment.start,\n  this.spacing = 0.0,\n  this.runAlignment = WrapAlignment.start,\n  this.runSpacing = 0.0,\n  this.crossAxisAlignment = WrapCrossAlignment.start,\n  this.textDirection,\n  this.verticalDirection = VerticalDirection.down,\n  List<Widget> children = const <Widget>[],\n})\n```\n\n我们可以看到Wrap的很多属性在`Row`（包括`Flex`和`Column`）中也有，如`direction`、`crossAxisAlignment`、`textDirection`、`verticalDirection`等，这些参数意义是相同的，我们不再重复介绍，读者可以查阅前面介绍`Row`的部分。读者可以认为`Wrap`和`Flex`（包括`Row`和`Column`）除了超出显示范围后`Wrap`会折行外，其它行为基本相同。下面我们看一下`Wrap`特有的几个属性：\n\n- `spacing`：主轴方向子widget的间距\n- `runSpacing`：纵轴方向的间距\n- `runAlignment`：纵轴方向的对齐方式\n\n下面看一个示例子：\n\n```dart\nWrap(\n  spacing: 8.0, // 主轴(水平)方向间距\n  runSpacing: 4.0, // 纵轴（垂直）方向间距\n  alignment: WrapAlignment.center, //沿主轴方向居中\n  children: <Widget>[\n    new Chip(\n      avatar: new CircleAvatar(backgroundColor: Colors.blue, child: Text('A')),\n      label: new Text('Hamilton'),\n    ),\n    new Chip(\n      avatar: new CircleAvatar(backgroundColor: Colors.blue, child: Text('M')),\n      label: new Text('Lafayette'),\n    ),\n    new Chip(\n      avatar: new CircleAvatar(backgroundColor: Colors.blue, child: Text('H')),\n      label: new Text('Mulligan'),\n    ),\n    new Chip(\n      avatar: new CircleAvatar(backgroundColor: Colors.blue, child: Text('J')),\n      label: new Text('Laurens'),\n    ),\n  ],\n)\n```\n\n运行效果如图4-7所示：\n\n![图4-7](../imgs/4-7.png)\n\n## 4.4.2 Flow\n\n我们一般很少会使用`Flow`，因为其过于复杂，需要自己实现子widget的位置转换，在很多场景下首先要考虑的是`Wrap`是否满足需求。`Flow`主要用于一些需要自定义布局策略或性能要求较高(如动画中)的场景。`Flow`有如下优点：\n\n- 性能好；`Flow`是一个对子组件尺寸以及位置调整非常高效的控件，`Flow`用转换矩阵在对子组件进行位置调整的时候进行了优化：在`Flow`定位过后，如果子组件的尺寸或者位置发生了变化，在`FlowDelegate`中的`paintChildren()`方法中调用`context.paintChild` 进行重绘，而`context.paintChild`在重绘时使用了转换矩阵，并没有实际调整组件位置。\n- 灵活；由于我们需要自己实现`FlowDelegate`的`paintChildren()`方法，所以我们需要自己计算每一个组件的位置，因此，可以自定义布局策略。\n\n缺点：\n\n- 使用复杂。\n- 不能自适应子组件大小，必须通过指定父容器大小或实现`TestFlowDelegate`的`getSize`返回固定大小。\n\n示例：\n\n我们对六个色块进行自定义流式布局：\n\n```dart\nFlow(\n  delegate: TestFlowDelegate(margin: EdgeInsets.all(10.0)),\n  children: <Widget>[\n    new Container(width: 80.0, height:80.0, color: Colors.red,),\n    new Container(width: 80.0, height:80.0, color: Colors.green,),\n    new Container(width: 80.0, height:80.0, color: Colors.blue,),\n    new Container(width: 80.0, height:80.0,  color: Colors.yellow,),\n    new Container(width: 80.0, height:80.0, color: Colors.brown,),\n    new Container(width: 80.0, height:80.0,  color: Colors.purple,),\n  ],\n)\n```\n\n实现TestFlowDelegate:\n\n```dart\nclass TestFlowDelegate extends FlowDelegate {\n  EdgeInsets margin = EdgeInsets.zero;\n  TestFlowDelegate({this.margin});\n  @override\n  void paintChildren(FlowPaintingContext context) {\n    var x = margin.left;\n    var y = margin.top;\n    //计算每一个子widget的位置  \n    for (int i = 0; i < context.childCount; i++) {\n      var w = context.getChildSize(i).width + x + margin.right;\n      if (w < context.size.width) {\n        context.paintChild(i,\n            transform: new Matrix4.translationValues(\n                x, y, 0.0));\n        x = w + margin.left;\n      } else {\n        x = margin.left;\n        y += context.getChildSize(i).height + margin.top + margin.bottom;\n        //绘制子widget(有优化)  \n        context.paintChild(i,\n            transform: new Matrix4.translationValues(\n                x, y, 0.0));\n         x += context.getChildSize(i).width + margin.left + margin.right;\n      }\n    }\n  }\n\n  @override\n  getSize(BoxConstraints constraints){\n    //指定Flow的大小  \n    return Size(double.infinity,200.0);\n  }\n\n  @override\n  bool shouldRepaint(FlowDelegate oldDelegate) {\n    return oldDelegate != this;\n  }\n}\n```\n\n运行效果见图4-8：\n\n![图4-8](../imgs/4-8.png)\n\n可以看到我们主要的任务就是实现`paintChildren`，它的主要任务是确定每个子widget位置。由于Flow不能自适应子widget的大小，我们通过在`getSize`返回一个固定大小来指定Flow的大小。\n\n"
  },
  {
    "path": "src/chapter5/clip.md",
    "content": "# 5.7 剪裁（Clip）\n\nFlutter中提供了一些剪裁函数，用于对组件进行剪裁。\n\n| 剪裁Widget | 作用                                                     |\n| ---------- | -------------------------------------------------------- |\n| ClipOval   | 子组件为正方形时剪裁为内贴圆形，为矩形时，剪裁为内贴椭圆 |\n| ClipRRect  | 将子组件剪裁为圆角矩形                                   |\n| ClipRect   | 剪裁子组件到实际占用的矩形大小（溢出部分剪裁）           |\n\n下面看一个例子：\n\n```dart\nimport 'package:flutter/material.dart';\n\nclass ClipTestRoute extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    // 头像  \n    Widget avatar = Image.asset(\"imgs/avatar.png\", width: 60.0);\n    return Center(\n      child: Column(\n        children: <Widget>[\n          avatar, //不剪裁\n          ClipOval(child: avatar), //剪裁为圆形\n          ClipRRect( //剪裁为圆角矩形\n            borderRadius: BorderRadius.circular(5.0),\n            child: avatar,\n          ), \n          Row(\n            mainAxisAlignment: MainAxisAlignment.center,\n            children: <Widget>[\n              Align(\n                alignment: Alignment.topLeft,\n                widthFactor: .5,//宽度设为原来宽度一半，另一半会溢出\n                child: avatar,\n              ),\n              Text(\"你好世界\", style: TextStyle(color: Colors.green),)\n            ],\n          ),\n          Row(\n            mainAxisAlignment: MainAxisAlignment.center,\n            children: <Widget>[\n              ClipRect(//将溢出部分剪裁\n                child: Align(\n                  alignment: Alignment.topLeft,\n                  widthFactor: .5,//宽度设为原来宽度一半\n                  child: avatar,\n                ),\n              ),\n              Text(\"你好世界\",style: TextStyle(color: Colors.green))\n            ],\n          ),\n        ],\n      ),\n    );\n  }\n}\n```\n\n\n\n运行效果如图5-24所示：\n\n![图5-24](../imgs/5-24.png)\n\n\n\n上面示例代码注释比较详细，在此不再赘述。但值得一提的是最后的两个`Row`！它们通过`Align`设置`widthFactor`为0.5后，图片的实际宽度等于60×0.5，即原宽度一半，但此时图片溢出部分依然会显示，所以第一个“你好世界”会和图片的另一部分重合，为了剪裁掉溢出部分，我们在第二个`Row`中通过`ClipRect`将溢出部分剪裁掉了。\n\n### CustomClipper\n\n如果我们想剪裁子组件的特定区域，比如，在上面示例的图片中，如果我们只想截取图片中部40×30像素的范围应该怎么做？这时我们可以使用`CustomClipper`来自定义剪裁区域，实现代码如下：\n\n首先，自定义一个`CustomClipper`：\n\n```dart\nclass MyClipper extends CustomClipper<Rect> {\n  @override\n  Rect getClip(Size size) => Rect.fromLTWH(10.0, 15.0, 40.0, 30.0);\n\n  @override\n  bool shouldReclip(CustomClipper<Rect> oldClipper) => false;\n}\n```\n\n- `getClip()`是用于获取剪裁区域的接口，由于图片大小是60×60，我们返回剪裁区域为`Rect.fromLTWH(10.0, 15.0, 40.0, 30.0)`，即图片中部40×30像素的范围。\n- `shouldReclip()` 接口决定是否重新剪裁。如果在应用中，剪裁区域始终不会发生变化时应该返回`false`，这样就不会触发重新剪裁，避免不必要的性能开销。如果剪裁区域会发生变化（比如在对剪裁区域执行一个动画），那么变化后应该返回`true`来重新执行剪裁。\n\n然后，我们通过`ClipRect`来执行剪裁，为了看清图片实际所占用的位置，我们设置一个红色背景：\n\n```dart\nDecoratedBox(\n  decoration: BoxDecoration(\n    color: Colors.red\n  ),\n  child: ClipRect(\n      clipper: MyClipper(), //使用自定义的clipper\n      child: avatar\n  ),\n)\n```\n\n运行效果如图5-25所示：\n\n![图5-25](../imgs/5-25.png)\n\n可以看到我们的剪裁成功了，但是图片所占用的空间大小仍然是60×60（红色区域），这是因为剪裁是在layout完成后的绘制阶段进行的，所以不会影响组件的大小，这和`Transform`原理是相似的。\n\n\n\n\n\n"
  },
  {
    "path": "src/chapter5/constrainedbox_and_sizebox.md",
    "content": "# 5.2 尺寸限制类容器\n\n尺寸限制类容器用于限制容器大小，Flutter中提供了多种这样的容器，如`ConstrainedBox`、`SizedBox`、`UnconstrainedBox`、`AspectRatio`等，本节将介绍一些常用的。\n\n## 5.2.1 ConstrainedBox\n\n`ConstrainedBox`用于对子组件添加额外的约束。例如，如果你想让子组件的最小高度是80像素，你可以使用`const BoxConstraints(minHeight: 80.0)`作为子组件的约束。\n\n#### 示例\n\n我们先定义一个`redBox`，它是一个背景颜色为红色的盒子，不指定它的宽度和高度：\n\n```dart\nWidget redBox=DecoratedBox(\n  decoration: BoxDecoration(color: Colors.red),\n);\n```\n\n我们实现一个最小高度为50，宽度尽可能大的红色容器。\n\n```dart\nConstrainedBox(\n  constraints: BoxConstraints(\n    minWidth: double.infinity, //宽度尽可能大\n    minHeight: 50.0 //最小高度为50像素\n  ),\n  child: Container(\n      height: 5.0, \n      child: redBox \n  ),\n)\n```\n\n运行效果如图5-2所示：\n\n![图5-2](../imgs/5-2.png)\n\n可以看到，我们虽然将Container的高度设置为5像素，但是最终却是50像素，这正是ConstrainedBox的最小高度限制生效了。如果将Container的高度设置为80像素，那么最终红色区域的高度也会是80像素，因为在此示例中，ConstrainedBox只限制了最小高度，并未限制最大高度。\n\n#### BoxConstraints\n\nBoxConstraints用于设置限制条件，它的定义如下：\n\n```dart\nconst BoxConstraints({\n  this.minWidth = 0.0, //最小宽度\n  this.maxWidth = double.infinity, //最大宽度\n  this.minHeight = 0.0, //最小高度\n  this.maxHeight = double.infinity //最大高度\n})\n```\n\nBoxConstraints还定义了一些便捷的构造函数，用于快速生成特定限制规则的BoxConstraints，如`BoxConstraints.tight(Size size)`，它可以生成给定大小的限制；`const BoxConstraints.expand()`可以生成一个尽可能大的用以填充另一个容器的BoxConstraints。除此之外还有一些其它的便捷函数，读者可以查看[API文档](https://docs.flutter.io/flutter/rendering/BoxConstraints-class.html)。\n\n## 5.2.2 SizedBox\n\n`SizedBox`用于给子元素指定固定的宽高，如：\n\n```dart\nSizedBox(\n  width: 80.0,\n  height: 80.0,\n  child: redBox\n)\n```\n运行效果如图5-3所示：\n\n![图5-3](../imgs/5-3.png)\n\n实际上`SizedBox`只是`ConstrainedBox`的一个定制，上面代码等价于：\n\n\n```dart\nConstrainedBox(\n  constraints: BoxConstraints.tightFor(width: 80.0,height: 80.0),\n  child: redBox, \n)\n```\n\n而`BoxConstraints.tightFor(width: 80.0,height: 80.0)`等价于：\n\n```dart\nBoxConstraints(minHeight: 80.0,maxHeight: 80.0,minWidth: 80.0,maxWidth: 80.0)\n```\n\n而实际上`ConstrainedBox`和`SizedBox`都是通过`RenderConstrainedBox`来渲染的，我们可以看到`ConstrainedBox`和`SizedBox`的`createRenderObject()`方法都返回的是一个`RenderConstrainedBox`对象：\n\n```dart\n@override\nRenderConstrainedBox createRenderObject(BuildContext context) {\n  return new RenderConstrainedBox(\n    additionalConstraints: ...,\n  );\n}\n```\n\n\n\n## 5.2.3 多重限制\n\n如果某一个组件有多个父级`ConstrainedBox`限制，那么最终会是哪个生效？我们看一个例子：\n\n```dart\nConstrainedBox(\n    constraints: BoxConstraints(minWidth: 60.0, minHeight: 60.0), //父\n    child: ConstrainedBox(\n      constraints: BoxConstraints(minWidth: 90.0, minHeight: 20.0),//子\n      child: redBox,\n    )\n)\n```\n\n上面我们有父子两个`ConstrainedBox`，他们的限制条件不同，运行后效果如图5-4所示：\n\n![图5-4](../imgs/5-4.png)\n\n最终显示效果是宽90，高60，也就是说是子`ConstrainedBox`的`minWidth`生效，而`minHeight`是父`ConstrainedBox`生效。单凭这个例子，我们还总结不出什么规律，我们将上例中父子限制条件换一下：\n\n```dart\nConstrainedBox(\n    constraints: BoxConstraints(minWidth: 90.0, minHeight: 20.0),\n    child: ConstrainedBox(\n      constraints: BoxConstraints(minWidth: 60.0, minHeight: 60.0),\n      child: redBox,\n    )\n)\n```\n\n运行效果如图5-5所示：\n\n![图5-5](../imgs/5-5.png)\n\n最终的显示效果仍然是90，高60，效果相同，但意义不同，因为此时`minWidth`生效的是父`ConstrainedBox`，而`minHeight`是子`ConstrainedBox`生效。\n\n通过上面示例，我们发现有多重限制时，对于`minWidth`和`minHeight`来说，是取父子中相应数值较大的。实际上，只有这样才能保证父限制与子限制不冲突。\n\n> 思考题：对于`maxWidth`和`maxHeight`，多重限制的策略是什么样的呢？\n\n\n\n## 5.2.4 UnconstrainedBox\n\n`UnconstrainedBox`不会对子组件产生任何限制，它允许其子组件按照其本身大小绘制。一般情况下，我们会很少直接使用此组件，但在\"去除\"多重限制的时候也许会有帮助，我们看下下面的代码：\n\n```dart\nConstrainedBox(\n    constraints: BoxConstraints(minWidth: 60.0, minHeight: 100.0),  //父\n    child: UnconstrainedBox( //“去除”父级限制\n      child: ConstrainedBox(\n        constraints: BoxConstraints(minWidth: 90.0, minHeight: 20.0),//子\n        child: redBox,\n      ),\n    )\n)\n```\n\n上面代码中，如果没有中间的`UnconstrainedBox`，那么根据上面所述的多重限制规则，那么最终将显示一个90×100的红色框。但是由于` UnconstrainedBox` “去除”了父`ConstrainedBox`的限制，则最终会按照子`ConstrainedBox`的限制来绘制`redBox`，即90×20：\n\n![图5-6](../imgs/5-6.png)\n\n但是，读者请注意，`UnconstrainedBox`对父组件限制的“去除”并非是真正的去除：上面例子中虽然红色区域大小是90×20，但上方仍然有80的空白空间。也就是说父限制的`minHeight`(100.0)仍然是生效的，只不过它不影响最终子元素`redBox`的大小，但仍然还是占有相应的空间，可以认为此时的父`ConstrainedBox`是作用于子`UnconstrainedBox`上，而`redBox`只受子`ConstrainedBox`限制，这一点请读者务必注意。\n\n那么有什么方法可以彻底去除父`ConstrainedBox`的限制吗？答案是否定的！所以在此提示读者，在定义一个通用的组件时，如果要对子组件指定限制，那么一定要注意，因为一旦指定限制条件，子组件如果要进行相关自定义大小时将可能非常困难，因为子组件在不更改父组件的代码的情况下无法彻底去除其限制条件。\n\n在实际开发中，当我们发现已经使用`SizedBox`或`ConstrainedBox`给子元素指定了宽高，但是仍然没有效果时，几乎可以断定：已经有父元素已经设置了限制！举个例子，如Material组件库中的`AppBar`（导航栏）的右侧菜单中，我们使用`SizedBox`指定了loading按钮的大小，代码如下：\n\n```dart\n AppBar(\n   title: Text(title),\n   actions: <Widget>[\n         SizedBox(\n             width: 20, \n             height: 20,\n             child: CircularProgressIndicator(\n                 strokeWidth: 3,\n                 valueColor: AlwaysStoppedAnimation(Colors.white70),\n             ),\n         )\n   ],\n)\n```\n\n上面代码运行后，效果如图5-7所示：\n\n![图5-6](../imgs/5-7.png)\n\n我们会发现右侧loading按钮大小并没有发生变化！这正是因为`AppBar`中已经指定了`actions`按钮的限制条件，所以我们要自定义loading按钮大小，就必须通过`UnconstrainedBox`来“去除”父元素的限制，代码如下：\n\n```dart\nAppBar(\n  title: Text(title),\n  actions: <Widget>[\n      UnconstrainedBox(\n            child: SizedBox(\n              width: 20,\n              height: 20,\n              child: CircularProgressIndicator(\n                strokeWidth: 3,\n                valueColor: AlwaysStoppedAnimation(Colors.white70),\n              ),\n          ),\n      )\n  ],\n)\n```\n\n运行后效果如图5-8所示：\n\n![图5-8](../imgs/5-8.png)\n\n生效了！\n\n## 5.2.4 其它尺寸限制类容器\n\n除了上面介绍的这些常用的尺寸限制类容器外，还有一些其他的尺寸限制类容器，比如`AspectRatio`，它可以指定子组件的长宽比、`LimitedBox` 用于指定最大宽高、`FractionallySizedBox` 可以根据父容器宽高的百分比来设置子组件宽高等，由于这些容器使用起来都比较简单，我们便不再赘述，读者可以自行了解。"
  },
  {
    "path": "src/chapter5/container.md",
    "content": "# 5.5 Container\n\n我们在前面的章节示例中多次用到过`Container`组件，本节我们就详细介绍一下`Container`组件。`Container`是一个组合类容器，它本身不对应具体的`RenderObject`，它是`DecoratedBox`、`ConstrainedBox、Transform`、`Padding`、`Align`等组件组合的一个多功能容器，所以我们只需通过一个`Container`组件可以实现同时需要装饰、变换、限制的场景。下面是`Container`的定义：\n\n```dart\nContainer({\n  this.alignment,\n  this.padding, //容器内补白，属于decoration的装饰范围\n  Color color, // 背景色\n  Decoration decoration, // 背景装饰\n  Decoration foregroundDecoration, //前景装饰\n  double width,//容器的宽度\n  double height, //容器的高度\n  BoxConstraints constraints, //容器大小的限制条件\n  this.margin,//容器外补白，不属于decoration的装饰范围\n  this.transform, //变换\n  this.child,\n})\n```\n\n`Container`的大多数属性在介绍其它容器时都已经介绍过了，不再赘述，但有两点需要说明：\n\n- 容器的大小可以通过`width`、`height`属性来指定，也可以通过`constraints`来指定；如果它们同时存在时，`width`、`height`优先。实际上Container内部会根据`width`、`height`来生成一个`constraints`。\n- `color`和`decoration`是互斥的，如果同时设置它们则会报错！实际上，当指定`color`时，`Container`内会自动创建一个`decoration`。\n\n### 实例\n\n我们通过`Container`来实现如图5-16所示的卡片：\n\n![图5-16](../imgs/5-16.png)\n\n\n\n实现代码如下：\n\n```dart\nContainer(\n  margin: EdgeInsets.only(top: 50.0, left: 120.0), //容器外填充\n  constraints: BoxConstraints.tightFor(width: 200.0, height: 150.0), //卡片大小\n  decoration: BoxDecoration(//背景装饰\n      gradient: RadialGradient( //背景径向渐变\n          colors: [Colors.red, Colors.orange],\n          center: Alignment.topLeft,\n          radius: .98\n      ),\n      boxShadow: [ //卡片阴影\n        BoxShadow(\n            color: Colors.black54,\n            offset: Offset(2.0, 2.0),\n            blurRadius: 4.0\n        )\n      ]\n  ),\n  transform: Matrix4.rotationZ(.2), //卡片倾斜变换\n  alignment: Alignment.center, //卡片内文字居中\n  child: Text( //卡片文字\n    \"5.20\", style: TextStyle(color: Colors.white, fontSize: 40.0),\n  ),\n);\n```\n\n\n\n可以看到`Container`具备多种组件的功能，通过查看`Container`源码，我们会很容易发现它正是前面我们介绍过的多种组件组合而成。在Flutter中，`Container`组件也正是组合优先于继承的实例。\n\n### Padding和Margin\n\n接下来我们来研究一下`Container`组件`margin`和`padding`属性的区别:\n\n```dart\n...\nContainer(\n  margin: EdgeInsets.all(20.0), //容器外补白\n  color: Colors.orange,\n  child: Text(\"Hello world!\"),\n),\nContainer(\n  padding: EdgeInsets.all(20.0), //容器内补白\n  color: Colors.orange,\n  child: Text(\"Hello world!\"),\n),\n...\n```\n\n![图5-17](../imgs/5-17.png)\n\n可以发现，直观的感觉就是`margin`的留白是在容器外部，而`padding`的留白是在容器内部，读者需要记住这个差异。事实上，`Container`内`margin`和`padding`都是通过`Padding` 组件来实现的，上面的示例代码实际上等价于：\n\n```dart\n...\nPadding(\n  padding: EdgeInsets.all(20.0),\n  child: DecoratedBox(\n    decoration: BoxDecoration(color: Colors.orange),\n    child: Text(\"Hello world!\"),\n  ),\n),\nDecoratedBox(\n  decoration: BoxDecoration(color: Colors.orange),\n  child: Padding(\n    padding: const EdgeInsets.all(20.0),\n    child: Text(\"Hello world!\"),\n  ),\n),\n...    \n```\n\n"
  },
  {
    "path": "src/chapter5/decoratedbox.md",
    "content": "# 5.3 装饰容器DecoratedBox\n\n`DecoratedBox`可以在其子组件绘制前(或后)绘制一些装饰（Decoration），如背景、边框、渐变等。`DecoratedBox`定义如下：\n\n```dart\nconst DecoratedBox({\n  Decoration decoration,\n  DecorationPosition position = DecorationPosition.background,\n  Widget child\n})\n```\n\n- `decoration`：代表将要绘制的装饰，它的类型为`Decoration`。`Decoration`是一个抽象类，它定义了一个接口 `createBoxPainter()`，子类的主要职责是需要通过实现它来创建一个画笔，该画笔用于绘制装饰。\n- `position`：此属性决定在哪里绘制`Decoration`，它接收`DecorationPosition`的枚举类型，该枚举类有两个值：\n  - `background`：在子组件之后绘制，即背景装饰。\n  - `foreground`：在子组件之上绘制，即前景。\n\n#### BoxDecoration\n\n我们通常会直接使用`BoxDecoration`类，它是一个Decoration的子类，实现了常用的装饰元素的绘制。\n\n```dart\nBoxDecoration({\n  Color color, //颜色\n  DecorationImage image,//图片\n  BoxBorder border, //边框\n  BorderRadiusGeometry borderRadius, //圆角\n  List<BoxShadow> boxShadow, //阴影,可以指定多个\n  Gradient gradient, //渐变\n  BlendMode backgroundBlendMode, //背景混合模式\n  BoxShape shape = BoxShape.rectangle, //形状\n})\n```\n\n各个属性名都是自解释的，详情读者可以查看API文档。下面我们实现一个带阴影的背景色渐变的按钮：\n\n```dart\n DecoratedBox(\n    decoration: BoxDecoration(\n      gradient: LinearGradient(colors:[Colors.red,Colors.orange[700]]), //背景渐变\n      borderRadius: BorderRadius.circular(3.0), //3像素圆角\n      boxShadow: [ //阴影\n        BoxShadow(\n            color:Colors.black54,\n            offset: Offset(2.0,2.0),\n            blurRadius: 4.0\n        )\n      ]\n    ),\n  child: Padding(padding: EdgeInsets.symmetric(horizontal: 80.0, vertical: 18.0),\n    child: Text(\"Login\", style: TextStyle(color: Colors.white),),\n  )\n)\n```\n\n运行后效果如图5-9所示：\n\n![图5-9](../imgs/5-9.png)\n\n怎么样，通过`BoxDecoration`我们实现了一个渐变按钮的外观，但此示例还不是一个标准的按钮，因为它还不能响应点击事件，我们将在后面“自定义组件”一章中实现一个完整功能的`GradientButton`。另外，上面的例子中使用了`LinearGradient`类，它用于定义线性渐变的类，Flutter中还提供了其它渐变配置类，如`RadialGradient`、`SweepGradient`，读者若有需要可以自行查看API文档。\n"
  },
  {
    "path": "src/chapter5/index.md",
    "content": "# 容器类Widget\n\n容器类Widget和布局类Widget都作用于其子Widget，不同的是：\n\n- 布局类Widget一般都需要接收一个widget数组（children），他们直接或间接继承自（或包含）MultiChildRenderObjectWidget ；而容器类Widget一般只需要接收一个子Widget（child），他们直接或间接继承自（或包含）SingleChildRenderObjectWidget。\n- 布局类Widget是按照一定的排列方式来对其子Widget进行排列；而容器类Widget一般只是包装其子Widget，对其添加一些修饰（补白或背景色等）、变换(旋转或剪裁等)、或限制(大小等)。\n\n注意，Flutter官方并没有对Widget进行官方分类，我们对其分类主要是为了方便讨论和对Widget功能区分的记忆。\n\n## 本章目录\n\n* [5.1：填充（Padding）](padding.md)\n* [5.2：尺寸限制类容器（ConstrainedBox等）](constrainedbox_and_sizebox.md)\n* [5.3：装饰容器（DecoratedBox）](decoratedbox.md)      \n* [5.4：变换（Transform）](transform.md) \n* [5.5：Container容器](container.md) \n* [5.6：Scaffold、TabBar、底部导航](material_scaffold.md) \n* [5.7：剪裁（Clip）](clip.md) \n"
  },
  {
    "path": "src/chapter5/material_scaffold.md",
    "content": "# 5.6 Scaffold、TabBar、底部导航\n\nMaterial组件库提供了丰富多样的组件，本节介绍一些常用的组件，其余的读者可以自行查看文档或Flutter Gallery中Material组件部分的示例。\n\n> Flutter Gallery是Flutter官方提供的Flutter Demo，源码位于flutter源码中的examples目录下，笔者强烈建议用户将Flutter Gallery示例跑起来，它是一个很全面的Flutter示例应用，是非常好的参考Demo，也是笔者学习Flutter的第一手资料。\n\n## 5.6.1 Scaffold\n\n一个完整的路由页可能会包含导航栏、抽屉菜单(Drawer)以及底部Tab导航菜单等。如果每个路由页面都需要开发者自己手动去实现这些，这会是一件非常麻烦且无聊的事。幸运的是，Flutter Material组件库提供了一些现成的组件来减少我们的开发任务。`Scaffold`是一个路由页的骨架，我们使用它可以很容易地拼装出一个完整的页面。\n\n### 示例\n\n我们实现一个页面，它包含：\n\n1. 一个导航栏\n2. 导航栏右边有一个分享按钮\n3. 有一个抽屉菜单\n4. 有一个底部导航\n5. 右下角有一个悬浮的动作按钮\n\n最终效果如图5-18、图5-19所示：\n\n![图5-18](../imgs/5-18.png) ![图5-19](../imgs/5-19.png)\n\n\n\n实现代码如下：\n\n```dart\nclass ScaffoldRoute extends StatefulWidget {\n  @override\n  _ScaffoldRouteState createState() => _ScaffoldRouteState();\n}\n\nclass _ScaffoldRouteState extends State<ScaffoldRoute> {\n  int _selectedIndex = 1;\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar( //导航栏\n        title: Text(\"App Name\"), \n        actions: <Widget>[ //导航栏右侧菜单\n          IconButton(icon: Icon(Icons.share), onPressed: () {}),\n        ],\n      ),\n      drawer: new MyDrawer(), //抽屉\n      bottomNavigationBar: BottomNavigationBar( // 底部导航\n        items: <BottomNavigationBarItem>[\n          BottomNavigationBarItem(icon: Icon(Icons.home), title: Text('Home')),\n          BottomNavigationBarItem(icon: Icon(Icons.business), title: Text('Business')),\n          BottomNavigationBarItem(icon: Icon(Icons.school), title: Text('School')),\n        ],\n        currentIndex: _selectedIndex,\n        fixedColor: Colors.blue,\n        onTap: _onItemTapped,\n      ),\n      floatingActionButton: FloatingActionButton( //悬浮按钮\n          child: Icon(Icons.add),\n          onPressed:_onAdd\n      ),\n    );\n  }\n  void _onItemTapped(int index) {\n    setState(() {\n      _selectedIndex = index;\n    });\n  }\n  void _onAdd(){\n  }\n}\n```\n\n上面代码中我们用到了如下组件：\n\n| 组件名称             | 解释           |\n| -------------------- | -------------- |\n| AppBar               | 一个导航栏骨架 |\n| MyDrawer             | 抽屉菜单       |\n| BottomNavigationBar  | 底部导航栏     |\n| FloatingActionButton | 漂浮按钮       |\n\n下面我们来分别介绍一下它们。\n\n## 5.6.2 AppBar\n\n`AppBar`是一个Material风格的导航栏，通过它可以设置导航栏标题、导航栏菜单、导航栏底部的Tab标题等。下面我们看看AppBar的定义：\n\n```dart\nAppBar({\n  Key key,\n  this.leading, //导航栏最左侧Widget，常见为抽屉菜单按钮或返回按钮。\n  this.automaticallyImplyLeading = true, //如果leading为null，是否自动实现默认的leading按钮\n  this.title,// 页面标题\n  this.actions, // 导航栏右侧菜单\n  this.bottom, // 导航栏底部菜单，通常为Tab按钮组\n  this.elevation = 4.0, // 导航栏阴影\n  this.centerTitle, //标题是否居中 \n  this.backgroundColor,\n  ...   //其它属性见源码注释\n})\n```\n\n如果给`Scaffold`添加了抽屉菜单，默认情况下`Scaffold`会自动将`AppBar`的`leading`设置为菜单按钮（如上面截图所示），点击它便可打开抽屉菜单。如果我们想自定义菜单图标，可以手动来设置`leading`，如：\n\n```dart\nScaffold(\n  appBar: AppBar(\n    title: Text(\"App Name\"),\n    leading: Builder(builder: (context) {\n      return IconButton(\n        icon: Icon(Icons.dashboard, color: Colors.white), //自定义图标\n        onPressed: () {\n          // 打开抽屉菜单  \n          Scaffold.of(context).openDrawer(); \n        },\n      );\n    }),\n    ...  \n  )  \n```\n\n代码运行效果如图5-20所示：\n\n![图5-20](../imgs/5-20.png)\n\n可以看到左侧菜单已经替换成功。\n\n代码中打开抽屉菜单的方法在`ScaffoldState`中，通过`Scaffold.of(context)`可以获取父级最近的`Scaffold` 组件的`State`对象。\n\n### TabBar\n\n下面我们通过“bottom”属性来添加一个导航栏底部Tab按钮组，将要实现的效果如图5-21所示：\n\n![图5-21](../imgs/5-21.png)\n\nMaterial组件库中提供了一个`TabBar`组件，它可以快速生成`Tab`菜单，下面是上图对应的源码：\n\n```dart\nclass _ScaffoldRouteState extends State<ScaffoldRoute>\n    with SingleTickerProviderStateMixin {\n\n  TabController _tabController; //需要定义一个Controller\n  List tabs = [\"新闻\", \"历史\", \"图片\"];\n\n  @override\n  void initState() {\n    super.initState();\n    // 创建Controller  \n    _tabController = TabController(length: tabs.length, vsync: this);\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        ... //省略无关代码\n        bottom: TabBar(   //生成Tab菜单\n          controller: _tabController,\n          tabs: tabs.map((e) => Tab(text: e)).toList()\n        ),\n      ),\n      ... //省略无关代码\n\n  }\n```\n\n上面代码首先创建了一个`TabController` ，它是用于控制/监听`Tab`菜单切换的。接下来通过TabBar生成了一个底部菜单栏，`TabBar`的`tabs`属性接受一个Widget数组，表示每一个Tab子菜单，我们可以自定义，也可以像示例中一样直接使用`Tab` 组件，它是Material组件库提供的Material风格的Tab菜单。\n\n`Tab`组件有三个可选参数，除了可以指定文字外，还可以指定Tab菜单图标，或者直接自定义组件样式。`Tab`组件定义如下：\n\n```dart\nTab({\n  Key key,\n  this.text, // 菜单文本\n  this.icon, // 菜单图标\n  this.child, // 自定义组件样式\n})\n```\n\n开发者可以根据实际需求来定制。\n\n### TabBarView\n\n通过`TabBar`我们只能生成一个静态的菜单，真正的Tab页还没有实现。由于`Tab`菜单和Tab页的切换需要同步，我们需要通过`TabController`去监听Tab菜单的切换去切换Tab页，代码如：\n\n```dart\n_tabController.addListener((){  \n  switch(_tabController.index){\n    case 1: ...;\n    case 2: ... ;   \n  }\n});\n```\n\n如果我们Tab页可以滑动切换的话，还需要在滑动过程中更新TabBar指示器的偏移！显然，要手动处理这些是很麻烦的，为此，Material库提供了一个`TabBarView`组件，通过它不仅可以轻松的实现Tab页，而且可以非常容易的配合TabBar来实现同步切换和滑动状态同步，示例如下：\n\n```dart\nScaffold(\n  appBar: AppBar(\n    ... //省略无关代码\n    bottom: TabBar(\n      controller: _tabController,\n      tabs: tabs.map((e) => Tab(text: e)).toList()),\n  ),\n  drawer: new MyDrawer(),\n  body: TabBarView(\n    controller: _tabController,\n    children: tabs.map((e) { //创建3个Tab页\n      return Container(\n        alignment: Alignment.center,\n        child: Text(e, textScaleFactor: 5),\n      );\n    }).toList(),\n  ),\n  ... // 省略无关代码  \n)    \n```\n\n运行后效果如图5-22所示：\n\n![图5-22](../imgs/5-22.png)\n\n现在，无论是点击导航栏Tab菜单还是在页面上左右滑动，Tab页面都会切换，并且Tab菜单的状态和Tab页面始终保持同步！那它们是如何实现同步的呢？细心的读者可能已经发现，上例中`TabBar`和`TabBarView`的`controller`是同一个！正是如此，`TabBar`和`TabBarView`正是通过同一个`controller`来实现菜单切换和滑动状态同步的，有关`TabController`的详细信息，我们不在本书做过多介绍，使用时读者直接查看SDK即可。\n\n另外，Material组件库也提供了一个`PageView` 组件，它和`TabBarView`功能相似，读者可以自行了解一下。\n\n\n\n## 5.6.3 抽屉菜单Drawer\n\n`Scaffold`的`drawer`和`endDrawer`属性可以分别接受一个Widget来作为页面的左、右抽屉菜单。如果开发者提供了抽屉菜单，那么当用户手指从屏幕左（或右）侧向里滑动时便可打开抽屉菜单。本节开始部分的示例中实现了一个左抽屉菜单`MyDrawer`，它的源码如下：\n\n```dart\nclass MyDrawer extends StatelessWidget {\n  const MyDrawer({\n    Key key,\n  }) : super(key: key);\n\n  @override\n  Widget build(BuildContext context) {\n    return Drawer(\n      child: MediaQuery.removePadding(\n        context: context,\n        //移除抽屉菜单顶部默认留白\n        removeTop: true,\n        child: Column(\n          crossAxisAlignment: CrossAxisAlignment.start,\n          children: <Widget>[\n            Padding(\n              padding: const EdgeInsets.only(top: 38.0),\n              child: Row(\n                children: <Widget>[\n                  Padding(\n                    padding: const EdgeInsets.symmetric(horizontal: 16.0),\n                    child: ClipOval(\n                      child: Image.asset(\n                        \"imgs/avatar.png\",\n                        width: 80,\n                      ),\n                    ),\n                  ),\n                  Text(\n                    \"Wendux\",\n                    style: TextStyle(fontWeight: FontWeight.bold),\n                  )\n                ],\n              ),\n            ),\n            Expanded(\n              child: ListView(\n                children: <Widget>[\n                  ListTile(\n                    leading: const Icon(Icons.add),\n                    title: const Text('Add account'),\n                  ),\n                  ListTile(\n                    leading: const Icon(Icons.settings),\n                    title: const Text('Manage accounts'),\n                  ),\n                ],\n              ),\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}\n```\n\n抽屉菜单通常将`Drawer`组件作为根节点，它实现了Material风格的菜单面板，`MediaQuery.removePadding`可以移除Drawer默认的一些留白（比如Drawer默认顶部会留和手机状态栏等高的留白），读者可以尝试传递不同的参数来看看实际效果。抽屉菜单页由顶部和底部组成，顶部由用户头像和昵称组成，底部是一个菜单列表，用ListView实现，关于ListView我们将在后面“可滚动组件”一节详细介绍。\n\n## 5.6.4 FloatingActionButton\n\n`FloatingActionButton`是Material设计规范中的一种特殊Button，通常悬浮在页面的某一个位置作为某种常用动作的快捷入口，如本节示例中页面右下角的\"➕\"号按钮。我们可以通过`Scaffold`的`floatingActionButton`属性来设置一个`FloatingActionButton`，同时通过`floatingActionButtonLocation`属性来指定其在页面中悬浮的位置，这个比较简单，不再赘述。\n\n## 5.6.5  底部Tab导航栏\n\n我们可以通过`Scaffold`的`bottomNavigationBar`属性来设置底部导航，如本节开始示例所示，我们通过Material组件库提供的`BottomNavigationBar`和`BottomNavigationBarItem`两种组件来实现Material风格的底部导航栏。可以看到上面的实现代码非常简单，所以不再赘述，但是如果我们想实现如图5-23所示效果的底部导航栏应该怎么做呢？\n\n![图5-23](../imgs/5-23.png)\n\nMaterial组件库中提供了一个`BottomAppBar` 组件，它可以和`FloatingActionButton`配合实现这种“打洞”效果，源码如下：\n\n```dart\nbottomNavigationBar: BottomAppBar(\n  color: Colors.white,\n  shape: CircularNotchedRectangle(), // 底部导航栏打一个圆形的洞\n  child: Row(\n    children: [\n      IconButton(icon: Icon(Icons.home)),\n      SizedBox(), //中间位置空出\n      IconButton(icon: Icon(Icons.business)),\n    ],\n    mainAxisAlignment: MainAxisAlignment.spaceAround, //均分底部导航栏横向空间\n  ),\n)\n```\n\n可以看到，上面代码中没有控制打洞位置的属性，实际上，打洞的位置取决于`FloatingActionButton`的位置，上面`FloatingActionButton`的位置为：\n\n```dart\nfloatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,\n```\n所以打洞位置在底部导航栏的正中间。\n\n`BottomAppBar`的`shape`属性决定洞的外形，`CircularNotchedRectangle`实现了一个圆形的外形，我们也可以自定义外形，比如，Flutter Gallery示例中就有一个“钻石”形状的示例，读者感兴趣可以自行查看。\n"
  },
  {
    "path": "src/chapter5/padding.md",
    "content": "\n\n## 5.1 填充（Padding）\n\n`Padding`可以给其子节点添加填充（留白），和边距效果类似。我们在前面很多示例中都已经使用过它了，现在来看看它的定义：\n\n```dart\nPadding({\n  ...\n  EdgeInsetsGeometry padding,\n  Widget child,\n})\n```\n\n`EdgeInsetsGeometry`是一个抽象类，开发中，我们一般都使用`EdgeInsets`类，它是`EdgeInsetsGeometry`的一个子类，定义了一些设置填充的便捷方法。\n\n### EdgeInsets\n\n我们看看`EdgeInsets`提供的便捷方法：\n\n- `fromLTRB(double left, double top, double right, double bottom) `：分别指定四个方向的填充。\n- `all(double value)` : 所有方向均使用相同数值的填充。\n- `only({left, top, right ,bottom })`：可以设置具体某个方向的填充(可以同时指定多个方向)。\n- `symmetric({  vertical, horizontal })`：用于设置对称方向的填充，`vertical`指`top`和`bottom`，`horizontal`指`left`和`right`。\n\n### 示例\n\n下面的示例主要展示了`EdgeInsets`的不同用法，比较简单，源码如下：\n\n```dart\nclass PaddingTestRoute extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return Padding(\n      //上下左右各添加16像素补白\n      padding: EdgeInsets.all(16.0),\n      child: Column(\n        //显式指定对齐方式为左对齐，排除对齐干扰\n        crossAxisAlignment: CrossAxisAlignment.start,\n        children: <Widget>[\n          Padding(\n            //左边添加8像素补白\n            padding: const EdgeInsets.only(left: 8.0),\n            child: Text(\"Hello world\"),\n          ),\n          Padding(\n            //上下各添加8像素补白\n            padding: const EdgeInsets.symmetric(vertical: 8.0),\n            child: Text(\"I am Jack\"),\n          ),\n          Padding(\n            // 分别指定四个方向的补白\n            padding: const EdgeInsets.fromLTRB(20.0,.0,20.0,20.0),\n            child: Text(\"Your friend\"),\n          )\n        ],\n      ),\n    );\n  }\n}\n```\n\n运行效果如图5-1所示：\n\n![图5-1](../imgs/5-1.png)"
  },
  {
    "path": "src/chapter5/transform.md",
    "content": "# 5.4 变换（Transform）\n\n`Transform`可以在其子组件绘制时对其应用一些矩阵变换来实现一些特效。`Matrix4`是一个4D矩阵，通过它我们可以实现各种矩阵操作，下面是一个例子：\n\n```dart\nContainer(\n  color: Colors.black,\n  child: new Transform(\n    alignment: Alignment.topRight, //相对于坐标系原点的对齐方式\n    transform: new Matrix4.skewY(0.3), //沿Y轴倾斜0.3弧度\n    child: new Container(\n      padding: const EdgeInsets.all(8.0),\n      color: Colors.deepOrange,\n      child: const Text('Apartment for rent!'),\n    ),\n  ),\n);\n```\n\n运行效果如图5-10所示：\n\n![图5-10](../imgs/5-10.png)\n\n> 关于矩阵变换的相关内容属于线性代数范畴，本书不做讨论，读者有兴趣可以自行了解。本书中，我们把焦点放在Flutter中一些常见的变换效果上。另外，由于矩阵变化时发生在绘制时，而无需重新布局和构建等过程，所以性能很好。\n>\n\n### 平移\n\n`Transform.translate`接收一个`offset`参数，可以在绘制时沿`x`、`y`轴对子组件平移指定的距离。\n\n```dart\nDecoratedBox(\n  decoration:BoxDecoration(color: Colors.red),\n  //默认原点为左上角，左移20像素，向上平移5像素  \n  child: Transform.translate(\n    offset: Offset(-20.0, -5.0),\n    child: Text(\"Hello world\"),\n  ),\n)\n```\n\n效果如图5-11所示：\n\n![图5-11](../imgs/5-11.png)\n\n### 旋转\n\n`Transform.rotate`可以对子组件进行旋转变换，如：\n\n```dart\nDecoratedBox(\n  decoration:BoxDecoration(color: Colors.red),\n  child: Transform.rotate(\n    //旋转90度\n    angle:math.pi/2 ,\n    child: Text(\"Hello world\"),\n  ),\n)；\n```\n> 注意：要使用`math.pi`需先进行如下导包。  \n```dart  \nimport 'dart:math' as math;  \n```\n\n效果如图5-12所示：\n\n![图5-12](../imgs/5-12.png)\n\n### 缩放\n\n`Transform.scale`可以对子组件进行缩小或放大，如：\n\n```dart\nDecoratedBox(\n  decoration:BoxDecoration(color: Colors.red),\n  child: Transform.scale(\n      scale: 1.5, //放大到1.5倍\n      child: Text(\"Hello world\")\n  )\n);\n```\n\n效果如图5-13所示：\n\n![图5-13](../imgs/5-13.png)\n\n### 注意\n\n- `Transform`的变换是应用在绘制阶段，而并不是应用在布局(layout)阶段，所以无论对子组件应用何种变化，其占用空间的大小和在屏幕上的位置都是固定不变的，因为这些是在布局阶段就确定的。下面我们具体说明：\n\n  ```dart\n   Row(\n    mainAxisAlignment: MainAxisAlignment.center,\n    children: <Widget>[\n      DecoratedBox(\n        decoration:BoxDecoration(color: Colors.red),\n        child: Transform.scale(scale: 1.5,\n            child: Text(\"Hello world\")\n        )\n      ),\n      Text(\"你好\", style: TextStyle(color: Colors.green, fontSize: 18.0),)\n    ],\n  )\n  ```\n\n  运行效果如图5-14所示：\n\n  ![图5-14](../imgs/5-14.png)\n\n  由于第一个`Text`应用变换(放大)后，其在绘制时会放大，但其占用的空间依然为红色部分，所以第二个`Text`会紧挨着红色部分，最终就会出现文字重合。\n\n- 由于矩阵变化只会作用在绘制阶段，所以在某些场景下，在UI需要变化时，可以直接通过矩阵变化来达到视觉上的UI改变，而不需要去重新触发build流程，这样会节省layout的开销，所以性能会比较好。如之前介绍的`Flow`组件，它内部就是用矩阵变换来更新UI，除此之外，Flutter的动画组件中也大量使用了`Transform`以提高性能。\n\n> 思考题：使用`Transform`对其子组件先进行平移然后再旋转和先旋转再平移，两者最终的效果一样吗？为什么？\n\n### RotatedBox\n\n`RotatedBox`和`Transform.rotate`功能相似，它们都可以对子组件进行旋转变换，但是有一点不同：`RotatedBox`的变换是在layout阶段，会影响在子组件的位置和大小。我们将上面介绍`Transform.rotate`时的示例改一下：\n\n```dart\nRow(\n  mainAxisAlignment: MainAxisAlignment.center,\n  children: <Widget>[\n    DecoratedBox(\n      decoration: BoxDecoration(color: Colors.red),\n      //将Transform.rotate换成RotatedBox  \n      child: RotatedBox(\n        quarterTurns: 1, //旋转90度(1/4圈)\n        child: Text(\"Hello world\"),\n      ),\n    ),\n    Text(\"你好\", style: TextStyle(color: Colors.green, fontSize: 18.0),)\n  ],\n),\n```\n\n效果如图5-15所示：\n\n![图5-15](../imgs/5-15.png)\n\n\n\n由于`RotatedBox`是作用于layout阶段，所以子组件会旋转90度（而不只是绘制的内容），`decoration`会作用到子组件所占用的实际空间上，所以最终就是上图的效果，读者可以和前面`Transform.rotate`示例对比理解。\n\n"
  },
  {
    "path": "src/chapter6/custom_scrollview.md",
    "content": "# 6.5 CustomScrollView\n\n`CustomScrollView`是可以使用Sliver来自定义滚动模型（效果）的组件。它可以包含多种滚动模型，举个例子，假设有一个页面，顶部需要一个`GridView`，底部需要一个`ListView`，而要求整个页面的滑动效果是统一的，即它们看起来是一个整体。如果使用`GridView`+`ListView`来实现的话，就不能保证一致的滑动效果，因为它们的滚动效果是分离的，所以这时就需要一个\"胶水\"，把这些彼此独立的可滚动组件\"粘\"起来，而`CustomScrollView`的功能就相当于“胶水”。\n\n### 可滚动组件的Sliver版\n\nSliver在前面讲过，有细片、薄片之意，在Flutter中，Sliver通常指可滚动组件子元素（就像一个个薄片一样）。但是在`CustomScrollView`中，需要粘起来的可滚动组件就是`CustomScrollView`的Sliver了，如果直接将`ListView`、`GridView`作为`CustomScrollView`是不行的，因为它们本身是可滚动组件而并不是Sliver！因此，为了能让可滚动组件能和`CustomScrollView`配合使用，Flutter提供了一些可滚动组件的Sliver版，如SliverList、SliverGrid等。实际上Sliver版的可滚动组件和非Sliver版的可滚动组件最大的区别就是**前者不包含滚动模型（自身不能再滚动），而后者包含滚动模型** ，也正因如此，`CustomScrollView`才可以将多个Sliver\"粘\"在一起，这些Sliver共用`CustomScrollView`的`Scrollable`，所以最终才实现了统一的滑动效果。\n\n> Sliver系列Widget比较多，我们不会一一介绍，读者只需记住它的特点，需要时再去查看文档即可。上面之所以说“大多数”Sliver都和可滚动组件对应，是由于还有一些如SliverPadding、SliverAppBar等是和可滚动组件无关的，它们主要是为了结合CustomScrollView一起使用，这是因为**CustomScrollView的子组件必须都是Sliver**。\n\n### 示例\n\n```dart\nimport 'package:flutter/material.dart';\n\nclass CustomScrollViewTestRoute extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    //因为本路由没有使用Scaffold，为了让子级Widget(如Text)使用\n    //Material Design 默认的样式风格,我们使用Material作为本路由的根。\n    return Material(\n      child: CustomScrollView(\n        slivers: <Widget>[\n          //AppBar，包含一个导航栏\n          SliverAppBar(\n            pinned: true,\n            expandedHeight: 250.0,\n            flexibleSpace: FlexibleSpaceBar(\n              title: const Text('Demo'),\n              background: Image.asset(\n                \"./images/avatar.png\", fit: BoxFit.cover,),\n            ),\n          ),\n\n          SliverPadding(\n            padding: const EdgeInsets.all(8.0),\n            sliver: new SliverGrid( //Grid\n              gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount(\n                crossAxisCount: 2, //Grid按两列显示\n                mainAxisSpacing: 10.0,\n                crossAxisSpacing: 10.0,\n                childAspectRatio: 4.0,\n              ),\n              delegate: new SliverChildBuilderDelegate(\n                    (BuildContext context, int index) {\n                  //创建子widget      \n                  return new Container(\n                    alignment: Alignment.center,\n                    color: Colors.cyan[100 * (index % 9)],\n                    child: new Text('grid item $index'),\n                  );\n                },\n                childCount: 20,\n              ),\n            ),\n          ),\n          //List\n          new SliverFixedExtentList(\n            itemExtent: 50.0,\n            delegate: new SliverChildBuilderDelegate(\n                    (BuildContext context, int index) {\n                  //创建列表项      \n                  return new Container(\n                    alignment: Alignment.center,\n                    color: Colors.lightBlue[100 * (index % 9)],\n                    child: new Text('list item $index'),\n                  );\n                },\n                childCount: 50 //50个列表项\n            ),\n          ),\n        ],\n      ),\n    );\n  }\n}\n```\n\n\n\n代码分为三部分：\n\n- 头部`SliverAppBar`：`SliverAppBar`对应`AppBar`，两者不同之处在于`SliverAppBar`可以集成到`CustomScrollView`。`SliverAppBar`可以结合`FlexibleSpaceBar`实现Material Design中头部伸缩的模型，具体效果，读者可以运行该示例查看。\n- 中间的`SliverGrid`：它用`SliverPadding`包裹以给`SliverGrid`添加补白。`SliverGrid`是一个两列，宽高比为4的网格，它有20个子组件。\n- 底部`SliverFixedExtentList`：它是一个所有子元素高度都为50像素的列表。\n\n运行效果如图：\n\n![图6-12](../imgs/6-12.png)![图6-13](../imgs/6-13.png)\n\n"
  },
  {
    "path": "src/chapter6/gridview.md",
    "content": "# 6.4 GridView\n\n`GridView`可以构建一个二维网格列表，其默认构造函数定义如下：\n\n```dart\nGridView({\n  Axis scrollDirection = Axis.vertical,\n  bool reverse = false,\n  ScrollController controller,\n  bool primary,\n  ScrollPhysics physics,\n  bool shrinkWrap = false,\n  EdgeInsetsGeometry padding,\n  @required SliverGridDelegate gridDelegate, //控制子widget layout的委托\n  bool addAutomaticKeepAlives = true,\n  bool addRepaintBoundaries = true,\n  double cacheExtent,\n  List<Widget> children = const <Widget>[],\n})\n```\n\n我们可以看到，`GridView`和`ListView`的大多数参数都是相同的，它们的含义也都相同的，如有疑惑读者可以翻阅ListView一节，在此不再赘述。我们唯一需要关注的是`gridDelegate`参数，类型是`SliverGridDelegate`，它的作用是控制`GridView`子组件如何排列(layout)。\n\n`SliverGridDelegate`是一个抽象类，定义了`GridView` Layout相关接口，子类需要通过实现它们来实现具体的布局算法。Flutter中提供了两个`SliverGridDelegate`的子类`SliverGridDelegateWithFixedCrossAxisCount`和`SliverGridDelegateWithMaxCrossAxisExtent`，我们可以直接使用，下面我们分别来介绍一下它们。\n\n### SliverGridDelegateWithFixedCrossAxisCount\n\n该子类实现了一个横轴为固定数量子元素的layout算法，其构造函数为：\n\n```dart\nSliverGridDelegateWithFixedCrossAxisCount({\n  @required double crossAxisCount, \n  double mainAxisSpacing = 0.0,\n  double crossAxisSpacing = 0.0,\n  double childAspectRatio = 1.0,\n})\n```\n\n- `crossAxisCount`：横轴子元素的数量。此属性值确定后子元素在横轴的长度就确定了，即ViewPort横轴长度除以`crossAxisCount`的商。\n- `mainAxisSpacing`：主轴方向的间距。\n- `crossAxisSpacing`：横轴方向子元素的间距。\n- `childAspectRatio`：子元素在横轴长度和主轴长度的比例。由于`crossAxisCount`指定后，子元素横轴长度就确定了，然后通过此参数值就可以确定子元素在主轴的长度。\n\n可以发现，子元素的大小是通过`crossAxisCount`和`childAspectRatio`两个参数共同决定的。注意，这里的子元素指的是子组件的最大显示空间，注意确保子组件的实际大小不要超出子元素的空间。\n\n下面看一个例子：\n\n```dart\nGridView(\n  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(\n      crossAxisCount: 3, //横轴三个子widget\n      childAspectRatio: 1.0 //宽高比为1时，子widget\n  ),\n  children:<Widget>[\n    Icon(Icons.ac_unit),\n    Icon(Icons.airport_shuttle),\n    Icon(Icons.all_inclusive),\n    Icon(Icons.beach_access),\n    Icon(Icons.cake),\n    Icon(Icons.free_breakfast)\n  ]\n);\n```\n\n![图6-9](../imgs/6-9.png)\n\n\n\n#### GridView.count\n\n`GridView.count`构造函数内部使用了`SliverGridDelegateWithFixedCrossAxisCount`，我们通过它可以快速的创建横轴固定数量子元素的`GridView`，上面的示例代码等价于：\n\n```dart\nGridView.count( \n  crossAxisCount: 3,\n  childAspectRatio: 1.0,\n  children: <Widget>[\n    Icon(Icons.ac_unit),\n    Icon(Icons.airport_shuttle),\n    Icon(Icons.all_inclusive),\n    Icon(Icons.beach_access),\n    Icon(Icons.cake),\n    Icon(Icons.free_breakfast),\n  ],\n);\n```\n\n\n\n### SliverGridDelegateWithMaxCrossAxisExtent\n\n该子类实现了一个横轴子元素为固定最大长度的layout算法，其构造函数为：\n\n```dart\nSliverGridDelegateWithMaxCrossAxisExtent({\n  double maxCrossAxisExtent,\n  double mainAxisSpacing = 0.0,\n  double crossAxisSpacing = 0.0,\n  double childAspectRatio = 1.0,\n})\n```\n\n`maxCrossAxisExtent`为子元素在横轴上的最大长度，之所以是“最大”长度，是**因为横轴方向每个子元素的长度仍然是等分的**，举个例子，如果ViewPort的横轴长度是450，那么当`maxCrossAxisExtent`的值在区间[450/4，450/3)内的话，子元素最终实际长度都为112.5，而`childAspectRatio`所指的子元素横轴和主轴的长度比为**最终的长度比**。其它参数和`SliverGridDelegateWithFixedCrossAxisCount`相同。\n\n下面我们看一个例子：\n\n```dart\nGridView(\n  padding: EdgeInsets.zero,\n  gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(\n      maxCrossAxisExtent: 120.0,\n      childAspectRatio: 2.0 //宽高比为2\n  ),\n  children: <Widget>[\n    Icon(Icons.ac_unit),\n    Icon(Icons.airport_shuttle),\n    Icon(Icons.all_inclusive),\n    Icon(Icons.beach_access),\n    Icon(Icons.cake),\n    Icon(Icons.free_breakfast),\n  ],\n);\n```\n\n![图6-10](../imgs/6-10.png)\n\n#### GridView.extent\n\nGridView.extent构造函数内部使用了SliverGridDelegateWithMaxCrossAxisExtent，我们通过它可以快速的创建纵轴子元素为固定最大长度的的GridView，上面的示例代码等价于：\n\n```dart\nGridView.extent(\n   maxCrossAxisExtent: 120.0,\n   childAspectRatio: 2.0,\n   children: <Widget>[\n     Icon(Icons.ac_unit),\n     Icon(Icons.airport_shuttle),\n     Icon(Icons.all_inclusive),\n     Icon(Icons.beach_access),\n     Icon(Icons.cake),\n     Icon(Icons.free_breakfast),\n   ],\n );\n```\n\n\n\n### GridView.builder\n\n上面我们介绍的GridView都需要一个widget数组作为其子元素，这些方式都会提前将所有子widget都构建好，所以只适用于子widget数量比较少时，当子widget比较多时，我们可以通过`GridView.builder`来动态创建子widget。`GridView.builder` 必须指定的参数有两个：\n\n```dart\nGridView.builder(\n ...\n @required SliverGridDelegate gridDelegate, \n @required IndexedWidgetBuilder itemBuilder,\n)\n```\n\n其中`itemBuilder`为子widget构建器。\n\n#### 示例\n\n假设我们需要从一个异步数据源（如网络）分批获取一些`Icon`，然后用`GridView`来展示：\n\n```dart\nclass InfiniteGridView extends StatefulWidget {\n  @override\n  _InfiniteGridViewState createState() => new _InfiniteGridViewState();\n}\n\nclass _InfiniteGridViewState extends State<InfiniteGridView> {\n\n  List<IconData> _icons = []; //保存Icon数据\n\n  @override\n  void initState() {\n    // 初始化数据  \n    _retrieveIcons();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return GridView.builder(\n        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(\n            crossAxisCount: 3, //每行三列\n            childAspectRatio: 1.0 //显示区域宽高相等\n        ),\n        itemCount: _icons.length,\n        itemBuilder: (context, index) {\n          //如果显示到最后一个并且Icon总数小于200时继续获取数据\n          if (index == _icons.length - 1 && _icons.length < 200) {\n            _retrieveIcons();\n          }\n          return Icon(_icons[index]);\n        }\n    );\n  }\n\n  //模拟异步获取数据\n  void _retrieveIcons() {\n    Future.delayed(Duration(milliseconds: 200)).then((e) {\n      setState(() {\n        _icons.addAll([\n          Icons.ac_unit,\n          Icons.airport_shuttle,\n          Icons.all_inclusive,\n          Icons.beach_access, Icons.cake,\n          Icons.free_breakfast\n        ]);\n      });\n    });\n  }\n}\n```\n\n\n\n- `_retrieveIcons()`：在此方法中我们通过`Future.delayed`来模拟从异步数据源获取数据，每次获取数据需要200毫秒，获取成功后将新数据添加到_icons，然后调用setState重新构建。\n- 在itemBuilder中，如果显示到最后一个时，判断是否需要继续获取数据，然后返回一个Icon。\n\n### 更多\n\nFlutter的`GridView`默认子元素显示空间是相等的，但在实际开发中，你可能会遇到子元素大小不等的情况，如下面这样的布局：\n\n![图6-11](../imgs/6-11.png)\n\nPub上有一个包“flutter_staggered_grid_view” ，它实现了一个交错GridView的布局模型，可以很轻松的实现这种布局，详情读者可以自行了解。\n\n"
  },
  {
    "path": "src/chapter6/index.md",
    "content": "# 本章目录\n\n* [6.1：可滚动组件简介](intro.md)\n* [6.2：SingleChildScrollView](single_child_scrollview.md)\n* [6.3：ListView](listview.md)\n* [6.4：GridView](gridview.md)      \n* [6.5：CustomScrollView](custom_scrollview.md) \n* [6.6：滚动监听及控制（ScrollController）](scroll_controller.md) \n"
  },
  {
    "path": "src/chapter6/intro.md",
    "content": "# 6.1 可滚动组件简介\n\n当组件内容超过当前显示视口(ViewPort)时，如果没有特殊处理，Flutter则会提示Overflow错误。为此，Flutter提供了多种可滚动组件（Scrollable Widget）用于显示列表和长布局。在本章中，我们先介绍一下常用的可滚动组件（如`ListView`、`GridView`等），然后介绍一下`ScrollController`。可滚动组件都直接或间接包含一个`Scrollable`组件，因此它们包括一些共同的属性，为了避免重复介绍，我们在此统一介绍一下：\n\n```dart\nScrollable({\n  ...\n  this.axisDirection = AxisDirection.down,\n  this.controller,\n  this.physics,\n  @required this.viewportBuilder, //后面介绍\n})\n```\n\n- `axisDirection`滚动方向。\n- `physics`：此属性接受一个`ScrollPhysics`类型的对象，它决定可滚动组件如何响应用户操作，比如用户滑动完抬起手指后，继续执行动画；或者滑动到边界时，如何显示。默认情况下，Flutter会根据具体平台分别使用不同的`ScrollPhysics`对象，应用不同的显示效果，如当滑动到边界时，继续拖动的话，在iOS上会出现弹性效果，而在Android上会出现微光效果。如果你想在所有平台下使用同一种效果，可以显式指定一个固定的`ScrollPhysics`，Flutter SDK中包含了两个`ScrollPhysics`的子类，他们可以直接使用：\n  - `ClampingScrollPhysics`：Android下微光效果。\n  - `BouncingScrollPhysics`：iOS下弹性效果。\n- `controller`：此属性接受一个`ScrollController`对象。`ScrollController`的主要作用是控制滚动位置和监听滚动事件。默认情况下，Widget树中会有一个默认的`PrimaryScrollController`，如果子树中的可滚动组件没有显式的指定`controller`，并且`primary`属性值为`true`时（默认就为`true`），可滚动组件会使用这个默认的`PrimaryScrollController`。这种机制带来的好处是父组件可以控制子树中可滚动组件的滚动行为，例如，`Scaffold`正是使用这种机制在iOS中实现了点击导航栏回到顶部的功能。我们将在本章后面“滚动控制”一节详细介绍`ScrollController`。\n\n### Scrollbar\n\n`Scrollbar`是一个Material风格的滚动指示器（滚动条），如果要给可滚动组件添加滚动条，只需将`Scrollbar`作为可滚动组件的任意一个父级组件即可，如：\n\n```dart\nScrollbar(\n  child: SingleChildScrollView(\n    ...\n  ),\n);\n```\n\n`Scrollbar`和`CupertinoScrollbar`都是通过监听滚动通知来确定滚动条位置的。关于的滚动通知的详细内容我们将在本章最后一节中专门介绍。\n\n#### CupertinoScrollbar\n\n`CupertinoScrollbar`是iOS风格的滚动条，如果你使用的是`Scrollbar`，那么在iOS平台它会自动切换为`CupertinoScrollbar`。\n\n### ViewPort视口\n\n在很多布局系统中都有ViewPort的概念，在Flutter中，术语ViewPort（视口），如无特别说明，则是指一个Widget的实际显示区域。例如，一个`ListView`的显示区域高度是800像素，虽然其列表项总高度可能远远超过800像素，但是其ViewPort仍然是800像素。\n\n### 基于Sliver的延迟构建\n\n通常可滚动组件的子组件可能会非常多、占用的总高度也会非常大；如果要一次性将子组件全部构建出将会非常昂贵！为此，Flutter中提出一个Sliver（中文为“薄片”的意思）概念，如果一个可滚动组件支持Sliver模型，那么该滚动可以将子组件分成好多个“薄片”（Sliver），只有当Sliver出现在视口中时才会去构建它，这种模型也称为“基于Sliver的延迟构建模型”。可滚动组件中有很多都支持基于Sliver的延迟构建模型，如`ListView`、`GridView`，但是也有不支持该模型的，如`SingleChildScrollView`。\n\n### 主轴和纵轴\n\n在可滚动组件的坐标描述中，通常将滚动方向称为主轴，非滚动方向称为纵轴。由于可滚动组件的默认方向一般都是沿垂直方向，所以默认情况下主轴就是指垂直方向，水平方向同理。\n\n"
  },
  {
    "path": "src/chapter6/listview.md",
    "content": "# 6.3 ListView\n\n`ListView`是最常用的可滚动组件之一，它可以沿一个方向线性排布所有子组件，并且它也支持基于Sliver的延迟构建模型。我们看看ListView的默认构造函数定义：\n\n```dart\nListView({\n  ...  \n  //可滚动widget公共参数\n  Axis scrollDirection = Axis.vertical,\n  bool reverse = false,\n  ScrollController controller,\n  bool primary,\n  ScrollPhysics physics,\n  EdgeInsetsGeometry padding,\n  \n  //ListView各个构造函数的共同参数  \n  double itemExtent,\n  bool shrinkWrap = false,\n  bool addAutomaticKeepAlives = true,\n  bool addRepaintBoundaries = true,\n  double cacheExtent,\n    \n  //子widget列表\n  List<Widget> children = const <Widget>[],\n})\n```\n\n上面参数分为两组：第一组是可滚动组件的公共参数，本章第一节中已经介绍过，不再赘述；第二组是`ListView`各个构造函数（`ListView`有多个构造函数）的共同参数，我们重点来看看这些参数，：\n\n- `itemExtent`：该参数如果不为`null`，则会强制`children`的“长度”为`itemExtent`的值；这里的“长度”是指滚动方向上子组件的长度，也就是说如果滚动方向是垂直方向，则`itemExtent`代表子组件的高度；如果滚动方向为水平方向，则`itemExtent`就代表子组件的宽度。在`ListView`中，指定`itemExtent`比让子组件自己决定自身长度会更高效，这是因为指定`itemExtent`后，滚动系统可以提前知道列表的长度，而无需每次构建子组件时都去再计算一下，尤其是在滚动位置频繁变化时（滚动系统需要频繁去计算列表高度）。\n- `shrinkWrap`：该属性表示是否根据子组件的总长度来设置`ListView`的长度，默认值为`false` 。默认情况下，`ListView`的会在滚动方向尽可能多的占用空间。当`ListView`在一个无边界(滚动方向上)的容器中时，`shrinkWrap`必须为`true`。\n- `addAutomaticKeepAlives`：该属性表示是否将列表项（子组件）包裹在`AutomaticKeepAlive` 组件中；典型地，在一个懒加载列表中，如果将列表项包裹在`AutomaticKeepAlive`中，在该列表项滑出视口时它也不会被GC（垃圾回收），它会使用`KeepAliveNotification`来保存其状态。如果列表项自己维护其`KeepAlive`状态，那么此参数必须置为`false`。\n- `addRepaintBoundaries`：该属性表示是否将列表项（子组件）包裹在`RepaintBoundary`组件中。当可滚动组件滚动时，将列表项包裹在`RepaintBoundary`中可以避免列表项重绘，但是当列表项重绘的开销非常小（如一个颜色块，或者一个较短的文本）时，不添加`RepaintBoundary`反而会更高效。和`addAutomaticKeepAlive`一样，如果列表项自己维护其`KeepAlive`状态，那么此参数必须置为`false`。\n\n> 注意：上面这些参数并非`ListView`特有，在本章后面介绍的其它可滚动组件也可能会拥有这些参数，它们的含义是相同的。\n\n### 默认构造函数\n\n默认构造函数有一个`children`参数，它接受一个Widget列表（List<Widget>）。这种方式适合只有少量的子组件的情况，因为这种方式需要将所有`children`都提前创建好（这需要做大量工作），而不是等到子widget真正显示的时候再创建，也就是说通过默认构造函数构建的ListView没有应用基于Sliver的懒加载模型。实际上通过此方式创建的`ListView`和使用`SingleChildScrollView`+`Column`的方式没有本质的区别。下面是一个例子：\n\n```dart\nListView(\n  shrinkWrap: true, \n  padding: const EdgeInsets.all(20.0),\n  children: <Widget>[\n    const Text('I\\'m dedicating every day to you'),\n    const Text('Domestic life was never quite my style'),\n    const Text('When you smile, you knock me out, I fall apart'),\n    const Text('And I thought I was so smart'),\n  ],\n);\n```\n\n> 再次强调，可滚动组件通过一个List<Widget>来作为其children属性时，只适用于子组件较少的情况，这是一个通用规律，并非`ListView`自己的特性，像`GridView`也是如此。\n\n### ListView.builder\n\n`ListView.builder`适合列表项比较多（或者无限）的情况，因为只有当子组件真正显示的时候才会被创建，也就说通过该构造函数创建的`ListView`是支持基于Sliver的懒加载模型的。下面看一下`ListView.builder`的核心参数列表：\n\n```dart\nListView.builder({\n  // ListView公共参数已省略  \n  ...\n  @required IndexedWidgetBuilder itemBuilder,\n  int itemCount,\n  ...\n})\n```\n\n- `itemBuilder`：它是列表项的构建器，类型为`IndexedWidgetBuilder`，返回值为一个widget。当列表滚动到具体的`index`位置时，会调用该构建器构建列表项。\n- `itemCount`：列表项的数量，如果为`null`，则为无限列表。\n\n> 可滚动组件的构造函数如果需要一个列表项Builder，那么通过该构造函数构建的可滚动组件通常就是支持基于Sliver的懒加载模型的，反之则不支持，这是个一般规律。我们在后面在介绍可滚动组件的构造函数时将不再专门说明其是否支持基于Sliver的懒加载模型了。\n\n下面看一个例子：\n\n```dart\nListView.builder(\n    itemCount: 100,\n    itemExtent: 50.0, //强制高度为50.0\n    itemBuilder: (BuildContext context, int index) {\n      return ListTile(title: Text(\"$index\"));\n    }\n);\n```\n\n运行效果如图6-2所示：\n\n![图6-2](../imgs/6-2.png)\n\n\n\n### ListView.separated\n\n`ListView.separated`可以在生成的列表项之间添加一个分割组件，它比`ListView.builder`多了一个`separatorBuilder`参数，该参数是一个分割组件生成器。\n\n下面我们看一个例子：奇数行添加一条蓝色下划线，偶数行添加一条绿色下划线。\n\n```dart\nclass ListView3 extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    //下划线widget预定义以供复用。  \n    Widget divider1=Divider(color: Colors.blue,);\n    Widget divider2=Divider(color: Colors.green);\n    return ListView.separated(\n        itemCount: 100,\n        //列表项构造器\n        itemBuilder: (BuildContext context, int index) {\n          return ListTile(title: Text(\"$index\"));\n        },\n        //分割器构造器\n        separatorBuilder: (BuildContext context, int index) {\n          return index%2==0?divider1:divider2;\n        },\n    );\n  }\n}\n```\n\n![图6-3](../imgs/6-3.png)\n\n### 实例：无限加载列表\n\n假设我们要从数据源异步分批拉取一些数据，然后用`ListView`展示，当我们滑动到列表末尾时，判断是否需要再去拉取数据，如果是，则去拉取，拉取过程中在表尾显示一个loading，拉取成功后将数据插入列表；如果不需要再去拉取，则在表尾提示\"没有更多\"。代码如下：\n\n```dart\nclass InfiniteListView extends StatefulWidget {\n  @override\n  _InfiniteListViewState createState() => new _InfiniteListViewState();\n}\n\nclass _InfiniteListViewState extends State<InfiniteListView> {\n  static const loadingTag = \"##loading##\"; //表尾标记\n  var _words = <String>[loadingTag];\n\n  @override\n  void initState() {\n    super.initState();\n    _retrieveData();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return ListView.separated(\n      itemCount: _words.length,\n      itemBuilder: (context, index) {\n        //如果到了表尾\n        if (_words[index] == loadingTag) {\n          //不足100条，继续获取数据\n          if (_words.length - 1 < 100) {\n            //获取数据\n            _retrieveData();\n            //加载时显示loading\n            return Container(\n              padding: const EdgeInsets.all(16.0),\n              alignment: Alignment.center,\n              child: SizedBox(\n                  width: 24.0,\n                  height: 24.0,\n                  child: CircularProgressIndicator(strokeWidth: 2.0)\n              ),\n            );\n          } else {\n            //已经加载了100条数据，不再获取数据。\n            return Container(\n                alignment: Alignment.center,\n                padding: EdgeInsets.all(16.0),\n                child: Text(\"没有更多了\", style: TextStyle(color: Colors.grey),)\n            );\n          }\n        }\n        //显示单词列表项\n        return ListTile(title: Text(_words[index]));\n      },\n      separatorBuilder: (context, index) => Divider(height: .0),\n    );\n  }\n\n  void _retrieveData() {\n    Future.delayed(Duration(seconds: 2)).then((e) {\n      setState(() {\n        //重新构建列表\n\t\t_words.insertAll(_words.length - 1,\n          //每次生成20个单词\n          generateWordPairs().take(20).map((e) => e.asPascalCase).toList()\n      \t);\n      });\n    });\n  }\n\n}\n```\n\n运行后效果如图6-4、6-5所示：\n\n![图6-4](../imgs/6-4.png)![图6-5](../imgs/6-5.png)\n\n代码比较简单，读者可以参照代码中的注释理解，故不再赘述。需要说明的是，`_retrieveData()`的功能是模拟从数据源异步获取数据，我们使用english_words包的`generateWordPairs()`方法每次生成20个单词。\n\n### 添加固定列表头\n\n很多时候我们需要给列表添加一个固定表头，比如我们想实现一个商品列表，需要在列表顶部添加一个“商品列表”标题，期望的效果如图6-6所示：\n\n![图6-6](../imgs/6-6.png)\n\n我们按照之前经验，写出如下代码：\n\n```dart\n@override\nWidget build(BuildContext context) {\n  return Column(children: <Widget>[\n    ListTile(title:Text(\"商品列表\")),\n    ListView.builder(itemBuilder: (BuildContext context, int index) {\n        return ListTile(title: Text(\"$index\"));\n    }),\n  ]);\n}\n```\n\n然后运行，发现并没有出现我们期望的效果，相反触发了一个异常；\n\n```\nError caught by rendering library, thrown during performResize()。\nVertical viewport was given unbounded height ...\n```\n\n从异常信息中我们可以看到是因为`ListView`高度边界无法确定引起，所以解决的办法也很明显，我们需要给`ListView`指定边界，我们通过`SizedBox`指定一个列表高度看看是否生效：\n\n```dart\n... //省略无关代码\nSizedBox(\n    height: 400, //指定列表高度为400\n    child: ListView.builder(itemBuilder: (BuildContext context, int index) {\n        return ListTile(title: Text(\"$index\"));\n    }),\n),\n...\n```\n\n运行效果如图6-7所示：\n\n![图6-7](../imgs/6-7.png)\n\n可以看到，现在没有触发异常并且列表已经显示出来了，但是我们的手机屏幕高度要大于400，所以底部会有一些空白。那如果我们要实现列表铺满除表头以外的屏幕空间应该怎么做？直观的方法是我们去动态计算，用屏幕高度减去状态栏、导航栏、表头的高度即为剩余屏幕高度，代码如下：\n\n```dart\n... //省略无关代码\nSizedBox(\n  //Material设计规范中状态栏、导航栏、ListTile高度分别为24、56、56 \n  height: MediaQuery.of(context).size.height-24-56-56,\n  child: ListView.builder(itemBuilder: (BuildContext context, int index) {\n    return ListTile(title: Text(\"$index\"));\n  }),\n)\n...    \n```\n\n运行效果如下图6-8所示：\n\n![图6-8](../imgs/6-8.png)\n\n可以看到，我们期望的效果实现了，但是这种方法并不优雅，如果页面布局发生变化，比如表头布局调整导致表头高度改变，那么剩余空间的高度就得重新计算。那么有什么方法可以自动拉伸`ListView`以填充屏幕剩余空间的方法吗？当然有！答案就是`Flex`。前面已经介绍过在弹性布局中，可以使用`Expanded`自动拉伸组件大小，并且我们也说过`Column`是继承自`Flex`的，所以我们可以直接使用`Column`+`Expanded`来实现，代码如下：\n\n```dart\n@override\nWidget build(BuildContext context) {\n  return Column(children: <Widget>[\n    ListTile(title:Text(\"商品列表\")),\n    Expanded(\n      child: ListView.builder(itemBuilder: (BuildContext context, int index) {\n        return ListTile(title: Text(\"$index\"));\n      }),\n    ),\n  ]);\n}\n```\n\n运行后，和上图一样，完美实现了！\n\n### 总结\n\n本节主要介绍了`ListView`的一些公共参数以及常用的构造函数。不同的构造函数对应了不同的列表项生成模型，如果需要自定义列表项生成模型，可以通过`ListView.custom`来自定义，它需要实现一个`SliverChildDelegate`用来给ListView生成列表项组件，更多详情请参考API文档。\n\n"
  },
  {
    "path": "src/chapter6/scroll_controller.md",
    "content": "\n# 6.6 滚动监听及控制\n\n在前几节中，我们介绍了Flutter中常用的可滚动组件，也说过可以用`ScrollController`来控制可滚动组件的滚动位置，本节先介绍一下`ScrollController`，然后以`ListView`为例，展示一下`ScrollController`的具体用法。最后，再介绍一下路由切换时如何来保存滚动位置。\n\n## 6.6.1 ScrollController\n\n`ScrollController`构造函数如下：\n\n```dart\nScrollController({\n  double initialScrollOffset = 0.0, //初始滚动位置\n  this.keepScrollOffset = true,//是否保存滚动位置\n  ...\n})\n```\n\n我们介绍一下`ScrollController`常用的属性和方法：\n\n- `offset`：可滚动组件当前的滚动位置。\n- `jumpTo(double offset)`、`animateTo(double offset,...)`：这两个方法用于跳转到指定的位置，它们不同之处在于，后者在跳转时会执行一个动画，而前者不会。\n\n`ScrollController`还有一些属性和方法，我们将在后面原理部分解释。\n\n#### 滚动监听\n\n`ScrollController`间接继承自`Listenable`，我们可以根据`ScrollController`来监听滚动事件，如：\n\n```dart\ncontroller.addListener(()=>print(controller.offset))\n```\n\n### 示例\n\n我们创建一个`ListView`，当滚动位置发生变化时，我们先打印出当前滚动位置，然后判断当前位置是否超过1000像素，如果超过则在屏幕右下角显示一个“返回顶部”的按钮，该按钮点击后可以使ListView恢复到初始位置；如果没有超过1000像素，则隐藏“返回顶部”按钮。代码如下：\n\n```dart\n\nclass ScrollControllerTestRoute extends StatefulWidget {\n  @override\n  ScrollControllerTestRouteState createState() {\n    return new ScrollControllerTestRouteState();\n  }\n}\n\nclass ScrollControllerTestRouteState extends State<ScrollControllerTestRoute> {\n  ScrollController _controller = new ScrollController();\n  bool showToTopBtn = false; //是否显示“返回到顶部”按钮\n\n  @override\n  void initState() {\n    super.initState();\n    //监听滚动事件，打印滚动位置\n    _controller.addListener(() {\n      print(_controller.offset); //打印滚动位置\n      if (_controller.offset < 1000 && showToTopBtn) {\n        setState(() {\n          showToTopBtn = false;\n        });\n      } else if (_controller.offset >= 1000 && showToTopBtn == false) {\n        setState(() {\n          showToTopBtn = true;\n        });\n      }\n    });\n  }\n\n  @override\n  void dispose() {\n    //为了避免内存泄露，需要调用_controller.dispose\n    _controller.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(title: Text(\"滚动控制\")),\n      body: Scrollbar(\n        child: ListView.builder(\n            itemCount: 100,\n            itemExtent: 50.0, //列表项高度固定时，显式指定高度是一个好习惯(性能消耗小)\n            controller: _controller,\n            itemBuilder: (context, index) {\n              return ListTile(title: Text(\"$index\"),);\n            }\n        ),\n      ),\n      floatingActionButton: !showToTopBtn ? null : FloatingActionButton(\n          child: Icon(Icons.arrow_upward),\n          onPressed: () {\n            //返回到顶部时执行动画\n            _controller.animateTo(.0,\n                duration: Duration(milliseconds: 200),\n                curve: Curves.ease\n            );\n          }\n      ),\n    );\n  }\n}\n```\n\n代码说明已经包含在注释里，下面我们看看运行效果：\n\n![图6-14](../imgs/6-14.png)![图6-15](../imgs/6-15.png)\n\n由于列表项高度为50像素，当滑动到第20个列表项后，右下角“返回顶部”按钮会显示，点击该按钮，ListView会在返回顶部的过程中执行一个滚动动画，动画时间是200毫秒，动画曲线是`Curves.ease`，关于动画的详细内容我们将在后面“动画”一章中详细介绍。\n\n### 滚动位置恢复\n\n`PageStorage`是一个用于保存页面(路由)相关数据的组件，它并不会影响子树的UI外观，其实，`PageStorage`是一个功能型组件，它拥有一个存储桶（bucket），子树中的Widget可以通过指定不同的`PageStorageKey`来存储各自的数据或状态。\n\n每次滚动结束，可滚动组件都会将滚动位置`offset`存储到`PageStorage`中，当可滚动组件重新创建时再恢复。如果`ScrollController.keepScrollOffset`为`false`，则滚动位置将不会被存储，可滚动组件重新创建时会使用`ScrollController.initialScrollOffset`；`ScrollController.keepScrollOffset`为`true`时，可滚动组件在**第一次**创建时，会滚动到`initialScrollOffset`处，因为这时还没有存储过滚动位置。在接下来的滚动中就会存储、恢复滚动位置，而`initialScrollOffset`会被忽略。\n\n当一个路由中包含多个可滚动组件时，如果你发现在进行一些跳转或切换操作后，滚动位置不能正确恢复，这时你可以通过显式指定`PageStorageKey`来分别跟踪不同的可滚动组件的位置，如：\n\n```dart\nListView(key: PageStorageKey(1), ... );\n...\nListView(key: PageStorageKey(2), ... );\n```\n\n不同的`PageStorageKey`，需要不同的值，这样才可以为不同可滚动组件保存其滚动位置。\n\n> 注意：一个路由中包含多个可滚动组件时，如果要分别跟踪它们的滚动位置，并非一定就得给他们分别提供`PageStorageKey`。这是因为Scrollable本身是一个StatefulWidget，它的状态中也会保存当前滚动位置，所以，只要可滚动组件本身没有被从树上detach掉，那么其State就不会销毁(dispose)，滚动位置就不会丢失。只有当Widget发生结构变化，导致可滚动组件的State销毁或重新构建时才会丢失状态，这种情况就需要显式指定`PageStorageKey`，通过`PageStorage`来存储滚动位置，一个典型的场景是在使用`TabBarView`时，在Tab发生切换时，Tab页中的可滚动组件的State就会销毁，这时如果想恢复滚动位置就需要指定`PageStorageKey`。\n\n\n\n### ScrollPosition\n\nScrollPosition是用来保存可滚动组件的滚动位置的。一个`ScrollController`对象可以同时被多个可滚动组件使用，`ScrollController`会为每一个可滚动组件创建一个`ScrollPosition`对象，这些`ScrollPosition`保存在`ScrollController`的`positions`属性中（`List<ScrollPosition>`）。`ScrollPosition`是真正保存滑动位置信息的对象，`offset`只是一个便捷属性：\n\n```dart\ndouble get offset => position.pixels;\n```\n\n一个`ScrollController`虽然可以对应多个可滚动组件，但是有一些操作，如读取滚动位置`offset`，则需要一对一！但是我们仍然可以在一对多的情况下，通过其它方法读取滚动位置，举个例子，假设一个`ScrollController`同时被两个可滚动组件使用，那么我们可以通过如下方式分别读取他们的滚动位置：\n\n```dart\n...\ncontroller.positions.elementAt(0).pixels\ncontroller.positions.elementAt(1).pixels\n...    \n```\n\n我们可以通过`controller.positions.length`来确定`controller`被几个可滚动组件使用。\n\n#### ScrollPosition的方法\n\n`ScrollPosition`有两个常用方法：`animateTo()` 和 `jumpTo()`，它们是真正来控制跳转滚动位置的方法，`ScrollController`的这两个同名方法，内部最终都会调用`ScrollPosition`的。\n\n### ScrollController控制原理\n\n我们来介绍一下`ScrollController`的另外三个方法：\n\n```dart\nScrollPosition createScrollPosition(\n    ScrollPhysics physics,\n    ScrollContext context,\n    ScrollPosition oldPosition);\nvoid attach(ScrollPosition position) ;\nvoid detach(ScrollPosition position) ;\n```\n\n当`ScrollController`和可滚动组件关联时，可滚动组件首先会调用`ScrollController`的`createScrollPosition()`方法来创建一个`ScrollPosition`来存储滚动位置信息，接着，可滚动组件会调用`attach()`方法，将创建的`ScrollPosition`添加到`ScrollController`的`positions`属性中，这一步称为“注册位置”，只有注册后`animateTo()` 和 `jumpTo()`才可以被调用。\n\n当可滚动组件销毁时，会调用`ScrollController`的`detach()`方法，将其`ScrollPosition`对象从`ScrollController`的`positions`属性中移除，这一步称为“注销位置”，注销后`animateTo()` 和 `jumpTo()` 将不能再被调用。\n\n需要注意的是，`ScrollController`的`animateTo()` 和 `jumpTo()`内部会调用所有`ScrollPosition`的`animateTo()` 和 `jumpTo()`，以实现所有和该`ScrollController`关联的可滚动组件都滚动到指定的位置。\n\n\n\n## 6.6.2 滚动监听\n\nFlutter Widget树中子Widget可以通过发送通知（Notification）与父(包括祖先)Widget通信。父级组件可以通过`NotificationListener`组件来监听自己关注的通知，这种通信方式类似于Web开发中浏览器的事件冒泡，我们在Flutter中沿用“冒泡”这个术语，关于通知冒泡我们将在后面“事件处理与通知”一章中详细介绍。\n\n可滚动组件在滚动时会发送`ScrollNotification`类型的通知，`ScrollBar`正是通过监听滚动通知来实现的。通过`NotificationListener`监听滚动事件和通过`ScrollController`有两个主要的不同：\n\n1. 通过NotificationListener可以在从可滚动组件到widget树根之间任意位置都能监听。而`ScrollController`只能和具体的可滚动组件关联后才可以。\n2. 收到滚动事件后获得的信息不同；`NotificationListener`在收到滚动事件时，通知中会携带当前滚动位置和ViewPort的一些信息，而`ScrollController`只能获取当前滚动位置。\n\n### 示例\n\n下面，我们监听`ListView`的滚动通知，然后显示当前滚动进度百分比：\n\n```dart\nimport 'package:flutter/material.dart';\n\nclass ScrollNotificationTestRoute extends StatefulWidget {\n  @override\n  _ScrollNotificationTestRouteState createState() =>\n      new _ScrollNotificationTestRouteState();\n}\n\nclass _ScrollNotificationTestRouteState\n    extends State<ScrollNotificationTestRoute> {\n  String _progress = \"0%\"; //保存进度百分比\n\n  @override\n  Widget build(BuildContext context) {\n    return Scrollbar( //进度条\n      // 监听滚动通知\n      child: NotificationListener<ScrollNotification>(\n        onNotification: (ScrollNotification notification) {\n          double progress = notification.metrics.pixels /\n              notification.metrics.maxScrollExtent;\n          //重新构建\n          setState(() {\n            _progress = \"${(progress * 100).toInt()}%\";\n          });\n          print(\"BottomEdge: ${notification.metrics.extentAfter == 0}\");\n          //return true; //放开此行注释后，进度条将失效\n        },\n        child: Stack(\n          alignment: Alignment.center,\n          children: <Widget>[\n            ListView.builder(\n                itemCount: 100,\n                itemExtent: 50.0,\n                itemBuilder: (context, index) {\n                  return ListTile(title: Text(\"$index\"));\n                }\n            ),\n            CircleAvatar(  //显示进度百分比\n              radius: 30.0,\n              child: Text(_progress),\n              backgroundColor: Colors.black54,\n            )\n          ],\n        ),\n      ),\n    );\n  }\n}\n```\n\n运行结果如图6-16所示：\n\n![图6-16](../imgs/6-16.png)\n\n在接收到滚动事件时，参数类型为`ScrollNotification`，它包括一个`metrics`属性，它的类型是`ScrollMetrics`，该属性包含当前ViewPort及滚动位置等信息：\n\n- `pixels`：当前滚动位置。\n- `maxScrollExtent`：最大可滚动长度。\n- `extentBefore`：滑出ViewPort顶部的长度；此示例中相当于顶部滑出屏幕上方的列表长度。\n- `extentInside`：ViewPort内部长度；此示例中屏幕显示的列表部分的长度。\n- `extentAfter`：列表中未滑入ViewPort部分的长度；此示例中列表底部未显示到屏幕范围部分的长度。\n- `atEdge`：是否滑到了可滚动组件的边界（此示例中相当于列表顶或底部）。\n\nScrollMetrics还有一些其它属性，读者可以自行查阅API文档。\n\n"
  },
  {
    "path": "src/chapter6/single_child_scrollview.md",
    "content": "\n# 6.2 SingleChildScrollView\n\n`SingleChildScrollView`类似于Android中的`ScrollView`，它只能接收一个子组件。定义如下：\n\n```dart\nSingleChildScrollView({\n  this.scrollDirection = Axis.vertical, //滚动方向，默认是垂直方向\n  this.reverse = false, \n  this.padding, \n  bool primary, \n  this.physics, \n  this.controller,\n  this.child,\n})\n```\n\n除了上一节我们介绍过的可滚动组件的通用属性外，我们重点看一下`reverse`和`primary`两个属性：\n\n- `reverse`：该属性API文档解释是：是否按照阅读方向相反的方向滑动，如：`scrollDirection`值为`Axis.horizontal`，如果阅读方向是从左到右(取决于语言环境，阿拉伯语就是从右到左)。`reverse`为`true`时，那么滑动方向就是从右往左。其实此属性本质上是决定可滚动组件的初始滚动位置是在“头”还是“尾”，取`false`时，初始滚动位置在“头”，反之则在“尾”，读者可以自己试验。\n- `primary`：指是否使用widget树中默认的`PrimaryScrollController`；当滑动方向为垂直方向（`scrollDirection`值为`Axis.vertical`）并且没有指定`controller`时，`primary`默认为`true`.\n\n需要注意的是，通常`SingleChildScrollView`只应在期望的内容不会超过屏幕太多时使用，这是因为`SingleChildScrollView`不支持基于Sliver的延迟实例化模型，所以如果预计视口可能包含超出屏幕尺寸太多的内容时，那么使用`SingleChildScrollView`将会非常昂贵（性能差），此时应该使用一些支持Sliver延迟加载的可滚动组件，如`ListView`。\n\n### 示例\n\n下面是一个将大写字母A-Z沿垂直方向显示的例子，由于垂直方向空间会超过屏幕视口高度，所以我们使用`SingleChildScrollView`：\n\n```dart\nclass SingleChildScrollViewTestRoute extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    String str = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\";\n    return Scrollbar( // 显示进度条\n      child: SingleChildScrollView(\n        padding: EdgeInsets.all(16.0),\n        child: Center(\n          child: Column( \n            //动态创建一个List<Widget>  \n            children: str.split(\"\") \n                //每一个字母都用一个Text显示,字体为原来的两倍\n                .map((c) => Text(c, textScaleFactor: 2.0,)) \n                .toList(),\n          ),\n        ),\n      ),\n    );\n  }\n}\n```\n\n\n\n运行效果如图6-1所示：\n\n![图6-1](../imgs/6-1.png)\n\n"
  },
  {
    "path": "src/chapter7/dailog.md",
    "content": "# 7.6 对话框详解\n\n本节将详细介绍一下Flutter中对话框的使用方式、实现原理、样式定制及状态管理。\n\n## 7.6.1 使用对话框\n\n对话框本质上也是UI布局，通常一个对话框会包含标题、内容，以及一些操作按钮，为此，Material库中提供了一些现成的对话框组件来用于快速的构建出一个完整的对话框。\n\n### AlertDialog\n\n下面我们主要介绍一下Material库中的`AlertDialog`组件，它的构造函数定义如下：\n\n```dart\nconst AlertDialog({\n  Key key,\n  this.title, //对话框标题组件\n  this.titlePadding, // 标题填充\n  this.titleTextStyle, //标题文本样式\n  this.content, // 对话框内容组件\n  this.contentPadding = const EdgeInsets.fromLTRB(24.0, 20.0, 24.0, 24.0), //内容的填充\n  this.contentTextStyle,// 内容文本样式\n  this.actions, // 对话框操作按钮组\n  this.backgroundColor, // 对话框背景色\n  this.elevation,// 对话框的阴影\n  this.semanticLabel, //对话框语义化标签(用于读屏软件)\n  this.shape, // 对话框外形\n})\n```\n\n参数都比较简单，不在赘述。下面我们看一个例子，假如我们要在删除文件时弹出一个确认对话框，该对话框如图7-10所示：\n\n![图7-10](../imgs/7-10.png)\n\n该对话框样式代码如下：\n\n```dart\nAlertDialog(\n  title: Text(\"提示\"),\n  content: Text(\"您确定要删除当前文件吗?\"),\n  actions: <Widget>[\n    FlatButton(\n      child: Text(\"取消\"),\n      onPressed: () => Navigator.of(context).pop(), //关闭对话框\n    ),\n    FlatButton(\n      child: Text(\"删除\"),\n      onPressed: () {\n        // ... 执行删除操作\n        Navigator.of(context).pop(true); //关闭对话框\n      },\n    ),\n  ],\n);\n```\n\n实现代码很简单，不在赘述。唯一需要注意的是我们是通过`Navigator.of(context).pop(…)`方法来关闭对话框的，这和路由返回的方式是一致的，并且都可以返回一个结果数据。现在，对话框我们已经构建好了，那么如何将它弹出来呢？还有对话框返回的数据应如何被接收呢？这些问题的答案都在`showDialog()`方法中。\n\n`showDialog()`是Material组件库提供的一个用于弹出Material风格对话框的方法，签名如下：\n\n```dart\nFuture<T> showDialog<T>({\n  @required BuildContext context,\n  bool barrierDismissible = true, //点击对话框barrier(遮罩)时是否关闭它\n  WidgetBuilder builder, // 对话框UI的builder\n})\n```\n\n该方法只有两个参数，含义见注释。该方法返回一个`Future`，它正是用于接收对话框的返回值：如果我们是通过点击对话框遮罩关闭的，则`Future`的值为`null`，否则为我们通过`Navigator.of(context).pop(result)`返回的result值，下面我们看一下整个示例：\n\n```dart\n//点击该按钮后弹出对话框\nRaisedButton(\n  child: Text(\"对话框1\"),\n  onPressed: () async {\n    //弹出对话框并等待其关闭\n    bool delete = await showDeleteConfirmDialog1();\n    if (delete == null) {\n      print(\"取消删除\");\n    } else {\n      print(\"已确认删除\");\n      //... 删除文件\n    }\n  },\n),\n\n// 弹出对话框\nFuture<bool> showDeleteConfirmDialog1() {\n  return showDialog<bool>(\n    context: context,\n    builder: (context) {\n      return AlertDialog(\n        title: Text(\"提示\"),\n        content: Text(\"您确定要删除当前文件吗?\"),\n        actions: <Widget>[\n          FlatButton(\n            child: Text(\"取消\"),\n            onPressed: () => Navigator.of(context).pop(), // 关闭对话框\n          ),\n          FlatButton(\n            child: Text(\"删除\"),\n            onPressed: () {\n              //关闭对话框并返回true\n              Navigator.of(context).pop(true);\n            },\n          ),\n        ],\n      );\n    },\n  );\n}\n```\n\n示例运行后，我们点击对话框“取消”按钮或遮罩，控制台就会输出\"取消删除\"，如果点击“删除”按钮，控制台就会输出\"已确认删除\"。\n\n> 注意：如果`AlertDialog`的内容过长，内容将会溢出，这在很多时候可能不是我们期望的，所以如果对话框内容过长时，可以用`SingleChildScrollView`将内容包裹起来。\n\n### SimpleDialog\n\n`SimpleDialog`也是Material组件库提供的对话框，它会展示一个列表，用于列表选择的场景。下面是一个选择APP语言的示例，运行结果如图7-11。\n\n![图7-11](../imgs/7-11.png)\n\n实现代码如下：\n\n```dart\nFuture<void> changeLanguage() async {\n  int i = await showDialog<int>(\n      context: context,\n      builder: (BuildContext context) {\n        return SimpleDialog(\n          title: const Text('请选择语言'),\n          children: <Widget>[\n            SimpleDialogOption(\n              onPressed: () {\n                // 返回1\n                Navigator.pop(context, 1);\n              },\n              child: Padding(\n                padding: const EdgeInsets.symmetric(vertical: 6),\n                child: const Text('中文简体'),\n              ),\n            ),\n            SimpleDialogOption(\n              onPressed: () {\n                // 返回2\n                Navigator.pop(context, 2);\n              },\n              child: Padding(\n                padding: const EdgeInsets.symmetric(vertical: 6),\n                child: const Text('美国英语'),\n              ),\n            ),\n          ],\n        );\n      });\n\n  if (i != null) {\n    print(\"选择了：${i == 1 ? \"中文简体\" : \"美国英语\"}\");\n  }\n}\n```\n\n列表项组件我们使用了`SimpleDialogOption`组件来包装了一下，它相当于一个FlatButton，只不过按钮文案是左对齐的，并且padding较小。上面示例运行后，用户选择一种语言后，控制台就会打印出它。\n\n### Dialog\n\n实际上`AlertDialog`和`SimpleDialog`都使用了`Dialog`类。由于`AlertDialog`和`SimpleDialog`中使用了`IntrinsicWidth`来尝试通过子组件的实际尺寸来调整自身尺寸，这就导致他们的子组件不能是延迟加载模型的组件（如`ListView`、`GridView` 、 `CustomScrollView`等），如下面的代码运行后会报错。\n\n```dart\nAlertDialog(\n  content: ListView(\n    children: ...//省略\n  ),\n);\n```\n\n如果我们就是需要嵌套一个`ListView`应该怎么做？这时，我们可以直接使用`Dialog`类，如：\n\n```dart\nDialog(\n  child: ListView(\n    children: ...//省略\n  ),\n);\n```\n\n下面我们看一个弹出一个有30个列表项的对话框示例，运行效果如图7-12所示：\n\n![图7-12](../imgs/7-12.png)\n\n实现代码如下：\n\n```dart\nFuture<void> showListDialog() async {\n  int index = await showDialog<int>(\n    context: context,\n    builder: (BuildContext context) {\n      var child = Column(\n        children: <Widget>[\n          ListTile(title: Text(\"请选择\")),\n          Expanded(\n              child: ListView.builder(\n            itemCount: 30,\n            itemBuilder: (BuildContext context, int index) {\n              return ListTile(\n                title: Text(\"$index\"),\n                onTap: () => Navigator.of(context).pop(index),\n              );\n            },\n          )),\n        ],\n      );\n      //使用AlertDialog会报错\n      //return AlertDialog(content: child);\n      return Dialog(child: child);\n    },\n  );\n  if (index != null) {\n    print(\"点击了：$index\");\n  }\n}\n```\n\n现在，我们己经介绍完了`AlertDialog`、`SimpleDialog`以及`Dialog`。上面的示例中，我们在调用`showDialog`时，在`builder`中都是构建了这三个对话框组件的一种，可能有些读者会惯性的以为在`builder`中只能返回这三者之一，其实这不是必须的！就拿`Dialog`的示例来举例，我们完全可以用下面的代码来替代`Dialog`：\n\n```dart\n// return Dialog(child: child) \nreturn UnconstrainedBox(\n  constrainedAxis: Axis.vertical,\n  child: ConstrainedBox(\n    constraints: BoxConstraints(maxWidth: 280),\n    child: Material(\n      child: child,\n      type: MaterialType.card,\n    ),\n  ),\n);\n```\n\n上面代码运行后可以实现一样的效果。现在我们总结一下：`AlertDialog`、`SimpleDialog`以及`Dialog`是Material组件库提供的三种对话框，旨在帮助开发者快速构建出符合Material设计规范的对话框，但读者完全可以自定义对话框样式，因此，我们仍然可以实现各种样式的对话框，这样即带来了易用性，又有很强的扩展性。\n\n## 7.6.2 对话框打开动画及遮罩\n\n我们可以把对话框分为内部样式和外部样式两部分。内部样式指对话框中显示的具体内容，这部分内容我们已经在上面介绍过了；外部样式包含对话框遮罩样式、打开动画等，本节主要介绍如何自定义这些外部样式。\n\n> 关于动画相关内容我们将在本书后面章节介绍，下面内容读者可以先了解一下（不必深究），读者可以在学习完动画相关内容后再回头来看。\n\n我们已经介绍过了`showDialog`方法，它是Material组件库中提供的一个打开Material风格对话框的方法。那如何打开一个普通风格的对话框呢（非Material风格）？ Flutter 提供了一个`showGeneralDialog`方法，签名如下：\n\n```dart\nFuture<T> showGeneralDialog<T>({\n  @required BuildContext context,\n  @required RoutePageBuilder pageBuilder, //构建对话框内部UI\n  bool barrierDismissible, //点击遮罩是否关闭对话框\n  String barrierLabel, // 语义化标签(用于读屏软件)\n  Color barrierColor, // 遮罩颜色\n  Duration transitionDuration, // 对话框打开/关闭的动画时长\n  RouteTransitionsBuilder transitionBuilder, // 对话框打开/关闭的动画\n})\n```\n\n实际上，`showDialog`方法正是`showGeneralDialog`的一个封装，定制了Material风格对话框的遮罩颜色和动画。Material风格对话框打开/关闭动画是一个Fade（渐隐渐显）动画，如果我们想使用一个缩放动画就可以通过`transitionBuilder`来自定义。下面我们自己封装一个`showCustomDialog`方法，它定制的对话框动画为缩放动画，并同时制定遮罩颜色为`Colors.black87`：\n\n```dart\nFuture<T> showCustomDialog<T>({\n  @required BuildContext context,\n  bool barrierDismissible = true,\n  WidgetBuilder builder,\n}) {\n  final ThemeData theme = Theme.of(context, shadowThemeOnly: true);\n  return showGeneralDialog(\n    context: context,\n    pageBuilder: (BuildContext buildContext, Animation<double> animation,\n        Animation<double> secondaryAnimation) {\n      final Widget pageChild = Builder(builder: builder);\n      return SafeArea(\n        child: Builder(builder: (BuildContext context) {\n          return theme != null\n              ? Theme(data: theme, child: pageChild)\n              : pageChild;\n        }),\n      );\n    },\n    barrierDismissible: barrierDismissible,\n    barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,\n    barrierColor: Colors.black87, // 自定义遮罩颜色\n    transitionDuration: const Duration(milliseconds: 150),\n    transitionBuilder: _buildMaterialDialogTransitions,\n  );\n}\n\nWidget _buildMaterialDialogTransitions(\n    BuildContext context,\n    Animation<double> animation,\n    Animation<double> secondaryAnimation,\n    Widget child) {\n  // 使用缩放动画\n  return ScaleTransition(\n    scale: CurvedAnimation(\n      parent: animation,\n      curve: Curves.easeOut,\n    ),\n    child: child,\n  );\n}\n```\n\n现在，我们使用`showCustomDialog`打开文件删除确认对话框，代码如下：\n\n```dart\n... //省略无关代码\nshowCustomDialog<bool>(\n  context: context,\n  builder: (context) {\n    return AlertDialog(\n      title: Text(\"提示\"),\n      content: Text(\"您确定要删除当前文件吗?\"),\n      actions: <Widget>[\n        FlatButton(\n          child: Text(\"取消\"),\n          onPressed: () => Navigator.of(context).pop(),\n        ),\n        FlatButton(\n          child: Text(\"删除\"),\n          onPressed: () {\n            // 执行删除操作\n            Navigator.of(context).pop(true);\n          },\n        ),\n      ],\n    );\n  },\n);\n```\n\n运行效果如图7-13所示：\n\n![图7-13](../imgs/7-13.png)\n\n可以发现，遮罩颜色比通过`showDialog`方法打开的对话框更深。另外对话框打开/关闭的动画已变为缩放动画了，读者可以亲自运行示例查看效果。\n\n## 7.6.3 对话框实现原理\n\n我们已经知道对话框最终都是由`showGeneralDialog`方法打开的，我们来看看它的具体实现：\n\n```dart\nFuture<T> showGeneralDialog<T>({\n  @required BuildContext context,\n  @required RoutePageBuilder pageBuilder,\n  bool barrierDismissible,\n  String barrierLabel,\n  Color barrierColor,\n  Duration transitionDuration,\n  RouteTransitionsBuilder transitionBuilder,\n}) {\n  return Navigator.of(context, rootNavigator: true).push<T>(_DialogRoute<T>(\n    pageBuilder: pageBuilder,\n    barrierDismissible: barrierDismissible,\n    barrierLabel: barrierLabel,\n    barrierColor: barrierColor,\n    transitionDuration: transitionDuration,\n    transitionBuilder: transitionBuilder,\n  ));\n}\n```\n\n实现很简单，直接调用`Navigator`的`push`方法打开了一个新的对话框路由`_DialogRoute`，然后返回了`push`的返回值。可见对话框实际上正是通过路由的形式实现的，这也是为什么我们可以使用`Navigator`的`pop` 方法来退出对话框的原因。关于对话框的样式定制在`_DialogRoute`中，没有什么新的东西，读者可以自行查看。\n\n## 7.6.4 对话框状态管理\n\n我们在用户选择删除一个文件时，会询问是否删除此文件；在用户选择一个文件夹是，应该再让用户确认是否删除子文件夹。为了在用户选择了文件夹时避免二次弹窗确认是否删除子目录，我们在确认对话框底部添加一个“同时删除子目录？”的复选框，如图7-14所示：\n\n![图7-14](../imgs/7-14.png)\n\n\n\n现在就有一个问题：如何管理复选框的选中状态？习惯上，我们会在路由页的State中来管理选中状态，我们可能会写出如下这样的代码：\n\n```dart\nclass _DialogRouteState extends State<DialogRoute> {\n  bool withTree = false; // 复选框选中状态\n\n  @override\n  Widget build(BuildContext context) {\n    return Column(\n      children: <Widget>[\n        RaisedButton(\n          child: Text(\"对话框2\"),\n          onPressed: () async {\n            bool delete = await showDeleteConfirmDialog2();\n            if (delete == null) {\n              print(\"取消删除\");\n            } else {\n              print(\"同时删除子目录: $delete\");\n            }\n          },\n        ),\n      ],\n    );\n  }\n\n  Future<bool> showDeleteConfirmDialog2() {\n    withTree = false; // 默认复选框不选中\n    return showDialog<bool>(\n      context: context,\n      builder: (context) {\n        return AlertDialog(\n          title: Text(\"提示\"),\n          content: Column(\n            crossAxisAlignment: CrossAxisAlignment.start,\n            mainAxisSize: MainAxisSize.min,\n            children: <Widget>[\n              Text(\"您确定要删除当前文件吗?\"),\n              Row(\n                children: <Widget>[\n                  Text(\"同时删除子目录？\"),\n                  Checkbox(\n                    value: withTree,\n                    onChanged: (bool value) {\n                      //复选框选中状态发生变化时重新构建UI\n                      setState(() {\n                        //更新复选框状态\n                        withTree = !withTree;\n                      });\n                    },\n                  ),\n                ],\n              ),\n            ],\n          ),\n          actions: <Widget>[\n            FlatButton(\n              child: Text(\"取消\"),\n              onPressed: () => Navigator.of(context).pop(),\n            ),\n            FlatButton(\n              child: Text(\"删除\"),\n              onPressed: () {\n                //执行删除操作\n                Navigator.of(context).pop(withTree);\n              },\n            ),\n          ],\n        );\n      },\n    );\n  }\n}\n```\n\n然后，当我们运行上面的代码时我们会发现复选框根本选不中！为什么会这样呢？其实原因很简单，我们知道`setState`方法只会针对当前context的子树重新build，但是我们的对话框并不是在`_DialogRouteState`的`build` 方法中构建的，而是通过`showDialog`单独构建的，所以在`_DialogRouteState`的context中调用`setState`是无法影响通过`showDialog`构建的UI的。另外，我们可以从另外一个角度来理解这个现象，前面说过对话框也是通过路由的方式来实现的，那么上面的代码实际上就等同于企图在父路由中调用`setState`来让子路由更新，这显然是不行的！简尔言之，根本原因就是context不对。那如何让复选框可点击呢？通常有如下三种方法：\n\n### 单独抽离出StatefulWidget\n\n既然是context不对，那么直接的思路就是将复选框的选中逻辑单独封装成一个`StatefulWidget`，然后在其内部管理复选状态。我们先来看看这种方法，下面是实现代码：\n\n```dart\n// 单独封装一个内部管理选中状态的复选框组件\nclass DialogCheckbox extends StatefulWidget {\n  DialogCheckbox({\n    Key key,\n    this.value,\n    @required this.onChanged,\n  });\n\n  final ValueChanged<bool> onChanged;\n  final bool value;\n\n  @override\n  _DialogCheckboxState createState() => _DialogCheckboxState();\n}\n\nclass _DialogCheckboxState extends State<DialogCheckbox> {\n  bool value;\n\n  @override\n  void initState() {\n    value = widget.value;\n    super.initState();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Checkbox(\n      value: value,\n      onChanged: (v) {\n        //将选中状态通过事件的形式抛出\n        widget.onChanged(v);\n        setState(() {\n          //更新自身选中状态\n          value = v;\n        });\n      },\n    );\n  }\n}\n```\n\n下面是弹出对话框的代码：\n\n```dart\nFuture<bool> showDeleteConfirmDialog3() {\n  bool _withTree = false; //记录复选框是否选中\n  return showDialog<bool>(\n    context: context,\n    builder: (context) {\n      return AlertDialog(\n        title: Text(\"提示\"),\n        content: Column(\n          crossAxisAlignment: CrossAxisAlignment.start,\n          mainAxisSize: MainAxisSize.min,\n          children: <Widget>[\n            Text(\"您确定要删除当前文件吗?\"),\n            Row(\n              children: <Widget>[\n                Text(\"同时删除子目录？\"),\n                DialogCheckbox(\n                  value: _withTree, //默认不选中\n                  onChanged: (bool value) {\n                    //更新选中状态\n                    _withTree = !_withTree;\n                  },\n                ),\n              ],\n            ),\n          ],\n        ),\n        actions: <Widget>[\n          FlatButton(\n            child: Text(\"取消\"),\n            onPressed: () => Navigator.of(context).pop(),\n          ),\n          FlatButton(\n            child: Text(\"删除\"),\n            onPressed: () {\n              // 将选中状态返回\n              Navigator.of(context).pop(_withTree);\n            },\n          ),\n        ],\n      );\n    },\n  );\n}\n```\n\n最后，就是使用：\n\n```dart\nRaisedButton(\n  child: Text(\"话框3（复选框可点击）\"),\n  onPressed: () async {\n    //弹出删除确认对话框，等待用户确认\n    bool deleteTree = await showDeleteConfirmDialog3();\n    if (deleteTree == null) {\n      print(\"取消删除\");\n    } else {\n      print(\"同时删除子目录: $deleteTree\");\n    }\n  },\n),\n```\n\n运行后效果如图7-15所示：\n\n![图7-15](../imgs/7-15.png)\n\n可见复选框能选中了，点击“取消”或“删除”后，控制台就会打印出最终的确认状态。\n\n### 使用StatefulBuilder方法\n\n上面的方法虽然能解决对话框状态更新的问题，但是有一个明显的缺点——对话框上所有可能会改变状态的组件都得单独封装在一个在内部管理状态的`StatefulWidget`中，这样不仅麻烦，而且复用性不大。因此，我们来想想能不能找到一种更简单的方法？上面的方法本质上就是将对话框的状态置于一个`StatefulWidget`的上下文中，由`StatefulWidget`在内部管理，那么我们有没有办法在不需要单独抽离组件的情况下创建一个`StatefulWidget`的上下文呢？想到这里，我们可以从`Builder`组件的实现获得灵感。在前面介绍过`Builder`组件可以获得组件所在位置的真正的Context，那它是怎么实现的呢，我们看看它的源码：\n\n```dart\nclass Builder extends StatelessWidget {\n  const Builder({\n    Key key,\n    @required this.builder,\n  }) : assert(builder != null),\n       super(key: key);\n  final WidgetBuilder builder;\n\n  @override\n  Widget build(BuildContext context) => builder(context);\n}\n```\n\n可以看到，`Builder`实际上只是继承了`StatelessWidget`，然后在`build`方法中获取当前context后将构建方法代理到了`builder`回调，可见，`Builder`实际上是获取了`StatelessWidget` 的上下文（context）。那么我们能否用相同的方法获取`StatefulWidget` 的上下文，并代理其`build`方法呢？下面我们照猫画虎，来封装一个`StatefulBuilder`方法：\n\n```dart\nclass StatefulBuilder extends StatefulWidget {\n  const StatefulBuilder({\n    Key key,\n    @required this.builder,\n  }) : assert(builder != null),\n       super(key: key);\n\n  final StatefulWidgetBuilder builder;\n\n  @override\n  _StatefulBuilderState createState() => _StatefulBuilderState();\n}\n\nclass _StatefulBuilderState extends State<StatefulBuilder> {\n  @override\n  Widget build(BuildContext context) => widget.builder(context, setState);\n}\n```\n\n代码很简单，`StatefulBuilder`获取了`StatefulWidget`的上下文，并代理了其构建过程。下面我们就可以通过`StatefulBuilder`来重构上面的代码了（变动只在`DialogCheckbox`部分）：\n\n```dart\n... //省略无关代码\nRow(\n  children: <Widget>[\n    Text(\"同时删除子目录？\"),\n    //使用StatefulBuilder来构建StatefulWidget上下文\n    StatefulBuilder(\n      builder: (context, _setState) {\n        return Checkbox(\n          value: _withTree, //默认不选中\n          onChanged: (bool value) {\n            //_setState方法实际就是该StatefulWidget的setState方法，\n            //调用后builder方法会重新被调用\n            _setState(() {\n              //更新选中状态\n              _withTree = !_withTree;\n            });\n          },\n        );\n      },\n    ),\n  ],\n),\n```\n\n实际上，这种方法本质上就是子组件通知父组件（StatefulWidget）重新build子组件本身来实现UI更新的，读者可以对比代码理解。实际上`StatefulBuilder`正是Flutter SDK中提供的一个类，它和`Builder`的原理是一样的，在此，提醒读者一定要将`StatefulBuilder`和`Builder`理解透彻，因为它们在Flutter中是非常实用的。\n\n### 精妙的解法\n\n是否还有更简单的解决方案呢？要确认这个问题，我们就得先搞清楚UI是怎么更新的，我们知道在调用`setState`方法后`StatefulWidget`就会重新build，那`setState`方法做了什么呢？我们能不能从中找到方法？顺着这个思路，我们就得看一下`setState`的核心源码：\n\n```dart\nvoid setState(VoidCallback fn) {\n  ... //省略无关代码\n  _element.markNeedsBuild();\n}\n```\n\n可以发现，`setState`中调用了`Element`的`markNeedsBuild()`方法，我们前面说过，Flutter是一个响应式框架，要更新UI只需改变状态后通知框架页面需要重构即可，而`Element`的`markNeedsBuild()`方法正是来实现这个功能的！`markNeedsBuild()`方法会将当前的`Element`对象标记为“dirty”（脏的），在每一个Frame，Flutter都会重新构建被标记为“dirty”`Element`对象。既然如此，我们有没有办法获取到对话框内部UI的`Element`对象，然后将其标示为为“dirty”呢？答案是肯定的！我们可以通过Context来得到`Element`对象，至于`Element`与`Context`的关系我们将会在后面“Flutter核心原理”一章中再深入介绍，现在只需要简单的认为：在组件树中，`context`实际上就是`Element`对象的引用。知道这个后，那么解决的方案就呼之欲出了，我们可以通过如下方式来让复选框可以更新：\n\n```dart\nFuture<bool> showDeleteConfirmDialog4() {\n  bool _withTree = false;\n  return showDialog<bool>(\n    context: context,\n    builder: (context) {\n      return AlertDialog(\n        title: Text(\"提示\"),\n        content: Column(\n          crossAxisAlignment: CrossAxisAlignment.start,\n          mainAxisSize: MainAxisSize.min,\n          children: <Widget>[\n            Text(\"您确定要删除当前文件吗?\"),\n            Row(\n              children: <Widget>[\n                Text(\"同时删除子目录？\"),\n                Checkbox( // 依然使用Checkbox组件\n                  value: _withTree,\n                  onChanged: (bool value) {\n                    // 此时context为对话框UI的根Element，我们 \n                    // 直接将对话框UI对应的Element标记为dirty\n                    (context as Element).markNeedsBuild();\n                    _withTree = !_withTree;\n                  },\n                ),\n              ],\n            ),\n          ],\n        ),\n        actions: <Widget>[\n          FlatButton(\n            child: Text(\"取消\"),\n            onPressed: () => Navigator.of(context).pop(),\n          ),\n          FlatButton(\n            child: Text(\"删除\"),\n            onPressed: () {\n              // 执行删除操作\n              Navigator.of(context).pop(_withTree);\n            },\n          ),\n        ],\n      );\n    },\n  );\n}\n```\n\n上面的代码运行后复选框也可以正常选中。可以看到，我们只用了一行代码便解决了这个问题！当然上面的代码并不是最优，因为我们只需要更新复选框的状态，而此时的`context`我们用的是对话框的根`context`，所以会导致整个对话框UI组件全部rebuild，因此最好的做法是将`context`的“范围”缩小，也就是说只将`Checkbox`的Element标记为`dirty`，优化后的代码为：\n\n```dart\n... //省略无关代码\nRow(\n  children: <Widget>[\n    Text(\"同时删除子目录？\"),\n    // 通过Builder来获得构建Checkbox的`context`，\n    // 这是一种常用的缩小`context`范围的方式\n    Builder(\n      builder: (BuildContext context) {\n        return Checkbox(\n          value: _withTree,\n          onChanged: (bool value) {\n            (context as Element).markNeedsBuild();\n            _withTree = !_withTree;\n          },\n        );\n      },\n    ),\n  ],\n),\n```\n\n## 7.6.5 其它类型的对话框\n\n### 底部菜单列表\n\n`showModalBottomSheet`方法可以弹出一个Material风格的底部菜单列表模态对话框，示例如下：\n\n```dart\n// 弹出底部菜单列表模态对话框\nFuture<int> _showModalBottomSheet() {\n  return showModalBottomSheet<int>(\n    context: context,\n    builder: (BuildContext context) {\n      return ListView.builder(\n        itemCount: 30,\n        itemBuilder: (BuildContext context, int index) {\n          return ListTile(\n            title: Text(\"$index\"),\n            onTap: () => Navigator.of(context).pop(index),\n          );\n        },\n      );\n    },\n  );\n}\n```\n\n点击按钮，弹出该对话框：\n\n```dart\nRaisedButton(\n  child: Text(\"显示底部菜单列表\"),\n  onPressed: () async {\n    int type = await _showModalBottomSheet();\n    print(type);\n  },\n),\n```\n\n运行后效果如图7-16所示：\n\n![图7-16](../imgs/7-16.png)\n\n`showModalBottomSheet`的实现原理和`showGeneralDialog`实现原理相同，都是通过路由的方式来实现的，读者可以查看源码对比。但值得一提的是还有一个`showBottomSheet`方法，该方法会从设备底部向上弹出一个全屏的菜单列表，示例如下：\n\n```dart\n// 返回的是一个controller\nPersistentBottomSheetController<int> _showBottomSheet() {\n  return showBottomSheet<int>(\n    context: context,\n    builder: (BuildContext context) {\n      return ListView.builder(\n        itemCount: 30,\n        itemBuilder: (BuildContext context, int index) {\n          return ListTile(\n            title: Text(\"$index\"),\n            onTap: (){\n              // do something\n              print(\"$index\");\n              Navigator.of(context).pop();\n            },\n          );\n        },\n      );\n    },\n  );\n}\n```\n\n运行效果如图7-17所示：\n\n![图7-17](../imgs/7-17.png)\n\n`PersistentBottomSheetController`中包含了一些控制对话框的方法比如`close`方法可以关闭该对话框，功能比较简单，读者可以自行查看源码。唯一需要注意的是，`showBottomSheet`和我们上面介绍的弹出对话框的方法原理不同：`showBottomSheet`是调用widget树顶部的`Scaffold`组件的`ScaffoldState `的`showBottomSheet`同名方法实现，也就是说要调用`showBottomSheet`方法就必须得保证父级组件中有`Scaffold`。\n\n### Loading框\n\n其实Loading框可以直接通过`showDialog`+`AlertDialog`来自定义：\n\n```dart\nshowLoadingDialog() {\n  showDialog(\n    context: context,\n    barrierDismissible: false, //点击遮罩不关闭对话框\n    builder: (context) {\n      return AlertDialog(\n        content: Column(\n          mainAxisSize: MainAxisSize.min,\n          children: <Widget>[\n            CircularProgressIndicator(),\n            Padding(\n              padding: const EdgeInsets.only(top: 26.0),\n              child: Text(\"正在加载，请稍后...\"),\n            )\n          ],\n        ),\n      );\n    },\n  );\n}\n```\n\n显示效果如图7-18所示：\n\n![图7-18](../imgs/7-18.png)\n\n如果我们嫌Loading框太宽，想自定义对话框宽度，这时只使用`SizedBox`或`ConstrainedBox`是不行的，原因是`showDialog`中已经给对话框设置了宽度限制，根据我们在第五章“尺寸限制类容器”一节中所述，我们可以使用`UnconstrainedBox`先抵消`showDialog`对宽度的限制，然后再使用`SizedBox`指定宽度，代码如下：\n\n```dart\n... //省略无关代码\nUnconstrainedBox(\n  constrainedAxis: Axis.vertical,\n  child: SizedBox(\n    width: 280,\n    child: AlertDialog(\n      content: Column(\n        mainAxisSize: MainAxisSize.min,\n        children: <Widget>[\n          CircularProgressIndicator(value: .8,),\n          Padding(\n            padding: const EdgeInsets.only(top: 26.0),\n            child: Text(\"正在加载，请稍后...\"),\n          )\n        ],\n      ),\n    ),\n  ),\n);\n```\n\n代码运行后，效果如图7-19所示：\n\n![图7-19](../imgs/7-19.png)\n\n### 日历选择\n\n我们先看一下Material风格的日历选择器，如图7-20所示：\n\n![图7-20](../imgs/7-20.png)\n\n实现代码：\n\n```dart\nFuture<DateTime> _showDatePicker1() {\n  var date = DateTime.now();\n  return showDatePicker(\n    context: context,\n    initialDate: date,\n    firstDate: date,\n    lastDate: date.add( //未来30天可选\n      Duration(days: 30),\n    ),\n  );\n}\n```\n\niOS风格的日历选择器需要使用`showCupertinoModalPopup`方法和`CupertinoDatePicker`组件来实现：\n\n```dart\nFuture<DateTime> _showDatePicker2() {\n  var date = DateTime.now();\n  return showCupertinoModalPopup(\n    context: context,\n    builder: (ctx) {\n      return SizedBox(\n        height: 200,\n        child: CupertinoDatePicker(\n          mode: CupertinoDatePickerMode.dateAndTime,\n          minimumDate: date,\n          maximumDate: date.add(\n            Duration(days: 30),\n          ),\n          maximumYear: date.year + 1,\n          onDateTimeChanged: (DateTime value) {\n            print(value);\n          },\n        ),\n      );\n    },\n  );\n}\n```\n\n运行效果如图7-21所示：\n\n![图7-21](../imgs/7-21.png)\n"
  },
  {
    "path": "src/chapter7/futurebuilder_and_streambuilder.md",
    "content": "# 7.5 异步UI更新（FutureBuilder、StreamBuilder）\n\n很多时候我们会依赖一些异步数据来动态更新UI，比如在打开一个页面时我们需要先从互联网上获取数据，在获取数据的过程中我们显示一个加载框，等获取到数据时我们再渲染页面；又比如我们想展示Stream（比如文件流、互联网数据接收流）的进度。当然，通过StatefulWidget我们完全可以实现上述这些功能。但由于在实际开发中依赖异步数据更新UI的这种场景非常常见，因此Flutter专门提供了`FutureBuilder`和`StreamBuilder`两个组件来快速实现这种功能。\n\n## 7.5.1 FutureBuilder\n\n`FutureBuilder`会依赖一个`Future`，它会根据所依赖的`Future`的状态来动态构建自身。我们看一下`FutureBuilder`构造函数：\n\n```dart\nFutureBuilder({\n  this.future,\n  this.initialData,\n  @required this.builder,\n})\n```\n\n- `future`：`FutureBuilder`依赖的`Future`，通常是一个异步耗时任务。\n\n- `initialData`：初始数据，用户设置默认数据。\n\n- `builder`：Widget构建器；该构建器会在`Future`执行的不同阶段被多次调用，构建器签名如下：\n\n  ```dart\n  Function (BuildContext context, AsyncSnapshot snapshot) \n  ```\n\n  `snapshot`会包含当前异步任务的状态信息及结果信息 ，比如我们可以通过`snapshot.connectionState`获取异步任务的状态信息、通过`snapshot.hasError`判断异步任务是否有错误等等，完整的定义读者可以查看`AsyncSnapshot`类定义。\n  \n  另外，`FutureBuilder`的`builder`函数签名和`StreamBuilder`的`builder`是相同的。\n\n### 示例\n\n我们实现一个路由，当该路由打开时我们从网上获取数据，获取数据时弹一个加载框；获取结束时，如果成功则显示获取到的数据，如果失败则显示错误。由于我们还没有介绍在flutter中如何发起网络请求，所以在这里我们不真正去网络请求数据，而是模拟一下这个过程，隔3秒后返回一个字符串：\n\n```dart\nFuture<String> mockNetworkData() async {\n  return Future.delayed(Duration(seconds: 2), () => \"我是从互联网上获取的数据\");\n}\n```\n\n`FutureBuilder`使用代码如下：\n\n```dart\n...\nWidget build(BuildContext context) {\n  return Center(\n    child: FutureBuilder<String>(\n      future: mockNetworkData(),\n      builder: (BuildContext context, AsyncSnapshot snapshot) {\n        // 请求已结束\n        if (snapshot.connectionState == ConnectionState.done) {\n          if (snapshot.hasError) {\n            // 请求失败，显示错误\n            return Text(\"Error: ${snapshot.error}\");\n          } else {\n            // 请求成功，显示数据\n            return Text(\"Contents: ${snapshot.data}\");\n          }\n        } else {\n          // 请求未结束，显示loading\n          return CircularProgressIndicator();\n        }\n      },\n    ),\n  );\n}\n```\n\n运行结果如图7-8、7-9所示：\n\n![图7-8](../imgs/7-8.png)![图7-9](../imgs/7-9.png)\n\n上面代码中我们在`builder`中根据当前异步任务状态`ConnectionState`来返回不同的widget。`ConnectionState`是一个枚举类，定义如下：\n\n```dart\nenum ConnectionState {\n  /// 当前没有异步任务，比如[FutureBuilder]的[future]为null时\n  none,\n\n  /// 异步任务处于等待状态\n  waiting,\n\n  /// Stream处于激活状态（流上已经有数据传递了），对于FutureBuilder没有该状态。\n  active,\n\n  /// 异步任务已经终止.\n  done,\n}\n```\n\n注意，`ConnectionState.active`只在`StreamBuilder`中才会出现。\n\n\n\n## 7.5.2 StreamBuilder\n\n我们知道，在Dart中`Stream` 也是用于接收异步事件数据，和`Future` 不同的是，它可以接收多个异步操作的结果，它常用于会多次读取数据的异步任务场景，如网络内容下载、文件读写等。`StreamBuilder`正是用于配合`Stream`来展示流上事件（数据）变化的UI组件。下面看一下`StreamBuilder`的默认构造函数：\n\n```dart\nStreamBuilder({\n  Key key,\n  this.initialData,\n  Stream<T> stream,\n  @required this.builder,\n}) \n```\n\n可以看到和`FutureBuilder`的构造函数只有一点不同：前者需要一个`future`，而后者需要一个`stream`。\n\n### 示例\n\n我们创建一个计时器的示例：每隔1秒，计数加1。这里，我们使用`Stream`来实现每隔一秒生成一个数字:\n\n```dart\nStream<int> counter() {\n  return Stream.periodic(Duration(seconds: 1), (i) {\n    return i;\n  });\n}\n```\n\n`StreamBuilder`使用代码如下：\n\n```dart\n  \n Widget build(BuildContext context) {\n    return StreamBuilder<int>(\n      stream: counter(), //\n      //initialData: ,// a Stream<int> or null\n      builder: (BuildContext context, AsyncSnapshot<int> snapshot) {\n        if (snapshot.hasError)\n          return Text('Error: ${snapshot.error}');\n        switch (snapshot.connectionState) {\n          case ConnectionState.none:\n            return Text('没有Stream');\n          case ConnectionState.waiting:\n            return Text('等待数据...');\n          case ConnectionState.active:\n            return Text('active: ${snapshot.data}');\n          case ConnectionState.done:\n            return Text('Stream已关闭');\n        }\n        return null; // unreachable\n      },\n    );\n }\n```\n\n读者可以自己运行本示例查看运行结果。注意，本示例只是为了演示`StreamBuilder`的使用，在实战中，凡是UI会依赖多个异步数据而发生变化的场景都可以使用`StreamBuilder`。\n"
  },
  {
    "path": "src/chapter7/index.md",
    "content": "## 功能型Widget简介\n\n功能型Widget指的是不会影响UI布局及外观的Widget，它们通常具有一定的功能，如事件监听、数据存储等，我们之前介绍过的FocusScope（焦点控制）、PageStorage（数据存储）、NotificationListener（事件监听）都属于功能型Widget。由于Widget是Flutter的一等公民，功能型Widget非常多，我们不会去一一介绍，本章中主要介绍几种常用的功能型Widget。\n\n## 本章目录\n\n* [7.1：导航返回拦截（WillPopScope）](willpopscope.md)\n* [7.2：数据共享（InheritedWidget）](inherited_widget.md)\n* [7.3： 跨组件状态共享（Provider）](provider.md)\n* [7.4：颜色和主题（Theme）](theme.md) \n* [7.5：异步UI更新（FutureBuilder、StreamBuilder）](futurebuilder_and_streambuilder.md)\n* [7.6：对话框详解](dailog.md)  \n"
  },
  {
    "path": "src/chapter7/inherited_widget.md",
    "content": "\n\n# 7.2 数据共享（InheritedWidget）\n\n`InheritedWidget`是Flutter中非常重要的一个功能型组件，它提供了一种数据在widget树中从上到下传递、共享的方式，比如我们在应用的根widget中通过`InheritedWidget`共享了一个数据，那么我们便可以在任意子widget中来获取该共享的数据！这个特性在一些需要在widget树中共享数据的场景中非常方便！如Flutter SDK中正是通过InheritedWidget来共享应用主题（`Theme`）和Locale (当前语言环境)信息的。\n\n> `InheritedWidget`和React中的context功能类似，和逐级传递数据相比，它们能实现组件跨级传递数据。`InheritedWidget`的在widget树中数据传递方向是从上到下的，这和通知`Notification`（将在下一章中介绍）的传递方向正好相反。\n\n### didChangeDependencies\n\n在之前介绍`StatefulWidget`时，我们提到`State`对象有一个`didChangeDependencies`回调，它会在“依赖”发生变化时被Flutter Framework调用。而这个“依赖”指的就是子widget是否使用了父widget中`InheritedWidget`的数据！如果使用了，则代表子widget依赖有依赖`InheritedWidget`；如果没有使用则代表没有依赖。这种机制可以使子组件在所依赖的`InheritedWidget`变化时来更新自身！比如当主题、locale(语言)等发生变化时，依赖其的子widget的`didChangeDependencies`方法将会被调用。\n\n下面我们看一下之前“计数器”示例应用程序的`InheritedWidget`版本。需要说明的是，本示例主要是为了演示`InheritedWidget`的功能特性，并不是计数器的推荐实现方式。\n\n首先，我们通过继承`InheritedWidget`，将当前计数器点击次数保存在`ShareDataWidget`的`data`属性中：\n\n```dart\nclass ShareDataWidget extends InheritedWidget {\n  ShareDataWidget({\n    @required this.data,\n    Widget child\n  }) :super(child: child);\n    \n  final int data; //需要在子树中共享的数据，保存点击次数\n    \n  //定义一个便捷方法，方便子树中的widget获取共享数据  \n  static ShareDataWidget of(BuildContext context) {\n    return context.dependOnInheritedWidgetOfExactType<ShareDataWidget>();\n  }\n\n  //该回调决定当data发生变化时，是否通知子树中依赖data的Widget  \n  @override\n  bool updateShouldNotify(ShareDataWidget old) {\n    //如果返回true，则子树中依赖(build函数中有调用)本widget\n    //的子widget的`state.didChangeDependencies`会被调用\n    return old.data != data;\n  }\n}\n```\n\n然后我们实现一个子组件` _TestWidget`，在其`build`方法中引用`ShareDataWidget`中的数据。同时，在其`didChangeDependencies()` 回调中打印日志：\n\n```dart\nclass _TestWidget extends StatefulWidget {\n  @override\n  __TestWidgetState createState() => new __TestWidgetState();\n}\n\nclass __TestWidgetState extends State<_TestWidget> {\n  @override\n  Widget build(BuildContext context) {\n    //使用InheritedWidget中的共享数据\n    return Text(ShareDataWidget\n        .of(context)\n        .data\n        .toString());\n  }\n\n  @override\n  void didChangeDependencies() {\n    super.didChangeDependencies();\n    //父或祖先widget中的InheritedWidget改变(updateShouldNotify返回true)时会被调用。\n    //如果build中没有依赖InheritedWidget，则此回调不会被调用。\n    print(\"Dependencies change\");\n  }\n}\n```\n\n最后，我们创建一个按钮，每点击一次，就将`ShareDataWidget`的值自增：\n\n```dart\nclass InheritedWidgetTestRoute extends StatefulWidget {\n  @override\n  _InheritedWidgetTestRouteState createState() => new _InheritedWidgetTestRouteState();\n}\n\nclass _InheritedWidgetTestRouteState extends State<InheritedWidgetTestRoute> {\n  int count = 0;\n\n  @override\n  Widget build(BuildContext context) {\n    return  Center(\n      child: ShareDataWidget( //使用ShareDataWidget\n        data: count,\n        child: Column(\n          mainAxisAlignment: MainAxisAlignment.center,\n          children: <Widget>[\n            Padding(\n              padding: const EdgeInsets.only(bottom: 20.0),\n              child: _TestWidget(),//子widget中依赖ShareDataWidget\n            ),\n            RaisedButton(\n              child: Text(\"Increment\"),\n              //每点击一次，将count自增，然后重新build,ShareDataWidget的data将被更新  \n              onPressed: () => setState(() => ++count),\n            )\n          ],\n        ),\n      ),\n    );\n  }\n}\n```\n\n运行后界面如图7-1所示：\n\n![图7-1](../imgs/7-1.png)\n\n每点击一次按钮，计数器就会自增，控制台就会打印一句日志：\n\n```\nI/flutter ( 8513): Dependencies change\n```\n\n可见依赖发生变化后，其`didChangeDependencies()`会被调用。但是读者要注意，**如果_TestWidget的build方法中没有使用ShareDataWidget的数据，那么它的`didChangeDependencies()`将不会被调用，因为它并没有依赖ShareDataWidget**。例如，我们将`__TestWidgetState`代码改为下面这样，`didChangeDependencies()`将不会被调用:\n\n```dart\nclass __TestWidgetState extends State<_TestWidget> {\n  @override\n  Widget build(BuildContext context) {\n    // 使用InheritedWidget中的共享数据\n    //    return Text(ShareDataWidget\n    //        .of(context)\n    //        .data\n    //        .toString());\n     return Text(\"text\");\n  }\n\n  @override\n  void didChangeDependencies() {\n    super.didChangeDependencies();\n    // build方法中没有依赖InheritedWidget，此回调不会被调用。\n    print(\"Dependencies change\");\n  }\n}\n```\n\n上面的代码中，我们将`build()`方法中依赖`ShareDataWidget`的代码注释掉了，然后返回一个固定`Text`，这样一来，当点击Increment按钮后，`ShareDataWidget`的`data`虽然发生变化，但由于`__TestWidgetState`并未依赖`ShareDataWidget`，所以`__TestWidgetState`的`didChangeDependencies`方法不会被调用。其实，这个机制很好理解，因为在数据发生变化时只对使用该数据的Widget更新是合理并且性能友好的。\n\n> 思考题：Flutter framework是怎么知道子widget有没有依赖InheritedWidget的？\n\n#### 应该在didChangeDependencies()中做什么？\n\n一般来说，子widget很少会重写此方法，因为在依赖改变后framework也都会调用`build()`方法。但是，如果你需要在依赖改变后执行一些昂贵的操作，比如网络请求，这时最好的方式就是在此方法中执行，这样可以避免每次`build()`都执行这些昂贵操作。\n\n### 深入了解InheritedWidget\n\n现在来思考一下，如果我们只想在`__TestWidgetState`中引用`ShareDataWidget`数据，但却不希望在`ShareDataWidget`发生变化时调用`__TestWidgetState`的`didChangeDependencies()`方法应该怎么办？其实答案很简单，我们只需要将`ShareDataWidget.of()`的实现改一下即可：\n\n```dart\n//定义一个便捷方法，方便子树中的widget获取共享数据\nstatic ShareDataWidget of(BuildContext context) {\n  //return context.dependOnInheritedWidgetOfExactType<ShareDataWidget>();\n  return context.getElementForInheritedWidgetOfExactType<ShareDataWidget>().widget;\n}\n```\n\n唯一的改动就是获取`ShareDataWidget`对象的方式，把`dependOnInheritedWidgetOfExactType()`方法换成了`context.getElementForInheritedWidgetOfExactType<ShareDataWidget>().widget`，那么他们到底有什么区别呢，我们看一下这两个方法的源码（实现代码在`Element`类中，`Context`和`Element`的关系我们将在后面专门介绍）：\n\n```dart \n@override\nInheritedElement getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() {\n  assert(_debugCheckStateIsActiveForAncestorLookup());\n  final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];\n  return ancestor;\n}\n@override\nInheritedWidget dependOnInheritedWidgetOfExactType({ Object aspect }) {\n  assert(_debugCheckStateIsActiveForAncestorLookup());\n  final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];\n  //多出的部分\n  if (ancestor != null) {\n    assert(ancestor is InheritedElement);\n    return dependOnInheritedElement(ancestor, aspect: aspect) as T;\n  }\n  _hadUnsatisfiedDependencies = true;\n  return null;\n}\n```\n\n我们可以看到，`dependOnInheritedWidgetOfExactType()` 比 `getElementForInheritedWidgetOfExactType()`多调了`dependOnInheritedElement`方法，`dependOnInheritedElement`源码如下：\n\n```dart\n  @override\n  InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect }) {\n    assert(ancestor != null);\n    _dependencies ??= HashSet<InheritedElement>();\n    _dependencies.add(ancestor);\n    ancestor.updateDependencies(this, aspect);\n    return ancestor.widget;\n  }\n```\n\n可以看到`dependOnInheritedElement`方法中主要是注册了依赖关系！看到这里也就清晰了，**调用`dependOnInheritedWidgetOfExactType()` 和 `getElementForInheritedWidgetOfExactType()`的区别就是前者会注册依赖关系，而后者不会**，所以在调用`dependOnInheritedWidgetOfExactType()`时，`InheritedWidget`和依赖它的子孙组件关系便完成了注册，之后当`InheritedWidget`发生变化时，就会更新依赖它的子孙组件，也就是会调这些子孙组件的`didChangeDependencies()`方法和`build()`方法。而当调用的是 `getElementForInheritedWidgetOfExactType()`时，由于没有注册依赖关系，所以之后当`InheritedWidget`发生变化时，就不会更新相应的子孙Widget。\n\n注意，如果将上面示例中`ShareDataWidget.of()`方法实现改成调用`getElementForInheritedWidgetOfExactType()`，运行示例后，点击\"Increment\"按钮，会发现`__TestWidgetState `的`didChangeDependencies()`方法确实不会再被调用，但是其`build()`仍然会被调用！造成这个的原因其实是，点击\"Increment\"按钮后，会调用`_InheritedWidgetTestRouteState`的`setState()`方法，此时会重新构建整个页面，由于示例中，`__TestWidget` 并没有任何缓存，所以它也都会被重新构建，所以也会调用`build()`方法。\n\n那么，现在就带来了一个问题：实际上，我们只想更新子树中依赖了`ShareDataWidget`的组件，而现在只要调用`_InheritedWidgetTestRouteState`的`setState()`方法，所有子节点都会被重新build，这很没必要，那么有什么办法可以避免呢？答案是缓存！一个简单的做法就是通过封装一个`StatefulWidget`，将子Widget树缓存起来，具体做法下一节我们将通过实现一个`Provider` Widget 来演示如何缓存，以及如何利用`InheritedWidget` 来实现Flutter全局状态共享。\n\n\n\n\n\n"
  },
  {
    "path": "src/chapter7/provider.md",
    "content": "# 7.3 跨组件状态共享（Provider）\n\n在Flutter开发中，状态管理是一个永恒的话题。一般的原则是：如果状态是组件私有的，则应该由组件自己管理；如果状态要跨组件共享，则该状态应该由各个组件共同的父元素来管理。对于组件私有的状态管理很好理解，但对于跨组件共享的状态，管理的方式就比较多了，如使用全局事件总线EventBus（将在下一章中介绍），它是一个观察者模式的实现，通过它就可以实现跨组件状态同步：状态持有方（发布者）负责更新、发布状态，状态使用方（观察者）监听状态改变事件来执行一些操作。下面我们看一个登陆状态同步的简单示例：\n\n定义事件：\n\n```dart\nenum Event{\n  login,\n  ... //省略其它事件\n}\n```\n\n登录页代码大致如下：\n\n```dart\n// 登录状态改变后发布状态改变事件\nbus.emit(Event.login);\n```\n\n依赖登录状态的页面：\n\n```dart\nvoid onLoginChanged(e){\n  //登录状态变化处理逻辑\n}\n\n@override\nvoid initState() {\n  //订阅登录状态改变事件\n  bus.on(Event.login,onLogin);\n  super.initState();\n}\n\n@override\nvoid dispose() {\n  //取消订阅\n  bus.off(Event.login,onLogin);\n  super.dispose();\n}\n```\n\n我们可以发现，通过观察者模式来实现跨组件状态共享有一些明显的缺点：\n\n1. 必须显式定义各种事件，不好管理\n2. 订阅者必须需显式注册状态改变回调，也必须在组件销毁时手动去解绑回调以避免内存泄露。\n\n在Flutter当中有没有更好的跨组件状态管理方式了呢？答案是肯定的，那怎么做的？我们想想前面介绍的`InheritedWidget`，它的天生特性就是能绑定`InheritedWidget`与依赖它的子孙组件的依赖关系，并且当`InheritedWidget`数据发生变化时，可以自动更新依赖的子孙组件！利用这个特性，我们可以将需要跨组件共享的状态保存在`InheritedWidget`中，然后在子组件中引用`InheritedWidget`即可，Flutter社区著名的Provider包正是基于这个思想实现的一套跨组件状态共享解决方案，接下来我们便详细介绍一下Provider的用法及原理。\n\n## Provider\n\n为了加强读者的理解，我们不直接去看Provider包的源代码，相反，我会带着你根据上面描述的通过`InheritedWidget`实现的思路来一步一步地实现一个最小功能的Provider。\n\n首先，我们需要一个保存需要共享的数据`InheritedWidget`，由于具体业务数据类型不可预期，为了通用性，我们使用泛型，定义一个通用的`InheritedProvider`类，它继承自`InheritedWidget`：\n\n```dart\n// 一个通用的InheritedWidget，保存任需要跨组件共享的状态\nclass InheritedProvider<T> extends InheritedWidget {\n  InheritedProvider({@required this.data, Widget child}) : super(child: child);\n  \n  //共享状态使用泛型\n  final T data;\n  \n  @override\n  bool updateShouldNotify(InheritedProvider<T> old) {\n    //在此简单返回true，则每次更新都会调用依赖其的子孙节点的`didChangeDependencies`。\n    return true;\n  }\n}\n```\n\n数据保存的地方有了，那么接下来我们需要做的就是在数据发生变化的时候来重新构建`InheritedProvider`，那么现在就面临两个问题：\n\n1. 数据发生变化怎么通知？\n2. 谁来重新构建`InheritedProvider`？\n\n第一个问题其实很好解决，我们当然可以使用之前介绍的eventBus来进行事件通知，但是为了更贴近Flutter开发，我们使用Flutter SDK中提供的`ChangeNotifier`类 ，它继承自`Listenable`，也实现了一个Flutter风格的发布者-订阅者模式，`ChangeNotifier`定义大致如下：\n\n```dart\nclass ChangeNotifier implements Listenable {\n  List listeners=[];\n  @override\n  void addListener(VoidCallback listener) {\n     //添加监听器\n     listeners.add(listener);\n  }\n  @override\n  void removeListener(VoidCallback listener) {\n    //移除监听器\n    listeners.remove(listener);\n  }\n  \n  void notifyListeners() {\n    //通知所有监听器，触发监听器回调 \n    listeners.forEach((item)=>item());\n  }\n   \n  ... //省略无关代码\n}\n\n```\n\n我们可以通过调用`addListener()`和`removeListener()`来添加、移除监听器（订阅者）；通过调用`notifyListeners()` 可以触发所有监听器回调。\n\n现在，我们将要共享的状态放到一个Model类中，然后让它继承自`ChangeNotifier`，这样当共享的状态改变时，我们只需要调用`notifyListeners()` 来通知订阅者，然后由订阅者来重新构建`InheritedProvider`，这也是第二个问题的答案！接下来我们便实现这个订阅者类：\n\n```dart\n\nclass ChangeNotifierProvider<T extends ChangeNotifier> extends StatefulWidget {\n  ChangeNotifierProvider({\n    Key key,\n    this.data,\n    this.child,\n  });\n\n  final Widget child;\n  final T data;\n\n  //定义一个便捷方法，方便子树中的widget获取共享数据\n  static T of<T>(BuildContext context) {\n    final type = _typeOf<InheritedProvider<T>>();\n    final provider =  context.dependOnInheritedWidgetOfExactType<InheritedProvider<T>>();\n    return provider.data;\n  }\n\n  @override\n  _ChangeNotifierProviderState<T> createState() => _ChangeNotifierProviderState<T>();\n}\n```\n\n该类继承`StatefulWidget`，然后定义了一个`of()`静态方法供子类方便获取Widget树中的`InheritedProvider`中保存的共享状态(model)，下面我们实现该类对应的`_ChangeNotifierProviderState`类：\n\n```dart\nclass _ChangeNotifierProviderState<T extends ChangeNotifier> extends State<ChangeNotifierProvider<T>> {\n  void update() {\n    //如果数据发生变化（model类调用了notifyListeners），重新构建InheritedProvider\n    setState(() => {});\n  }\n\n  @override\n  void didUpdateWidget(ChangeNotifierProvider<T> oldWidget) {\n    //当Provider更新时，如果新旧数据不\"==\"，则解绑旧数据监听，同时添加新数据监听\n    if (widget.data != oldWidget.data) {\n      oldWidget.data.removeListener(update);\n      widget.data.addListener(update);\n    }\n    super.didUpdateWidget(oldWidget);\n  }\n\n  @override\n  void initState() {\n    // 给model添加监听器\n    widget.data.addListener(update);\n    super.initState();\n  }\n\n  @override\n  void dispose() {\n    // 移除model的监听器\n    widget.data.removeListener(update);\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return InheritedProvider<T>(\n      data: widget.data,\n      child: widget.child,\n    );\n  }\n}\n```\n\n可以看到`_ChangeNotifierProviderState`类的主要作用就是监听到共享状态（model）改变时重新构建Widget树。注意，在`_ChangeNotifierProviderState`类中调用`setState()`方法，`widget.child`始终是同一个，所以执行build时，`InheritedProvider`的child引用的始终是同一个子widget，所以`widget.child`并不会重新`build`，这也就相当于对`child`进行了缓存！当然如果`ChangeNotifierProvider`父级Widget重新build时，则其传入的`child`便有可能会发生变化。\n\n现在我们所需要的各个工具类都已完成，下面我们通过一个购物车的例子来看看怎么使用上面的这些类。\n\n### 购物车示例\n\n我们需要实现一个显示购物车中所有商品总价的功能：\n\n1. 向购物车中添加新商品时总价更新\n\n定义一个`Item`类，用于表示商品信息：\n\n```dart\nclass Item {\n  Item(this.price, this.count);\n  double price; //商品单价\n  int count; // 商品份数\n  //... 省略其它属性\n}\n```\n\n定义一个保存购物车内商品数据的`CartModel`类:\n\n```dart\nclass CartModel extends ChangeNotifier {\n  // 用于保存购物车中商品列表\n  final List<Item> _items = [];\n\n  // 禁止改变购物车里的商品信息\n  UnmodifiableListView<Item> get items => UnmodifiableListView(_items);\n\n  // 购物车中商品的总价\n  double get totalPrice =>\n      _items.fold(0, (value, item) => value + item.count * item.price);\n\n  // 将 [item] 添加到购物车。这是唯一一种能从外部改变购物车的方法。\n  void add(Item item) {\n    _items.add(item);\n    // 通知监听器（订阅者），重新构建InheritedProvider， 更新状态。\n    notifyListeners();\n  }\n}\n```\n\n `CartModel `即要跨组件共享的model类。最后我们构建示例页面：\n\n```dart\nclass ProviderRoute extends StatefulWidget {\n  @override\n  _ProviderRouteState createState() => _ProviderRouteState();\n}\n\nclass _ProviderRouteState extends State<ProviderRoute> {\n  @override\n  Widget build(BuildContext context) {\n    return Center(\n      child: ChangeNotifierProvider<CartModel>(\n        data: CartModel(),\n        child: Builder(builder: (context) {\n          return Column(\n            children: <Widget>[\n              Builder(builder: (context){\n                var cart=ChangeNotifierProvider.of<CartModel>(context);\n                return Text(\"总价: ${cart.totalPrice}\");\n              }),\n              Builder(builder: (context){\n                print(\"RaisedButton build\"); //在后面优化部分会用到\n                return RaisedButton(\n                  child: Text(\"添加商品\"),\n                  onPressed: () {\n                    //给购物车中添加商品，添加后总价会更新\n                    ChangeNotifierProvider.of<CartModel>(context).add(Item(20.0, 1));\n                  },\n                );\n              }),\n            ],\n          );\n        }),\n      ),\n    );\n  }\n}\n```\n\n运行示例后效果如图7-2所示：\n\n![provider](../imgs/7-2.png)\n\n每次点击”添加商品“按钮，总价就会增加20，我们期望的功能实现了！可能有些读者会疑惑，我们饶了一大圈实现这么简单的功能有意义么？其实，就这个例子来看，只是更新同一个路由页中的一个状态，我们使用`ChangeNotifierProvider`的优势并不明显，但是如果我们是做一个购物APP呢？由于购物车数据是通常是会在整个APP中共享的，比如会跨路由共享。如果我们将`ChangeNotifierProvider`放在整个应用的Widget树的根上，那么整个APP就可以共享购物车的数据了，这时`ChangeNotifierProvider`的优势将会非常明显。\n\n虽然上面的例子比较简单，但它却将Provider的原理和流程体现的很清楚，图7-3是Provider的原理图：\n\n![图7-3](../imgs/7-3.png)\n\nModel变化后会自动通知`ChangeNotifierProvider`（订阅者），`ChangeNotifierProvider`内部会重新构建`InheritedWidget`，而依赖该`InheritedWidget`的子孙Widget就会更新。\n\n我们可以发现使用Provider，将会带来如下收益：\n\n1. 我们的业务代码更关注数据了，只要更新Model，则UI会自动更新，而不用在状态改变后再去手动调用`setState()`来显式更新页面。\n2. 数据改变的消息传递被屏蔽了，我们无需手动去处理状态改变事件的发布和订阅了，这一切都被封装在Provider中了。这真的很棒，帮我们省掉了大量的工作！\n3. 在大型复杂应用中，尤其是需要全局共享的状态非常多时，使用Provider将会大大简化我们的代码逻辑，降低出错的概率，提高开发效率。 \n\n### 优化\n\n我们上面实现的`ChangeNotifierProvider`是有两个明显缺点：代码组织问题和性能问题，下面我们一一讨论。\n\n#### 代码组织问题\n\n我们先看一下构建显示总价Text的代码：\n\n```dart\nBuilder(builder: (context){\n  var cart=ChangeNotifierProvider.of<CartModel>(context);\n  return Text(\"总价: ${cart.totalPrice}\");\n})\n```\n\n这段代码有两点可以优化：\n\n1. 需要显式调用`ChangeNotifierProvider.of`，当APP内部依赖`CartModel`很多时，这样的代码将很冗余。\n2. 语义不明确；由于`ChangeNotifierProvider`是订阅者，那么依赖`CartModel`的Widget自然就是订阅者，其实也就是状态的消费者，如果我们用`Builder` 来构建，语义就不是很明确；如果我们能使用一个具有明确语义的Widget，比如就叫`Consumer`，这样最终的代码语义将会很明确，只要看到`Consumer`，我们就知道它是依赖某个跨组件或全局的状态。\n\n为了优化这两个问题，我们可以封装一个`Consumer` Widget，实现如下：\n\n```dart\n// 这是一个便捷类，会获得当前context和指定数据类型的Provider\nclass Consumer<T> extends StatelessWidget {\n  Consumer({\n    Key key,\n    @required this.builder,\n    this.child,\n  })  : assert(builder != null),\n        super(key: key);\n\n  final Widget child;\n\n  final Widget Function(BuildContext context, T value) builder;\n\n  @override\n  Widget build(BuildContext context) {\n    return builder(\n      context,\n      ChangeNotifierProvider.of<T>(context), //自动获取Model\n    );\n  }\n}\n```\n\n`Consumer`实现非常简单，它通过指定模板参数，然后再内部自动调用`ChangeNotifierProvider.of`获取相应的Model，并且`Consumer`这个名字本身也是具有确切语义（消费者）。现在上面的代码块可以优化为如下这样：\n\n```dart\nConsumer<CartModel>(\n  builder: (context, cart)=> Text(\"总价: ${cart.totalPrice}\");\n)\n```\n\n是不是很优雅！\n\n#### 性能问题\n\n上面的代码还有一个性能问题，就在构建”添加按钮“的代码处：\n\n```dart\nBuilder(builder: (context) {\n  print(\"RaisedButton build\"); // 构建时输出日志\n  return RaisedButton(\n    child: Text(\"添加商品\"),\n    onPressed: () {\n      ChangeNotifierProvider.of<CartModel>(context).add(Item(20.0, 1));\n    },\n  );\n}\n```\n\n我们点击”添加商品“按钮后，由于购物车商品总价会变化，所以显示总价的Text更新是符合预期的，但是”添加商品“按钮本身没有变化，是不应该被重新build的。但是我们运行示例，每次点击”添加商品“按钮，控制台都会输出\"RaisedButton build\"日志，也就是说”添加商品“按钮在每次点击时其自身都会重新build！这是为什么呢？如果你已经理解了`InheritedWidget`的更新机制，那么答案一眼就能看出：这是因为构建`RaisedButton`的`Builder`中调用了`ChangeNotifierProvider.of`，也就是说依赖了Widget树上面的`InheritedWidget`（即`InheritedProvider` ）Widget，所以当添加完商品后，`CartModel`发生变化，会通知`ChangeNotifierProvider`, 而`ChangeNotifierProvider`则会重新构建子树，所以`InheritedProvider`将会更新，此时依赖它的子孙Widget就会被重新构建。\n\n问题的原因搞清楚了，那么我们如何避免这不必要重构呢？既然按钮重新被build是因为按钮和`InheritedWidget`建立了依赖关系，那么我们只要打破或解除这种依赖关系就可以了。那么如何解除按钮和`InheritedWidget`的依赖关系呢？我们上一节介绍`InheritedWidget`时已经讲过了：调用`dependOnInheritedWidgetOfExactType()` 和 `getElementForInheritedWidgetOfExactType()`的区别就是前者会注册依赖关系，而后者不会。所以我们只需要将`ChangeNotifierProvider.of`的实现改为下面这样即可：\n\n```dart\n //添加一个listen参数，表示是否建立依赖关系\n  static T of<T>(BuildContext context, {bool listen = true}) {\n    final type = _typeOf<InheritedProvider<T>>();\n    final provider = listen\n        ? context.dependOnInheritedWidgetOfExactType<InheritedProvider<T>>()\n        : context.getElementForInheritedWidgetOfExactType<InheritedProvider<T>>()?.widget\n            as InheritedProvider<T>;\n    return provider.data;\n  }\n```\n\n然后我们将调用部分代码改为：\n\n```dart\nColumn(\n    children: <Widget>[\n      Consumer<CartModel>(\n        builder: (BuildContext context, cart) =>Text(\"总价: ${cart.totalPrice}\"),\n      ),\n      Builder(builder: (context) {\n        print(\"RaisedButton build\");\n        return RaisedButton(\n          child: Text(\"添加商品\"),\n          onPressed: () {\n            // listen 设为false，不建立依赖关系\n            ChangeNotifierProvider.of<CartModel>(context, listen: false)\n                .add(Item(20.0, 1));\n          },\n        );\n      })\n    ],\n  )\n```\n\n修改后再次运行上面的示例，我们会发现点击”添加商品“按钮后，控制台不会再输出\"RaisedButton build\"了，即按钮不会被重新构建了。而总价仍然会更新，这是因为`Consumer`中调用`ChangeNotifierProvider.of`时`listen`值为默认值true，所以还是会建立依赖关系。\n\n至此我们便实现了一个迷你的Provider，它具备Pub上Provider Package中的核心功能；但是我们的迷你版功能并不全面，如只实现了一个可监听的ChangeNotifierProvider，并没有实现只用于数据共享的Provider；另外，我们的实现有些边界也没有考虑的到，比如如何保证在Widget树重新build时Model始终是单例等。所以建议读者在实战中还是使用Provider Package，而本节实现这个迷你Provider的主要目的主要是为了帮助读者了解Provider Package底层的原理。\n\n### 其它状态管理包\n\n现在Flutter社区已经有很多专门用于状态管理的包了，在此我们列出几个相对评分比较高的：\n\n| 包名                                                         | 介绍                                          |\n| ------------------------------------------------------------ | --------------------------------------------- |\n| [Provider](https://pub.flutter-io.cn/packages/provider) & [Scoped Model](https://pub.flutter-io.cn/packages/scoped_model) | 这两个包都是基于`InheritedWidget`的，原理相似 |\n| [Redux](https://pub.flutter-io.cn/packages/flutter_redux)    | 是Web开发中React生态链中Redux包的Flutter实现  |\n| [MobX](https://pub.dev/packages/flutter_mobx)                | 是Web开发中React生态链中MobX包的Flutter实现   |\n| [BLoC](https://pub.dev/packages/flutter_bloc)                | 是BLoC模式的Flutter实现                       |\n\n在此笔者不对这些包做推荐，读者有兴趣都可以研究一下，了解它们各自的思想。\n\n### 总结\n\n本节通过介绍事件总线在跨组件共享中的一些缺点引出了通过`InheritedWidget`来实现状态的共享的思想，然后基于该思想实现了一个简单的Provider，在实现的过程中也更深入的探索了`InheritedWidget`与其依赖项的注册机制和更新机制。通过本节的学习，读者应该达到两个目标，首先是对`InheritedWidget`彻底吃透，其次是Provider的设计思想。\n\n`InheritedWidget`是Flutter中非常重要的一个Widget，像国际化、主题等都是通过它来实现，所以我们也不惜篇幅，通过好几节来介绍它的，在下一节中，我们将介绍另一个基于`InheritedWidget`的组件Theme(主题)。\n\n\n\n\n\n\n\n"
  },
  {
    "path": "src/chapter7/theme.md",
    "content": "\n\n# 7.4 颜色和主题\n\n## 7.4.1 颜色\n\n在介绍主题前我们先了解一些Flutter中的Color类。Color类中颜色以一个int值保存，我们知道显示器颜色是由红、绿、蓝三基色组成，每种颜色占8比特，存储结构如下：\n\n| Bit（位） | 颜色             |\n| --------- | ---------------- |\n| 0-7       | 蓝色             |\n| 8-15      | 绿色             |\n| 16-23     | 红色             |\n| 24-31     | Alpha (不透明度) |\n\n上面表格中的的字段在Color类中都有对应的属性，而Color中的众多方法也就是操作这些属性的，由于大多比较简单，读者可以查看类定义了解。在此我们主要讨论两点：色值转换和亮度。\n\n### **如何将颜色字符串转成Color对象**\n\n如Web开发中的色值通常是一个字符串如\"#dc380d\"，它是一个RGB值，我们可以通过下面这些方法将其转为Color类：\n\n```dart\nColor(0xffdc380d); //如果颜色固定可以直接使用整数值\n//颜色是一个字符串变量\nvar c = \"dc380d\";\nColor(int.parse(c,radix:16)|0xFF000000) //通过位运算符将Alpha设置为FF\nColor(int.parse(c,radix:16)).withAlpha(255)  //通过方法将Alpha设置为FF\n```\n\n### 颜色亮度\n\n假如，我们要实现一个背景颜色和Title可以自定义的导航栏，并且背景色为深色时我们应该让Title显示为浅色；背景色为浅色时，Title显示为深色。要实现这个功能，我们就需要来计算背景色的亮度，然后动态来确定Title的颜色。Color类中提供了一个`computeLuminance()`方法，它可以返回一个[0-1]的一个值，数字越大颜色就越浅，我们可以根据它来动态确定Title的颜色，下面是导航栏NavBar的简单实现：\n\n```dart\nclass NavBar extends StatelessWidget {\n  final String title;\n  final Color color; //背景颜色\n\n  NavBar({\n    Key key,\n    this.color,\n    this.title,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return Container(\n      constraints: BoxConstraints(\n        minHeight: 52,\n        minWidth: double.infinity,\n      ),\n      decoration: BoxDecoration(\n        color: color,\n        boxShadow: [\n          //阴影\n          BoxShadow(\n            color: Colors.black26,\n            offset: Offset(0, 3),\n            blurRadius: 3,\n          ),\n        ],\n      ),\n      child: Text(\n        title,\n        style: TextStyle(\n          fontWeight: FontWeight.bold,\n          //根据背景色亮度来确定Title颜色\n          color: color.computeLuminance() < 0.5 ? Colors.white : Colors.black,\n        ),\n      ),\n      alignment: Alignment.center,\n    );\n  }\n}\n```\n\n测试代码如下：\n\n```dart\nColumn(\n  children: <Widget>[\n    //背景为蓝色，则title自动为白色\n    NavBar(color: Colors.blue, title: \"标题\"), \n    //背景为白色，则title自动为黑色\n    NavBar(color: Colors.white, title: \"标题\"),\n  ]\n)\n```\n\n运行效果如图7-4所示：\n\n![NavBar](../imgs/7-4.png)\n\n### MaterialColor\n\n`MaterialColor`是实现Material Design中的颜色的类，它包含一种颜色的10个级别的渐变色。`MaterialColor`通过\"[]\"运算符的索引值来代表颜色的深度，有效的索引有：50，100，200，…，900，数字越大，颜色越深。`MaterialColor`的默认值为索引等于500的颜色。举个例子，`Colors.blue`是预定义的一个`MaterialColor`类对象，定义如下：\n\n```dart\nstatic const MaterialColor blue = MaterialColor(\n  _bluePrimaryValue,\n  <int, Color>{\n     50: Color(0xFFE3F2FD),\n    100: Color(0xFFBBDEFB),\n    200: Color(0xFF90CAF9),\n    300: Color(0xFF64B5F6),\n    400: Color(0xFF42A5F5),\n    500: Color(_bluePrimaryValue),\n    600: Color(0xFF1E88E5),\n    700: Color(0xFF1976D2),\n    800: Color(0xFF1565C0),\n    900: Color(0xFF0D47A1),\n  },\n);\nstatic const int _bluePrimaryValue = 0xFF2196F3;\n```\n\n`Colors.blue[50]`到`Colors.blue[900]`的色值从浅蓝到深蓝渐变，效果如图7-5所示：\n\n![NavBar](../imgs/7-5.jpeg)\n\n\n\n## 7.4.2 Theme\n\n`Theme`组件可以为Material APP定义主题数据（ThemeData）。Material组件库里很多组件都使用了主题数据，如导航栏颜色、标题字体、Icon样式等。`Theme`内会使用`InheritedWidget`来为其子树共享样式数据。\n\n### ThemeData\n\n`ThemeData`用于保存是Material 组件库的主题数据，Material组件需要遵守相应的设计规范，而这些规范可自定义部分都定义在ThemeData中了，所以我们可以通过ThemeData来自定义应用主题。在子组件中，我们可以通过`Theme.of`方法来获取当前的`ThemeData`。\n\n> 注意：Material Design 设计规范中有些是不能自定义的，如导航栏高度，ThemeData只包含了可自定义部分。\n\n我们看看`ThemeData`部分数据定义：\n\n```dart\nThemeData({\n  Brightness brightness, //深色还是浅色\n  MaterialColor primarySwatch, //主题颜色样本，见下面介绍\n  Color primaryColor, //主色，决定导航栏颜色\n  Color accentColor, //次级色，决定大多数Widget的颜色，如进度条、开关等。\n  Color cardColor, //卡片颜色\n  Color dividerColor, //分割线颜色\n  ButtonThemeData buttonTheme, //按钮主题\n  Color cursorColor, //输入框光标颜色\n  Color dialogBackgroundColor,//对话框背景颜色\n  String fontFamily, //文字字体\n  TextTheme textTheme,// 字体主题，包括标题、body等文字样式\n  IconThemeData iconTheme, // Icon的默认样式\n  TargetPlatform platform, //指定平台，应用特定平台控件风格\n  ...\n})\n```\n\n上面只是`ThemeData`的一小部分属性，完整的数据定义读者可以查看SDK。上面属性中需要说明的是`primarySwatch`，它是主题颜色的一个\"样本色\"，通过这个样本色可以在一些条件下生成一些其它的属性，例如，如果没有指定`primaryColor`，并且当前主题不是深色主题，那么`primaryColor`就会默认为`primarySwatch`指定的颜色，还有一些相似的属性如`accentColor` 、`indicatorColor`等也会受`primarySwatch`影响。\n\n### 示例\n\n我们实现一个路由换肤功能：\n\n```dart\n\nclass ThemeTestRoute extends StatefulWidget {\n  @override\n  _ThemeTestRouteState createState() => new _ThemeTestRouteState();\n}\n\nclass _ThemeTestRouteState extends State<ThemeTestRoute> {\n  Color _themeColor = Colors.teal; //当前路由主题色\n\n  @override\n  Widget build(BuildContext context) {\n    ThemeData themeData = Theme.of(context);\n    return Theme(\n      data: ThemeData(\n          primarySwatch: _themeColor, //用于导航栏、FloatingActionButton的背景色等\n          iconTheme: IconThemeData(color: _themeColor) //用于Icon颜色\n      ),\n      child: Scaffold(\n        appBar: AppBar(title: Text(\"主题测试\")),\n        body: Column(\n          mainAxisAlignment: MainAxisAlignment.center,\n          children: <Widget>[\n            //第一行Icon使用主题中的iconTheme\n            Row(\n                mainAxisAlignment: MainAxisAlignment.center,\n                children: <Widget>[\n                  Icon(Icons.favorite),\n                  Icon(Icons.airport_shuttle),\n                  Text(\"  颜色跟随主题\")\n                ]\n            ),\n            //为第二行Icon自定义颜色（固定为黑色)\n            Theme(\n              data: themeData.copyWith(\n                iconTheme: themeData.iconTheme.copyWith(\n                    color: Colors.black\n                ),\n              ),\n              child: Row(\n                  mainAxisAlignment: MainAxisAlignment.center,\n                  children: <Widget>[\n                    Icon(Icons.favorite),\n                    Icon(Icons.airport_shuttle),\n                    Text(\"  颜色固定黑色\")\n                  ]\n              ),\n            ),\n          ],\n        ),\n        floatingActionButton: FloatingActionButton(\n            onPressed: () =>  //切换主题\n                setState(() =>\n                _themeColor =\n                _themeColor == Colors.teal ? Colors.blue : Colors.teal\n                ),\n            child: Icon(Icons.palette)\n        ),\n      ),\n    );\n  }\n}\n```\n\n运行后点击右下角悬浮按钮则可以切换主题，如图7-6、7-7所示：\n\n![图7-6](../imgs/7-6.png)![图7-7](../imgs/7-7.png)\n\n\n\n需要注意的有三点：\n\n- 可以通过局部主题覆盖全局主题，正如代码中通过Theme为第二行图标指定固定颜色（黑色）一样，这是一种常用的技巧，Flutter中会经常使用这种方法来自定义子树主题。那么为什么局部主题可以覆盖全局主题？这主要是因为widget中使用主题样式时是通过`Theme.of(BuildContext context)`来获取的，我们看看其简化后的代码：\n\n- ```dart \n  static ThemeData of(BuildContext context, { bool shadowThemeOnly = false }) {\n     // 简化代码，并非源码  \n     return context.dependOnInheritedWidgetOfExactType<_InheritedTheme>().theme.data\n  }\n  ```\n\n  `context.dependOnInheritedWidgetOfExactType` 会在widget树中从当前位置向上查找第一个类型为`_InheritedTheme`的widget。所以当局部指定`Theme`后，其子树中通过`Theme.of()`向上查找到的第一个`_InheritedTheme`便是我们指定的`Theme`。\n\n- 本示例是对单个路由换肤，如果想要对整个应用换肤，则可以去修改`MaterialApp`的`theme`属性。\n"
  },
  {
    "path": "src/chapter7/willpopscope.md",
    "content": "# 7.1 导航返回拦截（WillPopScope）\n\n为了避免用户误触返回按钮而导致APP退出，在很多APP中都拦截了用户点击返回键的按钮，然后进行一些防误触判断，比如当用户在某一个时间段内点击两次时，才会认为用户是要退出（而非误触）。Flutter中可以通过`WillPopScope`来实现返回按钮拦截，我们看看`WillPopScope`的默认构造函数：\n\n```dart\nconst WillPopScope({\n  ...\n  @required WillPopCallback onWillPop,\n  @required Widget child\n})\n```\n\n`onWillPop`是一个回调函数，当用户点击返回按钮时被调用（包括导航返回按钮及Android物理返回按钮）。该回调需要返回一个`Future`对象，如果返回的`Future`最终值为`false`时，则当前路由不出栈(不会返回)；最终值为`true`时，当前路由出栈退出。我们需要提供这个回调来决定是否退出。\n\n### 示例\n\n为了防止用户误触返回键退出，我们拦截返回事件。当用户在1秒内点击两次返回按钮时，则退出；如果间隔超过1秒则不退出，并重新记时。代码如下：\n\n```dart\nimport 'package:flutter/material.dart';\n\nclass WillPopScopeTestRoute extends StatefulWidget {\n  @override\n  WillPopScopeTestRouteState createState() {\n    return new WillPopScopeTestRouteState();\n  }\n}\n\nclass WillPopScopeTestRouteState extends State<WillPopScopeTestRoute> {\n  DateTime _lastPressedAt; //上次点击时间\n\n  @override\n  Widget build(BuildContext context) {\n    return new WillPopScope(\n        onWillPop: () async {\n          if (_lastPressedAt == null ||\n              DateTime.now().difference(_lastPressedAt) > Duration(seconds: 1)) {\n            //两次点击间隔超过1秒则重新计时\n            _lastPressedAt = DateTime.now();\n            return false;\n          }\n          return true;\n        },\n        child: Container(\n          alignment: Alignment.center,\n          child: Text(\"1秒内连续按两次返回键退出\"),\n        )\n    );\n  }\n}\n```\n\n读者可以运行示例看看效果。\n"
  },
  {
    "path": "src/chapter8/eventbus.md",
    "content": "\n\n# 8.3 事件总线\n\n在APP中，我们经常会需要一个广播机制，用以跨页面事件通知，比如一个需要登录的APP中，页面会关注用户登录或注销事件，来进行一些状态更新。这时候，一个事件总线便会非常有用，事件总线通常实现了订阅者模式，订阅者模式包含发布者和订阅者两种角色，可以通过事件总线来触发事件和监听事件，本节我们实现一个简单的全局事件总线，我们使用单例模式，代码如下：\n\n```dart\n//订阅者回调签名\ntypedef void EventCallback(arg);\n\nclass EventBus {\n  //私有构造函数\n  EventBus._internal();\n\n  //保存单例\n  static EventBus _singleton = new EventBus._internal();\n\n  //工厂构造函数\n  factory EventBus()=> _singleton;\n\n  //保存事件订阅者队列，key:事件名(id)，value: 对应事件的订阅者队列\n  var _emap = new Map<Object, List<EventCallback>>();\n\n  //添加订阅者\n  void on(eventName, EventCallback f) {\n    if (eventName == null || f == null) return;\n    _emap[eventName] ??= new List<EventCallback>();\n    _emap[eventName].add(f);\n  }\n\n  //移除订阅者\n  void off(eventName, [EventCallback f]) {\n    var list = _emap[eventName];\n    if (eventName == null || list == null) return;\n    if (f == null) {\n      _emap[eventName] = null;\n    } else {\n      list.remove(f);\n    }\n  }\n\n  //触发事件，事件触发后该事件所有订阅者会被调用\n  void emit(eventName, [arg]) {\n    var list = _emap[eventName];\n    if (list == null) return;\n    int len = list.length - 1;\n    //反向遍历，防止订阅者在回调中移除自身带来的下标错位 \n    for (var i = len; i > -1; --i) {\n      list[i](arg);\n    }\n  }\n}\n\n//定义一个top-level（全局）变量，页面引入该文件后可以直接使用bus\nvar bus = new EventBus();\n```\n\n使用示例：\n\n```dart\n//页面A中\n...\n //监听登录事件\nbus.on(\"login\", (arg) {\n  // do something\n});\n\n//登录页B中\n...\n//登录成功后触发登录事件，页面A中订阅者会被调用\nbus.emit(\"login\", userInfo);\n\n```\n\n\n> 注意：Dart中实现单例模式的标准做法就是使用static变量+工厂构造函数的方式，这样就可以保证`new EventBus()`始终返回都是同一个实例，读者应该理解并掌握这种方法。\n\n事件总线通常用于组件之间状态共享，但关于组件之间状态共享也有一些专门的包如redux、以及前面介绍过的Provider。对于一些简单的应用，事件总线是足以满足业务需求的，如果你决定使用状态管理包的话，一定要想清楚您的APP是否真的有必要使用它，防止“化简为繁”、过度设计。\n"
  },
  {
    "path": "src/chapter8/gesture.md",
    "content": "# 8.2 手势识别 \n\n本节先介绍一些Flutter中用于处理手势的`GestureDetector`和`GestureRecognizer`，然后再仔细讨论一下手势竞争与冲突问题。\n\n## 8.2.1 GestureDetector\n\n`GestureDetector`是一个用于手势识别的功能性组件，我们通过它可以来识别各种手势。`GestureDetector`实际上是指针事件的语义化封装，接下来我们详细介绍一下各种手势识别。\n\n### 点击、双击、长按\n\n我们通过`GestureDetector`对`Container`进行手势识别，触发相应事件后，在`Container`上显示事件名，为了增大点击区域，将`Container`设置为200×100，代码如下：\n\n```dart\n\nclass GestureDetectorTestRoute extends StatefulWidget {\n  @override\n  _GestureDetectorTestRouteState createState() =>\n      new _GestureDetectorTestRouteState();\n}\n\nclass _GestureDetectorTestRouteState extends State<GestureDetectorTestRoute> {\n  String _operation = \"No Gesture detected!\"; //保存事件名\n  @override\n  Widget build(BuildContext context) {\n    return Center(\n      child: GestureDetector(\n        child: Container(\n          alignment: Alignment.center,\n          color: Colors.blue,\n          width: 200.0, \n          height: 100.0,\n          child: Text(_operation,\n            style: TextStyle(color: Colors.white),\n          ),\n        ),\n        onTap: () => updateText(\"Tap\"),//点击\n        onDoubleTap: () => updateText(\"DoubleTap\"), //双击\n        onLongPress: () => updateText(\"LongPress\"), //长按\n      ),\n    );\n  }\n\n  void updateText(String text) {\n    //更新显示的事件名\n    setState(() {\n      _operation = text;\n    });\n  }\n}\n```\n\n运行效果如图8-2所示：\n\n![图8-2](../imgs/8-2.png)\n\n\n\n> **注意**： 当同时监听`onTap`和`onDoubleTap`事件时，当用户触发tap事件时，会有200毫秒左右的延时，这是因为当用户点击完之后很可能会再次点击以触发双击事件，所以`GestureDetector`会等一段时间来确定是否为双击事件。如果用户只监听了`onTap`（没有监听`onDoubleTap`）事件时，则没有延时。\n\n\n\n### 拖动、滑动\n\n一次完整的手势过程是指用户手指按下到抬起的整个过程，期间，用户按下手指后可能会移动，也可能不会移动。`GestureDetector`对于拖动和滑动事件是没有区分的，他们本质上是一样的。`GestureDetector`会将要监听的组件的原点（左上角）作为本次手势的原点，当用户在监听的组件上按下手指时，手势识别就会开始。下面我们看一个拖动圆形字母A的示例：\n\n```dart\nclass _Drag extends StatefulWidget {\n  @override\n  _DragState createState() => new _DragState();\n}\n\nclass _DragState extends State<_Drag> with SingleTickerProviderStateMixin {\n  double _top = 0.0; //距顶部的偏移\n  double _left = 0.0;//距左边的偏移\n\n  @override\n  Widget build(BuildContext context) {\n    return Stack(\n      children: <Widget>[\n        Positioned(\n          top: _top,\n          left: _left,\n          child: GestureDetector(\n            child: CircleAvatar(child: Text(\"A\")),\n            //手指按下时会触发此回调\n            onPanDown: (DragDownDetails e) {\n              //打印手指按下的位置(相对于屏幕)\n              print(\"用户手指按下：${e.globalPosition}\");\n            },\n            //手指滑动时会触发此回调\n            onPanUpdate: (DragUpdateDetails e) {\n              //用户手指滑动时，更新偏移，重新构建\n              setState(() {\n                _left += e.delta.dx;\n                _top += e.delta.dy;\n              });\n            },\n            onPanEnd: (DragEndDetails e){\n              //打印滑动结束时在x、y轴上的速度\n              print(e.velocity);\n            },\n          ),\n        )\n      ],\n    );\n  }\n}\n```\n\n运行后，就可以在任意方向拖动了，运行效果如图8-3所示：\n\n![图8-3](../imgs/8-3.png)\n\n\n\n日志：\n\n```\nI/flutter ( 8513): 用户手指按下：Offset(26.3, 101.8)\nI/flutter ( 8513): Velocity(235.5, 125.8)\n```\n\n\n\n代码解释：\n\n- `DragDownDetails.globalPosition`：当用户按下时，此属性为用户按下的位置相对于**屏幕**（而非父组件）原点(左上角)的偏移。\n- `DragUpdateDetails.delta`：当用户在屏幕上滑动时，会触发多次Update事件，`delta`指一次Update事件的滑动的偏移量。\n- `DragEndDetails.velocity`：该属性代表用户抬起手指时的滑动速度(包含x、y两个轴的），示例中并没有处理手指抬起时的速度，常见的效果是根据用户抬起手指时的速度做一个减速动画。\n\n### 单一方向拖动\n\n在本示例中，是可以朝任意方向拖动的，但是在很多场景，我们只需要沿一个方向来拖动，如一个垂直方向的列表，`GestureDetector`可以只识别特定方向的手势事件，我们将上面的例子改为只能沿垂直方向拖动：\n\n```dart\nclass _DragVertical extends StatefulWidget {\n  @override\n  _DragVerticalState createState() => new _DragVerticalState();\n}\n\nclass _DragVerticalState extends State<_DragVertical> {\n  double _top = 0.0;\n\n  @override\n  Widget build(BuildContext context) {\n    return Stack(\n      children: <Widget>[\n        Positioned(\n          top: _top,\n          child: GestureDetector(\n            child: CircleAvatar(child: Text(\"A\")),\n            //垂直方向拖动事件\n            onVerticalDragUpdate: (DragUpdateDetails details) {\n              setState(() {\n                _top += details.delta.dy;\n              });\n            }\n          ),\n        )\n      ],\n    );\n  }\n}\n```\n\n这样就只能在垂直方向拖动了，如果只想在水平方向滑动同理。\n\n### 缩放\n\n`GestureDetector`可以监听缩放事件，下面示例演示了一个简单的图片缩放效果：\n\n```dart\nclass _ScaleTestRouteState extends State<_ScaleTestRoute> {\n  double _width = 200.0; //通过修改图片宽度来达到缩放效果\n\n  @override\n  Widget build(BuildContext context) {\n   return Center(\n     child: GestureDetector(\n        //指定宽度，高度自适应\n        child: Image.asset(\"./images/sea.png\", width: _width),\n        onScaleUpdate: (ScaleUpdateDetails details) {\n          setState(() {\n            //缩放倍数在0.8到10倍之间\n            _width=200*details.scale.clamp(.8, 10.0);\n          });\n        },\n      ),\n   );\n  }\n}\n```\n\n运行效果如图8-4所示：\n\n![图8-4](../imgs/8-4.png)\n\n现在在图片上双指张开、收缩就可以放大、缩小图片。本示例比较简单，实际中我们通常还需要一些其它功能，如双击放大或缩小一定倍数、双指张开离开屏幕时执行一个减速放大动画等，读者可以在学习完后面“动画”一章中的内容后自己来尝试实现一下。\n\n## 8.2.2 GestureRecognizer\n\n`GestureDetector`内部是使用一个或多个`GestureRecognizer`来识别各种手势的，而`GestureRecognizer`的作用就是通过`Listener`来将原始指针事件转换为语义手势，`GestureDetector`直接可以接收一个子widget。`GestureRecognizer`是一个抽象类，一种手势的识别器对应一个`GestureRecognizer`的子类，Flutter实现了丰富的手势识别器，我们可以直接使用。\n\n#### 示例\n\n假设我们要给一段富文本（`RichText`）的不同部分分别添加点击事件处理器，但是`TextSpan`并不是一个widget，这时我们不能用`GestureDetector`，但`TextSpan`有一个`recognizer`属性，它可以接收一个`GestureRecognizer`。\n\n假设我们需要在点击时给文本变色:\n\n```dart\nimport 'package:flutter/gestures.dart';\n\nclass _GestureRecognizerTestRouteState\n    extends State<_GestureRecognizerTestRoute> {\n  TapGestureRecognizer _tapGestureRecognizer = new TapGestureRecognizer();\n  bool _toggle = false; //变色开关\n\n  @override\n  void dispose() {\n     //用到GestureRecognizer的话一定要调用其dispose方法释放资源\n    _tapGestureRecognizer.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Center(\n      child: Text.rich(\n          TextSpan(\n              children: [\n                TextSpan(text: \"你好世界\"),\n                TextSpan(\n                  text: \"点我变色\",\n                  style: TextStyle(\n                      fontSize: 30.0,\n                      color: _toggle ? Colors.blue : Colors.red\n                  ),\n                  recognizer: _tapGestureRecognizer\n                    ..onTap = () {\n                      setState(() {\n                        _toggle = !_toggle;\n                      });\n                    },\n                ),\n                TextSpan(text: \"你好世界\"),\n              ]\n          )\n      ),\n    );\n  }\n}\n```\n\n运行效果：\n\n![图8-5](../imgs/8-5.png)\n\n\n\n> 注意：使用`GestureRecognizer`后一定要调用其`dispose()`方法来释放资源（主要是取消内部的计时器）。\n>\n\n\n\n## 8.2.3 手势竞争与冲突\n\n### 竞争\n\n如果在上例中我们同时监听水平和垂直方向的拖动事件，那么我们斜着拖动时哪个方向会生效？实际上取决于第一次移动时两个轴上的位移分量，哪个轴的大，哪个轴在本次滑动事件竞争中就胜出。实际上Flutter中的手势识别引入了一个Arena的概念，Arena直译为“竞技场”的意思，每一个手势识别器（`GestureRecognizer`）都是一个“竞争者”（`GestureArenaMember`），当发生滑动事件时，他们都要在“竞技场”去竞争本次事件的处理权，而最终只有一个“竞争者”会胜出(win)。例如，假设有一个`ListView`，它的第一个子组件也是`ListView`，如果现在滑动这个子`ListView`，父`ListView`会动吗？答案是否定的，这时只有子`ListView`会动，因为这时子`ListView`会胜出而获得滑动事件的处理权。\n\n### **示例**\n\n我们以拖动手势为例，同时识别水平和垂直方向的拖动手势，当用户按下手指时就会触发竞争（水平方向和垂直方向），一旦某个方向“获胜”，则直到当次拖动手势结束都会沿着该方向移动。代码如下：\n\n```dart\nimport 'package:flutter/material.dart';\n\nclass BothDirectionTestRoute extends StatefulWidget {\n  @override\n  BothDirectionTestRouteState createState() =>\n      new BothDirectionTestRouteState();\n}\n\nclass BothDirectionTestRouteState extends State<BothDirectionTestRoute> {\n  double _top = 0.0;\n  double _left = 0.0;\n\n  @override\n  Widget build(BuildContext context) {\n    return Stack(\n      children: <Widget>[\n        Positioned(\n          top: _top,\n          left: _left,\n          child: GestureDetector(\n            child: CircleAvatar(child: Text(\"A\")),\n            //垂直方向拖动事件\n            onVerticalDragUpdate: (DragUpdateDetails details) {\n              setState(() {\n                _top += details.delta.dy;\n              });\n            },\n            onHorizontalDragUpdate: (DragUpdateDetails details) {\n              setState(() {\n                _left += details.delta.dx;\n              });\n            },\n          ),\n        )\n      ],\n    );\n  }\n}\n```\n\n此示例运行后，每次拖动只会沿一个方向移动（水平或垂直），而竞争发生在手指按下后首次移动（move）时，此例中具体的“获胜”条件是：首次移动时的位移在水平和垂直方向上的分量大的一个获胜。\n\n### 手势冲突\n\n由于手势竞争最终只有一个胜出者，所以，当有多个手势识别器时，可能会产生冲突。假设有一个widget，它可以左右拖动，现在我们也想检测在它上面手指按下和抬起的事件，代码如下：\n\n```dart\nclass GestureConflictTestRouteState extends State<GestureConflictTestRoute> {\n  double _left = 0.0;\n  @override\n  Widget build(BuildContext context) {\n    return Stack(\n      children: <Widget>[\n        Positioned(\n          left: _left,\n          child: GestureDetector(\n              child: CircleAvatar(child: Text(\"A\")), //要拖动和点击的widget\n              onHorizontalDragUpdate: (DragUpdateDetails details) {\n                setState(() {\n                  _left += details.delta.dx;\n                });\n              },\n              onHorizontalDragEnd: (details){\n                print(\"onHorizontalDragEnd\");\n              },\n              onTapDown: (details){\n                print(\"down\");\n              },\n              onTapUp: (details){\n                print(\"up\");\n              },\n          ),\n        )\n      ],\n    );\n  }\n}\n```\n\n现在我们按住圆形“A”拖动然后抬起手指，控制台日志如下:\n\n```\nI/flutter (17539): down\nI/flutter (17539): onHorizontalDragEnd\n```\n\n我们发现没有打印\"up\"，这是因为在拖动时，刚开始按下手指时在没有移动时，拖动手势还没有完整的语义，此时TapDown手势胜出(win)，此时打印\"down\"，而拖动时，拖动手势会胜出，当手指抬起时，`onHorizontalDragEnd` 和 `onTapUp`发生了冲突，但是因为是在拖动的语义中，所以`onHorizontalDragEnd`胜出，所以就会打印 “onHorizontalDragEnd”。如果我们的代码逻辑中，对于手指按下和抬起是强依赖的，比如在一个轮播图组件中，我们希望手指按下时，暂停轮播，而抬起时恢复轮播，但是由于轮播图组件中本身可能已经处理了拖动手势（支持手动滑动切换），甚至可能也支持了缩放手势，这时我们如果在外部再用`onTapDown`、`onTapUp`来监听的话是不行的。这时我们应该怎么做？其实很简单，通过Listener监听原始指针事件就行：\n\n```dart\nPositioned(\n  top:80.0,\n  left: _leftB,\n  child: Listener(\n    onPointerDown: (details) {\n      print(\"down\");\n    },\n    onPointerUp: (details) {\n      //会触发\n      print(\"up\");\n    },\n    child: GestureDetector(\n      child: CircleAvatar(child: Text(\"B\")),\n      onHorizontalDragUpdate: (DragUpdateDetails details) {\n        setState(() {\n          _leftB += details.delta.dx;\n        });\n      },\n      onHorizontalDragEnd: (details) {\n        print(\"onHorizontalDragEnd\");\n      },\n    ),\n  ),\n)\n```\n\n手势冲突只是手势级别的，而手势是对原始指针的语义化的识别，所以在遇到复杂的冲突场景时，都可以通过`Listener`直接识别原始指针事件来解决冲突。\n\n"
  },
  {
    "path": "src/chapter8/index.md",
    "content": "## 事件处理与通知\n\nFlutter中的手势系统有两个独立的层。第一层为原始指针(pointer)事件，它描述了屏幕上指针（例如，触摸、鼠标和触控笔）的位置和移动。 第二层为手势，描述由一个或多个指针移动组成的语义动作，如拖动、缩放、双击等。本章将先分别介绍如何处理这两种事件，最后再介绍一下Flutter中重要的Notification机制。\n\n## 本章目录\n\n* [原始指针事件处理](listener.md)\n* [手势识别](gesture.md)\n* [全局事件总线](eventbus.md) \n* [通知Notification](notification.md) \n"
  },
  {
    "path": "src/chapter8/listener.md",
    "content": "# 8.1 原始指针事件处理\n\n本节先来介绍一下原始指针事件(Pointer Event，在移动设备上通常为触摸事件)，下一节再介绍手势处理。\n\n在移动端，各个平台或UI系统的原始指针事件模型基本都是一致，即：一次完整的事件分为三个阶段：手指按下、手指移动、和手指抬起，而更高级别的手势（如点击、双击、拖动等）都是基于这些原始事件的。\n\n当指针按下时，Flutter会对应用程序执行**命中测试(Hit Test)**，以确定指针与屏幕接触的位置存在哪些组件（widget）， 指针按下事件（以及该指针的后续事件）然后被分发到由命中测试发现的最内部的组件，然后从那里开始，事件会在组件树中向上冒泡，这些事件会从最内部的组件被分发到组件树根的路径上的所有组件，这和Web开发中浏览器的**事件冒泡**机制相似， 但是Flutter中没有机制取消或停止“冒泡”过程，而浏览器的冒泡是可以停止的。注意，只有通过命中测试的组件才能触发事件。\n\nFlutter中可以使用`Listener`来监听原始触摸事件，按照本书对组件的分类，则`Listener`也是一个功能性组件。下面是`Listener`的构造函数定义：\n\n```dart\nListener({\n  Key key,\n  this.onPointerDown, //手指按下回调\n  this.onPointerMove, //手指移动回调\n  this.onPointerUp,//手指抬起回调\n  this.onPointerCancel,//触摸事件取消回调\n  this.behavior = HitTestBehavior.deferToChild, //在命中测试期间如何表现\n  Widget child\n})\n```\n\n我们先看一个示例，后面再单独讨论一下`behavior`属性。\n\n```dart\n...\n//定义一个状态，保存当前指针位置\nPointerEvent _event;\n...\nListener(\n  child: Container(\n    alignment: Alignment.center,\n    color: Colors.blue,\n    width: 300.0,\n    height: 150.0,\n    child: Text(_event?.toString()??\"\",style: TextStyle(color: Colors.white)),\n  ),\n  onPointerDown: (PointerDownEvent event) => setState(()=>_event=event),\n  onPointerMove: (PointerMoveEvent event) => setState(()=>_event=event),\n  onPointerUp: (PointerUpEvent event) => setState(()=>_event=event),\n),\n```\n\n运行后效果如图8-1所示：\n\n![图8-1](../imgs/8-1.png)\n\n手指在蓝色矩形区域内移动即可看到当前指针偏移，当触发指针事件时，参数`PointerDownEvent`、`PointerMoveEvent`、`PointerUpEvent`都是`PointerEvent`的一个子类，`PointerEvent`类中包括当前指针的一些信息，如：\n\n- `position`：它是鼠标相对于当对于全局坐标的偏移。\n- `delta`：两次指针移动事件（`PointerMoveEvent`）的距离。\n- `pressure`：按压力度，如果手机屏幕支持压力传感器(如iPhone的3D Touch)，此属性会更有意义，如果手机不支持，则始终为1。\n- `orientation`：指针移动方向，是一个角度值。\n\n上面只是`PointerEvent`一些常用属性，除了这些它还有很多属性，读者可以查看API文档。\n\n现在，我们重点来介绍一下`behavior`属性，它决定子组件如何响应命中测试，它的值类型为`HitTestBehavior`，这是一个枚举类，有三个枚举值：\n\n- `deferToChild`：子组件会一个接一个的进行命中测试，如果子组件中有测试通过的，则当前组件通过，这就意味着，如果指针事件作用于子组件上时，其父级组件也肯定可以收到该事件。\n\n- `opaque`：在命中测试时，将当前组件当成不透明处理(即使本身是透明的)，最终的效果相当于当前Widget的整个区域都是点击区域。举个例子：\n\n  ```dart\n  Listener(\n      child: ConstrainedBox(\n          constraints: BoxConstraints.tight(Size(300.0, 150.0)),\n          child: Center(child: Text(\"Box A\")),\n      ),\n      //behavior: HitTestBehavior.opaque,\n      onPointerDown: (event) => print(\"down A\")\n  ),\n  ```\n\n  上例中，只有点击文本内容区域才会触发点击事件，因为 `deferToChild` 会去子组件判断是否命中测试，而该例中子组件就是 `Text(\"Box A\")` 。\n  如果我们想让整个300×150的矩形区域都能点击我们可以将`behavior`设为`HitTestBehavior.opaque`。注意，该属性并不能用于在组件树中拦截（忽略）事件，它只是决定命中测试时的组件大小。\n\n- `translucent`：当点击组件透明区域时，可以对自身边界内及底部可视区域都进行命中测试，这意味着点击顶部组件透明区域时，顶部组件和底部组件都可以接收到事件，例如：\n\n  ```dart\n  Stack(\n    children: <Widget>[\n      Listener(\n        child: ConstrainedBox(\n          constraints: BoxConstraints.tight(Size(300.0, 200.0)),\n          child: DecoratedBox(\n              decoration: BoxDecoration(color: Colors.blue)),\n        ),\n        onPointerDown: (event) => print(\"down0\"),\n      ),\n      Listener(\n        child: ConstrainedBox(\n          constraints: BoxConstraints.tight(Size(200.0, 100.0)),\n          child: Center(child: Text(\"左上角200*100范围内非文本区域点击\")),\n        ),\n        onPointerDown: (event) => print(\"down1\"),\n        //behavior: HitTestBehavior.translucent, //放开此行注释后可以\"点透\"\n      )\n    ],\n  )\n  ```\n\n  上例中，当注释掉最后一行代码后，在左上角200*100范围内非文本区域点击时（顶部组件透明区域），控制台只会打印“down0”，也就是说顶部组件没有接收到事件，而只有底部接收到了。当放开注释后，再点击时顶部和底部都会接收到事件，此时会打印：\n\n  ```\n  I/flutter ( 3039): down1\n  I/flutter ( 3039): down0\n  ```\n  如果`behavior`值改为`HitTestBehavior.opaque`，则只会打印\"down1\"。\n\n### 忽略PointerEvent\n\n假如我们不想让某个子树响应`PointerEvent`的话，我们可以使用`IgnorePointer`和`AbsorbPointer`，这两个组件都能阻止子树接收指针事件，不同之处在于`AbsorbPointer`本身会参与命中测试，而`IgnorePointer`本身不会参与，这就意味着`AbsorbPointer`本身是可以接收指针事件的(但其子树不行)，而`IgnorePointer`不可以。一个简单的例子如下：\n\n```dart\nListener(\n  child: AbsorbPointer(\n    child: Listener(\n      child: Container(\n        color: Colors.red,\n        width: 200.0,\n        height: 100.0,\n      ),\n      onPointerDown: (event)=>print(\"in\"),\n    ),\n  ),\n  onPointerDown: (event)=>print(\"up\"),\n)\n```\n\n点击`Container`时，由于它在`AbsorbPointer`的子树上，所以不会响应指针事件，所以日志不会输出\"in\"，但`AbsorbPointer`本身是可以接收指针事件的，所以会输出\"up\"。如果将`AbsorbPointer`换成`IgnorePointer`，那么两个都不会输出。\n\n\n"
  },
  {
    "path": "src/chapter8/notification.md",
    "content": "# 8.4 Notification\n\n通知（Notification）是Flutter中一个重要的机制，在widget树中，每一个节点都可以分发通知，通知会沿着当前节点向上传递，所有父节点都可以通过`NotificationListener`来监听通知。Flutter中将这种由子向父的传递通知的机制称为**通知冒泡**（Notification Bubbling）。通知冒泡和用户触摸事件冒泡是相似的，但有一点不同：通知冒泡可以中止，但用户触摸事件不行。\n\n> 通知冒泡和Web开发中浏览器事件冒泡原理是相似的，都是事件从出发源逐层向上传递，我们可以在上层节点任意位置来监听通知/事件，也可以终止冒泡过程，终止冒泡后，通知将不会再向上传递。\n\nFlutter中很多地方使用了通知，如可滚动组件（Scrollable Widget）滑动时就会分发**滚动通知**（ScrollNotification），而Scrollbar正是通过监听ScrollNotification来确定滚动条位置的。\n\n下面是一个监听可滚动组件滚动通知的例子：\n\n```dart\nNotificationListener(\n  onNotification: (notification){\n    switch (notification.runtimeType){\n      case ScrollStartNotification: print(\"开始滚动\"); break;\n      case ScrollUpdateNotification: print(\"正在滚动\"); break;\n      case ScrollEndNotification: print(\"滚动停止\"); break;\n      case OverscrollNotification: print(\"滚动到边界\"); break;\n    }\n  },\n  child: ListView.builder(\n      itemCount: 100,\n      itemBuilder: (context, index) {\n        return ListTile(title: Text(\"$index\"),);\n      }\n  ),\n);\n```\n\n上例中的滚动通知如`ScrollStartNotification`、`ScrollUpdateNotification`等都是继承自`ScrollNotification`类，不同类型的通知子类会包含不同的信息，比如`ScrollUpdateNotification`有一个`scrollDelta`属性，它记录了移动的位移，其它通知属性读者可以自己查看SDK文档。\n\n上例中，我们通过`NotificationListener`来监听子`ListView`的滚动通知的，`NotificationListener`定义如下：\n\n```dart\nclass NotificationListener<T extends Notification> extends StatelessWidget {\n  const NotificationListener({\n    Key key,\n    @required this.child,\n    this.onNotification,\n  }) : super(key: key);\n ...//省略无关代码 \n}  \n```\n\n我们可以看到：\n\n1. `NotificationListener` 继承自`StatelessWidget `类，所以它可以直接嵌套到Widget树中。\n\n2. `NotificationListener` 可以指定一个模板参数，该模板参数类型必须是继承自`Notification`；当显式指定模板参数时，`NotificationListener` 便只会接收该参数类型的通知。举个例子，如果我们将上例子代码改为：\n\n   ```dart\n   //指定监听通知的类型为滚动结束通知(ScrollEndNotification)\n   NotificationListener<ScrollEndNotification>(\n     onNotification: (notification){\n       //只会在滚动结束时才会触发此回调\n       print(notification);\n     },\n     child: ListView.builder(\n         itemCount: 100,\n         itemBuilder: (context, index) {\n           return ListTile(title: Text(\"$index\"),);\n         }\n     ),\n   );\n   ```\n\n   上面代码运行后便只会在滚动结束时在控制台打印出通知的信息。\n\n3. `onNotification`回调为通知处理回调，其函数签名如下：\n\n   ```dart\n   typedef NotificationListenerCallback<T extends Notification> = bool Function(T notification);\n   ```\n\n   它的返回值类型为布尔值，当返回值为`true`时，阻止冒泡，其父级Widget将再也收不到该通知；当返回值为`false` 时继续向上冒泡通知。\n\nFlutter的UI框架实现中，除了在可滚动组件在滚动过程中会发出`ScrollNotification`之外，还有一些其它的通知，如`SizeChangedLayoutNotification`、`KeepAliveNotification` 、`LayoutChangedNotification`等，Flutter正是通过这种通知机制来使父元素可以在一些特定时机来做一些事情。\n\n#### 自定义通知\n\n除了Flutter内部通知，我们也可以自定义通知，下面我们看看如何实现自定义通知：\n\n1. 定义一个通知类，要继承自Notification类；\n\n   ```dart\n   class MyNotification extends Notification {\n     MyNotification(this.msg);\n     final String msg;\n   }\n   ```\n\n2. 分发通知。\n\n   `Notification`有一个`dispatch(context)`方法，它是用于分发通知的，我们说过`context`实际上就是操作`Element`的一个接口，它与`Element`树上的节点是对应的，通知会从`context`对应的`Element`节点向上冒泡。\n\n下面我们看一个完整的例子：\n\n```dart\nclass NotificationRoute extends StatefulWidget {\n  @override\n  NotificationRouteState createState() {\n    return new NotificationRouteState();\n  }\n}\n\nclass NotificationRouteState extends State<NotificationRoute> {\n  String _msg=\"\";\n  @override\n  Widget build(BuildContext context) {\n    //监听通知  \n    return NotificationListener<MyNotification>(\n      onNotification: (notification) {\n        setState(() {\n          _msg+=notification.msg+\"  \";\n        });\n       return true;\n      },\n      child: Center(\n        child: Column(\n          mainAxisSize: MainAxisSize.min,\n          children: <Widget>[\n//          RaisedButton(\n//           onPressed: () => MyNotification(\"Hi\").dispatch(context),\n//           child: Text(\"Send Notification\"),\n//          ),  \n            Builder(\n              builder: (context) {\n                return RaisedButton(\n                  //按钮点击时分发通知  \n                  onPressed: () => MyNotification(\"Hi\").dispatch(context),\n                  child: Text(\"Send Notification\"),\n                );\n              },\n            ),\n            Text(_msg)\n          ],\n        ),\n      ),\n    );\n  }\n}\n\nclass MyNotification extends Notification {\n  MyNotification(this.msg);\n  final String msg;\n}\n```\n\n上面代码中，我们每点一次按钮就会分发一个`MyNotification`类型的通知，我们在Widget根上监听通知，收到通知后我们将通知通过Text显示在屏幕上。\n\n> 注意：代码中注释的部分是不能正常工作的，因为这个`context`是根Context，而NotificationListener是监听的子树，所以我们通过`Builder`来构建RaisedButton，来获得按钮位置的context。\n\n运行效果如图8-6所示：\n\n![图8-6](../imgs/8-6.png)\n\n### 阻止冒泡\n\n我们将上面的例子改为：\n\n```dart\nclass NotificationRouteState extends State<NotificationRoute> {\n  String _msg=\"\";\n  @override\n  Widget build(BuildContext context) {\n    //监听通知\n    return NotificationListener<MyNotification>(\n      onNotification: (notification){\n        print(notification.msg); //打印通知\n        return false;\n      },\n      child: NotificationListener<MyNotification>(\n        onNotification: (notification) {\n          setState(() {\n            _msg+=notification.msg+\"  \";\n          });\n          return false; \n        },\n        child: ...//省略重复代码\n      ),\n    );\n  }\n}\n```\n\n上列中两个`NotificationListener`进行了嵌套，子`NotificationListener`的`onNotification`回调返回了`false`，表示不阻止冒泡，所以父`NotificationListener`仍然会受到通知，所以控制台会打印出通知信息；如果将子`NotificationListener`的`onNotification`回调的返回值改为`true`，则父`NotificationListener`便不会再打印通知了，因为子`NotificationListener`已经终止通知冒泡了。\n\n### 通知冒泡原理\n\n我们在上面介绍了通知冒泡的现象及使用，现在我们更深入一些，介绍一下Flutter框架中是如何实现通知冒泡的。为了搞清楚这个问题，就必须看一下源码，我们从通知分发的的源头出发，然后再顺藤摸瓜。由于通知是通过`Notification`的`dispatch(context)`方法发出的，那我们先看看`dispatch(context)`方法中做了什么，下面是相关源码：\n\n```dart\nvoid dispatch(BuildContext target) {\n  target?.visitAncestorElements(visitAncestor);\n}\n```\n\n`dispatch(context)`中调用了当前context的`visitAncestorElements`方法，该方法会从当前Element开始向上遍历父级元素；`visitAncestorElements`有一个遍历回调参数，在遍历过程中对遍历到的父级元素都会执行该回调。遍历的终止条件是：已经遍历到根Element或某个遍历回调返回`false`。源码中传给`visitAncestorElements`方法的遍历回调为`visitAncestor`方法，我们看看`visitAncestor`方法的实现：\n\n```dart\n//遍历回调，会对每一个父级Element执行此回调\nbool visitAncestor(Element element) {\n  //判断当前element对应的Widget是否是NotificationListener。\n  \n  //由于NotificationListener是继承自StatelessWidget，\n  //故先判断是否是StatelessElement\n  if (element is StatelessElement) {\n    //是StatelessElement，则获取element对应的Widget，判断\n    //是否是NotificationListener 。\n    final StatelessWidget widget = element.widget;\n    if (widget is NotificationListener<Notification>) {\n      //是NotificationListener，则调用该NotificationListener的_dispatch方法\n      if (widget._dispatch(this, element)) \n        return false;\n    }\n  }\n  return true;\n}\n```\n\n`visitAncestor `会判断每一个遍历到的父级Widget是否是`NotificationListener`，如果不是，则返回`true`继续向上遍历，如果是，则调用`NotificationListener`的`_dispatch`方法，我们看看`_dispatch`方法的源码：\n\n```dart\n  bool _dispatch(Notification notification, Element element) {\n    // 如果通知监听器不为空，并且当前通知类型是该NotificationListener\n    // 监听的通知类型，则调用当前NotificationListener的onNotification\n    if (onNotification != null && notification is T) {\n      final bool result = onNotification(notification);\n      // 返回值决定是否继续向上遍历\n      return result == true; \n    }\n    return false;\n  }\n```\n\n我们可以看到`NotificationListener`的`onNotification`回调最终是在`_dispatch`方法中执行的，然后会根据返回值来确定是否继续向上冒泡。上面的源码实现其实并不复杂，通过阅读这些源码，一些额外的点读者可以注意一下：\n\n1. `Context`上也提供了遍历Element树的方法。\n2. 我们可以通过`Element.widget`得到`element`节点对应的widget；我们已经反复讲过Widget和Element的对应关系，读者通过这些源码来加深理解。\n\n### 总结\n\nFlutter中通过通知冒泡实现了一套自低向上的消息传递机制，这个和Web开发中浏览器的事件冒泡原理类似，Web开发者可以类比学习。另外我们通过源码了解了Flutter 通知冒泡的流程和原理，便于读者加深理解和学习Flutter的框架设计思想，在此，再次建议读者在平时学习中能多看看源码，定会受益匪浅。\n"
  },
  {
    "path": "src/chapter9/animated_switcher.md",
    "content": "# 9.6 通用“动画切换”组件（AnimatedSwitcher）\n\n实际开发中，我们经常会遇到切换UI元素的场景，比如Tab切换、路由切换。为了增强用户体验，通常在切换时都会指定一个动画，以使切换过程显得平滑。Flutter SDK组件库中已经提供了一些常用的切换组件，如`PageView`、`TabView`等，但是，这些组件并不能覆盖全部的需求场景，为此，Flutter SDK中提供了一个`AnimatedSwitcher`组件，它定义了一种通用的UI切换抽象。\n\n## 9.6.1 AnimatedSwitcher\n\n`AnimatedSwitcher` 可以同时对其新、旧子元素添加显示、隐藏动画。也就是说在`AnimatedSwitcher`的子元素发生变化时，会对其旧元素和新元素，我们先看看`AnimatedSwitcher` 的定义：\n\n```dart\nconst AnimatedSwitcher({\n  Key key,\n  this.child,\n  @required this.duration, // 新child显示动画时长\n  this.reverseDuration,// 旧child隐藏的动画时长\n  this.switchInCurve = Curves.linear, // 新child显示的动画曲线\n  this.switchOutCurve = Curves.linear,// 旧child隐藏的动画曲线\n  this.transitionBuilder = AnimatedSwitcher.defaultTransitionBuilder, // 动画构建器\n  this.layoutBuilder = AnimatedSwitcher.defaultLayoutBuilder, //布局构建器\n})\n```\n\n当`AnimatedSwitcher`的child发生变化时（类型或Key不同），旧child会执行隐藏动画，新child会执行执行显示动画。究竟执行何种动画效果则由`transitionBuilder `参数决定，该参数接受一个`AnimatedSwitcherTransitionBuilder `类型的builder，定义如下：\n\n```dart\ntypedef AnimatedSwitcherTransitionBuilder =\n  Widget Function(Widget child, Animation<double> animation);\n```\n\n该`builder`在`AnimatedSwitcher`的child切换时会分别对新、旧child绑定动画：\n\n1. 对旧child，绑定的动画会反向执行（reverse）\n2. 对新child，绑定的动画会正向指向（forward）\n\n这样一下，便实现了对新、旧child的动画绑定。`AnimatedSwitcher`的默认值是`AnimatedSwitcher.defaultTransitionBuilder` ：\n\n```dart\nWidget defaultTransitionBuilder(Widget child, Animation<double> animation) {\n  return FadeTransition(\n    opacity: animation,\n    child: child,\n  );\n}\n```\n\n可以看到，返回了`FadeTransition`对象，也就是说默认情况，`AnimatedSwitcher`会对新旧child执行“渐隐”和“渐显”动画。\n\n### 例子\n\n下面我们看一个列子：实现一个计数器，然后再每一次自增的过程中，旧数字执行缩小动画隐藏，新数字执行放大动画显示，代码如下：\n\n```dart\nimport 'package:flutter/material.dart';\n\nclass AnimatedSwitcherCounterRoute extends StatefulWidget {\n   const AnimatedSwitcherCounterRoute({Key key}) : super(key: key);\n\n   @override\n   _AnimatedSwitcherCounterRouteState createState() => _AnimatedSwitcherCounterRouteState();\n }\n\n class _AnimatedSwitcherCounterRouteState extends State<AnimatedSwitcherCounterRoute> {\n   int _count = 0;\n\n   @override\n   Widget build(BuildContext context) {\n     return Center(\n       child: Column(\n         mainAxisAlignment: MainAxisAlignment.center,\n         children: <Widget>[\n           AnimatedSwitcher(\n             duration: const Duration(milliseconds: 500),\n             transitionBuilder: (Widget child, Animation<double> animation) {\n               //执行缩放动画\n               return ScaleTransition(child: child, scale: animation);\n             },\n             child: Text(\n               '$_count',\n               //显示指定key，不同的key会被认为是不同的Text，这样才能执行动画\n               key: ValueKey<int>(_count),\n               style: Theme.of(context).textTheme.headline4,\n             ),\n           ),\n           RaisedButton(\n             child: const Text('+1',),\n             onPressed: () {\n               setState(() {\n                 _count += 1;\n               });\n             },\n           ),\n         ],\n       ),\n     );\n   }\n }\n```\n\n运行示例代码，当点击“+1”按钮时，原先的数字会逐渐缩小直至隐藏，而新数字会逐渐放大，我截取了动画执行过程的一帧，如图9-5所示：\n\n![图9-5](../imgs/9-5.png)\n\n上图是第一次点击“+1”按钮后切换动画的一帧，此时“0”正在逐渐缩小，而“1”正在“0”的中间，正在逐渐放大。\n\n> 注意：AnimatedSwitcher的新旧child，如果类型相同，则Key必须不相等。\n\n### AnimatedSwitcher实现原理\n\n实际上，`AnimatedSwitcher`的实现原理是比较简单的，我们根据`AnimatedSwitcher`的使用方式也可以猜个大概。要想实现新旧child切换动画，只需要明确两个问题：动画执行的时机是和如何对新旧child执行动画。从`AnimatedSwitcher`的使用方式我们可以看到，当child发生变化时（子widget的key和类型**不**同时相等则认为发生变化），则重新会重新执行`build`，然后动画开始执行。我们可以通过继承StatefulWidget来实现`AnimatedSwitcher`，具体做法是在`didUpdateWidget` 回调中判断其新旧child是否发生变化，如果发生变化，则对旧child执行反向退场（reverse）动画，对新child执行正向（forward）入场动画即可。下面是`AnimatedSwitcher`实现的部分核心伪代码：\n\n```dart\nWidget _widget; //\nvoid didUpdateWidget(AnimatedSwitcher oldWidget) {\n  super.didUpdateWidget(oldWidget);\n  // 检查新旧child是否发生变化(key和类型同时相等则返回true，认为没变化)\n  if (Widget.canUpdate(widget.child, oldWidget.child)) {\n    // child没变化，...\n  } else {\n    //child发生了变化，构建一个Stack来分别给新旧child执行动画\n   _widget= Stack(\n      alignment: Alignment.center,\n      children:[\n        //旧child应用FadeTransition\n        FadeTransition(\n         opacity: _controllerOldAnimation,\n         child : oldWidget.child,\n        ),\n        //新child应用FadeTransition\n        FadeTransition(\n         opacity: _controllerNewAnimation,\n         child : widget.child,\n        ),\n      ]\n    );\n    // 给旧child执行反向退场动画\n    _controllerOldAnimation.reverse();\n    //给新child执行正向入场动画\n    _controllerNewAnimation.forward();\n  }\n}\n\n//build方法\nWidget build(BuildContext context){\n  return _widget;\n}\n```\n\n上面伪代码展示了`AnimatedSwitcher`实现的核心逻辑，当然`AnimatedSwitcher`真正的实现比这个复杂，它可以自定义进退场过渡动画以及执行动画时的布局等。在此，我们删繁就简，通过伪代码形式让读者能够清楚看到主要的实现思路，具体的实现读者可以参考`AnimatedSwitcher`源码。\n\n另外，Flutter SDK中还提供了一个`AnimatedCrossFade`组件，它也可以切换两个子元素，切换过程执行渐隐渐显的动画，和`AnimatedSwitcher`不同的是`AnimatedCrossFade`是针对两个子元素，而`AnimatedSwitcher`是在一个子元素的新旧值之间切换。`AnimatedCrossFade`实现原理比较简单，也有和`AnimatedSwitcher`类似的地方，因此不再赘述，读者有兴趣可以查看其源码。\n\n## 9.6.2 AnimatedSwitcher高级用法\n\n假设现在我们想实现一个类似路由平移切换的动画：旧页面屏幕中向左侧平移退出，新页面重屏幕右侧平移进入。如果要用AnimatedSwitcher的话，我们很快就会发现一个问题：做不到！我们可能会写出下面的代码：\n\n```dart\nAnimatedSwitcher(\n  duration: Duration(milliseconds: 200),\n  transitionBuilder: (Widget child, Animation<double> animation) {\n    var tween=Tween<Offset>(begin: Offset(1, 0), end: Offset(0, 0))\n     return SlideTransition(\n       child: child,\n       position: tween.animate(animation),\n    );\n  },\n  ...//省略\n)\n```\n\n上面的代码有什么问题呢？我们前面说过在`AnimatedSwitcher`的child切换时会分别对新child执行正向动画（forward），而对旧child执行反向动画（reverse），所以真正的效果便是：新child确实从屏幕右侧平移进入了，但旧child却会从屏幕**右侧**（而不是左侧）退出。其实也很容易理解，因为在没有特殊处理的情况下，同一个动画的正向和逆向正好是相反（对称）的。\n\n那么问题来了，难道就不能使用`AnimatedSwitcher`了？答案当然是否定的！仔细想想这个问题，究其原因，就是因为同一个`Animation`正向（forward）和反向（reverse）是对称的。所以如果我们可以打破这种对称性，那么便可以实现这个功能了，下面我们来封装一个`MySlideTransition`，它与`SlideTransition`唯一的不同就是对动画的反向执行进行了定制（从左边滑出隐藏），代码如下：\n\n```dart\nclass MySlideTransition extends AnimatedWidget {\n  MySlideTransition({\n    Key key,\n    @required Animation<Offset> position,\n    this.transformHitTests = true,\n    this.child,\n  })\n      : assert(position != null),\n        super(key: key, listenable: position) ;\n\n  Animation<Offset> get position => listenable;\n  final bool transformHitTests;\n  final Widget child;\n\n  @override\n  Widget build(BuildContext context) {\n    Offset offset=position.value;\n    //动画反向执行时，调整x偏移，实现“从左边滑出隐藏”\n    if (position.status == AnimationStatus.reverse) {\n         offset = Offset(-offset.dx, offset.dy);\n    }\n    return FractionalTranslation(\n      translation: offset,\n      transformHitTests: transformHitTests,\n      child: child,\n    );\n  }\n}\n```\n\n调用时，将`SlideTransition`替换成`MySlideTransition `即可：\n\n```dart\nAnimatedSwitcher(\n  duration: Duration(milliseconds: 200),\n  transitionBuilder: (Widget child, Animation<double> animation) {\n    var tween=Tween<Offset>(begin: Offset(1, 0), end: Offset(0, 0))\n     return MySlideTransition(\n              child: child,\n              position: tween.animate(animation),\n    \t      );\n  },\n  ...//省略\n)\n```\n\n运行后，我截取动画执行过程中的一帧，如图9-6所示：\n\n![图9-6](../imgs/9-6.png)\n\n上图中“0”从左侧滑出，而“1”从右侧滑入。可以看到，我们通过这种巧妙的方式实现了类似路由进场切换的动画，实际上Flutter路由切换也正是通过`AnimatedSwitcher`来实现的。\n\n### SlideTransitionX\n\n上面的示例我们实现了“左出右入”的动画，那如果要实现“右入左出”、“上入下出”或者 “下入上出”怎么办？当然，我们可以分别修改上面的代码，但是这样每种动画都得单独定义一个“Transition”，这很麻烦。本节将封装一个通用的`SlideTransitionX` 来实现这种“出入滑动动画”，代码如下：\n\n```dart\nclass SlideTransitionX extends AnimatedWidget {\n  SlideTransitionX({\n    Key key,\n    @required Animation<double> position,\n    this.transformHitTests = true,\n    this.direction = AxisDirection.down,\n    this.child,\n  })\n      : assert(position != null),\n        super(key: key, listenable: position) {\n    // 偏移在内部处理      \n    switch (direction) {\n      case AxisDirection.up:\n        _tween = Tween(begin: Offset(0, 1), end: Offset(0, 0));\n        break;\n      case AxisDirection.right:\n        _tween = Tween(begin: Offset(-1, 0), end: Offset(0, 0));\n        break;\n      case AxisDirection.down:\n        _tween = Tween(begin: Offset(0, -1), end: Offset(0, 0));\n        break;\n      case AxisDirection.left:\n        _tween = Tween(begin: Offset(1, 0), end: Offset(0, 0));\n        break;\n    }\n  }\n\n\n  Animation<double> get position => listenable;\n\n  final bool transformHitTests;\n\n  final Widget child;\n\n  //退场（出）方向\n  final AxisDirection direction;\n\n  Tween<Offset> _tween;\n\n  @override\n  Widget build(BuildContext context) {\n    Offset offset = _tween.evaluate(position);\n    if (position.status == AnimationStatus.reverse) {\n      switch (direction) {\n        case AxisDirection.up:\n          offset = Offset(offset.dx, -offset.dy);\n          break;\n        case AxisDirection.right:\n          offset = Offset(-offset.dx, offset.dy);\n          break;\n        case AxisDirection.down:\n          offset = Offset(offset.dx, -offset.dy);\n          break;\n        case AxisDirection.left:\n          offset = Offset(-offset.dx, offset.dy);\n          break;\n      }\n    }\n    return FractionalTranslation(\n      translation: offset,\n      transformHitTests: transformHitTests,\n      child: child,\n    );\n  }\n}\n```\n\n现在如果我们想实现各种“滑动出入动画”便非常容易，只需给`direction `传递不同的方向值即可，比如要实现“上入下出”，则：\n\n```dart\nAnimatedSwitcher(\n  duration: Duration(milliseconds: 200),\n  transitionBuilder: (Widget child, Animation<double> animation) {\n    var tween=Tween<Offset>(begin: Offset(1, 0), end: Offset(0, 0))\n     return SlideTransitionX(\n              child: child,\n     \t\t\t\t  direction: AxisDirection.down, //上入下出\n              position: animation,\n    \t      );\n  },\n  ...//省略其余代码\n)\n```\n\n运行后，我截取动画执行过程中的一帧，如图9-7所示：\n\n![图9-7](../imgs/9-7.png)\n\n上图中“1”从底部滑出，而“2”从顶部滑入。读者可以尝试给`SlideTransitionX`的`direction`取不同的值来查看运行效果。\n\n## 总结\n\n本节我们学习了`AnimatedSwitcher`的详细用法，同时也介绍了打破`AnimatedSwitcher`动画对称性的方法。我们可以发现：在需要切换新旧UI元素的场景，`AnimatedSwitcher`将十分实用。\n\n"
  },
  {
    "path": "src/chapter9/animated_widgets.md",
    "content": "# 9.7 动画过渡组件\n\n为了表述方便，本书约定，将在Widget属性发生变化时会执行过渡动画的组件统称为”动画过渡组件“，而动画过渡组件最明显的一个特征就是它会在内部自管理`AnimationController`。我们知道，为了方便使用者可以自定义动画的曲线、执行时长、方向等，在前面介绍过的动画封装方法中，通常都需要使用者自己提供一个`AnimationController`对象来自定义这些属性值。但是，如此一来，使用者就必须得手动管理`AnimationController`，这又会增加使用的复杂性。因此，如果也能将`AnimationController`进行封装，则会大大提高动画组件的易用性。\n\n## 9.7.1 自定义动画过渡组件\n\n我们要实现一个`AnimatedDecoratedBox`，它可以在`decoration`属性发生变化时，从旧状态变成新状态的过程可以执行一个过渡动画。根据前面所学的知识，我们实现了一个`AnimatedDecoratedBox1`组件：\n\n```dart\nclass AnimatedDecoratedBox1 extends StatefulWidget {\n  AnimatedDecoratedBox1({\n    Key key,\n    @required this.decoration,\n    this.child,\n    this.curve = Curves.linear,\n    @required this.duration,\n    this.reverseDuration,\n  });\n\n  final BoxDecoration decoration;\n  final Widget child;\n  final Duration duration;\n  final Curve curve;\n  final Duration reverseDuration;\n\n  @override\n  _AnimatedDecoratedBox1State createState() => _AnimatedDecoratedBox1State();\n}\n\nclass _AnimatedDecoratedBox1State extends State<AnimatedDecoratedBox1>\n    with SingleTickerProviderStateMixin {\n  @protected\n  AnimationController get controller => _controller;\n  AnimationController _controller;\n\n  Animation<double> get animation => _animation;\n  Animation<double> _animation;\n\n  DecorationTween _tween;\n\n  @override\n  Widget build(BuildContext context) {\n    return AnimatedBuilder(\n      animation: _animation,\n      builder: (context, child){\n        return DecoratedBox(\n          decoration: _tween.animate(_animation).value,\n          child: child,\n        );\n      },\n      child: widget.child,\n    );\n  }\n\n  @override\n  void initState() {\n    super.initState();\n    _controller = AnimationController(\n      duration: widget.duration,\n      reverseDuration: widget.reverseDuration,\n      vsync: this,\n    );\n    _tween = DecorationTween(begin: widget.decoration);\n    _updateCurve();\n  }\n\n  void _updateCurve() {\n    if (widget.curve != null)\n      _animation = CurvedAnimation(parent: _controller, curve: widget.curve);\n    else\n      _animation = _controller;\n  }\n\n\n  @override\n  void didUpdateWidget(AnimatedDecoratedBox1 oldWidget) {\n    super.didUpdateWidget(oldWidget);\n    if (widget.curve != oldWidget.curve)\n      _updateCurve();\n    _controller.duration = widget.duration;\n    _controller.reverseDuration = widget.reverseDuration;\n    if(widget.decoration!= (_tween.end ?? _tween.begin)){\n      _tween\n        ..begin = _tween.evaluate(_animation)\n        ..end = widget.decoration;\n      _controller\n        ..value = 0.0\n        ..forward();\n    }\n  }\n\n  @override\n  void dispose() {\n    _controller.dispose();\n    super.dispose();\n  }\n}\n```\n\n下面我们来使用`AnimatedDecoratedBox1`来实现按钮点击后背景色从蓝色过渡到红色的效果：\n\n```dart\nColor _decorationColor = Colors.blue;\nvar duration = Duration(seconds: 1);\n...//省略无关代码\nAnimatedDecoratedBox(\n  duration: duration,\n  decoration: BoxDecoration(color: _decorationColor),\n  child: FlatButton(\n    onPressed: () {\n      setState(() {\n        _decorationColor = Colors.red;\n      });\n    },\n    child: Text(\n      \"AnimatedDecoratedBox\",\n      style: TextStyle(color: Colors.white),\n    ),\n  ),\n)\n```\n\n点击前效果如图9-8所示，点击后截取了过渡过程的一帧如图9-9所示： ![img](../imgs/9-8.png?lastModify=1565699462)![img](../imgs/9-9.png?lastModify=1565699462)\n\n点击后，按钮背景色会从蓝色向红色过渡，图9-9是过渡过程中的一帧，有点偏紫色，整个过渡动画结束后背景会变为红色。\n\n上面的代码虽然实现了我们期望的功能，但是代码却比较复杂。稍加思考后，我们就可以发现，`AnimationController`的管理以及Tween更新部分的代码都是可以抽象出来的，如果我们这些通用逻辑封装成基类，那么要实现动画过渡组件只需要继承这些基类，然后定制自身不同的代码（比如动画每一帧的构建方法）即可，这样将会简化代码。\n\n为了方便开发者来实现动画过渡组件的封装，Flutter提供了一个`ImplicitlyAnimatedWidget`抽象类，它继承自StatefulWidget，同时提供了一个对应的`ImplicitlyAnimatedWidgetState`类，`AnimationController`的管理就在`ImplicitlyAnimatedWidgetState`类中。开发者如果要封装动画，只需要分别继承`ImplicitlyAnimatedWidget`和`ImplicitlyAnimatedWidgetState`类即可，下面我们演示一下具体如何实现。\n\n我们需要分两步实现：\n\n1. 继承`ImplicitlyAnimatedWidget`类。\n\n   ```dart\n   class AnimatedDecoratedBox extends ImplicitlyAnimatedWidget {\n     AnimatedDecoratedBox({\n       Key key,\n       @required this.decoration,\n       this.child,\n       Curve curve = Curves.linear, //动画曲线\n       @required Duration duration, // 正向动画执行时长\n       Duration reverseDuration, // 反向动画执行时长\n     }) : super(\n             key: key,\n             curve: curve,\n             duration: duration,\n             reverseDuration: reverseDuration,\n           );\n     final BoxDecoration decoration;\n     final Widget child;\n   \n     @override\n     _AnimatedDecoratedBoxState createState() {\n       return _AnimatedDecoratedBoxState();\n     }\n   }\n   ```\n\n   其中`curve`、`duration`、`reverseDuration`三个属性在`ImplicitlyAnimatedWidget `中已定义。 可以看到`AnimatedDecoratedBox`类和普通继承自`StatefulWidget`的类没有什么不同。\n\n2. State类继承自`AnimatedWidgetBaseState`（该类继承自`ImplicitlyAnimatedWidgetState`类）。\n\n   ```dart\n   class _AnimatedDecoratedBoxState\n       extends AnimatedWidgetBaseState<AnimatedDecoratedBox> {\n     DecorationTween _decoration; //定义一个Tween\n   \n     @override\n     Widget build(BuildContext context) {\n       return DecoratedBox(\n         decoration: _decoration.evaluate(animation),\n         child: widget.child,\n       );\n     }\n   \n     @override\n     void forEachTween(visitor) {\n       // 在需要更新Tween时，基类会调用此方法\n       _decoration = visitor(_decoration, widget.decoration,\n           (value) => DecorationTween(begin: value));\n     }\n   }\n   ```\n\n   可以看到我们实现了` build`和`forEachTween`两个方法。在动画执行过程中，每一帧都会调用`build`方法（调用逻辑在`ImplicitlyAnimatedWidgetState`中），所以在`build`方法中我们需要构建每一帧的`DecoratedBox`状态，因此得算出每一帧的`decoration` 状态，这个我们可以通过` _decoration.evaluate(animation)` 来算出，其中`animation`是`ImplicitlyAnimatedWidgetState`基类中定义的对象，`_decoration`是我们自定义的一个`DecorationTween`类型的对象，那么现在的问题就是它是在什么时候被赋值的呢？要回答这个问题，我们就得搞清楚什么时候需要对`_decoration`赋值。我们知道`_decoration`是一个Tween，而Tween的主要职责就是定义动画的起始状态（begin）和终止状态(end)。对于`AnimatedDecoratedBox`来说，`decoration`的终止状态就是用户传给它的值，而起始状态是不确定的，有以下两种情况：\n\n   1. `AnimatedDecoratedBox`首次build，此时直接将其`decoration`值置为起始状态，即`_decoration`值为`DecorationTween(begin: decoration)` 。\n   2. `AnimatedDecoratedBox`的`decoration`更新时，则起始状态为`_decoration.animate(animation)`，即`_decoration`值为`DecorationTween(begin: _decoration.animate(animation)，end:decoration)`。\n   \n\n现在`forEachTween`的作用就很明显了，它正是用于来更新Tween的初始值的，在上述两种情况下会被调用，而开发者只需重写此方法，并在此方法中更新Tween的起始状态值即可。而一些更新的逻辑被屏蔽在了`visitor`回调，我们只需要调用它并给它传递正确的参数即可，`visitor`方法签名如下：\n\n```dart\n   Tween visitor(\n     Tween<dynamic> tween, //当前的tween，第一次调用为null\n     dynamic targetValue, // 终止状态\n     TweenConstructor<dynamic> constructor，//Tween构造器，在上述三种情况下会被调用以更新tween\n   );\n```\n\n可以看到，通过继承`ImplicitlyAnimatedWidget`和`ImplicitlyAnimatedWidgetState`类可以快速的实现动画过渡组件的封装，这和我们纯手工实现相比，代码简化了很多。\n\n> 如果读者还有疑惑，建议查看`ImplicitlyAnimatedWidgetState`的源码并结合本示例代码对比理解。\n\n### 动画过渡组件的反向动画\n\n在使用动画过渡组件，我们只需要在改变一些属性值后重新build组件即可，所以要实现状态反向过渡，只需要将前后状态值互换即可实现，这本来是不需要再浪费笔墨的。但是`ImplicitlyAnimatedWidget`构造函数中却有一个`reverseDuration`属性用于设置反向动画的执行时长，这貌似在告诉读者`ImplicitlyAnimatedWidget`本身也提供了执行反向动画的接口，于是笔者查看了`ImplicitlyAnimatedWidgetState`源码并未发现有执行反向动画的接口，唯一有用的是它暴露了控制动画的`controller`。所以如果要让`reverseDuration`生效，我们只能先获取`controller`，然后再通过`controller.reverse()`来启动反向动画，比如我们在上面示例的基础上实现一个循环的点击背景颜色变换效果，要求从蓝色变为红色时动画执行时间为400ms，从红变蓝为2s，如果要使`reverseDuration`生效，我们需要这么做：\n\n```dart\nAnimatedDecoratedBox(\n  duration: Duration( milliseconds: 400),\n  decoration: BoxDecoration(color: _decorationColor),\n  reverseDuration: Duration(seconds: 2),\n  child: Builder(builder: (context) {\n    return FlatButton(\n      onPressed: () {\n        if (_decorationColor == Colors.red) {\n          ImplicitlyAnimatedWidgetState _state =\n              context.findAncestorStateOfType<ImplicitlyAnimatedWidgetState>();\n           // 通过controller来启动反向动画\n          _state.controller.reverse().then((e) {\n            // 经验证必须调用setState来触发rebuild，否则状态同步会有问题\n            setState(() {\n              _decorationColor = Colors.blue;\n            });\n          });\n        } else {\n          setState(() {\n            _decorationColor = Colors.red;\n          });\n        }\n      },\n      child: Text(\n        \"AnimatedDecoratedBox toggle\",\n        style: TextStyle(color: Colors.white),\n      ),\n    );\n  }),\n)\n```\n\n上面的代码实际上是非常糟糕且没必要的，它需要我们了解`ImplicitlyAnimatedWidgetState `内部实现，并且要手动去启动反向动画。我们完全可以通过如下代码实现相同的效果：\n\n```dart\nAnimatedDecoratedBox(\n  duration: Duration(\n      milliseconds: _decorationColor == Colors.red ? 400 : 2000),\n  decoration: BoxDecoration(color: _decorationColor),\n  child: Builder(builder: (context) {\n    return FlatButton(\n      onPressed: () {\n        setState(() {\n          _decorationColor = _decorationColor == Colors.blue\n              ? Colors.red\n              : Colors.blue;\n        });\n      },\n      child: Text(\n        \"AnimatedDecoratedBox toggle\",\n        style: TextStyle(color: Colors.white),\n      ),\n    );\n  }),\n)\n```\n\n这样的代码是不是优雅的多！那么现在问题来了，为什么`ImplicitlyAnimatedWidgetState `要提供一个`reverseDuration`参数呢？笔者仔细研究了`ImplicitlyAnimatedWidgetState `的实现，发现唯一的解释就是该参数并非是给`ImplicitlyAnimatedWidgetState `用的，而是给子类用的！原因正如我们前面说的，要使`reverseDuration` 有用就必须得获取`controller ` 属性来手动启动反向动画，`ImplicitlyAnimatedWidgetState `中的`controller ` 属性是一个保护属性，定义如下：\n\n```dart\n @protected\n  AnimationController get controller => _controller;\n```\n\n而保护属性原则上只应该在子类中使用，而不应该像上面示例代码一样在外部使用。综上，我们可以得出两条结论：\n\n1. 使用动画过渡组件时如果需要执行反向动画的场景，应尽量使用状态互换的方法，而不应该通过获取`ImplicitlyAnimatedWidgetState `中`controller`的方式。\n\n2. 如果我们自定义的动画过渡组件用不到`reverseDuration` ，那么最好就不要暴露此参数，比如我们上面自定义的`AnimatedDecoratedBox`定义中就可以去除`reverseDuration` 可选参数，如：\n\n   ```dart\n   class AnimatedDecoratedBox extends ImplicitlyAnimatedWidget {\n     AnimatedDecoratedBox({\n       Key key,\n       @required this.decoration,\n       this.child,\n       Curve curve = Curves.linear,\n       @required Duration duration,\n     }) : super(\n             key: key,\n             curve: curve,\n             duration: duration,\n           );\n   ```\n\n## 9.7.2 Flutter预置的动画过渡组件\n\nFlutter SDK中也预置了很多动画过渡组件，实现方式和大都和`AnimatedDecoratedBox`差不多，如表9-1所示：\n\n| 组件名                   | 功能                                                         |\n| ------------------------ | ------------------------------------------------------------ |\n| AnimatedPadding          | 在padding发生变化时会执行过渡动画到新状态                    |\n| AnimatedPositioned       | 配合Stack一起使用，当定位状态发生变化时会执行过渡动画到新的状态。 |\n| AnimatedOpacity          | 在透明度opacity发生变化时执行过渡动画到新状态                |\n| AnimatedAlign            | 当`alignment`发生变化时会执行过渡动画到新的状态。            |\n| AnimatedContainer        | 当Container属性发生变化时会执行过渡动画到新的状态。          |\n| AnimatedDefaultTextStyle | 当字体样式发生变化时，子组件中继承了该样式的文本组件会动态过渡到新样式。 |\n\n<center>表9-1：Flutter预置的动画过渡组件</center>\n下面我们通过一个示例来感受一下这些预置的动画过渡组件效果：\n\n```dart\nimport 'package:flutter/material.dart';\n\nclass AnimatedWidgetsTest extends StatefulWidget {\n  @override\n  _AnimatedWidgetsTestState createState() => _AnimatedWidgetsTestState();\n}\n\nclass _AnimatedWidgetsTestState extends State<AnimatedWidgetsTest> {\n  double _padding = 10;\n  var _align = Alignment.topRight;\n  double _height = 100;\n  double _left = 0;\n  Color _color = Colors.red;\n  TextStyle _style = TextStyle(color: Colors.black);\n  Color _decorationColor = Colors.blue;\n\n  @override\n  Widget build(BuildContext context) {\n    var duration = Duration(seconds: 5);\n    return SingleChildScrollView(\n      child: Column(\n        children: <Widget>[\n          RaisedButton(\n            onPressed: () {\n              setState(() {\n                _padding = 20;\n              });\n            },\n            child: AnimatedPadding(\n              duration: duration,\n              padding: EdgeInsets.all(_padding),\n              child: Text(\"AnimatedPadding\"),\n            ),\n          ),\n          SizedBox(\n            height: 50,\n            child: Stack(\n              children: <Widget>[\n                AnimatedPositioned(\n                  duration: duration,\n                  left: _left,\n                  child: RaisedButton(\n                    onPressed: () {\n                      setState(() {\n                        _left = 100;\n                      });\n                    },\n                    child: Text(\"AnimatedPositioned\"),\n                  ),\n                )\n              ],\n            ),\n          ),\n          Container(\n            height: 100,\n            color: Colors.grey,\n            child: AnimatedAlign(\n              duration: duration,\n              alignment: _align,\n              child: RaisedButton(\n                onPressed: () {\n                  setState(() {\n                    _align = Alignment.center;\n                  });\n                },\n                child: Text(\"AnimatedAlign\"),\n              ),\n            ),\n          ),\n          AnimatedContainer(\n            duration: duration,\n            height: _height,\n            color: _color,\n            child: FlatButton(\n              onPressed: () {\n                setState(() {\n                  _height = 150;\n                  _color = Colors.blue;\n                });\n              },\n              child: Text(\n                \"AnimatedContainer\",\n                style: TextStyle(color: Colors.white),\n              ),\n            ),\n          ),\n          AnimatedDefaultTextStyle(\n            child: GestureDetector(\n              child: Text(\"hello world\"),\n              onTap: () {\n                setState(() {\n                  _style = TextStyle(\n                    color: Colors.blue,\n                    decorationStyle: TextDecorationStyle.solid,\n                    decorationColor: Colors.blue,\n                  );\n                });\n              },\n            ),\n            style: _style,\n            duration: duration,\n          ),\n          AnimatedDecoratedBox(\n            duration: duration,\n            decoration: BoxDecoration(color: _decorationColor),\n            child: FlatButton(\n              onPressed: () {\n                setState(() {\n                  _decorationColor = Colors.red;\n                });\n              },\n              child: Text(\n                \"AnimatedDecoratedBox\",\n                style: TextStyle(color: Colors.white),\n              ),\n            ),\n          )\n        ].map((e) {\n          return Padding(\n            padding: EdgeInsets.symmetric(vertical: 16),\n            child: e,\n          );\n        }).toList(),\n      ),\n    );\n  }\n}\n```\n\n运行后效果如图9-10所示：\n\n![图9-10](../imgs/9-10.png)\n\n读者可以点击一下相应组件来查看一下实际的运行效果。\n"
  },
  {
    "path": "src/chapter9/animation_structure.md",
    "content": "# 9.2 动画基本结构及状态监听\n\n## 9.2.1 动画基本结构\n\n在Flutter中我们可以通过多种方式来实现动画，下面通过一个图片逐渐放大示例的不同实现来演示Flutter中动画的不同实现方式的区别。\n\n### 基础版本\n\n下面我们演示一下最基础的动画实现方式：\n\n```dart\nclass ScaleAnimationRoute extends StatefulWidget {\n  @override\n  _ScaleAnimationRouteState createState() => new _ScaleAnimationRouteState();\n}\n\n//需要继承TickerProvider，如果有多个AnimationController，则应该使用TickerProviderStateMixin。\nclass _ScaleAnimationRouteState extends State<ScaleAnimationRoute>  with SingleTickerProviderStateMixin{ \n    \n  Animation<double> animation;\n  AnimationController controller;\n    \n  initState() {\n    super.initState();\n    controller = new AnimationController(\n        duration: const Duration(seconds: 3), vsync: this);\n    //图片宽高从0变到300\n    animation = new Tween(begin: 0.0, end: 300.0).animate(controller)\n      ..addListener(() {\n        setState(()=>{});\n      });\n    //启动动画(正向执行)\n    controller.forward();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return new Center(\n       child: Image.asset(\"imgs/avatar.png\",\n          width: animation.value,\n          height: animation.value\n      ),\n    );\n  }\n\n  dispose() {\n    //路由销毁时需要释放动画资源\n    controller.dispose();\n    super.dispose();\n  }\n}\n```\n\n上面代码中`addListener()`函数调用了`setState()`，所以每次动画生成一个新的数字时，当前帧被标记为脏(dirty)，这会导致widget的`build()`方法再次被调用，而在`build()`中，改变Image的宽高，因为它的高度和宽度现在使用的是`animation.value` ，所以就会逐渐放大。值得注意的是动画完成时要释放控制器(调用`dispose()`方法)以防止内存泄漏。\n\n上面的例子中并没有指定Curve，所以放大的过程是线性的（匀速），下面我们指定一个Curve，来实现一个类似于弹簧效果的动画过程，我们只需要将`initState`中的代码改为下面这样即可：\n\n```dart\n  initState() {\n    super.initState();\n    controller = new AnimationController(\n        duration: const Duration(seconds: 3), vsync: this);\n    //使用弹性曲线\n    animation=CurvedAnimation(parent: controller, curve: Curves.bounceIn);\n    //图片宽高从0变到300\n    animation = new Tween(begin: 0.0, end: 300.0).animate(animation)\n      ..addListener(() {\n        setState(() {\n        });\n      });\n    //启动动画\n    controller.forward();\n  }\n```\n\n上面代码执行后截取了其中的两帧，效果如图9-1、9-2所示：\n\n![图9-1](../imgs/9-1.png)![图9-2](../imgs/9-2.png)\n\n### 使用AnimatedWidget简化\n\n细心的读者可能已经发现上面示例中通过`addListener()`和`setState()` 来更新UI这一步其实是通用的，如果每个动画中都加这么一句是比较繁琐的。`AnimatedWidget`类封装了调用`setState()`的细节，并允许我们将widget分离出来，重构后的代码如下：\n\n```dart\nclass AnimatedImage extends AnimatedWidget {\n  AnimatedImage({Key key, Animation<double> animation})\n      : super(key: key, listenable: animation);\n\n  Widget build(BuildContext context) {\n    final Animation<double> animation = listenable;\n    return new Center(\n      child: Image.asset(\"imgs/avatar.png\",\n          width: animation.value,\n          height: animation.value\n      ),\n    );\n  }\n}\n\n\nclass ScaleAnimationRoute1 extends StatefulWidget {\n  @override\n  _ScaleAnimationRouteState createState() => new _ScaleAnimationRouteState();\n}\n\nclass _ScaleAnimationRouteState extends State<ScaleAnimationRoute1>\n    with SingleTickerProviderStateMixin {\n\n  Animation<double> animation;\n  AnimationController controller;\n\n  initState() {\n    super.initState();\n    controller = new AnimationController(\n        duration: const Duration(seconds: 3), vsync: this);\n    //图片宽高从0变到300\n    animation = new Tween(begin: 0.0, end: 300.0).animate(controller);\n    //启动动画\n    controller.forward();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return AnimatedImage(animation: animation,);\n  }\n\n  dispose() {\n    //路由销毁时需要释放动画资源\n    controller.dispose();\n    super.dispose();\n  }\n}\n```\n\n\n\n### 用AnimatedBuilder重构\n\n用AnimatedWidget可以从动画中分离出widget，而动画的渲染过程（即设置宽高）仍然在AnimatedWidget中，假设如果我们再添加一个widget透明度变化的动画，那么我们需要再实现一个AnimatedWidget，这样不是很优雅，如果我们能把渲染过程也抽象出来，那就会好很多，而AnimatedBuilder正是将渲染逻辑分离出来, 上面的build方法中的代码可以改为：\n\n```dart\n@override\nWidget build(BuildContext context) {\n  //return AnimatedImage(animation: animation,);\n    return AnimatedBuilder(\n      animation: animation,\n      child: Image.asset(\"images/avatar.png\"),\n      builder: (BuildContext ctx, Widget child) {\n        return new Center(\n          child: Container(\n              height: animation.value, \n              width: animation.value, \n              child: child,\n          ),\n        );\n      },\n    );\n}\n```\n\n上面的代码中有一个迷惑的问题是，`child`看起来像被指定了两次。但实际发生的事情是：将外部引用`child`传递给`AnimatedBuilder`后`AnimatedBuilder`再将其传递给匿名构造器， 然后将该对象用作其子对象。最终的结果是`AnimatedBuilder`返回的对象插入到widget树中。\n\n也许你会说这和我们刚开始的示例差不了多少，其实它会带来三个好处：\n\n1. 不用显式的去添加帧监听器，然后再调用`setState()` 了，这个好处和`AnimatedWidget`是一样的。\n\n2. 动画构建的范围缩小了，如果没有`builder`，`setState()`将会在父组件上下文中调用，这将会导致父组件的`build`方法重新调用；而有了`builder`之后，只会导致动画widget自身的`build`重新调用，避免不必要的rebuild。\n\n3. 通过`AnimatedBuilder`可以封装常见的过渡效果来复用动画。下面我们通过封装一个`GrowTransition`来说明，它可以对子widget实现放大动画：\n\n   ```dart\n   class GrowTransition extends StatelessWidget {\n     GrowTransition({this.child, this.animation});\n   \n     final Widget child;\n     final Animation<double> animation;\n       \n     Widget build(BuildContext context) {\n       return new Center(\n         child: new AnimatedBuilder(\n             animation: animation,\n             builder: (BuildContext context, Widget child) {\n               return new Container(\n                   height: animation.value, \n                   width: animation.value, \n                   child: child\n               );\n             },\n             child: child\n         ),\n       );\n     }\n   }\n   ```\n\n   这样，最初的示例就可以改为：\n\n   ```dart\n   ...\n   Widget build(BuildContext context) {\n       return GrowTransition(\n       child: Image.asset(\"images/avatar.png\"), \n       animation: animation,\n       );\n   }\n   ```\n\n   **Flutter中正是通过这种方式封装了很多动画，如：FadeTransition、ScaleTransition、SizeTransition等，很多时候都可以复用这些预置的过渡类。**\n\n## 9.2.2 动画状态监听\n\n上面说过，我们可以通过`Animation`的`addStatusListener()`方法来添加动画状态改变监听器。Flutter中，有四种动画状态，在`AnimationStatus`枚举类中定义，下面我们逐个说明：\n\n| 枚举值      | 含义             |\n| ----------- | ---------------- |\n| `dismissed` | 动画在起始点停止 |\n| `forward`   | 动画正在正向执行 |\n| `reverse`   | 动画正在反向执行 |\n| `completed` | 动画在终点停止   |\n\n#### 示例\n\n我们将上面图片放大的示例改为先放大再缩小再放大……这样的循环动画。要实现这种效果，我们只需要监听动画状态的改变即可，即：在动画正向执行结束时反转动画，在动画反向执行结束时再正向执行动画。代码如下：\n\n```dart\n  initState() {\n    super.initState();\n    controller = new AnimationController(\n        duration: const Duration(seconds: 1), vsync: this);\n    //图片宽高从0变到300\n    animation = new Tween(begin: 0.0, end: 300.0).animate(controller);\n    animation.addStatusListener((status) {\n      if (status == AnimationStatus.completed) {\n        //动画执行结束时反向执行动画\n        controller.reverse();\n      } else if (status == AnimationStatus.dismissed) {\n        //动画恢复到初始状态时执行动画（正向）\n        controller.forward();\n      }\n    });\n\n    //启动动画（正向）\n    controller.forward();\n  }\n```\n\n"
  },
  {
    "path": "src/chapter9/hero.md",
    "content": "# 9.4 Hero动画\n\nHero指的是可以在路由(页面)之间“飞行”的widget，简单来说Hero动画就是在路由切换时，有一个共享的widget可以在新旧路由间切换。由于共享的widget在新旧路由页面上的位置、外观可能有所差异，所以在路由切换时会从旧路逐渐过渡到新路由中的指定位置，这样就会产生一个Hero动画。\n\n你可能多次看到过 hero 动画。例如，一个路由中显示待售商品的缩略图列表，选择一个条目会将其跳转到一个新路由，新路由中包含该商品的详细信息和“购买”按钮。 在Flutter中将图片从一个路由“飞”到另一个路由称为**hero动画**，尽管相同的动作有时也称为 **共享元素转换**。下面我们通过一个示例来体验一下hero 动画。\n\n> 为什么要将这种可飞行的共享组件称为hero（英雄），有一种说法是说美国文化中的超人是可以飞的，那是美国人心中的大英雄，还有漫威中的超级英雄基本上都是会飞的，所以Flutter开发人员就对这种“会飞的widget”就起了一个富有浪漫主义的名字hero。当然这种说法并非官方解释，但却很有意思。\n\n#### 示例\n\n假设有两个路由A和B，他们的内容交互如下：\n\nA：包含一个用户头像，圆形，点击后跳到B路由，可以查看大图。\n\nB：显示用户头像原图，矩形；\n\n在AB两个路由之间跳转的时候，用户头像会逐渐过渡到目标路由页的头像上，接下来我们先看看代码，然后再解析：\n\n```dart\n// 路由A\nclass HeroAnimationRoute extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return Container(\n      alignment: Alignment.topCenter,\n      child: InkWell(\n        child: Hero(\n          tag: \"avatar\", //唯一标记，前后两个路由页Hero的tag必须相同\n          child: ClipOval(\n            child: Image.asset(\"images/avatar.png\",\n              width: 50.0,\n            ),\n          ),\n        ),\n        onTap: () {\n          //打开B路由  \n          Navigator.push(context, PageRouteBuilder(\n              pageBuilder: (BuildContext context, Animation animation,\n                  Animation secondaryAnimation) {\n                return new FadeTransition(\n                  opacity: animation,\n                  child: Scaffold(\n                    appBar: AppBar(\n                      title: Text(\"原图\"),\n                    ),\n                    body: HeroAnimationRouteB(),\n                  ),\n                );\n              })\n          );\n        },\n      ),\n    );\n  }\n}\n```\n\n路由B:\n\n```dart\nclass HeroAnimationRouteB extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return Center(\n      child: Hero(\n          tag: \"avatar\", //唯一标记，前后两个路由页Hero的tag必须相同\n          child: Image.asset(\"images/avatar.png\"),\n      ),\n    );\n  }\n}\n```\n\n我们可以看到，实现Hero动画只需要用`Hero`组件将要共享的widget包装起来，并提供一个相同的tag即可，中间的过渡帧都是Flutter Framework自动完成的。必须要注意， 前后路由页的共享`Hero`的tag必须是相同的，Flutter Framework内部正是通过tag来确定新旧路由页widget的对应关系的。\n\nHero动画的原理比较简单，Flutter Framework知道新旧路由页中共享元素的位置和大小，所以根据这两个端点，在动画执行过程中求出过渡时的插值（中间态）即可，而感到幸运的是，这些事情不需要我们自己动手，Flutter已经帮我们做了！\n\n"
  },
  {
    "path": "src/chapter9/index.md",
    "content": "## 简介\n\n精心设计的动画会让用户界面感觉更直观、流畅，能改善用户体验。 Flutter可以轻松实现各种动画类型，对于许多widget，特别是[Material Design widgets](https://flutter.io/docs/reference/widgets/material)，都带有在其设计规范中定义的标准动画效果(但也可以自定义这些效果)。本章将详细介绍Flutter的动画系统，并会通过几个小实例来演示，以帮助开发者迅速理解并掌握动画的开发流程与原理。\n\n## 本章目录\n\n* [9.1：Flutter动画简介](intro.md)\n* [9.2：动画结构](animation_structure.md)\n* [9.3：自定义路由过渡动画](route_transition.md) \n* [9.4：Hero动画](hero.md) \n* [9.5：交织动画](stagger_animation.md) \n* [9.6：通用“动画切换”组件（AnimatedSwitcher）](animated_switcher.md) \n* [9.7：动画过渡组件](animated_widgets.md) \n"
  },
  {
    "path": "src/chapter9/intro.md",
    "content": "# 9.1 Flutter动画简介\n\n\n\n在任何系统的UI框架中，动画实现的原理都是相同的，即：在一段时间内，快速地多次改变UI外观；由于人眼会产生视觉暂留，所以最终看到的就是一个“连续”的动画，这和电影的原理是一样的。我们将UI的一次改变称为一个动画帧，对应一次屏幕刷新，而决定动画流畅度的一个重要指标就是帧率FPS（Frame Per Second），即每秒的动画帧数。很明显，帧率越高则动画就会越流畅！一般情况下，对于人眼来说，动画帧率超过16FPS，就比较流畅了，超过32FPS就会非常的细腻平滑，而超过32FPS，人眼基本上就感受不到差别了。由于动画的每一帧都是要改变UI输出，所以在一个时间段内连续的改变UI输出是比较耗资源的，对设备的软硬件系统要求都较高，所以在UI系统中，动画的平均帧率是重要的性能指标，而在Flutter中，理想情况下是可以实现60FPS的，这和原生应用能达到的帧率是基本是持平的。\n\n### Flutter中动画抽象\n\n为了方便开发者创建动画，不同的UI系统对动画都进行了一些抽象，比如在Android中可以通过XML来描述一个动画然后设置给View。Flutter中也对动画进行了抽象，主要涉及Animation、Curve、Controller、Tween这四个角色，它们一起配合来完成一个完整动画，下面我们一一来介绍它们。\n\n### Animation\n\n`Animation`是一个抽象类，它本身和UI渲染没有任何关系，而它主要的功能是保存动画的插值和状态；其中一个比较常用的`Animation`类是`Animation<double>`。`Animation`对象是一个在一段时间内依次生成一个区间(Tween)之间值的类。`Animation`对象在整个动画执行过程中输出的值可以是线性的、曲线的、一个步进函数或者任何其他曲线函数等等，这由`Curve`来决定。 根据`Animation`对象的控制方式，动画可以正向运行（从起始状态开始，到终止状态结束），也可以反向运行，甚至可以在中间切换方向。`Animation`还可以生成除`double`之外的其他类型值，如：`Animation<Color>` 或` Animation<Size>`。在动画的每一帧中，我们可以通过`Animation`对象的`value`属性获取动画的当前状态值。\n\n#### 动画通知\n\n我们可以通过`Animation`来监听动画每一帧以及执行状态的变化，`Animation`有如下两个方法：\n\n1. `addListener()`；它可以用于给`Animation`添加帧监听器，在每一帧都会被调用。帧监听器中最常见的行为是改变状态后调用`setState()`来触发UI重建。\n2. `addStatusListener()`；它可以给`Animation`添加“动画状态改变”监听器；动画开始、结束、正向或反向（见`AnimationStatus`定义）时会调用状态改变的监听器。\n\n读者在此只需要知道帧监听器和状态监听器的区别，在后面的章节中我们将会举例说明。\n\n### Curve\n\n动画过程可以是匀速的、匀加速的或者先加速后减速等。Flutter中通过`Curve`（曲线）来描述动画过程，我们把匀速动画称为线性的(Curves.linear)，而非匀速动画称为非线性的。\n\n我们可以通过`CurvedAnimation`来指定动画的曲线，如：\n\n```dart\nfinal CurvedAnimation curve =\n    new CurvedAnimation(parent: controller, curve: Curves.easeIn);\n```\n\n`CurvedAnimation`和`AnimationController`（下面介绍）都是`Animation<double>`类型。`CurvedAnimation`可以通过包装`AnimationController`和`Curve`生成一个新的动画对象 ，我们正是通过这种方式来将动画和动画执行的曲线关联起来的。我们指定动画的曲线为` Curves.easeIn`，它表示动画开始时比较慢，结束时比较快。 [Curves](https://docs.flutter.io/flutter/animation/Curves-class.html) 类是一个预置的枚举类，定义了许多常用的曲线，下面列几种常用的：\n\n| Curves曲线 | 动画过程                     |\n| ---------- | ---------------------------- |\n| linear     | 匀速的                       |\n| decelerate | 匀减速                       |\n| ease       | 开始加速，后面减速           |\n| easeIn     | 开始慢，后面快               |\n| easeOut    | 开始快，后面慢               |\n| easeInOut  | 开始慢，然后加速，最后再减速 |\n\n除了上面列举的， [Curves](https://docs.flutter.io/flutter/animation/Curves-class.html) 类中还定义了许多其它的曲线，在此便不一一介绍，读者可以自行查看Curves类定义。\n\n当然我们也可以创建自己Curve，例如我们定义一个正弦曲线：\n\n```dart\nclass ShakeCurve extends Curve {\n  @override\n  double transform(double t) {\n    return math.sin(t * math.PI * 2);\n  }\n}\n```\n\n\n\n### AnimationController\n\n`AnimationController`用于控制动画，它包含动画的启动`forward()`、停止`stop()` 、反向播放 `reverse()`等方法。`AnimationController`会在动画的每一帧，就会生成一个新的值。默认情况下，`AnimationController`在给定的时间段内线性的生成从0.0到1.0（默认区间）的数字。 例如，下面代码创建一个`Animation`对象（但不会启动动画）：\n\n```dart\nfinal AnimationController controller = new AnimationController(\n    duration: const Duration(milliseconds: 2000), vsync: this);\n```\n\n`AnimationController`生成数字的区间可以通过`lowerBound`和`upperBound`来指定，如：\n\n```dart\nfinal AnimationController controller = new AnimationController( \n duration: const Duration(milliseconds: 2000), \n lowerBound: 10.0,\n upperBound: 20.0,\n vsync: this\n);\n```\n\n`AnimationController`派生自`Animation<double>`，因此可以在需要`Animation`对象的任何地方使用。 但是，`AnimationController`具有控制动画的其他方法，例如`forward()`方法可以启动正向动画，`reverse()`可以启动反向动画。在动画开始执行后开始生成动画帧，屏幕每刷新一次就是一个动画帧，在动画的每一帧，会随着根据动画的曲线来生成当前的动画值（`Animation.value`），然后根据当前的动画值去构建UI，当所有动画帧依次触发时，动画值会依次改变，所以构建的UI也会依次变化，所以最终我们可以看到一个完成的动画。 另外在动画的每一帧，`Animation`对象会调用其帧监听器，等动画状态发生改变时（如动画结束）会调用状态改变监听器。\n\n`duration`表示动画执行的时长，通过它我们可以控制动画的速度。\n\n> **注意**： 在某些情况下，动画值可能会超出`AnimationController`的[0.0，1.0]的范围，这取决于具体的曲线。例如，`fling()`函数可以根据我们手指滑动（甩出）的速度(velocity)、力量(force)等来模拟一个手指甩出动画，因此它的动画值可以在[0.0，1.0]范围之外 。也就是说，根据选择的曲线，`CurvedAnimation`的输出可以具有比输入更大的范围。例如，Curves.elasticIn等弹性曲线会生成大于或小于默认范围的值。\n\n#### Ticker\n\n当创建一个`AnimationController`时，需要传递一个`vsync`参数，它接收一个`TickerProvider`类型的对象，它的主要职责是创建`Ticker`，定义如下：\n\n```dart\nabstract class TickerProvider {\n  //通过一个回调创建一个Ticker\n  Ticker createTicker(TickerCallback onTick);\n}\n```\n\nFlutter应用在启动时都会绑定一个`SchedulerBinding`，通过`SchedulerBinding`可以给每一次屏幕刷新添加回调，而`Ticker`就是通过`SchedulerBinding`来添加屏幕刷新回调，这样一来，每次屏幕刷新都会调用`TickerCallback`。使用`Ticker`(而不是`Timer`)来驱动动画会防止屏幕外动画（动画的UI不在当前屏幕时，如锁屏时）消耗不必要的资源，因为Flutter中屏幕刷新时会通知到绑定的`SchedulerBinding`，而`Ticker`是受`SchedulerBinding`驱动的，由于锁屏后屏幕会停止刷新，所以`Ticker`就不会再触发。\n\n通常我们会将`SingleTickerProviderStateMixin`添加到`State`的定义中，然后将State对象作为`vsync`的值，这在后面的例子中可以见到。\n\n### Tween\n\n默认情况下，`AnimationController`对象值的范围是[0.0，1.0]。如果我们需要构建UI的动画值在不同的范围或不同的数据类型，则可以使用`Tween`来添加映射以生成不同的范围或数据类型的值。例如，像下面示例，`Tween`生成[-200.0，0.0]的值：\n\n```dart\nfinal Tween doubleTween = new Tween<double>(begin: -200.0, end: 0.0);\n```\n\n`Tween`构造函数需要`begin`和`end`两个参数。`Tween`的唯一职责就是定义从输入范围到输出范围的映射。输入范围通常为[0.0，1.0]，但这不是必须的，我们可以自定义需要的范围。\n\n`Tween`继承自`Animatable<T>`，而不是继承自`Animation<T>`，`Animatable`中主要定义动画值的映射规则。\n\n下面我们看一个ColorTween将动画输入范围映射为两种颜色值之间过渡输出的例子：\n\n```dart\nfinal Tween colorTween =\n    new ColorTween(begin: Colors.transparent, end: Colors.black54);\n```\n\n\n\n`Tween`对象不存储任何状态，相反，它提供了`evaluate(Animation<double> animation)`方法，它可以获取动画当前映射值。 `Animation`对象的当前值可以通过`value()`方法取到。`evaluate`函数还执行一些其它处理，例如分别确保在动画值为0.0和1.0时返回开始和结束状态。\n\n#### Tween.animate\n\n要使用Tween对象，需要调用其`animate()`方法，然后传入一个控制器对象。例如，以下代码在500毫秒内生成从0到255的整数值。\n\n```dart\nfinal AnimationController controller = new AnimationController(\n    duration: const Duration(milliseconds: 500), vsync: this);\nAnimation<int> alpha = new IntTween(begin: 0, end: 255).animate(controller);\n```\n\n注意`animate()`返回的是一个`Animation`，而不是一个`Animatable`。\n\n以下示例构建了一个控制器、一条曲线和一个Tween：\n\n```dart\nfinal AnimationController controller = new AnimationController(\n    duration: const Duration(milliseconds: 500), vsync: this);\nfinal Animation curve =\n    new CurvedAnimation(parent: controller, curve: Curves.easeOut);\nAnimation<int> alpha = new IntTween(begin: 0, end: 255).animate(curve);\n```\n\n"
  },
  {
    "path": "src/chapter9/route_transition.md",
    "content": "\n# 9.3 自定义路由切换动画\n\n我们在第二章“路由管理”一节中讲过：Material组件库中提供了一个`MaterialPageRoute`组件，它可以使用和平台风格一致的路由切换动画，如在iOS上会左右滑动切换，而在Android上会上下滑动切换。现在，我们如果在Android上也想使用左右切换风格，该怎么做？一个简单的作法是可以直接使用`CupertinoPageRoute`，如：\n\n```dart\n Navigator.push(context, CupertinoPageRoute(  \n   builder: (context)=>PageB(),\n ));\n```\n\n`CupertinoPageRoute`是Cupertino组件库提供的iOS风格的路由切换组件，它实现的就是左右滑动切换。那么我们如何来自定义路由切换动画呢？答案就是`PageRouteBuilder`。下面我们来看看如何使用`PageRouteBuilder`来自定义路由切换动画。例如我们想以渐隐渐入动画来实现路由过渡，实现代码如下：\n\n```dart\nNavigator.push(\n  context,\n  PageRouteBuilder(\n    transitionDuration: Duration(milliseconds: 500), //动画时间为500毫秒\n    pageBuilder: (BuildContext context, Animation animation,\n        Animation secondaryAnimation) {\n      return new FadeTransition(\n        //使用渐隐渐入过渡,\n        opacity: animation,\n        child: PageB(), //路由B\n      );\n    },\n  ),\n);\n```\n\n我们可以看到` pageBuilder` 有一个`animation`参数，这是Flutter路由管理器提供的，在路由切换时` pageBuilder`在每个动画帧都会被回调，因此我们可以通过`animation`对象来自定义过渡动画。\n\n无论是`MaterialPageRoute`、`CupertinoPageRoute`，还是`PageRouteBuilder`，它们都继承自PageRoute类，而`PageRouteBuilder`其实只是`PageRoute`的一个包装，我们可以直接继承`PageRoute`类来实现自定义路由，上面的例子可以通过如下方式实现：\n\n1. 定义一个路由类`FadeRoute`\n\n   ```dart\n   class FadeRoute extends PageRoute {\n     FadeRoute({\n       @required this.builder,\n       this.transitionDuration = const Duration(milliseconds: 300),\n       this.opaque = true,\n       this.barrierDismissible = false,\n       this.barrierColor,\n       this.barrierLabel,\n       this.maintainState = true,\n     });\n   \n     final WidgetBuilder builder;\n   \n     @override\n     final Duration transitionDuration;\n   \n     @override\n     final bool opaque;\n   \n     @override\n     final bool barrierDismissible;\n   \n     @override\n     final Color barrierColor;\n   \n     @override\n     final String barrierLabel;\n   \n     @override\n     final bool maintainState;\n   \n     @override\n     Widget buildPage(BuildContext context, Animation<double> animation,\n         Animation<double> secondaryAnimation) => builder(context);\n   \n     @override\n     Widget buildTransitions(BuildContext context, Animation<double> animation,\n         Animation<double> secondaryAnimation, Widget child) {\n        return FadeTransition( \n          opacity: animation,\n          child: builder(context),\n        );\n     }\n   }\n   ```\n\n2. 使用`FadeRoute`\n\n   ```dart\n   Navigator.push(context, FadeRoute(builder: (context) {\n     return PageB();\n   }));\n   ```\n\n虽然上面的两种方法都可以实现自定义切换动画，但实际使用时应优先考虑使用PageRouteBuilder，这样无需定义一个新的路由类，使用起来会比较方便。但是有些时候`PageRouteBuilder`是不能满足需求的，例如在应用过渡动画时我们需要读取当前路由的一些属性，这时就只能通过继承`PageRoute`的方式了，举个例子，假如我们只想在打开新路由时应用动画，而在返回时不使用动画，那么我们在构建过渡动画时就必须判断当前路由`isActive`属性是否为`true`，代码如下：\n\n```dart\n@override\nWidget buildTransitions(BuildContext context, Animation<double> animation,\n    Animation<double> secondaryAnimation, Widget child) {\n //当前路由被激活，是打开新路由\n if(isActive) {\n   return FadeTransition(\n     opacity: animation,\n     child: builder(context),\n   );\n }else{\n   //是返回，则不应用过渡动画\n   return Padding(padding: EdgeInsets.zero);\n }\n}\n```\n\n关于路由参数的详细信息读者可以自行查阅API文档，比较简单，不再赘述。\n"
  },
  {
    "path": "src/chapter9/stagger_animation.md",
    "content": "\n# 9.5 交织动画\n\n有些时候我们可能会需要一些复杂的动画，这些动画可能由一个动画序列或重叠的动画组成，比如：有一个柱状图，需要在高度增长的同时改变颜色，等到增长到最大高度后，我们需要在X轴上平移一段距离。可以发现上述场景在不同阶段包含了多种动画，要实现这种效果，使用交织动画（Stagger Animation）会非常简单。交织动画需要注意以下几点：\n\n1. 要创建交织动画，需要使用多个动画对象（`Animation`）。\n2. 一个`AnimationController`控制所有的动画对象。\n3. 给每一个动画对象指定时间间隔（Interval）\n\n所有动画都由同一个[AnimationController](https://docs.flutter.io/flutter/animation/AnimationController-class.html)驱动，无论动画需要持续多长时间，控制器的值必须在0.0到1.0之间，而每个动画的间隔（Interval）也必须介于0.0和1.0之间。对于在间隔中设置动画的每个属性，需要分别创建一个[Tween](https://docs.flutter.io/flutter/animation/Tween-class.html) 用于指定该属性的开始值和结束值。也就是说0.0到1.0代表整个动画过程，我们可以给不同动画指定不同的起始点和终止点来决定它们的开始时间和终止时间。\n\n### 示例\n\n下面我们看一个例子，实现一个柱状图增长的动画：\n\n1. 开始时高度从0增长到300像素，同时颜色由绿色渐变为红色；这个过程占据整个动画时间的60%。\n2. 高度增长到300后，开始沿X轴向右平移100像素；这个过程占用整个动画时间的40%。\n\n我们将执行动画的Widget分离出来：\n\n```dart\nclass StaggerAnimation extends StatelessWidget {\n  StaggerAnimation({ Key key, this.controller }): super(key: key){\n    //高度动画\n    height = Tween<double>(\n      begin:.0 ,\n      end: 300.0,\n    ).animate(\n      CurvedAnimation(\n        parent: controller,\n        curve: Interval(\n          0.0, 0.6, //间隔，前60%的动画时间\n          curve: Curves.ease,\n        ),\n      ),\n    );\n\n    color = ColorTween(\n      begin:Colors.green ,\n      end:Colors.red,\n    ).animate(\n      CurvedAnimation(\n        parent: controller,\n        curve: Interval(\n          0.0, 0.6,//间隔，前60%的动画时间\n          curve: Curves.ease,\n        ),\n      ),\n    );\n\n    padding = Tween<EdgeInsets>(\n      begin:EdgeInsets.only(left: .0),\n      end:EdgeInsets.only(left: 100.0),\n    ).animate(\n      CurvedAnimation(\n        parent: controller,\n        curve: Interval(\n          0.6, 1.0, //间隔，后40%的动画时间\n          curve: Curves.ease,\n        ),\n      ),\n    );\n  }\n\n\n  final Animation<double> controller;\n  Animation<double> height;\n  Animation<EdgeInsets> padding;\n  Animation<Color> color;\n\n  Widget _buildAnimation(BuildContext context, Widget child) {\n    return Container(\n      alignment: Alignment.bottomCenter,\n      padding:padding.value ,\n      child: Container(\n        color: color.value,\n        width: 50.0,\n        height: height.value,\n      ),\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return AnimatedBuilder(\n      builder: _buildAnimation,\n      animation: controller,\n    );\n  }\n}\n```\n\n`StaggerAnimation`中定义了三个动画，分别是对`Container`的`height`、`color`、`padding`属性设置的动画，然后通过`Interval`来为每个动画指定在整个动画过程中的起始点和终点。下面我们来实现启动动画的路由：\n\n```dart\nclass StaggerRoute extends StatefulWidget {\n  @override\n  _StaggerRouteState createState() => _StaggerRouteState();\n}\n\nclass _StaggerRouteState extends State<StaggerRoute> with TickerProviderStateMixin {\n  AnimationController _controller;\n\n  @override\n  void initState() {\n    super.initState();\n\n    _controller = AnimationController(\n        duration: const Duration(milliseconds: 2000),\n        vsync: this\n    );\n  }\n\n\n  Future<Null> _playAnimation() async {\n    try {\n      //先正向执行动画\n      await _controller.forward().orCancel;\n      //再反向执行动画\n      await _controller.reverse().orCancel;\n    } on TickerCanceled {\n      // the animation got canceled, probably because we were disposed\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return  GestureDetector(\n      behavior: HitTestBehavior.opaque,\n      onTap: () {\n        _playAnimation();\n      },\n      child: Center(\n        child: Container(\n          width: 300.0,\n          height: 300.0,\n          decoration: BoxDecoration(\n            color: Colors.black.withOpacity(0.1),\n            border: Border.all(\n              color:  Colors.black.withOpacity(0.5),\n            ),\n          ),\n          //调用我们定义的交织动画Widget\n          child: StaggerAnimation(\n              controller: _controller\n          ),\n        ),\n      ),\n    );\n  }\n}\n```\n执行效果如图，点击图9-3灰色矩形，就可以看到整个动画效果，图9-4是动画执行过程中的一帧。\n\n![图9-3](../imgs/9-3.png)![图9-4](../imgs/9-4.png)\n\n"
  },
  {
    "path": "src/gitbook/pub.js",
    "content": "!function(t,n){for(var r in n)t[r]=n[r]}(window,function(t){function n(o){if(r[o])return r[o].exports;var e=r[o]={i:o,l:!1,exports:{}};return t[o].call(e.exports,e,e.exports,n),e.l=!0,e.exports}var r={};return n.m=t,n.c=r,n.i=function(t){return t},n.d=function(t,r,o){n.o(t,r)||Object.defineProperty(t,r,{configurable:!1,enumerable:!0,get:o})},n.n=function(t){var r=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(r,\"a\",r),r},n.o=function(t,n){return Object.prototype.hasOwnProperty.call(t,n)},n.p=\"\",n(n.s=2)}([function(t,n,r){\"use strict\";function o(t,n){var r={};for(var o in t)r[o]=t[o];return r.target=r.currentTarget=n,r}function e(t){function n(n){return function(){var r=this.hasOwnProperty(n+\"_\")?this[n+\"_\"]:this.xhr[n],o=(t[n]||{}).getter;return o&&o(r,this)||r}}function r(n){return function(r){var e=this.xhr,i=this,u=t[n];if(\"on\"===n.substring(0,2))i[n+\"_\"]=r,e[n]=function(u){u=o(u,i),t[n]&&t[n].call(i,e,u)||r.call(i,u)};else{var c=(u||{}).setter;r=c&&c(r,i)||r,this[n+\"_\"]=r;try{e[n]=r}catch(t){}}}}function e(n){return function(){var r=[].slice.call(arguments);if(t[n]){var o=t[n].call(this,r,this.xhr);if(o)return o}return this.xhr[n].apply(this.xhr,r)}}return window[c]=window[c]||XMLHttpRequest,XMLHttpRequest=function(){var t=new window[c];for(var o in t){var i=\"\";try{i=u(t[o])}catch(t){}\"function\"===i?this[o]=e(o):Object.defineProperty(this,o,{get:n(o),set:r(o),enumerable:!0})}var f=this;t.getProxy=function(){return f},this.xhr=t},window[c]}function i(){window[c]&&(XMLHttpRequest=window[c]),window[c]=void 0}Object.defineProperty(n,\"__esModule\",{value:!0});var u=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&\"function\"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?\"symbol\":typeof t};n.configEvent=o,n.hook=e,n.unHook=i;var c=\"_rxhr\"},,function(t,n,r){\"use strict\";Object.defineProperty(n,\"__esModule\",{value:!0}),n.ah=void 0;var o=r(0);n.ah={hook:o.hook,unHook:o.unHook}}]));\n\nvar _hmt = _hmt || [];\n$(\"#bd\").remove();\n(function () {\n    var hm = document.createElement(\"script\");\n    hm.src = \"https://hm.baidu.com/hm.js?170231fea4f81697eb046edc1a91fe5b\";\n    var s = document.getElementsByTagName(\"script\")[0];\n    hm.id = \"bd\"\n    s.parentNode.insertBefore(hm, s);\n})();\nvar timer;\nfunction init() {\n    var p = location.pathname;\n    if (p[p.length - 1] === '/') {\n        p += \"index.md\"\n    } else {\n        p = p.split(\".\")[0] + \".md\";\n    }\n    p = \"https://github.com/flutterchina/flutter-in-action/blob/master/docs\" + p;\n    $(\".pull-right .fa-edit\").parent(\"a\").attr(\"href\", p);\n    $(\"table\").wrap(\"<div style='overflow: auto'></div>\");\n    //百度统计\n    var e = /([http|https]:\\/\\/[a-zA-Z0-9\\_\\.]+\\.baidu\\.com)/gi, r = window.location.href,\n        t = document.referrer;\n    if (!e.test(r)) {\n        var o = \"https://sp0.baidu.com/9_Q4simg2RQJ8t7jm9iCKT-xh_/s.gif\";\n        t ? (o += \"?r=\" + encodeURIComponent(document.referrer), r && (o += \"&l=\" + r)) : r && (o += \"?l=\" + r);\n        var i = new Image;\n        i.src = o\n    }\n    $(\".copyright,.maoyun\").remove();\n    $(\"<div class='copyright'> 版权所有，禁止私自转发、克隆网站。</div><div style='text-align: center' class='f-links'><a onclick='buy(\\\"link\\\")' href='https://item.jd.com/12816296.html' title='点击购买' target='_blank' > 购买实体书 </a> | <a  href='https://flutterchina.club/docs'> Flutter中文网 </a></div>\").appendTo(\".page-inner\");\n    $(\"<div style='text-align: center' class='maoyun'> <span style='position: relative; top: -3px; left: -4px'>感谢</span><a href='https://www.maoyuncloud.com/' target='_blank'><img src=//pcdn.flutterchina.club/imgs/maoyun.png height='20'></a></div>\").appendTo(\".page-inner\");\n}\n\nfunction _track(p,url) {\n    _hmt.push(['_trackEvent', 'ad', 'click', p]);\n    setTimeout(function () {\n        location.href = url\n    }, 100);\n}\n\nfunction buy(p){\n    _hmt.push(['_trackEvent', 'buy', 'click', p]);\n}\n\ninit();\n\nah.hook({\n    open: function (arg, xhr) {\n        if (location.hostname !== 'localhost') {\n            if (arg[1][0] === '.') arg[1] = arg[1].slice(1);\n            arg[1] = \"https://pcdn.flutterchina.club\" + arg[1].replace(\".html\", \".1\")\n        }\n    },\n    setRequestHeader: function (arg) {\n        if (arg[0] !== 'Accept') return true;\n    },\n    onload:function(xhr){\n        setTimeout(function () {\n            if ( $(\"#book-search-results .ad\").length === 0) {\n                $(\".ad\").clone().show().prependTo(\"#book-search-results\")\n            }\n            var extension=xhr.responseURL.split(\".\").pop()\n            if(extension!=='json'){\n                console.log(\"jump:\"+location.href)\n                _hmt.push(['_trackPageview', location.pathname]);\n                init()\n            }\n        });\n    }\n})\n"
  },
  {
    "path": "src/img_des.txt",
    "content": "1-1  Flutter框架图\n1-2  Flutter安装包下载\n1-3  Android Studio工具栏\n1-4  应用首页\n1-5  登录Xcode\n1-6  添加信任\n1-7  验证bundle id是否唯一\n1-8  缺少依赖报错\n1-9  安装依赖\n1-10 下载依赖失败\n\n2-1  计数器示例\n2-2  添加打开新路由按钮\n2-3  新路由页\n2-4  路由传参示例\n2-5  Pub上的包信息\n2-6  在.yaml中添加包依赖\n2-7  热重载\n2-8  设置APP图标-Android\n2-9  设置APP图标-iOS\n2-10 应用启动页\n2-11 Xcode中设置启动页\n2-12 Dart单线程模型\n\n3-1  StatelessWidget示例\n3-1-1  通过Context查找父Widget\n3-1-2  显示显示SnackBar\n3-2  StatefulWidget生命周期图\n3-3  Cupertino组件示例\n3-4  状态管理示例\n3-5  Text示例\n3-6  Text居中对齐示例\n3-7  TextStyle示例\n3-8  TextSpan示例\n3-9  DefaultTextStyle示例\n3-10 RaisedButton示例\n3-11 FlatButton示例\n3-12 OutlineButton示例\n3-13 IconButton示例\n3-14 带图标的按钮\n3-15 两边圆角的按钮\n3-16 两边圆角、带阴影的按钮\n3-17 Image示例\n3-18 Image各种不同的fit效果示例\n3-19 Image colorBlendMode效果示例\n3-20 Image repeat效果示例\n3-21 字体图标示例\n3-22 自定义字体图标示例\n3-23 单选、复选框示例\n3-24 Android键盘搜索模式\n3-25 登录输入框示例\n3-26 输入框内容选中示例\n3-27 输入框焦点控制示例\n3-28 自定义输入框样式示例\n3-29 表单预验证示例\n3-30 LinearProgressIndicator示例\n3-31 CircularProgressIndicator示例\n3-32 自定义进度指示器尺寸示例\n3-33 椭圆形进度指示器示例\n\n\n4-1  线性布局示例\n4-2  Column示例\n4-3  Column嵌套示例\n4-4  Column和Expanded组件示例\n4-5  弹性布局示例\n4-6  溢出示例\n4-7  Wrap示例\n4-8  Flow示例\n4-9  Stack、Positioned示例（一）\n4-10 Stack、Positioned示例（二）\n4-11 Align示例\n4-12 Alignment效果示例\n4-13 FractionalOffset效果示例\n4-14 缩放因子效果对比\n\n\n5-1  Padding示例\n5-2  ConstrainedBox示例\n5-3  SizedBox示例\n5-4  多重限制示例（一）\n5-5  多重限制示例（二）\n5-6  UnconstrainedBox示例\n5-7  导航栏自定义Loading大小（一）\n5-8  导航栏自定义Loading大小（二）\n5-9  DecoratedBox示例\n5-10 Transform倾斜变换示例\n5-11 Transform平移变换示例\n5-12 Transform旋转变换示例\n5-13 Transform缩放变换示例\n5-14 Transform变换不影响组件位置\n5-15 RotatedBox示例\n5-16 Container示例\n5-17 Padding和Margin对比示例\n5-18 包含顶部和底部导航的主页\n5-19 抽屉菜单\n5-20 自定义菜单图标\n5-21 TabBar示例\n5-22 完整Tab示例\n5-23 \"打洞\"效果的底部导航栏示例\n5-24 剪裁效果示例\n5-25 自定义剪裁区域示例\n\n\n6-1  SingleChildScrollView示例\n6-2  ListView.builder示例\n6-3  ListView.separated示例\n6-4  加载更多\n6-5  没有更多\n6-6  添加列表头\n6-7  指定列表高度\n6-8  动态计算列表高度\n6-9  GridView示例一\n6-10 GridView示例二\n6-11 StaggeredGridView\n6-12 CustomScrollView示例\n6-13 CustomScrollView示例\n6-14 未显示回到顶部按钮\n6-15 显示返回顶部按钮\n6-16 监听滚动通知\n\n7-1  InheritedWidget版的计数器示例\n7-2  Provider示例\n7-3  Provider原理图\n7-4  前景色自适应的NavBar\n7-5  MaterialColor示例\n7-6  青色主题\n7-7  蓝色主题\n7-8  加载中\n7-9  加载成功\n7-10 删除确认对话框\n7-11 SimpleDialog示例\n7-12 Dialog示例\n7-13 自定义对话框样式\n7-14 带复选框的对话框\n7-15 复选框可选中\n7-16 Material风格的底部菜单列表模态对话框\n7-17 showBottomSheet示例\n7-18 Loading框\n7-19 Loading框(自定义宽度)\n7-20 Material风格的日历选择框\n7-21 iOS风格的日历选择框\n\n8-1  Listener示例\n8-2  手势检测（点击、双击、长按）示例\n8-3  拖动（任意方向）示例\n8-4  缩放示例\n8-5  GestureRecognizer示例\n8-6  Notification示例\n\n9-1  放大动画（一）\n9-2  放大动画（二）\n9-3  交织动画（一）\n9-4  交织动画的一帧\n9-5  左出右进\n9-6  左出右进\n9-7  上入下出\n9-8  AnimatedDecoratedBox点击前\n9-9  AnimatedDecoratedBox过渡中的一帧\n9-10 动画过渡组件示例\n\n\n10-1  渐变按钮示例\n10-2  TurnBox示例\n10-3  五子棋盘示例\n\n11-1  请求百度首页\n11-2  Websocket示例\n11-3  Socket示例\n11-4  IDE错误提示\n\n12-1  创建Package工程\n12-2  Package目录结构\n12-3  MessageChannel\n12-4  相机示例\n12-5  Platform View 示例\n\n13-1  Android系统语言设置\n\n14-0  Flutter中的三棵树\n14-1  自定义UI框架（一）\n14-2  自定义UI框架（二）\n14-3  布局溢出提示\n14-4  MyImage示例\n\n15-1 主页（未登录）\n15-2 主页（已登录）\n15-3 抽屉菜单（未登录）\n15-4 抽屉菜单（已登录）\n15-5 登录页\n15-6 语言选择页（中文简体）\n15-7 语言选择页（英文）\n15-8 主题切换页\n\n"
  },
  {
    "path": "src/imgs/index.md",
    "content": "\n# 前言\n\n本书是第一本系统介绍Flutter技术的中文书籍，它是Flutter中文社区发起的开源项目之一，旨在帮助开发者入门，系统地、循序渐进的了解Flutter。\n\n> 本书官网访问量较大，由于服务器配置有限，所以某些时段访问本站可能略有延迟，如果延迟较久，读者可以直接在[Github上阅读本书](https://github.com/flutterchina/flutter-in-action/blob/master/docs/SUMMARY.md)。\n\n\n## 缘起\n\n在全球，随着Flutter被越来越多的知名公司应用在自己的商业APP中，Flutter这门新技术也逐渐进入了移动开发者的视野，尤其是当Google在2018年IO大会上发布了第一个Preview版本后，国内刮起来一股学习Flutter的热潮。\n\n在Flutter发布之初，当时，我在看完Flutter原理介绍后，就对它产生了浓厚的兴趣。当时笔者身边也一些人比较关注Flutter，我也被经常问到关于Flutter的一些问题，比如Flutter怎么样？和RN有什么区别？Flutter为什么要用Dart？当时也听到了一些批评的声音，比如有些人说Flutter只是重复造轮子，没啥亮点、Flutter最大的缺点就是使用了Dart语言等。在听到这些问题及论调后，我深知这是对Flutter的不了解而造成的，这和当时国内缺乏Flutter中文文档和教程有直接关系，很多人对Flutter的了解都只停留在Google的发布会介绍（有中文翻译）。\n\n在笔者深入的了解Flutter后，深知Flutter必将是一个会改变移动开发格局的里程碑级作品，它从设计之初就对性能和开发效率兼顾，并且借鉴了React（一个Web开发框架）的响应式的UI框架设计思想等，总之，很难用一两句话说完Flutter的优点，同时我也很快成为了Flutter的路转粉。\n\n为了更好的帮助中国开发者了解这门新技术，我在2018年初开始翻译Flutter官网文档，同年4月份上线了[Flutter中文网](https://flutterchina.club/)，上线后反响很强烈，Flutter中文网也很快被传播开，百度搜索排名迅速蹿升到前三，截止目前，Flutter中文官网日PV在7万左右，每日独立访问人数近一万多。\n\n虽然Flutter中文网给中国开发者提供了很好的第一手了解Flutter的资料，但是笔者还会经常遇到一些对Flutter技术处于围观而不愿尝试的开发者。这主要是因为当时Flutter在国内没有成功案例，再加上新技术都有学习成本，所以即使有文档，也会有一些开发者犹豫是否来学习。为了解决这部分开发者的疑虑，我就想如果能用Flutter开发一个完整的APP发布到应用商店，这样开发者就可以在犹豫的时候可以先实际感受一下Flutter应用，这样有个直观的了解后，就会容易做出判断，为此，我开发了[Gitme](https://flutterchina.club/app/gm.html)，它是一个Github客户端，它支持了源码浏览、Issue、Label等Github的大多数功能，到目前为止，通过Gitme登录过Github账号的用户有8000多人，日活用户有1000人。更重要的是，有很多人正是用了Gitme后，才来学Flutter的。\n\n无论是做Flutter中文网，还是写Gitme，主要目标都是帮助开发者学习Flutter，同时消除围观开发者疑虑。但当开发者们真正开始动手时，Flutter的生态问题就变得尤为突出。由于在2018年初Flutter刚起步时，很多基础的包和库都是空白，少数已有的一些库也大都是预览版（未到1.0），存在很多bug。这个状况不是一两个人花一两天能搞定的，这是需要聚整个Flutter开发者社群之力，耗费数年时间才可能有所改善。因此，在2018年4月份，我以Flutter中文网名义发起了Flutter开源计划，该计划主要是开发一些常用的包来丰富Flutter生态，帮助开发者提高开发效率。自在github建立[Flutter中文开发者社区账号](https://github.com/flutterchina)以来，前后开源了dio、cookieJar、flukit等多个项目，而dio在开源两周后，就迅速成为Flutter第三方包中Star排名第一的开源库。\n\n虽然做的事情已经够多了，但是仍有一些很有必要去做的事情，由于时间原因，一直被搁置。\n\n随着学习Flutter的人越来越多，一部分开发者通过查看官网的文档就能入门，但也有很多开发者感觉学习时仍然有些吃力，主要原因有两个，首先官网的文档主要是为了引导开发者快速上手的，讲的并不是很细；其次是我们虽然翻译了官方文档，但是对于Flutter SDK文档并没有翻译，而在开发中遇到的一些具体问题通常都得去查看SDK文档。所以，要解决这两个问题，必须得有一个系统化的Flutter教程，它不仅可以快速引导开发者入门，而且也能触及到一些细节和原理，最好也能提供一些学习和研究Flutter的方法。因此，如果能有一本能系统地介绍Flutter的书籍便是便是非常棒的！但是，当时没有一本关于Flutter的中文书籍，因此，我便计划写一本能由浅入深、系统介绍Flutter的书。2018年12月，《Flutter实战》完成初稿，并在Github上开源，同时上线了[《Flutter实战》电子书官网](https://book.flutterchina.club/) ，至今每天有3000多人在线浏览本书。那为什么不直接出版？如果直接出版，不仅有稿费，而且也能保护知识产权，而直接开源，不就只能当雷锋了？其实，无论是做中文网、写Gitme、做Flutter开源项目，我的出发点都是为了能帮助中国开发者了解、学习Flutter，而这是一件非常有意义的事，只要坚持做对别人有价值的事，那么上帝迟早会奖赏你；当然我们也在网站中留了打赏的按钮，如果读者觉得有帮助，可以扫码打赏，请笔者喝一杯咖啡。另外由于成书比较仓促，当时书中也有很多错误，开源后，有很多读者提PR来纠正书中的错别字，时至今日，有78名开发者为本书提过PR，我在此，衷心的感谢你们。\n\n起初，我没有出版实体书的打算，当时我以为开发者直接通过在线访问本书官网即方便又不用付费，何乐而不为。但在本书上线后，很多读者来添加微信好友，表示非常有收益，很期待出版纸质书，甚至有比较热心的读者想提前付定金预定！我理解，这是大家对我所做之事的认可和鼓励。考虑到，确实有一部分读者，尤其是还没毕业的同学，可能更喜欢通过书籍去学习，为此，我已经和机械工业出版社合作，目前本书纸质版正在出版中，敬请关注。\n\n### 本书组织结构\n\n本书采用由浅入深的方式介绍Flutter技术原理，分为三篇，总计15章，各篇的主要内容如下。\n\n- 第一篇，入门篇（第1章~第7章），包括Flutter技术的出现背景和简介、Flutter的各种类型的Widget以及如何构建UI。通过学习本章，读者可以掌握如何使用Flutter来构建UI界面。\n- 第二篇，进阶篇（第8章~第14章），包括Flutter中的事件机制、动画、自定义组件、文件和网络、插件、国际化以及Flutter核心原理等。通过本章内容，读者可以对Flutter整体构建及原理有一个深入的认识。\n- 第三篇，实例篇（第15章），本章主要通过一个简版的Github APP来将前面介绍的内容串起来，让开发者对一个完整的Flutter APP开发流有个了解。\n\n## 本书特色\n\n笔者在大学时读过候捷（真名侯俊杰）写的一些C++相关书籍，在他的《深入浅出MFC》一书中，有一句话我印象像非常深 “唯有深入，方能浅出”。我非常认同这句话，对于一门技术，只有了解的深入，才能用最浅显、通俗的话语描述出。我在写作本书时，深入浅出就是一个主要目标。所以，本书的目标不仅是想告诉读者如何使用Flutter，而且也非常关注各个知识点的底层实现以及设计思想。从本书章节划分上来看，入门篇为“浅出”，进阶篇则是“深入”。另外由于PC客户端开发、移动开发、Web开发这些经验我都有，而Flutter本质上是一个UI系统，而UI系统的设计和实现在”大前端“下有很多相通之处，所以在本书中的一些知识点我也会对比一些其他UI系统（主要是Android或Web）相应的实现，便于有相关开发经验的读者对比理解。\n\n## 本书读者对象\n\n- 读者至少熟悉一种编程语言。\n- 读者最好接触过PC客户端、移动开发或Web前端开发中的一种。\n- 本书不适合做为编程的入门读物。\n\n## 关于随书源码\n\n由于篇幅所限，本书中大多数示例代码都只是部分核心代码，读者可以去[这里]( https://github.com/wendux/flutter_in_action_source_code )查看下载。\n\n## 致谢\n\n感谢一直以来支持Flutter中文网、Flutter开源项目的人以及所有对本书提过PR的人，正是因为有你们，才有这本书。另外尤其感谢给本书打赏过的同学，你们的支持给了我很大的鼓励。\n\n\n## 权益\n\n最后，知识是应该付费的，创作不易，开源不等于免费，如果您是本书读者并手头宽裕，可以点击下面打赏按钮打赏；当然，如果您囊中羞涩，您也可以阅读本书，但我对您有个小小的要求，希望您在阅读的过程中能积极参与到本书的纠错以及未完成内容的创作上来，也算是有所付出。\n\n近来在网上发现很多**原封不动复制本书**的镜像网站和大量复制或**引用了本书但未注明出处**的博客、文章甚至书籍；对此，笔者在此声明，本书著作权归wendux所有，任何组织或个人在未经授权的情况下复制、拷贝、抄袭本书用于商业目的，笔者保留追究其法律责任的权利。如果是非商业目的的转载和引用，请注明出处并附上本书网址。另外如有出版机构愿意为本书出版实体书或者想转载本书内容，亦或是想合作，请加微信Demons-du.\n\n### 勘误\n\n由于Flutter SDK在不断更新，本书中的部分内容（如类的继承关系、参数等）可能会和新版本的Flutter不一致，读者以最新的Flutter SDK为准。另外，由于时间仓促，书中难免有错误之处，如果您发现本书中的错误，欢迎点击右上角的”编辑按钮“，提PR。如果您想一起参与本书创作，可以参考[《Flutter实战》贡献指南](https://github.com/flutterchina/flutter-in-action#%E8%B4%A1%E7%8C%AE%E9%A1%BB%E7%9F%A5)。\n\n\n\n\n\n"
  },
  {
    "path": "src/index.md",
    "content": "\n# 前言\n\n本书是第一本系统介绍Flutter技术的中文书籍，它是Flutter中文社区发起的开源项目之一，旨在帮助开发者入门，系统地、循序渐进的了解Flutter。\n\n> 本书官网访问量较大，由于服务器配置有限，所以某些时段访问本站可能略有延迟，如果延迟较久，读者可以直接在[Github上阅读本书](https://github.com/flutterchina/flutter-in-action/blob/master/docs/SUMMARY.md)。\n\n\n## 缘起\n\n在全球，随着Flutter被越来越多的知名公司应用在自己的商业APP中，Flutter这门新技术也逐渐进入了移动开发者的视野，尤其是当Google在2018年IO大会上发布了第一个Preview版本后，国内刮起来一股学习Flutter的热潮。\n\n在Flutter发布之初，当时，我在看完Flutter原理介绍后，就对它产生了浓厚的兴趣。当时笔者身边也一些人比较关注Flutter，我也被经常问到关于Flutter的一些问题，比如Flutter怎么样？和RN有什么区别？Flutter为什么要用Dart？当时也听到了一些批评的声音，比如有些人说Flutter只是重复造轮子，没啥亮点、Flutter最大的缺点就是使用了Dart语言等。在听到这些问题及论调后，我深知这是对Flutter的不了解而造成的，这和当时国内缺乏Flutter中文文档和教程有直接关系，很多人对Flutter的了解都只停留在Google的发布会介绍（有中文翻译）。\n\n在笔者深入的了解Flutter后，深知Flutter必将是一个会改变移动开发格局的里程碑级作品，它从设计之初就对性能和开发效率兼顾，并且借鉴了React（一个Web开发框架）的响应式的UI框架设计思想等，总之，很难用一两句话说完Flutter的优点，同时我也很快成为了Flutter的路转粉。\n\n为了更好的帮助中国开发者了解这门新技术，我在2018年初开始翻译Flutter官网文档，同年4月份上线了[Flutter中文网](https://flutterchina.club/)，上线后反响很强烈，Flutter中文网也很快被传播开，百度搜索排名迅速蹿升到前三，截止目前，Flutter中文官网日PV在7万左右，每日独立访问人数近一万多。\n\n虽然Flutter中文网给中国开发者提供了很好的第一手了解Flutter的资料，但是笔者还会经常遇到一些对Flutter技术处于围观而不愿尝试的开发者。这主要是因为当时Flutter在国内没有成功案例，再加上新技术都有学习成本，所以即使有文档，也会有一些开发者犹豫是否来学习。为了解决这部分开发者的疑虑，我就想如果能用Flutter开发一个完整的APP发布到应用商店，这样开发者就可以在犹豫的时候可以先实际感受一下Flutter应用，这样有个直观的了解后，就会容易做出判断，为此，我开发了[Gitme](https://flutterchina.club/app/gm.html)，它是一个Github客户端，它支持了源码浏览、Issue、Label等Github的大多数功能，到目前为止，通过Gitme登录过Github账号的用户有8000多人，日活用户有1000人。更重要的是，有很多人正是用了Gitme后，才来学Flutter的。\n\n无论是做Flutter中文网，还是写Gitme，主要目标都是帮助开发者学习Flutter，同时消除围观开发者疑虑。但当开发者们真正开始动手时，Flutter的生态问题就变得尤为突出。由于在2018年初Flutter刚起步时，很多基础的包和库都是空白，少数已有的一些库也大都是预览版（未到1.0），存在很多bug。这个状况不是一两个人花一两天能搞定的，这是需要聚整个Flutter开发者社群之力，耗费数年时间才可能有所改善。因此，在2018年4月份，我以Flutter中文网名义发起了Flutter开源计划，该计划主要是开发一些常用的包来丰富Flutter生态，帮助开发者提高开发效率。自在github建立[Flutter中文开发者社区账号](https://github.com/flutterchina)以来，前后开源了dio、cookieJar、flukit等多个项目，而dio在开源两周后，就迅速成为Flutter第三方包中Star排名第一的开源库。\n\n虽然做的事情已经够多了，但是仍有一些很有必要去做的事情，由于时间原因，一直被搁置。\n\n随着学习Flutter的人越来越多，一部分开发者通过查看官网的文档就能入门，但也有很多开发者感觉学习时仍然有些吃力，主要原因有两个，首先官网的文档主要是为了引导开发者快速上手的，讲的并不是很细；其次是我们虽然翻译了官方文档，但是对于Flutter SDK文档并没有翻译，而在开发中遇到的一些具体问题通常都得去查看SDK文档。所以，要解决这两个问题，必须得有一个系统化的Flutter教程，它不仅可以快速引导开发者入门，而且也能触及到一些细节和原理，最好也能提供一些学习和研究Flutter的方法。因此，如果能有一本能系统地介绍Flutter的书籍便是便是非常棒的！但是，当时没有一本关于Flutter的中文书籍，因此，我便计划写一本能由浅入深、系统介绍Flutter的书。2018年12月，《Flutter实战》完成初稿，并在Github上开源，同时上线了[《Flutter实战》电子书官网](https://book.flutterchina.club/) ，至今每天有3000多人在线浏览本书。那为什么不直接出版？如果直接出版，不仅有稿费，而且也能保护知识产权，而直接开源，不就只能当雷锋了？其实，无论是做中文网、写Gitme、做Flutter开源项目，我的出发点都是为了能帮助中国开发者了解、学习Flutter，而这是一件非常有意义的事，只要坚持做对别人有价值的事，那么上帝迟早会奖赏你；当然我们也在网站中留了打赏的按钮，如果读者觉得有帮助，可以扫码打赏，请笔者喝一杯咖啡。另外由于成书比较仓促，当时书中也有很多错误，开源后，有很多读者提PR来纠正书中的错别字，时至今日，有78名开发者为本书提过PR，我在此，衷心的感谢你们。\n\n起初，我没有出版实体书的打算，当时我以为开发者直接通过在线访问本书官网即方便又不用付费，何乐而不为。但在本书上线后，很多读者来添加微信好友，表示非常有收益，很期待出版纸质书，甚至有比较热心的读者想提前付定金预定！我理解，这是大家对我所做之事的认可和鼓励。考虑到，确实有一部分读者，尤其是还没毕业的同学，可能更喜欢通过书籍去学习，为此，我已经和机械工业出版社合作，目前本书纸质版正在出版中，敬请关注。\n\n### 本书组织结构\n\n本书采用由浅入深的方式介绍Flutter技术原理，分为三篇，总计15章，各篇的主要内容如下。\n\n- 第一篇，入门篇（第1章~第7章），包括Flutter技术的出现背景和简介、Flutter的各种类型的Widget以及如何构建UI。通过学习本章，读者可以掌握如何使用Flutter来构建UI界面。\n- 第二篇，进阶篇（第8章~第14章），包括Flutter中的事件机制、动画、自定义组件、文件和网络、插件、国际化以及Flutter核心原理等。通过本章内容，读者可以对Flutter整体构建及原理有一个深入的认识。\n- 第三篇，实例篇（第15章），本章主要通过一个简版的Github APP来将前面介绍的内容串起来，让开发者对一个完整的Flutter APP开发流有个了解。\n\n## 本书特色\n\n笔者在大学时读过候捷（真名侯俊杰）写的一些C++相关书籍，在他的《深入浅出MFC》一书中，有一句话我印象像非常深 “唯有深入，方能浅出”。我非常认同这句话，对于一门技术，只有了解的深入，才能用最浅显、通俗的话语描述出。我在写作本书时，深入浅出就是一个主要目标。所以，本书的目标不仅是想告诉读者如何使用Flutter，而且也非常关注各个知识点的底层实现以及设计思想。从本书章节划分上来看，入门篇为“浅出”，进阶篇则是“深入”。另外由于PC客户端开发、移动开发、Web开发这些经验我都有，而Flutter本质上是一个UI系统，而UI系统的设计和实现在”大前端“下有很多相通之处，所以在本书中的一些知识点我也会对比一些其他UI系统（主要是Android或Web）相应的实现，便于有相关开发经验的读者对比理解。\n\n## 本书读者对象\n\n- 读者至少熟悉一种编程语言。\n- 读者最好接触过PC客户端、移动开发或Web前端开发中的一种。\n- 本书不适合做为编程的入门读物。\n\n## 关于随书源码\n\n由于篇幅所限，本书中大多数示例代码都只是部分核心代码，读者可以去[这里]( https://github.com/wendux/flutter_in_action_source_code )查看下载。\n\n## 致谢\n\n感谢一直以来支持Flutter中文网、Flutter开源项目的人以及所有对本书提过PR的人，正是因为有你们，才有这本书。另外尤其感谢给本书打赏过的同学，你们的支持给了我很大的鼓励。\n\n\n## 权益\n\n最后，知识是应该付费的，创作不易，开源不等于免费，如果您是本书读者并手头宽裕，可以点击下面打赏按钮打赏；当然，如果您囊中羞涩，您也可以阅读本书，但我对您有个小小的要求，希望您在阅读的过程中能积极参与到本书的纠错以及未完成内容的创作上来，也算是有所付出。\n\n近来在网上发现很多**原封不动复制本书**的镜像网站和大量复制或**引用了本书但未注明出处**的博客、文章甚至书籍；对此，笔者在此声明，本书著作权归wendux所有，任何组织或个人在未经授权的情况下复制、拷贝、抄袭本书用于商业目的，笔者保留追究其法律责任的权利。如果是非商业目的的转载和引用，请注明出处并附上本书网址。另外如有出版机构愿意为本书出版实体书或者想转载本书内容，亦或是想合作，请加微信Demons-du.\n\n### 勘误\n\n由于Flutter SDK在不断更新，本书中的部分内容（如类的继承关系、参数等）可能会和新版本的Flutter不一致，读者以最新的Flutter SDK为准。另外，由于时间仓促，书中难免有错误之处，如果您发现本书中的错误，欢迎点击右上角的”编辑按钮“，提PR。如果您想一起参与本书创作，可以参考[《Flutter实战》贡献指南](https://github.com/flutterchina/flutter-in-action#%E8%B4%A1%E7%8C%AE%E9%A1%BB%E7%9F%A5)。\n\n\n\n\n\n"
  },
  {
    "path": "src/intro.md",
    "content": "\n# 前言\n\n本书是第一本系统介绍Flutter技术的中文书籍，它是Flutter中文社区发起的开源项目之一，旨在帮助开发者入门，系统地、循序渐进的了解Flutter。\n\n> 本书官网访问量较大，由于服务器配置有限，所以某些时段访问本站可能略有延迟，如果延迟较久，读者可以直接在[Github上阅读本书](https://github.com/flutterchina/flutter-in-action/blob/master/docs/SUMMARY.md)。\n\n\n## 缘起\n\n在全球，随着Flutter被越来越多的知名公司应用在自己的商业APP中，Flutter这门新技术也逐渐进入了移动开发者的视野，尤其是当Google在2018年IO大会上发布了第一个Preview版本后，国内刮起来一股学习Flutter的热潮。\n\n在Flutter发布之初，当时，我在看完Flutter原理介绍后，就对它产生了浓厚的兴趣。当时笔者身边也一些人比较关注Flutter，我也被经常问到关于Flutter的一些问题，比如Flutter怎么样？和RN有什么区别？Flutter为什么要用Dart？当时也听到了一些批评的声音，比如有些人说Flutter只是重复造轮子，没啥亮点、Flutter最大的缺点就是使用了Dart语言等。在听到这些问题及论调后，我深知这是对Flutter的不了解而造成的，这和当时国内缺乏Flutter中文文档和教程有直接关系，很多人对Flutter的了解都只停留在Google的发布会介绍（有中文翻译）。\n\n在笔者深入的了解Flutter后，深知Flutter必将是一个会改变移动开发格局的里程碑级作品，它从设计之初就对性能和开发效率兼顾，并且借鉴了React（一个Web开发框架）的响应式的UI框架设计思想等，总之，很难用一两句话说完Flutter的优点，同时我也很快成为了Flutter的路转粉。\n\n为了更好的帮助中国开发者了解这门新技术，我在2018年初开始翻译Flutter官网文档，同年4月份上线了[Flutter中文网](https://flutterchina.club/)，上线后反响很强烈，Flutter中文网也很快被传播开，百度搜索排名迅速蹿升到前三，截止目前，Flutter中文官网日PV在7万左右，每日独立访问人数近一万多。\n\n虽然Flutter中文网给中国开发者提供了很好的第一手了解Flutter的资料，但是笔者还会经常遇到一些对Flutter技术处于围观而不愿尝试的开发者。这主要是因为当时Flutter在国内没有成功案例，再加上新技术都有学习成本，所以即使有文档，也会有一些开发者犹豫是否来学习。为了解决这部分开发者的疑虑，我就想如果能用Flutter开发一个完整的APP发布到应用商店，这样开发者就可以在犹豫的时候可以先实际感受一下Flutter应用，这样有个直观的了解后，就会容易做出判断，为此，我开发了[Gitme](https://flutterchina.club/app/gm.html)，它是一个Github客户端，它支持了源码浏览、Issue、Label等Github的大多数功能，到目前为止，通过Gitme登录过Github账号的用户有8000多人，日活用户有1000人。更重要的是，有很多人正是用了Gitme后，才来学Flutter的。\n\n无论是做Flutter中文网，还是写Gitme，主要目标都是帮助开发者学习Flutter，同时消除围观开发者疑虑。但当开发者们真正开始动手时，Flutter的生态问题就变得尤为突出。由于在2018年初Flutter刚起步时，很多基础的包和库都是空白，少数已有的一些库也大都是预览版（未到1.0），存在很多bug。这个状况不是一两个人花一两天能搞定的，这是需要聚整个Flutter开发者社群之力，耗费数年时间才可能有所改善。因此，在2018年4月份，我以Flutter中文网名义发起了Flutter开源计划，该计划主要是开发一些常用的包来丰富Flutter生态，帮助开发者提高开发效率。自在github建立[Flutter中文开发者社区账号](https://github.com/flutterchina)以来，前后开源了dio、cookieJar、flukit等多个项目，而dio在开源两周后，就迅速成为Flutter第三方包中Star排名第一的开源库。\n\n虽然做的事情已经够多了，但是仍有一些很有必要去做的事情，由于时间原因，一直被搁置。\n\n随着学习Flutter的人越来越多，一部分开发者通过查看官网的文档就能入门，但也有很多开发者感觉学习时仍然有些吃力，主要原因有两个，首先官网的文档主要是为了引导开发者快速上手的，讲的并不是很细；其次是我们虽然翻译了官方文档，但是对于Flutter SDK文档并没有翻译，而在开发中遇到的一些具体问题通常都得去查看SDK文档。所以，要解决这两个问题，必须得有一个系统化的Flutter教程，它不仅可以快速引导开发者入门，而且也能触及到一些细节和原理，最好也能提供一些学习和研究Flutter的方法。因此，如果能有一本能系统地介绍Flutter的书籍便是便是非常棒的！但是，当时没有一本关于Flutter的中文书籍，因此，我便计划写一本能由浅入深、系统介绍Flutter的书。2018年12月，《Flutter实战》完成初稿，并在Github上开源，同时上线了[《Flutter实战》电子书官网](https://book.flutterchina.club/) ，至今每天有3000多人在线浏览本书。那为什么不直接出版？如果直接出版，不仅有稿费，而且也能保护知识产权，而直接开源，不就只能当雷锋了？其实，无论是做中文网、写Gitme、做Flutter开源项目，我的出发点都是为了能帮助中国开发者了解、学习Flutter，而这是一件非常有意义的事，只要坚持做对别人有价值的事，那么上帝迟早会奖赏你；当然我们也在网站中留了打赏的按钮，如果读者觉得有帮助，可以扫码打赏，请笔者喝一杯咖啡。另外由于成书比较仓促，当时书中也有很多错误，开源后，有很多读者提PR来纠正书中的错别字，时至今日，有78名开发者为本书提过PR，我在此，衷心的感谢你们。\n\n起初，我没有出版实体书的打算，当时我以为开发者直接通过在线访问本书官网即方便又不用付费，何乐而不为。但在本书上线后，很多读者来添加微信好友，表示非常有收益，很期待出版纸质书，甚至有比较热心的读者想提前付定金预定！我理解，这是大家对我所做之事的认可和鼓励。考虑到，确实有一部分读者，尤其是还没毕业的同学，可能更喜欢通过书籍去学习，为此，我已经和机械工业出版社合作，目前本书纸质版正在出版中，敬请关注。\n\n### 本书组织结构\n\n本书采用由浅入深的方式介绍Flutter技术原理，分为三篇，总计15章，各篇的主要内容如下。\n\n- 第一篇，入门篇（第1章~第7章），包括Flutter技术的出现背景和简介、Flutter的各种类型的Widget以及如何构建UI。通过学习本章，读者可以掌握如何使用Flutter来构建UI界面。\n- 第二篇，进阶篇（第8章~第14章），包括Flutter中的事件机制、动画、自定义组件、文件和网络、插件、国际化以及Flutter核心原理等。通过本章内容，读者可以对Flutter整体构建及原理有一个深入的认识。\n- 第三篇，实例篇（第15章），本章主要通过一个简版的Github APP来将前面介绍的内容串起来，让开发者对一个完整的Flutter APP开发流有个了解。\n\n## 本书特色\n\n笔者在大学时读过候捷（真名侯俊杰）写的一些C++相关书籍，在他的《深入浅出MFC》一书中，有一句话我印象像非常深 “唯有深入，方能浅出”。我非常认同这句话，对于一门技术，只有了解的深入，才能用最浅显、通俗的话语描述出。我在写作本书时，深入浅出就是一个主要目标。所以，本书的目标不仅是想告诉读者如何使用Flutter，而且也非常关注各个知识点的底层实现以及设计思想。从本书章节划分上来看，入门篇为“浅出”，进阶篇则是“深入”。另外由于PC客户端开发、移动开发、Web开发这些经验我都有，而Flutter本质上是一个UI系统，而UI系统的设计和实现在”大前端“下有很多相通之处，所以在本书中的一些知识点我也会对比一些其他UI系统（主要是Android或Web）相应的实现，便于有相关开发经验的读者对比理解。\n\n## 本书读者对象\n\n- 读者至少熟悉一种编程语言。\n- 读者最好接触过PC客户端、移动开发或Web前端开发中的一种。\n- 本书不适合做为编程的入门读物。\n\n## 关于随书源码\n\n由于篇幅所限，本书中大多数示例代码都只是部分核心代码，读者可以去[这里]( https://github.com/wendux/flutter_in_action_source_code )查看下载。\n\n## 致谢\n\n感谢一直以来支持Flutter中文网、Flutter开源项目的人以及所有对本书提过PR的人，正是因为有你们，才有这本书。另外尤其感谢给本书打赏过的同学，你们的支持给了我很大的鼓励。\n\n\n## 权益\n\n最后，知识是应该付费的，创作不易，开源不等于免费，如果您是本书读者并手头宽裕，可以点击下面打赏按钮打赏；当然，如果您囊中羞涩，您也可以阅读本书，但我对您有个小小的要求，希望您在阅读的过程中能积极参与到本书的纠错以及未完成内容的创作上来，也算是有所付出。\n\n近来在网上发现很多**原封不动复制本书**的镜像网站和大量复制或**引用了本书但未注明出处**的博客、文章甚至书籍；对此，笔者在此声明，本书著作权归wendux所有，任何组织或个人在未经授权的情况下复制、拷贝、抄袭本书用于商业目的，笔者保留追究其法律责任的权利。如果是非商业目的的转载和引用，请注明出处并附上本书网址。另外如有出版机构愿意为本书出版实体书或者想转载本书内容，亦或是想合作，请加微信Demons-du.\n\n### 勘误\n\n由于Flutter SDK在不断更新，本书中的部分内容（如类的继承关系、参数等）可能会和新版本的Flutter不一致，读者以最新的Flutter SDK为准。另外，由于时间仓促，书中难免有错误之处，如果您发现本书中的错误，欢迎点击右上角的”编辑按钮“，提PR。如果您想一起参与本书创作，可以参考[《Flutter实战》贡献指南](https://github.com/flutterchina/flutter-in-action#%E8%B4%A1%E7%8C%AE%E9%A1%BB%E7%9F%A5)。\n\n\n\n\n\n"
  },
  {
    "path": "src/join_us.md",
    "content": "---\nsidebar: auto\n---\n\n# 字节跳动-内推\n\n欢迎来字节跳动，和作者（wendux） 一起做同事！我们有大量HC，社招、校招、实习，前端、客户端、后端都有，欢迎对技术有热情的同学来投递！风里雨里，我在字节跳动等你~\n\n> 投递方式：加微信（Demons-du），好友申请时请按 \"姓名+学校+职位+来自flutter社区\" 备注信息，微信请求通过后，字节跳动VIP通道就建立了（后续发简历、进度跟进、问题咨询都可以直接微信联系哦）。\n\n## 前端团队介绍\n\n我们是字节跳动-幸福里FE团队，诞生于2018年9月，从最初的4人组成长到今天的30多人，成员年龄跨度从90后到00后。技术栈覆盖当下前端主流全方向（Vue、React、Typescript、nodejs、webgl、flutter、小程序)，团队内大牛多多，技术氛围浓厚，有VR专家老吴、3D渲染一哥博哥、《Flutter实战》作者wendux、深谙 Web 框架及工程化的董老师、以及技能树满点的杰哥等等，还有，团队经常组织线下学习及娱乐活动，是一个开心且战斗力极强的team。只要你觉得自己够出色，或想让自己变得更出色，还等什么，放肆地加入我们吧。 \n\n### 业务线介绍\n\n幸福里是字节跳动旗下集内容、社区、工具于一体的房产信息、服务、交易平台。产品基于个性化推荐引擎向用户推荐优质的房产内容和全面、真实的房源信息，致力于为用户提供全面、专业、可靠的购房决策支持。\n\n幸福里始于2018年8月，是国内发展最快的，集内容、社区、工具于一体的房产信息与服务平台，业务覆盖一二线共23城，现累积注册用户千万，目前进入高速增长期。\n\n### 团队福利\n\n五险一金、补充医疗保险、定期体检、年终奖、股票期权、带薪年假、员工旅游、交通补助、包吃、节日福利、住房补贴，不限量零食下午茶、弹性工作制\n\n#### 实习生\n\n1. 团队为每一位实习生提供专职mentor，手把手带入工作业务。\n2. 团队为没有基础的实习生提供“筑基计划”的课程学习，轻松进阶前端技能。\n\n## 职位介绍\n\n### 职位一：前端开发工程师(校招/社招)急\n\n**职位描述:**\n\n1. 负责移动端 /PC 端业务系统、小程序、跨端页面开发；\n\n2. 负责推动与优化业务线中前端基础架构、组件抽象、技术调研；\n\n4. 积极推动改进产品，包括技术、用户体验、产品等各个维度的改进。\n\n**职位要求:**\n\n1. 本科及以上学历，计算机、通信等相关专业；\n\n2. 熟练掌握 EcmaScript，CSS，HTML，DOM、绘图、动画、协议、安全、网络、性能优化等前端技术，对主流前端框架（ React \\ Vue 等）至少一种有深入应用并深入理解其设计原理；\n\n3. 有安卓、iOS 开发经验或跨端技术flutter者优先；\n\n4. 对用户体验、交互操作流程，及用户需求有一定了解；\n\n5. 积极乐观，责任心强，工作认真细致，具备良好的服务意识，具有良好的团队沟通与协作能力；\n\n6. 热爱前端技术，有较强的学习能力，有强烈的求知欲、好奇心和进取心 ，能及时关注和学习业界最新的前端技术。\n\n### 职位二：前端开发实习生(可转正)\n\n**职位描述：**\n\n1、负责字节跳动-幸福里业务h5、小程序、中台系统的维护与开发；\n\n2、负责根据已有前端项目的基础架构进行合理的技术优化；\n\n3、积极推动改进产品，包括技术、用户体验、产品等各个维度。\n\n**职位要求：**\n\n1、计算机基础扎实：数据结构、算法、操作系统；\n\n2、熟悉掌握javascript、ES6 语言特性，熟练掌握css中常见布局方式，以及CSS3动画技术；\n\n3、熟练掌握VUE或React技术（非必须）；\n\n4、有ACM竞赛且获奖者优先；\n\n5、具较强的学习能力、主动、自驱、有责任心。\n\n**实习生培养计划**：\n\n我们会为每一位实习生配备一名mentor进行日常决疑解惑和指导，同时我们针对不太熟悉前端的同学进行一个专门的【筑基培训】，旨在帮助快速补齐前端基础，以及明确后续学习和成长路线，有老司机带，不迷路！\n\n### 职位三：web3D开发工程师（急）\n\n**职位描述：**\n\n1. 负责 VR 看房相关的业务开发，包括渲染SDK、标注平台等。\n2. 负责 VR 数据平台相关开发：包括数据预处理\n\n**职位要求：**\n\n1. 熟练掌握 JavaScript，WebGL；\n\n2. 熟悉计算机图形学，渲染管线/线性代数； \n\n3. 熟悉常用 Shader 原理及编写；\n\n4. 熟悉至少一款 H5 渲染引擎，如ThreeJS，Babylon等； \n\n6. 热爱钻研新技术，有强烈的好奇心和求知欲，有良好的编码规范；\n7. 加分项：\n    - 熟悉VR看房相关业务 \n    - 熟悉后端开发（Node）、对服务稳定性、并发了解同学优先（VR数据平台）。\n    - 熟悉 ThreeJS，Babylon, Unity3D 有相关 3D 作品或 DEMO。\n    -  各大前端技术社区活跃者、有自己的开源项目；\n\n## 其它职位（实习/校招/社招均可）\n\n### Android开发工程师\n\n职位描述\n\n1、负责公司移动产品的研发, 编写高质量的代码；\n\n2、和产品经理配合, 深度参与手机产品需求讨论, 功能定义等； \n\n3、设计良好的代码结构, 不断迭代重构 。\n\n职位要求\n\n1、智能手机爱好者和使用者, 追求良好的用户体验；\n\n2、热爱移动产品研发, 愿意在移动开发领域深入钻研, 并成为专家；\n\n3、熟练掌握JAVA, 熟悉Android SDK；\n\n4、一年以上Android开发经验, 能独立开发Android App； 5、对软件产品有强烈的责任心, 具备良好的沟通能力和优秀的团队协作能力。\n\n5、有flutter开发经验者加分。\n\n\n\n### iOS开发工程师\n\n职位描述\n\n1、负责公司移动产品的研发，编写高质量的代码；\n\n2、和产品经理配合，深度参与手机产品需求讨论，功能定义等； \n\n3、设计良好的代码结构，不断迭代重构 ；\n\n4、导并带领初级工程师共同完成研发任务。\n\n职位要求\n\n1、有强烈的求知欲和进取心；\n\n2、具有扎实的编程工底，良好的设计能力和编程习惯；\n\n3、至少精通一门编程语言 ，熟练掌握Objective-C，熟悉Swift的优先 ；\n\n4、一年以上iOS开发经验，能独立开发iPhoneApp者先。\n\n5、有flutter开发经验者加分。\n\n### 后端开发工程师\n\n**职位描述**\n\n1、主导或参与系统设计、研发、部署等相关工作；\n\n2、研发基础服务组件，解决共性需求，减少重复开发与运维；\n\n3、有较强的系统问题分析经验和能力，能够解决复杂的系统问题； \n\n4、参与部分生产系统维护工作，解决生产系统问题及进行系统调优。\n\n**职位要求**\n\n1、本科及以上学历，计算机相关专业；\n\n2、热爱计算机科学和互联网技术，精通至少一门编程语言，包括但不仅限于：Java、C、C++、PHP、 Python、Go； \n\n3、掌握扎实的计算机基础知识，深入理解数据结构、算法和操作系统知识； \n\n4、有优秀的逻辑分析能力，能够对业务逻辑进行合理的抽象和拆分； \n\n5、有强烈的求知欲，优秀的学习和沟通能力。\n\n## [返回书籍菜单列表](/index)\n\n\n\n\n\n"
  },
  {
    "path": "src/next.md",
    "content": "# 下一步\n\n### 其它平台\n\n本书主要讲的是Flutter在移动端开发\n\n- "
  },
  {
    "path": "src/preface.md",
    "content": "# 前言\n\n### 缘起\n\n在全球，随着Flutter被越来越多的知名公司应用在自己的商业APP中，Flutter这门新技术也逐渐进入了移动开发者的视野，尤其是当Google在2018年IO大会上发布了第一个Preview版本后，国内刮起来一股学习Flutter的热潮。\n\n在Flutter发布之初，当时，我在看完Flutter原理介绍后，就对它产生了浓厚的兴趣。当时笔者身边也一些人比较关注Flutter，我也被经常问到关于Flutter的一些问题，比如Flutter怎么样？和RN有什么区别？Flutter为什么要用Dart？当时也听到了一些批评的声音，比如有些人说Flutter只是重复造轮子，没啥亮点、Flutter最大的缺点就是使用了Dart语言等。在听到这些问题及论调后，我深知这是对Flutter的不了解而造成的，这和当时国内缺乏Flutter中文文档和教程有直接关系，很多人对Flutter的了解都只停留在Google的发布会介绍（有中文翻译）。\n\n在笔者深入的了解Flutter后，深知Flutter必将是一个会改变移动开发格局的里程碑级作品，它从设计之初就对性能和开发效率兼顾，并且借鉴了React（一个Web开发框架）的响应式的UI框架设计思想等，总之，很难用一两句话说完Flutter的优点，同时我也很快成为了Flutter的路转粉。\n\n为了更好的帮助中国开发者了解这门新技术，我在2018年初开始翻译Flutter官网文档，同年4月份上线了[Flutter中文网](https://flutterchina.club/)，上线后反响很强烈，Flutter中文网也很快被传播开，百度搜索排名迅速蹿升到前三，截止目前，Flutter中文官网日PV在7万左右，每日独立访问人数近一万多。\n\n虽然Flutter中文网给中国开发者提供了很好的第一手了解Flutter的资料，但是笔者还会经常遇到一些对Flutter技术处于围观而不愿尝试的开发者。这主要是因为当时Flutter在国内没有成功案例，再加上新技术都有学习成本，所以即使有文档，也会有一些开发者犹豫是否来学习。为了解决这部分开发者的疑虑，我就想如果能用Flutter开发一个完整的APP发布到应用商店，这样开发者就可以在犹豫的时候可以先实际感受一下Flutter应用，这样有个直观的了解后，就会容易做出判断，为此，我开发了[Gitme](https://flutterchina.club/app/gm.html)，它是一个Github客户端，它支持了源码浏览、Issue、Label等Github的大多数功能，到目前为止，通过Gitme登录过Github账号的用户有8000多人，日活用户有1000人。更重要的是，有很多人正是用了Gitme后，才来学Flutter的。\n\n无论是做Flutter中文网，还是写Gitme，主要目标都是帮助开发者学习Flutter，同时消除围观开发者疑虑。但当开发者们真正开始动手时，Flutter的生态问题就变得尤为突出。由于在2018年初Flutter刚起步时，很多基础的包和库都是空白，少数已有的一些库也大都是预览版（未到1.0），存在很多bug。这个状况不是一两个人花一两天能搞定的，这是需要聚整个Flutter开发者社群之力，耗费数年时间才可能有所改善。因此，在2018年4月份，我以Flutter中文网名义发起了Flutter开源计划，该计划主要是开发一些常用的包来丰富Flutter生态，帮助开发者提高开发效率。自在github建立[Flutter中文开发者社区官方账号](https://github.com/flutterchina)以来，前后开源了dio、cookieJar、flukit等多个项目，而dio在开源两周后，就迅速成为Flutter第三方包中Star排名第一的开源库。\n\n虽然做的事情已经够多了，但是仍有一些很有必要去做的事情，由于时间原因，一直被搁置。\n\n随着学习Flutter的人越来越多，一部分开发者通过查看官网的文档就能入门，但也有很多开发者感觉学习时仍然有些吃力，主要原因有两个，首先官网的文档主要是为了引导开发者快速上手的，讲的并不是很细；其次是我们虽然翻译了官方文档，但是对于Flutter SDK文档并没有翻译，而在开发中遇到的一些具体问题通常都得去查看SDK文档。所以，要解决这两个问题，必须得有一个系统化的Flutter教程，它不仅可以快速引导开发者入门，而且也能触及到一些细节和原理，最好也能提供一些学习和研究Flutter的方法。因此，如果能有一本能系统地介绍Flutter的书籍便是便是非常棒的！但是，当时没有一本关于Flutter的中文书籍，因此，我便计划写一本能由浅入深、系统介绍Flutter的书。2018年12月，《Flutter实战》完成初稿，并在Github上开源，同时上线了《Flutter实战》电子书官网（https://book.flutterchina.club/） ，至今每天有3000多人在线浏览本书。那为什么不直接出版？如果直接出版，不仅有稿费，而且也能保护知识产权，而直接开源，不就就只能当雷锋了？其实，无论是做中文网、写Gitme、做Flutter开源项目，我的出发点都是为了能帮助中国开发者了解、学习Flutter，而这是一件非常有意义的事，只要坚持做对别人有价值的事，那么上帝迟早会奖赏你；当然我们也在网站中留了打赏的按钮，如果读者觉得有帮助，可以扫码打赏，请笔者喝一杯咖啡。另外由于成书比较仓促，当时书中也有很多错误，开源后，有很多读者提PR来纠正书中的错别字，时至今日，有78名开发者为本书提过PR，我在此，衷心的感谢你们。\n\n起初，我没有出版实体书的打算，当时我以为开发者直接通过在线访问本书官网即方便又不用付费，何乐而不为。但在本书上线后，很多读者来添加微信好友，表示非常有收益，很期待出版纸质书，甚至有比较热心的读者想提前付定金预定！我理解，这是大家对我所做之事的认可和鼓励。考虑到，确实有一部分读者，尤其是还没毕业的同学，可能更喜欢通过书籍去学习，为此，我已经和机械工业出版社合作，目前本书纸质版正在出版中，敬请关注。\n\n### 本书组织结构\n\n本书采用由浅入深的方式介绍Flutter技术原理，分为三篇，总计15章，各篇的主要内容如下。\n\n- 第一篇，入门篇（第1章~第7章），包括Flutter技术的出现背景和简介、Flutter的各种类型的Widget以及如何构建UI。通过学习本章，读者可以掌握如何使用Flutter来构建UI界面。\n- 第二篇，进阶篇（第8章~第14章），包括Flutter中的事件机制、动画、自定义组件、文件和网络、插件、国际化以及Flutter核心原理等。通过本章内容，读者可以对Flutter整体构建及原理有一个深入的认识。\n- 第三篇，实例篇（第15张），本章主要通过一个简版的Github APP来将前面介绍的内容串起来，让开发者对一个完整的Flutter APP开发流有个了解。\n\n由于Flutter的很多知识点是相互交织的，很难将它们彻底划分开，所以本书中也难免会出现一些在前面章节会使用在后面章节的场景，比如我们在入门篇介绍进度指示器时会用到在进阶篇中才介绍的动画相关知识。本书中对于这种情况会在相应的章节进行说明。读者可以直接跳到后面相应知识点章节阅读后再返回，也可以先有个印象，待学习到后面相关章节后再回头来看。\n\n### 本书特色\n\n笔者在大学时读过候捷（真名侯俊杰）写的一些C++相关书籍，在他的《深入浅出MFC》一书中，有一句话我印象非常深 “唯有深入，方能浅出”。我非常认同这句话，对于一门技术，只有了解的深入，才能用最浅显、通俗的话语描述出。我在写作本书时，深入浅出就是一个主要目标。所以，本书的目标不仅是想告诉读者如何使用Flutter，而且也非常关注各个知识点的底层实现以及设计思想。从本书章节划分上来看，入门篇为“浅出”，进阶篇则是“深入”。另外由于PC客户端开发、移动开发、Web开发这些经验我都有，而Flutter本质上是一个UI系统，而UI系统的设计和实现在”大前端“下有很多相通之处，所以在本书中的一些知识点我也会对比一些其他UI系统（主要是Android或Web）相应的实现，便于有相关开发经验的读者对比理解。\n\n### 本书读者对象\n\n- 读者至少熟悉一种编程语言。\n- 读者最好接触过PC客户端、移动开发或Web前端开发中的一种。\n- 本书不适合做为编程的入门读物。\n\n### 关于随书源码\n\n由于篇幅所限，本书中大多数示例代码都只是部分核心代码，读者可以去https://github.com/wendux/flutter_in_action_source_code 查看\n\n### 勘误\n\n由于Flutter SDK在不断更新，本书中的部分内容（如类的继承关系、参数等）可能会和新版本的Flutter不一致，读者以最新的Flutter SDK为准。另外，由于时间仓促，书中难免有错误之处，如果发现错误，可以在本书Github项目issue列表中去反馈，地址是https://github.com/flutterchina/flutter-in-action/issues 。\n\n### 致谢\n\n感谢一直以来支持Flutter中文网、Flutter开源项目的人以及所有对本书提过PR的人，正是因为有你们，才有这本书。另外尤其感谢给本书打赏过的同学，你们的支持给了我很大的鼓励。\n\n"
  },
  {
    "path": "src/reference.md",
    "content": "# 参考文献\n\n- React Native官网：https://facebook.github.io/react-native/\n\n- Weex：https://weex.apache.org/zh/guide/introduction.html\n\n- 快应用：https://www.quickapp.cn/\n\n- QT for mobile：https://www.qt.io/mobile-app-development/\n\n- Flutter官网：https://flutter.dev/\n\n- Flutter中文网社区：https://flutterchina.club/docs/\n\n- Dart Packages官网：https://pub.dev/\n\n- Flutter中文开发者社区开源项目：https://github.com/flutterchina\n\n- Material Design：https://material.io/\n\n- Github 开发者中心官网：https://developer.github.com/v3/\n\n- Android开发者中心官网：https://developer.android.google.cn/\n\n- Apple开发者中心官网：https://developer.apple.com/\n\n  "
  },
  {
    "path": "src/summary.md",
    "content": "﻿# Summary\n\n* [简介](README.md)\n* [前言](intro.md)\n\n## 入门篇\n\n* [第一章：起步](chapter1/index.md)\n    * [1.1：移动开发技术简介](chapter1/mobile_development_intro.md)\n    * [1.2：初识Flutter](chapter1/flutter_intro.md)  \n    * [1.3：搭建Flutter开发环境](chapter1/install_flutter.md)      \n    * [1.4：Dart语言简介](chapter1/dart.md)    \n    \n* [第二章：第一个Flutter应用](chapter2/index.md)\n    * [2.1：计数器示例](chapter2/first_flutter_app.md)\n    * [2.2：路由管理](chapter2/flutter_router.md)  \n    * [2.3：包管理](chapter2/flutter_package_mgr.md)        \n    * [2.4：资源管理](chapter2/flutter_assets_mgr.md)    \n    * [2.5：调试Flutter APP](chapter2/flutter_app_debug.md)\n    * [2.6：Dart线程模型及异常捕获](chapter2/thread_model_and_error_report.md)\n    \n* [第三章：基础组件](chapter3/index.md)\n    * [3.1：Widget简介](chapter3/flutter_widget_intro.md)\n    * [3.2：状态管理](chapter3/state_manage.md)\n    * [3.3：文本、字体样式](chapter3/text.md)\n    * [3.4：按钮](chapter3/buttons.md)      \n    * [3.5：图片和Icon](chapter3/img_and_icon.md)   \n    * [3.6：单选框和复选框](chapter3/radio_and_checkbox.md)   \n    * [3.7：输入框和表单](chapter3/input_and_form.md) \n    * [3.8：进度指示器](chapter3/progress.md)     \n    \n* [第四章：布局类组件](chapter4/index.md)\n    * [4.1：布局类组件简介](chapter4/intro.md)\n    * [4.2：线性布局（Row、Column）](chapter4/row_and_column.md)\n    * [4.3：弹性布局（Flex）](chapter4/flex.md)\n    * [4.4：流式布局（Wrap、Flow）](chapter4/wrap_and_flow.md)      \n    * [4.5：层叠布局（Stack、Positioned）](chapter4/stack.md)\n    * [4.6：对齐与相对定位（Align）](chapter4/alignment.md)    \n\n* [第五章：容器类组件](chapter5/index.md)\n    * [5.1：填充（Padding）](chapter5/padding.md)\n    * [5.2：尺寸限制类容器（ConstrainedBox等）](chapter5/constrainedbox_and_sizebox.md)\n    * [5.3：装饰容器（DecoratedBox）](chapter5/decoratedbox.md)      \n    * [5.4：变换（Transform）](chapter5/transform.md) \n    * [5.5：Container容器](chapter5/container.md) \n    * [5.6：Scaffold、TabBar、底部导航](chapter5/material_scaffold.md) \n    * [5.7：剪裁（Clip）](chapter5/clip.md) \n    \n* [第六章：可滚动组件](chapter6/index.md)\n    * [6.1：可滚动组件简介](chapter6/intro.md)\n    * [6.2：SingleChildScrollView](chapter6/single_child_scrollview.md)\n    * [6.3：ListView](chapter6/listview.md)\n    * [6.4：GridView](chapter6/gridview.md)      \n    * [6.5：CustomScrollView](chapter6/custom_scrollview.md) \n    * [6.6：滚动监听及控制（ScrollController）](chapter6/scroll_controller.md) \n    \n* [第七章：功能型组件](chapter7/index.md)\n    * [7.1：导航返回拦截（WillPopScope）](chapter7/willpopscope.md)\n    * [7.2：数据共享（InheritedWidget）](chapter7/inherited_widget.md)\n    * [7.3： 跨组件状态共享（Provider）](chapter7/provider.md)\n    * [7.4：颜色和主题（Theme）](chapter7/theme.md) \n    * [7.5：异步UI更新（FutureBuilder、StreamBuilder）](chapter7/futurebuilder_and_streambuilder.md)\n    * [7.6：对话框详解](chapter7/dailog.md)  \n     \n## 进阶篇\n\n* [第八章：事件处理与通知](chapter8/index.md)\n    * [8.1：原始指针事件处理](chapter8/listener.md)\n    * [8.2：手势识别](chapter8/gesture.md)\n    * [8.3：全局事件总线](chapter8/eventbus.md) \n    * [8.4：通知(Notification)](chapter8/notification.md)     \n    \n* [第九章：动画](chapter9/index.md)\n    * [9.1：Flutter动画简介](chapter9/intro.md)\n    * [9.2：动画结构](chapter9/animation_structure.md)\n    * [9.3：自定义路由过渡动画](chapter9/route_transition.md) \n    * [9.4：Hero动画](chapter9/hero.md) \n    * [9.5：交织动画](chapter9/stagger_animation.md) \n    * [9.6：通用“动画切换”组件（AnimatedSwitcher）](chapter9/animated_switcher.md) \n    * [9.7：动画过渡组件](chapter9/animated_widgets.md)     \n* [第十章：自定义组件](chapter10/index.md)\n    * [10.1：自定义组件方法简介](chapter10/intro.md)\n    * [10.2：组合现有组件](chapter10/combine.md)\n    * [10.3：组合实例：TurnBox](chapter10/turn_box.md)\n    * [10.4：自绘组件（CustomPaint与Canvas）](chapter10/custom_paint.md) \n    * [10.5：自绘实例：圆形渐变进度条(自绘)](chapter10/gradient_circular_progress_demo.md) \n    \n* [第十一章：文件操作与网络请求](chapter11/index.md)\n    * [11.1：文件操作](chapter11/file_operation.md)\n    * [11.2：Http请求-HttpClient](chapter11/http.md)\n    * [11.3：Http请求-Dio package](chapter11/dio.md) \n    * [11.4：实例：Http分块下载](chapter11/download_with_chunks.md) \n    * [11.5：WebSocket](chapter11/websocket.md) \n    * [11.6：使用Socket API](chapter11/socket.md) \n    * [11.7：Json转Dart Model类](chapter11/json_model.md) \n    \n* [第十二章：包与插件](chapter12/index.md)\n    * [12.1：开发package](chapter12/develop_package.md)\n    * [12.2：平台通道简介](chapter12/platform-channel.md)\n    * [12.3：开发Flutter插件](chapter12/develop_plugin.md)\n    * [12.4：插件开发：实现Android端API](chapter12/android_implement.md)\n    * [12.5：插件开发：实现IOS端API](chapter12/ios_implement.md)\n    * [12.6：Texture和PlatformView](chapter12/texture_platformview.md) \n    \n* [第十三章：国际化](chapter13/index.md)\n    * [13.1：让App支持多语言](chapter13/multi_languages_support.md)\n    * [13.2：实现Localizations](chapter13/locallization_implement.md) \n    * [13.3：使用Intl包](chapter13/intl.md) \n    * [13.4：国际化常见问题](chapter13/faq.md) \n    \n* [第十四章：Flutter核心原理](chapter14/index.md)\n    * [14.1：Flutter UI系统](chapter14/flutter_ui_system.md)\n    * [14.2：Element和BuildContext](chapter14/element_buildcontext.md)\n    * [14.3：RenderObject与RenderBox](chapter14/render_object.md)\n    * [14.4：Flutter从启动到显示](chapter14/flutter_app_startup.md)\n    * [14.5：Flutter图片加载与缓存](chapter14/image_and_cache.md)\n \n## 实例篇\n\n* [第十五章：一个完整的Flutter应用](chapter15/intro.md)\n    * [15.1：应用简介](chapter15/intro.md)    \n    * [15.2：APP代码结构](chapter15/code_structure.md) \n    * [15.3：Model类定义](chapter15/models.md) \n    * [15.4：全局变量及共享状态](chapter15/globals.md)  \n    * [15.5：网络请求封装](chapter15/network.md) \n    * [15.6：App入口及首页](chapter15/entry.md) \n    * [15.7：登录页](chapter15/login_page.md) \n    * [15.8：多语言和多主题](chapter15/language_and_theme_setting.md)      \n\n\n"
  },
  {
    "path": "src/v2/README.md",
    "content": "## 关于作者\n\n作者杜文（网名[wendux](https://github.com/wendux)），高级技术专家、[掘金知名专栏作者](https://juejin.im/user/58211b88a0bb9f0058c25b7f)、[Flutter中文网社区](https://flutterchina.club/)创办者、[Flutter中文社区开源项目](https://github.com/flutterchina)发起人、Github社区知名开发者，是[dio](https://github.com/flutterchina/dio)、[fly](https://github.com/wendux/fly)、[dsBridge](https://github.com/wendux/DSBridge-Android)等多个知名开源项目作者。作者曾就职于百度、小赢科技等互联网公司，从事过PC桌面开发、移动端开发以及Web开发，负责过多次核心技术攻关，现就职于字节跳动。目前闲暇时，主要关注大前端行业发展。\n\n## 内容简介\n\n本书由浅入深的介绍了Flutter技术和开发流程。本书包含不仅包含大量示例、图片，还有配套的示例源码，可帮助读者循序渐进的掌握Flutter开发技术。本书分为入门、进阶、实例三大篇，其中入门篇（第1章~第7章）主要介绍了Flutter技术产生的背景、常用的组件以及布局方式，通过入门篇的学习，读者可以掌握如何使用Flutter来构建UI界面。进阶篇（第8章~第14章），包括Flutter中的事件机制、动画、自定义组件、文件和网络、插件、国际化以及Flutter核心原理等。通过本章内容，读者可以对Flutter整体构建及原理有一个深入的认识。实例篇（第15章），本章主要通过一个简版的Github APP来将前面介绍的内容串起来，让开发者对一个完整的Flutter APP开发流有个了解。\n"
  },
  {
    "path": "src/v2/chapter1/dart.md",
    "content": "# 1.4 Dart语言简介\n\n在之前我们已经介绍过Dart语言的相关特性，读者可以翻看一下，如果读者已经熟悉Dart语法，可以跳过本节，如果你还不了解Dart，也不用担心，按照笔者经验，如果你有过其他编程语言经验（尤其是Java和JavaScript）的话会非常容易上手Dart。当然，如果你是iOS开发者，也不用担心，Dart中也有一些与Swift比较相似的特性，如命名参数等，笔者当时学习Dart时，只是花了一个小时，看完Dart官网的Language Tour，就开始动手写Flutter了。\n\n在笔者看来，Dart的设计目标应该是同时借鉴了Java和JavaScript。Dart在静态语法方面和Java非常相似，如类型定义、函数声明、泛型等，而在动态特性方面又和JavaScript很像，如函数式特性、异步支持等。除了融合Java和JavaScript语言之所长之外，Dart也具有一些其它具有表现力的语法，如可选命名参数、`..`（级联运算符）和`?.`（条件成员访问运算符）以及`??`（判空赋值运算符）。其实，对编程语言了解比较多的读者会发现，在Dart中其实看到的不仅有Java和JavaScript的影子，它还具有其它编程语言中的身影，如命名参数在Objective-C和Swift中早就很普遍，而`??`操作符在PHP 7.0语法中就已经存在了，因此我们可以看到Google对Dart语言给予厚望，是想把Dart打造成一门集百家之所长的编程语言。\n\n接下来，我们先对Dart语法做一个简单的介绍，然后再将Dart与JavaScript和Java做一个简要的对比，方便读者更好的理解。\n\n> 注意：由于本书并非专门介绍Dart语言的书籍，所以本章主要会介绍一下在Flutter开发中常用的语法特性，如果想更多了解Dart，读者可以去Dart官网学习，现在互联网上Dart相关资料已经很多了。另外Dart 2.0已经正式发布，所以本书所有示例均采用Dart 2.0语法。\n\n\n\n## 1.4.1 变量声明\n\n1. **var**\n\n   类似于JavaScript中的`var`，它可以接收任何类型的变量，但最大的不同是Dart中var变量一旦赋值，类型便会确定，则不能再改变其类型，如：\n\n   ```dart\n   var t;\n   t = \"hi world\";\n   // 下面代码在dart中会报错，因为变量t的类型已经确定为String，\n   // 类型一旦确定后则不能再更改其类型。\n   t = 1000;\n   ```\n\n   上面的代码在JavaScript是没有问题的，前端开发者需要注意一下，之所以有此差异是因为Dart本身是一个强类型语言，任何变量都是有确定类型的，在Dart中，当用`var`声明一个变量后，Dart在编译时会根据第一次赋值数据的类型来推断其类型，编译结束后其类型就已经被确定，而JavaScript是纯粹的弱类型脚本语言，var只是变量的声明方式而已。\n\n2. **dynamic**和**Object**\n\n    `Object` 是Dart所有对象的根基类，也就是说所有类型都是`Object`的子类(包括Function和Null)，所以任何类型的数据都可以赋值给`Object`声明的对象.\n    `dynamic`与`var`一样都是关键词,声明的变量可以赋值任意对象。\n    而`dynamic`与`Object`相同之处在于,他们声明的变量可以在后期改变赋值类型。\n    \n    ```dart\n    dynamic t;\n    Object x;\n    t = \"hi world\";\n    x = 'Hello Object';\n    //下面代码没有问题\n    t = 1000;\n    x = 1000;\n    ```\n   \n   `dynamic`与`Object`不同的是,`dynamic`声明的对象编译器会提供所有可能的组合,\n   而`Object`声明的对象只能使用Object的属性与方法, 否则编译器会报错。如:\n   \n   ```dart\n    dynamic a;\n    Object b;\n    main() {\n        a = \"\";\n        b = \"\";\n        printLengths();\n    }   \n\n    printLengths() {\n        // no warning\n        print(a.length);\n        // warning:\n        // The getter 'length' is not defined for the class 'Object'\n        print(b.length);\n    }\n   ```\n   \n   变量a不会报错, 变量b编译器会报错\n\n   `dynamic`的这个特性与`Objective-C`中的`id`作用很像.\n   `dynamic`的这个特点使得我们在使用它时需要格外注意,这很容易引入一个运行时错误.\n   \n3. **final**和**const**\n\n   如果您从未打算更改一个变量，那么使用 `final` 或 `const`，不是`var`，也不是一个类型。 一个 `final` 变量只能被设置一次，两者区别在于：`const` 变量是一个编译时常量，`final`变量在第一次使用时被初始化。被`final`或者`const`修饰的变量，变量类型可以省略，如：\n\n   ```dart\n   //可以省略String这个类型声明\n   final str = \"hi world\";\n   //final String str = \"hi world\"; \n   const str1 = \"hi world\";\n   //const String str1 = \"hi world\";\n   ```\n\n    \n\n## 1.4.2 函数\n\nDart是一种真正的面向对象的语言，所以即使是函数也是对象，并且有一个类型**Function**。这意味着函数可以赋值给变量或作为参数传递给其他函数，这是函数式编程的典型特征。\n\n1. 函数声明\n\n   ```dart\n   bool isNoble(int atomicNumber) {\n     return _nobleGases[atomicNumber] != null;\n   }\n   ```\n\n   Dart函数声明如果没有显式声明返回值类型时会默认当做`dynamic`处理，注意，函数返回值没有类型推断：\n\n   ```dart\n   typedef bool CALLBACK();\n   \n   //不指定返回类型，此时默认为dynamic，不是bool\n   isNoble(int atomicNumber) {\n     return _nobleGases[atomicNumber] != null;\n   }\n   \n   void test(CALLBACK cb){\n      print(cb()); \n   }\n   //报错，isNoble不是bool类型\n   test(isNoble);\n   ```\n\n2. 对于只包含一个表达式的函数，可以使用简写语法\n\n   ```dart\n   bool isNoble (int atomicNumber)=> _nobleGases [ atomicNumber ] ！= null ;   \n   ```\n\n3. 函数作为变量\n\n   ```dart\n   var say = (str){\n     print(str);\n   };\n   say(\"hi world\");\n   ```\n\n4. 函数作为参数传递\n\n   ```dart\n   void execute(var callback) {\n       callback();\n   }\n   execute(() => print(\"xxx\"))\n   ```\n\n5. 可选的位置参数\n\n   包装一组函数参数，用[]标记为可选的位置参数，并放在参数列表的最后面：\n\n   ```dart\n   String say(String from, String msg, [String device]) {\n     var result = '$from says $msg';\n     if (device != null) {\n       result = '$result with a $device';\n     }\n     return result;\n   }\n   ```\n\n   下面是一个不带可选参数调用这个函数的例子：\n\n   ```dart\n   say('Bob', 'Howdy'); //结果是： Bob says Howdy\n   ```\n\n   下面是用第三个参数调用这个函数的例子：\n\n   ```dart\n   say('Bob', 'Howdy', 'smoke signal'); //结果是：Bob says Howdy with a smoke signal\n   ```\n\n6. 可选的命名参数\n\n   定义函数时，使用{param1, param2, …}，放在参数列表的最后面，用于指定命名参数。例如：\n\n   ```dart\n   //设置[bold]和[hidden]标志\n   void enableFlags({bool bold, bool hidden}) {\n       // ... \n   }\n   ```\n\n   调用函数时，可以使用指定命名参数。例如：`paramName: value`\n\n   ```dart\n   enableFlags(bold: true, hidden: false);\n   ```\n\n   可选命名参数在Flutter中使用非常多。\n   \n   **注意，不能同时使用可选的位置参数和可选的命名参数**\n\n## 1.4.3 异步支持\n\nDart类库有非常多的返回`Future`或者`Stream`对象的函数。 这些函数被称为**异步函数**：它们只会在设置好一些耗时操作之后返回，比如像 IO操作。而不是等到这个操作完成。\n\n`async`和`await`关键词支持了异步编程，允许您写出和同步代码很像的异步代码。\n\n### Future\n\n`Future`与JavaScript中的`Promise`非常相似，表示一个异步操作的最终完成（或失败）及其结果值的表示。简单来说，它就是用于处理异步操作的，异步处理成功了就执行成功的操作，异步处理失败了就捕获错误或者停止后续操作。一个Future只会对应一个结果，要么成功，要么失败。\n\n由于本身功能较多，这里我们只介绍其常用的API及特性。还有，请记住，`Future` 的所有API的返回值仍然是一个`Future`对象，所以可以很方便的进行链式调用。\n\n#### Future.then\n\n为了方便示例，在本例中我们使用`Future.delayed` 创建了一个延时任务（实际场景会是一个真正的耗时任务，比如一次网络请求），即2秒后返回结果字符串\"hi world!\"，然后我们在`then`中接收异步结果并打印结果，代码如下：\n\n```dart\nFuture.delayed(new Duration(seconds: 2),(){\n   return \"hi world!\";\n}).then((data){\n   print(data);\n});\n```\n\n#### Future.catchError\n\n如果异步任务发生错误，我们可以在`catchError`中捕获错误，我们将上面示例改为：\n\n```dart\nFuture.delayed(new Duration(seconds: 2),(){\n   //return \"hi world!\";\n   throw AssertionError(\"Error\");  \n}).then((data){\n   //执行成功会走到这里  \n   print(\"success\");\n}).catchError((e){\n   //执行失败会走到这里  \n   print(e);\n});\n```\n\n在本示例中，我们在异步任务中抛出了一个异常，`then `的回调函数将不会被执行，取而代之的是 `catchError`回调函数将被调用；但是，并不是只有 `catchError`回调才能捕获错误，`then`方法还有一个可选参数`onError`，我们也可以它来捕获异常：\n\n```dart\nFuture.delayed(new Duration(seconds: 2), () {\n\t//return \"hi world!\";\n\tthrow AssertionError(\"Error\");\n}).then((data) {\n\tprint(\"success\");\n}, onError: (e) {\n\tprint(e);\n});\n```\n\n#### Future.whenComplete\n\n有些时候，我们会遇到无论异步任务执行成功或失败都需要做一些事的场景，比如在网络请求前弹出加载对话框，在请求结束后关闭对话框。这种场景，有两种方法，第一种是分别在`then`或`catch`中关闭一下对话框，第二种就是使用`Future`的`whenComplete`回调，我们将上面示例改一下：\n\n```dart\nFuture.delayed(new Duration(seconds: 2),(){\n   //return \"hi world!\";\n   throw AssertionError(\"Error\");\n}).then((data){\n   //执行成功会走到这里 \n   print(data);\n}).catchError((e){\n   //执行失败会走到这里   \n   print(e);\n}).whenComplete((){\n   //无论成功或失败都会走到这里\n});\n```\n\n\n\n#### Future.wait\n\n有些时候，我们需要等待多个异步任务都执行结束后才进行一些操作，比如我们有一个界面，需要先分别从两个网络接口获取数据，获取成功后，我们需要将两个接口数据进行特定的处理后再显示到UI界面上，应该怎么做？答案是`Future.wait`，它接受一个`Future`数组参数，只有数组中所有`Future`都执行成功后，才会触发`then`的成功回调，只要有一个`Future`执行失败，就会触发错误回调。下面，我们通过模拟`Future.delayed` 来模拟两个数据获取的异步任务，等两个异步任务都执行成功时，将两个异步任务的结果拼接打印出来，代码如下：\n\n```dart\nFuture.wait([\n  // 2秒后返回结果  \n  Future.delayed(new Duration(seconds: 2), () {\n    return \"hello\";\n  }),\n  // 4秒后返回结果  \n  Future.delayed(new Duration(seconds: 4), () {\n    return \" world\";\n  })\n]).then((results){\n  print(results[0]+results[1]);\n}).catchError((e){\n  print(e);\n});\n```\n\n执行上面代码，4秒后你会在控制台中看到“hello world”。\n\n### Async/await\n\nDart中的`async/await` 和JavaScript中的`async/await`功能和用法是一模一样的，如果你已经了解JavaScript中的`async/await`的用法，可以直接跳过本节。\n\n#### 回调地狱(Callback Hell)\n\n如果代码中有大量异步逻辑，并且出现大量异步任务依赖其它异步任务的结果时，必然会出现`Future.then`回调中套回调情况。举个例子，比如现在有个需求场景是用户先登录，登录成功后会获得用户ID，然后通过用户ID，再去请求用户个人信息，获取到用户个人信息后，为了使用方便，我们需要将其缓存在本地文件系统，代码如下：\n\n```dart\n//先分别定义各个异步任务\nFuture<String> login(String userName, String pwd){\n\t...\n    //用户登录\n};\nFuture<String> getUserInfo(String id){\n\t...\n    //获取用户信息 \n};\nFuture saveUserInfo(String userInfo){\n\t...\n\t// 保存用户信息 \n}; \n```\n\n接下来，执行整个任务流：\n\n```dart\nlogin(\"alice\",\"******\").then((id){\n //登录成功后通过，id获取用户信息    \n getUserInfo(id).then((userInfo){\n    //获取用户信息后保存 \n    saveUserInfo(userInfo).then((){\n       //保存用户信息，接下来执行其它操作\n        ...\n    });\n  });\n})\n```\n\n可以感受一下，如果业务逻辑中有大量异步依赖的情况，将会出现上面这种在回调里面套回调的情况，过多的嵌套会导致的代码可读性下降以及出错率提高，并且非常难维护，这个问题被形象的称为**回调地狱（Callback Hell）**。回调地狱问题在之前JavaScript中非常突出，也是JavaScript被吐槽最多的点，但随着ECMAScript6和ECMAScript7标准发布后，这个问题得到了非常好的解决，而解决回调地狱的两大神器正是ECMAScript6引入了`Promise`，以及ECMAScript7中引入的`async/await`。 而在Dart中几乎是完全平移了JavaScript中的这两者：`Future`相当于`Promise`，而`async/await`连名字都没改。接下来我们看看通过`Future`和`async/await`如何消除上面示例中的嵌套问题。\n\n##### 使用Future消除Callback Hell\n\n```dart\nlogin(\"alice\",\"******\").then((id){\n  \treturn getUserInfo(id);\n}).then((userInfo){\n    return saveUserInfo(userInfo);\n}).then((e){\n   //执行接下来的操作 \n}).catchError((e){\n  //错误处理  \n  print(e);\n});\n```\n\n正如上文所述， *“`Future` 的所有API的返回值仍然是一个`Future`对象，所以可以很方便的进行链式调用”* ，如果在then中返回的是一个`Future`的话，该`future`会执行，执行结束后会触发后面的`then`回调，这样依次向下，就避免了层层嵌套。\n\n##### 使用async/await消除callback hell\n\n通过`Future`回调中再返回`Future`的方式虽然能避免层层嵌套，但是还是有一层回调，有没有一种方式能够让我们可以像写同步代码那样来执行异步任务而不使用回调的方式？答案是肯定的，这就要使用`async/await`了，下面我们先直接看代码，然后再解释，代码如下：\n\n```dart\ntask() async {\n   try{\n    String id = await login(\"alice\",\"******\");\n    String userInfo = await getUserInfo(id);\n    await saveUserInfo(userInfo);\n    //执行接下来的操作   \n   } catch(e){\n    //错误处理   \n    print(e);   \n   }  \n}\n```\n\n- `async`用来表示函数是异步的，定义的函数会返回一个`Future`对象，可以使用then方法添加回调函数。\n- `await` 后面是一个`Future`，表示等待该异步任务完成，异步完成后才会往下走；`await`必须出现在 `async` 函数内部。\n\n可以看到，我们通过`async/await`将一个异步流用同步的代码表示出来了。\n\n> 其实，无论是在JavaScript还是Dart中，`async/await`都只是一个语法糖，编译器或解释器最终都会将其转化为一个Promise（Future）的调用链。\n\n\n\n## 1.4.4 Stream\n\n`Stream` 也是用于接收异步事件数据，和`Future` 不同的是，它可以接收多个异步操作的结果（成功或失败）。 也就是说，在执行异步任务时，可以通过多次触发成功或失败事件来传递结果数据或错误异常。 `Stream` 常用于会多次读取数据的异步任务场景，如网络内容下载、文件读写等。举个例子：\n\n```dart\nStream.fromFutures([\n  // 1秒后返回结果\n  Future.delayed(new Duration(seconds: 1), () {\n    return \"hello 1\";\n  }),\n  // 抛出一个异常\n  Future.delayed(new Duration(seconds: 2),(){\n    throw AssertionError(\"Error\");\n  }),\n  // 3秒后返回结果\n  Future.delayed(new Duration(seconds: 3), () {\n    return \"hello 3\";\n  })\n]).listen((data){\n   print(data);\n}, onError: (e){\n   print(e.message);\n},onDone: (){\n\n});\n```\n\n上面的代码依次会输出：\n\n```\nI/flutter (17666): hello 1\nI/flutter (17666): Error\nI/flutter (17666): hello 3\n```\n\n代码很简单，就不赘述了。\n\n> 思考题：既然Stream可以接收多次事件，那能不能用Stream来实现一个订阅者模式的事件总线？\n\n\n\n## 1.4.5 Dart和Java及JavaScript对比\n\n通过上面介绍，相信你对Dart应该有了一个初步的印象，由于笔者平时也使用Java和JavaScript，下面笔者根据自己的经验，结合Java和JavaScript，谈一下自己的看法。\n\n> 之所以将Dart与Java和JavaScript对比，是因为，这两者分别是强类型语言和弱类型语言的典型代表，并且Dart 语法中很多地方也都借鉴了Java和JavaScript。\n\n### Dart vs Java\n\n客观的来讲，Dart在语法层面确实比Java更有表现力；在VM层面，Dart VM在内存回收和吞吐量都进行了反复的优化，但具体的性能对比，笔者没有找到相关测试数据，但在笔者看来，只要Dart语言能流行，VM的性能就不用担心，毕竟Google在Go（没用VM但有GC）、JavaScript（v8）、Dalvik（Android上的Java VM）上已经有了很多技术积淀。值得注意的是Dart在Flutter中已经可以将GC做到10ms以内，所以Dart和Java相比，决胜因素并不会是在性能方面。而在语法层面，Dart要比Java更有表现力，最重要的是Dart对函数式编程支持要远强于Java(目前只停留在Lambda表达式)，而Dart目前真正的不足是**生态**，但笔者相信，随着Flutter的逐渐火热，会回过头来反推Dart生态加速发展，对于Dart来说，现在需要的是时间。\n\n### Dart vs JavaScript\n\nJavaScript的弱类型一直被抓短，所以TypeScript、CoffeeScript甚至是Facebook的flow（虽然并不能算JavaScript的一个超集，但也通过标注和打包工具提供了静态类型检查）才有市场。就笔者使用过的脚本语言中（笔者曾使用过Python、PHP），JavaScript无疑是**动态化**支持最好的脚本语言，比如在JavaScript中，可以给任何对象在任何时候动态扩展属性，对于精通JavaScript的高手来说，这无疑是一把利剑。但是，任何事物都有两面性，JavaScript的强大的动态化特性也是把双刃剑，你可经常听到另一个声音，认为JavaScript的这种动态性糟糕透了，太过灵活反而导致代码很难预期，无法限制不被期望的修改。毕竟有些人总是对自己或别人写的代码不放心，他们希望能够让代码变得可控，并期望有一套静态类型检查系统来帮助自己减少错误。正因如此，在Flutter中，Dart几乎放弃了脚本语言动态化的特性，如不支持反射、也不支持动态创建函数等。并且Dart在2.0强制开启了类型检查（Strong Mode），原先的检查模式（checked mode）和可选类型（optional type）将淡出，所以在类型安全这个层面来说，Dart和TypeScript、CoffeeScript是差不多的，所以单从这一点来看，Dart并不具备什么明显优势，但综合起来看，Dart既能进行服务端脚本、APP开发、web开发，这就有优势了！\n\n综上所述，笔者还是很看好Dart语言的将来，之所以表这个态，是因为在新技术发展初期，很多人可能还有所摇摆，有所犹豫，所以有必要给大家打一剂强心针，当然，这是一个见仁见智的问题，大家可以各抒己见。\n\n"
  },
  {
    "path": "src/v2/chapter1/flutter_intro.md",
    "content": "# 1.2 初识Flutter\n\n## 1.2.1 Flutter简介\n\nFlutter 是 Google推出并开源的移动应用开发框架，主打跨平台、高保真、高性能。开发者可以通过 Dart语言开发 App，一套代码同时运行在 iOS 和 Android平台。 Flutter提供了丰富的组件、接口，开发者可以很快地为 Flutter添加 native扩展。同时 Flutter还使用 Native引擎渲染视图，这无疑能为用户提供良好的体验。\n\n#### 跨平台自绘引擎\n\nFlutter与用于构建移动应用程序的其它大多数框架不同，因为Flutter既不使用WebView，也不使用操作系统的原生控件。 相反，Flutter使用自己的高性能渲染引擎来绘制widget。这样不仅可以保证在Android和iOS上UI的一致性，而且也可以避免对原生控件依赖而带来的限制及高昂的维护成本。\n\nFlutter使用Skia作为其2D渲染引擎，Skia是Google的一个2D图形处理函数库，包含字型、坐标转换，以及点阵图都有高效能且简洁的表现，Skia是跨平台的，并提供了非常友好的API，目前Google Chrome浏览器和Android均采用Skia作为其绘图引擎。\n\n目前Flutter默认支持iOS、Android、Fuchsia（Google新的自研操作系统）三个移动平台。但Flutter亦可支持Web开发（Flutter for web）和PC开发，本书的示例和介绍主要是基于iOS和Android平台的，其它平台读者可以自行了解。\n\n#### 高性能\n\nFlutter高性能主要靠两点来保证，首先，Flutter APP采用Dart语言开发。Dart在 JIT（即时编译）模式下，速度与 JavaScript基本持平。但是 Dart支持 AOT，当以 AOT模式运行时，JavaScript便远远追不上了。速度的提升对高帧率下的视图数据计算很有帮助。其次，Flutter使用自己的渲染引擎来绘制UI，布局数据等由Dart语言直接控制，所以在布局过程中不需要像RN那样要在JavaScript和Native之间通信，这在一些滑动和拖动的场景下具有明显优势，因为在滑动和拖动过程往往都会引起布局发生变化，所以JavaScript需要和Native之间不停的同步布局信息，这和在浏览器中要JavaScript频繁操作DOM所带来的问题是相同的，都会带来比较可观的性能开销。\n\n\n\n#### 采用Dart语言开发\n\n这是一个很有意思，但也很有争议的问题，在了解Flutter为什么选择了 Dart而不是 JavaScript之前我们先来介绍两个概念：JIT和AOT。\n\n目前，程序主要有两种运行方式：静态编译与动态解释。静态编译的程序在执行前全部被翻译为机器码，通常将这种类型称为**AOT** （Ahead of time）即 “提前编译”；而解释执行的则是一句一句边翻译边运行，通常将这种类型称为**JIT**（Just-in-time）即“即时编译”。AOT程序的典型代表是用C/C++开发的应用，它们必须在执行前编译成机器码，而JIT的代表则非常多，如JavaScript、python等，事实上，所有脚本语言都支持JIT模式。但需要注意的是JIT和AOT指的是程序运行方式，和编程语言并非强关联的，有些语言既可以以JIT方式运行也可以以AOT方式运行，如Java、Python，它们可以在第一次执行时编译成中间字节码、然后在之后执行时可以直接执行字节码，也许有人会说，中间字节码并非机器码，在程序执行时仍然需要动态将字节码转为机器码，是的，这没有错，不过通常我们区分是否为AOT的标准就是看代码在执行之前是否需要编译，只要需要编译，无论其编译产物是字节码还是机器码，都属于AOT。在此，读者不必纠结于概念，概念就是为了传达精神而发明的，只要读者能够理解其原理即可，得其神忘其形。\n\n现在我们看看Flutter为什么选择Dart语言？笔者根据官方解释以及自己对Flutter的理解总结了以下几条（由于其它跨平台框架都将JavaScript作为其开发语言，所以主要将Dart和JavaScript做一个对比）：\n\n1. **开发效率高**\n\n   Dart运行时和编译器支持Flutter的两个关键特性的组合：\n\n   **基于JIT的快速开发周期**：Flutter在开发阶段采用，采用JIT模式，这样就避免了每次改动都要进行编译，极大的节省了开发时间；\n\n   **基于AOT的发布包**:  Flutter在发布时可以通过AOT生成高效的ARM代码以保证应用性能。而JavaScript则不具有这个能力。\n\n2. **高性能**\n\n   Flutter旨在提供流畅、高保真的的UI体验。为了实现这一点，Flutter中需要能够在每个动画帧中运行大量的代码。这意味着需要一种既能提供高性能的语言，而不会出现会丢帧的周期性暂停，而Dart支持AOT，在这一点上可以做的比JavaScript更好。\n\n3. **快速内存分配**\n\n   Flutter框架使用函数式流，这使得它在很大程度上依赖于底层的内存分配器。因此，拥有一个能够有效地处理琐碎任务的内存分配器将显得十分重要，在缺乏此功能的语言中，Flutter将无法有效地工作。当然Chrome V8的JavaScript引擎在内存分配上也已经做的很好，事实上Dart开发团队的很多成员都是来自Chrome团队的，所以在内存分配上Dart并不能作为超越JavaScript的优势，而对于Flutter来说，它需要这样的特性，而Dart也正好满足而已。\n\n4. **类型安全**\n\n   由于Dart是类型安全的语言，支持静态类型检测，所以可以在编译前发现一些类型的错误，并排除潜在问题，这一点对于前端开发者来说可能会更具有吸引力。与之不同的，JavaScript是一个弱类型语言，也因此前端社区出现了很多给JavaScript代码添加静态类型检测的扩展语言和工具，如：微软的TypeScript以及Facebook的Flow。相比之下，Dart本身就支持静态类型，这是它的一个重要优势。\n\n5. **Dart团队就在你身边**\n\n   看似不起眼，实则举足轻重。由于有Dart团队的积极投入，Flutter团队可以获得更多、更方便的支持，正如Flutter官网所述“我们正与Dart社区进行密切合作，以改进Dart在Flutter中的使用。例如，当我们最初采用Dart时，该语言并没有提供生成原生二进制文件的工具链（这对于实现可预测的高性能具有很大的帮助），但是现在它实现了，因为Dart团队专门为Flutter构建了它。同样，Dart VM之前已经针对吞吐量进行了优化，但团队现在正在优化VM的延迟时间，这对于Flutter的工作负载更为重要。” \n\n#### 总结\n\n本节主要介绍了一下Flutter的特点，如果你感到有些点还不是很好理解，不用着急，随着日后对Flutter细节的了解，再回过头来看，相信你会有更深的体会。\n\n\n\n   \n\n## 1.2.2 Flutter框架结构\n\n本节我们先对Flutter的框架做一个整体介绍，旨在让读者心中有一个整体的印象，这对初学者来说非常重要。如果一下子便深入到Flutter中，就会像是一个在沙漠中没有地图的人，即使可以找到一个绿洲，但是他也不会知道下一个绿洲在哪。因此，无论学什么技术，都要先有一张清晰的“地图”，而我们的学习过程就是“按图索骥”，这样我们才不会陷于细节而“目无全牛”。言归正传，我们看一下Flutter官方提供的Flutter框架图，如图1-1所示：\n\n![图1-1](../imgs/1-1.png)\n\n\n\n### Flutter Framework\n\n这是一个纯 Dart实现的 SDK，它实现了一套基础库，自底向上，我们来简单介绍一下：\n\n- 底下两层（Foundation和Animation、Painting、Gestures）在Google的一些视频中被合并为一个dart UI层，对应的是Flutter中的`dart:ui`包，它是Flutter引擎暴露的底层UI库，提供动画、手势及绘制能力。\n\n- Rendering层，这一层是一个抽象的布局层，它依赖于dart UI层，Rendering层会构建一个UI树，当UI树有变化时，会计算出有变化的部分，然后更新UI树，最终将UI树绘制到屏幕上，这个过程类似于React中的虚拟DOM。Rendering层可以说是Flutter UI框架最核心的部分，它除了确定每个UI元素的位置、大小之外还要进行坐标变换、绘制(调用底层dart:ui)。\n\n- Widgets层是Flutter提供的的一套基础组件库，在基础组件库之上，Flutter还提供了 Material 和Cupertino两种视觉风格的组件库。而**我们Flutter开发的大多数场景，只是和这两层打交道**。\n\n\n### Flutter Engine\n\n这是一个纯 C++实现的 SDK，其中包括了 Skia引擎、Dart运行时、文字排版引擎等。在代码调用 `dart:ui`库时，调用最终会走到Engine层，然后实现真正的绘制逻辑。\n\n### 总结\n\nFlutter框架本身有着良好的分层设计，本节旨在让读者对Flutter整体框架有个大概的印象，相信到现在为止，读者已经对Flutter有一个初始印象，在我们正式动手之前，我们还需要了解一下Flutter的开发语言Dart。\n\n\n## 1.2.3 如何学习Flutter\n\n本节给大家一些学习建议，分享一下笔者在学习Flutter中的一些心得，希望可以帮助你提高学习效率，避免不必要的坑。\n\n### 资源\n\n- **官网**：阅读Flutter官网的资源是快速入门的最佳方式，同时官网也是了解最新Flutter发展动态的地方，由于目前Flutter仍然处于快速发展阶段，所以建议读者还是时不时的去官网看看有没有新的动态。\n\n- **源码及注释**：源码注释应作为学习Flutter的第一文档，Flutter SDK的源码是开源的，并且注释非常详细，也有很多示例，实际上，Flutter官方的SDK文档就是通过注释生成的。源码结合注释可以帮你解决大多数问题。\n- **Github**：如果遇到的问题在StackOverflow上也没有找到答案，可以去github flutter 项目下提issue。\n- **Gallery源码**：Gallery是Flutter官方示例APP，里面有丰富的示例，读者可以在网上下载安装。Gallery的源码在Flutter源码“examples”目录下。\n\n### 社区\n\n- **StackOverflow**：如果你还没听过StackOverflow，这是目前全球最大的程序员问答社区，现在也是活跃度最高的Flutter问答社区。StackOverflow上面除了世界各地的Flutter使用者会在上面交流之外，Flutter开发团队的成员也经常会在上面回答问题。\n- **Flutter中文网社区**：Flutter中文网(https://flutterchina.club)是笔者维护中文网站，目前也是最大的中文资源社区，上面提供了Flutter官网的文档翻译、开源项目、及案例，还有申请加入组织的入口哦。\n- **博客**：随着Flutter技术的推广，相信很快网上将会有很多Flutter相关的文章、博客，读者可以多去浏览、阅读。\n\n### 总结\n\n有了资料和社区后，对于我们学习者自身来说，最重要的还是要多动手、多实践，在本书后面的章节中，希望读者能够亲自动手写一下示例。准备好了吗，下一章中，我们将正式进入Flutter的世界！\n\n\n\n\n\n\n\n"
  },
  {
    "path": "src/v2/chapter1/index.md",
    "content": "## 本章目录   \n* [移动开发技术简介](mobile_development_intro.md)\n* [Flutter简介](flutter_intro.md)  \n* [搭建Flutter开发环境](install_flutter.md)        \n* [Dart语言简介](dart.md)    \n"
  },
  {
    "path": "src/v2/chapter1/install_flutter.md",
    "content": "# 1.3 搭建Flutter开发环境\n\n工欲善其事必先利其器，本节首先会分别介绍一下在Windows和macOS下Flutter SDK的安装，然后再介绍一下配IDE和模拟器的使用。\n\n## 1.3.1 安装Flutter\n\n由于Flutter会同时构建Android和IOS两个平台的发布包，所以Flutter同时依赖Android SDK和iOS SDK，在安装Flutter时也需要安装相应平台的构建工具和SDK。下面我们分别介绍一下Windows和macOS下的环境搭建。\n\n> 注意：本节介绍的安装方式随着Flutter的升级可能会发生变化，如果下面介绍的内容在您安装Flutter时已经失效，请访问Flutter官网，按照官网最新的安装教程安装。\n\n### 使用镜像\n\n由于在国内访问Flutter有时可能会受到限制，Flutter官方为中国开发者搭建了临时镜像，大家可以将如下环境变量加入到用户环境变量中：\n\n```\nexport PUB_HOSTED_URL=https://pub.flutter-io.cn\nexport FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn\n```\n\n**注意：** 此镜像为临时镜像，并不能保证一直可用，读者可以参考https://flutter.io/community/china 以获得有关镜像服务器的最新动态。\n\n### 在Windows上搭建Flutter开发环境\n\n#### 系统要求\n\n要安装并运行Flutter，您的开发环境必须满足以下最低要求:\n\n- 操作系统: Windows 7 或更高版本 (64-bit)\n\n- 磁盘空间: 400 MB (不包括Android Studio的磁盘空间).\n\n- 工具: Flutter 依赖下面这些命令行工具.\n\n  * [PowerShell 5.0](https://docs.microsoft.com/en-us/powershell/scripting/setup/installing-windows-powershell#upgrading-existing-windows-powershell) 或更新的版本\n\n  * [Git for Windows](https://git-scm.com/download/win)  (Git命令行工具)；\n\n  如果已安装Git for Windows，请确保可以在命令提示符或PowerShell中运行 git 命令\n\n#### 获取Flutter SDK\n\n1. 去flutter官网下载其最新可用的安装包，下载地址：https://flutter.dev/docs/development/tools/sdk/releases ，打开后如图1-2所示：\n\n   ![图1-2](../imgs/1-2.png)\n\n   \n\n   注意，Flutter的渠道版本会不停变动，请以Flutter官网为准。另外，在中国大陆地区，要想正常获取安装包列表或下载安装包，可能需要翻墙，读者也可以去Flutter github项目下去下载安装包，地址：https://github.com/flutter/flutter/releases 。\n\n2. 将安装包zip解压到你想安装Flutter SDK的路径（如：`C:\\src\\flutter`；注意，**不要**将flutter安装到需要一些高权限的路径如`C:\\Program Files\\`）。\n\n3. 在Flutter安装目录的`flutter`文件下找到`flutter_console.bat`，双击运行并启动**flutter命令行**，接下来，你就可以在Flutter命令行运行flutter命令了。\n\n##### 更新环境变量\n\n如果你想在Windows系统自带命令行运行flutter命令，需要添加以下环境变量到用户PATH：\n\n- 转到 “控制面板>用户帐户>用户帐户>更改我的环境变量”\n- 在“用户变量”下检查是否有名为“Path”的条目:\n  * 如果该条目存在， 追加 flutter\\bin的全路径，使用 ; 作为分隔符.\n  * 如果该条目不存在，创建一个新用户变量 Path ，然后将 `flutter\\bin` 的全路径作为它的值.\n\n重启Windows以应用此更改.\n\n##### 运行 flutter doctor命令\n\n在Flutter命令行运行如下命令来查看是否还需要安装其它依赖，如果需要，安装它们：\n\n```shell\nflutter doctor\n```\n\n该命令检查你的环境并在命令行窗口中显示报告。Dart SDK已经在打包在Flutter SDK里了，没有必要单独安装Dart。 仔细检查命令行输出以获取可能需要安装的其他软件或进一步需要执行的任务。\n\n例如：\n\n```\n[-] Android toolchain - develop for Android devices\n    • Android SDK at D:\\Android\\sdk\n    ✗ Android SDK is missing command line tools; download from https://goo.gl/XxQghQ\n    • Try re-installing or updating your Android SDK,\n      visit https://flutter.io/setup/#android-setup for detailed instructions.\n```\n\n第一次运行flutter命令（如`flutter doctor`）时，它会下载它自己的依赖项并自行编译。以后再运行就会快得多。缺失的依赖需要安装一下，安装完成后再运行`flutter doctor`命令来验证是否安装成功。\n\n#### Android设置\n\nFlutter依赖于Android Studio的全量安装。Android Studio不仅可以管理Android 平台依赖、SDK版本等，而且它也是Flutter开发推荐的IDE之一（当然，你也可以使用其它编辑器或IDE，我们将会在后面讨论）。\n\n##### 安装Android Studio\n\n1. 下载并安装 Android Studio，下载地址：https://developer.android.com/studio/index.html 。\n2. 启动Android Studio，然后执行“Android Studio安装向导”。这将安装最新的Android SDK、Android SDK平台工具和Android SDK构建工具，这些是用Flutter进行Android开发所需要的。\n\n#### 安装遇到问题？\n\n如果在安装过程中遇到问题，可以先去flutter官网查看一下安装方式是否发生变化，或者在网上搜索一下解决方案。\n\n\n\n### 在macOS上搭建Flutter开发环境\n\n在masOS下可以同时进行Android和iOS设备的测试。\n\n#### 系统要求\n\n要安装并运行Flutter，您的开发环境必须满足以下最低要求:\n\n- 操作系统: macOS (64-bit)\n- 磁盘空间: 700 MB (不包括Xcode或Android Studio的磁盘空间）.\n- 工具: Flutter 依赖下面这些命令行工具.\n  * `bash、mkdir、rm、git、curl、unzip、which`\n\n#### 获取Flutter SDK\n\n1. 去flutter官网下载其最新可用的安装包，官网地址：https://flutter.io/sdk-archive/#macos\n\n   注意，Flutter的渠道版本会不停变动，请以Flutter官网为准。另外，在中国大陆地区，要想正常获取安装包列表或下载安装包，可能需要翻墙，读者也可以去Flutter github项目下去下载安装包，地址：https://github.com/flutter/flutter/releases 。\n\n2. 解压安装包到你想安装的目录，如：\n\n   ```shell\n   cd ~/development\n   unzip ~/Downloads/flutter_macos_v0.5.1-beta.zip\n   ```\n\n3. 添加`flutter`相关工具到path中：\n\n   ```shell\n   export PATH=`pwd`/flutter/bin:$PATH\n   ```\n\n   此代码只能暂时针对当前命令行窗口设置PATH环境变量，要想永久将Flutter添加到PATH中请参考下面**更新环境变量** 部分。\n\n##### 运行 flutter doctor命令\n\n这一步和Windows下步骤一致，不再赘述。\n\n##### 更新环境变量\n\n将Flutter添加到PATH中，可以在任何终端会话中运行`flutter`命令。\n\n对于所有终端会话永久修改此变量的步骤是和特定计算机系统相关的。通常，您会在打开新窗口时将设置环境变量的命令添加到执行的文件中。例如\n\n1. 确定您Flutter SDK的目录记为“FLUTTER_INSTALL_PATH”，您将在步骤3中用到。\n\n2. 打开(或创建) `$HOME/.bash_profile`。文件路径和文件名可能在你的电脑上不同.\n\n3. 添加以下路径:\n\n   ```shell\n   export PATH=[FLUTTER_INSTALL_PATH]/flutter/bin:$PATH\n   ```\n   例如笔者Flutter 安装目录是“~/code/flutter_dir”，那么代码为：\n\n   ```\n   export PATH=~/code/flutter_dir/flutter/bin:$PATH\n   ```\n\n4. 运行 `source $HOME/.bash_profile` 刷新当前终端窗口。\n\n   > **注意:** 如果你使用终端是zsh，终端启动时 `~/.bash_profile` 将不会被加载，解决办法就是修改 `～/.zshrc` ，在其中添加：source ～/.bash_profile\n\n5. 验证“flutter/bin”是否已在PATH中：\n\n   ```\n   echo $PATH\n   ```\n\n\n\n#### 安装 Xcode\n\n要为iOS开发Flutter应用程序，您需要Xcode 9.0或更高版本:\n\n1. 安装Xcode 9.0或更新版本(通过[链接下载](https://developer.apple.com/xcode/)或[苹果应用商店](https://itunes.apple.com/us/app/xcode/id497799835)).\n2. 配置Xcode命令行工具以使用新安装的Xcode版本 `sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer` 对于大多数情况，当您想要使用最新版本的Xcode时，这是正确的路径。如果您需要使用不同的版本，请指定相应路径。\n3. 确保Xcode许可协议是通过打开一次Xcode或通过命令`sudo xcodebuild -license`同意过了.\n\n使用Xcode，您可以在iOS设备或模拟器上运行Flutter应用程序。\n\n#### 安装Android Studio\n\n和Window一样，要在Android设备上构建并运行Flutter程序都需要先安装Android Studio，读者可以先自行下载并安装Android Studio，在此不再赘述。\n\n### 升级 Flutter\n\n#### Flutter SDK分支\n\nFlutter SDK有多个分支，如beta、dev、master、stable，其中stable分支为稳定分支（日后有新的稳定版本发布后可能也会有新的稳定分支，如1.0.0），dev和master为开发分支，安装flutter后，你可以运行`flutter channel`查看所有分支，如笔者本地运行后，结果如下：\n\n```\nFlutter channels:\n  beta\n  dev\n* master\n```\n\n带\"*\"号的分支即你本地的Flutter SDK 跟踪的分支，要切换分支，可以使用`flutter channel beta` 或 `flutter channel master`，Flutter官方建议跟踪稳定分支，但你也可以跟踪`master`分支，这样可以查看最新的变化，但这样稳定性要低的多。\n\n#### 升级Flutter SDK和依赖包\n\n要升级flutter sdk，只需一句命令：\n\n```shell\nflutter upgrade\n```\n\n该命令会同时更新Flutter SDK和你的flutter项目依赖包。如果你只想更新项目依赖包（不包括Flutter SDK），可以使用如下命令：\n\n- `flutter packages get`获取项目所有的依赖包。\n- `flutter packages upgrade` 获取项目所有依赖包的最新版本。\n\n#### \n\n\n\n## 1.3.2 IDE配置与使用\n\n理论上可以使用任何文本编辑器与命令行工具来构建Flutter应用程序。 不过，Flutter官方建议使用Android Studio和VS Code之一以获得更好的开发体验。Flutter官方提供了这两款编辑器插件，通过IDE和插件可获得代码补全、语法高亮、widget编辑辅助、运行和调试支持等功能，可以帮助我们极大的提高开发效率。下面我们分别介绍一下Android Studio和VS Code的配置及使用（Android Studio和VS Code读者可以在其官网获得最新的安装，由于安装比较简单，故不再赘述）。\n\n### Android Studio 配置与使用\n\n由于Android Studio是基于IntelliJ IDEA开发的，所以读者也可以使用IntelliJ IDEA。\n\n#### 安装Flutter和Dart插件\n\n需要安装两个插件:\n\n- `Flutter`插件： 支持Flutter开发工作流 (运行、调试、热重载等)。\n- `Dart`插件： 提供代码分析 (输入代码时进行验证、代码补全等)。\n\n安装步骤：\n\n1. 启动Android Studio。\n2. 打开插件首选项 (macOS：**Preferences>Plugins**, Windows：**File>Settings>Plugins**)。\n3. 选择 **Browse repositories…**，选择 flutter 插件并点击 `install`。\n4. 重启Android Studio后插件生效。\n\n接下来，让我们用Android Studio创建一个Flutter项目，然后运行它，并体验“热重载”。\n\n#### 创建Flutter应用\n\n1. 选择 **File>New Flutter Project** 。\n2. 选择 **Flutter application** 作为 project 类型, 然后点击 Next。\n3. 输入项目名称 (如 `myapp`)，然后点击 Next。\n4. 点击 **Finish**。\n5. 等待Android Studio安装SDK并创建项目。\n\n上述命令创建一个Flutter项目，项目名为myapp，其中包含一个使用[Material 组件](https://material.io/guidelines/)的简单演示应用程序。\n\n在项目目录中，您应用程序的代码位于 `lib/main.dart`。\n\n#### 运行应用程序\n\n1. 定位到Android Studio工具栏，如图1-3所示：\n\n   ![图1-3](../imgs/1-3.png)\n\n2. 在 **target selector** 中, 选择一个运行该应用的Android设备。如果没有列出可用，请选择 **Tools>Android>AVD Manager** 并在那里创建一个。\n\n3. 在工具栏中点击 **Run图标**。\n\n4. 如果一切正常, 您应该在您的设备或模拟器上会看到启动的应用程序：\n\n   ![图1-4](../imgs/1-4.png)\n\n\n#### 体验热重载\n\nFlutter 可以通过 *热重载（hot reload）* 实现快速的开发周期，热重载就是无需重启应用程序就能实时加载修改后的代码，并且不会丢失状态。简单的对代码进行更改，然后告诉IDE或命令行工具你需要重新加载（点击reload按钮），你就会在你的设备或模拟器上看到更改。\n\n1. 打开`lib/main.dart`文件\n2. 将字符串\n`'You have pushed the button this many times:'` 更改为\n`'You have clicked the button this many times:'`\n3. 不要按“停止”按钮; 让您的应用继续运行.\n4. 要查更改，请调用 **Save** (`cmd-s` / `ctrl-s`)，或者点击 **热重载按钮** (带有闪电⚡️图标的按钮)。\n\n   你会立即在运行的应用程序中看到更新的字符串。\n\n### VS Code的配置与使用\n\nVS Code是一个轻量级编辑器，支持Flutter运行和调试。\n\n#### 安装flutter插件\n\n1. 启动 VS Code。\n2. 调用 **View>Command Palette…**。\n3. 输入 ‘install’, 然后选择 **Extensions: Install Extension** action。\n4. 在搜索框输入 `flutter` ，在搜索结果列表中选择 ‘Flutter’, 然后点击 **Install**。\n5. 选择 ‘OK’ 重新启动 VS Code。\n6. 验证配置\n   * 调用 **View>Command Palette…**\n   * 输入 ‘doctor’, 然后选择 **‘Flutter: Run Flutter Doctor’** action。\n   * 查看“OUTPUT”窗口中的输出是否有问题\n\n#### 创建Flutter应用\n\n1. 启动 VS Code\n2. 调用 **View>Command Palette…**\n3. 输入 ‘flutter’, 然后选择 **‘Flutter: New Project’** action\n4. 输入 Project 名称 (如`myapp`), 然后按回车键\n5. 指定放置项目的位置，然后按蓝色的确定按钮\n6. 等待项目创建继续，并显示main.dart文件\n\n#### 体验热重载\n\n1. 打开`lib/main.dart`文件。\n2. 将字符串\n   `'You have pushed the button this many times:'` 更改为\n   `'You have clicked the button this many times:'`。\n3. 不要按“停止”按钮; 让您的应用继续运行。\n4. 要查看您的更改，请调用 **Save** (`cmd-s` / `ctrl-s`), 或者点击 **热重载按钮** (绿色圆形箭头按钮)。\n\n你会立即在运行的应用程序中看到更新的字符串。\n\n\n\n## 1.3.3 连接设备运行Flutter应用\n\nWindow下只支持为Android设备构建并运行Flutter应用，而macOS同时支持iOS和Android设备。下面分别介绍如何连接Android和iOS设备来运行flutter应用。\n\n### 连接Android模拟器\n\n要准备在Android模拟器上运行并测试Flutter应用，请按照以下步骤操作：\n\n1. 启动 **Android Studio>Tools>Android>AVD Manager** 并选择 **Create Virtual Device**.\n\n2. 选择一个设备并选择 **Next**。\n\n3. 为要模拟的Android版本选择一个或多个系统印象，然后选择 **Next**. 建议使用 *x86* 或 *x86_64* image .\n\n4. 在 “Emulated Performance”下, 选择 **Hardware - GLES 2.0** 以启用 [硬件加速](https://developer.android.com/studio/run/emulator-acceleration.html).\n\n5. 验证AVD配置是否正确，然后选择 **Finish**。\n\n   有关上述步骤的详细信息，请参阅 [Managing AVDs](https://developer.android.com/studio/run/managing-avds.html).\n\n6. 在“Android Virtual Device Manager”中，点击工具栏的 **Run**。模拟器启动并显示所选操作系统版本或设备的启动画面。\n\n7. 运行 `flutter run` 启动您的设备。 连接的设备名是 `Android SDK built for <platform>`，其中 *platform* 是芯片系列，如 x86。\n\n### 连接Android真机设备\n\n要准备在Android设备上运行并测试Flutter应用，需要Android 4.1（API level 16）或更高版本的Android设备.\n\n1. 在Android设备上启用 **开发人员选项** 和 **USB调试** 。详细说明可在[Android文档](https://developer.android.com/studio/debug/dev-options.html)中找到。\n2. 使用USB将手机插入电脑。如果设备出现调试授权提示，请授权你的电脑可以访问该设备。\n3. 在命令行运行 `flutter devices` 命令以验证Flutter识别您连接的Android设备。\n4. 运行启动你应用程序 `flutter run`。\n\n默认情况下，Flutter使用的Android SDK版本是基于你的 `adb` 工具版本。 如果想让Flutter使用不同版本的Android SDK，则必须将该 `ANDROID_HOME` 环境变量设置为相应的SDK安装目录。\n\n### 连接iOS模拟器\n\n要准备在iOS模拟器上运行并测试Flutter应用，请按以下步骤操作：\n\n1. 在你的MAC上，通过 Spotlight 或以下命令找到模拟器：\n\n   ```shell\n   open -a Simulator\n   ```\n\n2. 通过检查模拟器 **Hardware > Device** 菜单中的设置，确保模拟器正在使用64位设备（iPhone 5s或更高版本）。\n\n3. 根据你电脑屏幕大小，模拟高清屏iOS设备可能会溢出屏幕。可以在模拟器的 **Window> Scale** 菜单下设置设备比例。\n\n4. 运行 `flutter run`启动flutter应用程序。\n\n### 连接iOS真机设备\n\n要将Flutter应用安装到iOS真机设备，需要一些额外的工具和一个Apple帐户，还需要在Xcode中进行一些设置。\n\n1. 安装 [homebrew](http://brew.sh/) （如果已经安装了brew,跳过此步骤）。\n\n2. 打开终端并运行如下这些命令:\n\n   ```shell\n   brew update\n   brew install --HEAD libimobiledevice\n   brew install ideviceinstaller ios-deploy cocoapods\n   pod setup\n   ```\n\n   如果这些命令中的任何一个失败并出现错误，请运行brew doctor并按照说明解决问题.\n\n3. 遵循Xcode签名流程来配置您的项目:\n\n   * 在你Flutter项目目录中通过 `open ios/Runner.xcworkspace` 打开默认的Xcode workspace.\n\n   * 在Xcode中，选择导航面板左侧中的`Runner`项目。\n\n   * 在`Runner` target设置页面中，确保在 **General > Signing > Team** 下选择了你的开发团队。当你选择一个团队时，Xcode会创建并下载开发证书，向你的设备注册你的帐户，并创建和下载配置文件（如果需要）。\n\n   * 要开始您的第一个iOS开发项目，您可能需要使用您的Apple ID登录Xcode，如图1-5：\n\n     ![图1-5](../imgs/1-5.png)\n\n     任何Apple ID都支持开发和测试，但若想将应用分发到App Store，就必须注册Apple开发者计划，有关详情读者可以自行了解。\n\n   4. 当您第一次attach真机设备进行iOS开发时，需要同时信任你的Mac和该设备上的开发证书。首次将iOS设备连接到Mac时，请在对话框中选择 `Trust`。 \n\n      ![添加信任](../imgs/1-6.png)\n\n      然后，转到iOS设备上的**设置**菜单，选择 **常规>设备管理** 并信任您的证书。\n\n   5. 如果Xcode中的自动签名失败，请验证项目的 **General > Identity > Bundle Identifier** 值是否唯一，如图1-7所示：\n\n      ![验证bundle id是否唯一](../imgs/1-7.png)\n\n   6. 运行 `flutter run`启动flutter应用程序。\n   \n\n## 1.3.4 常见配置问题\n\n### Android Studio问题\n\n#### 缺少依赖库问题\n\n上手安卓最常遇见的问题之一，错误如图1-8所示，此时点击超链接即可自动跳转到安装页面\n\n![图1-8](../imgs/1-8.png)\n\n安装之后重新运行即可，如图1-9：\n\n![install_request_components.png](../imgs/1-9.png)\n\n#### 连接不上Android Repository\n\n这也是最常见的问题之一，当你发现自己无法下载部分依赖的时候，请优先考虑这种情况。进入 `File` -> `Settings` -> `Appearance & Behavior` -> `System Settings` -> `Android SDK` -> `SDK Update Sites` 列表，可以看到此时的 `Android Repository` 无法连接，如图1-10所示：\n\n![下载依赖失败](../imgs/1-10.png)\n\n这是由于要去Google下载Android SDK，但在国内目前无法访问Google所致，因此，我们可以配置代理或使用vpn。\n\n#### 安卓包配置问题\n\n一般格式为\n\n```\nCould not HEAD **\nCould not Get **\n```\n\n如：`Android Studio Could not GET gradle-3.2.0.pom`\n\n这一类问题是由于无法连接到 Maven 库造成的，解决方法如下：\n\n1. 进入`当前所在项目名/android`\n\n2. 打开 `build.gradle` \n\n3. 找到下面这一部分，并加上 `maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }` \n\n   ```\n   allprojects {\n       repositories {\n         google()\n         jcenter()\n         maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' } //添加这一句\n   \t}\n   }\n   ```\n\n4. 进入 File/ Settings/ Build, Execution, Deployment/ BuildTools/ Gradle/ Android Studio 中，勾选上 Enable embedded Maven repository ，重启 Android Studio 即可解决。\n\n   > **注意：**存在这样的一种情况，当你根据上述步骤设置了之后，依旧无法解决这个问题，并有类似于 `Could not HEAD maven.aliyun.com` 的报错信息，请检查 `C:\\Users\\{user_name}\\.gradle\\gradle.properties` 是否有设置代理。删除后问题即可解决。\n\n#### Hot Reload 热重载失效问题\n\n在给 `Terminal` 之类的终端模拟器设置代理之后，会导致“Hot Reload”重载失效，此时调用 **Save** (`cmd-s` / `ctrl-s`)将不会进行热重载，**热重载按钮** (带有闪电⚡️图标的按钮)也不会显示，将代理移除即可解决。\n\n另外，有些情况下热重载是不生效的，比如修改了`main`函数、修改了全局静态方法等，读者可以认为“Hot Reload”只会重新构建整个widget树，如果变动不在构建widget树的过程中，“Hot Reload”就不会起作用。\n\n\n\n\n\n\n"
  },
  {
    "path": "src/v2/chapter1/mobile_development_intro.md",
    "content": "\n\n# 1.1 移动开发技术简介\n\n本节将主要介绍一下移动开发技术的进化历程，主要是想让读者知道Flutter技术出现的背景。笔者认为，了解一门新技术出现的背景是非常重要的，因为只有了解之前是什么样的，才能理解为什么会是现在这样。\n\n## 1.1.1 原生开发与跨平台技术\n\n### 原生开发\n\n原生应用程序是指某一个移动平台（比如iOS或安卓）所特有的应用，使用相应平台支持的开发工具和语言，并直接调用系统提供的SDK API。比如Android原生应用就是指使用Java或Kotlin语言直接调用Android SDK开发的应用程序；而iOS原生应用就是指通过Objective-C或Swift语言直接调用iOS SDK开发的应用程序。原生开发有以下主要优势：\n\n- 可访问平台全部功能（GPS、摄像头）；\n- 速度快、性能高、可以实现复杂动画及绘制，整体用户体验好；\n\n主要缺点：\n\n- 平台特定，开发成本高；不同平台必须维护不同代码，人力成本随之变大；\n- 内容固定，动态化弱，大多数情况下，有新功能更新时只能发版；\n\n在移动互联网发展初期，业务场景并不复杂，原生开发还可以应对产品需求迭代。 但近几年，随着物联网时代到来、移动互联网高歌猛进，日新月异，在很多业务场景中，传统的纯原生开发已经不能满足日益增长的业务需求。主要表现在：\n\n- 动态化内容需求增大；当需求发生变化时，纯原生应用需要通过版本升级来更新内容，但应用上架、审核是需要周期的，这对高速变化的互联网时代来说是很难接受的，所以，对应用动态化(不发版也可以更新应用内容)的需求就变的迫在眉睫。\n- 业务需求变化快，开发成本变大；由于原生开发一般都要维护Android、iOS两个开发团队，版本迭代时，无论人力成本，还是测试成本都会变大。\n\n总结一下，纯原生开发主要面临动态化和开发成本两个问题，而针对这两个问题，诞生了一些跨平台的动态化框架。\n\n\n\n### 跨平台技术简介\n\n针对原生开发面临问题，人们一直都在努力寻找好的解决方案，而时至今日，已经有很多跨平台框架(注意，本书中所指的“跨平台”若无特殊说明，即特指Android和iOS两个平台)，根据其原理，主要分为三类：\n\n- H5+原生（Cordova、Ionic、微信小程序）\n- JavaScript开发+原生渲染 （React Native、Weex、快应用）\n- 自绘UI+原生(QT for mobile、Flutter)\n\n在接下来的章节中我们逐个来看看这三类框架的原理及优缺点。\n\n\n\n## 1.1.2 Hybrid技术简介\n\n### H5+原生混合开发\n\n这类框架主要原理就是将APP的一部分需要动态变动的内容通过H5来实现，通过原生的网页加载控件WebView (Android)或WKWebView（iOS）来加载（以后若无特殊说明，我们用WebView来统一指代android和iOS中的网页加载控件）。这样以来，H5部分是可以随时改变而不用发版，动态化需求能满足；同时，由于h5代码只需要一次开发，就能同时在Android和iOS两个平台运行，这也可以减小开发成本，也就是说，H5部分功能越多，开发成本就越小。我们称这种h5+原生的开发模式为**混合开发 ** ，采用混合模式开发的APP我们称之为**混合应用**或**Hybrid APP**  ，如果一个应用的大多数功能都是H5实现的话，我们称其为**Web APP** 。\n\n目前混合开发框架的典型代表有：Cordova、Ionic 和微信小程序，值得一提的是微信小程序目前是在webview中渲染的，并非原生渲染，但将来有可能会采用原生渲染。\n\n### 混合开发技术点 \n\n如之前所述，原生开发可以访问平台所有功能，而混合开发中，H5代码是运行在WebView中，而WebView实质上就是一个浏览器内核，其JavaScript依然运行在一个权限受限的沙箱中，所以对于大多数系统能力都没有访问权限，如无法访问文件系统、不能使用蓝牙等。所以，对于H5不能实现的功能，都需要原生去做。而混合框架一般都会在原生代码中预先实现一些访问系统能力的API， 然后暴露给WebView以供JavaScript调用，这样一来，WebView就成为了JavaScript与原生API之间通信的桥梁，主要负责JavaScript与原生之间传递调用消息，而消息的传递必须遵守一个标准的协议，它规定了消息的格式与含义，我们把依赖于WebView的用于在JavaScript与原生之间通信并实现了某种消息传输协议的工具称之为**WebView JavaScript Bridge**, 简称 **JsBridge**，它也是混合开发框架的核心。\n\n#### 示例：JavaScript调用原生API获取手机型号\n\n下面我们以Android为例，实现一个获取手机型号的原生API供JavaScript调用。在这个示例中将展示JavaScript调用原生API的流程，读者可以直观的感受一下调用流程。我们选用笔者在Github上开源的dsBridge作为JsBridge来进行通信。dsBridge是一个支持同步调用的跨平台的JsBridge，此示例中只使用其同步调用功能。\n\n1. 首先在原生中实现获取手机型号的API `getPhoneModel`\n\n   ```java\n   class JSAPI {\n    @JavascriptInterface\n    public Object getPhoneModel(Object msg) {\n      return Build.MODEL;\n    }\n   }    \n   ```\n\n2. 将原生API通过WebView注册到JsBridge中\n\n   ```java\n   import wendu.dsbridge.DWebView\n   ...\n   //DWebView继承自WebView，由dsBridge提供  \n   DWebView dwebView = (DWebView) findViewById(R.id.dwebview);\n   //注册原生API到JsBridge\n   dwebView.addJavascriptObject(new JsAPI(), null);\n   ```\n\n3. 在JavaScript中调用原生API\n\n   ```javascript\n   var dsBridge = require(\"dsbridge\")\n   //直接调用原生API `getPhoneModel`\n   var model = dsBridge.call(\"getPhoneModel\");\n   //打印机型\n   console.log(model);\n   ```\n\n上面示例演示了JavaScript调用原生API的过程，同样的，一般来说优秀的JsBridge也支持原生调用JavaScript，dsBridge也是支持的，如果您感兴趣，可以去github dsBridge项目主页查看。\n\n现在，我们回头来看一下，混合应用无非就是在第一步中预先实现一系列API供JavaScript调用，让JavaScript有访问系统的能力，看到这里，我相信你也可以自己实现一个混合开发框架了。\n\n### 总结\n\n混合应用的优点是动态内容是H5，web技术栈，社区及资源丰富，缺点是性能不好，对于复杂用户界面或动画，WebView不堪重任。\n\n\n\n## 1.1.3 React Native、Weex及快应用\n\n本篇主要介绍一下 **JavaScript开发+原生渲染**的跨平台框架原理。\n\nReact Native (简称RN)是Facebook于2015年4月开源的跨平台移动应用开发框架，是Facebook早先开源的JS框架 React 在原生移动应用平台的衍生产物，目前支持iOS和Android两个平台。RN使用Javascript语言，类似于HTML的JSX，以及CSS来开发移动应用，因此熟悉Web前端开发的技术人员只需很少的学习就可以进入移动应用开发领域。\n\n由于RN和React原理相通，并且Flutter也是受React启发，很多思想也都是相通的，万丈高楼平地起，我们有必要深入了解一下React原理。React是一个响应式的Web框架，我们先了解一下两个重要的概念：DOM树与响应式编程。\n\n### DOM树与控件树\n\n文档对象模型（Document Object Model，简称DOM），是W3C组织推荐的处理可扩展标志语言的标准编程接口，一种独立于平台和语言的方式访问和修改一个文档的内容和结构。换句话说，这是表示和处理一个HTML或XML文档的标准接口。简单来说，DOM就是文档树，与用户界面控件树对应，在前端开发中通常指HTML对应的渲染树，但广义的DOM也可以指Android中的XML布局文件对应的控件树，而术语**DOM操作**就是指直接来操作渲染树（或控件树）， 因此，可以看到其实DOM树和控件树是等价的概念，只不过前者常用于Web开发中，而后者常用于原生开发中。\n\n### 响应式编程\n\nReact中提出一个重要思想：状态改变则UI随之自动改变，而React框架本身就是响应用户状态改变的事件而执行重新构建用户界面的工作，这就是典型的**响应式**编程范式，下面我们总结一下React中响应式原理：\n\n- 开发者只需关注状态转移（数据），当状态发生变化，React框架会自动根据新的状态重新构建UI。\n- React框架在接收到用户状态改变通知后，会根据当前渲染树，结合最新的状态改变，通过Diff算法，计算出树中变化的部分，然后只更新变化的部分（DOM操作），从而避免整棵树重构，提高性能。\n\n值得注意的是，在第二步中，状态变化后React框架并不会立即去计算并渲染DOM树的变化部分，相反，React会在DOM的基础上建立一个抽象层，即**虚拟DOM**树，对数据和状态所做的任何改动，都会被自动且高效的同步到虚拟DOM，最后再批量同步到真实DOM中，而不是每次改变都去操作一下DOM。为什么不能每次改变都直接去操作DOM树？这是因为在浏览器中每一次DOM操作都有可能引起浏览器的重绘或回流：\n\n1. 如果DOM只是外观风格发生变化，如颜色变化，会导致浏览器重绘界面。\n2. 如果DOM树的结构发生变化，如尺寸、布局、节点隐藏等导致，浏览器就需要回流（及重新排版布局）。\n\n而浏览器的重绘和回流都是比较昂贵的操作，如果每一次改变都直接对DOM进行操作，这会带来性能问题，而批量操作只会触发一次DOM更新。\n\n> 思考题：Diff操作和DOM批量更新难道不应该是浏览器的职责吗？第三方框架中去做合不合适？\n\n\n\n> 此处需要有一张插图\n\n\n\n### React Native\n\n上文已经提到React Native 是React 在原生移动应用平台的衍生产物，那两者主要的区别是什么呢？其实，主要的区别在于虚拟DOM映射的对象是什么？React中虚拟DOM最终会映射为浏览器DOM树，而RN中虚拟DOM会通过 JavaScriptCore 映射为原生控件树。\n\nJavaScriptCore 是一个JavaScript解释器，它在React Native中主要有两个作用：\n\n1. 为JavaScript提供运行环境。\n2. 是JavaScript与原生应用之间通信的桥梁，作用和JsBridge一样，事实上，在iOS中，很多JsBridge的实现都是基于 JavaScriptCore 。\n\n而RN中将虚拟DOM映射为原生控件的过程中分两步：\n\n1. 布局消息传递； 将虚拟DOM布局信息传递给原生；\n2. 原生根据布局信息通过对应的原生控件渲染控件树；\n\n至此，React Native 便实现了跨平台。 相对于混合应用，由于React Native是原生控件渲染，所以性能会比混合应用中H5好很多，同时React Native使用了Web开发技术栈，也只需维护一份代码，同样是跨平台框架。\n\n### Weex\n\nWeex是阿里巴巴于2016年发布的跨平台移动端开发框架，思想及原理和React Native类似，最大的不同是语法层面，Weex支持Vue语法和Rax语法，Rax 的 DSL(Domain Specific Language) 语法是基于 React JSX 语法而创造。与 React 不同，在 Rax 中 JSX 是必选的，它不支持通过其它方式创建组件，所以学习 JSX 是使用 Rax 的必要基础。而React Native只支持JSX语法。\n\n### 快应用\n\n快应用是华为、小米、OPPO、魅族等国内9大主流手机厂商共同制定的轻量级应用标准，目标直指微信小程序。它也是采用JavaScript语言开发，原生控件渲染，与React Native和Weex相比主要有两点不同：\n\n1. 快应用自身不支持Vue或React语法，其采用原生JavaScript开发，其开发框架和微信小程序很像，值得一提的是小程序目前已经可以使用Vue语法开发（mpvue），从原理上来讲，Vue的语法也可以移植到快应用上。\n2. React Native和Weex的渲染/排版引擎是集成到框架中的，每一个APP都需要打包一份，安装包体积较大；而快应用渲染/排版引擎是集成到ROM中的，应用中无需打包，安装包体积小，正因如此，快应用才能在保证性能的同时做到快速分发。\n\n### 总结\n\nJavaScript开发+原生渲染的方式主要优点如下：\n\n1. 采用Web开发技术栈，社区庞大、上手快、开发成本相对较低。\n2. 原生渲染，性能相比H5提高很多。\n3. 动态化较好，支持热更新。\n\n不足：\n\n1. 渲染时需要JavaScript和原生之间通信，在有些场景如拖动可能会因为通信频繁导致卡顿。\n2. JavaScript为脚本语言，执行时需要JIT(Just In Time)，执行效率和AOT(Ahead Of Time)代码仍有差距。\n3. 由于渲染依赖原生控件，不同平台的控件需要单独维护，并且当系统更新时，社区控件可能会滞后；除此之外，其控件系统也会受到原生UI系统限制，例如，在Android中，手势冲突消歧规则是固定的，这在使用不同人写的控件嵌套时，手势冲突问题将会变得非常棘手。\n\n\n\n## 1.1.4 QT Mobile\n\n在本篇中，我们看看最后一种跨平台技术：自绘UI+原生。这种技术的思路是，通过在不同平台实现一个统一接口的渲染引擎来绘制UI，而不依赖系统原生控件，所以可以做到不同平台UI的一致性。注意，自绘引擎解决的是UI的跨平台问题，如果涉及其它系统能力调用，依然要涉及原生开发。这种平台技术的优点如下：\n\n1. 性能高；由于自绘引擎是直接调用系统API来绘制UI，所以性能和原生控件接近。\n\n2. 灵活、组件库易维护、UI外观保真度和一致性高；由于UI渲染不依赖原生控件，也就不需要根据不同平台的控件单独维护一套组件库，所以代码容易维护。由于组件库是同一套代码、同一个渲染引擎，所以在不同平台，组件显示外观可以做到高保真和高一致性；另外，由于不依赖原生控件，也就不会受原生布局系统的限制，这样布局系统会非常灵活。\n\n不足：\n\n1. 动态性不足；为了保证UI绘制性能，自绘UI系统一般都会采用AOT模式编译其发布包，所以应用发布后，不能像Hybrid和RN那些使用JavaScript（JIT）作为开发语言的框架那样动态下发代码。\n2. 开发效率低：QT使用C++作为其开发语言，而编程效率是直接会影响APP开发效率的，C++作为一门静态语言，在UI开发方面灵活性不及JavaScript这样的动态语言，另外，C++需要开发者手动去管理内存分配，没有JavaScript及Java中垃圾回收（GC）的机制。\n\n\n\n\n也许你已经猜到Flutter就属于这一类跨平台技术，没错，Flutter正是实现一套自绘引擎，并拥有一套自己的UI布局系统。不过，自绘制引擎的思路并不是什么新概念，Flutter并不是第一个尝试这么做的，在它之前有一个典型的代表，即大名鼎鼎的QT。\n\n### QT简介\n\nQt是一个1991年由Qt Company开发的跨平台C++图形用户界面应用程序开发框架。2008年，Qt Company科技被诺基亚公司收购，Qt也因此成为诺基亚旗下的编程语言工具。2012年，Qt被Digia收购。2014年4月，跨平台集成开发环境Qt Creator 3.1.0正式发布，实现了对于iOS的完全支持，新增WinRT、Beautifier等插件，废弃了无Python接口的GDB调试支持，集成了基于Clang的C/C++代码模块，并对Android支持做出了调整，至此实现了全面支持iOS、Android、WP，它提供给应用程序开发者构建图形用户界面所需的所有功能。但是，QT虽然在PC端获得了巨大成功，备受社区追捧，然而其在移动端却表现不佳，在近几年，虽然偶尔能听到QT的声音，但一直很弱，无论QT本身技术如何、设计思想如何，但事实上终究是败了，究其原因，笔者认为主要有四：\n\n第一：QT移动开发社区太小，学习资料不足，生态不好。\n\n第二：官方推广不利，支持不够。\n\n第三：移动端发力较晚，市场已被其它动态化框架占领（Hybrid和RN)。\n\n第四：在移动开发中，C++开发和Web开发栈相比有着先天的劣势，直接结果就是QT开发效率太低。\n\n基于此四点，尽管QT是移动端开发跨平台自绘引擎的先驱，但却成为了烈士。\n\n## 1.1.5 Flutter出世\n\n“千呼万唤始出来”，铺垫这么久，现在终于等到本书的主角出场了！\n\nFlutter是Google发布的一个用于创建跨平台、高性能移动应用的框架。Flutter和QT mobile一样，都没有使用原生控件，相反都实现了一个自绘引擎，使用自身的布局、绘制系统。那么，我们会担心，QT mobile面对的问题Flutter是否也一样，Flutter会不会步入QT mobile后尘，成为另一个烈士？要回到这个问题，我们先来看看Flutter诞生过程：\n\n- 2017 年 Google I/O 大会上，Google 首次推出了一款新的用于创建跨平台、高性能的移动应用框架——Flutter。\n- 2018年2月，Flutter发布了第一个Beta版本，同年五月， 在2018年Google I/O 大会上，Flutter 更新到了 beta 3 版本。\n- 2018年6月，Flutter发布了首个预览版本，这意味着 Flutter 进入了正式版（1.0）发布前的最后阶段。\n\n观其发展，在2018年5月份，Flutter 进入了 GitHub stars 排行榜前 100 名，已有 27k  star。而今天(2019年5月29日)，已经有65K的Star。经历了短短2年多的时间，Flutter 生态系统得以快速增长，由此可见，Flutter在开发者中受到了热烈的欢迎，其未来发展值得期待！\n\n现在，我们来和QT mobile做一个对比：\n\n1. 生态：从Github上来看，目前Flutter活跃用户正在高速增长。从Stackoverflow上提问来看，Flutter社区现在已经很庞大。Flutter的文档、资源也越来越丰富，开发过程中遇到的很多问题都可以在Stackoverflow或其github issue中找到答案。\n2. 技术支持：现在Google正在大力推广Flutter，Flutter的作者中很多人都是来自Chromium团队，并且github上活跃度很高。另一个角度，从今年上半年Flutter频繁的版本发布也可以看出Google对Flutter的投入的资源不小，所以在官方技术支持这方面，大可不必担心。\n3. 开发效率：Flutter的热重载可帮助开发者快速地进行测试、构建UI、添加功能并更快地修复错误。在iOS和Android模拟器或真机上可以实现毫秒级热重载，并且不会丢失状态。这真的很棒，相信我，如果你是一名原生开发者，体验了Flutter开发流后，很可能就不想重新回去做原生了，毕竟很少有人不吐槽原生开发的编译速度。\n\n基于以上三点，相信读者和笔者一样，Flutter未来如何，心中自有定论。到现在为止，我们已经对移动端开发技术有了一个全面的了解，接下来我们便要进入本书的主题，你准备好了吗！\n\n\n\n## 1.1.6 小结\n\n本章主要介绍了目前移动开发中三种跨平台技术，现在我们从框架角度对比一下它们，如表1-1所示：\n\n| 技术类型            | UI渲染方式      | 性能 | 开发效率 | 动态化     | 框架代表       |\n| ------------------- | --------------- | ---- | -------- | ---------- | -------------- |\n| H5+原生             | WebView渲染     | 一般 | 高       | 支持        | Cordova、Ionic |\n| JavaScript+原生渲染 | 原生控件渲染    | 好   | 中    | 支持        | RN、Weex       |\n| 自绘UI+原生         | 调用系统API渲染 | 好   | Flutter高, QT低       | 默认不支持 | QT、Flutter    |\n\n<center>表1-1: 跨平台技术对比</center>\n上表中开发语言主要指UI的开发语言。而开发效率，是指整个开发周期的效率，包括编码时间、调试时间、以及排错、兼容时间。动态化主要指是否支持动态下发代码和是否支持热更新。值得注意的是Flutter的Release包默认是使用Dart AOT模式编译的，所以不支持动态化，但Dart还有JIT或snapshot运行方式，这些模式都是支持动态化的。\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n  \n\n\n"
  },
  {
    "path": "src/v2/chapter10/combine.md",
    "content": "# 10.2 组合现有组件\n\n在Flutter中页面UI通常都是由一些低阶别的组件组合而成，当我们需要封装一些通用组件时，应该首先考虑是否可以通过组合其它组件来实现，如果可以，则应优先使用组合，因为直接通过现有组件拼装会非常简单、灵活、高效。\n\n### 示例：自定义渐变按钮\n\nFlutter Material组件库中的按钮默认不支持渐变背景，为了实现渐变背景按钮，我们自定义一个`GradientButton `组件，它需要支持一下功能：\n\n1. 背景支持渐变色\n2. 手指按下时有涟漪效果\n3. 可以支持圆角\n\n我们先来看看最终要实现的效果（图10-1）：\n\n![gradient-button](../imgs/10-1.png)\n\n我们`DecoratedBox`可以支持背景色渐变和圆角，`InkWell`在手指按下有涟漪效果，所以我们可以通过组合`DecoratedBox`和`InkWell`来实现`GradientButton`，代码如下：\n\n```dart\nimport 'package:flutter/material.dart';\n\nclass GradientButton extends StatelessWidget {\n  GradientButton({\n    this.colors,\n    this.width,\n    this.height,\n    this.onPressed,\n    this.borderRadius,\n    @required this.child,\n  });\n\n  // 渐变色数组\n  final List<Color> colors;\n\n  // 按钮宽高\n  final double width;\n  final double height;\n\n  final Widget child;\n  final BorderRadius borderRadius;\n\n  //点击回调\n  final GestureTapCallback onPressed;\n\n  @override\n  Widget build(BuildContext context) {\n    ThemeData theme = Theme.of(context);\n\n    //确保colors数组不空\n    List<Color> _colors = colors ??\n        [theme.primaryColor, theme.primaryColorDark ?? theme.primaryColor];\n\n    return DecoratedBox(\n      decoration: BoxDecoration(\n        gradient: LinearGradient(colors: _colors),\n        borderRadius: borderRadius,\n      ),\n      child: Material(\n        type: MaterialType.transparency,\n        child: InkWell(\n          splashColor: _colors.last,\n          highlightColor: Colors.transparent,\n          borderRadius: borderRadius,\n          onTap: onPressed,\n          child: ConstrainedBox(\n            constraints: BoxConstraints.tightFor(height: height, width: width),\n            child: Center(\n              child: Padding(\n                padding: const EdgeInsets.all(8.0),\n                child: DefaultTextStyle(\n                  style: TextStyle(fontWeight: FontWeight.bold),\n                  child: child,\n                ),\n              ),\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n```\n\n可以看到`GradientButton`是由`DecoratedBox`、`Padding`、`Center`、`InkWell`等组件组合而成。当然上面的代码只是一个示例，作为一个按钮它还并不完整，比如没有禁用状态，读者可以根据实际需要来完善。\n\n#### 使用GradientButton\n\n```dart\nimport 'package:flutter/material.dart';\nimport '../widgets/index.dart';\n\nclass GradientButtonRoute extends StatefulWidget {\n  @override\n  _GradientButtonRouteState createState() => _GradientButtonRouteState();\n}\n\nclass _GradientButtonRouteState extends State<GradientButtonRoute> {\n  @override\n  Widget build(BuildContext context) {\n    return Container(\n      child: Column(\n        children: <Widget>[\n          GradientButton(\n            colors: [Colors.orange, Colors.red],\n            height: 50.0,\n            child: Text(\"Submit\"),\n            onPressed: onTap,\n          ),\n          GradientButton(\n            height: 50.0,\n            colors: [Colors.lightGreen, Colors.green[700]],\n            child: Text(\"Submit\"),\n            onPressed: onTap,\n          ),\n          GradientButton(\n            height: 50.0,\n            colors: [Colors.lightBlue[300], Colors.blueAccent],\n            child: Text(\"Submit\"),\n            onPressed: onTap,\n          ),\n        ],\n      ),\n    );\n  }\n  onTap() {\n    print(\"button click\");\n  }\n}\n```\n\n### 总结\n\n通过组合的方式定义组件和我们之前写界面并无差异，不过在抽离出单独的组件时我们要考虑代码规范性，如必要参数要用`@required` 标注，对于可选参数在特定场景需要判空或设置默认值等。这是由于使用者大多时候可能不了解组件的内部细节，所以为了保证代码健壮性，我们需要在用户错误地使用组件时能够兼容或报错提示（使用`assert`断言函数）。\n"
  },
  {
    "path": "src/v2/chapter10/custom_paint.md",
    "content": "# 10.4 自绘组件 （CustomPaint与Canvas）\n\n对于一些复杂或不规则的UI，我们可能无法通过组合其它组件的方式来实现，比如我们需要一个正六边形、一个渐变的圆形进度条、一个棋盘等。当然，有时候我们可以使用图片来实现，但在一些需要动态交互的场景静态图片也是实现不了的，比如要实现一个手写输入面板，这时，我们就需要来自己绘制UI外观。\n\n几乎所有的UI系统都会提供一个自绘UI的接口，这个接口通常会提供一块2D画布`Canvas`，`Canvas`内部封装了一些基本绘制的API，开发者可以通过`Canvas`绘制各种自定义图形。在Flutter中，提供了一个`CustomPaint` 组件，它可以结合画笔`CustomPainter`来实现自定义图形绘制。\n\n### CustomPaint\n\n我们看看`CustomPaint`构造函数：\n\n```dart\nCustomPaint({\n  Key key,\n  this.painter, \n  this.foregroundPainter,\n  this.size = Size.zero, \n  this.isComplex = false, \n  this.willChange = false, \n  Widget child, //子节点，可以为空\n})\n```\n\n- `painter`: 背景画笔，会显示在子节点后面;\n- `foregroundPainter`: 前景画笔，会显示在子节点前面\n- `size`：当child为null时，代表默认绘制区域大小，如果有child则忽略此参数，画布尺寸则为child尺寸。如果有child但是想指定画布为特定大小，可以使用SizeBox包裹CustomPaint实现。\n- `isComplex`：是否复杂的绘制，如果是，Flutter会应用一些缓存策略来减少重复渲染的开销。\n- `willChange`：和`isComplex`配合使用，当启用缓存时，该属性代表在下一帧中绘制是否会改变。\n\n可以看到，绘制时我们需要提供前景或背景画笔，两者也可以同时提供。我们的画笔需要继承`CustomPainter`类，我们在画笔类中实现真正的绘制逻辑。\n\n#### 注意\n\n如果`CustomPaint`有子节点，为了避免子节点不必要的重绘并提高性能，通常情况下都会将子节点包裹在`RepaintBoundary `组件中，这样会在绘制时就会创建一个新的绘制层（Layer），其子组件将在新的Layer上绘制，而父组件将在原来Layer上绘制，也就是说`RepaintBoundary` 子组件的绘制将独立于父组件的绘制，`RepaintBoundary`会隔离其子节点和`CustomPaint`本身的绘制边界。示例如下：\n\n```dart\nCustomPaint(\n  size: Size(300, 300), //指定画布大小\n  painter: MyPainter(),\n  child: RepaintBoundary(child:...)), \n)\n```\n\n### CustomPainter\n\n`CustomPainter`中提定义了一个虚函数`paint`：\n\n```\nvoid paint(Canvas canvas, Size size);\n```\n\n`paint`有两个参数:\n\n- `Canvas`：一个画布，包括各种绘制方法，我们列出一下常用的方法：\n\n  |API名称     | 功能   |\n  | ---------- | ------ |\n  | drawLine   | 画线   |\n  | drawPoint  | 画点   |\n  | drawPath   | 画路径 |\n  | drawImage  | 画图像 |\n  | drawRect   | 画矩形 |\n  | drawCircle | 画圆   |\n  | drawOval   | 画椭圆 |\n  | drawArc    | 画圆弧 |\n\n- `Size`：当前绘制区域大小。\n\n### 画笔Paint\n\n现在画布有了，我们最后还缺一个画笔，Flutter提供了`Paint`类来实现画笔。在`Paint`中，我们可以配置画笔的各种属性如粗细、颜色、样式等。如：\n\n```dart\nvar paint = Paint() //创建一个画笔并配置其属性\n  ..isAntiAlias = true //是否抗锯齿\n  ..style = PaintingStyle.fill //画笔样式：填充\n  ..color=Color(0x77cdb175);//画笔颜色\n```\n\n更多的配置属性读者可以参考Paint类定义。\n\n### 示例：五子棋/盘\n\n下面我们通过一个五子棋游戏中棋盘和棋子的绘制来演示自绘UI的过程，首先我们看一下我们的目标效果，如图10-3所示：\n\n![图10-3](../imgs/10-3.png)\n\n代码：\n\n```dart\nimport 'package:flutter/material.dart';\nimport 'dart:math';\n\nclass CustomPaintRoute extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return Center(\n      child: CustomPaint(\n        size: Size(300, 300), //指定画布大小\n        painter: MyPainter(),\n      ),\n    );\n  }\n}\n\nclass MyPainter extends CustomPainter {\n  @override\n  void paint(Canvas canvas, Size size) {\n    double eWidth = size.width / 15;\n    double eHeight = size.height / 15;\n      \n    //画棋盘背景\n    var paint = Paint()\n      ..isAntiAlias = true\n      ..style = PaintingStyle.fill //填充\n      ..color = Color(0x77cdb175); //背景为纸黄色\n    canvas.drawRect(Offset.zero & size, paint);\n\n    //画棋盘网格\n    paint\n      ..style = PaintingStyle.stroke //线\n      ..color = Colors.black87\n      ..strokeWidth = 1.0;\n\n    for (int i = 0; i <= 15; ++i) {\n      double dy = eHeight * i;\n      canvas.drawLine(Offset(0, dy), Offset(size.width, dy), paint);\n    }\n\n    for (int i = 0; i <= 15; ++i) {\n      double dx = eWidth * i;\n      canvas.drawLine(Offset(dx, 0), Offset(dx, size.height), paint);\n    }\n\n    //画一个黑子\n    paint\n      ..style = PaintingStyle.fill\n      ..color = Colors.black;\n    canvas.drawCircle(\n      Offset(size.width / 2 - eWidth / 2, size.height / 2 - eHeight / 2),\n      min(eWidth / 2, eHeight / 2) - 2,\n      paint,\n    );\n      \n    //画一个白子\n    paint.color = Colors.white;\n    canvas.drawCircle(\n      Offset(size.width / 2 + eWidth / 2, size.height / 2 - eHeight / 2),\n      min(eWidth / 2, eHeight / 2) - 2,\n      paint,\n    );\n  }\n\n  //在实际场景中正确利用此回调可以避免重绘开销，本示例我们简单的返回true\n  @override\n  bool shouldRepaint(CustomPainter oldDelegate) => true;\n}\n```\n\n### 性能\n\n绘制是比较昂贵的操作，所以我们在实现自绘控件时应该考虑到性能开销，下面是两条关于性能优化的建议：\n\n- 尽可能的利用好`shouldRepaint`返回值；在UI树重新build时，控件在绘制前都会先调用该方法以确定是否有必要重绘；假如我们绘制的UI不依赖外部状态，那么就应该始终返回`false`，因为外部状态改变导致重新build时不会影响我们的UI外观；如果绘制依赖外部状态，那么我们就应该在`shouldRepaint`中判断依赖的状态是否改变，如果已改变则应返回`true`来重绘，反之则应返回`false`不需要重绘。\n\n- 绘制尽可能多的分层；在上面五子棋的示例中，我们将棋盘和棋子的绘制放在了一起，这样会有一个问题：由于棋盘始终是不变的，用户每次落子时变的只是棋子，但是如果按照上面的代码来实现，每次绘制棋子时都要重新绘制一次棋盘，这是没必要的。优化的方法就是将棋盘单独抽为一个组件，并设置其`shouldRepaint`回调值为`false`，然后将棋盘组件作为背景。然后将棋子的绘制放到另一个组件中，这样每次落子时只需要绘制棋子。\n\n### 总结\n\n自绘控件非常强大，理论上可以实现任何2D图形外观，实际上Flutter提供的所有组件最终都是通过调用Canvas绘制出来的，只不过绘制的逻辑被封装起来了，读者有兴趣可以查看具有外观样式的组件源码，找到其对应的`RenderObject`对象，如`Text`对应的`RenderParagraph`对象最终会通过`Canvas`实现文本绘制逻辑。下一节我们会再通过一个自绘的圆形背景渐变进度条的实例来帮助读者加深印象。\n"
  },
  {
    "path": "src/v2/chapter10/gradient_circular_progress_demo.md",
    "content": "# 10.5 自绘实例：圆形背景渐变进度条\n\n本节我们实现一个圆形背景渐变进度条，它支持：\n\n1. 支持多种背景渐变色。\n2. 任意弧度；进度条可以不是整圆。\n3. 可以自定义粗细、两端是否圆角等样式。\n\n可以发现要实现这样的一个进度条是无法通过现有组件组合而成的，所以我们通过自绘方式实现，代码如下：\n\n```dart\nimport 'dart:math';\nimport 'package:flutter/material.dart';\n\nclass GradientCircularProgressIndicator extends StatelessWidget {\n  GradientCircularProgressIndicator({\n    this.strokeWidth = 2.0,\n    @required this.radius,\n    @required this.colors,\n    this.stops,\n    this.strokeCapRound = false,\n    this.backgroundColor = const Color(0xFFEEEEEE),\n    this.totalAngle = 2 * pi,\n    this.value\n  });\n\n  ///粗细\n  final double strokeWidth;\n\n  /// 圆的半径\n  final double radius;\n\n  ///两端是否为圆角\n  final bool strokeCapRound;\n\n  /// 当前进度，取值范围 [0.0-1.0]\n  final double value;\n\n  /// 进度条背景色\n  final Color backgroundColor;\n\n  /// 进度条的总弧度，2*PI为整圆，小于2*PI则不是整圆\n  final double totalAngle;\n\n  /// 渐变色数组\n  final List<Color> colors;\n\n  /// 渐变色的终止点，对应colors属性\n  final List<double> stops;\n\n  @override\n  Widget build(BuildContext context) {\n    double _offset = .0;\n    // 如果两端为圆角，则需要对起始位置进行调整，否则圆角部分会偏离起始位置\n    // 下面调整的角度的计算公式是通过数学几何知识得出，读者有兴趣可以研究一下为什么是这样  \n    if (strokeCapRound) {\n      _offset = asin(strokeWidth / (radius * 2 - strokeWidth));\n    }\n    var _colors = colors;\n    if (_colors == null) {\n      Color color = Theme\n          .of(context)\n          .accentColor;\n      _colors = [color, color];\n    }\n    return Transform.rotate(\n      angle: -pi / 2.0 - _offset, \n      child: CustomPaint(\n          size: Size.fromRadius(radius),\n          painter: _GradientCircularProgressPainter(\n            strokeWidth: strokeWidth,\n            strokeCapRound: strokeCapRound,\n            backgroundColor: backgroundColor,\n            value: value,\n            total: totalAngle,\n            radius: radius,\n            colors: _colors,\n          )\n      ),\n    );\n  }\n}\n\n//实现画笔\nclass _GradientCircularProgressPainter extends CustomPainter {\n  _GradientCircularProgressPainter({\n    this.strokeWidth: 10.0,\n    this.strokeCapRound: false,\n    this.backgroundColor = const Color(0xFFEEEEEE),\n    this.radius,\n    this.total = 2 * pi,\n    @required this.colors,\n    this.stops,\n    this.value\n  });\n\n  final double strokeWidth;\n  final bool strokeCapRound;\n  final double value;\n  final Color backgroundColor;\n  final List<Color> colors;\n  final double total;\n  final double radius;\n  final List<double> stops;\n\n  @override\n  void paint(Canvas canvas, Size size) {\n    if (radius != null) {\n      size = Size.fromRadius(radius);\n    }\n    double _offset = strokeWidth / 2.0;\n    double _value = (value ?? .0);\n    _value = _value.clamp(.0, 1.0) * total;\n    double _start = .0;\n\n    if (strokeCapRound) {\n      _start = asin(strokeWidth/ (size.width - strokeWidth));\n    }\n\n    Rect rect = Offset(_offset, _offset) & Size(\n        size.width - strokeWidth,\n        size.height - strokeWidth\n    );\n\n    var paint = Paint()\n      ..strokeCap = strokeCapRound ? StrokeCap.round : StrokeCap.butt\n      ..style = PaintingStyle.stroke\n      ..isAntiAlias = true\n      ..strokeWidth = strokeWidth;\n\n    // 先画背景\n    if (backgroundColor != Colors.transparent) {\n      paint.color = backgroundColor;\n      canvas.drawArc(\n          rect,\n          _start,\n          total,\n          false,\n          paint\n      );\n    }\n\n    // 再画前景，应用渐变\n    if (_value > 0) {\n      paint.shader = SweepGradient(\n        startAngle: 0.0,\n        endAngle: _value,\n        colors: colors,\n        stops: stops,\n      ).createShader(rect);\n\n      canvas.drawArc(\n          rect,\n          _start,\n          _value,\n          false,\n          paint\n      );\n    }\n  }\n\n  @override\n  bool shouldRepaint(CustomPainter oldDelegate) => true;\n\n}\n```\n\n下面我们来测试一下，为了尽可能多的展示`GradientCircularProgressIndicator`的不同外观和用途，这个示例代码会比较长，并且添加了动画，建议读者将此示例运行起来观看实际效果，我们先看看其中的一帧动画的截图：\n\n![gradient_circular_progress](../imgs/gradient_circular_progress.png)\n\n示例代码：\n\n```dart\nimport 'dart:math';\nimport 'package:flutter/material.dart';\nimport '../widgets/index.dart';\n\nclass GradientCircularProgressRoute extends StatefulWidget {\n  @override\n  GradientCircularProgressRouteState createState() {\n    return new GradientCircularProgressRouteState();\n  }\n}\n\nclass GradientCircularProgressRouteState\n    extends State<GradientCircularProgressRoute> with TickerProviderStateMixin {\n  AnimationController _animationController;\n\n  @override\n  void initState() {\n    super.initState();\n    _animationController =\n        new AnimationController(vsync: this, duration: Duration(seconds: 3));\n    bool isForward = true;\n    _animationController.addStatusListener((status) {\n      if (status == AnimationStatus.forward) {\n        isForward = true;\n      } else if (status == AnimationStatus.completed ||\n          status == AnimationStatus.dismissed) {\n        if (isForward) {\n          _animationController.reverse();\n        } else {\n          _animationController.forward();\n        }\n      } else if (status == AnimationStatus.reverse) {\n        isForward = false;\n      }\n    });\n    _animationController.forward();\n  }\n\n  @override\n  void dispose() {\n    _animationController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return SingleChildScrollView(\n      child: Center(\n        child: Column(\n          crossAxisAlignment: CrossAxisAlignment.center,\n          children: <Widget>[\n            AnimatedBuilder(\n              animation: _animationController,\n              builder: (BuildContext context, Widget child) {\n                return Padding(\n                  padding: const EdgeInsets.symmetric(vertical: 16.0),\n                  child: Column(\n                    children: <Widget>[\n                      Wrap(\n                        spacing: 10.0,\n                        runSpacing: 16.0,\n                        children: <Widget>[\n                          GradientCircularProgressIndicator(\n                            // No gradient\n                            colors: [Colors.blue, Colors.blue],\n                            radius: 50.0,\n                            strokeWidth: 3.0,\n                            value: _animationController.value,\n                          ),\n                          GradientCircularProgressIndicator(\n                            colors: [Colors.red, Colors.orange],\n                            radius: 50.0,\n                            strokeWidth: 3.0,\n                            value: _animationController.value,\n                          ),\n                          GradientCircularProgressIndicator(\n                            colors: [Colors.red, Colors.orange, Colors.red],\n                            radius: 50.0,\n                            strokeWidth: 5.0,\n                            value: _animationController.value,\n                          ),\n                          GradientCircularProgressIndicator(\n                            colors: [Colors.teal, Colors.cyan],\n                            radius: 50.0,\n                            strokeWidth: 5.0,\n                            strokeCapRound: true,\n                            value: CurvedAnimation(\n                                    parent: _animationController,\n                                    curve: Curves.decelerate)\n                                .value,\n                          ),\n                          TurnBox(\n                            turns: 1 / 8,\n                            child: GradientCircularProgressIndicator(\n                                colors: [Colors.red, Colors.orange, Colors.red],\n                                radius: 50.0,\n                                strokeWidth: 5.0,\n                                strokeCapRound: true,\n                                backgroundColor: Colors.red[50],\n                                totalAngle: 1.5 * pi,\n                                value: CurvedAnimation(\n                                        parent: _animationController,\n                                        curve: Curves.ease)\n                                    .value),\n                          ),\n                          RotatedBox(\n                            quarterTurns: 1,\n                            child: GradientCircularProgressIndicator(\n                                colors: [Colors.blue[700], Colors.blue[200]],\n                                radius: 50.0,\n                                strokeWidth: 3.0,\n                                strokeCapRound: true,\n                                backgroundColor: Colors.transparent,\n                                value: _animationController.value),\n                          ),\n                          GradientCircularProgressIndicator(\n                            colors: [\n                              Colors.red,\n                              Colors.amber,\n                              Colors.cyan,\n                              Colors.green[200],\n                              Colors.blue,\n                              Colors.red\n                            ],\n                            radius: 50.0,\n                            strokeWidth: 5.0,\n                            strokeCapRound: true,\n                            value: _animationController.value,\n                          ),\n                        ],\n                      ),\n                      GradientCircularProgressIndicator(\n                        colors: [Colors.blue[700], Colors.blue[200]],\n                        radius: 100.0,\n                        strokeWidth: 20.0,\n                        value: _animationController.value,\n                      ),\n\n                      Padding(\n                        padding: const EdgeInsets.symmetric(vertical: 16.0),\n                        child: GradientCircularProgressIndicator(\n                          colors: [Colors.blue[700], Colors.blue[300]],\n                          radius: 100.0,\n                          strokeWidth: 20.0,\n                          value: _animationController.value,\n                          strokeCapRound: true,\n                        ),\n                      ),\n                      //剪裁半圆\n                      ClipRect(\n                        child: Align(\n                          alignment: Alignment.topCenter,\n                          heightFactor: .5,\n                          child: Padding(\n                            padding: const EdgeInsets.only(bottom: 8.0),\n                            child: SizedBox(\n                              //width: 100.0,\n                              child: TurnBox(\n                                turns: .75,\n                                child: GradientCircularProgressIndicator(\n                                  colors: [Colors.teal, Colors.cyan[500]],\n                                  radius: 100.0,\n                                  strokeWidth: 8.0,\n                                  value: _animationController.value,\n                                  totalAngle: pi,\n                                  strokeCapRound: true,\n                                ),\n                              ),\n                            ),\n                          ),\n                        ),\n                      ),\n                      SizedBox(\n                        height: 104.0,\n                        width: 200.0,\n                        child: Stack(\n                          alignment: Alignment.center,\n                          children: <Widget>[\n                            Positioned(\n                              height: 200.0,\n                              top: .0,\n                              child: TurnBox(\n                                turns: .75,\n                                child: GradientCircularProgressIndicator(\n                                  colors: [Colors.teal, Colors.cyan[500]],\n                                  radius: 100.0,\n                                  strokeWidth: 8.0,\n                                  value: _animationController.value,\n                                  totalAngle: pi,\n                                  strokeCapRound: true,\n                                ),\n                              ),\n                            ),\n                            Padding(\n                              padding: const EdgeInsets.only(top: 10.0),\n                              child: Text(\n                                \"${(_animationController.value * 100).toInt()}%\",\n                                style: TextStyle(\n                                  fontSize: 25.0,\n                                  color: Colors.blueGrey,\n                                ),\n                              ),\n                            )\n                          ],\n                        ),\n                      ),\n                    ],\n                  ),\n                );\n              },\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}\n```\n\n怎么样，很炫酷吧！`GradientCircularProgressIndicator`已经被添加进了笔者维护的flukit组件库中了，读者如果有需要，可以直接依赖flukit包。\n\n"
  },
  {
    "path": "src/v2/chapter10/index.md",
    "content": "# 本章目录\n\n* [10.1：自定义组件方法简介](intro.md)\n* [10.2：组合现有组件](combine.md)\n* [10.3：组合实例：TurnBox](turn_box.md)\n* [10.4：自绘组件（CustomPaint与Canvas）](custom_paint.md) \n* [10.5：自绘实例：圆形渐变进度条(自绘)](gradient_circular_progress_demo.md) \n"
  },
  {
    "path": "src/v2/chapter10/intro.md",
    "content": "# 10.1 自定义组件方法简介\n\n当Flutter提供的现有组件无法满足我们的需求，或者我们为了共享代码需要封装一些通用组件，这时我们就需要自定义组件。在Flutter中自定义组件有三种方式：通过组合其它组件、自绘和实现RenderObject。本节我们先分别介绍一下这三种方式的特点，后面章节中则详细介绍它们的细节。\n\n### 组合其它Widget\n\n这种方式是通过拼装其它组件来组合成一个新的组件。例如我们之前介绍的`Container`就是一个组合组件，它是由`DecoratedBox`、`ConstrainedBox`、`Transform`、`Padding`、`Align`等组件组成。\n\n在Flutter中，组合的思想非常重要，Flutter提供了非常多的基础组件，而我们的界面开发其实就是按照需要组合这些组件来实现各种不同的布局而已。 \n\n### 自绘\n\n如果遇到无法通过现有的组件来实现需要的UI时，我们可以通过自绘组件的方式来实现，例如我们需要一个颜色渐变的圆形进度条，而Flutter提供的`CircularProgressIndicator`并不支持在显示精确进度时对进度条应用渐变色（其`valueColor` 属性只支持执行旋转动画时变化Indicator的颜色），这时最好的方法就是通过自定义组件来绘制出我们期望的外观。我们可以通过Flutter中提供的`CustomPaint`和`Canvas`来实现UI自绘。\n\n\n\n### 实现RenderObject\n\nFlutter提供的自身具有UI外观的组件，如文本`Text`、`Image`都是通过相应的`RenderObject`（我们将在“Flutter核心原理”一章中详细介绍`RenderObject`）渲染出来的，如Text是由`RenderParagraph`渲染；而`Image`是由`RenderImage`渲染。`RenderObject`是一个抽象类，它定义了一个抽象方法`paint(...)`：\n\n```dart\nvoid paint(PaintingContext context, Offset offset)\n```\n\n`PaintingContext`代表组件的绘制上下文，通过`PaintingContext.canvas`可以获得`Canvas`，而绘制逻辑主要是通过`Canvas` API来实现。子类需要重写此方法以实现自身的绘制逻辑，如`RenderParagraph`需要实现文本绘制逻辑，而`RenderImage`需要实现图片绘制逻辑。\n\n可以发现，`RenderObject`中最终也是通过`Canvas` API来绘制的，那么通过实现`RenderObject`的方式和上面介绍的通过`CustomPaint`和`Canvas`自绘的方式有什么区别？其实答案很简单，`CustomPaint`只是为了方便开发者封装的一个代理类，它直接继承自`SingleChildRenderObjectWidget`，通过`RenderCustomPaint`的`paint`方法将`Canvas`和画笔`Painter`(需要开发者实现，后面章节介绍)连接起来实现了最终的绘制（绘制逻辑在`Painter`中）。\n\n### 总结\n\n“组合”是自定义组件最简单的方法，在任何需要自定义组件的场景下，我们都应该优先考虑是否能够通过组合来实现。而自绘和通过实现`RenderObject`的方法本质上是一样的，都需要开发者调用`Canvas` API手动去绘制UI，优点是强大灵活，理论上可以实现任何外观的UI，而缺点是必须了解`Canvas` API细节，并且得自己去实现绘制逻辑。\n\n在本章接下来的小节中，我们将通过一些实例来详细介绍自定义UI的过程，由于后两种方法本质是相同的，并且Flutter中很多基础组件都是通过`RenderObject`的形式来实现的，所以后续我们只介绍`CustomPaint`和`Canvas`的方式，读者如果对自定义`RenderObject`的方法好奇，可以查看Flutter中相关基础组件对应的`RenderObject`的实现源码，如`RenderParagraph`或`RenderImage`。\n"
  },
  {
    "path": "src/v2/chapter10/turn_box.md",
    "content": "# 10.3 组合实例：TurnBox\n\n我们之前已经介绍过`RotatedBox`，它可以旋转子组件，但是它有两个缺点：一是只能将其子节点以90度的倍数旋转；二是当旋转的角度发生变化时，旋转角度更新过程没有动画。\n\n本节我们将实现一个`TurnBox`组件，它不仅可以以任意角度来旋转其子节点，而且可以在角度发生变化时执行一个动画以过渡到新状态，同时，我们可以手动指定动画速度。\n\n`TurnBox`的完整代码如下：\n\n```dart\nimport 'package:flutter/widgets.dart';\n\nclass TurnBox extends StatefulWidget {\n  const TurnBox({\n    Key key,\n    this.turns = .0, //旋转的“圈”数,一圈为360度，如0.25圈即90度\n    this.speed = 200, //过渡动画执行的总时长\n    this.child\n  }) :super(key: key);\n\n  final double turns;\n  final int speed;\n  final Widget child;\n\n  @override\n  _TurnBoxState createState() => new _TurnBoxState();\n}\n\nclass _TurnBoxState extends State<TurnBox>\n    with SingleTickerProviderStateMixin {\n  AnimationController _controller;\n\n  @override\n  void initState() {\n    super.initState();\n    _controller = new AnimationController(\n        vsync: this,\n        lowerBound: -double.infinity,\n        upperBound: double.infinity\n    );\n    _controller.value = widget.turns;\n  }\n\n  @override\n  void dispose() {\n    _controller.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return RotationTransition(\n      turns: _controller,\n      child: widget.child,\n    );\n  }\n\n  @override\n  void didUpdateWidget(TurnBox oldWidget) {\n    super.didUpdateWidget(oldWidget);\n    //旋转角度发生变化时执行过渡动画  \n    if (oldWidget.turns != widget.turns) {\n      _controller.animateTo(\n        widget.turns,\n        duration: Duration(milliseconds: widget.speed??200),\n        curve: Curves.easeOut,\n      );\n    }\n  }\n}\n```\n\n上面代码中：\n\n1. 我们是通过组合`RotationTransition`和child来实现的旋转效果。\n2. 在`didUpdateWidget`中，我们判断要旋转的角度是否发生了变化，如果变了，则执行一个过渡动画。\n\n下面我们测试一下`TurnBox`的功能，测试代码如下：\n\n```dart\nimport 'package:flutter/material.dart';\nimport '../widgets/index.dart';\n\nclass TurnBoxRoute extends StatefulWidget {\n  @override\n  _TurnBoxRouteState createState() => new _TurnBoxRouteState();\n}\n\nclass _TurnBoxRouteState extends State<TurnBoxRoute> {\n  double _turns = .0;\n\n  @override\n  Widget build(BuildContext context) {\n\n    return Center(\n      child: Column(\n        children: <Widget>[\n          TurnBox(\n            turns: _turns,\n            speed: 500,\n            child: Icon(Icons.refresh, size: 50,),\n          ),\n          TurnBox(\n            turns: _turns,\n            speed: 1000,\n            child: Icon(Icons.refresh, size: 150.0,),\n          ),\n          RaisedButton(\n            child: Text(\"顺时针旋转1/5圈\"),\n            onPressed: () {\n              setState(() {\n                _turns += .2;\n              });\n            },\n          ),\n          RaisedButton(\n            child: Text(\"逆时针旋转1/5圈\"),\n            onPressed: () {\n              setState(() {\n                _turns -= .2;\n              });\n            },\n          )\n        ],\n      ),\n    );\n  }\n}\n```\n\n测试代码运行后效果如图10-2所示：\n\n![图10-2](../imgs/10-2.png)\n\n\n\n当我们点击旋转按钮时，两个图标的旋转都会旋转1/5圈，但旋转的速度是不同的，读者可以自己运行一下示例看看效果。\n\n实际上本示例只组合了`RotationTransition`一个组件，它是一个最简的组合类组件示例。另外，如果我们封装的是`StatefulWidget`，那么一定要注意在组件更新时是否需要同步状态。比如我们要封装一个富文本展示组件`MyRichText` ，它可以自动处理url链接，定义如下：\n\n```dart\nclass MyRichText extends StatefulWidget {\n  MyRichText({\n    Key key,\n    this.text, // 文本字符串\n    this.linkStyle, // url链接样式\n  }) : super(key: key);\n\n  final String text;\n  final TextStyle linkStyle;\n\n  @override\n  _MyRichTextState createState() => _MyRichTextState();\n}\n```\n\n接下来我们在`_MyRichTextState`中要实现的功能有两个：\n\n1. 解析文本字符串“text”，生成`TextSpan`缓存起来；\n2. 在`build`中返回最终的富文本样式；\n\n`_MyRichTextState` 实现的代码大致如下：\n\n```dart\nclass _MyRichTextState extends State<MyRichText> {\n\n  TextSpan _textSpan;\n\n  @override\n  Widget build(BuildContext context) {\n    return RichText(\n      text: _textSpan,\n    );\n  }\n\n  TextSpan parseText(String text) {\n    // 耗时操作：解析文本字符串，构建出TextSpan。\n    // 省略具体实现。\n  }\n\n  @override\n  void initState() {\n    _textSpan = parseText(widget.text)\n    super.initState();\n  }\n}\n```\n\n由于解析文本字符串，构建出`TextSpan`是一个耗时操作，为了不在每次build的时候都解析一次，所以我们在`initState`中对解析的结果进行了缓存，然后再`build`中直接使用解析的结果`_textSpan`。这看起来很不错，但是上面的代码有一个严重的问题，就是父组件传入的`text`发生变化时（组件树结构不变），那么`MyRichText`显示的内容不会更新，原因就是`initState`只会在State创建时被调用，所以在`text`发生变化时，`parseText`没有重新执行，导致`_textSpan`任然是旧的解析值。要解决这个问题也很简单，我们只需添加一个`didUpdateWidget`回调，然后再里面重新调用`parseText`即可：\n\n```dart\n@override\nvoid didUpdateWidget(MyRichText oldWidget) {\n  if (widget.text != oldWidget.text) {\n    _textSpan = parseText(widget.text);\n  }\n  super.didUpdateWidget(oldWidget);\n}\n```\n\n有些读者可能会觉得这个点也很简单，是的，的确很简单，之所以要在这里反复强调是因为这个点在实际开发中很容易被忽略，它虽然简单，但却很重要。总之，当我们在State中会缓存某些依赖Widget参数的数据时，一定要注意在组件更新时是否需要同步状态。"
  },
  {
    "path": "src/v2/chapter11/dio.md",
    "content": "\n\n# 11.3 Http请求-Dio http库\n\n通过上一节介绍，我们可以发现直接使用HttpClient发起网络请求是比较麻烦的，很多事情得我们手动处理，如果再涉及到文件上传/下载、Cookie管理等就会非常繁琐。幸运的是，Dart社区有一些第三方http请求库，用它们来发起http请求将会简单的多，本节我们介绍一下目前人气较高的[dio](https://github.com/flutterchina/dio)库。\n\n>  dio是一个强大的Dart Http请求库，支持Restful API、FormData、拦截器、请求取消、Cookie管理、文件上传/下载、超时等。dio的使用方式随着其版本升级可能会发生变化，如果本节所述内容和dio官方有差异，请以dio官方文档为准。\n\n### 引入\n\n引入dio:\n\n```yaml\ndependencies:\n  dio: ^x.x.x #请使用pub上的最新版本\n```\n\n导入并创建dio实例：\n\n```dart\nimport 'package:dio/dio.dart';\nDio dio =  Dio();\n```\n\n接下来就可以通过 dio实例来发起网络请求了，注意，一个dio实例可以发起多个http请求，一般来说，APP只有一个http数据源时，dio应该使用单例模式。\n\n### 示例\n\n发起 `GET` 请求 :\n\n```dart\nResponse response;\nresponse=await dio.get(\"/test?id=12&name=wendu\")\nprint(response.data.toString());\n```\n\n对于`GET`请求我们可以将query参数通过对象来传递，上面的代码等同于：\n\n```dart\nresponse=await dio.get(\"/test\",queryParameters:{\"id\":12,\"name\":\"wendu\"})\nprint(response);\n```\n\n发起一个 `POST` 请求:\n\n```dart\nresponse=await dio.post(\"/test\",data:{\"id\":12,\"name\":\"wendu\"})\n```\n\n发起多个并发请求:\n\n```dart\nresponse= await Future.wait([dio.post(\"/info\"),dio.get(\"/token\")]);\n```\n\n下载文件:\n\n```dart\nresponse=await dio.download(\"https://www.google.com/\",_savePath);\n```\n\n发送 FormData:\n\n```dart\nFormData formData = new FormData.from({\n   \"name\": \"wendux\",\n   \"age\": 25,\n});\nresponse = await dio.post(\"/info\", data: formData)\n```\n\n如果发送的数据是FormData，则dio会将请求header的`contentType`设为“multipart/form-data”。\n\n通过FormData上传多个文件:\n\n```dart\nFormData formData = new FormData.from({\n   \"name\": \"wendux\",\n   \"age\": 25,\n   \"file1\": new UploadFileInfo(new File(\"./upload.txt\"), \"upload1.txt\"),\n   \"file2\": new UploadFileInfo(new File(\"./upload.txt\"), \"upload2.txt\"),\n     // 支持文件数组上传\n   \"files\": [\n      new UploadFileInfo(new File(\"./example/upload.txt\"), \"upload.txt\"),\n      new UploadFileInfo(new File(\"./example/upload.txt\"), \"upload.txt\")\n    ]\n});\nresponse = await dio.post(\"/info\", data: formData)\n```\n\n值得一提的是，dio内部仍然使用HttpClient发起的请求，所以代理、请求认证、证书校验等和HttpClient是相同的，我们可以在`onHttpClientCreate `回调中设置，例如：\n\n```dart\n(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) {\n    //设置代理 \n    client.findProxy = (uri) {\n      return \"PROXY 192.168.1.2:8888\";\n    };\n    //校验证书\n    httpClient.badCertificateCallback=(X509Certificate cert, String host, int port){\n      if(cert.pem==PEM){\n      return true; //证书一致，则允许发送数据\n     }\n     return false;\n    };   \n  };\n```\n\n注意，`onHttpClientCreate `会在当前dio实例内部需要创建HttpClient时调用，所以通过此回调配置HttpClient会对整个dio实例生效，如果你想针对某个应用请求单独的代理或证书校验策略，可以创建一个新的dio实例即可。\n\n怎么样，是不是很简单，除了这些基本的用法，dio还支持请求配置、拦截器等，官方资料比较详细，故本书不再赘述，详情可以参考dio主页：https://github.com/flutterchina/dio 。 下一节我们将使用dio实现一个分块下载器。\n\n### 实例\n\n我们通过Github开放的API来请求flutterchina组织下的所有公开的开源项目，实现：\n\n1. 在请求阶段弹出loading\n2. 请求结束后，如果请求失败，则展示错误信息；如果成功，则将项目名称列表展示出来。\n\n代码如下：\n\n```dart\nclass _FutureBuilderRouteState extends State<FutureBuilderRoute> {\n  Dio _dio = new Dio();\n\n  @override\n  Widget build(BuildContext context) {\n\n    return new Container(\n      alignment: Alignment.center,\n      child: FutureBuilder(\n          future: _dio.get(\"https://api.github.com/orgs/flutterchina/repos\"),\n          builder: (BuildContext context, AsyncSnapshot snapshot) {\n            //请求完成\n            if (snapshot.connectionState == ConnectionState.done) {\n              Response response = snapshot.data;\n              //发生错误\n              if (snapshot.hasError) {\n                return Text(snapshot.error.toString());\n              }\n              //请求成功，通过项目信息构建用于显示项目名称的ListView\n              return ListView(\n                children: response.data.map<Widget>((e) =>\n                    ListTile(title: Text(e[\"full_name\"]))\n                ).toList(),\n              );\n            }\n            //请求未完成时弹出loading\n            return CircularProgressIndicator();\n          }\n      ),\n    );\n  }\n}\n```"
  },
  {
    "path": "src/v2/chapter11/download_with_chunks.md",
    "content": "\n# 11.4 实例：Http分块下载\n\n本节将通过一个“Http分块下载”的示例演示一下dio的具体用法。\n\n### 原理\n\nHttp协议定义了分块传输的响应header字段，但具体是否支持取决于Server的实现，我们可以指定请求头的\"range\"字段来验证服务器是否支持分块传输。例如，我们可以利用curl命令来验证：\n\n```shell\nbogon:~ duwen$ curl -H \"Range: bytes=0-10\" http://download.dcloud.net.cn/HBuilder.9.0.2.macosx_64.dmg -v\n# 请求头\n> GET /HBuilder.9.0.2.macosx_64.dmg HTTP/1.1\n> Host: download.dcloud.net.cn\n> User-Agent: curl/7.54.0\n> Accept: */*\n> Range: bytes=0-10\n# 响应头\n< HTTP/1.1 206 Partial Content\n< Content-Type: application/octet-stream\n< Content-Length: 11\n< Connection: keep-alive\n< Date: Thu, 21 Feb 2019 06:25:15 GMT\n< Content-Range: bytes 0-10/233295878\n\n```\n\n我们在请求头中添加\"Range: bytes=0-10\"的作用是，告诉服务器本次请求我们只想获取文件0-10(包括10，共11字节)这块内容。如果服务器支持分块传输，则响应状态码为206，表示“部分内容”，并且同时响应头中包含“Content-Range”字段，如果不支持则不会包含。我们看看上面“Content-Range”的内容：\n\n```\nContent-Range: bytes 0-10/233295878\n```\n\n0-10表示本次返回的区块，233295878代表文件的总长度，单位都是byte,  也就是该文件大概233M多一点。\n\n基于此，我们可以设计一个简单的多线程的文件分块下载器，实现的思路是：\n\n1. 先检测是否支持分块传输，如果不支持，则直接下载；若支持，则将剩余内容分块下载。\n2. 各个分块下载时保存到各自临时文件，等到所有分块下载完后合并临时文件。\n3. 删除临时文件。\n\n### 实现\n\n下面是整体的流程：\n\n```dart\n// 通过第一个分块请求检测服务器是否支持分块传输  \nResponse response = await downloadChunk(url, 0, firstChunkSize, 0);\nif (response.statusCode == 206) {    //如果支持\n    //解析文件总长度，进而算出剩余长度\n    total = int.parse(\n        response.headers.value(HttpHeaders.contentRangeHeader).split(\"/\").last);\n    int reserved = total -\n        int.parse(response.headers.value(HttpHeaders.contentLengthHeader));\n    //文件的总块数(包括第一块)\n    int chunk = (reserved / firstChunkSize).ceil() + 1;\n    if (chunk > 1) {\n        int chunkSize = firstChunkSize;\n        if (chunk > maxChunk + 1) {\n            chunk = maxChunk + 1;\n            chunkSize = (reserved / maxChunk).ceil();\n        }\n        var futures = <Future>[];\n        for (int i = 0; i < maxChunk; ++i) {\n            int start = firstChunkSize + i * chunkSize;\n            //分块下载剩余文件  \n            futures.add(downloadChunk(url, start, start + chunkSize, i + 1));\n        }\n        //等待所有分块全部下载完成\n        await Future.wait(futures);\n    }\n    //合并文件文件  \n    await mergeTempFiles(chunk);\n}\n```\n\n下面我们使用dio的`download` API 实现`downloadChunk`：\n\n```dart\n//start 代表当前块的起始位置，end代表结束位置\n//no 代表当前是第几块\nFuture<Response> downloadChunk(url, start, end, no) async {\n  progress.add(0); //progress记录每一块已接收数据的长度\n  --end;\n  return dio.download(\n    url,\n    savePath + \"temp$no\", //临时文件按照块的序号命名，方便最后合并\n    onReceiveProgress: createCallback(no), // 创建进度回调，后面实现\n    options: Options(\n      headers: {\"range\": \"bytes=$start-$end\"}, //指定请求的内容区间\n    ),\n  );\n}\n```\n\n接下来实现`mergeTempFiles`:\n\n```dart\nFuture mergeTempFiles(chunk) async {\n  File f = File(savePath + \"temp0\");\n  IOSink ioSink= f.openWrite(mode: FileMode.writeOnlyAppend);\n  //合并临时文件  \n  for (int i = 1; i < chunk; ++i) {\n    File _f = File(savePath + \"temp$i\");\n    await ioSink.addStream(_f.openRead());\n    await _f.delete(); //删除临时文件\n  }\n  await ioSink.close();\n  await f.rename(savePath); //合并后的文件重命名为真正的名称\n}\n```\n\n下面我们看一下完整实现：\n\n```dart\n/// Downloading by spiting as file in chunks\nFuture downloadWithChunks(\n  url,\n  savePath, {\n  ProgressCallback onReceiveProgress,\n}) async {\n  const firstChunkSize = 102;\n  const maxChunk = 3;\n\n  int total = 0;\n  var dio = Dio();\n  var progress = <int>[];\n\n  createCallback(no) {\n    return (int received, _) {\n      progress[no] = received;\n      if (onReceiveProgress != null && total != 0) {\n        onReceiveProgress(progress.reduce((a, b) => a + b), total);\n      }\n    };\n  }\n\n  Future<Response> downloadChunk(url, start, end, no) async {\n    progress.add(0);\n    --end;\n    return dio.download(\n      url,\n      savePath + \"temp$no\",\n      onReceiveProgress: createCallback(no),\n      options: Options(\n        headers: {\"range\": \"bytes=$start-$end\"},\n      ),\n    );\n  }\n\n  Future mergeTempFiles(chunk) async {\n    File f = File(savePath + \"temp0\");\n    IOSink ioSink= f.openWrite(mode: FileMode.writeOnlyAppend);\n    for (int i = 1; i < chunk; ++i) {\n      File _f = File(savePath + \"temp$i\");\n      await ioSink.addStream(_f.openRead());\n      await _f.delete();\n    }\n    await ioSink.close();\n    await f.rename(savePath);\n  }\n\n  Response response = await downloadChunk(url, 0, firstChunkSize, 0);\n  if (response.statusCode == 206) {\n    total = int.parse(\n        response.headers.value(HttpHeaders.contentRangeHeader).split(\"/\").last);\n    int reserved = total -\n        int.parse(response.headers.value(HttpHeaders.contentLengthHeader));\n    int chunk = (reserved / firstChunkSize).ceil() + 1;\n    if (chunk > 1) {\n      int chunkSize = firstChunkSize;\n      if (chunk > maxChunk + 1) {\n        chunk = maxChunk + 1;\n        chunkSize = (reserved / maxChunk).ceil();\n      }\n      var futures = <Future>[];\n      for (int i = 0; i < maxChunk; ++i) {\n        int start = firstChunkSize + i * chunkSize;\n        futures.add(downloadChunk(url, start, start + chunkSize, i + 1));\n      }\n      await Future.wait(futures);\n    }\n    await mergeTempFiles(chunk);\n  }\n}\n```\n\n现在可以进行分块下载了：\n\n```dart\nmain() async {\n  var url = \"http://download.dcloud.net.cn/HBuilder.9.0.2.macosx_64.dmg\";\n  var savePath = \"./example/HBuilder.9.0.2.macosx_64.dmg\";\n  await downloadWithChunks(url, savePath, onReceiveProgress: (received, total) {\n    if (total != -1) {\n      print(\"${(received / total * 100).floor()}%\");\n    }\n  });\n}\n```\n\n### 思考\n\n1. 分块下载真的能提高下载速度吗？\n\n   其实下载速度的主要瓶颈是取决于网络速度和服务器的出口速度，如果是同一个数据源，分块下载的意义并不大，因为服务器是同一个，出口速度确定的，主要取决于网速，而上面的例子正式同源分块下载，读者可以自己对比一下分块和不分块的的下载速度。如果有多个下载源，并且每个下载源的出口带宽都是有限制的，这时分块下载可能会更快一下，之所以说“可能”，是由于这并不是一定的，比如有三个源，三个源的出口带宽都为1G/s，而我们设备所连网络的峰值假设只有800M/s，那么瓶颈就在我们的网络。即使我们设备的带宽大于任意一个源，下载速度依然不一定就比单源单线下载快，试想一下，假设有两个源A和B，速度A源是B源的3倍，如果采用分块下载，两个源各下载一半的话，读者可以算一下所需的下载时间，然后再算一下只从A源下载所需的时间，看看哪个更快。\n\n   分块下载的最终速度受设备所在网络带宽、源出口速度、每个块大小、以及分块的数量等诸多因素影响，实际过程中很难保证速度最优。在实际开发中，读者可可以先测试对比后再决定是否使用。\n\n2. 分块下载有什么实际的用处吗？\n\n   分块下载还有一个比较使用的场景是断点续传，可以将文件分为若干个块，然后维护一个下载状态文件用以记录每一个块的状态，这样即使在网络中断后，也可以恢复中断前的状态，具体实现读者可以自己尝试一下，还是有一些细节需要特别注意的，比如分块大小多少合适？下载到一半的块如何处理？要不要维护一个任务队列？\n"
  },
  {
    "path": "src/v2/chapter11/file_operation.md",
    "content": "# 11.1 文件操作\n\nDart的IO库包含了文件读写的相关类，它属于Dart语法标准的一部分，所以通过Dart IO库，无论是Dart VM下的脚本还是Flutter，都是通过Dart IO库来操作文件的，不过和Dart VM相比，Flutter有一个重要差异是文件系统路径不同，这是因为Dart VM是运行在PC或服务器操作系统下，而Flutter是运行在移动操作系统中，他们的文件系统会有一些差异。\n\n#### APP目录\n\nAndroid和iOS的应用存储目录不同，[`PathProvider`](https://pub.dartlang.org/packages/path_provider) 插件提供了一种平台透明的方式来访问设备文件系统上的常用位置。该类当前支持访问两个文件系统位置：\n\n- **临时目录:**  可以使用 `getTemporaryDirectory()` 来获取临时目录； 系统可随时清除的临时目录（缓存）。在iOS上，这对应于[`NSTemporaryDirectory()`](https://developer.apple.com/reference/foundation/1409211-nstemporarydirectory) 返回的值。在Android上，这是[`getCacheDir()`](https://developer.android.com/reference/android/content/Context.html#getCacheDir())返回的值。\n- **文档目录:** 可以使用`getApplicationDocumentsDirectory()`来获取应用程序的文档目录，该目录用于存储只有自己可以访问的文件。只有当应用程序被卸载时，系统才会清除该目录。在iOS上，这对应于`NSDocumentDirectory`。在Android上，这是`AppData`目录。\n- **外部存储目录**：可以使用`getExternalStorageDirectory()`来获取外部存储目录，如SD卡；由于iOS不支持外部目录，所以在iOS下调用该方法会抛出`UnsupportedError `异常，而在Android下结果是android SDK中`getExternalStorageDirectory`的返回值。\n\n一旦你的Flutter应用程序有一个文件位置的引用，你可以使用[dart:io](https://api.dartlang.org/stable/dart-io/dart-io-library.html)API来执行对文件系统的读/写操作。有关使用Dart处理文件和目录的详细内容可以参考Dart语言文档，下面我们看一个简单的例子。\n\n#### 示例\n\n我们还是以计数器为例，实现在应用退出重启后可以恢复点击次数。 这里，我们使用文件来保存数据：\n\n1. 引入PathProvider插件；在`pubspec.yaml`文件中添加如下声明：\n\n   ```yaml\n   path_provider: ^0.4.1\n   ```\n\n   添加后，执行`flutter packages get` 获取一下, 版本号可能随着时间推移会发生变化，读者可以使用最新版。\n\n2. 实现：\n\n   ```dart\n   import 'dart:io';\n   import 'dart:async';\n   import 'package:flutter/material.dart';\n   import 'package:path_provider/path_provider.dart';\n   \n   class FileOperationRoute extends StatefulWidget {\n     FileOperationRoute({Key key}) : super(key: key);\n   \n     @override\n     _FileOperationRouteState createState() => new _FileOperationRouteState();\n   }\n   \n   class _FileOperationRouteState extends State<FileOperationRoute> {\n     int _counter;\n   \n     @override\n     void initState() {\n       super.initState();\n       //从文件读取点击次数\n       _readCounter().then((int value) {\n         setState(() {\n           _counter = value;\n         });\n       });\n     }\n   \n     Future<File> _getLocalFile() async {\n       // 获取应用目录\n       String dir = (await getApplicationDocumentsDirectory()).path;\n       return new File('$dir/counter.txt');\n     }\n   \n     Future<int> _readCounter() async {\n       try {\n         File file = await _getLocalFile();\n         // 读取点击次数（以字符串）\n         String contents = await file.readAsString();\n         return int.parse(contents);\n       } on FileSystemException {\n         return 0;\n       }\n     }\n   \n     Future<Null> _incrementCounter() async {\n       setState(() {\n         _counter++;\n       });\n       // 将点击次数以字符串类型写到文件中\n       await (await _getLocalFile()).writeAsString('$_counter');\n     }\n   \n     @override\n     Widget build(BuildContext context) {\n       return new Scaffold(\n         appBar: new AppBar(title: new Text('文件操作')),\n         body: new Center(\n           child: new Text('点击了 $_counter 次'),\n         ),\n         floatingActionButton: new FloatingActionButton(\n           onPressed: _incrementCounter,\n           tooltip: 'Increment',\n           child: new Icon(Icons.add),\n         ),\n       );\n     }\n   }\n   ```\n\n   上面代码比较简单，不再赘述，需要说明的是，本示例只是为了演示文件读写，而在实际开发中，如果要存储一些简单的数据，使用shared_preferences插件会比较简单。\n\n   > 注意，Dart IO库操作文件的API非常丰富，但本书不是介绍Dart语言的，故不详细说明，读者需要的话可以自行学习。\n\n   \n\n   \n\n   \n\n   "
  },
  {
    "path": "src/v2/chapter11/http.md",
    "content": "# 11.2 通过HttpClient发起HTTP请求\n\nDart IO库中提供了用于发起Http请求的一些类，我们可以直接使用`HttpClient`来发起请求。使用`HttpClient`发起请求分为五步：\n\n1. 创建一个`HttpClient`：\n\n   ```dart\n    HttpClient httpClient = new HttpClient();\n   ```\n\n2. 打开Http连接，设置请求头：\n\n   ```dart\n   HttpClientRequest request = await httpClient.getUrl(uri);\n   ```\n\n   这一步可以使用任意Http Method，如`httpClient.post(...)`、`httpClient.delete(...)`等。如果包含Query参数，可以在构建uri时添加，如：\n\n   ```dart\n   Uri uri=Uri(scheme: \"https\", host: \"flutterchina.club\", queryParameters: {\n       \"xx\":\"xx\",\n       \"yy\":\"dd\"\n     });\n   ```\n\n   通过`HttpClientRequest`可以设置请求header，如：\n\n   ```dart\n   request.headers.add(\"user-agent\", \"test\");\n   ```\n   如果是post或put等可以携带请求体方法，可以通过HttpClientRequest对象发送request body，如：\n\n   ```dart\n   String payload=\"...\";\n   request.add(utf8.encode(payload)); \n   //request.addStream(_inputStream); //可以直接添加输入流\n   ```\n\n3. 等待连接服务器：\n\n   ```dart\n   HttpClientResponse response = await request.close();\n   ```\n\n   这一步完成后，请求信息就已经发送给服务器了，返回一个`HttpClientResponse`对象，它包含响应头（header）和响应流(响应体的Stream)，接下来就可以通过读取响应流来获取响应内容。\n\n4. 读取响应内容：\n\n   ```dart\n   String responseBody = await response.transform(utf8.decoder).join();\n   ```\n\n   我们通过读取响应流来获取服务器返回的数据，在读取时我们可以设置编码格式，这里是utf8。\n\n5. 请求结束，关闭`HttpClient`：\n\n   ```dart\n   httpClient.close();\n   ```\n\n    关闭client后，通过该client发起的所有请求都会中止。\n\n#### 示例\n\n我们实现一个获取百度首页html的例子，示例效果如图11-1所示：\n\n​    ![图11-1](../imgs/11-1.png)\n\n点击“获取百度首页”按钮后，会请求百度首页，请求成功后，我们将返回内容显示出来并在控制台打印响应header，代码如下：\n\n```dart\nimport 'dart:convert';\nimport 'dart:io';\n\nimport 'package:flutter/material.dart';\n\nclass HttpTestRoute extends StatefulWidget {\n  @override\n  _HttpTestRouteState createState() => new _HttpTestRouteState();\n}\n\nclass _HttpTestRouteState extends State<HttpTestRoute> {\n  bool _loading = false;\n  String _text = \"\";\n\n  @override\n  Widget build(BuildContext context) {\n    return ConstrainedBox(\n      constraints: BoxConstraints.expand(),\n      child: SingleChildScrollView(\n        child: Column(\n          children: <Widget>[\n            RaisedButton(\n                child: Text(\"获取百度首页\"),\n                onPressed: _loading ? null : () async {\n                  setState(() {\n                    _loading = true;\n                    _text = \"正在请求...\";\n                  });\n                  try {\n                    //创建一个HttpClient\n                    HttpClient httpClient = new HttpClient();\n                    //打开Http连接\n                    HttpClientRequest request = await httpClient.getUrl(\n                        Uri.parse(\"https://www.baidu.com\"));\n                    //使用iPhone的UA\n                    request.headers.add(\"user-agent\", \"Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1\");\n                    //等待连接服务器（会将请求信息发送给服务器）\n                    HttpClientResponse response = await request.close();\n                    //读取响应内容\n                    _text = await response.transform(utf8.decoder).join();\n                    //输出响应头\n                    print(response.headers);\n\n                    //关闭client后，通过该client发起的所有请求都会中止。\n                    httpClient.close();\n\n                  } catch (e) {\n                    _text = \"请求失败：$e\";\n                  } finally {\n                    setState(() {\n                      _loading = false;\n                    });\n                  }\n                }\n            ),\n            Container(\n                width: MediaQuery.of(context).size.width-50.0,\n                child: Text(_text.replaceAll(new RegExp(r\"\\s\"), \"\"))\n            )\n          ],\n        ),\n      ),\n    );\n  }\n}\n```\n\n控制台输出：\n\n```\nI/flutter (18545): connection: Keep-Alive\nI/flutter (18545): cache-control: no-cache\nI/flutter (18545): set-cookie: ....  //有多个，省略...\nI/flutter (18545): transfer-encoding: chunked\nI/flutter (18545): date: Tue, 30 Oct 2018 10:00:52 GMT\nI/flutter (18545): content-encoding: gzip\nI/flutter (18545): vary: Accept-Encoding\nI/flutter (18545): strict-transport-security: max-age=172800\nI/flutter (18545): content-type: text/html;charset=utf-8\nI/flutter (18545): tracecode: 00525262401065761290103018, 00522983\n```\n\n#### HttpClient配置\n\n`HttpClient`有很多属性可以配置，常用的属性列表如下：\n\n| 属性                  | 含义                                                         |\n| --------------------- | ------------------------------------------------------------ |\n| idleTimeout           | 对应请求头中的keep-alive字段值，为了避免频繁建立连接，httpClient在请求结束后会保持连接一段时间，超过这个阈值后才会关闭连接。 |\n| connectionTimeout     | 和服务器建立连接的超时，如果超过这个值则会抛出SocketException异常。 |\n| maxConnectionsPerHost | 同一个host，同时允许建立连接的最大数量。                     |\n| autoUncompress        | 对应请求头中的Content-Encoding，如果设置为true，则请求头中Content-Encoding的值为当前HttpClient支持的压缩算法列表，目前只有\"gzip\" |\n| userAgent             | 对应请求头中的User-Agent字段。                               |\n\n可以发现，有些属性只是为了更方便的设置请求头，对于这些属性，你完全可以通过`HttpClientRequest`直接设置header，不同的是通过`HttpClient`设置的对整个`httpClient`都生效，而通过`HttpClientRequest`设置的只对当前请求生效。\n\n#### HTTP请求认证\n\nHttp协议的认证（Authentication）机制可以用于保护非公开资源。如果Http服务器开启了认证，那么用户在发起请求时就需要携带用户凭据，如果你在浏览器中访问了启用Basic认证的资源时，浏览就会弹出一个登录框，如：\n\n![image-20181031114207514](https://cdn.jsdelivr.net/gh/flutterchina/flutter-in-action@1.0/docs/imgs/image-20181031114207514.png)\n\n\n\n我们先看看Basic认证的基本过程：\n\n1. 客户端发送http请求给服务器，服务器验证该用户是否已经登录验证过了，如果没有的话，  服务器会返回一个401 Unauthozied给客户端，并且在响应header中添加一个 “WWW-Authenticate” 字段，例如：\n\n   ```\n   WWW-Authenticate: Basic realm=\"admin\"\n   ```\n   其中\"Basic\"为认证方式，realm为用户角色的分组，可以在后台添加分组。\n\n2. 客户端得到响应码后，将用户名和密码进行base64编码（格式为用户名:密码），设置请求头Authorization，继续访问 :\n\n   ```\n   Authorization: Basic YXXFISDJFISJFGIJIJG\n   ```\n\n   服务器验证用户凭据，如果通过就返回资源内容。\n\n注意，Http的方式除了Basic认证之外还有：Digest认证、Client认证、Form Based认证等，目前Flutter的HttpClient只支持Basic和Digest两种认证方式，这两种认证方式最大的区别是发送用户凭据时，对于用户凭据的内容，前者只是简单的通过Base64编码（可逆），而后者会进行哈希运算，相对来说安全一点点，但是为了安全起见，**无论是采用Basic认证还是Digest认证，都应该在Https协议下**，这样可以防止抓包和中间人攻击。\n\n`HttpClient`关于Http认证的方法和属性：\n\n1. `addCredentials(Uri url, String realm, HttpClientCredentials credentials)`\n\n   该方法用于添加用户凭据,如：\n\n   ```dart\n   httpClient.addCredentials(_uri,\n    \"admin\", \n     new HttpClientBasicCredentials(\"username\",\"password\"), //Basic认证凭据\n   );\n   ```\n\n   如果是Digest认证，可以创建Digest认证凭据：\n\n   ```dart\n   HttpClientDigestCredentials(\"username\",\"password\")\n   ```\n\n2. ` authenticate(Future<bool> f(Uri url, String scheme, String realm))`\n\n   这是一个setter，类型是一个回调，当服务器需要用户凭据且该用户凭据未被添加时，httpClient会调用此回调，在这个回调当中，一般会调用`addCredential()`来动态添加用户凭证，例如：\n\n   ```dart\n   httpClient.authenticate=(Uri url, String scheme, String realm) async{\n     if(url.host==\"xx.com\" && realm==\"admin\"){\n       httpClient.addCredentials(url,\n         \"admin\",\n         new HttpClientBasicCredentials(\"username\",\"pwd\"), \n       );\n       return true;\n     }\n     return false;\n   };\n   ```\n\n   一个建议是，如果所有请求都需要认证，那么应该在HttpClient初始化时就调用`addCredentials()`来添加全局凭证，而不是去动态添加。\n\n#### 代理\n\n可以通过`findProxy`来设置代理策略，例如，我们要将所有请求通过代理服务器（192.168.1.2:8888）发送出去：\n\n```dart\n  client.findProxy = (uri) {\n    // 如果需要过滤uri，可以手动判断\n    return \"PROXY 192.168.1.2:8888\";\n };\n```\n\n`findProxy` 回调返回值是一个遵循浏览器PAC脚本格式的字符串，详情可以查看API文档，如果不需要代理，返回\"DIRECT\"即可。\n\n在APP开发中，很多时候我们需要抓包来调试，而抓包软件(如charles)就是一个代理，这时我们就可以将请求发送到我们的抓包软件，我们就可以在抓包软件中看到请求的数据了。\n\n有时代理服务器也启用了身份验证，这和http协议的认证是相似的，HttpClient提供了对应的Proxy认证方法和属性：\n\n```dart\nset authenticateProxy(\n    Future<bool> f(String host, int port, String scheme, String realm));\nvoid addProxyCredentials(\n    String host, int port, String realm, HttpClientCredentials credentials);\n```\n\n他们的使用方法和上面“HTTP请求认证”一节中介绍的`addCredentials`和`authenticate` 相同，故不再赘述。\n\n#### 证书校验\n\nHttps中为了防止通过伪造证书而发起的中间人攻击，客户端应该对自签名或非CA颁发的证书进行校验。`HttpClient`对证书校验的逻辑如下：\n\n1. 如果请求的Https证书是可信CA颁发的，并且访问host包含在证书的domain列表中(或者符合通配规则)并且证书未过期，则验证通过。\n2. 如果第一步验证失败，但在创建HttpClient时，已经通过SecurityContext将证书添加到证书信任链中，那么当服务器返回的证书在信任链中的话，则验证通过。\n3. 如果1、2验证都失败了，如果用户提供了`badCertificateCallback`回调，则会调用它，如果回调返回`true`，则允许继续链接，如果返回`false`，则终止链接。\n\n综上所述，我们的证书校验其实就是提供一个`badCertificateCallback`回调，下面通过一个示例来说明。\n\n##### 示例\n\n假设我们的后台服务使用的是自签名证书，证书格式是PEM格式，我们将证书的内容保存在本地字符串中，那么我们的校验逻辑如下：\n\n```dart\nString PEM=\"XXXXX\";//可以从文件读取\n...\nhttpClient.badCertificateCallback=(X509Certificate cert, String host, int port){\n  if(cert.pem==PEM){\n    return true; //证书一致，则允许发送数据\n  }\n  return false;\n};\n```\n\n`X509Certificate`是证书的标准格式，包含了证书除私钥外所有信息，读者可以自行查阅文档。另外，上面的示例没有校验host，是因为只要服务器返回的证书内容和本地的保存一致就已经能证明是我们的服务器了（而不是中间人），host验证通常是为了防止证书和域名不匹配。\n\n对于自签名的证书，我们也可以将其添加到本地证书信任链中，这样证书验证时就会自动通过，而不会再走到`badCertificateCallback`回调中：\n\n```dart\nSecurityContext sc=new SecurityContext();\n//file为证书路径\nsc.setTrustedCertificates(file);\n//创建一个HttpClient\nHttpClient httpClient = new HttpClient(context: sc);\n```\n\n注意，通过`setTrustedCertificates()`设置的证书格式必须为PEM或PKCS12，如果证书格式为PKCS12，则需将证书密码传入，这样则会在代码中暴露证书密码，所以客户端证书校验不建议使用PKCS12格式的证书。\n\n#### 总结\n\n值得注意的是，`HttpClient`提供的这些属性和方法最终都会作用在请求header里，我们完全可以通过手动去设置header来实现，之所以提供这些方法，只是为了方便开发者而已。另外，Http协议是一个非常重要的、使用最多的网络协议，每一个开发者都应该对http协议非常熟悉。\n\n"
  },
  {
    "path": "src/v2/chapter11/index.md",
    "content": "# 本章目录\n\n* [11.1：文件操作](file_operation.md)\n* [11.2：Http请求-HttpClient](http.md)\n* [11.3：Http请求-Dio package](dio.md) \n* [11.4：实例：Http分块下载](download_with_chunks.md) \n* [11.5：WebSocket](websocket.md) \n* [11.6：使用Socket API](socket.md) \n* [11.7：Json转Dart Model类](json_model.md) \n"
  },
  {
    "path": "src/v2/chapter11/json_model.md",
    "content": "# 11.7 Json转Dart Model类\n\n在实战中，后台接口往往会返回一些结构化数据，如JSON、XML等，如之前我们请求Github API的示例，它返回的数据就是JSON格式的字符串，为了方便我们在代码中操作JSON，我们先将JSON格式的字符串转为Dart对象，这个可以通过`dart:convert`中内置的JSON解码器json.decode() 来实现，该方法可以根据JSON字符串具体内容将其转为List或Map，这样我们就可以通过他们来查找所需的值，如：\n\n```dart\n//一个JSON格式的用户列表字符串\nString jsonStr='[{\"name\":\"Jack\"},{\"name\":\"Rose\"}]';\n//将JSON字符串转为Dart对象(此处是List)\nList items=json.decode(jsonStr);\n//输出第一个用户的姓名\nprint(items[0][\"name\"]);\n```\n\n通过json.decode() 将JSON字符串转为List/Map的方法比较简单，它没有外部依赖或其它的设置，对于小项目很方便。但当项目变大时，这种手动编写序列化逻辑可能变得难以管理且容易出错，例如有如下JSON:\n\n```json\n{\n  \"name\": \"John Smith\",\n  \"email\": \"john@example.com\"\n}\n```\n\n我们可以通过调用`json.decode`方法来解码JSON ，使用JSON字符串作为参数:\n\n```dart\nMap<String, dynamic> user = json.decode(json);\n\nprint('Howdy, ${user['name']}!');\nprint('We sent the verification link to ${user['email']}.');\n```\n\n\n\n由于`json.decode()`仅返回一个`Map<String, dynamic>`，这意味着直到运行时我们才知道值的类型。 通过这种方法，我们失去了大部分静态类型语言特性：类型安全、自动补全和最重要的编译时异常。这样一来，我们的代码可能会变得非常容易出错。例如，当我们访问`name`或`email`字段时，我们输入的很快，导致字段名打错了。但由于这个JSON在map结构中，所以编译器不知道这个错误的字段名，所以编译时不会报错。\n\n其实，这个问题在很多平台上都会遇到，而也早就有了好的解决方法即“Json Model化”，具体做法就是，通过预定义一些与Json结构对应的Model类，然后在请求到数据后再动态根据数据创建出Model类的实例。这样一来，在开发阶段我们使用的是Model类的实例，而不再是Map/List，这样访问内部属性时就不会发生拼写错误。例如，我们可以通过引入一个简单的模型类(Model class)来解决前面提到的问题，我们称之为`User`。在User类内部，我们有：\n\n- 一个`User.fromJson` 构造函数, 用于从一个map构造出一个 `User`实例 map structure\n- 一个`toJson` 方法, 将 `User` 实例转化为一个map.\n\n这样，调用代码现在可以具有类型安全、自动补全字段（name和email）以及编译时异常。如果我们将拼写错误字段视为`int`类型而不是`String`， 那么我们的代码就不会通过编译，而不是在运行时崩溃。\n\n**user.dart**\n\n```dart\nclass User {\n  final String name;\n  final String email;\n\n  User(this.name, this.email);\n\n  User.fromJson(Map<String, dynamic> json)\n      : name = json['name'],\n        email = json['email'];\n\n  Map<String, dynamic> toJson() =>\n    <String, dynamic>{\n      'name': name,\n      'email': email,\n    };\n}\n```\n\n现在，序列化逻辑移到了模型本身内部。采用这种新方法，我们可以非常容易地反序列化user.\n\n```dart\nMap userMap = json.decode(json);\nvar user = new User.fromJson(userMap);\n\nprint('Howdy, ${user.name}!');\nprint('We sent the verification link to ${user.email}.');\n```\n\n要序列化一个user，我们只是将该`User`对象传递给该`json.encode`方法。我们不需要手动调用`toJson`这个方法，因为`JSON.encode内部会自动调用。\n\n```dart\nString json = json.encode(user);\n```\n\n这样，调用代码就不用担心JSON序列化了，但是，Model类还是必须的。在实践中，`User.fromJson`和`User.toJson`方法都需要单元测试到位，以验证正确的行为。\n\n另外，实际场景中，JSON对象很少会这么简单，嵌套的JSON对象并不罕见，如果有什么能为我们自动处理JSON序列化，那将会非常好。幸运的是，有！\n\n### 自动生成Model\n\n尽管还有其他库可用，但在本书中，我们介绍一下官方推荐的[json_serializable package](https://pub.dartlang.org/packages/json_serializable)包。 它是一个自动化的源代码生成器，可以在开发阶段为我们生成JSON序列化模板，这样一来，由于序列化代码不再由我们手写和维护，我们将运行时产生JSON序列化异常的风险降至最低。\n\n### 在项目中设置json_serializable\n\n要包含`json_serializable`到我们的项目中，我们需要一个常规和两个**开发依赖**项。简而言之，**开发依赖项**是不包含在我们的应用程序源代码中的依赖项，它是开发过程中的一些辅助工具、脚本，和node中的开发依赖项相似。\n\n**pubspec.yaml**\n\n```yaml\ndependencies:\n  # Your other regular dependencies here\n  json_annotation: ^2.0.0\n\ndev_dependencies:\n  # Your other dev_dependencies here\n  build_runner: ^1.0.0\n  json_serializable: ^2.0.0\n```\n\n在您的项目根文件夹中运行 `flutter packages get` (或者在编辑器中点击 “Packages Get”) 以在项目中使用这些新的依赖项.\n\n### 以json_serializable的方式创建model类\n\n让我们看看如何将我们的`User`类转换为一个`json_serializable`。为了简单起见，我们使用前面示例中的简化JSON model。\n\n**user.dart**\n\n```dart\nimport 'package:json_annotation/json_annotation.dart';\n\n// user.g.dart 将在我们运行生成命令后自动生成\npart 'user.g.dart';\n\n///这个标注是告诉生成器，这个类是需要生成Model类的\n@JsonSerializable()\n\nclass User{\n  User(this.name, this.email);\n\n  String name;\n  String email;\n  //不同的类使用不同的mixin即可\n  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);\n  Map<String, dynamic> toJson() => _$UserToJson(this);  \n}\n```\n\n有了上面的设置，源码生成器将生成用于序列化`name`和`email`字段的JSON代码。\n\n如果需要，自定义命名策略也很容易。例如，如果我们正在使用的API返回带有_snake_case_的对象，但我们想在我们的模型中使用_lowerCamelCase_， 那么我们可以使用@JsonKey标注：\n\n```dart\n//显式关联JSON字段名与Model属性的对应关系 \n@JsonKey(name: 'registration_date_millis')\nfinal int registrationDateMillis;\n```\n\n### 运行代码生成程序\n\n`json_serializable`第一次创建类时，您会看到与图11-4类似的错误。\n\n![ide_warning](../imgs/11-4.png)\n\n这些错误是完全正常的，这是因为Model类的生成代码还不存在。为了解决这个问题，我们必须运行代码生成器来为我们生成序列化模板。有两种运行代码生成器的方法：\n\n#### 一次性生成\n\n通过在我们的项目根目录下运行:\n\n```shell\nflutter packages pub run build_runner build\n```\n\n 这触发了一次性构建，我们可以在需要时为我们的Model生成json序列化代码，它通过我们的源文件，找出需要生成Model类的源文件（包含@JsonSerializable标注的）来生成对应的.g.dart文件。一个好的建议是将所有Model类放在一个单独的目录下，然后在该目录下执行命令。\n\n虽然这非常方便，但如果我们不需要每次在Model类中进行更改时都要手动运行构建命令的话会更好。\n\n#### 持续生成\n\n使用_watcher_可以使我们的源代码生成的过程更加方便。它会监视我们项目中文件的变化，并在需要时自动构建必要的文件，我们可以通过`flutter packages pub run build_runner watch`在项目根目录下运行来启动_watcher_。只需启动一次观察器，然后它就会在后台运行，这是安全的。\n\n\n\n### 自动化生成模板\n\n上面的方法有一个最大的问题就是要为每一个json写模板，这是比较枯燥的。如果有一个工具可以直接根据JSON文本生成模板，那我们就能彻底解放双手了。笔者自己用dart实现了一个脚本，它可以自动生成模板，并直接将JSON转为Model类，下面我们看看怎么做：\n\n1. 定义一个\"模板的模板\"，名为\"template.dart\"：\n\n   ```dart\n   import 'package:json_annotation/json_annotation.dart';\n   %t\n   part '%s.g.dart';\n   @JsonSerializable()\n   class %s {\n       %s();\n   \n       %s\n       factory %s.fromJson(Map<String,dynamic> json) => _$%sFromJson(json);\n       Map<String, dynamic> toJson() => _$%sToJson(this);\n   }\n   ```\n\n   模板中的“%t”、“%s”为占位符，将在脚本运行时动态被替换为合适的导入头和类名。\n\n2. 写一个自动生成模板的脚本(mo.dart)，它可以根据指定的JSON目录，遍历生成模板，在生成时我们定义一些规则：\n\n   - 如果JSON文件名以下划线“_”开始，则忽略此JSON文件。\n   - 复杂的JSON对象往往会出现嵌套，我们可以通过一个特殊标志来手动指定嵌套的对象（后面举例）。\n\n   脚本我们通过Dart来写，源码如下：\n\n   ```dart\n   import 'dart:convert';\n   import 'dart:io';\n   import 'package:path/path.dart' as path;\n   const TAG=\"\\$\";\n   const SRC=\"./json\"; //JSON 目录\n   const DIST=\"lib/models/\"; //输出model目录\n   \n   void walk() { //遍历JSON目录生成模板\n     var src = new Directory(SRC);\n     var list = src.listSync();\n     var template=new File(\"./template.dart\").readAsStringSync();\n     File file;\n     list.forEach((f) {\n       if (FileSystemEntity.isFileSync(f.path)) {\n         file = new File(f.path);\n         var paths=path.basename(f.path).split(\".\");\n         String name=paths.first;\n         if(paths.last.toLowerCase()!=\"json\"||name.startsWith(\"_\")) return ;\n         if(name.startsWith(\"_\")) return;\n         //下面生成模板\n         var map = json.decode(file.readAsStringSync());\n         //为了避免重复导入相同的包，我们用Set来保存生成的import语句。\n         var set= new Set<String>();\n         StringBuffer attrs= new StringBuffer();\n         (map as Map<String, dynamic>).forEach((key, v) {\n             if(key.startsWith(\"_\")) return ;\n             attrs.write(getType(v,set,name));\n             attrs.write(\" \");\n             attrs.write(key);\n             attrs.writeln(\";\");\n             attrs.write(\"    \");\n         });\n         String  className=name[0].toUpperCase()+name.substring(1);\n         var dist=format(template,[name,className,className,attrs.toString(),\n                                   className,className,className]);\n         var _import=set.join(\";\\r\\n\");\n         _import+=_import.isEmpty?\"\":\";\";\n         dist=dist.replaceFirst(\"%t\",_import );\n         //将生成的模板输出\n         new File(\"$DIST$name.dart\").writeAsStringSync(dist);\n       }\n     });\n   }\n   \n   String changeFirstChar(String str, [bool upper=true] ){\n     return (upper?str[0].toUpperCase():str[0].toLowerCase())+str.substring(1);\n   }\n   \n   //将JSON类型转为对应的dart类型\n    String getType(v,Set<String> set,String current){\n     current=current.toLowerCase();\n     if(v is bool){\n       return \"bool\";\n     }else if(v is num){\n       return \"num\";\n     }else if(v is Map){\n       return \"Map<String,dynamic>\";\n     }else if(v is List){\n       return \"List\";\n     }else if(v is String){ //处理特殊标志\n       if(v.startsWith(\"$TAG[]\")){\n         var className=changeFirstChar(v.substring(3),false);\n         if(className.toLowerCase()!=current) {\n           set.add('import \"$className.dart\"');\n         }\n         return \"List<${changeFirstChar(className)}>\";\n   \n       }else if(v.startsWith(TAG)){\n         var fileName=changeFirstChar(v.substring(1),false);\n         if(fileName.toLowerCase()!=current) {\n           set.add('import \"$fileName.dart\"');\n         }\n         return changeFirstChar(fileName);\n       }\n       return \"String\";\n     }else{\n       return \"String\";\n     }\n    }\n   \n   //替换模板占位符\n   String format(String fmt, List<Object> params) {\n     int matchIndex = 0;\n     String replace(Match m) {\n       if (matchIndex < params.length) {\n         switch (m[0]) {\n           case \"%s\":\n             return params[matchIndex++].toString();\n         }\n       } else {\n         throw new Exception(\"Missing parameter for string format\");\n       }\n       throw new Exception(\"Invalid format string: \" + m[0].toString());\n     }\n     return fmt.replaceAllMapped(\"%s\", replace);\n   }\n   \n   void main(){\n     walk();\n   }\n   ```\n\n3. 写一个shell(mo.sh)，将生成模板和生成model串起来：\n\n   ```sh\n   dart mo.dart\n   flutter packages pub run build_runner build --delete-conflicting-outputs\n   ```\n\n至此，我们的脚本写好了，我们在根目录下新建一个json目录，然后把user.json移进去，然后在lib目录下创建一个models目录，用于保存最终生成的Model类。现在我们只需要一句命令即可生成Model类了:\n\n```\n./mo.sh  \n```\n\n运行后，一切都将自动执行，现在好多了，不是吗？\n\n#### 嵌套JSON\n\n我们定义一个person.json内容修改为：\n\n```json\n{\n  \"name\": \"John Smith\",\n  \"email\": \"john@example.com\",\n  \"mother\":{\n    \"name\": \"Alice\",\n    \"email\":\"alice@example.com\"\n  },\n  \"friends\":[\n    {\n      \"name\": \"Jack\",\n      \"email\":\"Jack@example.com\"\n    },\n    {\n      \"name\": \"Nancy\",\n      \"email\":\"Nancy@example.com\"\n    }\n  ]\n}\n```\n\n每个Person都有`name` 、`email` 、 `mother`和`friends`四个字段，由于`mother`也是一个Person，朋友是多个Person(数组)，所以我们期望生成的Model是下面这样：\n\n```dart\nimport 'package:json_annotation/json_annotation.dart';\npart 'person.g.dart';\n\n@JsonSerializable()\nclass Person {\n    Person();\n    \n    String name;\n    String email;\n    Person mother;\n    List<Person> friends;\n\n    factory Person.fromJson(Map<String,dynamic> json) => _$PersonFromJson(json);\n    Map<String, dynamic> toJson() => _$PersonToJson(this);\n}\n\n```\n\n这时，我们只需要简单修改一下JSON，添加一些特殊标志，重新运行mo.sh即可：\n\n```json\n{\n  \"name\": \"John Smith\",\n  \"email\": \"john@example.com\",\n  \"mother\":\"$person\",\n  \"friends\":\"$[]person\"\n}\n```\n\n我们使用美元符“$”作为特殊标志符(如果与内容冲突，可以修改mo.dart中的`TAG`常量，自定义标志符)，脚本在遇到特殊标志符后会先把相应字段转为相应的对象或对象数组，对象数组需要在标志符后面添加数组符“[]”，符号后面接具体的类型名，此例中是person。其它类型同理，加入我们给User添加一个Person类型的 `boss`字段：\n\n```json\n{\n  \"name\": \"John Smith\",\n  \"email\": \"john@example.com\",\n  \"boss\":\"$person\"\n}\n```\n\n \n\n重新运行mo.sh，生成的user.dart如下：\n\n```dart\nimport 'package:json_annotation/json_annotation.dart';\nimport \"person.dart\";\npart 'user.g.dart';\n\n@JsonSerializable()\n\nclass User {\n    User();\n\n    String name;\n    String email;\n    Person boss;\n    \n    factory User.fromJson(Map<String,dynamic> json) => _$UserFromJson(json);\n    Map<String, dynamic> toJson() => _$UserToJson(this);\n}\n```\n可以看到，`boss`字段已自动添加，并自动导入了“person.dart”。\n\n### Json_model 包\n\n如果每个项目都要构建一个上面这样的脚本显然很麻烦，为此，我们将上面脚本和生成模板封装了一个包,已经发布到了Pub上，包名为[Json_model](https://github.com/flutterchina/json_model)，开发者把该包加入开发依赖后，便可以用一条命令，根据Json文件生成Dart类。另外[Json_model](https://github.com/flutterchina/json_model) 处于迭代中，功能会逐渐完善，所以建议读者直接使用该包（而不是手动复制上面的代码）。\n\n### 使用IDE插件生成model\n\n目前Android Studio(或IntelliJ)有几个插件，可以将json文件转成Model类，但插件质量参差不齐，甚至还有一些沾染上了抄袭风波，故笔者在此不做优先推荐，读者有兴趣可以自行了解。但是，我们还是要了解一下IDE插件和[Json_model](https://link.juejin.im?target=https%3A%2F%2Fgithub.com%2Fflutterchina%2Fjson_model)的优劣：\n\n1. [Json_model](https://link.juejin.im?target=https%3A%2F%2Fgithub.com%2Fflutterchina%2Fjson_model)需要单独维护一个存放Json文件的文件夹，如果有改动，只需修改Json文件便可重新生成Model类；而IDE插件一般需要用户手动将Json内容拷贝复制到一个输入框中，这样生成之后Json文件没有存档的化，之后要改动就需要手动。\n2. [Json_model](https://link.juejin.im?target=https%3A%2F%2Fgithub.com%2Fflutterchina%2Fjson_model)可以手动指定某个字段引用的其它Model类，可以避免生成重复的类；而IDE插件一般会为每一个Json文件中所有嵌套对象都单独生成一个Model类，即使这些嵌套对象可能在其它Model类中已经生成过。\n3. [Json_model](https://link.juejin.im?target=https%3A%2F%2Fgithub.com%2Fflutterchina%2Fjson_model) 提供了命令行转化方式，可以方便集成到CI等非UI环境的场景。\n\n### FAQ\n\n很多人可能会问Flutter中有没有像Java开发中的Gson/Jackson一样的Json序列化类库？答案是没有！因为这样的库需要使用运行时反射，这在Flutter中是禁用的。运行时反射会干扰Dart的_tree shaking_，使用_tree shaking_，可以在release版中“去除”未使用的代码，这可以显著优化应用程序的大小。由于反射会默认应用到所有代码，因此_tree shaking_会很难工作，因为在启用反射时很难知道哪些代码未被使用，因此冗余代码很难剥离，所以Flutter中禁用了Dart的反射功能，而正因如此也就无法实现动态转化Model的功能。\n\n \n\n\n"
  },
  {
    "path": "src/v2/chapter11/socket.md",
    "content": "# 11.6 使用Socket API\n\n我们之前介绍的Http协议和WebSocket协议都属于应用层协议，除了它们，应用层协议还有很多如：SMTP、FTP等，这些应用层协议的实现都是通过Socket API来实现的。其实，操作系统中提供的原生网络请求API是标准的，在C语言的Socket库中，它主要提供了端到端建立链接和发送数据的基础API，而高级编程语言中的Socket库其实都是对操作系统的socket API的一个封装。所以，如果我们需要自定义协议或者想直接来控制管理网络链接、又或者我们觉得自带的HttpClient不好用想重新实现一个，这时我们就需要使用Socket。Flutter的Socket API在dart：io包中，下面我们看一个使用Socket实现简单http请求的示例，以请求百度首页为例：\n\n```dart\n_request() async{\n  //建立连接\n  var socket=await Socket.connect(\"baidu.com\", 80);\n  //根据http协议，发送请求头\n  socket.writeln(\"GET / HTTP/1.1\");\n  socket.writeln(\"Host:baidu.com\");\n  socket.writeln(\"Connection:close\");\n  socket.writeln();\n  await socket.flush(); //发送\n  //读取返回内容\n  _response =await socket.transform(utf8.decoder).join();\n  await socket.close();\n}\n```\n\n可以看到，使用Socket需要我们自己实现Http协议（需要自己实现和服务器的通信过程），本例只是一个简单示例，没有处理重定向、cookie等。本示例完整代码参考示例demo，运行后效果如图11-2所示：\n\n![图11-2](../imgs/11-2.png)\n\n可以看到响应内容分两个部分，第一部分是响应头，第二部分是响应体，服务端可以根据请求信息动态来输出响应体。由于本示例请求头比较简单，所以响应体和浏览器中访问的会有差别，读者可以补充一些请求头(如user-agent)来看看输出的变化。\n\n"
  },
  {
    "path": "src/v2/chapter11/websocket.md",
    "content": "\n# 使用WebSockets\n\nHttp协议是无状态的，只能由客户端主动发起，服务端再被动响应，服务端无法向客户端主动推送内容，并且一旦服务器响应结束，链接就会断开(见注解部分)，所以无法进行实时通信。WebSocket协议正是为解决客户端与服务端实时通信而产生的技术，现在已经被主流浏览器支持，所以对于Web开发者来说应该比较熟悉了，Flutter也提供了专门的包来支持WebSocket协议。\n\n> 注意：Http协议中虽然可以通过keep-alive机制使服务器在响应结束后链接会保持一段时间，但最终还是会断开，keep-alive机制主要是用于避免在同一台服务器请求多个资源时频繁创建链接，它本质上是支持链接复用的技术，而并非用于实时通信，读者需要知道这两者的区别。\n\nWebSocket协议本质上是一个基于tcp的协议，它是先通过HTTP协议发起一条特殊的http请求进行握手后，如果服务端支持WebSocket协议，则会进行协议升级。WebSocket会使用http协议握手后创建的tcp链接，和http协议不同的是，WebSocket的tcp链接是个长链接（不会断开），所以服务端与客户端就可以通过此TCP连接进行实时通信。有关WebSocket协议细节，读者可以看RFC文档，下面我们重点看看Flutter中如何使用WebSocket。\n\n在接下来例子中，我们将连接到由[websocket.org提供的测试服务器](http://www.websocket.org/echo.html)。服务器将简单地返回我们发送给它的相同消息！\n\n### 步骤\n\n1. 连接到WebSocket服务器。\n2. 监听来自服务器的消息。\n3. 将数据发送到服务器。\n4. 关闭WebSocket连接。\n\n### 1. 连接到WebSocket服务器\n\n[web_socket_channel](https://pub.dartlang.org/packages/web_socket_channel) package 提供了我们需要连接到WebSocket服务器的工具。该package提供了一个`WebSocketChannel`允许我们既可以监听来自服务器的消息，又可以将消息发送到服务器的方法。\n\n在Flutter中，我们可以创建一个`WebSocketChannel`连接到一台服务器：\n\n```dart\nfinal channel = IOWebSocketChannel.connect('ws://echo.websocket.org');\n```\n\n### 2. 监听来自服务器的消息\n\n现在我们建立了连接，我们可以监听来自服务器的消息，在我们发送消息给测试服务器之后，它会返回相同的消息。\n\n我们如何收取消息并显示它们？在这个例子中，我们将使用一个[`StreamBuilder`](https://docs.flutter.io/flutter/widgets/StreamBuilder-class.html) 来监听新消息， 并用一个Text来显示它们。\n\n```dart\nnew StreamBuilder(\n  stream: widget.channel.stream,\n  builder: (context, snapshot) {\n    return new Text(snapshot.hasData ? '${snapshot.data}' : '');\n  },\n);\n```\n\n#### 工作原理\n\n`WebSocketChannel`提供了一个来自服务器的消息`Stream` 。该`Stream`类是`dart:async`包中的一个基础类。它提供了一种方法来监听来自数据源的异步事件。与`Future`返回单个异步响应不同，`Stream`类可以随着时间推移传递很多事件。该[`StreamBuilder`](https://docs.flutter.io/flutter/widgets/StreamBuilder-class.html) 组件将连接到一个`Stream`， 并在每次收到消息时通知Flutter重新构建界面。\n\n### 3. 将数据发送到服务器\n\n为了将数据发送到服务器，我们会`add`消息给`WebSocketChannel`提供的sink。\n\n```dart\nchannel.sink.add('Hello!');\n```\n\n#### 工作原理\n\n`WebSocketChannel`提供了一个[`StreamSink`](https://docs.flutter.io/flutter/dart-async/StreamSink-class.html)，它将消息发给服务器。\n\n`StreamSink`类提供了给数据源同步或异步添加事件的一般方法。\n\n### 4. 关闭WebSocket连接\n\n在我们使用`WebSocket`后，要关闭连接：\n\n```dart\nchannel.sink.close();\n```\n\n### 完整的例子\n\n```dart\nimport 'package:flutter/material.dart';\nimport 'package:web_socket_channel/io.dart';\n\nclass WebSocketRoute extends StatefulWidget {\n  @override\n  _WebSocketRouteState createState() => new _WebSocketRouteState();\n}\n\nclass _WebSocketRouteState extends State<WebSocketRoute> {\n  TextEditingController _controller = new TextEditingController();\n  IOWebSocketChannel channel;\n  String _text = \"\";\n\n\n  @override\n  void initState() {\n    //创建websocket连接\n    channel = new IOWebSocketChannel.connect('ws://echo.websocket.org');\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return new Scaffold(\n      appBar: new AppBar(\n        title: new Text(\"WebSocket(内容回显)\"),\n      ),\n      body: new Padding(\n        padding: const EdgeInsets.all(20.0),\n        child: new Column(\n          crossAxisAlignment: CrossAxisAlignment.start,\n          children: <Widget>[\n            new Form(\n              child: new TextFormField(\n                controller: _controller,\n                decoration: new InputDecoration(labelText: 'Send a message'),\n              ),\n            ),\n            new StreamBuilder(\n              stream: channel.stream,\n              builder: (context, snapshot) {\n                //网络不通会走到这\n                if (snapshot.hasError) {\n                  _text = \"网络不通...\";\n                } else if (snapshot.hasData) {\n                  _text = \"echo: \"+snapshot.data;\n                }\n                return new Padding(\n                  padding: const EdgeInsets.symmetric(vertical: 24.0),\n                  child: new Text(_text),\n                );\n              },\n            )\n          ],\n        ),\n      ),\n      floatingActionButton: new FloatingActionButton(\n        onPressed: _sendMessage,\n        tooltip: 'Send message',\n        child: new Icon(Icons.send),\n      ),\n    );\n  }\n\n  void _sendMessage() {\n    if (_controller.text.isNotEmpty) {\n      channel.sink.add(_controller.text);\n    }\n  }\n\n  @override\n  void dispose() {\n    channel.sink.close();\n    super.dispose();\n  }\n}\n```\n\n上面的例子比较简单，不再赘述。我们现在思考一个问题，假如我们想通过WebSocket传输二进制数据应该怎么做（比如要从服务器接收一张图片）？我们发现`StreamBuilder`和`Stream`都没有指定接收类型的参数，并且在创建WebSocket链接时也没有相应的配置，貌似没有什么办法……其实很简单，要接收二进制数据仍然使用`StreamBuilder`，因为WebSocket中所有发送的数据使用帧的形式发送，而帧是有固定格式，每一个帧的数据类型都可以通过Opcode字段指定，它可以指定当前帧是文本类型还是二进制类型（还有其它类型），所以客户端在收到帧时就已经知道了其数据类型，所以flutter完全可以在收到数据后解析出正确的类型，所以就无需开发者去关心，当服务器传输的数据是指定为二进制时，`StreamBuilder`的`snapshot.data`的类型就是`List<int>`，是文本时，则为`String`。\n"
  },
  {
    "path": "src/v2/chapter12/android_implement.md",
    "content": "\n\n# 12.4 插件开发：Android端API实现\n\n本节我们接着上一节\"获取电池电量\"插件的示例，来完成Android端API的实现。以下步骤是使用Java的示例，如果您更喜欢Kotlin，可以直接跳到后面Kotlin部分。\n\n首先在Android Studio中打开您的Flutter应用的Android部分：\n\n1. 启动 Android Studio\n2. 选择 File > Open…\n3. 定位到您 Flutter app目录, 然后选择里面的 `android`文件夹，点击 OK\n4. 在`java`目录下打开 `MainActivity.java`\n\n接下来，在`onCreate`里创建MethodChannel并设置一个`MethodCallHandler`。确保使用和Flutter客户端中使用的通道名称相同的名称。\n\n```dart\nimport io.flutter.app.FlutterActivity;\nimport io.flutter.plugin.common.MethodCall;\nimport io.flutter.plugin.common.MethodChannel;\nimport io.flutter.plugin.common.MethodChannel.MethodCallHandler;\nimport io.flutter.plugin.common.MethodChannel.Result;\n\npublic class MainActivity extends FlutterActivity {\n    private static final String CHANNEL = \"samples.flutter.io/battery\";\n\n    @Override\n    public void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(\n          new MethodCallHandler() {\n             @Override\n             public void onMethodCall(MethodCall call, Result result) {\n                 // TODO\n             }\n          });\n    }\n}\n```\n\n接下来，我们添加Java代码，使用Android电池API来获取电池电量。此代码和在原生Android应用中编写的代码完全相同。\n\n首先，添加需要导入的依赖。\n\n```dart\nimport android.content.ContextWrapper;\nimport android.content.Intent;\nimport android.content.IntentFilter;\nimport android.os.BatteryManager;\nimport android.os.Build.VERSION;\nimport android.os.Build.VERSION_CODES;\nimport android.os.Bundle;\n```\n\n然后，将下面的新方法添加到activity类中的，位于onCreate 方法下方：\n\n```java\nprivate int getBatteryLevel() {\n  int batteryLevel = -1;\n  if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {\n    BatteryManager batteryManager = (BatteryManager) getSystemService(BATTERY_SERVICE);\n    batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);\n  } else {\n    Intent intent = new ContextWrapper(getApplicationContext()).\n        registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));\n    batteryLevel = (intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100) /\n        intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);\n  }\n\n  return batteryLevel;\n}\n```\n\n最后，我们完成之前添加的`onMethodCall`方法。我们需要处理平台方法名为`getBatteryLevel`的调用消息，所以我们需要先在call参数判断调用的方法是否为`getBatteryLevel`。 这个平台方法的实现只需调用我们在前一步中编写的Android代码，并通过result参数返回成功或错误情况的响应信息。如果调用了未定义的API，我们也会通知返回：\n\n```java\n@Override\npublic void onMethodCall(MethodCall call, Result result) {\n    if (call.method.equals(\"getBatteryLevel\")) {\n        int batteryLevel = getBatteryLevel();\n\n        if (batteryLevel != -1) {\n            result.success(batteryLevel);\n        } else {\n            result.error(\"UNAVAILABLE\", \"Battery level not available.\", null);\n        }\n    } else {\n        result.notImplemented();\n    }\n}  \n```\n\n现在就可以在Android上运行该应用程序了，如果使用的是Android模拟器，则可以通过工具栏中的\"...\"按钮访问Extended Controls面板中的电池电量。\n\n### 使用Kotlin添加Android平台特定的实现\n\n使用Kotlin和使用Java的步骤类似，首先在Android Studio中打开您的Flutter应用的Android部分：\n\n1. 启动 Android Studio。\n2. 选择 the menu item \"File > Open…\"。\n3. 定位到 Flutter app目录, 然后选择里面的 `android`文件夹，点击 OK。\n4. 在`kotlin`目录中打开`MainActivity.kt`。\n\n接下来，在`onCreate`里创建MethodChannel并设置一个`MethodCallHandler`。确保使用与在Flutter客户端使用的通道名称相同。\n\n```kotlin\nimport android.os.Bundle\nimport io.flutter.app.FlutterActivity\nimport io.flutter.plugin.common.MethodChannel\nimport io.flutter.plugins.GeneratedPluginRegistrant\n\nclass MainActivity() : FlutterActivity() {\n  private val CHANNEL = \"samples.flutter.io/battery\"\n\n  override fun onCreate(savedInstanceState: Bundle?) {\n    super.onCreate(savedInstanceState)\n    GeneratedPluginRegistrant.registerWith(this)\n\n    MethodChannel(flutterView, CHANNEL).setMethodCallHandler { call, result ->\n      // TODO\n    }\n  }\n}\n```\n\n接下来，我们添加Kotlin代码，使用Android电池API来获取电池电量，这和原生开发是一样的。\n\n首先，添加需要导入的依赖。\n\n```kotlin\nimport android.content.Context\nimport android.content.ContextWrapper\nimport android.content.Intent\nimport android.content.IntentFilter\nimport android.os.BatteryManager\nimport android.os.Build.VERSION\nimport android.os.Build.VERSION_CODES\n```\n\n然后，将下面的新方法添加到activity类中的，位于onCreate 方法下方：\n\n```kotlin\n  private fun getBatteryLevel(): Int {\n    val batteryLevel: Int\n    if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {\n      val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager\n      batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)\n    } else {\n      val intent = ContextWrapper(applicationContext).registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))\n      batteryLevel = intent!!.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100 / intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)\n    }\n\n    return batteryLevel\n  }\n```\n\n最后，我们完成之前添加的`onMethodCall`方法。我们需要处理平台方法名为`getBatteryLevel`的调用消息，所以我们需要先在call参数判断调用的方法是否为`getBatteryLevel`。 这个平台方法的实现只需调用我们在前一步中编写的Android代码，并通过result参数返回成功或错误情况的响应信息。如果调用了未定义的API，我们也会通知返回：\n​           \n```kotlin\nMethodChannel(flutterView, CHANNEL).setMethodCallHandler { call, result ->\n  if (call.method == \"getBatteryLevel\") {\n     val batteryLevel = getBatteryLevel()\n     if (batteryLevel != -1) {\n       result.success(batteryLevel)\n     } else {\n       result.error(\"UNAVAILABLE\", \"Battery level not available.\", null)\n     }\n  } else {\n      result.notImplemented()\n  }\n}\n```\n\n您现在就可以在Android上运行该应用程序。如果您使用的是Android模拟器，则可以通过工具栏中的\"...\"按钮访问Extended Controls面板中的电池电量。\n"
  },
  {
    "path": "src/v2/chapter12/develop_package.md",
    "content": "# 12.1 开发Package\n\n第二章中已经讲过如何使用Package（包），我们知道通过package可以创建共享的模块化代码，本节将重点讲一下如何开发并发布我们自己的Package。一个最小的Package包括：\n\n- 一个`pubspec.yaml`文件：声明了Package的名称、版本、作者等的元数据文件。\n- 一个 `lib` 文件夹：包括包中公开的(public)代码，最少应有一个`<package-name>.dart`文件\n\nFlutter Packages分为两类：\n\n- Dart包：其中一些可能包含Flutter的特定功能，因此对Flutter框架具有依赖性，这种包仅用于Flutter，例如[`fluro`](https://pub.dartlang.org/packages/fluro)包。\n- 插件包：一种专用的Dart包，其中包含用Dart代码编写的API，以及针对Android（使用Java或Kotlin）和针对iOS（使用OC或Swift）平台的特定实现，也就是说插件包括原生代码，一个具体的例子是[`battery`](https://pub.dartlang.org/packages/battery)插件包。\n\n注意，虽然Flutter的Dart运行时和Dart VM运行时不是完全相同，但是如果Package中没有涉及这些存在差异的部分，那么这样的包可以同时支持Flutter和Dart VM，如Dart http网络库[dio](https://github.com/flutterchina/dio)。\n\n下面我将带领读者一步步来开发一个Dart Package。\n\n### 第一步：创建Dart包\n\n您可以通过Android Studio：File>New>New Flutter Project 来创建一个Package工程，如图12-1所示：\n\n![图12-1](../imgs/12-1.png)\n\n您也可以通过使用`--template=package` 来执行 `flutter create` 命令来创建：\n\n```shell\nflutter create --template=package hello\n```\n\n这将在`hello/`文件夹下创建一个具有以下专用内容的package工程：\n\n- `lib/hello.dart`：Package的Dart代码\n\n- `test/hello_test.dart`：Package的单元测试代码。\n\n### 实现package\n\n对于纯Dart包，只需在主`lib/<package name>.dart`文件内或`lib`目录中的文件中添加功能即可 。要测试软件包，请在`test`目录中添加[unit tests](https://flutter.io/testing/#unit-testing)。下面我们看看如何组织Package包的代码，我们以shelf Package为例，它的目录结构如图12-2所示：\n\n![图12-2](../imgs/12-2.png)\n\n在lib根目录下的“shelf.dart”中，导出了多个“lib/src”目录下的dart文件：\n\n```dart\nexport 'src/cascade.dart';\nexport 'src/handler.dart';\nexport 'src/handlers/logger.dart';\nexport 'src/hijack_exception.dart';\nexport 'src/middleware.dart';\nexport 'src/pipeline.dart';\nexport 'src/request.dart';\nexport 'src/response.dart';\nexport 'src/server.dart';\nexport 'src/server_handler.dart';\n```\n\n而Package中主要的功能的源码都在src目录下。shelf Package也导出了一个迷你库: shelf_io，它主要是处理HttpRequest的。\n\n### **导入包**\n\n当需要使用这个Package时，我们可以通过\"package:\"指令来指定包的入口文件：\n\n```dart\nimport 'package:utilities/utilities.dart';\n```\n\n同一个包中的源码文件之间也可以使用相对路径来导入。\n\n### 生成文档\n\n可以使用 [dartdoc](https://github.com/dart-lang/dartdoc#dartdoc) 工具来为Package生成文档，开发者需要做的就是遵守文档注释语法在代码中添加文档注释，最后使用dartdoc可以直接生成API文档（一个静态网站）。文档注释是使用三斜线\"///\"开始，如：\n\n```dart\n/// The event handler responsible for updating the badge in the UI.\nvoid updateBadge() {\n  ...\n}\n```\n\n详细的文档语法请参考[dartdoc](https://github.com/dart-lang/dartdoc#dartdoc) 。\n\n### 处理包的相互依赖\n\n如果我们正在开发一个`hello`包，它依赖于另一个包，则需要将该依赖包添加到`pubspec.yaml`文件的`dependencies`部分。 下面的代码使`url_launcher`插件的API在`hello`包中是可用的：\n\n在 `hello/pubspec.yaml`中:\n\n```yaml\ndependencies:\n  url_launcher: ^0.4.2\n```\n\n现在可以在`hello`中`import 'package:url_launcher/url_launcher.dart'` 然后调用 `launch()`方法了。\n\n这与在Flutter应用程序或任何其他Dart项目中引用软件包没有什么不同。\n\n但是，如果`hello`碰巧是一个插件包，其平台特定的代码需要访问`url_launcher`公开的特定于平台的API，那么我们还需要为特定于平台的构建文件添加合适的依赖声明，如下所示。\n\n**Android**\n\n在 `hello/android/build.gradle`:\n\n```groovy\nandroid {\n    // lines skipped\n    dependencies {\n        provided rootProject.findProject(\":url_launcher\")\n    }\n}\n```\n\n您现在可以在`hello/android/src`源码中`import io.flutter.plugins.urllauncher.UrlLauncherPlugin`访问`UrlLauncherPlugin`类。\n\n**iOS**\n\n在`hello/ios/hello.podspec`:\n\n```ruby\nPod::Spec.new do |s|\n  # lines skipped\n  s.dependency 'url_launcher'\n```\n\n您现在可以在`hello/ios/Classes`源码中 `#import \"UrlLauncherPlugin.h\"` 然后访问 `UrlLauncherPlugin`类。\n\n### 解决依赖冲突\n\n假设我们想在我们的`hello`包中使用`some_package`和`other_package`，并且这两个包都依赖`url_launcher`，但是依赖的是`url_launcher`的不同的版本。 那我们就有潜在的冲突。避免这种情况的最好方法是在指定依赖关系时，程序包作者使用[版本范围](https://www.dartlang.org/tools/pub/dependencies#version-constraints)而不是特定版本。\n\n```yaml\ndependencies:\n  url_launcher: ^0.4.2    # 这样会较好, 任何0.4.x(x >= 2)都可.\n  image_picker: '0.1.1'   # 不是很好，只有0.1.1版本.\n```\n\n如果`some_package`声明了上面的依赖关系,`other_package`声明了`url_launcher`版本像’0.4.5’或’^0.4.0’，pub将能够自动解决问题。 \n\n即使`some_package`和`other_package`声明了不兼容的`url_launcher`版本，它仍然可能会和`url_launcher`以兼容的方式正常工作。 你可以通过向`hello`包的`pubspec.yaml`文件中添加依赖性覆盖声明来处理冲突，从而强制使用特定版本：\n\n强制使用 `0.4.3`版本的`url_launcher`，在 `hello/pubspec.yaml`中:\n\n```yaml\ndependencies:\n  some_package:\n  other_package:\ndependency_overrides:\n  url_launcher: '0.4.3'\n```\n\n如果冲突的依赖不是一个包，而是一个特定于Android的库，比如`guava`，那么必须将依赖重写声明添加到Gradle构建逻辑中。\n\n强制使用`23.0`版本的`guava`库，在`hello/android/build.gradle`中：\n\n```groovy\nconfigurations.all {\n    resolutionStrategy {\n        force 'com.google.guava:guava:23.0-android'\n    }\n}\n```\n\nCocoapods目前不提供依赖覆盖功能。\n\n### 发布Package\n\n一旦实现了一个包，我们可以在[Pub](https://pub.dartlang.org/)上发布它 ，这样其他开发者就可以轻松使用它。\n\n在发布之前，检查`pubspec.yaml`、`README.md`以及`CHANGELOG.md`文件，以确保其内容的完整性和正确性。然后，运行 dry-run 命令以查看是否都准备OK了:\n\n```shell\nflutter packages pub publish --dry-run\n```\n\n验证无误后，我们就可以运行发布命令了：\n\n```shell\nflutter packages pub publish\n```\n\n> 如果你遇到包发布失败的情况，先检查是否因为众所周知的网络原因，如果是网络问题，可以使用VPN，这里需要注意的是一些代理只会代理部分APP的网络请求，如浏览器的，它们可能并不能代理dart的网络请求，所以在这种情况下，即使开了代理也依然无法连接到Pub，因此，在发布Pub包时使用全局代理或全局VPN会保险些。如果网络没有问题，以管理员权限(sudo)运行发布命令重试。  \n> 很多时候开启全局代理也不会让terminal中的流量打代理服务器走，以socks5为例，应该在终端下输入以下指令：\n```shell\nexport all_proxy=socks5://127.0.0.1:1080\n```\n> 此时终端中的http和https流量会打代理服务器走，可以通过<code>curl -i https://ip.cn</code>指令查看代理设置是否成功。\n\n"
  },
  {
    "path": "src/v2/chapter12/develop_plugin.md",
    "content": "# 12.3 开发Flutter插件\n\n下面我们通过一个获取电池电量的插件来介绍一下Flutter插件的开发流程。该插件中我们在Dart中通过`getBatteryLevel` 调用Android `BatteryManager` API和iOS `device.batteryLevel` API。 \n\n### 创建一个新的应用程序项目\n\n首先创建一个新的应用程序:\n\n- 在终端中运行：`flutter create batterylevel`\n\n默认情况下，模板支持使用Java编写Android代码，或使用Objective-C编写iOS代码。要使用Kotlin或Swift，请使用-i和/或-a标志:\n\n- 在终端中运行: `flutter create -i swift -a kotlin batterylevel`\n\n### 创建Flutter平台客户端\n\n该应用的`State`类拥有当前的应用状态。我们需要延长这一点以保持当前的电量\n\n首先，我们构建通道。我们使用`MethodChannel`调用一个方法来返回电池电量。\n\n通道的客户端和宿主通过通道构造函数中传递的通道名称进行连接。单个应用中使用的所有通道名称必须是唯一的; 我们建议在通道名称前加一个唯一的“域名前缀”，例如`samples.flutter.io/battery`。\n\n```dart\nimport 'dart:async';\n\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\n...\nclass _MyHomePageState extends State<MyHomePage> {\n  static const platform = const MethodChannel('samples.flutter.io/battery');\n\n  // Get battery level.\n}\n```\n\n接下来，我们调用通道上的方法，指定通过字符串标识符调用方法`getBatteryLevel`。 该调用可能失败(平台不支持平台API，例如在模拟器中运行时)，所以我们将invokeMethod调用包装在try-catch语句中。\n\n我们使用返回的结果，在`setState`中来更新用户界面状态`batteryLevel`。\n\n```dart\n  // Get battery level.\n  String _batteryLevel = 'Unknown battery level.';\n\n  Future<Null> _getBatteryLevel() async {\n    String batteryLevel;\n    try {\n      final int result = await platform.invokeMethod('getBatteryLevel');\n      batteryLevel = 'Battery level at $result % .';\n    } on PlatformException catch (e) {\n      batteryLevel = \"Failed to get battery level: '${e.message}'.\";\n    }\n\n    setState(() {\n      _batteryLevel = batteryLevel;\n    });\n  }\n```\n\n最后，我们在build创建包含一个小字体显示电池状态和一个用于刷新值的按钮的用户界面。\n\n```dart\n@override\nWidget build(BuildContext context) {\n  return new Material(\n    child: new Center(\n      child: new Column(\n        mainAxisAlignment: MainAxisAlignment.spaceEvenly,\n        children: [\n          new RaisedButton(\n            child: new Text('Get Battery Level'),\n            onPressed: _getBatteryLevel,\n          ),\n          new Text(_batteryLevel),\n        ],\n      ),\n    ),\n  );\n}\n```\n\n至此Flutter部分的测试代码写好了，接下来我们需要实现Android和iOS平台下的API，由于平台API实现部分篇幅较大，我们将在接下来的两节中，分别介绍Android和iOS端API的实现。\n"
  },
  {
    "path": "src/v2/chapter12/index.md",
    "content": "\n# 包与插件\n\n* [12.1：开发package](develop_package.md)\n* [12.2：平台通道简介](platform-channel.md)\n* [12.3：开发Flutter插件](develop_plugin.md)\n* [12.4：插件开发：实现Android端API](android_implement.md)\n* [12.5：插件开发：实现IOS端API](ios_implement.md)\n* [12.6：Texture和PlatformView](texture_platformview.md) \n"
  },
  {
    "path": "src/v2/chapter12/ios_implement.md",
    "content": "# 12.5 插件开发：iOS端API实现\n\n本节我们接着之前\"获取电池电量\"插件的示例，来完成iOS端API的实现。以下步骤使用Objective-C，如果您更喜欢Swift，可以直接跳到后面Swift部分。\n\n首先打开Xcode中Flutter应用程序的iOS部分:\n\n1. 启动 Xcode\n2. 选择 File > Open…\n3. 定位到您 Flutter app目录, 然后选择里面的 `iOS`文件夹，点击 OK\n4. 确保Xcode项目的构建没有错误。\n5. 选择 Runner > Runner ，打开`AppDelegate.m`\n\n接下来，在`application didFinishLaunchingWithOptions:`方法内部创建一个`FlutterMethodChannel`，并添加一个处理方法。 确保与在Flutter客户端使用的通道名称相同。\n\n```objectivec\n#import <Flutter/Flutter.h>\n\n@implementation AppDelegate\n- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {\n  FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController;\n\n  FlutterMethodChannel* batteryChannel = [FlutterMethodChannel\n                                          methodChannelWithName:@\"samples.flutter.io/battery\"\n                                          binaryMessenger:controller];\n\n  [batteryChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {\n    // TODO\n  }];\n\n  return [super application:application didFinishLaunchingWithOptions:launchOptions];\n}\n```\n\n接下来，我们添加Objective-C代码，使用iOS电池API来获取电池电量，这和原生是相同的。\n\n在`AppDelegate`类中添加以下新的方法：\n\n```objectivec\n- (int)getBatteryLevel {\n  UIDevice* device = UIDevice.currentDevice;\n  device.batteryMonitoringEnabled = YES;\n  if (device.batteryState == UIDeviceBatteryStateUnknown) {\n    return -1;\n  } else {\n    return (int)(device.batteryLevel * 100);\n  }\n}\n```\n\n最后，我们完成之前添加的`setMethodCallHandler`方法。我们需要处理的平台方法名为`getBatteryLevel`，所以我们在call参数中需要先判断是否为`getBatteryLevel`。 这个平台方法的实现只需调用我们在前一步中编写的iOS代码，并使用result参数返回成功或错误的响应。如果调用了未定义的API，我们也会通知返回：\n\n```objectivec\n[batteryChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {\n  if ([@\"getBatteryLevel\" isEqualToString:call.method]) {\n    int batteryLevel = [self getBatteryLevel];\n\n    if (batteryLevel == -1) {\n      result([FlutterError errorWithCode:@\"UNAVAILABLE\"\n                                 message:@\"电池信息不可用\"\n                                 details:nil]);\n    } else {\n      result(@(batteryLevel));\n    }\n  } else {\n    result(FlutterMethodNotImplemented);\n  }\n}];\n```\n\n现在可以在iOS上运行该应用程序了，如果使用的是iOS模拟器，请注意，它不支持电池API，因此应用程序将显示“电池信息不可用”。\n\n### 使用Swift实现iOS API\n\n以下步骤与上面使用Objective-C相似，首先打开Xcode中Flutter应用程序的iOS部分:\n\n1. 启动 Xcode\n2. 选择 File > Open…\n3. 定位到您 Flutter app目录, 然后选择里面的 `ios`文件夹，点击 OK\n4. 确保Xcode项目的构建没有错误。\n5. 选择 Runner > Runner ，然后打开`AppDelegate.swift`\n\n接下来，覆盖application方法并创建一个`FlutterMethodChannel`绑定通道名称`samples.flutter.io/battery`：\n\n```swift\n@UIApplicationMain\n@objc class AppDelegate: FlutterAppDelegate {\n  override func application(\n    _ application: UIApplication,\n    didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {\n    GeneratedPluginRegistrant.register(with: self);\n\n    let controller : FlutterViewController = window?.rootViewController as! FlutterViewController;\n    let batteryChannel = FlutterMethodChannel.init(name: \"samples.flutter.io/battery\",\n                                                   binaryMessenger: controller);\n    batteryChannel.setMethodCallHandler({\n      (call: FlutterMethodCall, result: FlutterResult) -> Void in\n      // Handle battery messages.\n    });\n\n    return super.application(application, didFinishLaunchingWithOptions: launchOptions);\n  }\n}\n```\n\n接下来，我们添加Swift代码，使用iOS电池API来获取电池电量，这和原生开发是相同的。\n\n将以下新方法添加到`AppDelegate.swift`底部:\n\n```swift\nprivate func receiveBatteryLevel(result: FlutterResult) {\n  let device = UIDevice.current;\n  device.isBatteryMonitoringEnabled = true;\n  if (device.batteryState == UIDeviceBatteryState.unknown) {\n    result(FlutterError.init(code: \"UNAVAILABLE\",\n                             message: \"电池信息不可用\",\n                             details: nil));\n  } else {\n    result(Int(device.batteryLevel * 100));\n  }\n}\n```\n\n最后，我们完成之前添加的`setMethodCallHandler`方法。我们需要处理的平台方法名为`getBatteryLevel`，所以我们在call参数中需要先判断是否为`getBatteryLevel`。 这个平台方法的实现只需调用我们在前一步中编写的iOS代码，并使用result参数返回成功或错误的响应。如果调用了未定义的API，我们也会通知返回：\n\n```swift\nbatteryChannel.setMethodCallHandler({\n  (call: FlutterMethodCall, result: FlutterResult) -> Void in\n  if (\"getBatteryLevel\" == call.method) {\n    receiveBatteryLevel(result: result);\n  } else {\n    result(FlutterMethodNotImplemented);\n  }\n});\n```\n\n现在可以在iOS上运行应用程序，如果使用的是iOS模拟器，请注意，它不支持电池API，因此应用程序将显示“电池信息不可用”。\n"
  },
  {
    "path": "src/v2/chapter12/platform-channel.md",
    "content": "# 12.2 插件开发：平台通道简介\n\n“平台特定”或“特定平台”中的平台指的就是Flutter应用程序运行的平台，如Android或IOS。我们知道一个完整的Flutter应用程序实际上包括原生代码和Flutter代码两部分。由于Flutter本身只是一个UI系统，它本身是无法提供一些系统能力，比如使用蓝牙、相机、GPS等，因此要在Flutter APP中调用这些能力就必须和原生平台进行通信。为此，Flutter中提供了一个平台通道（platform channel），用于Flutter和原生平台的通信。平台通道正是Flutter和原生之间通信的桥梁，它也是Flutter插件的底层基础设施。\n\nFlutter使用了一个灵活的系统，允许您调用特定平台的API，无论在Android上的Java或Kotlin代码中，还是iOS上的ObjectiveC或Swift代码中均可用。\n\nFlutter与原生之间的通信依赖灵活的消息传递方式：\n\n- 应用的Flutter部分通过平台通道（platform channel）将消息发送到其应用程序的所在的宿主（iOS或Android）应用（原生应用）。\n- 宿主监听平台通道，并接收该消息。然后它会调用该平台的API，并将响应发送回客户端，即应用程序的Flutter部分。\n\n### 平台通道\n\n使用平台通道在Flutter(client)和原生(host)之间传递消息，如下图所示：\n\n![平台通道](../imgs/12-3.png)\n\n当在Flutter中调用原生方法时，调用信息通过平台通道传递到原生，原生收到调用信息后方可执行指定的操作，如需返回数据，则原生会将数据再通过平台通道传递给Flutter。值得注意的是消息传递是异步的，这确保了用户界面在消息传递时不会被挂起。\n\n在客户端，[MethodChannel  API](https://docs.flutter.io/flutter/services/MethodChannel-class.html) 可以发送与方法调用相对应的消息。 在宿主平台上，`MethodChannel` 在[Android API](https://docs.flutter.io/javadoc/io/flutter/plugin/common/MethodChannel.html) 和 [FlutterMethodChannel iOS API](https://docs.flutter.io/objcdoc/Classes/FlutterMethodChannel.html)可以接收方法调用并返回结果。这些类可以帮助我们用很少的代码就能开发平台插件。\n\n> **注意**: 如果需要，方法调用(消息传递)可以是反向的，即宿主作为客户端调用Dart中实现的API。 [`quick_actions`](https://pub.dartlang.org/packages/quick_actions)插件就是一个具体的例子。\n\n### 平台通道数据类型支持\n\n平台通道使用标准消息编/解码器对消息进行编解码，它可以高效的对消息进行二进制序列化与反序列化。由于Dart与原生平台之间数据类型有所差异，下面我们列出数据类型之间的映射关系。\n\n| Dart              | Android              | iOS                                            |\n| ----------------- | -------------------- | ---------------------------------------------- |\n| null              | null                 | nil (NSNull when nested)                       |\n| bool              | java.lang.Boolean    | NSNumber numberWithBool:                       |\n| int               | java.lang.Integer    | NSNumber numberWithInt:                        |\n| int, 如果不足32位 | java.lang.Long       | NSNumber numberWithLong:                       |\n| int, 如果不足64位 | java.math.BigInteger | FlutterStandardBigInteger                      |\n| double            | java.lang.Double     | NSNumber numberWithDouble:                     |\n| String            | java.lang.String     | NSString                                       |\n| Uint8List         | byte[]               | FlutterStandardTypedData typedDataWithBytes:   |\n| Int32List         | int[]                | FlutterStandardTypedData typedDataWithInt32:   |\n| Int64List         | long[]               | FlutterStandardTypedData typedDataWithInt64:   |\n| Float64List       | double[]             | FlutterStandardTypedData typedDataWithFloat64: |\n| List              | java.util.ArrayList  | NSArray                                        |\n| Map               | java.util.HashMap    | NSDictionary                                   |\n\n 当在发送和接收值时，这些值在消息中的序列化和反序列化会自动进行。\n\n### 自定义编解码器\n\n除了上面提到的`MethodChannel`，还可以使用[`BasicMessageChannel`](https://docs.flutter.io/flutter/services/BasicMessageChannel-class.html)，它支持使用自定义消息编解码器进行基本的异步消息传递。 此外，可以使用专门的[`BinaryCodec`](https://docs.flutter.io/flutter/services/BinaryCodec-class.html)、[`StringCodec`](https://docs.flutter.io/flutter/services/StringCodec-class.html)和 [`JSONMessageCodec`](https://docs.flutter.io/flutter/services/JSONMessageCodec-class.html)类，或创建自己的编解码器。\n\n### 如何获取平台信息\n\nFlutter 中提供了一个全局变量`defaultTargetPlatform`来获取当前应用的平台信息，`defaultTargetPlatform`定义在\"platform.dart\"中，它的类型是`TargetPlatform`，这是一个枚举类，定义如下：\n\n```dart\nenum TargetPlatform {\n  android,\n  fuchsia,\n  iOS,\n}\n```\n\n可以看到目前Flutter只支持这三个平台。我们可以通过如下代码判断平台：\n\n```dart\nif(defaultTargetPlatform==TargetPlatform.android){\n  // 是安卓系统，do something\n  ...\n}\n...\n```\n\n由于不同平台有它们各自的交互规范，Flutter Material库中的一些组件都针对相应的平台做了一些适配，比如路由组件`MaterialPageRoute`，它在android和ios中会应用各自平台规范的切换动画。那如果我们想让我们的APP在所有平台都表现一致，比如希望在所有平台路由切换动画都按照ios平台一致的左右滑动切换风格该怎么做？Flutter中提供了一种覆盖默认平台的机制，我们可以通过显式指定`debugDefaultTargetPlatformOverride`全局变量的值来指定应用平台。比如：\n\n```dart\ndebugDefaultTargetPlatformOverride=TargetPlatform.iOS;\nprint(defaultTargetPlatform); // 会输出TargetPlatform.iOS\n```\n\n上面代码即在Android中运行后，Flutter APP就会认为是当前系统是iOS，Material组件库中所有组件交互方式都会和iOS平台对齐，`defaultTargetPlatform`的值也会变为`TargetPlatform.iOS`。"
  },
  {
    "path": "src/v2/chapter12/texture_platformview.md",
    "content": "# 12.6 Texture和PlatformView\n\n本节主要介绍原生和Flutter之间如何共享图像，以及如何在Flutter中嵌套原生组件。\n\n## 12.6.1 Texture（示例：使用摄像头）\n\n前面说过Flutter本身只是一个UI系统，对于一些系统能力的调用我们可以通过消息传送机制与原生交互。但是这种消息传送机制并不能覆盖所有的应用场景，比如我们想调用摄像头来拍照或录视频，但在拍照和录视频的过程中我们需要将预览画面显示到我们的Flutter UI中，如果我们要用Flutter定义的消息通道机制来实现这个功能，就需要将摄像头采集的每一帧图片都要从原生传递到Flutter中，这样做代价将会非常大，因为将图像或视频数据通过消息通道实时传输必然会引起内存和CPU的巨大消耗！为此，Flutter提供了一种基于Texture的图片数据共享机制。\n\nTexture可以理解为GPU内保存将要绘制的图像数据的一个对象，Flutter engine会将Texture的数据在内存中直接进行映射（而无需在原生和Flutter之间再进行数据传递），Flutter会给每一个Texture分配一个id，同时Flutter中提供了一个`Texture`组件，`Texture`构造函数定义如下：\n\n```dart\nconst Texture({\n  Key key,\n  @required this.textureId,\n})\n```\n\n`Texture` 组件正是通过`textureId`与Texture数据关联起来；在`Texture`组件绘制时，Flutter会自动从内存中找到相应id的Texture数据，然后进行绘制。可以总结一下整个流程：图像数据先在原生部分缓存，然后在Flutter部分再通过`textureId`和缓存关联起来，最后绘制由Flutter完成。\n\n如果我们作为一个插件开发者，我们在原生代码中分配了`textureId`，那么在Flutter侧使用`Texture`组件时要如何获取`textureId`呢？这又回到了之前的内容了，`textureId`完全可以通过MethodChannel来传递。\n\n另外，值得注意的是，当原生摄像头捕获的图像发生变化时，`Texture` 组件会自动重绘，这不需要我们写任何Dart 代码去控制。\n\n### Texture用法\n\n如果我们要手动实现一个相机插件，和前面几节介绍的“获取剩余电量”插件的步骤一样，需要分别实现原生部分和Flutter部分。考虑到大多数读者可能并非同时既了解Android开发，又了解iOS开发，如果我们再花大量篇幅来介绍不同端的实现可能会没什么意义，另外，由于Flutter官方提供的相机（camera）插件和视频播放（video_player）插件都是使用Texture来实现的，它们本身就是Texture非常好的示例，所以在本书中将不会再介绍使用Texture的具体流程了，读者有兴趣查看camera和video_player的实现代码。下面我们重点介绍一下如何使用camera和video_player。\n\n### 相机示例\n\n下面我们看一下camera包自带的一个示例，它包含如下功能：\n\n1. 可以拍照，也可以拍视频，拍摄完成后可以保存；排号的视频可以播放预览。\n2. 可以切换摄像头（前置摄像头、后置摄像头、其它）\n3. 可以显示已经拍摄内容的预览图。\n\n下面我们看一下具体代码：\n\n1. 首先，依赖camera插件的最新版，并下载依赖。\n\n   ```yaml\n   dependencies:\n     ...  //省略无关代码\n     camera: ^0.5.2+2\n   ```\n\n2. 在`main`方法中获取可用摄像头列表。\n\n   ```dart\n   void main() async {\n     // 获取可用摄像头列表，cameras为全局变量\n     cameras = await availableCameras();\n     runApp(MyApp());\n   }\n   ```\n\n3. 构建UI。现在我们构建如图12-4的测试界面：\n\n   ![12-4](../imgs/12-4.jpg)\n   线面是完整的代码：\n   \n   ```dart\n   import 'package:camera/camera.dart';\n   import 'package:flutter/material.dart';\n   import '../common.dart';\n   import 'dart:async';\n   import 'dart:io';\n   import 'package:path_provider/path_provider.dart';\n   import 'package:video_player/video_player.dart'; //用于播放录制的视频\n   \n   /// 获取不同摄像头的图标（前置、后置、其它）\n   IconData getCameraLensIcon(CameraLensDirection direction) {\n     switch (direction) {\n       case CameraLensDirection.back:\n         return Icons.camera_rear;\n       case CameraLensDirection.front:\n         return Icons.camera_front;\n       case CameraLensDirection.external:\n         return Icons.camera;\n     }\n     throw ArgumentError('Unknown lens direction');\n   }\n   \n   void logError(String code, String message) =>\n       print('Error: $code\\nError Message: $message');\n   \n   // 示例页面路由\n   class CameraExampleHome extends StatefulWidget {\n     @override\n     _CameraExampleHomeState createState() {\n       return _CameraExampleHomeState();\n     }\n   }\n   \n   class _CameraExampleHomeState extends State<CameraExampleHome>\n       with WidgetsBindingObserver {\n     CameraController controller;\n     String imagePath; // 图片保存路径\n     String videoPath; //视频保存路径\n     VideoPlayerController videoController;\n     VoidCallback videoPlayerListener;\n     bool enableAudio = true;\n   \n     @override\n     void initState() {\n       super.initState();\n       // 监听APP状态改变，是否在前台\n       WidgetsBinding.instance.addObserver(this);\n     }\n   \n     @override\n     void dispose() {\n       WidgetsBinding.instance.removeObserver(this);\n       super.dispose();\n     }\n   \n     @override\n     void didChangeAppLifecycleState(AppLifecycleState state) {\n       // 如果APP不在在前台\n       if (state == AppLifecycleState.inactive) {\n         controller?.dispose();\n       } else if (state == AppLifecycleState.resumed) {\n         // 在前台\n         if (controller != null) {\n           onNewCameraSelected(controller.description);\n         }\n       }\n     }\n   \n     final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();\n   \n     @override\n     Widget build(BuildContext context) {\n       return Scaffold(\n         key: _scaffoldKey,\n         appBar: AppBar(\n           title: const Text('相机示例'),\n         ),\n         body: Column(\n           children: <Widget>[\n             Expanded(\n               child: Container(\n                 child: Padding(\n                   padding: const EdgeInsets.all(1.0),\n                   child: Center(\n                     child: _cameraPreviewWidget(),\n                   ),\n                 ),\n                 decoration: BoxDecoration(\n                   color: Colors.black,\n                   border: Border.all(\n                     color: controller != null && controller.value.isRecordingVideo\n                         ? Colors.redAccent\n                         : Colors.grey,\n                     width: 3.0,\n                   ),\n                 ),\n               ),\n             ),\n             _captureControlRowWidget(),\n             _toggleAudioWidget(),\n             Padding(\n               padding: const EdgeInsets.all(5.0),\n               child: Row(\n                 mainAxisAlignment: MainAxisAlignment.start,\n                 children: <Widget>[\n                   _cameraTogglesRowWidget(),\n                   _thumbnailWidget(),\n                 ],\n               ),\n             ),\n           ],\n         ),\n       );\n     }\n   \n     /// 展示预览窗口\n     Widget _cameraPreviewWidget() {\n       if (controller == null || !controller.value.isInitialized) {\n         return const Text(\n           '选择一个摄像头',\n           style: TextStyle(\n             color: Colors.white,\n             fontSize: 24.0,\n             fontWeight: FontWeight.w900,\n           ),\n         );\n       } else {\n         return AspectRatio(\n           aspectRatio: controller.value.aspectRatio,\n           child: CameraPreview(controller),\n         );\n       }\n     }\n   \n     /// 开启或关闭录音\n     Widget _toggleAudioWidget() {\n       return Padding(\n         padding: const EdgeInsets.only(left: 25),\n         child: Row(\n           children: <Widget>[\n             const Text('开启录音:'),\n             Switch(\n               value: enableAudio,\n               onChanged: (bool value) {\n                 enableAudio = value;\n                 if (controller != null) {\n                   onNewCameraSelected(controller.description);\n                 }\n               },\n             ),\n           ],\n         ),\n       );\n     }\n   \n     /// 显示已拍摄的图片/视频缩略图。\n     Widget _thumbnailWidget() {\n       return Expanded(\n         child: Align(\n           alignment: Alignment.centerRight,\n           child: Row(\n             mainAxisSize: MainAxisSize.min,\n             children: <Widget>[\n               videoController == null && imagePath == null\n                   ? Container()\n                   : SizedBox(\n                 child: (videoController == null)\n                     ? Image.file(File(imagePath))\n                     : Container(\n                   child: Center(\n                     child: AspectRatio(\n                         aspectRatio:\n                         videoController.value.size != null\n                             ? videoController.value.aspectRatio\n                             : 1.0,\n                         child: VideoPlayer(videoController)),\n                   ),\n                   decoration: BoxDecoration(\n                       border: Border.all(color: Colors.pink)),\n                 ),\n                 width: 64.0,\n                 height: 64.0,\n               ),\n             ],\n           ),\n         ),\n       );\n     }\n   \n     /// 相机工具栏\n     Widget _captureControlRowWidget() {\n       return Row(\n         mainAxisAlignment: MainAxisAlignment.spaceEvenly,\n         mainAxisSize: MainAxisSize.max,\n         children: <Widget>[\n           IconButton(\n             icon: const Icon(Icons.camera_alt),\n             color: Colors.blue,\n             onPressed: controller != null &&\n                 controller.value.isInitialized &&\n                 !controller.value.isRecordingVideo\n                 ? onTakePictureButtonPressed\n                 : null,\n           ),\n           IconButton(\n             icon: const Icon(Icons.videocam),\n             color: Colors.blue,\n             onPressed: controller != null &&\n                 controller.value.isInitialized &&\n                 !controller.value.isRecordingVideo\n                 ? onVideoRecordButtonPressed\n                 : null,\n           ),\n           IconButton(\n             icon: const Icon(Icons.stop),\n             color: Colors.red,\n             onPressed: controller != null &&\n                 controller.value.isInitialized &&\n                 controller.value.isRecordingVideo\n                 ? onStopButtonPressed\n                 : null,\n           )\n         ],\n       );\n     }\n   \n     /// 展示所有摄像头\n     Widget _cameraTogglesRowWidget() {\n       final List<Widget> toggles = <Widget>[];\n   \n       if (cameras.isEmpty) {\n         return const Text('没有检测到摄像头');\n       } else {\n         for (CameraDescription cameraDescription in cameras) {\n           toggles.add(\n             SizedBox(\n               width: 90.0,\n               child: RadioListTile<CameraDescription>(\n                 title: Icon(getCameraLensIcon(cameraDescription.lensDirection)),\n                 groupValue: controller?.description,\n                 value: cameraDescription,\n                 onChanged: controller != null && controller.value.isRecordingVideo\n                     ? null\n                     : onNewCameraSelected,\n               ),\n             ),\n           );\n         }\n       }\n   \n       return Row(children: toggles);\n     }\n   \n     String timestamp() => DateTime.now().millisecondsSinceEpoch.toString();\n   \n     void showInSnackBar(String message) {\n       _scaffoldKey.currentState.showSnackBar(SnackBar(content: Text(message)));\n     }\n   \n     // 摄像头选中回调\n     void onNewCameraSelected(CameraDescription cameraDescription) async {\n       if (controller != null) {\n         await controller.dispose();\n       }\n       controller = CameraController(\n         cameraDescription,\n         ResolutionPreset.high,\n         enableAudio: enableAudio,\n       );\n   \n       controller.addListener(() {\n         if (mounted) setState(() {});\n         if (controller.value.hasError) {\n           showInSnackBar('Camera error ${controller.value.errorDescription}');\n         }\n       });\n   \n       try {\n         await controller.initialize();\n       } on CameraException catch (e) {\n         _showCameraException(e);\n       }\n   \n       if (mounted) {\n         setState(() {});\n       }\n     }\n   \n     // 拍照按钮点击回调\n     void onTakePictureButtonPressed() {\n       takePicture().then((String filePath) {\n         if (mounted) {\n           setState(() {\n             imagePath = filePath;\n             videoController?.dispose();\n             videoController = null;\n           });\n           if (filePath != null) showInSnackBar('图片保存在 $filePath');\n         }\n       });\n     }\n   \n     // 开始录制视频\n     void onVideoRecordButtonPressed() {\n       startVideoRecording().then((String filePath) {\n         if (mounted) setState(() {});\n         if (filePath != null) showInSnackBar('正在保存视频于 $filePath');\n       });\n     }\n   \n     // 终止视频录制\n     void onStopButtonPressed() {\n       stopVideoRecording().then((_) {\n         if (mounted) setState(() {});\n         showInSnackBar('视频保存在: $videoPath');\n       });\n     }\n   \n     Future<String> startVideoRecording() async {\n       if (!controller.value.isInitialized) {\n         showInSnackBar('请先选择一个摄像头');\n         return null;\n       }\n   \n       // 确定视频保存的路径\n       final Directory extDir = await getApplicationDocumentsDirectory();\n       final String dirPath = '${extDir.path}/Movies/flutter_test';\n       await Directory(dirPath).create(recursive: true);\n       final String filePath = '$dirPath/${timestamp()}.mp4';\n   \n       if (controller.value.isRecordingVideo) {\n         // 如果正在录制，则直接返回\n         return null;\n       }\n   \n       try {\n         videoPath = filePath;\n         await controller.startVideoRecording(filePath);\n       } on CameraException catch (e) {\n         _showCameraException(e);\n         return null;\n       }\n       return filePath;\n     }\n   \n     Future<void> stopVideoRecording() async {\n       if (!controller.value.isRecordingVideo) {\n         return null;\n       }\n   \n       try {\n         await controller.stopVideoRecording();\n       } on CameraException catch (e) {\n         _showCameraException(e);\n         return null;\n       }\n   \n       await _startVideoPlayer();\n     }\n   \n     Future<void> _startVideoPlayer() async {\n       final VideoPlayerController vcontroller =\n       VideoPlayerController.file(File(videoPath));\n       videoPlayerListener = () {\n         if (videoController != null && videoController.value.size != null) {\n           // Refreshing the state to update video player with the correct ratio.\n           if (mounted) setState(() {});\n           videoController.removeListener(videoPlayerListener);\n         }\n       };\n       vcontroller.addListener(videoPlayerListener);\n       await vcontroller.setLooping(true);\n       await vcontroller.initialize();\n       await videoController?.dispose();\n       if (mounted) {\n         setState(() {\n           imagePath = null;\n           videoController = vcontroller;\n         });\n       }\n       await vcontroller.play();\n     }\n   \n     Future<String> takePicture() async {\n       if (!controller.value.isInitialized) {\n         showInSnackBar('错误: 请先选择一个相机');\n         return null;\n       }\n       final Directory extDir = await getApplicationDocumentsDirectory();\n       final String dirPath = '${extDir.path}/Pictures/flutter_test';\n       await Directory(dirPath).create(recursive: true);\n       final String filePath = '$dirPath/${timestamp()}.jpg';\n   \n       if (controller.value.isTakingPicture) {\n         // A capture is already pending, do nothing.\n         return null;\n       }\n   \n       try {\n         await controller.takePicture(filePath);\n       } on CameraException catch (e) {\n         _showCameraException(e);\n         return null;\n       }\n       return filePath;\n     }\n   \n     void _showCameraException(CameraException e) {\n       logError(e.code, e.description);\n       showInSnackBar('Error: ${e.code}\\n${e.description}');\n     }\n   }\n   ```\n\n> 如果代码运行遇到困难，请直接查看[camera官方文档](https://pub.dev/packages/camera)。\n\n## 12.6.2 PlatformView （示例：WebView）\n\n如果我们在开发过程中需要使用一个原生组件，但这个原生组件在Flutter中很难实现时怎么办（如webview）？这时一个简单的方法就是将需要使用原生组件的页面全部用原生实现，在flutter中需要打开该页面时通过消息通道打开这个原生的页面。但是这种方法有一个最大的缺点，就是原生组件很难和Flutter组件进行组合。\n\n在 Flutter 1.0版本中，Flutter SDK中新增了`AndroidView`和`UIKitView` 两个组件，这两个组件的主要功能就是将原生的Android组件和iOS组件嵌入到Flutter的组件树中，这个功能是非常重要的，尤其是对一些实现非常复杂的组件，比如webview，这些组件原生已经有了，如果Flutter中要用，重新实现的话成本将非常高，所以如果有一种机制能让Flutter共享原生组件，这将会非常有用，也正因如此，Flutter才提供了这两个组件。\n\n由于`AndroidView`和`UIKitView` 是和具体平台相关的，所以称它们为PlatformView。需要说明的是将来Flutter支持的平台可能会增多，则相应的PlatformView也将会变多。那么如何使用Platform View呢？我们以Flutter官方提供的[webview_flutter插件](https://github.com/flutter/plugins/tree/master/packages/webview_flutter)为例：\n\n> 注意，在本书写作之时，webview_flutter仍处于预览阶段，如您想在项目中使用它，请查看一下webview_flutter插件最新版本及动态。\n\n1. 原生代码中注册要被Flutter嵌入的组件工厂，如webview_flutter插件中Android端注册webview插件代码：\n\n   ```java\n   public static void registerWith(Registrar registrar) {\n      registrar.platformViewRegistry().registerViewFactory(\"webview\", \n      WebViewFactory(registrar.messenger()));\n   }\n   ```\n\n   `WebViewFactory`的具体实现请参考webview_flutter插件的实现源码，在此不再赘述。\n\n2. 在Flutter中使用；打开Flutter中文社区首页。\n\n   ```dart\n   class PlatformViewRoute extends StatelessWidget {\n     @override\n     Widget build(BuildContext context) {\n       return WebView(\n         initialUrl: \"https://flutterchina.club\",\n         javascriptMode: JavascriptMode.unrestricted,\n       );\n     }\n   }\n   ```\n\n   运行效果如图12-5所示：\n\n   ![12-5](../imgs/12-5.jpg)\n\n注意，使用PlatformView的开销是非常大的，因此，如果一个原生组件用Flutter实现的难度不大时，我们应该首选Flutter实现。\n\n另外，PlatformView的相关功能在作者写作时还处于预览阶段，可能还会发生变化，因此，读者如果需要在项目中使用的话，应查看一下最新的文档。"
  },
  {
    "path": "src/v2/chapter13/faq.md",
    "content": "# 13.4 国际化常见问题\n\n本节主要解答一下在国际化中常见的问题。\n\n### 默认语言区域不对\n\n在一些非大陆行货渠道买的一些Android和iOS设备，会出现默认的Locale不是中文简体的情况。这属于正常现象，但是为了防止设备获取的Locale与实际的地区不一致，所有的支持多语言的APP都必须提供一个手动选择语言的入口。\n\n### 如何对应用标题进行国际化\n\n`MaterialApp`有一个`title`属性，用于指定APP的标题。在Android系统中，APP的标题会出现在任务管理器中。所以也需要对`title`进行国际化。但是问题是很多国际化的配置都是在`MaterialApp`上设置的，我们无法在构建`MaterialApp`时通过`Localizations.of`来获取本地化资源，如：\n\n```dart\nMaterialApp(\n  title: DemoLocalizations.of(context).title, //不能正常工作！\n  localizationsDelegates: [\n    // 本地化的代理类\n    GlobalMaterialLocalizations.delegate,\n    GlobalWidgetsLocalizations.delegate,\n    DemoLocalizationsDelegate() // 设置Delegate\n  ],\n);\n```\n\n上面代码运行后，`DemoLocalizations.of(context).title` 是会报错的，原因是`Localizations.of`会从当前的context沿着widget树向顶部查找`DemoLocalizations`，但是我们在`MaterialApp`中设置完`DemoLocalizationsDelegate`后，实际上`DemoLocalizations`是在当前context的子树中的，所以`DemoLocalizations.of(context)`会返回null，报错。那么我们该如何处理这种情况呢？其实很简单，我们只需要设置一个`onGenerateTitle`回调即可：\n\n```dart\nMaterialApp(\n  onGenerateTitle: (context){\n    // 此时context在Localizations的子树中\n    return DemoLocalizations.of(context).title;\n  },\n  localizationsDelegates: [\n    DemoLocalizationsDelegate(),\n    ...\n  ],\n);\n```\n\n### 如何为英语系的国家指定同一个locale\n\n英语系的国家非常多，如美国、英国、澳大利亚等，这些英语系国家虽然说的都是英语，但也会有一些区别。如果我们的APP只想提供一种英语（如美国英语）供所有英语系国家使用，我们可以在前面介绍的`localeListResolutionCallback`中来做兼容：\n\n```dart\nlocaleListResolutionCallback:\n    (List<Locale> locales, Iterable<Locale> supportedLocales) {\n  // 判断当前locale是否为英语系国家，如果是直接返回Locale('en', 'US')     \n}\n```\n\n"
  },
  {
    "path": "src/v2/chapter13/index.md",
    "content": "\n## 本章目录\n\n* [13.1：让App支持多语言](multi_languages_support.md)\n* [13.2：实现Localizations](locallization_implement.md) \n* [13.3：使用Intl包](intl.md) \n* [13.4：国际化常见问题](faq.md) \n"
  },
  {
    "path": "src/v2/chapter13/intl.md",
    "content": "# 使用Intl包\n\n使用[Intl](https://pub.dartlang.org/packages/intl)包我们不仅可以非常轻松的实现国际化，而且也可以将字符串文本分离成单独的文件，方便开发人员和翻译人员分工协作。为了使用[Intl](https://pub.dartlang.org/packages/intl)包我们需要添加两个依赖：\n\n```yaml\ndependencies:\n  #...省略无关项\n  intl: ^0.15.7 \ndev_dependencies:\n   #...省略无关项\n  intl_translation: ^0.17.2  \n```\n\n[intl_translation](https://pub.dartlang.org/packages/intl_translation) 包主要包含了一些工具，它在开发阶段主要主要的作用是从代码中提取要国际化的字符串到单独的arb文件和根据arb文件生成对应语言的dart代码，而intl包主要是引用和加载intl_translation生成后的dart代码。下面我们将一步步来说明如何使用：\n\n### 第一步：创建必要目录\n\n首先，在项目根目录下创建一个l10n-arb目录，该目录保存我们接下来通过intl_translation命令生成的arb文件。一个简单的arb文件内容如下：\n\n```json\n{\n  \"@@last_modified\": \"2018-12-10T15:46:20.897228\",\n  \"@@locale\":\"zh_CH\",\n  \"title\": \"Flutter应用\",\n  \"@title\": {\n    \"description\": \"Title for the Demo application\",\n    \"type\": \"text\",\n    \"placeholders\": {}\n  }\n}\n```\n\n我们根据\"@@locale\"字段可以看出这个arb对应的是中文简体的翻译，里面的`title`字段对应的正是我们应用标题的中文简体翻译。`@title`字段是对`title`的一些描述信息。\n\n接下来，我们在lib目录下创建一个l10n的目录，该目录用于保存从arb文件生成的dart代码文件。\n\n### 第二步：实现Localizations和Delegate类\n\n和上一节中的步骤类似，我们仍然要实现`Localizations`和Delegate类，不同的是，现在我们在实现时要使用intl包的一些方法（有些是动态生成的）。\n\n下面我们在`lib/l10n`目录下新建一个“localization_intl.dart”的文件，文件内容如下：\n\n```dart\nimport 'package:flutter/material.dart';\nimport 'package:intl/intl.dart';\nimport 'messages_all.dart'; //1\n\nclass DemoLocalizations {\n  static Future<DemoLocalizations> load(Locale locale) {\n    final String name = locale.countryCode.isEmpty ? locale.languageCode : locale.toString();\n    final String localeName = Intl.canonicalizedLocale(name);\n    //2\n    return initializeMessages(localeName).then((b) {\n      Intl.defaultLocale = localeName;\n      return new DemoLocalizations();\n    });\n  }\n\n  static DemoLocalizations of(BuildContext context) {\n    return Localizations.of<DemoLocalizations>(context, DemoLocalizations);\n  }\n\n  String get title {\n    return Intl.message(\n      'Flutter APP',\n      name: 'title',\n      desc: 'Title for the Demo application',\n    );\n  }\n}\n\n//Locale代理类\nclass DemoLocalizationsDelegate extends LocalizationsDelegate<DemoLocalizations> {\n  const DemoLocalizationsDelegate();\n\n  //是否支持某个Local\n  @override\n  bool isSupported(Locale locale) => ['en', 'zh'].contains(locale.languageCode);\n\n  // Flutter会调用此类加载相应的Locale资源类\n  @override\n  Future<DemoLocalizations> load(Locale locale) {\n    //3\n    return  DemoLocalizations.load(locale);\n  }\n\n  // 当Localizations Widget重新build时，是否调用load重新加载Locale资源.\n  @override\n  bool shouldReload(DemoLocalizationsDelegate old) => false;\n}\n```\n\n注意：\n\n- 注释1的\"messages_all.dart\"文件是通过[intl_translation](https://pub.dartlang.org/packages/intl_translation)工具从arb文件生成的代码，所以在第一次运行生成命令之前，此文件不存在。注释2处的`initializeMessages()`方法和\"messages_all.dart\"文件一样，是同时生成的。\n- 注释3处和上一节示例代码不同，这里我们直接调用`DemoLocalizations.load()`即可。\n\n### 第三步：添加需要国际化的属性\n\n现在我们可以在DemoLocalizations类中添加需要国际化的属性或方法，如上面示例代码中的`title`属性，这时我们就要用到Intl库提供的一些方法，这些方法可以帮我们轻松实现不同语言的一些语法特性，如复数语境，举个例子，比如我们有一个电子邮件列表页，我们需要在顶部显示未读邮件的数量，在未读数量不同事，我们展示的文本可能会不同：\n\n| 未读邮件数 | 提示语                   |\n| ---------- | ------------------------ |\n| 0          | There are no emails left |\n| 1          | There is 1 email left    |\n| n(n>1)     | There are n emails left  |\n\n我们可以通过`Intl.plural(...)`来实现：\n\n```dart\nremainingEmailsMessage(int howMany) => Intl.plural(howMany,\n    zero: 'There are no emails left',\n    one: 'There is $howMany email left',\n    other: 'There are $howMany emails left',\n    name: \"remainingEmailsMessage\",\n    args: [howMany],\n    desc: \"How many emails remain after archiving.\",\n    examples: const {'howMany': 42, 'userName': 'Fred'});\n```\n\n可以看到通过`Intl.plural`方法可以在`howMany`值不同时输出不同的提示信息。\n\n[Intl](https://pub.dartlang.org/packages/intl)包还有一些其他的方法，读者可以自行查看其文档，本书不在赘述。\n\n### 第四步：生成arb文件\n\n现在我们可以通[intl_translation](https://pub.dartlang.org/packages/intl_translation)包的工具来提取代码中的字符串到一个arb文件，运行如下命名：\n\n```shell\nflutter pub pub run intl_translation:extract_to_arb --output-dir=l10n-arb \\ lib/l10n/localization_intl.dart\n```\n\n运行此命令后，会将我们之前通过Intl API标识的属性和字符串提取到“l10n-arb/intl_messages.arb”文件中，我们看看其内容：\n\n```json\n{\n  \"@@last_modified\": \"2018-12-10T17:37:28.505088\",\n  \"title\": \"Flutter APP\",\n  \"@title\": {\n    \"description\": \"Title for the Demo application\",\n    \"type\": \"text\",\n    \"placeholders\": {}\n  },\n  \"remainingEmailsMessage\": \"{howMany,plural, =0{There are no emails left}=1{There is {howMany} email left}other{There are {howMany} emails left}}\",\n  \"@remainingEmailsMessage\": {\n    \"description\": \"How many emails remain after archiving.\",\n    \"type\": \"text\",\n    \"placeholders\": {\n      \"howMany\": {\n        \"example\": 42\n      }\n    }\n  }\n}\n```\n\n这个是默认的Locale资源文件，如果我们现在要支持中文简体，只需要在该文件同级目录创建一个\"intl_zh_CN.arb\"文件，然后将\"intl_messages.arb\"的内容拷贝到\"intl_zh_CN.arb\"文件，接下来将英文翻译为中文即可，翻译后的\"intl_zh_CN.arb\"文件内容如下：\n\n```json\n{\n  \"@@last_modified\": \"2018-12-10T15:46:20.897228\",\n  \"@@locale\":\"zh_CN\",\n  \"title\": \"Flutter应用\",\n  \"@title\": {\n    \"description\": \"Title for the Demo application\",\n    \"type\": \"text\",\n    \"placeholders\": {}\n  },\n  \"remainingEmailsMessage\": \"{howMany,plural, =0{没有未读邮件}=1{有{howMany}封未读邮件}other{有{howMany}封未读邮件}}\",\n  \"@remainingEmailsMessage\": {\n    \"description\": \"How many emails remain after archiving.\",\n    \"type\": \"text\",\n    \"placeholders\": {\n      \"howMany\": {\n        \"example\": 42\n      }\n    }\n  }\n}\n```\n\n我们必须要翻译`title`和`remainingEmailsMessage`字段，`description`是该字段的说明，通常给翻译人员看，代码中不会用到。\n\n有两点需要说明：\n\n1. 如果某个特定的arb中缺失某个属性，那么应用将会加载默认的arb文件(intl_messages.arb)中的相应属性，这是Intl的托底策略。\n2. 每次运行提取命令时，intl_messages.arb都会根据代码重新生成，但其他arb文件不会，所以当要添加新的字段或方法时，其他arb文件是增量的，不用担心会覆盖。\n3. arb文件是标准的，其格式规范可以自行了解。通常会将arb文件交给翻译人员，当他们完成翻译后，我们再通过下面的步骤根据arb文件生成最终的dart代码。\n\n### 第五步：生成dart代码\n\n最后一步就是根据arb生成dart文件：\n\n```shell\nflutter pub pub run intl_translation:generate_from_arb --output-dir=lib/l10n --no-use-deferred-loading lib/l10n/localization_intl.dart l10n-arb/intl_*.arb\n```\n\n这句命令在首次运行时会在\"lib/l10n\"目录下生成多个文件，对应多种Locale，这些代码便是最终要使用的dart代码。\n\n### 总结\n\n至此，我们将使用[Intl](https://pub.dartlang.org/packages/intl)包对APP进行国际化的流程介绍完了，我们可以发现，其中第一步和第二步只在第一次需要，而我们开发时的主要的工作都是在第三步。由于最后两步在第三步完成后每次也都需要，所以我们可以将最后两步放在一个shell脚本里，当我们完成第三步或完成arb文件翻译后只需要分别执行该脚本即可。我们在根目录下创建一个intl.sh的脚本，内容为：\n\n```shell\nflutter pub pub run intl_translation:extract_to_arb --output-dir=l10n-arb lib/l10n/localization_intl.dart\nflutter pub pub run intl_translation:generate_from_arb --output-dir=lib/l10n --no-use-deferred-loading lib/l10n/localization_intl.dart l10n-arb/intl_*.arb\n```\n\n然后授予执行权限：\n\n```shell\nchmod +x intl.sh\n```\n\n执行intl.sh\n\n```shell\n./intl.sh\n```\n\n"
  },
  {
    "path": "src/v2/chapter13/locallization_implement.md",
    "content": "# 13.2 实现Localizations\n\n前面讲了Material组件库如何支持国际化，本节我们将介绍一下我们自己的UI中如何支持多语言。根据上节所述，我们需要实现两个类：一个`Delegate`类一个`Localizations`类，下面我们通过一个实例说明。\n\n### 实现Localizations类\n\n我们已经知道`Localizations`类中主要实现提供了本地化值，如文本：\n\n```dart\n//Locale资源类\nclass DemoLocalizations {\n  DemoLocalizations(this.isZh);\n  //是否为中文\n  bool isZh = false;\n  //为了使用方便，我们定义一个静态方法\n  static DemoLocalizations of(BuildContext context) {\n    return Localizations.of<DemoLocalizations>(context, DemoLocalizations);\n  }\n  //Locale相关值，title为应用标题\n  String get title {\n    return isZh ? \"Flutter应用\" : \"Flutter APP\";\n  }\n  //... 其它的值  \n}\n```\n\n`DemoLocalizations`中会根据当前的语言来返回不同的文本，如`title`，我们可以将所有需要支持多语言的文本都在此类中定义。`DemoLocalizations`的实例将会在Delegate类的`load`方法中创建。\n\n### 实现Delegate类\n\nDelegate类的职责是在Locale改变时加载新的Locale资源，所以它有一个`load`方法，Delegate类需要继承自`LocalizationsDelegate`类，实现相应的接口，示例如下：\n\n```dart\n//Locale代理类\nclass DemoLocalizationsDelegate extends LocalizationsDelegate<DemoLocalizations> {\n  const DemoLocalizationsDelegate();\n\n  //是否支持某个Local\n  @override\n  bool isSupported(Locale locale) => ['en', 'zh'].contains(locale.languageCode);\n\n  // Flutter会调用此类加载相应的Locale资源类\n  @override\n  Future<DemoLocalizations> load(Locale locale) {\n    print(\"$locale\");\n    return SynchronousFuture<DemoLocalizations>(\n        DemoLocalizations(locale.languageCode == \"zh\")\n    );\n  }\n\n  @override\n  bool shouldReload(DemoLocalizationsDelegate old) => false;\n}\n```\n\n`shouldReload`的返回值决定当Localizations组件重新build时，是否调用`load`方法重新加载Locale资源。一般情况下，Locale资源只应该在Locale切换时加载一次，不需要每次在`Localizations`重新build时都加载，所以返回`false`即可。可能有些人会担心返回`false`的话在APP启动后用户再改变系统语言时`load`方法将不会被调用，所以Locale资源将不会被加载。事实上，每当Locale改变时Flutter都会再调用`load`方法加载新的Locale，无论`shouldReload`返回`true`还是`false`。\n\n### 最后一步：添加多语言支持\n\n和上一节中介绍的相同，我们现在需要先注册`DemoLocalizationsDelegate`类，然后再通过`DemoLocalizations.of(context)`来动态获取当前Locale文本。\n\n只需要在MaterialApp或WidgetsApp的`localizationsDelegates`列表中添加我们的Delegate实例即可完成注册：\n\n```dart\nlocalizationsDelegates: [\n // 本地化的代理类\n GlobalMaterialLocalizations.delegate,\n GlobalWidgetsLocalizations.delegate,\n // 注册我们的Delegate\n DemoLocalizationsDelegate()\n],\n```\n\n接下来我们可以在Widget中使用Locale值：\n\n```dart\nreturn Scaffold(\n  appBar: AppBar(\n    //使用Locale title  \n    title: Text(DemoLocalizations.of(context).title),\n  ),\n  ... //省略无关代码\n ） \n```\n\n这样，当在美国英语和中文简体之间切换系统语言时，APP的标题将会分别为“Flutter APP”和“Flutter应用”。\n\n### 总结\n\n本节我们通过一个简单的示例说明了Flutter应用国际化的基本过程及原理。但是上面的实例还有一个严重的不足就是我们需要在DemoLocalizations类中获取`title`时手动的判断当前语言Locale，然后返回合适的文本。试想一下，当我们要支持的语言不是两种而是8种甚至20几种时，如果为每个文本属性都要分别去判断到底是哪种Locale从而获取相应语言的文本将会是一件非常复杂的事。还有，通常情况下翻译人员并不是开发人员，能不能像i18n或l10n标准那样可以将翻译单独保存为一个arb文件交由翻译人员去翻译，翻译好之后开发人员再通过工具将arb文件转为代码。答案是肯定的！我们将在下一节介绍如何通过Dart intl包来实现这些。\n"
  },
  {
    "path": "src/v2/chapter13/multi_languages_support.md",
    "content": "# 13.1 让App支持多语言\n\n如果我们的应用要支持多种语言，那么我们需要“国际化”它。这意味着我们在开发时需要为应用程序支持的每种语言环境设置“本地化”的一些值，如文本和布局。Flutter SDK已经提供了一些组件和类来帮助我们实现国际化，下面我们来介绍一下Flutter中实现国际化的步骤。\n\n接下来我们以`MaterialApp`类为入口的应用来说明如何支持国际化。\n\n> 大多数应用程序都是通过`MaterialApp`为入口，但根据低级别的`WidgetsApp`类为入口编写的应用程序也可以使用相同的类和逻辑进行国际化。`MaterialApp`实际上也是`WidgetsApp`的一个包装。\n\n注意，”本地化的值和资源“是指我们针对不同语言准备的不同资源，这些资源一般是指文案（字符串），当然也会有一些其他的资源会根据不同语言地区而不同，比如我们需要显示一个APP上架地的国旗图片，那么不同Locale区域我们就需要提供不同的的国旗图片。\n\n### 支持国际化\n\n默认情况下，Flutter SDK中的组件仅提供美国英语本地化资源（主要是文本）。要添加对其他语言的支持，应用程序须添加一个名为“flutter_localizations”的包依赖，然后还需要在`MaterialApp`中进行一些配置。 要使用`flutter_localizations`包，首先需要添加依赖到`pubspec.yaml`文件中：\n\n```yaml\ndependencies:\n  flutter:\n    sdk: flutter\n  flutter_localizations:\n    sdk: flutter\n```\n\n接下来，下载`flutter_localizations`库，然后指定`MaterialApp`的`localizationsDelegates`和`supportedLocales`：\n\n```dart\nimport 'package:flutter_localizations/flutter_localizations.dart';\n\nnew MaterialApp(\n localizationsDelegates: [\n   // 本地化的代理类\n   GlobalMaterialLocalizations.delegate,\n   GlobalWidgetsLocalizations.delegate,\n ],\n supportedLocales: [\n    const Locale('en', 'US'), // 美国英语\n    const Locale('zh', 'CN'), // 中文简体\n    //其它Locales\n  ],\n  // ...\n)\n```\n\n> 与`MaterialApp`类为入口的应用不同, 对基于`WidgetsApp`类为入口的应用程序进行国际化时,不需要`GlobalMaterialLocalizations.delegate`。\n\n`localizationsDelegates`列表中的元素是生成本地化值集合的工厂。`GlobalMaterialLocalizations.delegate` 为Material 组件库提供的本地化的字符串和其他值，它可以使Material 组件支持多语言。 `GlobalWidgetsLocalizations.delegate`定义组件默认的文本方向，从左到右或从右到左，这是因为有些语言的阅读习惯并不是从左到右，比如如阿拉伯语就是从右向左的。\n\n`supportedLocales`也接收一个Locale数组，表示我们的应用支持的语言列表，在本例中我们的应用只支持美国英语和中文简体两种语言。\n\n### 获取当前区域Locale\n\n[`Locale`](https://docs.flutter.io/flutter/dart-ui/Locale-class.html)类是用来标识用户的语言环境的，它包括语言和国家两个标志如：\n\n```dart\nconst Locale('zh', 'CN') // 中文简体\n```\n\n我们始终可以通过以下方式来获取应用的当前区域Locale：\n\n```dart\nLocale myLocale = Localizations.localeOf(context);\n```\n\n[`Localizations`](https://docs.flutter.io/flutter/widgets/Localizations-class.html) 组件一般位于widget树中其它业务组件的顶部，它的作用是定义区域Locale以及设置子树依赖的本地化资源。 如果系统的语言环境发生变化，[WidgetsApp](https://docs.flutter.io/flutter/widgets/WidgetsApp-class.html)将创建一个新的Localizations 组件并重建它，这样子树中通过`Localizations.localeOf(context)` 获取的Locale就会更新。\n\n### 监听系统语言切换\n\n当我们更改系统语言设置时，APP中的Localizations组件会重新构建，`Localizations.localeOf(context)` 获取的Locale就会更新，最终界面会重新build达到切换语言的效果。但是这个过程是隐式完成的，我们并没有主动去监听系统语言切换，但是有时我们需要在系统语言发生改变时做一些事，比如系统语言切换为一种我们APP不支持的语言时，我们需要设置一个默认的语言，这时我们就需要监听locale改变事件。\n\n我们可以通过`localeResolutionCallback`或`localeListResolutionCallback`回调来监听locale改变的事件，我们先看看`localeResolutionCallback`的回调函数签名：\n\n```dart\nLocale Function(Locale locale, Iterable<Locale> supportedLocales)\n```\n\n- 参数`locale`的值为当前的当前的系统语言设置，当应用启动时或用户动态改变系统语言设置时此locale即为系统的当前locale。当开发者手动指定APP的locale时，那么此locale参数代表开发者指定的locale，此时将忽略系统locale如：\n\n  ```dart\n  MaterialApp(\n   ...\n   locale: const Locale('en', 'US'), //手动指定locale\n   ...\n  )\n  ```\n\n  上面的例子中手动指定了应用locale为美国英语，指定后即使设备当前语言是中文简体，应用中的locale也依然是美国英语。如果`locale`为`null`，则表示Flutter未能获取到设备的Locale信息，所以我们在使用`locale`之前一定要先判空。\n\n- `supportedLocales` 为当前应用支持的locale列表，是开发者在MaterialApp中通过`supportedLocales`属性注册的。\n\n- 返回值是一个`Locale`，此`Locale`为Flutter APP最终使用的`Locale`。通常在不支持的语言区域时返回一个默认的`Locale`。\n\n`localeListResolutionCallback`和`localeResolutionCallback`唯一的不同就在第一个参数类型，前者接收的是一个`Locale`列表，而后者接收的是单个`Locale`。\n\n```dart\nLocale Function(List<Locale> locales, Iterable<Locale> supportedLocales)\n```\n\n在较新的Android系统中，用户可以设置一个语言列表，这样一来，支持多语言的应用就会得到这个列表，应用通常的处理方式就是按照列表的顺序依次尝试加载相应的Locale，如果某一种语言加载成功则会停止。图13-1是Android系统中设置语言列表的截图：\n\n![设置语言列表](../imgs/13-1.jpeg)\n\n在Flutter中，应该优先使用`localeListResolutionCallback`，当然你不必担心Android系统的差异性，如果在低版本的Android系统中，Flutter会自动处理这种情况，这时Locale列表只会包含一项。\n\n### Localization 组件\n\nLocalizations组件用于加载和查找应用当前语言下的本地化值或资源。应用程序通过[`Localizations.of(context,type)`](https://docs.flutter.io/flutter/widgets/Localizations/of.html)来引用这些对象。 如果设备的Locale区域设置发生更改，则Localizations 组件会自动加载新区域的Locale值，然后重新build使用（依赖）了它们的组件，之所以会这样，是因为`Localizations`内部使用了[InheritedWidget](https://book.flutterchina.club/chapter7/inherited_widget.html) ，我们在介绍该组件时讲过：当子组件的`build`函数引用了`InheritedWidget`时，会创建对`InheritedWidget`的隐式依赖关系。因此，当`InheritedWidget`发生更改时，即`Localizations`的Locale设置发生更改时，将重建所有依赖它的子组件。\n\n本地化值由`Localizations`的 [LocalizationsDelegates](https://docs.flutter.io/flutter/widgets/LocalizationsDelegate-class.html) 列表加载 。 **每个委托必须定义一个异步load() 方法**，以生成封装了一系列本地化值的对象。通常这些对象为每个本地化值定义一个方法。\n\n在大型应用程序中，不同模块或Package可能会与自己的本地化值捆绑在一起。 这就是为什么要用`Localizations` 管理对象表的原因。 要使用由`LocalizationsDelegate `的`load`方法之一产生的对象，可以指定一个`BuildContext`和对象的类型来找到它。例如，Material 组件库的本地化字符串由[MaterialLocalizations](https://docs.flutter.io/flutter/material/MaterialLocalizations-class.html)类定义，此类的实例由[MaterialApp](https://docs.flutter.io/flutter/material/MaterialApp-class.html)类提供的`LocalizationDelegate`创建， 它们可以如下方式获取到：\n\n```dart\nLocalizations.of<MaterialLocalizations>(context, MaterialLocalizations);\n```\n\n这个特殊的`Localizations.of()`表达式会经常使用，所以MaterialLocalizations类提供了一个便捷方法：\n\n```dart\nstatic MaterialLocalizations of(BuildContext context) {\n  return Localizations.of<MaterialLocalizations>(context, MaterialLocalizations);\n}\n\n// 可以直接调用便捷方法\ntooltip: MaterialLocalizations.of(context).backButtonTooltip,\n```\n\n### 使用打包好的LocalizationsDelegates\n\n为了尽可能小而且简单，flutter软件包中仅提供美国英语值的`MaterialLocalizations`和`WidgetsLocalizations`接口的实现。 这些实现类分别称为`DefaultMaterialLocalizations`和`DefaultWidgetsLocalizations`。flutter_localizations 包包含`GlobalMaterialLocalizations`和`GlobalWidgetsLocalizations`的本地化接口的多语言实现， 国际化的应用程序必须按照本节开头说明的那样为这些类指定本地化Delegate。\n\n上述的`GlobalMaterialLocalizations`和`GlobalWidgetsLocalizations`只是Material组件库的本地化实现，如果我们要让自己的布局支持多语言，那么就需要实现在即的`Localizations`，我们将在下一节介绍其具体的实现方式。\n\n\n\n"
  },
  {
    "path": "src/v2/chapter14/element_buildcontext.md",
    "content": "# 14.2 Element与BuildContext\n\n## 14.2.1 Element\n\n在“Widget简介”一节，我们介绍了Widget和Element的关系，我们知道最终的UI树其实是由一个个独立的Element节点构成。我们也说过组件最终的Layout、渲染都是通过`RenderObject`来完成的，从创建到渲染的大体流程是：根据Widget生成Element，然后创建相应的`RenderObject`并关联到`Element.renderObject`属性上，最后再通过`RenderObject`来完成布局排列和绘制。\n\nElement就是Widget在UI树具体位置的一个实例化对象，大多数Element只有唯一的`renderObject`，但还有一些Element会有多个子节点，如继承自`RenderObjectElement`的一些类，比如`MultiChildRenderObjectElement`。最终所有Element的RenderObject构成一棵树，我们称之为”Render Tree“即”渲染树“。总结一下，我们可以认为Flutter的UI系统包含三棵树：Widget树、Element树、渲染树。他们的依赖关系是：Element树根据Widget树生成，而渲染树又依赖于Element树，如图14-0所示。\n\n![图14-0](../imgs/14-0.png)\n\n现在我们重点看一下Element，Element的生命周期如下：\n\n1. Framework 调用`Widget.createElement` 创建一个Element实例，记为`element`\n2. Framework 调用 `element.mount(parentElement,newSlot)` ，mount方法中首先调用`element`所对应Widget的`createRenderObject`方法创建与`element`相关联的RenderObject对象，然后调用`element.attachRenderObject`方法将`element.renderObject`添加到渲染树中插槽指定的位置（这一步不是必须的，一般发生在Element树结构发生变化时才需要重新attach）。插入到渲染树后的`element`就处于“active”状态，处于“active”状态后就可以显示在屏幕上了（可以隐藏）。\n3. 当有父Widget的配置数据改变时，同时其`State.build`返回的Widget结构与之前不同，此时就需要重新构建对应的Element树。为了进行Element复用，在Element重新构建前会先尝试是否可以复用旧树上相同位置的element，element节点在更新前都会调用其对应Widget的`canUpdate`方法，如果返回`true`，则复用旧Element，旧的Element会使用新Widget配置数据更新，反之则会创建一个新的Element。`Widget.canUpdate`主要是判断`newWidget`与`oldWidget`的`runtimeType`和`key`是否同时相等，如果同时相等就返回`true`，否则就会返回`false`。根据这个原理，当我们需要强制更新一个Widget时，可以通过指定不同的Key来避免复用。\n4. 当有祖先Element决定要移除`element ` 时（如Widget树结构发生了变化，导致`element`对应的Widget被移除），这时该祖先Element就会调用`deactivateChild` 方法来移除它，移除后`element.renderObject`也会被从渲染树中移除，然后Framework会调用`element.deactivate ` 方法，这时`element`状态变为“inactive”状态。\n5. “inactive”态的element将不会再显示到屏幕。为了避免在一次动画执行过程中反复创建、移除某个特定element，“inactive”态的element在当前动画最后一帧结束前都会保留，如果在动画执行结束后它还未能重新变成“active”状态，Framework就会调用其`unmount`方法将其彻底移除，这时element的状态为`defunct`,它将永远不会再被插入到树中。\n6. 如果`element`要重新插入到Element树的其它位置，如`element`或`element`的祖先拥有一个GlobalKey（用于全局复用元素），那么Framework会先将element从现有位置移除，然后再调用其`activate`方法，并将其`renderObject`重新attach到渲染树。\n\n看完Element的生命周期，可能有些读者会有疑问，开发者会直接操作Element树吗？其实对于开发者来说，大多数情况下只需要关注Widget树就行，Flutter框架已经将对Widget树的操作映射到了Element树上，这可以极大的降低复杂度，提高开发效率。但是了解Element对理解整个Flutter UI框架是至关重要的，Flutter正是通过Element这个纽带将Widget和RenderObject关联起来，了解Element层不仅会帮助读者对Flutter UI框架有个清晰的认识，而且也会提高自己的抽象能力和设计能力。另外在有些时候，我们必须得直接使用Element对象来完成一些操作，比如获取主题Theme数据，具体细节将在下文介绍。\n\n## 14.2.2 BuildContext\n\n我们已经知道，`StatelessWidget`和`StatefulWidget`的`build`方法都会传一个`BuildContext`对象：\n\n```dart\nWidget build(BuildContext context) {}\n```\n\n我们也知道，在很多时候我们都需要使用这个`context` 做一些事，比如：\n\n```dart\nTheme.of(context) //获取主题\nNavigator.push(context, route) //入栈新路由\nLocalizations.of(context, type) //获取Local\ncontext.size //获取上下文大小\ncontext.findRenderObject() //查找当前或最近的一个祖先RenderObject\n```\n\n那么`BuildContext`到底是什么呢，查看其定义，发现其是一个抽象接口类：\n\n```dart\nabstract class BuildContext {\n    ...\n}\n```\n\n那这个`context`对象对应的实现类到底是谁呢？我们顺藤摸瓜，发现`build`调用是发生在`StatelessWidget`和`StatefulWidget`对应的`StatelessElement`和`StatefulElement`的`build`方法中，以`StatelessElement`为例：\n\n```dart\n\nclass StatelessElement extends ComponentElement {\n  ...\n  @override\n  Widget build() => widget.build(this);\n  ...\n}\n```\n\n发现`build`传递的参数是`this`，很明显！这个`BuildContext`就是`StatelessElement`。同样，我们同样发现`StatefulWidget`的`context`是`StatefulElement`。但`StatelessElement`和`StatefulElement`本身并没有实现`BuildContext`接口，继续跟踪代码，发现它们间接继承自`Element`类，然后查看`Element`类定义，发现`Element`类果然实现了`BuildContext`接口:\n\n```dart\nclass Element extends DiagnosticableTree implements BuildContext {\n    ...\n}\n```\n\n至此真相大白，`BuildContext`就是widget对应的`Element`，所以我们可以通过`context`在`StatelessWidget`和`StatefulWidget`的`build`方法中直接访问`Element`对象。我们获取主题数据的代码`Theme.of(context)`内部正是调用了Element的`dependOnInheritedWidgetOfExactType()`方法。\n\n> 思考题：为什么build方法的参数不定义成Element对象，而要定义成BuildContext ?\n\n### 进阶\n\n我们可以看到Element是Flutter UI框架内部连接widget和`RenderObject`的纽带，大多数时候开发者只需要关注widget层即可，但是widget层有时候并不能完全屏蔽`Element`细节，所以Framework在`StatelessWidget`和`StatefulWidget`中通过`build`方法参数又将`Element`对象也传递给了开发者，这样一来，开发者便可以在需要时直接操作`Element`对象。那么现在笔者提两个问题，请读者先自己思考一下：\n\n1. 如果没有widget层，单靠`Element`层是否可以搭建起一个可用的UI框架？如果可以应该是什么样子？\n2. Flutter UI框架能不做成响应式吗？\n\n对于问题1，答案当然是肯定的，因为我们之前说过widget树只是`Element`树的映射，我们完全可以直接通过Element来搭建一个UI框架。下面举一个例子：\n\n我们通过纯粹的Element来模拟一个`StatefulWidget`的功能，假设有一个页面，该页面有一个按钮，按钮的文本是一个9位数，点击一次按钮，则对9个数随机排一次序，代码如下：\n\n```dart\nclass HomeView extends ComponentElement{\n  HomeView(Widget widget) : super(widget);\n  String text = \"123456789\";\n\n  @override\n  Widget build() {\n    Color primary=Theme.of(this).primaryColor; //1\n    return GestureDetector(\n      child: Center(\n        child: FlatButton(\n          child: Text(text, style: TextStyle(color: primary),),\n          onPressed: () {\n            var t = text.split(\"\")..shuffle();\n            text = t.join();\n            markNeedsBuild(); //点击后将该Element标记为dirty，Element将会rebuild\n          },\n        ),\n      ),\n    );\n  }\n}\n```\n\n- 上面`build`方法不接收参数，这一点和在`StatelessWidget`和`StatefulWidget`中`build(BuildContext)`方法不同。代码中需要用到`BuildContext`的地方直接用`this`代替即可，如代码注释1处`Theme.of(this)`参数直接传`this`即可，因为当前对象本身就是`Element`实例。\n- 当`text`发生改变时，我们调用`markNeedsBuild()`方法将当前Element标记为dirty即可，标记为dirty的Element会在下一帧中重建。实际上，`State.setState()`在内部也是调用的`markNeedsBuild()`方法。\n\n- 上面代码中build方法返回的仍然是一个widget，这是由于Flutter框架中已经有了widget这一层，并且组件库都已经是以widget的形式提供了，如果在Flutter框架中所有组件都像示例的`HomeView`一样以`Element`形式提供，那么就可以用纯`Element`来构建UI了`HomeView`的build方法返回值类型就可以是`Element`了。\n\n如果我们需要将上面代码在现有Flutter框架中跑起来，那么还是得提供一个“适配器”widget将`HomeView`结合到现有框架中，下面`CustomHome`就相当于“适配器”：\n\n```dart\nclass CustomHome extends Widget {\n  @override\n  Element createElement() {\n    return HomeView(this);\n  }\n}\n```\n\n现在就可以将`CustomHome`添加到widget树了，我们在一个新路由页创建它，最终效果如下如图14-1和14-2（点击后）所示：\n\n![图14-1](../imgs/14-1.png) ![图14-2](../imgs/14-2.png)\n\n点击按钮则按钮文本会随机排序。\n\n对于问题2，答案当然也是肯定的，Flutter engine提供的dart API是原始且独立的，这个与操作系统提供的API类似，上层UI框架设计成什么样完全取决于设计者，完全可以将UI框架设计成Android风格或iOS风格，但这些事Google不会再去做，我们也没必要再去搞这一套，这是因为响应式的思想本身是很棒的，之所以提出这个问题，是因为笔者认为做与不做是一回事，但知道能不能做是另一回事，这能反映出我们对知识的理解程度。\n\n\n### 总结\n\n本节详细的介绍了`Element`的生命周期，以及它Widget、BuildContext的关系，也介绍了Element在Flutter UI系统中的角色和作用，我们将在下一节介绍Flutter UI系统中另一个重要的角色RenderObject。\n\n\n\n"
  },
  {
    "path": "src/v2/chapter14/flutter_app_startup.md",
    "content": "# 14.4 Flutter运行机制-从启动到显示\n\n本节我们主要介绍一下Flutter从启动到显示的过程。\n\n### 启动\n\nFlutter的入口在\"lib/main.dart\"的`main()`函数中，它是Dart应用程序的起点。在Flutter应用中，`main()`函数最简单的实现如下：\n\n```dart\nvoid main() {\n  runApp(MyApp());\n}\n```\n\n可以看`main()`函数只调用了一个`runApp()`方法，我们看看`runApp()`方法中都做了什么：\n\n```dart\nvoid runApp(Widget app) {\n  WidgetsFlutterBinding.ensureInitialized()\n    ..attachRootWidget(app)\n    ..scheduleWarmUpFrame();\n}\n```\n\n参数`app`是一个widget，它是Flutter应用启动后要展示的第一个Widget。而`WidgetsFlutterBinding`正是绑定widget 框架和Flutter engine的桥梁，定义如下：\n\n```dart\nclass WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {\n  static WidgetsBinding ensureInitialized() {\n    if (WidgetsBinding.instance == null)\n      WidgetsFlutterBinding();\n    return WidgetsBinding.instance;\n  }\n}\n```\n\n可以看到`WidgetsFlutterBinding`继承自`BindingBase` 并混入了很多`Binding`，在介绍这些`Binding`之前我们先介绍一下`Window`，下面是`Window`的官方解释：\n\n> The most basic interface to the host operating system's user interface.\n\n很明显，`Window`正是Flutter Framework连接宿主操作系统的接口。我们看一下`Window`类的部分定义：\n\n```dart\nclass Window {\n    \n  // 当前设备的DPI，即一个逻辑像素显示多少物理像素，数字越大，显示效果就越精细保真。\n  // DPI是设备屏幕的固件属性，如Nexus 6的屏幕DPI为3.5 \n  double get devicePixelRatio => _devicePixelRatio;\n  \n  // Flutter UI绘制区域的大小\n  Size get physicalSize => _physicalSize;\n\n  // 当前系统默认的语言Locale\n  Locale get locale;\n    \n  // 当前系统字体缩放比例。  \n  double get textScaleFactor => _textScaleFactor;  \n    \n  // 当绘制区域大小改变回调\n  VoidCallback get onMetricsChanged => _onMetricsChanged;  \n  // Locale发生变化回调\n  VoidCallback get onLocaleChanged => _onLocaleChanged;\n  // 系统字体缩放变化回调\n  VoidCallback get onTextScaleFactorChanged => _onTextScaleFactorChanged;\n  // 绘制前回调，一般会受显示器的垂直同步信号VSync驱动，当屏幕刷新时就会被调用\n  FrameCallback get onBeginFrame => _onBeginFrame;\n  // 绘制回调  \n  VoidCallback get onDrawFrame => _onDrawFrame;\n  // 点击或指针事件回调\n  PointerDataPacketCallback get onPointerDataPacket => _onPointerDataPacket;\n  // 调度Frame，该方法执行后，onBeginFrame和onDrawFrame将紧接着会在合适时机被调用，\n  // 此方法会直接调用Flutter engine的Window_scheduleFrame方法\n  void scheduleFrame() native 'Window_scheduleFrame';\n  // 更新应用在GPU上的渲染,此方法会直接调用Flutter engine的Window_render方法\n  void render(Scene scene) native 'Window_render';\n\n  // 发送平台消息\n  void sendPlatformMessage(String name,\n                           ByteData data,\n                           PlatformMessageResponseCallback callback) ;\n  // 平台通道消息处理回调  \n  PlatformMessageCallback get onPlatformMessage => _onPlatformMessage;\n  \n  ... //其它属性及回调\n   \n}\n```\n\n可以看到`Window`类包含了当前设备和系统的一些信息以及Flutter Engine的一些回调。现在我们再回来看看`WidgetsFlutterBinding`混入的各种Binding。通过查看这些 Binding的源码，我们可以发现这些Binding中基本都是监听并处理`Window`对象的一些事件，然后将这些事件按照Framework的模型包装、抽象然后分发。可以看到`WidgetsFlutterBinding`正是粘连Flutter engine与上层Framework的“胶水”。\n\n- `GestureBinding`：提供了`window.onPointerDataPacket` 回调，绑定Framework手势子系统，是Framework事件模型与底层事件的绑定入口。\n- `ServicesBinding`：提供了`window.onPlatformMessage` 回调， 用于绑定平台消息通道（message channel），主要处理原生和Flutter通信。\n- `SchedulerBinding`：提供了`window.onBeginFrame`和`window.onDrawFrame`回调，监听刷新事件，绑定Framework绘制调度子系统。\n- `PaintingBinding`：绑定绘制库，主要用于处理图片缓存。\n- `SemanticsBinding`：语义化层与Flutter engine的桥梁，主要是辅助功能的底层支持。\n- `RendererBinding`: 提供了`window.onMetricsChanged` 、`window.onTextScaleFactorChanged` 等回调。它是渲染树与Flutter engine的桥梁。\n- `WidgetsBinding`：提供了`window.onLocaleChanged`、`onBuildScheduled ` 等回调。它是Flutter widget层与engine的桥梁。\n\n` WidgetsFlutterBinding.ensureInitialized()`负责初始化一个`WidgetsBinding`的全局单例，紧接着会调用`WidgetsBinding`的`attachRootWidget`方法，该方法负责将根Widget添加到`RenderView`上，代码如下：\n\n```dart\nvoid attachRootWidget(Widget rootWidget) {\n  _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(\n    container: renderView, \n    debugShortDescription: '[root]',\n    child: rootWidget\n  ).attachToRenderTree(buildOwner, renderViewElement);\n}\n```\n\n注意，代码中的有`renderView`和`renderViewElement`两个变量，`renderView`是一个`RenderObject`，它是渲染树的根，而`renderViewElement`是`renderView`对应的`Element`对象，可见该方法主要完成了根widget到根 `RenderObject`再到根`Element`的整个关联过程。我们看看`attachToRenderTree`的源码实现：\n\n```dart\nRenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [RenderObjectToWidgetElement<T> element]) {\n  if (element == null) {\n    owner.lockState(() {\n      element = createElement();\n      assert(element != null);\n      element.assignOwner(owner);\n    });\n    owner.buildScope(element, () {\n      element.mount(null, null);\n    });\n  } else {\n    element._newWidget = this;\n    element.markNeedsBuild();\n  }\n  return element;\n}\n```\n\n该方法负责创建根element，即` RenderObjectToWidgetElement`，并且将element与widget 进行关联，即创建出 widget树对应的element树。如果element 已经创建过了，则将根element 中关联的widget 设为新的，由此可以看出element 只会创建一次，后面会进行复用。那么`BuildOwner`是什么呢？其实他就是widget framework的管理类，它跟踪哪些widget需要重新构建。\n\n### 渲染\n\n回到`runApp`的实现中，当调用完`attachRootWidget`后，最后一行会调用 `WidgetsFlutterBinding` 实例的 `scheduleWarmUpFrame()` 方法，该方法的实现在`SchedulerBinding` 中，它被调用后会立即进行一次绘制（而不是等待\"vsync\" 信号），在此次绘制结束前，该方法会锁定事件分发，也就是说在本次绘制结束完成之前Flutter将不会响应各种事件，这可以保证在绘制过程中不会再触发新的重绘。下面是`scheduleWarmUpFrame()` 方法的部分实现(省略了无关代码)：\n\n```dart\nvoid scheduleWarmUpFrame() {\n  ...\n  Timer.run(() {\n    handleBeginFrame(null); \n  });\n  Timer.run(() {\n    handleDrawFrame();  \n    resetEpoch();\n  });\n  // 锁定事件\n  lockEvents(() async {\n    await endOfFrame;\n    Timeline.finishSync();\n  });\n ...\n}\n```\n\n可以看到该方法中主要调用了`handleBeginFrame()` 和 `handleDrawFrame()` 两个方法，在看这两个方法之前我们首先了解一下Frame 和 FrameCallback 的概念：\n\n- Frame: 一次绘制过程，我们称其为一帧。Flutter engine受显示器垂直同步信号\"VSync\"的驱使不断的触发绘制。我们之前说的Flutter可以实现60fps（Frame Per-Second），就是指一秒钟可以触发60次重绘，FPS值越大，界面就越流畅。\n\n- FrameCallback：`SchedulerBinding` 类中有三个FrameCallback回调队列， 在一次绘制过程中，这三个回调队列会放在不同时机被执行：\n\n  1. `transientCallbacks`：用于存放一些临时回调，一般存放动画回调。可以通过`SchedulerBinding.instance.scheduleFrameCallback` 添加回调。\n  2. `persistentCallbacks`：用于存放一些持久的回调，不能在此类回调中再请求新的绘制帧，持久回调一经注册则不能移除。`SchedulerBinding.instance.addPersitentFrameCallback()`，这个回调中处理了布局与绘制工作。\n  3. `postFrameCallbacks`：在Frame结束时只会被调用一次，调用后会被系统移除，可由 `SchedulerBinding.instance.addPostFrameCallback()` 注册，注意，不要在此类回调中再触发新的Frame，这可以会导致循环刷新。\n\n现在请读者自行查看`handleBeginFrame()` 和 `handleDrawFrame()` 两个方法的源码，可以发现前者主要是执行了`transientCallbacks`队列，而后者执行了 `persistentCallbacks` 和 `postFrameCallbacks` 队列。\n\n### 绘制\n\n渲染和绘制逻辑在`RendererBinding`中实现，查看其源码，发现在其`initInstances()`方法中有如下代码：\n\n```dart\nvoid initInstances() {\n  ... //省略无关代码\n      \n  //监听Window对象的事件  \n  ui.window\n    ..onMetricsChanged = handleMetricsChanged\n    ..onTextScaleFactorChanged = handleTextScaleFactorChanged\n    ..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged\n    ..onSemanticsAction = _handleSemanticsAction;\n   \n  //添加PersistentFrameCallback    \n  addPersistentFrameCallback(_handlePersistentFrameCallback);\n}\n```\n\n我们看最后一行，通过`addPersistentFrameCallback` 向`persistentCallbacks`队列添加了一个回调 `_handlePersistentFrameCallback`:\n\n```dart\nvoid _handlePersistentFrameCallback(Duration timeStamp) {\n  drawFrame();\n}\n```\n\n该方法直接调用了`RendererBinding`的`drawFrame()`方法：\n\n```dart\nvoid drawFrame() {\n  assert(renderView != null);\n  pipelineOwner.flushLayout(); //布局\n  pipelineOwner.flushCompositingBits(); //重绘之前的预处理操作，检查RenderObject是否需要重绘\n  pipelineOwner.flushPaint(); // 重绘\n  renderView.compositeFrame(); // 将需要绘制的比特数据发给GPU\n  pipelineOwner.flushSemantics(); // this also sends the semantics to the OS.\n}\n```\n\n我们看看这些方法分别做了什么：\n\n#### flushLayout()\n\n```dart\nvoid flushLayout() {\n   ...\n    while (_nodesNeedingLayout.isNotEmpty) {\n      final List<RenderObject> dirtyNodes = _nodesNeedingLayout;\n      _nodesNeedingLayout = <RenderObject>[];\n      for (RenderObject node in \n           dirtyNodes..sort((RenderObject a, RenderObject b) => a.depth - b.depth)) {\n        if (node._needsLayout && node.owner == this)\n          node._layoutWithoutResize();\n      }\n    }\n  } \n}\n```\n\n源码很简单，该方法主要任务是更新了所有被标记为“dirty”的`RenderObject`的布局信息。主要的动作发生在`node._layoutWithoutResize()`方法中，该方法中会调用`performLayout()`进行重新布局。\n\n#### flushCompositingBits()\n\n```dart\nvoid flushCompositingBits() {\n  _nodesNeedingCompositingBitsUpdate.sort(\n      (RenderObject a, RenderObject b) => a.depth - b.depth\n  );\n  for (RenderObject node in _nodesNeedingCompositingBitsUpdate) {\n    if (node._needsCompositingBitsUpdate && node.owner == this)\n      node._updateCompositingBits(); //更新RenderObject.needsCompositing属性值\n  }\n  _nodesNeedingCompositingBitsUpdate.clear();\n}\n```\n\n检查`RenderObject`是否需要重绘，然后更新`RenderObject.needsCompositing`属性，如果该属性值被标记为`true`则需要重绘。\n\n#### flushPaint()\n\n```dart\nvoid flushPaint() {\n ...\n  try {\n    final List<RenderObject> dirtyNodes = _nodesNeedingPaint; \n    _nodesNeedingPaint = <RenderObject>[];\n    // 反向遍历需要重绘的RenderObject\n    for (RenderObject node in \n         dirtyNodes..sort((RenderObject a, RenderObject b) => b.depth - a.depth)) {\n      if (node._needsPaint && node.owner == this) {\n        if (node._layer.attached) {\n          // 真正的绘制逻辑  \n          PaintingContext.repaintCompositedChild(node);\n        } else {\n          node._skippedPaintingOnLayer();\n        }\n      }\n    }\n  } \n}\n```\n\n该方法进行了最终的绘制，可以看出它不是重绘了所有 `RenderObject`，而是只重绘了需要重绘的 `RenderObject`。真正的绘制是通过`PaintingContext.repaintCompositedChild()`来绘制的，该方法最终会调用Flutter engine提供的Canvas API来完成绘制。\n\n#### compositeFrame()\n\n```dart\nvoid compositeFrame() {\n  ...\n  try {\n    final ui.SceneBuilder builder = ui.SceneBuilder();\n    final ui.Scene scene = layer.buildScene(builder);\n    if (automaticSystemUiAdjustment)\n      _updateSystemChrome();\n    ui.window.render(scene); //调用Flutter engine的渲染API\n    scene.dispose(); \n  } finally {\n    Timeline.finishSync();\n  }\n}\n```\n\n这个方法中有一个`Scene`对象，Scene对象是一个数据结构，保存最终渲染后的像素信息。这个方法将Canvas画好的`Scene`传给`window.render()`方法，该方法会直接将scene信息发送给Flutter engine，最终由engine将图像画在设备屏幕上。\n\n#### 最后\n\n需要注意的是：由于`RendererBinding`只是一个mixin，而with它的是`WidgetsBinding`，所以我们需要看看`WidgetsBinding`中是否重写该方法，查看`WidgetsBinding`的`drawFrame()`方法源码：\n\n```dart\n@override\nvoid drawFrame() {\n ...//省略无关代码\n  try {\n    if (renderViewElement != null)\n      buildOwner.buildScope(renderViewElement); \n    super.drawFrame(); //调用RendererBinding的drawFrame()方法\n    buildOwner.finalizeTree();\n  } \n}\n```\n\n我们发现在调用`RendererBinding.drawFrame()`方法前会调用 `buildOwner.buildScope()` （非首次绘制），该方法会将被标记为“dirty” 的 element 进行 `rebuild()` 。\n\n### 总结\n\n本节介绍了Flutter APP从启动到显示到屏幕上的主流程，读者可以结合前面章节对Widget、Element以及RenderObject的介绍来加强细节理解。\n\n"
  },
  {
    "path": "src/v2/chapter14/flutter_ui_system.md",
    "content": "# 14.1 Flutter UI系统\n\n在本书的前面章节中，我们多次提到\"UI系统\"这个概念，本书中所指的UI系统特指：基于一个平台，在此平台上实现GUI的一个系统，这里的平台特指操作系统，如Android、iOS或者Windows、macOS。我们说过各个平台UI系统的原理是相通的，也就是说无论是Android还是iOS，他们将一个用户界面展示到屏幕的流程是相似的，所以，在介绍Flutter UI系统之前，我们先看看UI系统的基本原理，这样可以帮助读者对操作系统和系统底层UI逻辑有一个清晰的认识。\n\n### 硬件绘图基本原理\n\n提到原理，我们要从屏幕显示图像的基本原理谈起。我们知道显示器（屏幕）是由一个个物理显示单元组成，每一个单元我们可以称之为一个物理像素点，而每一个像素点可以发出多种颜色，显示器成相的原理就是在不同的物理像素点上显示不同的颜色，最终构成完整的图像。\n\n一个像素点能发出的所有颜色总数是显示器的一个重要指标，比如我们所说的1600万色的屏幕就是指一个像素点可以显示出1600万种颜色，而显示器颜色是有RGB三基色组成，所以1600万即2的24次方，即每个基本色（R、G、B）深度扩展至8 bit(位)，颜色深度越深，所能显示的色彩更加丰富靓丽。\n\n为了更新显示画面，显示器是以固定的频率刷新（从GPU取数据），比如有一部手机屏幕的刷新频率是 60Hz。当一帧图像绘制完毕后准备绘制下一帧时，显示器会发出一个垂直同步信号（如VSync）， 60Hz的屏幕就会一秒内发出 60次这样的信号。而这个信号主要是用于同步CPU、GPU和显示器的。一般地来说，计算机系统中，CPU、GPU和显示器以一种特定的方式协作：CPU将计算好的显示内容提交给 GPU，GPU渲染后放入帧缓冲区，然后视频控制器按照同步信号从帧缓冲区取帧数据传递给显示器显示。\n\nCPU和GPU的任务是各有偏重的，CPU主要用于基本数学和逻辑计算，而GPU主要执行和图形处理相关的复杂的数学，如矩阵变化和几何计算，GPU的主要作用就是确定最终输送给显示器的各个像素点的色值。\n\n### 操作系统绘制API的封装\n\n由于最终的图形计算和绘制都是由相应的硬件来完成，而直接操作硬件的指令通常都会有操作系统屏蔽，应用开发者通常不会直接面对硬件，操作系统屏蔽了这些底层硬件操作后会提供一些封装后的API供操作系统之上的应用调用，但是对于应用开发者来说，直接调用这些操作系统提供的API是比较复杂和低效的，因为操作系统提供的API往往比较基础，直接调用需要了解API的很多细节。正是因为这个原因，几乎所有用于开发GUI程序的编程语言都会在操作系统之上再封装一层，将操作系统原生API封装在一个编程框架和模型中，然后定义一种简单的开发规则来开发GUI应用程序，而这一层抽象，正是我们所说的“UI”系统，如Android SDK正是封装了Android操作系统API，提供了一个“UI描述文件XML+Java操作DOM”的UI系统，而iOS的UIKit 对View的抽象也是一样的，他们都将操作系统API抽象成一个基础对象（如用于2D图形绘制的Canvas），然后再定义一套规则来描述UI，如UI树结构，UI操作的单线程原则等。\n\n### Flutter UI系统\n\n我们可以看到，无论是Android SDK还是iOS的UIKit 的职责都是相同的，它们只是语言载体和底层的系统不同而已。那么可不可以实现这么一个UI系统：可以使用同一种编程语言开发，然后针对不同操作系统API抽象一个对上接口一致，对下适配不同操作系统的的中间层，然后在打包编译时再使用相应的中间层代码？如果可以做到，那么我们就可以使用同一套代码编写跨平台的应用了。而Flutter的原理正是如此，它提供了一套Dart API，然后在底层通过OpenGL这种跨平台的绘制库（内部会调用操作系统API）实现了一套代码跨多端。由于Dart API也是调用操作系统API，所以它的性能接近原生。\n\n> 注意，虽然Dart是先调用了OpenGL，OpenGL才会调用操作系统API，但是这仍然是原生渲染，因为OpenGL只是操作系统API的一个封装库，它并不像WebView渲染那样需要JavaScript运行环境和CSS渲染器，所以不会有性能损失。\n\n至此，我们已经介绍了Flutter UI系统和操作系统交互的这一部分原理，现在需要说一些它对应用开发者定义的开发标准。其实在前面的章节中，我们已经对这个标准非常熟悉了, 简单概括就是：组合和响应式。我们要开发一个UI界面，需要通过组合其它Widget来实现，Flutter中，一切都是Widget，当UI要发生变化时，我们不去直接修改DOM，而是通过更新状态，让Flutter UI系统来根据新的状态来重新构建UI。\n\n讲到这里，读者可能发现Flutter UI系统和Flutter Framework的概念是差不多的，的确如此，之所以用“UI系统”，是因为其他平台中可能不这么叫，我们只是为了概念统一，便于描述，读者不必纠结于概念本身。\n\n在接下来的小节中，我们先详细介绍一下`Element`、`RenderObject`，它们是组成Flutter UI系统的基石。最后我们再分析一下Flutter应用启动和运行的整体过程。\n\n\n\n\n\n\n\n"
  },
  {
    "path": "src/v2/chapter14/image_and_cache.md",
    "content": "# 14.5 图片加载原理与缓存\n\n在本书前面章节已经介绍过`Image` 组件，并提到Flutter框架对加载过的图片是有缓存的（内存），默认最大缓存数量是1000，最大缓存空间为100M。本节便详细介绍Image的原理及图片缓存机制，下面我们先看看`ImageProvider` 类。\n\n## 14.5.1 ImageProvider\n\n我们已经知道`Image` 组件的`image` 参数是一个必选参数，它是`ImageProvider`类型。下面我们便详细介绍一下`ImageProvider`，`ImageProvider`是一个抽象类，定义了图片数据获取和加载的相关接口。它的主要职责有两个：\n\n1. 提供图片数据源\n2. 缓存图片\n\n我们看看`ImageProvider`抽象类的详细定义：\n\n```dart\nabstract class ImageProvider<T> {\n\n  ImageStream resolve(ImageConfiguration configuration) {\n    // 实现代码省略\n  }\n  Future<bool> evict({ ImageCache cache,\n                      ImageConfiguration configuration = ImageConfiguration.empty }) async {\n    // 实现代码省略\n  }\n\n  Future<T> obtainKey(ImageConfiguration configuration); \n  @protected\n  ImageStreamCompleter load(T key); // 需子类实现\n}\n```\n\n#### `load(T key)`方法\n\n加载图片数据源的接口，不同的数据源的加载方法不同，每个`ImageProvider`的子类必须实现它。比如`NetworkImage`类和`AssetImage`类，它们都是`ImageProvider`的子类，但它们需要从不同的数据源来加载图片数据：`NetworkImage`是从网络来加载图片数据，而`AssetImage`则是从最终的应用包里来加载（加载打到应用安装包里的资源图片）。 我们以`NetworkImage`为例，看看其load方法的实现：\n\n```dart\n\n@override\nImageStreamCompleter load(image_provider.NetworkImage key) {\n\n  final StreamController<ImageChunkEvent> chunkEvents = StreamController<ImageChunkEvent>();\n  \n  return MultiFrameImageStreamCompleter(\n    codec: _loadAsync(key, chunkEvents), //调用\n    chunkEvents: chunkEvents.stream,\n    scale: key.scale,\n    ... //省略无关代码\n  );\n}\n```\n\n我们看到，`load`方法的返回值类型是`ImageStreamCompleter` ，它是一个抽象类，定义了管理图片加载过程的一些接口，`Image` Widget中正是通过它来监听图片加载状态的（我们将在下面介绍`Image` 原理时详细介绍）。\n\n`MultiFrameImageStreamCompleter` 是 `ImageStreamCompleter`的一个子类，是flutter sdk预置的类，通过该类，我们以方便、轻松地创建出一个`ImageStreamCompleter`实例来做为`load`方法的返回值。\n\n我们可以看到，`MultiFrameImageStreamCompleter` 需要一个`codec`参数，该参数类型为`Future<ui.Codec> `。`Codec ` 是处理图片编解码的类的一个handler，实际上，它只是一个flutter engine API 的包装类，也就是说图片的编解码逻辑不是在Dart 代码部分实现，而是在flutter engine中实现的。`Codec`类部分定义如下：\n\n```dart\n@pragma('vm:entry-point')\nclass Codec extends NativeFieldWrapperClass2 {\n  // 此类由flutter engine创建，不应该手动实例化此类或直接继承此类。\n  @pragma('vm:entry-point')\n  Codec._();\n\n  /// 图片中的帧数(动态图会有多帧)\n  int get frameCount native 'Codec_frameCount';\n\n  /// 动画重复的次数\n  /// * 0 表示只执行一次\n  /// * -1 表示循环执行\n  int get repetitionCount native 'Codec_repetitionCount';\n\n  /// 获取下一个动画帧\n  Future<FrameInfo> getNextFrame() {\n    return _futurize(_getNextFrame);\n  }\n\n  String _getNextFrame(_Callback<FrameInfo> callback) native 'Codec_getNextFrame';\n```\n\n我们可以看到`Codec`最终的结果是一个或多个（动图）帧，而这些帧最终会绘制到屏幕上。\n\n`MultiFrameImageStreamCompleter 的` `codec`参数值为`_loadAsync`方法的返回值，我们继续看`_loadAsync`方法的实现：\n\n```dart\n\n Future<ui.Codec> _loadAsync(\n    NetworkImage key,\n    StreamController<ImageChunkEvent> chunkEvents,\n  ) async {\n    try {\n      //下载图片\n      final Uri resolved = Uri.base.resolve(key.url);\n      final HttpClientRequest request = await _httpClient.getUrl(resolved);\n      headers?.forEach((String name, String value) {\n        request.headers.add(name, value);\n      });\n      final HttpClientResponse response = await request.close();\n      if (response.statusCode != HttpStatus.ok)\n        throw Exception(...);\n      // 接收图片数据 \n      final Uint8List bytes = await consolidateHttpClientResponseBytes(\n        response,\n        onBytesReceived: (int cumulative, int total) {\n          chunkEvents.add(ImageChunkEvent(\n            cumulativeBytesLoaded: cumulative,\n            expectedTotalBytes: total,\n          ));\n        },\n      );\n      if (bytes.lengthInBytes == 0)\n        throw Exception('NetworkImage is an empty file: $resolved');\n      // 对图片数据进行解码\n      return PaintingBinding.instance.instantiateImageCodec(bytes);\n    } finally {\n      chunkEvents.close();\n    }\n  }\n```\n\n可以看到`_loadAsync`方法主要做了两件事：\n\n1. 下载图片。\n2. 对下载的图片数据进行解码。\n\n下载逻辑比较简单：通过`HttpClient`从网上下载图片，另外下载请求会设置一些自定义的header，开发者可以通过`NetworkImage`的`headers`命名参数来传递。\n\n在图片下载完成后调用了`PaintingBinding.instance.instantiateImageCodec(bytes)`对图片进行解码，值得注意的是`instantiateImageCodec(...)`也是一个Native API的包装，实际上会调用Flutter engine的`instantiateImageCodec`方法，源码如下：\n\n```dart\nString _instantiateImageCodec(Uint8List list, _Callback<Codec> callback, _ImageInfo imageInfo, int targetWidth, int targetHeight)\n  native 'instantiateImageCodec';\n```\n\n#### `obtainKey(ImageConfiguration)`方法\n\n该接口主要是为了配合实现图片缓存，`ImageProvider`从数据源加载完数据后，会在全局的`ImageCache`中缓存图片数据，而图片数据缓存是一个Map，而Map的key便是调用此方法的返回值，不同的key代表不同的图片数据缓存。\n\n#### `resolve(ImageConfiguration)` 方法\n\n`resolve`方法是`ImageProvider`的暴露的给`Image`的主入口方法，它接受一个`ImageConfiguration`参数，返回`ImageStream`，即图片数据流。我们重点看一下`resolve`执行流程：\n\n```dart\nImageStream resolve(ImageConfiguration configuration) {\n  ... //省略无关代码\n  final ImageStream stream = ImageStream();\n  T obtainedKey; //\n  //定义错误处理函数\n  Future<void> handleError(dynamic exception, StackTrace stack) async {\n    ... //省略无关代码\n    stream.setCompleter(imageCompleter);\n    imageCompleter.setError(...);\n  }\n\n  // 创建一个新Zone，主要是为了当发生错误时不会干扰MainZone\n  final Zone dangerZone = Zone.current.fork(...);\n  \n  dangerZone.runGuarded(() {\n    Future<T> key;\n    // 先验证是否已经有缓存\n    try {\n      // 生成缓存key，后面会根据此key来检测是否有缓存\n      key = obtainKey(configuration);\n    } catch (error, stackTrace) {\n      handleError(error, stackTrace);\n      return;\n    }\n    key.then<void>((T key) {\n      obtainedKey = key;\n      // 缓存的处理逻辑在这里，记为A，下面详细介绍\n      final ImageStreamCompleter completer = PaintingBinding.instance\n          .imageCache.putIfAbsent(key, () => load(key), onError: handleError);\n      if (completer != null) {\n        stream.setCompleter(completer);\n      }\n    }).catchError(handleError);\n  });\n  return stream;\n}\n```\n\n`ImageConfiguration`  包含图片和设备的相关信息，如图片的大小、所在的`AssetBundle `(只有打到安装包的图片存在)以及当前的设备平台、devicePixelRatio（设备像素比等）。Flutter SDK提供了一个便捷函数`createLocalImageConfiguration`来创建`ImageConfiguration`  对象：\n\n```dart\nImageConfiguration createLocalImageConfiguration(BuildContext context, { Size size }) {\n  return ImageConfiguration(\n    bundle: DefaultAssetBundle.of(context),\n    devicePixelRatio: MediaQuery.of(context, nullOk: true)?.devicePixelRatio ?? 1.0,\n    locale: Localizations.localeOf(context, nullOk: true),\n    textDirection: Directionality.of(context),\n    size: size,\n    platform: defaultTargetPlatform,\n  );\n}\n```\n\n我们可以发现这些信息基本都是通过`Context`来获取。\n\n上面代码A处就是处理缓存的主要代码，这里的`PaintingBinding.instance.imageCache` 是 `ImageCache`的一个实例，它是`PaintingBinding`的一个属性，而Flutter框架中的`PaintingBinding.instance`是一个单例，`imageCache`事实上也是一个单例，也就是说图片缓存是全局的，统一由`PaintingBinding.instance.imageCache` 来管理。\n\n下面我们看看`ImageCache`类定义：\n\n```dart\nconst int _kDefaultSize = 1000;\nconst int _kDefaultSizeBytes = 100 << 20; // 100 MiB\n\nclass ImageCache {\n  // 正在加载中的图片队列\n  final Map<Object, _PendingImage> _pendingImages = <Object, _PendingImage>{};\n  // 缓存队列\n  final Map<Object, _CachedImage> _cache = <Object, _CachedImage>{};\n\n  // 缓存数量上限(1000)\n  int _maximumSize = _kDefaultSize;\n  // 缓存容量上限 (100 MB)\n  int _maximumSizeBytes = _kDefaultSizeBytes;\n  \n  // 缓存上限设置的setter\n  set maximumSize(int value) {...}\n  set maximumSizeBytes(int value) {...}\n \n  ... // 省略部分定义\n\n  // 清除所有缓存\n  void clear() {\n    // ...省略具体实现代码\n  }\n\n  // 清除指定key对应的图片缓存\n  bool evict(Object key) {\n   // ...省略具体实现代码\n  }\n\n \n  ImageStreamCompleter putIfAbsent(Object key, ImageStreamCompleter loader(), { ImageErrorListener onError }) {\n    assert(key != null);\n    assert(loader != null);\n    ImageStreamCompleter result = _pendingImages[key]?.completer;\n    // 图片还未加载成功，直接返回\n    if (result != null)\n      return result;\n \n    // 有缓存，继续往下走\n    // 先移除缓存，后再添加，可以让最新使用过的缓存在_map中的位置更近一些，清理时会LRU来清除\n    final _CachedImage image = _cache.remove(key);\n    if (image != null) {\n      _cache[key] = image;\n      return image.completer;\n    }\n    try {\n      result = loader();\n    } catch (error, stackTrace) {\n      if (onError != null) {\n        onError(error, stackTrace);\n        return null;\n      } else {\n        rethrow;\n      }\n    }\n    void listener(ImageInfo info, bool syncCall) {\n      final int imageSize = info?.image == null ? 0 : info.image.height * info.image.width * 4;\n      final _CachedImage image = _CachedImage(result, imageSize);\n      // 下面是缓存处理的逻辑\n      if (maximumSizeBytes > 0 && imageSize > maximumSizeBytes) {\n        _maximumSizeBytes = imageSize + 1000;\n      }\n      _currentSizeBytes += imageSize;\n      final _PendingImage pendingImage = _pendingImages.remove(key);\n      if (pendingImage != null) {\n        pendingImage.removeListener();\n      }\n\n      _cache[key] = image;\n      _checkCacheSize();\n    }\n    if (maximumSize > 0 && maximumSizeBytes > 0) {\n      final ImageStreamListener streamListener = ImageStreamListener(listener);\n      _pendingImages[key] = _PendingImage(result, streamListener);\n      // Listener is removed in [_PendingImage.removeListener].\n      result.addListener(streamListener);\n    }\n    return result;\n  }\n\n  // 当缓存数量超过最大值或缓存的大小超过最大缓存容量，会调用此方法清理到缓存上限以内\n  void _checkCacheSize() {\n   while (_currentSizeBytes > _maximumSizeBytes || _cache.length > _maximumSize) {\n      final Object key = _cache.keys.first;\n      final _CachedImage image = _cache[key];\n      _currentSizeBytes -= image.sizeBytes;\n      _cache.remove(key);\n    }\n    ... //省略无关代码\n  }\n}\n```\n\n有缓存则使用缓存，没有缓存则调用load方法加载图片，加载成功后:\n\n1. 先判断图片数据有没有缓存，如果有，则直接返回`ImageStream`。\n2. 如果没有缓存，则调用`load(T key)`方法从数据源加载图片数据，加载成功后先缓存，然后返回ImageStream。\n\n另外，我们可以看到`ImageCache`类中有设置缓存上限的setter，所以，如果我们可以自定义缓存上限：\n\n```dart\n PaintingBinding.instance.imageCache.maximumSize=2000; //最多2000张\n PaintingBinding.instance.imageCache.maximumSizeBytes = 200 << 20; //最大200M\n```\n\n现在我们看一下缓存的key，因为Map中相同key的值会被覆盖，也就是说key是图片缓存的一个唯一标识，只要是不同key，那么图片数据就会分别缓存（即使事实上是同一张图片）。那么图片的唯一标识是什么呢？跟踪源码，很容易发现key正是`ImageProvider.obtainKey()`方法的返回值，而此方法需要`ImageProvider`子类去重写，这也就意味着不同的`ImageProvider`对key的定义逻辑会不同。其实也很好理解，比如对于`NetworkImage`，将图片的url作为key会很合适，而对于`AssetImage`，则应该将“包名+路径”作为唯一的key。下面我们以`NetworkImage`为例，看一下它的`obtainKey()`实现：\n\n```dart\n@override\nFuture<NetworkImage> obtainKey(image_provider.ImageConfiguration configuration) {\n  return SynchronousFuture<NetworkImage>(this);\n}\n```\n\n代码很简单，创建了一个同步的future，然后直接将自身做为key返回。因为Map中在判断key（此时是`NetworkImage`对象）是否相等时会使用“==”运算符，那么定义key的逻辑就是`NetworkImage`的“==”运算符：\n\n```dart\n@override\nbool operator ==(dynamic other) {\n  ... //省略无关代码\n  final NetworkImage typedOther = other;\n  return url == typedOther.url\n      && scale == typedOther.scale;\n}\n```\n\n很清晰，对于网络图片来说，会将其“url+缩放比例”作为缓存的key。也就是说**如果两张图片的url或scale只要有一个不同，便会重新下载并分别缓存**。\n\n另外，我们需要注意的是，图片缓存是在内存中，并没有进行本地文件持久化存储，这也是为什么网络图片在应用重启后需要重新联网下载的原因。\n\n同时也意味着在应用生命周期内，如果缓存没有超过上限，相同的图片只会被下载一次。\n\n### 总结\n\n上面主要结合源码，探索了`ImageProvider`的主要功能和原理，如果要用一句话来总结`ImageProvider`功能，那么应该是：加载图片数据并进行缓存、解码。在此再次提醒读者，Flutter的源码是非常好的第一手资料，建议读者多多探索，另外，在阅读源码学习的同时一定要有总结，这样才不至于在源码中迷失。\n\n## 14.5.2 Image组件原理\n\n前面章节中我们介绍过`Image`的基础用法，现在我们更深入一些，研究一下`Image`是如何和`ImageProvider`配合来获取最终解码后的数据，然后又如何将图片绘制到屏幕上的。\n\n本节换一个思路，我们先不去直接看`Image`的源码，而根据已经掌握的知识来实现一个简版的“`Image`组件” `MyImage`，代码大致如下：\n\n```dart\nclass MyImage extends StatefulWidget {\n  const MyImage({\n    Key key,\n    @required this.imageProvider,\n  })\n      : assert(imageProvider != null),\n        super(key: key);\n\n  final ImageProvider imageProvider;\n\n  @override\n  _MyImageState createState() => _MyImageState();\n}\n\nclass _MyImageState extends State<MyImage> {\n  ImageStream _imageStream;\n  ImageInfo _imageInfo;\n\n  @override\n  void didChangeDependencies() {\n    super.didChangeDependencies();\n    // 依赖改变时，图片的配置信息可能会发生改变\n    _getImage();\n  }\n\n  @override\n  void didUpdateWidget(MyImage oldWidget) {\n    super.didUpdateWidget(oldWidget);\n    if (widget.imageProvider != oldWidget.imageProvider)\n      _getImage();\n  }\n\n  void _getImage() {\n    final ImageStream oldImageStream = _imageStream;\n    // 调用imageProvider.resolve方法，获得ImageStream。\n    _imageStream =\n        widget.imageProvider.resolve(createLocalImageConfiguration(context));\n    //判断新旧ImageStream是否相同，如果不同，则需要调整流的监听器\n    if (_imageStream.key != oldImageStream?.key) {\n      final ImageStreamListener listener = ImageStreamListener(_updateImage);\n      oldImageStream?.removeListener(listener);\n      _imageStream.addListener(listener);\n    }\n  }\n\n  void _updateImage(ImageInfo imageInfo, bool synchronousCall) {\n    setState(() {\n      // Trigger a build whenever the image changes.\n      _imageInfo = imageInfo;\n    });\n  }\n\n  @override\n  void dispose() {\n    _imageStream.removeListener(ImageStreamListener(_updateImage));\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return RawImage(\n      image: _imageInfo?.image, // this is a dart:ui Image object\n      scale: _imageInfo?.scale ?? 1.0,\n    );\n  }\n}\n```\n\n\n\n上面代码流程如下：\n\n1. 通过`imageProvider.resolve`方法可以得到一个`ImageStream`（图片数据流），然后监听`ImageStream`的变化。当图片数据源发生变化时，`ImageStream`会触发相应的事件，而本例中我们只设置了图片成功的监听器`_updateImage`，而`_updateImage`中只更新了`_imageInfo`。值得注意的是，如果是静态图，`ImageStream`只会触发一次时间，如果是动态图，则会触发多次事件，每一次都会有一个解码后的图片帧。\n2. `_imageInfo` 更新后会rebuild，此时会创建一个`RawImage` Widget。`RawImage`最终会通过`RenderImage`来将图片绘制在屏幕上。如果继续跟进`RenderImage`类，我们会发现`RenderImage`的`paint` 方法中调用了`paintImage`方法，而`paintImage`方法中通过`Canvas`的`drawImageRect(…)`、`drawImageNine(...)`等方法来完成最终的绘制。\n3. 最终的绘制由`RawImage`来完成。\n\n下面测试一下`MyImage`：\n\n```dart\nclass ImageInternalTestRoute extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return Column(\n      children: <Widget>[\n        MyImage(\n          imageProvider: NetworkImage(\n            \"https://avatars2.githubusercontent.com/u/20411648?s=460&v=4\",\n          ),\n        )\n      ],\n    );\n  }\n}\n```\n\n运行效果如图14-4所示：\n\n![图14-4](../imgs/14-4.png)\n\n成功了！ 现在，想必`Image` Widget的源码已经没必要在花费篇章去介绍了，读者有兴趣可以自行去阅读。\n\n\n\n## 总结\n\n本节主要介绍了Flutter 图片的加载、缓存和绘制流程。其中`ImageProvider`主要负责图片数据的加载和缓存，而绘制部分逻辑主要是由`RawImage`来完成。 而`Image`正是连接起`ImageProvider`和`RawImage` 的桥梁。\n\n\n\n\n\n\n\n"
  },
  {
    "path": "src/v2/chapter14/index.md",
    "content": "# 本章目录\n* [Flutter UI系统](flutter_ui_system.md)\n* [Element和BuildContext](element_buildcontext.md)\n* [RenderObject和RenderBox](render_object.md)\n* [Flutter从启动到显示](flutter_app_startup.md)\n"
  },
  {
    "path": "src/v2/chapter14/render_object.md",
    "content": "# 14.3 RenderObject和RenderBox\n\n在上一节我们说过每个`Element`都对应一个`RenderObject`，我们可以通过`Element.renderObject` 来获取。并且我们也说过`RenderObject`的主要职责是Layout和绘制，所有的`RenderObject`会组成一棵渲染树Render Tree。本节我们将重点介绍一下`RenderObject`的作用。\n\n`RenderObject`就是渲染树中的一个对象，它拥有一个`parent`和一个`parentData` 插槽（slot），所谓插槽，就是指预留的一个接口或位置，这个接口和位置是由其它对象来接入或占据的，这个接口或位置在软件中通常用预留变量来表示，而`parentData`正是一个预留变量，它正是由`parent` 来赋值的，`parent`通常会通过子`RenderObject`的`parentData`存储一些和子元素相关的数据，如在Stack布局中，`RenderStack`就会将子元素的偏移数据存储在子元素的`parentData`中（具体可以查看`Positioned`实现）。\n\n`RenderObject`类本身实现了一套基础的layout和绘制协议，但是并没有定义子节点模型（如一个节点可以有几个子节点，没有子节点？一个？两个？或者更多？）。 它也没有定义坐标系统（如子节点定位是在笛卡尔坐标中还是极坐标？）和具体的布局协议（是通过宽高还是通过constraint和size?，或者是否由父节点在子节点布局之前或之后设置子节点的大小和位置等）。为此，Flutter提供了一个`RenderBox`类，它继承自``RenderObject`，布局坐标系统采用笛卡尔坐标系，这和Android和iOS原生坐标系是一致的，都是屏幕的top、left是原点，然后分宽高两个轴，大多数情况下，我们直接使用`RenderBox`就可以了，除非遇到要自定义布局模型或坐标系统的情况，下面我们重点介绍一下`RenderBox`。\n\n## 14.3.1 布局过程\n\n### Constraints\n\n在`RenderBox` 中，有个`size`属性用来保存控件的宽和高。`RenderBox`的layout是通过在组件树中从上往下传递`BoxConstraints`对象的实现的。`BoxConstraints`对象可以限制子节点的最大和最小宽高，子节点必须遵守父节点给定的限制条件。\n\n在布局阶段，父节点会调用子节点的`layout()`方法，下面我们看看`RenderObject`中`layout()`方法的大致实现（删掉了一些无关代码和异常捕获）:\n\n```dart\nvoid layout(Constraints constraints, { bool parentUsesSize = false }) {\n   ...\n   RenderObject relayoutBoundary; \n    if (!parentUsesSize || sizedByParent || constraints.isTight \n    \t|| parent is! RenderObject) {\n      relayoutBoundary = this;\n    } else {\n      final RenderObject parent = this.parent;\n      relayoutBoundary = parent._relayoutBoundary;\n    }\n    ...\n    if (sizedByParent) {\n        performResize();\n    }\n    performLayout();\n    ...\n}\n```\n\n可以看到`layout`方法需要传入两个参数，第一个为`constraints`，即 父节点对子节点大小的限制，该值根据父节点的布局逻辑确定。另外一个参数是 `parentUsesSize`，该值用于确定 `relayoutBoundary`，该参数表示子节点布局变化是否影响父节点，如果为`true`，当子节点布局发生变化时父节点都会标记为需要重新布局，如果为`false`，则子节点布局发生变化后不会影响父节点。\n\n#### relayoutBoundary\n\n上面`layout()`源码中定义了一个`relayoutBoundary`变量，什么是 `relayoutBoundary`？在前面介绍`Element`时，我们讲过当一个`Element`标记为 dirty 时便会重新build，这时`RenderObject`便会重新布局，我们是通过调用 `markNeedsBuild()` 来标记`Element`为dirty的。在`RenderObject`中有一个类似的`markNeedsLayout()`方法，它会将`RenderObject`的布局状态标记为 dirty，这样在下一个frame中便会重新layout，我们看看`RenderObject`的`markNeedsLayout()`的部分源码：\n\n```dart\nvoid markNeedsLayout() {\n  ...\n  assert(_relayoutBoundary != null);\n  if (_relayoutBoundary != this) {\n    markParentNeedsLayout();\n  } else {\n    _needsLayout = true;\n    if (owner != null) {\n      ...\n      owner._nodesNeedingLayout.add(this);\n      owner.requestVisualUpdate();\n    }\n  }\n}\n```\n\n代码大致逻辑是先判断自身是不是`relayoutBoundary`，如果不是就继续向parent 查找，一直向上查找到是 `relayoutBoundary` 的 `RenderObject`为止，然后再将其标记为 dirty 的。这样来看它的作用就比较明显了，意思就是当一个控件的大小被改变时可能会影响到它的 parent，因此 parent 也需要被重新布局，那么到什么时候是个头呢？答案就是 `relayoutBoundary`，如果一个 `RenderObject` 是 `relayoutBoundary`，就表示它的大小变化不会再影响到 parent 的大小了，于是 parent 也就不用重新布局了。\n\n#### performResize 和 performLayout\n\n`RenderBox`实际的测量和布局逻辑是在`performResize()` 和 `performLayout()`两个方法中，RenderBox子类需要实现这两个方法来定制自身的布局逻辑。根据`layout()` 源码可以看出只有 `sizedByParent` 为 `true` 时，`performResize()` 才会被调用，而 `performLayout()` 是每次布局都会被调用的。`sizedByParent` 意为该节点的大小是否仅通过 parent 传给它的 constraints 就可以确定了，即该节点的大小与它自身的属性和其子节点无关，比如如果一个控件永远充满 parent 的大小，那么 `sizedByParent `就应该返回` true`，此时其大小在 `performResize()` 中就确定了，在后面的 `performLayout()` 方法中将不会再被修改了，这种情况下 `performLayout()` 只负责布局子节点。\n\n在 `performLayout()` 方法中除了完成自身布局，也必须完成子节点的布局，这是因为只有父子节点全部完成后布局流程才算真正完成。所以最终的调用栈将会变成：*layout() > performResize()/performLayout() > child.layout() > ...*  ，如此递归完成整个UI的布局。\n\n`RenderBox`子类要定制布局算法不应该重写`layout()`方法，因为对于任何RenderBox的子类来说，它的layout流程基本是相同的，不同之处只在具体的布局算法，而具体的布局算法子类应该通过重写`performResize()` 和 `performLayout()`两个方法来实现，他们会在`layout()`中被调用。\n\n#### ParentData\n\n当layout结束后，每个节点的位置（相对于父节点的偏移）就已经确定了，`RenderObject`就可以根据位置信息来进行最终的绘制。但是在layout过程中，节点的位置信息怎么保存？对于大多数`RenderBox`子类来说如果子类只有一个子节点，那么子节点偏移一般都是`Offset.zero` ，如果有多个子节点，则每个子节点的偏移就可能不同。而子节点在父节点的偏移数据正是通过`RenderObject`的`parentData`属性来保存的。在`RenderBox`中，其`parentData`属性默认是一个`BoxParentData`对象，该属性只能通过父节点的`setupParentData()`方法来设置：\n\n```dart\nabstract class RenderBox extends RenderObject {\n  @override\n  void setupParentData(covariant RenderObject child) {\n    if (child.parentData is! BoxParentData)\n      child.parentData = BoxParentData();\n  }\n  ...\n}\n```\n\n`BoxParentData`定义如下：\n\n```dart\n/// Parentdata 会被RenderBox和它的子类使用.\nclass BoxParentData extends ParentData {\n  /// offset表示在子节点在父节点坐标系中的绘制偏移  \n  Offset offset = Offset.zero;\n\n  @override\n  String toString() => 'offset=$offset';\n}\n```\n\n> 一定要注意，`RenderObject`的`parentData` 只能通过父元素设置.\n\n当然，`ParentData`并不仅仅可以用来存储偏移信息，通常所有和子节点特定的数据都可以存储到子节点的`ParentData`中，如`ContainerBox`的`ParentData`就保存了指向兄弟节点的`previousSibling`和`nextSibling`，`Element.visitChildren()`方法也正是通过它们来实现对子节点的遍历。再比如`KeepAlive` 组件，它使用`KeepAliveParentDataMixin`（继承自`ParentData`） 来保存子节的`keepAlive`状态。\n\n## 14.3.2 绘制过程\n\n`RenderObject`可以通过`paint()`方法来完成具体绘制逻辑，流程和布局流程相似，子类可以实现`paint()`方法来完成自身的绘制逻辑，`paint()`签名如下：\n\n```dart\nvoid paint(PaintingContext context, Offset offset) { }\n```\n\n通过`context.canvas`可以取到`Canvas`对象，接下来就可以调用`Canvas` API来实现具体的绘制逻辑。\n\n如果节点有子节点，它除了完成自身绘制逻辑之外，还要调用子节点的绘制方法。我们以`RenderFlex`对象为例说明：\n\n```dart\n@override\nvoid paint(PaintingContext context, Offset offset) {\n\n  // 如果子元素未超出当前边界，则绘制子元素  \n  if (_overflow <= 0.0) {\n    defaultPaint(context, offset);\n    return;\n  }\n\n  // 如果size为空，则无需绘制\n  if (size.isEmpty)\n    return;\n\n  // 剪裁掉溢出边界的部分\n  context.pushClipRect(needsCompositing, offset, Offset.zero & size, defaultPaint);\n\n  assert(() {\n    final String debugOverflowHints = '...'; //溢出提示内容，省略\n    // 绘制溢出部分的错误提示样式\n    Rect overflowChildRect;\n    switch (_direction) {\n      case Axis.horizontal:\n        overflowChildRect = Rect.fromLTWH(0.0, 0.0, size.width + _overflow, 0.0);\n        break;\n      case Axis.vertical:\n        overflowChildRect = Rect.fromLTWH(0.0, 0.0, 0.0, size.height + _overflow);\n        break;\n    }  \n    paintOverflowIndicator(context, offset, Offset.zero & size,\n                           overflowChildRect, overflowHints: debugOverflowHints);\n    return true;\n  }());\n}\n```\n\n代码很简单，首先判断有无溢出，如果没有则调用`defaultPaint(context, offset)`来完成绘制，该方法源码如下：\n\n```dart\nvoid defaultPaint(PaintingContext context, Offset offset) {\n  ChildType child = firstChild;\n  while (child != null) {\n    final ParentDataType childParentData = child.parentData;\n    //绘制子节点， \n    context.paintChild(child, childParentData.offset + offset);\n    child = childParentData.nextSibling;\n  }\n}\n```\n\n很明显，由于Flex本身没有需要绘制的东西，所以直接遍历其子节点，然后调用`paintChild()`来绘制子节点，同时将子节点`ParentData`中在layout阶段保存的offset加上自身偏移作为第二个参数传递给`paintChild()`。而如果子节点还有子节点时，`paintChild()`方法还会调用子节点的`paint()`方法，如此递归完成整个节点树的绘制，最终调用栈为： *paint() > paintChild() > paint() ...* 。\n\n当需要绘制的内容大小溢出当前空间时，将会执行`paintOverflowIndicator()` 来绘制溢出部分提示，这个就是我们经常看到的溢出提示，如图14-3所示：\n\n![overflow](../imgs/14-3.png)\n\n### RepaintBoundary\n\n我们已经在`CustomPaint`一节中介绍过`RepaintBoundary`，现在我们深入的了解一些。与 `RelayoutBoundary` 相似，`RepaintBoundary`是用于在确定重绘边界的，与`RelayoutBoundary`不同的是，这个绘制边界需要由开发者通过`RepaintBoundary` 组件自己指定，如：\n\n```dart\nCustomPaint(\n  size: Size(300, 300), //指定画布大小\n  painter: MyPainter(),\n  child: RepaintBoundary(\n    child: Container(...),\n  ),\n),\n```\n\n下面我们看看`RepaintBoundary`的原理，`RenderObject`有一个`isRepaintBoundary `属性，该属性决定这个`RenderObject`重绘时是否独立于其父元素，如果该属性值为`true` ，则独立绘制，反之则一起绘制。那独立绘制是怎么实现的呢？ 答案就在`paintChild()`源码中：\n\n```dart\nvoid paintChild(RenderObject child, Offset offset) {\n  ...\n  if (child.isRepaintBoundary) {\n    stopRecordingIfNeeded();\n    _compositeChild(child, offset);\n  } else {\n    child._paintWithContext(this, offset);\n  }\n  ...\n}\n```\n\n我们可以看到，在绘制子节点时，如果`child.isRepaintBoundary` 为 `true`则会调用`_compositeChild()`方法，`_compositeChild()`源码如下：\n\n```dart\nvoid _compositeChild(RenderObject child, Offset offset) {\n  // 给子节点创建一个layer ，然后再上面绘制子节点 \n  if (child._needsPaint) {\n    repaintCompositedChild(child, debugAlsoPaintedParent: true);\n  } else {\n    ...\n  }\n  assert(child._layer != null);\n  child._layer.offset = offset;\n  appendLayer(child._layer);\n}\n```\n\n很明显了，独立绘制是通过在不同的layer（层）上绘制的。所以，很明显，正确使用`isRepaintBoundary`属性可以提高绘制效率，避免不必要的重绘。具体原理是：和触发重新build和layout类似，`RenderObject`也提供了一个`markNeedsPaint()`方法，其源码如下：\n\n```dart\nvoid markNeedsPaint() {\n ...\n  //如果RenderObject.isRepaintBoundary 为true,则该RenderObject拥有layer，直接绘制  \n  if (isRepaintBoundary) {\n    ...\n    if (owner != null) {\n      //找到最近的layer，绘制  \n      owner._nodesNeedingPaint.add(this);\n      owner.requestVisualUpdate();\n    }\n  } else if (parent is RenderObject) {\n    // 没有自己的layer, 会和一个祖先节点共用一个layer  \n    assert(_layer == null);\n    final RenderObject parent = this.parent;\n    // 向父级递归查找  \n    parent.markNeedsPaint();\n    assert(parent == this.parent);\n  } else {\n    // 如果直到根节点也没找到一个Layer，那么便需要绘制自身，因为没有其它节点可以绘制根节点。  \n    if (owner != null)\n      owner.requestVisualUpdate();\n  }\n}\n```\n\n可以看出，当调用 `markNeedsPaint()` 方法时，会从当前 `RenderObject` 开始一直向父节点查找，直到找到 一个`isRepaintBoundary` 为 `true`的`RenderObject` 时，才会触发重绘，这样便可以实现局部重绘。当 有`RenderObject` 绘制的很频繁或很复杂时，可以通过RepaintBoundary Widget来指定`isRepaintBoundary` 为 `true`，这样在绘制时仅会重绘自身而无需重绘它的 parent，如此便可提高性能。\n\n还有一个问题，通过`RepaintBoundary` 如何设置`isRepaintBoundary`属性呢？其实，如果使用了`RepaintBoundary`，其对应的`RenderRepaintBoundary`会自动将`isRepaintBoundary`设为`true`的：\n\n```dart\nclass RenderRepaintBoundary extends RenderProxyBox {\n  /// Creates a repaint boundary around [child].\n  RenderRepaintBoundary({ RenderBox child }) : super(child);\n\n  @override\n  bool get isRepaintBoundary => true;\n}\n```\n\n\n\n## 14.3.3 命中测试\n\n我们在“事件处理与通知”一章中已经讲过Flutter事件机制和命中测试流程，本节我们看一下其内部实现原理。\n\n一个对象是否可以响应事件，取决于其对命中测试的返回，当发生用户事件时，会从根节点（`RenderView`）开始进行命中测试，下面是`RenderView`的`hitTest()`源码：\n\n```dart\nbool hitTest(HitTestResult result, { Offset position }) {\n  if (child != null)\n    child.hitTest(result, position: position); //递归子RenderBox进行命中测试\n  result.add(HitTestEntry(this)); //将测试结果添加到result中\n  return true;\n}\n```\n\n我们再看看`RenderBox`默认的`hitTest()`实现：\n\n```dart\nbool hitTest(HitTestResult result, { @required Offset position }) {\n  ...  \n  if (_size.contains(position)) {\n    if (hitTestChildren(result, position: position) || hitTestSelf(position)) {\n      result.add(BoxHitTestEntry(this, position));\n      return true;\n    }\n  }\n  return false;\n}\n```\n\n我们看到默认的实现里调用了`hitTestSelf()`和`hitTestChildren()`两个方法，这两个方法默认实现如下：\n\n```dart\n \n@protected\nbool hitTestSelf(Offset position) => false;\n \n@protected\nbool hitTestChildren(HitTestResult result, { Offset position }) => false;\n```\n\n`hitTest` 方法用来判断该` RenderObject` 是否在被点击的范围内，同时负责将被点击的 `RenderBox` 添加到 `HitTestResult` 列表中，参数 `position` 为事件触发的坐标（如果有的话），返回 true 则表示有` RenderBox` 通过了命中测试，需要响应事件，反之则认为当前`RenderBox`没有命中。在继承`RenderBox`时，可以直接重写`hitTest()`方法，也可以重写 `hitTestSelf()` 或 `hitTestChildren()`, 唯一不同的是 `hitTest()`中需要将通过命中测试的节点信息添加到命中测试结果列表中，而 `hitTestSelf()` 和 `hitTestChildren()`则只需要简单的返回`true`或`false`。\n\n## 14.3.4 语义化\n\n语义化即Semantics，主要是提供给读屏软件的接口，也是实现辅助功能的基础，通过语义化接口可以让机器理解页面上的内容，对于有视力障碍用户可以使用读屏软件来理解UI内容。如果一个`RenderObject`要支持语义化接口，可以实现 `describeApproximatePaintClip`和 `visitChildrenForSemantics`方法和`semanticsAnnotator` getter。更多关于语义化的信息可以查看API文档。\n\n## 14.3.5 总结\n\n本节我们介绍了`RenderObject`主要的功能和方法，理解这些内容可以帮助我们更好的理解Flutter UI底层原理。我们也可以看到，如果要从头到尾实现一个`RenderObject`是比较麻烦的，我们必须去实现layout、绘制和命中测试逻辑，但是值得庆幸的是，大多数时候我们可以直接在Widget层通过组合或者`CustomPaint`完成自定义UI。如果遇到只能定义一个新`RenderObject`的场景时（如要实现一个新的layout算法的布局容器），可以直接继承自`RenderBox`，这样可以帮我们减少一部分工作。\n\n"
  },
  {
    "path": "src/v2/chapter15/code_structure.md",
    "content": "# 15.2 Flutter APP代码结构\n\n我们先来创建一个全新的Flutter工程，命名为\"github_client_app\"；创建新工程的步骤视读者使用的编辑器而定，都比较简单，在此不再赘述。创建完成后，工程结构如下：\n\n```shell\ngithub_client_app\n├── android\n├── ios\n├── lib\n└── test\n```\n\n由于我们需要使用外部图片和Icon资源，所以我们在项目根目录下分别创建“imgs”和“fonts”文件夹，前者用于保存图片，后者用于保存Icon文件。关于图片和Icon，读者可以参考第三章中相应的内容。\n\n由于在网络数据传输和持久化时，我们需要通过Json来传输、保存数据；但是在应用开发时我们又需要将Json转成Dart Model类，现在我们使用在第十一章中“Json转Model”小节中介绍的方案，所以，我们需要在根目录下再创建一个用于保存Json文件的“jsons”文件夹。\n\n多语言支持我们使用第十三章“国际化”中介绍的方案，所以还需要在根目录下创建一个“l10n”文件夹，用于保存各国语言对应的arb文件。\n\n现在工程目录变为：\n\n```shell\ngithub_client_app\n├── android\n├── fonts\n├── l10n-arb\n├── imgs\n├── ios\n├── jsons\n├── lib\n└── test\n```\n\n由于我们的Dart代码都在“lib”文件夹下，笔者根据技术选型和经验在lib文件下创建了如下目录：\n\n```shell\nlib\n├── common\n├── l10n\n├── models\n├── states\n├── routes\n└── widgets \n```\n\n| 文件夹  | 作用                                                         |\n| ------- | ------------------------------------------------------------ |\n| common  | 一些工具类，如通用方法类、网络接口类、保存全局变量的静态类等 |\n| l10n    | 国际化相关的类都在此目录下                                   |\n| models  | Json文件对应的Dart Model类会在此目录下                       |\n| states  | 保存APP中需要跨组件共享的状态类                              |\n| routes  | 存放所有路由页面类                                           |\n| widgets | APP内封装的一些Widget组件都在该目录下                        |\n\n注意，使用不同的框架或技术选型会对代码有不同的组织方式，因此，本节介绍的代码组织结构并不是固定或者“最佳”的，在实战中，读者可以自己根据情况调整源码结构。但是无论采取何种源码组织结构，清晰和解耦都是一个通用原则，我们应该让自己的代码结构清晰，以便交流和维护。\n\n\n\n\n\n\n\n"
  },
  {
    "path": "src/v2/chapter15/entry.md",
    "content": "# 15.6 APP入口及主页\n\n本节来介绍一下APP入口及首页。\n\n## 15.6.1 APP入口\n\n`main`函数为APP入口函数，实现如下：\n\n```dart\nvoid main() => Global.init().then((e) => runApp(MyApp()));\n```\n\n初始化完成后才会加载UI(`MyApp`)，`MyApp` 是应用的入口Widget，实现如下：\n\n```dart\nclass MyApp extends StatelessWidget {\n  // This widget is the root of your application.\n  @override\n  Widget build(BuildContext context) {\n    return MultiProvider(\n      providers: <SingleChildCloneableWidget>[\n        ChangeNotifierProvider.value(value: ThemeModel()),\n        ChangeNotifierProvider.value(value: UserModel()),\n        ChangeNotifierProvider.value(value: LocaleModel()),\n      ],\n      child: Consumer2<ThemeModel, LocaleModel>(\n        builder: (BuildContext context, themeModel, localeModel, Widget child) {\n          return MaterialApp(\n            theme: ThemeData(\n              primarySwatch: themeModel.theme,\n            ),\n            onGenerateTitle: (context){\n              return GmLocalizations.of(context).title;\n            },\n            home: HomeRoute(), //应用主页\n            locale: localeModel.getLocale(),\n            //我们只支持美国英语和中文简体\n            supportedLocales: [\n              const Locale('en', 'US'), // 美国英语\n              const Locale('zh', 'CN'), // 中文简体\n              //其它Locales\n            ],\n            localizationsDelegates: [\n              // 本地化的代理类\n              GlobalMaterialLocalizations.delegate,\n              GlobalWidgetsLocalizations.delegate,\n              GmLocalizationsDelegate()\n            ],\n            localeResolutionCallback:\n                (Locale _locale, Iterable<Locale> supportedLocales) {\n              if (localeModel.getLocale() != null) {\n                //如果已经选定语言，则不跟随系统\n                return localeModel.getLocale();\n              } else {\n         \n                Locale locale;\n                //APP语言跟随系统语言，如果系统语言不是中文简体或美国英语，\n                //则默认使用美国英语\n                if (supportedLocales.contains(_locale)) {\n                  locale= _locale;\n                } else {\n                  locale= Locale('en', 'US');\n                }\n                return locale;\n              }\n            },\n            // 注册命名路由表\n            routes: <String, WidgetBuilder>{\n              \"login\": (context) => LoginRoute(),\n              \"themes\": (context) => ThemeChangeRoute(),\n              \"language\": (context) => LanguageRoute(),\n            },\n          );\n        },\n      ),\n    );\n  }\n}\n```\n\n在上面的代码中：\n\n1. 我们的根widget是`MultiProvider`，它将主题、用户、语言三种状态绑定到了应用的根上，如此一来，任何路由中都可以通过`Provider.of()`来获取这些状态，也就是说这三种状态是全局共享的！\n2. `HomeRoute`是应用的主页。\n3. 在构建`MaterialApp`时，我们配置了APP支持的语言列表，以及监听了系统语言改变事件；另外`MaterialApp`消费（依赖）了`ThemeModel`和`LocaleModel`，所以当APP主题或语言改变时`MaterialApp`会重新构建\n4. 我们注册了命名路由表，以便在APP中可以直接通过路由名跳转。\n5. 为了支持多语言（本APP中我们支持美国英语和中文简体两种语言）我们实现了一个`GmLocalizationsDelegate`，子Widget中都可以通过`GmLocalizations`来动态获取APP当前语言对应的文案。关于`GmLocalizationsDelegate`和`GmLocalizations`的实现方式读者可以参考“国际化”一章中的介绍，此处不再赘述。\n\n## 15.6.2 主页\n\n为了简单起见，当APP启动后，如果之前已登录了APP，则显示该用户项目列表；如果之前未登录，则显示一个登录按钮，点击后跳转到登录页。另外，我们实现一个抽屉菜单，里面包含当前用户头像及APP的菜单。下面我们先看看要实现的效果，如图15-1、15-2所示：\n\n![15-1](../imgs/15-1.png)![15-2](../imgs/15-2.png)\n\n我们在“lib/routes”下创建一个“home_page.dart”文件，实现如下：\n\n```dart\nclass HomeRoute extends StatefulWidget {\n  @override\n  _HomeRouteState createState() => _HomeRouteState();\n}\n\nclass _HomeRouteState extends State<HomeRoute> {\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        title: Text(GmLocalizations.of(context).home),\n      ),\n      body: _buildBody(), // 构建主页面\n      drawer: MyDrawer(), //抽屉菜单\n    );\n  }\n  ...// 省略\n}\n```\n\n上面代码中，主页的标题（title）我们是通过`GmLocalizations.of(context).home`来获得，`GmLocalizations`是我们提供的一个`Localizations`类，用于支持多语言，因此当APP语言改变时，凡是使用`GmLocalizations`动态获取的文案都会是相应语言的文案，这在前面“国际化”一章中已经介绍过，读者可以前翻查阅。\n\n我们通过 `_buildBody()`方法来构建主页内容，`_buildBody()`方法实现代码如下：\n\n```dart\n  Widget _buildBody() {\n    UserModel userModel = Provider.of<UserModel>(context);\n    if (!userModel.isLogin) {\n      //用户未登录，显示登录按钮\n      return Center(\n        child: RaisedButton(\n          child: Text(GmLocalizations.of(context).login),\n          onPressed: () => Navigator.of(context).pushNamed(\"login\"),\n        ),\n      );\n    } else {\n      //已登录，则展示项目列表\n      return InfiniteListView<Repo>(\n        onRetrieveData: (int page, List<Repo> items, bool refresh) async {\n          var data = await Git(context).getRepos(\n            refresh: refresh,\n            queryParameters: {\n              'page': page,\n              'page_size': 20,\n            },\n          );\n          //把请求到的新数据添加到items中\n          items.addAll(data); \n          // 如果接口返回的数量等于'page_size'，则认为还有数据，反之则认为最后一页\n          return data.length==20;\n        },\n        itemBuilder: (List list, int index, BuildContext ctx) {\n          // 项目信息列表项\n          return RepoItem(list[index]);\n        },\n      );\n    }\n  }\n}\n```\n\n上面代码注释很清楚：如果用户未登录，显示登录按钮；如果用户已登录，则展示项目列表。这里项目列表使用了`InfiniteListView` Widget，它是flukit package中提供的。`InfiniteListView`同时支持了下拉刷新和上拉加载更多两种功能。`onRetrieveData` 为数据获取回调，该回调函数接收三个参数：\n\n| 参数名  | 类型    | 解释                   |\n| ------- | ------- | ---------------------- |\n| page    | int     | 当前页号               |\n| items   | List<T> | 保存当前列表数据的List |\n| refresh | bool    | 是否是下拉刷新触发     |\n\n返回值类型为`bool`，为`true`时表示还有数据，为`false`时则表示后续没有数据了。`onRetrieveData` 回调中我们调用`Git(context).getRepos(...)`来获取用户项目列表，同时指定每次请求获取20条。当获取成功时，首先要将新获取的项目数据添加到`items`中，然后根据本次请求的项目条数是否等于期望的20条来判断还有没有更多的数据。在此需要注意，`Git(context).getRepos(…)`方法中需要`refresh`参数来判断是否使用缓存。\n\n`itemBuilder`为列表项的builder，我们需要在该回调中构建每一个列表项Widget。由于列表项构建逻辑较复杂，我们单独封装一个`RepoItem` Widget 专门用于构建列表项UI。`RepoItem` 实现如下：\n\n```dart\nimport '../index.dart';\n\nclass RepoItem extends StatefulWidget {\n  // 将`repo.id`作为RepoItem的默认key\n  RepoItem(this.repo) : super(key: ValueKey(repo.id));\n\n  final Repo repo;\n\n  @override\n  _RepoItemState createState() => _RepoItemState();\n}\n\nclass _RepoItemState extends State<RepoItem> {\n  @override\n  Widget build(BuildContext context) {\n    var subtitle;\n    return Padding(\n      padding: const EdgeInsets.only(top: 8.0),\n      child: Material(\n        color: Colors.white,\n        shape: BorderDirectional(\n          bottom: BorderSide(\n            color: Theme.of(context).dividerColor,\n            width: .5,\n          ),\n        ),\n        child: Padding(\n          padding: const EdgeInsets.only(top: 0.0, bottom: 16),\n          child: Column(\n            crossAxisAlignment: CrossAxisAlignment.start,\n            children: <Widget>[\n              ListTile(\n                dense: true,\n                leading: gmAvatar(\n                  //项目owner头像\n                  widget.repo.owner.avatar_url,\n                  width: 24.0,\n                  borderRadius: BorderRadius.circular(12),\n                ),\n                title: Text(\n                  widget.repo.owner.login,\n                  textScaleFactor: .9,\n                ),\n                subtitle: subtitle,\n                trailing: Text(widget.repo.language ?? \"\"),\n              ),\n              // 构建项目标题和简介\n              Padding(\n                padding: const EdgeInsets.symmetric(horizontal: 16.0),\n                child: Column(\n                  crossAxisAlignment: CrossAxisAlignment.start,\n                  children: <Widget>[\n                    Text(\n                      widget.repo.fork\n                          ? widget.repo.full_name\n                          : widget.repo.name,\n                      style: TextStyle(\n                        fontSize: 15,\n                        fontWeight: FontWeight.bold,\n                        fontStyle: widget.repo.fork\n                            ? FontStyle.italic\n                            : FontStyle.normal,\n                      ),\n                    ),\n                    Padding(\n                      padding: const EdgeInsets.only(top: 8, bottom: 12),\n                      child: widget.repo.description == null\n                          ? Text(\n                              GmLocalizations.of(context).noDescription,\n                              style: TextStyle(\n                                  fontStyle: FontStyle.italic,\n                                  color: Colors.grey[700]),\n                            )\n                          : Text(\n                              widget.repo.description,\n                              maxLines: 3,\n                              style: TextStyle(\n                                height: 1.15,\n                                color: Colors.blueGrey[700],\n                                fontSize: 13,\n                              ),\n                            ),\n                    ),\n                  ],\n                ),\n              ),\n              // 构建卡片底部信息\n              _buildBottom()\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n\n  // 构建卡片底部信息\n  Widget _buildBottom() {\n    const paddingWidth = 10;\n    return IconTheme(\n      data: IconThemeData(\n        color: Colors.grey,\n        size: 15,\n      ),\n      child: DefaultTextStyle(\n        style: TextStyle(color: Colors.grey, fontSize: 12),\n        child: Padding(\n          padding: const EdgeInsets.symmetric(horizontal: 16),\n          child: Builder(builder: (context) {\n            var children = <Widget>[\n              Icon(Icons.star),\n              Text(\" \" +\n                  widget.repo.stargazers_count\n                      .toString()\n                      .padRight(paddingWidth)),\n              Icon(Icons.info_outline),\n              Text(\" \" +\n                  widget.repo.open_issues_count\n                      .toString()\n                      .padRight(paddingWidth)),\n\n              Icon(MyIcons.fork), //我们的自定义图标\n              Text(widget.repo.forks_count.toString().padRight(paddingWidth)),\n            ];\n\n            if (widget.repo.fork) {\n              children.add(Text(\"Forked\".padRight(paddingWidth)));\n            }\n\n            if (widget.repo.private == true) {\n              children.addAll(<Widget>[\n                Icon(Icons.lock),\n                Text(\" private\".padRight(paddingWidth))\n              ]);\n            }\n            return Row(children: children);\n          }),\n        ),\n      ),\n    );\n  }\n}\n```\n\n上面代码有两点需要注意：\n\n1. 在构建项目拥有者头像时调用了`gmAvatar(…)`方法，该方法是是一个全局工具函数，专门用于获取头像图片，实现如下：\n\n   ```dart\n   Widget gmAvatar(String url, {\n     double width = 30,\n     double height,\n     BoxFit fit,\n     BorderRadius borderRadius,\n   }) {\n     var placeholder = Image.asset(\n         \"imgs/avatar-default.png\", //头像占位图，加载过程中显示\n         width: width,\n         height: height\n     );\n     return ClipRRect(\n       borderRadius: borderRadius ?? BorderRadius.circular(2),\n       child: CachedNetworkImage( \n         imageUrl: url,\n         width: width,\n         height: height,\n         fit: fit,\n         placeholder: (context, url) =>placeholder,\n         errorWidget: (context, url, error) =>placeholder,\n       ),\n     );\n   }\n   ```\n\n   代码中调用了`CachedNetworkImage` 是cached_network_image包中提供的一个Widget，它不仅可以在图片加载过程中指定一个占位图，而且还可以对网络请求的图片进行缓存，更多详情读者可以自行查阅其文档。\n\n2. 由于Flutter 的Material 图标库中没有fork图标，所以我们在iconfont.cn上找了一个fork图标，然后根据“图片和Icon”一节中介绍的使用自定义字体图标的方法集成到了我们的项目中。\n\n## 15.6.3 抽屉菜单\n\n抽屉菜单分为两部分：顶部头像和底部功能菜单项。当用户未登录，则抽屉菜单顶部会显示一个默认的灰色占位图，若用户已登录，则会显示用户的头像。抽屉菜单底部有“换肤”和“语言”两个固定菜单，若用户已登录，则会多一个“注销”菜单。用户点击“换肤”和“语言”两个菜单项，会进入相应的设置页面。我们的抽屉菜单效果如图15-3、15-4所示：\n\n![15-3](../imgs/15-3.png)![15-4](../imgs/15-4.png)\n\n实现代码如下：\n\n```dart\nclass MyDrawer extends StatelessWidget {\n  const MyDrawer({\n    Key key,\n  }) : super(key: key);\n\n  @override\n  Widget build(BuildContext context) {\n    return Drawer(\n      //移除顶部padding\n      child: MediaQuery.removePadding(\n        context: context,\n        removeTop: true,\n        child: Column(\n          crossAxisAlignment: CrossAxisAlignment.start,\n          children: <Widget>[\n            _buildHeader(), //构建抽屉菜单头部\n            Expanded(child: _buildMenus()), //构建功能菜单\n          ],\n        ),\n      ),\n    );\n  }\n\n  Widget _buildHeader() {\n    return Consumer<UserModel>(\n      builder: (BuildContext context, UserModel value, Widget child) {\n        return GestureDetector(\n          child: Container(\n            color: Theme.of(context).primaryColor,\n            padding: EdgeInsets.only(top: 40, bottom: 20),\n            child: Row(\n              children: <Widget>[\n                Padding(\n                  padding: const EdgeInsets.symmetric(horizontal: 16.0),\n                  child: ClipOval(\n                    // 如果已登录，则显示用户头像；若未登录，则显示默认头像\n                    child: value.isLogin\n                        ? gmAvatar(value.user.avatar_url, width: 80)\n                        : Image.asset(\n                            \"imgs/avatar-default.png\",\n                            width: 80,\n                          ),\n                  ),\n                ),\n                Text(\n                  value.isLogin\n                      ? value.user.login\n                      : GmLocalizations.of(context).login,\n                  style: TextStyle(\n                    fontWeight: FontWeight.bold,\n                    color: Colors.white,\n                  ),\n                )\n              ],\n            ),\n          ),\n          onTap: () {\n            if (!value.isLogin) Navigator.of(context).pushNamed(\"login\");\n          },\n        );\n      },\n    );\n  }\n\n  // 构建菜单项\n  Widget _buildMenus() {\n    return Consumer<UserModel>(\n      builder: (BuildContext context, UserModel userModel, Widget child) {\n        var gm = GmLocalizations.of(context);\n        return ListView(\n          children: <Widget>[\n            ListTile(\n              leading: const Icon(Icons.color_lens),\n              title: Text(gm.theme),\n              onTap: () => Navigator.pushNamed(context, \"themes\"),\n            ),\n            ListTile(\n              leading: const Icon(Icons.language),\n              title: Text(gm.language),\n              onTap: () => Navigator.pushNamed(context, \"language\"),\n            ),\n            if(userModel.isLogin) ListTile(\n              leading: const Icon(Icons.power_settings_new),\n              title: Text(gm.logout),\n              onTap: () {\n                showDialog(\n                  context: context,\n                  builder: (ctx) {\n                    //退出账号前先弹二次确认窗\n                    return AlertDialog(\n                      content: Text(gm.logoutTip),\n                      actions: <Widget>[\n                        FlatButton(\n                          child: Text(gm.cancel),\n                          onPressed: () => Navigator.pop(context),\n                        ),\n                        FlatButton(\n                          child: Text(gm.yes),\n                          onPressed: () {\n                            //该赋值语句会触发MaterialApp rebuild\n                            userModel.user = null;\n                            Navigator.pop(context);\n                          },\n                        ),\n                      ],\n                    );\n                  },\n                );\n              },\n            ),\n          ],\n        );\n      },\n    );\n  }\n}\n```\n\n用户点击“注销”，`userModel.user` 会被置空，此时所有依赖`userModel`的组件都会被`rebuild`，如主页会恢复成未登录的状态。\n\n本小节我们介绍了APP入口`MaterialApp`的一些配置，然后实现了APP的首页。后面我们将展示登录页、换肤页、语言切换页。\n"
  },
  {
    "path": "src/v2/chapter15/globals.md",
    "content": "# 15.4 全局变量及共享状态\n\n应用程序中通常会包含一些贯穿APP生命周期的变量信息，这些信息在APP大多数地方可能都会被用到，比如当前用户信息、Local信息等。在Flutter中我们把需要全局共享的信息分为两类：全局变量和共享状态。全局变量就是单纯指会贯穿整个APP生命周期的变量，用于单纯的保存一些信息，或者封装一些全局工具和方法的对象。而共享状态则是指哪些需要跨组件或跨路由共享的信息，这些信息通常也是全局变量，而共享状态和全局变量的不同在于前者发生改变时需要通知所有使用该状态的组件，而后者不需要。为此，我们将全局变量和共享状态分开单独管理。\n\n## 15.4.1 全局变量-Global类\n\n我们在“lib/common”目录下创建一个`Global`类，它主要管理APP的全局变量，定义如下：\n\n```dart\n// 提供五套可选主题色\nconst _themes = <MaterialColor>[\n  Colors.blue,\n  Colors.cyan,\n  Colors.teal,\n  Colors.green,\n  Colors.red,\n];\n\nclass Global {\n  static SharedPreferences _prefs;\n  static Profile profile = Profile();\n  // 网络缓存对象\n  static NetCache netCache = NetCache();\n\n  // 可选的主题列表\n  static List<MaterialColor> get themes => _themes;\n\n  // 是否为release版\n  static bool get isRelease => bool.fromEnvironment(\"dart.vm.product\");\n\n  //初始化全局信息，会在APP启动时执行\n  static Future init() async {\n    _prefs = await SharedPreferences.getInstance();\n    var _profile = _prefs.getString(\"profile\");\n    if (_profile != null) {\n      try {\n        profile = Profile.fromJson(jsonDecode(_profile));\n      } catch (e) {\n        print(e);\n      }\n    }\n\n    // 如果没有缓存策略，设置默认缓存策略\n    profile.cache = profile.cache ?? CacheConfig()\n      ..enable = true\n      ..maxAge = 3600\n      ..maxCount = 100;\n\n    //初始化网络请求相关配置\n    Git.init();\n  }\n\n  // 持久化Profile信息\n  static saveProfile() =>\n      _prefs.setString(\"profile\", jsonEncode(profile.toJson()));\n}\n```\n\nGlobal类的各个字段的意义都有注释，在此不再赘述，需要注意的是`init()`需要在App启动时就要执行，所以应用的`main`方法如下：\n\n```dart\nvoid main() => Global.init().then((e) => runApp(MyApp()));\n```\n\n在此，一定要确保`Global.init()`方法不能抛出异常，否则 `runApp(MyApp())`根本执行不到。\n\n## 15.4.2 共享状态\n\n有了全局变量，我们还需要考虑如何跨组件共享状态。当然，如果我们将要共享的状态全部用全局变量替代也是可以的，但是这在Flutter开发中并不是一个好主意，因为组件的状态是和UI相关，而在状态改变时我们会期望依赖该状态的UI组件会自动更新，如果使用全局变量，那么我们必须得去手动处理状态变动通知、接收机制以及变量和组件依赖关系。因此，本实例中，我们使用前面介绍过的Provider包来实现跨组件状态共享，因此我们需要定义相关的Provider。在本实例中，需要共享的状态有登录用户信息、APP主题信息、APP语言信息。由于这些信息改变后都要立即通知其它依赖的该信息的Widget更新，所以我们应该使用`ChangeNotifierProvider`，另外，这些信息改变后都是需要更新Profile信息并进行持久化的。综上所述，我们可以定义一个`ProfileChangeNotifier`基类，然后让需要共享的Model继承自该类即可，`ProfileChangeNotifier`定义如下：\n\n```dart\nclass ProfileChangeNotifier extends ChangeNotifier {\n  Profile get _profile => Global.profile;\n\n  @override\n  void notifyListeners() {\n    Global.saveProfile(); //保存Profile变更\n    super.notifyListeners(); //通知依赖的Widget更新\n  }\n}\n```\n\n### 用户状态\n\n用户状态在登录状态发生变化时更新、通知其依赖项，我们定义如下：\n\n```dart\nclass UserModel extends ProfileChangeNotifier {\n  User get user => _profile.user;\n\n  // APP是否登录(如果有用户信息，则证明登录过)\n  bool get isLogin => user != null;\n\n  //用户信息发生变化，更新用户信息并通知依赖它的子孙Widgets更新\n  set user(User user) {\n    if (user?.login != _profile.user?.login) {\n      _profile.lastLogin = _profile.user?.login;\n      _profile.user = user;\n      notifyListeners();\n    }\n  }\n}\n```\n\n### APP主题状态\n\n主题状态在用户更换APP主题时更新、通知其依赖项，定义如下：\n\n```dart\nclass ThemeModel extends ProfileChangeNotifier {\n  // 获取当前主题，如果为设置主题，则默认使用蓝色主题\n  ColorSwatch get theme => Global.themes\n      .firstWhere((e) => e.value == _profile.theme, orElse: () => Colors.blue);\n\n  // 主题改变后，通知其依赖项，新主题会立即生效\n  set theme(ColorSwatch color) {\n    if (color != theme) {\n      _profile.theme = color[500].value;\n      notifyListeners();\n    }\n  }\n}\n```\n\n### APP语言状态\n\n当APP语言选为跟随系统（Auto）时，在系通语言改变时，APP语言会更新；当用户在APP中选定了具体语言时（美国英语或中文简体），则APP便会一直使用用户选定的语言，不会再随系统语言而变。语言状态类定义如下：\n\n```dart\nclass LocaleModel extends ProfileChangeNotifier {\n  // 获取当前用户的APP语言配置Locale类，如果为null，则语言跟随系统语言\n  Locale getLocale() {\n    if (_profile.locale == null) return null;\n    var t = _profile.locale.split(\"_\");\n    return Locale(t[0], t[1]);\n  }\n\n  // 获取当前Locale的字符串表示\n  String get locale => _profile.locale;\n\n  // 用户改变APP语言后，通知依赖项更新，新语言会立即生效\n  set locale(String locale) {\n    if (locale != _profile.locale) {\n      _profile.locale = locale;\n      notifyListeners();\n    }\n  }\n}\n```\n\n"
  },
  {
    "path": "src/v2/chapter15/intro.md",
    "content": "# 15.1 Github客户端示例\n\n本章新建一个Flutter工程，实现一个简单的Github客户端。这个实例的主要目标有两个：\n\n1. 带领读者了解如何使用Flutter来开发一个完整APP，了解Flutter应用开发流程及工程结构等。\n2. 对前面章节所学内容的一个应用及总结。\n\n需要注意的是，由于Github本身功能非常多，我们的焦点并不是去实现Github的所有业务功能。因此，我们只需要实现一个APP的骨架，能达到上面这两点即可。下面对我们要实现的功能如下：\n\n1. 实现Github账号登录、退出登录功能\n2. 登录后可以查看自己的项目主页\n3. 支持换肤\n4. 支持多语言\n5. 登录状态可以持久化；\n\n要实现上面这些功能会涉及到如下技术点：\n\n1. 网络请求；需要请求Github API。\n2. Json转Dart Model类；\n3. 全局状态管理；语言、主题、登录态等都需要全局共享。\n4. 持久化存储；保存登录信息，用户信息等。\n5. 支持国际化、Intl包的使用\n\n现在，目标已经确定，在接下来章节中，我们将分模块一步一步实现上述功能。\n\n"
  },
  {
    "path": "src/v2/chapter15/language_and_theme_setting.md",
    "content": "# 15.8 多语言和多主题\n\n本实例APP中语言和主题都是可以设置的，而两者都是通过`ChangeNotifierProvider`来实现的：我们在`main`函数中使用了`Consumer2`，依赖了`ThemeModel`和`LocaleModel`，因此，当我们在语言和主题设置页更该当前的配置后，`Consumer2`的`builder`都会重新执行，构建一个新的`MaterialApp`，所以修改会立即生效。下面看一下语言和主题设置页的实现。\n\n## 15.8.1 语言选择页\n\nAPP语言选择页提供三个选项：中文简体、美国英语、跟随系统。我们将当前APP使用的语言高亮显示，并且在后面添加一个“对号”图标，实现如下：\n\n```dart\nclass LanguageRoute extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    var color = Theme.of(context).primaryColor;\n    var localeModel = Provider.of<LocaleModel>(context);\n    var gm = GmLocalizations.of(context);\n    //构建语言选择项\n    Widget _buildLanguageItem(String lan, value) {\n      return ListTile(\n        title: Text(\n          lan,\n          // 对APP当前语言进行高亮显示\n          style: TextStyle(color: localeModel.locale == value ? color : null),\n        ),\n        trailing:\n            localeModel.locale == value ? Icon(Icons.done, color: color) : null,\n        onTap: () {\n          // 更新locale后MaterialApp会重新build\n          localeModel.locale = value;\n        },\n      );\n    }\n\n    return Scaffold(\n      appBar: AppBar(\n        title: Text(gm.language),\n      ),\n      body: ListView(\n        children: <Widget>[\n          _buildLanguageItem(\"中文简体\", \"zh_CN\"),\n          _buildLanguageItem(\"English\", \"en_US\"),\n          _buildLanguageItem(gm.auto, null),\n        ],\n      ),\n    );\n  }\n}\n```\n\n上面代码逻辑很简单，唯一需要注意的是我们在`build(…)`方法里面定义了`_buildLanguageItem(…)`方法，它和在`LanguageRoute`类中定义该方法的区别就在于：在`build(…)`内定义的方法可以共享`build(...)`方法上下文中的变量，本例中是共享了`localeModel`。当然，如果`_buildLanguageItem(…)`的实现复杂一些的话不建议这样做，此时最好是将其作为`LanguageRoute`类的方法。该页面运行效果如图15-6、15-7所示：\n\n![15-6](../imgs/15-6.png)![15-7](../imgs/15-7.png)\n\n切换语言后立即生效。\n\n## 15.8.2 主题选择页\n\n一个完整的主题`Theme`包括很多选项，这些选项在`ThemeData`中定义。本实例为了简单起见，我们只配置主题颜色。我们提供几种默认预定义的主题色供用户选择，用户点击一种色块后则更新主题。主题选择页的实现代码如下：\n\n```dart\nclass ThemeChangeRoute extends StatelessWidget{\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        title: Text(GmLocalizations.of(context).theme),\n      ),\n      body: ListView( //显示主题色块\n        children: Global.themes.map<Widget>((e) {\n          return GestureDetector(\n            child: Padding(\n              padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 16),\n              child: Container(\n                color: e,\n                height: 40,\n              ),\n            ),\n            onTap: () {\n              //主题更新后，MaterialApp会重新build\n              Provider.of<ThemeModel>(context).theme = e;\n            },\n          );\n        }).toList(),\n      ),\n    );\n  }\n}\n```\n\n运行效果如图15-8所示：\n\n![15-8](../imgs/15-8.png)\n\n点击其它主题色块后，APP主题色立马切换生效。\n"
  },
  {
    "path": "src/v2/chapter15/login_page.md",
    "content": "# 15.7 登录页\n\n我们说过Github有多种登录方式，为了简单起见，我们只实现通过用户名和密码登录。在实现登录页时有四点需要注意：\n\n1. 可以自动填充上次登录的用户名（如果有）。\n2. 为了防止密码输入错误，密码框应该有开关可以看明文。\n3. 用户名或密码字段在调用登录接口前有本地合法性校验（比如不能为空）。\n4. 登录成功后需更新用户信息。\n\n实现代码如下：\n\n```dart\nimport '../index.dart';\n\nclass LoginRoute extends StatefulWidget {\n  @override\n  _LoginRouteState createState() => _LoginRouteState();\n}\n\nclass _LoginRouteState extends State<LoginRoute> {\n  TextEditingController _unameController = new TextEditingController();\n  TextEditingController _pwdController = new TextEditingController();\n  bool pwdShow = false; //密码是否显示明文\n  GlobalKey _formKey = new GlobalKey<FormState>();\n  bool _nameAutoFocus = true;\n\n  @override\n  void initState() {\n    // 自动填充上次登录的用户名，填充后将焦点定位到密码输入框\n    _unameController.text = Global.profile.lastLogin;\n    if (_unameController.text != null) {\n      _nameAutoFocus = false;\n    }\n    super.initState();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    var gm = GmLocalizations.of(context);\n    return Scaffold(\n      appBar: AppBar(title: Text(gm.login)),\n      body: Padding(\n        padding: const EdgeInsets.all(16.0),\n        child: Form(\n          key: _formKey,\n          autovalidate: true,\n          child: Column(\n            children: <Widget>[\n              TextFormField(\n                  autofocus: _nameAutoFocus,\n                  controller: _unameController,\n                  decoration: InputDecoration(\n                    labelText: gm.userName,\n                    hintText: gm.userNameOrEmail,\n                    prefixIcon: Icon(Icons.person),\n                  ),\n                  // 校验用户名（不能为空）\n                  validator: (v) {\n                    return v.trim().isNotEmpty ? null : gm.userNameRequired;\n                  }),\n              TextFormField(\n                controller: _pwdController,\n                autofocus: !_nameAutoFocus,\n                decoration: InputDecoration(\n                    labelText: gm.password,\n                    hintText: gm.password,\n                    prefixIcon: Icon(Icons.lock),\n                    suffixIcon: IconButton(\n                      icon: Icon(\n                          pwdShow ? Icons.visibility_off : Icons.visibility),\n                      onPressed: () {\n                        setState(() {\n                          pwdShow = !pwdShow;\n                        });\n                      },\n                    )),\n                obscureText: !pwdShow,\n                //校验密码（不能为空）\n                validator: (v) {\n                  return v.trim().isNotEmpty ? null : gm.passwordRequired;\n                },\n              ),\n              Padding(\n                padding: const EdgeInsets.only(top: 25),\n                child: ConstrainedBox(\n                  constraints: BoxConstraints.expand(height: 55.0),\n                  child: RaisedButton(\n                    color: Theme.of(context).primaryColor,\n                    onPressed: _onLogin,\n                    textColor: Colors.white,\n                    child: Text(gm.login),\n                  ),\n                ),\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n\n  void _onLogin() async {\n    // 提交前，先验证各个表单字段是否合法\n    if ((_formKey.currentState as FormState).validate()) {\n      showLoading(context);\n      User user;\n      try {\n        user = await Git(context).login(_unameController.text, _pwdController.text);\n        // 因为登录页返回后，首页会build，所以我们传false，更新user后不触发更新\n        Provider.of<UserModel>(context, listen: false).user = user;\n      } catch (e) {\n        //登录失败则提示\n        if (e.response?.statusCode == 401) {\n          showToast(GmLocalizations.of(context).userNameOrPasswordWrong);\n        } else {\n          showToast(e.toString());\n        }\n      } finally {\n        // 隐藏loading框\n        Navigator.of(context).pop();\n      }\n      if (user != null) {\n        // 返回\n        Navigator.of(context).pop();\n      }\n    }\n  }\n}\n```\n\n代码很简单，关键地方都有注释，不再赘述，下面我们看一下运行效果，如图15-5所示。\n\n![图15-5](../imgs/15-5.png)"
  },
  {
    "path": "src/v2/chapter15/models.md",
    "content": "# 15.3 Model类定义\n\n本节我们先梳理一下APP中将用到的数据，然后生成相应的Dart Model类。Json文件转Dart Model的方案采用前面介绍过的 json_model 包方案\n\n### Github账号信息\n\n登录Github后，我们需要获取当前登录者的Github账号信息，Github API接口返回Json结构如下：\n\n```json\n{\n  \"login\": \"octocat\", //用户登录名\n  \"avatar_url\": \"https://github.com/images/error/octocat_happy.gif\", //用户头像地址\n  \"type\": \"User\", //用户类型，可能是组织\n  \"name\": \"monalisa octocat\", //用户名字\n  \"company\": \"GitHub\", //公司\n  \"blog\": \"https://github.com/blog\", //博客地址\n  \"location\": \"San Francisco\", // 用户所处地理位置\n  \"email\": \"octocat@github.com\", // 邮箱\n  \"hireable\": false,\n  \"bio\": \"There once was...\", // 用户简介\n  \"public_repos\": 2, // 公开项目数\n  \"followers\": 20, //关注该用户的人数\n  \"following\": 0, // 该用户关注的人数\n  \"created_at\": \"2008-01-14T04:33:35Z\", // 账号创建时间\n  \"updated_at\": \"2008-01-14T04:33:35Z\", // 账号信息更新时间\n  \"total_private_repos\": 100, //该用户总的私有项目数(包括参与的其它组织的私有项目)\n  \"owned_private_repos\": 100 //该用户自己的私有项目数\n  ... //省略其它字段\n}\n```\n\n我们在“jsons”目录下创建一个“user.json”文件保存上述信息。\n\n### API缓存策略信息\n\n由于Github服务器在国内访问速度较慢，我们对Github API应用一些简单的缓存策略。我们在“jsons”目录下创建一个“cacheConfig.json”文件缓存策略信息，定义如下：\n\n```json\n{\n  \"enable\":true, // 是否启用缓存\n  \"maxAge\":1000, // 缓存的最长时间，单位（秒）\n  \"maxCount\":100 // 最大缓存数\n}\n```\n\n### 用户信息\n\n用户信息(Profile)应包括如下信息：\n\n1. Github账号信息；由于我们的APP可以切换账号登录，且登录后再次打开则不需要登录，所以我们需要对用户账号信息和登录状态进行持久化。\n2. 应用使用配置信息；每一个用户都应有自己的APP配置信息，如主题、语言、以及数据缓存策略等。\n3. 用户注销登录后，为了便于用户在退出APP前再次登录，我们需要记住上次登录的用户名。\n\n需要注意的是，目前Github有三种登录方式，分别是账号密码登录、oauth授权登录、二次认证登录；这三种登录方式的安全性依次加强，但是在本示例中，为了简单起见，我们使用账号密码登录，因此我们需要保存用户的密码。\n\n> 注意：在这里需要提醒读者，在登录场景中，保护用户账号安全是一个非常重要且永恒的话题，在实际开发中应严格杜绝直接明文存储用户账密的行为。\n\n我们在“jsons”目录下创建一个“profile.json”文件，结构如下：\n\n```json\n{\n  \"user\":\"$user\", //Github账号信息，结构见\"user.json\"\n  \"token\":\"\", // 登录用户的token(oauth)或密码\n  \"theme\":5678, //主题色值\n  \"cache\":\"$cacheConfig\", // 缓存策略信息，结构见\"cacheConfig.json\"\n  \"lastLogin\":\"\", //最近一次的注销登录的用户名\n  \"locale\":\"\" // APP语言信息\n}\n```\n\n### 项目信息\n\n由于APP主页要显示其所有项目信息，我们在“jsons”目录下创建一个“repo.json”文件保存项目信息。通过参考Github 获取项目信息的API文档，定义出最终的“repo.json”文件结构，如下：\n\n```json\n{\n  \"id\": 1296269,\n  \"name\": \"Hello-World\", //项目名称\n  \"full_name\": \"octocat/Hello-World\", //项目完整名称\n  \"owner\": \"$user\", // 项目拥有者，结构见\"user.json\"\n  \"parent\":\"$repo\", // 如果是fork的项目，则此字段表示fork的父项目信息\n  \"private\": false, // 是否私有项目\n  \"description\": \"This your first repo!\", //项目描述\n  \"fork\": false, // 该项目是否为fork的项目\n  \"language\": \"JavaScript\",//该项目的主要编程语言\n  \"forks_count\": 9, // fork了该项目的数量\n  \"stargazers_count\": 80, //该项目的star数量\n  \"size\": 108, // 项目占用的存储大小\n  \"default_branch\": \"master\", //项目的默认分支\n  \"open_issues_count\": 2, //该项目当前打开的issue数量\n  \"pushed_at\": \"2011-01-26T19:06:43Z\",\n  \"created_at\": \"2011-01-26T19:01:12Z\",\n  \"updated_at\": \"2011-01-26T19:14:43Z\",\n  \"subscribers_count\": 42, //订阅（关注）该项目的人数\n  \"license\": { // 该项目的开源许可证\n    \"key\": \"mit\",\n    \"name\": \"MIT License\",\n    \"spdx_id\": \"MIT\",\n    \"url\": \"https://api.github.com/licenses/mit\",\n    \"node_id\": \"MDc6TGljZW5zZW1pdA==\"\n  }\n  ...//省略其它字段\n}\n```\n\n### 生成Dart Model类\n\n现在，我们需要的Json数据已经定义完毕，现在只需要运行json_model package提供的命令来通过json文件生成相应的Dart类：\n\n```shell\nflutter packages pub run json_model\n```\n\n命令执行成功后，可以看到lib/models文件夹下会生成相应的Dart Model类：\n\n```\n├── models\n│   ├── cacheConfig.dart\n│   ├── cacheConfig.g.dart\n│   ├── index.dart\n│   ├── profile.dart\n│   ├── profile.g.dart\n│   ├── repo.dart\n│   ├── repo.g.dart\n│   ├── user.dart\n│   └── user.g.dart\n\n```\n\n### 数据持久化\n\n我们使用shared_preferences包来对登录用户的Profile信息进行持久化。shared_preferences是一个Flutter插件，它通过Android和iOS平台提供的机制来实现数据持久化。由于shared_preferences的使用非常简单，读者可以自行查看其文档，在此不再赘述。\n"
  },
  {
    "path": "src/v2/chapter15/network.md",
    "content": "# 15.5 网络请求封装\n\n本节我们会基于前面介绍过的dio网络库封装APP中用到的网络请求接口，并同时应用一个简单的缓存策略。下面我们先介绍一下网络接口缓存原理，然后再封装APP的业务请求接口。\n\n## 15.5.1 网络接口缓存\n\n由于在国内访问Github服务器速度较慢，所以我们应用一些简单的缓存策略：将请求的url作为key，对请求的返回值在一个指定时间段类进行缓存，另外设置一个最大缓存数，当超过最大缓存数后移除最早的一条缓存。但是也得提供一种针对特定接口或请求决定是否启用缓存的机制，这种机制可以指定哪些接口或那次请求不应用缓存，这种机制是很有必要的，比如登录接口就不应该缓存，又比如用户在下拉刷新时就不应该再应用缓存。在实现缓存之前我们先定义保存缓存信息的`CacheObject`类：\n\n```dart\nclass CacheObject {\n  CacheObject(this.response)\n      : timeStamp = DateTime.now().millisecondsSinceEpoch;\n  Response response;\n  int timeStamp; // 缓存创建时间\n\n  @override\n  bool operator ==(other) {\n    return response.hashCode == other.hashCode;\n  }\n\n  //将请求uri作为缓存的key\n  @override\n  int get hashCode => response.realUri.hashCode;\n}\n```\n\n接下来我们需要实现具体的缓存策略，由于我们使用的是dio package，所以我们可以直接通过拦截器来实现缓存策略：\n\n```dart\nimport 'dart:collection';\nimport 'package:dio/dio.dart';\nimport '../index.dart';\n\nclass CacheObject {\n  CacheObject(this.response)\n      : timeStamp = DateTime.now().millisecondsSinceEpoch;\n  Response response;\n  int timeStamp;\n\n  @override\n  bool operator ==(other) {\n    return response.hashCode == other.hashCode;\n  }\n\n  @override\n  int get hashCode => response.realUri.hashCode;\n}\n\nclass NetCache extends Interceptor {\n  // 为确保迭代器顺序和对象插入时间一致顺序一致，我们使用LinkedHashMap\n  var cache = LinkedHashMap<String, CacheObject>();\n\n  @override\n  onRequest(RequestOptions options) async {\n    if (!Global.profile.cache.enable) return options;\n    // refresh标记是否是\"下拉刷新\"\n    bool refresh = options.extra[\"refresh\"] == true;\n    //如果是下拉刷新，先删除相关缓存\n    if (refresh) {\n      if (options.extra[\"list\"] == true) {\n        //若是列表，则只要url中包含当前path的缓存全部删除（简单实现，并不精准）\n        cache.removeWhere((key, v) => key.contains(options.path));\n      } else {\n        // 如果不是列表，则只删除uri相同的缓存\n        delete(options.uri.toString());\n      }\n      return options;\n    }\n    if (options.extra[\"noCache\"] != true &&\n        options.method.toLowerCase() == 'get') {\n      String key = options.extra[\"cacheKey\"] ?? options.uri.toString();\n      var ob = cache[key];\n      if (ob != null) {\n        //若缓存未过期，则返回缓存内容\n        if ((DateTime.now().millisecondsSinceEpoch - ob.timeStamp) / 1000 <\n            Global.profile.cache.maxAge) {\n          return cache[key].response;\n        } else {\n          //若已过期则删除缓存，继续向服务器请求\n          cache.remove(key);\n        }\n      }\n    }\n  }\n\n  @override\n  onError(DioError err) async {\n    // 错误状态不缓存\n  }\n\n  @override\n  onResponse(Response response) async {\n    // 如果启用缓存，将返回结果保存到缓存\n    if (Global.profile.cache.enable) {\n      _saveCache(response);\n    }\n  }\n\n  _saveCache(Response object) {\n    RequestOptions options = object.request;\n    if (options.extra[\"noCache\"] != true &&\n        options.method.toLowerCase() == \"get\") {\n      // 如果缓存数量超过最大数量限制，则先移除最早的一条记录\n      if (cache.length == Global.profile.cache.maxCount) {\n        cache.remove(cache[cache.keys.first]);\n      }\n      String key = options.extra[\"cacheKey\"] ?? options.uri.toString();\n      cache[key] = CacheObject(object);\n    }\n  }\n\n  void delete(String key) {\n    cache.remove(key);\n  }\n}\n```\n\n关于代码的解释都在注释中了，在此需要说明的是dio包的`option.extra`是专门用于扩展请求参数的，我们通过定义了“refresh”和“noCache”两个参数实现了“针对特定接口或请求决定是否启用缓存的机制”，这两个参数含义如下：\n\n| 参数名  | 类型 | 解释                                                         |\n| ------- | ---- | ------------------------------------------------------------ |\n| refresh | bool | 如果为true，则本次请求不使用缓存，但新的请求结果依然会被缓存 |\n| noCache | bool | 本次请求禁用缓存，请求结果也不会被缓存。                     |\n\n## 15.5.2 封装网络请求\n\n一个完整的APP，可能会涉及很多网络请求，为了便于管理、收敛请求入口，工程上最好的作法就是将所有网络请求放到同一个源码文件中。由于我们的接口都是请求的Github 开发平台提供的API，所以我们定义一个Git类，专门用于Github API接口调用。另外，在调试过程中，我们通常需要一些工具来查看网络请求、响应报文，使用网络代理工具来调试网络数据问题是主流方式。配置代理需要在应用中指定代理服务器的地址和端口，另外Github API是HTTPS协议，所以在配置完代理后还应该禁用证书校验，这些配置我们在Git类初始化时执行（`init()方法`）。下面是Git类的源码：\n\n```dart\nimport 'dart:async';\nimport 'dart:convert';\nimport 'dart:io';\nimport 'package:dio/dio.dart';\nimport 'package:dio/adapter.dart';\nimport 'package:flutter/material.dart';\nimport '../index.dart';\n\nclass Git {\n  // 在网络请求过程中可能会需要使用当前的context信息，比如在请求失败时\n  // 打开一个新路由，而打开新路由需要context信息。\n  Git([this.context]) {\n    _options = Options(extra: {\"context\": context});\n  }\n\n  BuildContext context;\n  Options _options;\n  static Dio dio = new Dio(BaseOptions(\n    baseUrl: 'https://api.github.com/',\n    headers: {\n      HttpHeaders.acceptHeader: \"application/vnd.github.squirrel-girl-preview,\"\n          \"application/vnd.github.symmetra-preview+json\",\n    },\n  ));\n\n  static void init() {\n    // 添加缓存插件\n    dio.interceptors.add(Global.netCache);\n    // 设置用户token（可能为null，代表未登录）\n    dio.options.headers[HttpHeaders.authorizationHeader] = Global.profile.token;\n\n    // 在调试模式下需要抓包调试，所以我们使用代理，并禁用HTTPS证书校验\n    if (!Global.isRelease) {\n      (dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =\n          (client) {\n        client.findProxy = (uri) {\n          return \"PROXY 10.1.10.250:8888\";\n        };\n        //代理工具会提供一个抓包的自签名证书，会通不过证书校验，所以我们禁用证书校验\n        client.badCertificateCallback =\n            (X509Certificate cert, String host, int port) => true;\n      };\n    }\n  }\n\n  // 登录接口，登录成功后返回用户信息\n  Future<User> login(String login, String pwd) async {\n    String basic = 'Basic ' + base64.encode(utf8.encode('$login:$pwd'));\n    var r = await dio.get(\n      \"/users/$login\",\n      options: _options.merge(headers: {\n        HttpHeaders.authorizationHeader: basic\n      }, extra: {\n        \"noCache\": true, //本接口禁用缓存\n      }),\n    );\n    //登录成功后更新公共头（authorization），此后的所有请求都会带上用户身份信息\n    dio.options.headers[HttpHeaders.authorizationHeader] = basic;\n    //清空所有缓存\n    Global.netCache.cache.clear();\n    //更新profile中的token信息\n    Global.profile.token = basic;\n    return User.fromJson(r.data);\n  }\n\n  //获取用户项目列表\n  Future<List<Repo>> getRepos(\n      {Map<String, dynamic> queryParameters, //query参数，用于接收分页信息\n      refresh = false}) async {\n    if (refresh) {\n      // 列表下拉刷新，需要删除缓存（拦截器中会读取这些信息）\n      _options.extra.addAll({\"refresh\": true, \"list\": true});\n    }\n    var r = await dio.get<List>(\n      \"user/repos\",\n      queryParameters: queryParameters,\n      options: _options,\n    );\n    return r.data.map((e) => Repo.fromJson(e)).toList();\n  }\n}\n```\n\n可以看到我们在`init()`方法中，我们判断了是否是调试环境，然后做了一些针对调试环境的网络配置（设置代理和禁用证书校验）。而`Git.init()`方法是应用启动时被调用的（`Global.init()`方法中会调用`Git.init()`）。\n\n另外需要注意，我们所有的网络请求是通过同一个`dio`实例（静态变量）发出的，在创建该`dio`实例时我们将Github API的基地址和API支持的Header进行了全局配置，这样所有通过该`dio`实例发出的请求都会默认使用者些配置。\n\n在本实例中，我们只用到了登录接口和获取用户项目的接口，所以在`Git`类中只定义了`login(…)`和`getRepos(…)`方法，如果读者要在本实例的基础上扩充功能，读者可以将其它的接口请求方法添加到`Git`类中，这样便实现了网络请求接口在代码层面的集中管理和维护。\n\n"
  },
  {
    "path": "src/v2/chapter2/first_flutter_app.md",
    "content": "\n# 2.1 计数器应用示例\n\n用Android Studio和VS Code创建的Flutter应用模板默认是一个简单的计数器示例。本节先仔细讲解一下这个计数器Demo的源码，让读者对Flutter应用程序结构有个基本了解，然后在随后的小节中将会基于此示例，一步一步添加一些新的功能来介绍Flutter应用的其它概念与技术。\n\n对于接下来的示例，希望读者可以跟着笔者一起亲自动手来写一下，这样不仅可以加深印象，而且也会对介绍的概念与技术有一个真切的体会。如果你还不是很熟悉Dart语言或者没有移动开发经验，不用担心，只要你熟悉面向对象和基本编程概念（如变量、循环和条件控制），则可以完成本示例。\n\n## 2.1.1 创建Flutter应用模板\n\n通过Android Studio或VS Code创建一个新的Flutter工程，命名为\"first_flutter_app\"。创建好后，就会得到一个计数器应用的Demo。\n\n> 注意，默认Demo示例可能随着编辑器Flutter插件的版本变化而变化，本例中会介绍计数器示例的全部代码，所以不会对本示例产生影响。\n\n我们先运行创建的工程，效果如图2-1所示：\n\n![图2-1](../imgs/2-1.png)\n\n\n\n该计数器示例中，每点击一次右下角带“+”号的悬浮按钮，屏幕中央的数字就会加1。\n\n在这个示例中，主要Dart代码是在 **lib/main.dart** 文件中，下面是它的源码：\n\n```dart\nimport 'package:flutter/material.dart';\n\nvoid main() => runApp(new MyApp());\n\nclass MyApp extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return new MaterialApp(\n      title: 'Flutter Demo',\n      theme: new ThemeData(\n        primarySwatch: Colors.blue,\n      ),\n      home: new MyHomePage(title: 'Flutter Demo Home Page'),\n    );\n  }\n}\n\nclass MyHomePage extends StatefulWidget {\n  MyHomePage({Key key, this.title}) : super(key: key);\n  final String title;\n\n  @override\n  _MyHomePageState createState() => new _MyHomePageState();\n}\n\nclass _MyHomePageState extends State<MyHomePage> {\n  int _counter = 0;\n\n  void _incrementCounter() {\n    setState(() {\n      _counter++;\n    });\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return new Scaffold(\n      appBar: new AppBar(\n        title: new Text(widget.title),\n      ),\n      body: new Center(\n        child: new Column(\n          mainAxisAlignment: MainAxisAlignment.center,\n          children: <Widget>[\n            new Text(\n              'You have pushed the button this many times:',\n            ),\n            new Text(\n              '$_counter',\n              style: Theme.of(context).textTheme.headline4,\n            ),\n          ],\n        ),\n      ),\n      floatingActionButton: new FloatingActionButton(\n        onPressed: _incrementCounter,\n        tooltip: 'Increment',\n        child: new Icon(Icons.add),\n      ), // This trailing comma makes auto-formatting nicer for build methods.\n    );\n  }\n}\n\n```\n\n### 分析\n\n1. 导入包。\n\n   ```dart\n   import 'package:flutter/material.dart';\n   ```\n\n   此行代码作用是导入了Material UI组件库。[Material](https://material.io/guidelines/)是一种标准的移动端和web端的视觉设计语言， Flutter默认提供了一套丰富的Material风格的UI组件。\n\n2. 应用入口。\n\n   ```dart\n   void main() => runApp(MyApp());\n   ```\n\n   - 与C/C++、Java类似，Flutter 应用中`main`函数为应用程序的入口。`main`函数中调用了`runApp` 方法，它的功能是启动Flutter应用。`runApp`它接受一个`Widget`参数，在本示例中它是一个`MyApp`对象，`MyApp()`是Flutter应用的根组件。\n   - `main`函数使用了(`=>`)符号，这是Dart中单行函数或方法的简写。\n\n3. 应用结构。\n\n   ```dart\n   class MyApp extends StatelessWidget {\n     @override\n     Widget build(BuildContext context) {\n       return new MaterialApp(\n         //应用名称  \n         title: 'Flutter Demo', \n         theme: new ThemeData(\n           //蓝色主题  \n           primarySwatch: Colors.blue,\n         ),\n         //应用首页路由  \n         home: new MyHomePage(title: 'Flutter Demo Home Page'),\n       );\n     }\n   }\n   ```\n\n   - `MyApp`类代表Flutter应用，它继承了 `StatelessWidget `类，这也就意味着应用本身也是一个widget。\n\n   - 在Flutter中，大多数东西都是widget（后同“组件”或“部件”），包括对齐(alignment)、填充(padding)和布局(layout)等，它们都是以widget的形式提供。\n\n   - Flutter在构建页面时，会调用组件的`build`方法，widget的主要工作是提供一个build()方法来描述如何构建UI界面（通常是通过组合、拼装其它基础widget）。\n\n   - `MaterialApp` 是Material库中提供的Flutter APP框架，通过它可以设置应用的名称、主题、语言、首页及路由列表等。`MaterialApp`也是一个widget。\n\n   - `home` 为Flutter应用的首页，它也是一个widget。\n\n## 2.1.2 首页\n\n   ```dart\n   class MyHomePage extends StatefulWidget {\n     MyHomePage({Key key, this.title}) : super(key: key);\n     final String title;\n     @override\n     _MyHomePageState createState() => new _MyHomePageState();\n   }\n   \n   class _MyHomePageState extends State<MyHomePage> {\n    ...\n   }\n   ```\n\n`MyHomePage` 是Flutter应用的首页，它继承自`StatefulWidget`类，表示它是一个有状态的组件（Stateful widget）。关于Stateful widget我们将在第三章“Widget简介”一节仔细介绍，现在我们只需简单认为有状态的组件（Stateful widget） 和无状态的组件（Stateless widget）有两点不同：\n\n1. Stateful widget可以拥有状态，这些状态在widget生命周期中是可以变的，而Stateless widget是不可变的。\n\n2. Stateful widget至少由两个类组成：\n   - 一个` StatefulWidget`类。\n   - 一个 `State`类； `StatefulWidget`类本身是不变的，但是`State`类中持有的状态在widget生命周期中可能会发生变化。\n\n   `_MyHomePageState`类是`MyHomePage`类对应的状态类。看到这里，读者可能已经发现：和`MyApp` 类不同， `MyHomePage`类中并没有`build`方法，取而代之的是，`build`方法被挪到了`_MyHomePageState`方法中，至于为什么这么做，先留个疑问，在分析完完整代码后再来解答。\n   \n### State类\n\n接下来，我们看看`_MyHomePageState`中都包含哪些东西：\n\n1. 该组件的状态。由于我们只需要维护一个点击次数计数器，所以定义一个`_counter`状态：\n\n   ```dart\n   int _counter = 0; //用于记录按钮点击的总次数\n   ```\n\n   `_counter` 为保存屏幕右下角带“+”号按钮点击次数的状态。\n\n2. 设置状态的自增函数。\n\n   ```dart\n   void _incrementCounter() {\n     setState(() {\n        _counter++;\n     });\n   }\n   ```\n\n   当按钮点击时，会调用此函数，该函数的作用是先自增`_counter`，然后调用`setState` 方法。`setState`方法的作用是通知Flutter框架，有状态发生了改变，Flutter框架收到通知后，会执行`build`方法来根据新的状态重新构建界面， Flutter 对此方法做了优化，使重新执行变的很快，所以你可以重新构建任何需要更新的东西，而无需分别去修改各个widget。\n\n3. 构建UI界面\n\n   构建UI界面的逻辑在`build`方法中，当`MyHomePage`第一次创建时，`_MyHomePageState`类会被创建，当初始化完成后，Flutter框架会调用Widget的`build`方法来构建widget树，最终将widget树渲染到设备屏幕上。所以，我们看看`_MyHomePageState`的`build`方法中都干了什么事：\n\n   ```dart\n     Widget build(BuildContext context) {\n       return new Scaffold(\n         appBar: new AppBar(\n           title: new Text(widget.title),\n         ),\n         body: new Center(\n           child: new Column(\n             mainAxisAlignment: MainAxisAlignment.center,\n             children: <Widget>[\n               new Text(\n                 'You have pushed the button this many times:',\n               ),\n               new Text(\n                 '$_counter',\n                 style: Theme.of(context).textTheme.headline4,\n               ),\n             ],\n           ),\n         ),\n         floatingActionButton: new FloatingActionButton(\n           onPressed: _incrementCounter,\n           tooltip: 'Increment',\n           child: new Icon(Icons.add),\n         ),\n       );\n     }\n   ```\n\n   - `Scaffold` 是 Material 库中提供的页面脚手架，它提供了默认的导航栏、标题和包含主屏幕widget树（后同“组件树”或“部件树”）的`body`属性，组件树可以很复杂。本书后面示例中，路由默认都是通过`Scaffold`创建。\n   - `body`的组件树中包含了一个`Center` 组件，`Center` 可以将其子组件树对齐到屏幕中心。此例中， `Center` 子组件是一个`Column` 组件，`Column`的作用是将其所有子组件沿屏幕垂直方向依次排列； 此例中`Column`子组件是两个 `Text `，第一个`Text` 显示固定文本 “You have pushed the button this many times:”，第二个`Text` 显示`_counter`状态的数值。\n   - `floatingActionButton`是页面右下角的带“+”的悬浮按钮，它的`onPressed`属性接受一个回调函数，代表它被点击后的处理器，本例中直接将`_incrementCounter`方法作为其处理函数。\n\n\n\n现在，我们将整个计数器执行流程串起来：当右下角的`floatingActionButton`按钮被点击之后，会调用`_incrementCounter`方法。在`_incrementCounter`方法中，首先会自增`_counter`计数器（状态），然后`setState`会通知Flutter框架状态发生变化，接着，Flutter框架会调用`build`方法以新的状态重新构建UI，最终显示在设备屏幕上。\n\n\n#### 为什么要将build方法放在State中，而不是放在StatefulWidget中？\n\n现在，我们回答之前提出的问题，为什么`build()`方法放在State（而不是`StatefulWidget`）中 ？这主要是为了提高开发的灵活性。如果将`build()`方法放在`StatefulWidget`中则会有两个问题：\n\n- 状态访问不便\n\n  试想一下，如果我们的`StatefulWidget`有很多状态，而每次状态改变都要调用`build`方法，由于状态是保存在State中的，如果`build`方法在`StatefulWidget`中，那么`build`方法和状态分别在两个类中，那么构建时读取状态将会很不方便！试想一下，如果真的将`build`方法放在StatefulWidget中的话，由于构建用户界面过程需要依赖State，所以`build`方法将必须加一个`State`参数，大概是下面这样：\n\n  ```dart\n    Widget build(BuildContext context, State state){\n        //state.counter\n        ...\n    }\n  ```\n\n  这样的话就只能将State的所有状态声明为公开的状态，这样才能在State类外部访问状态！但是，将状态设置为公开后，状态将不再具有私密性，这就会导致对状态的修改将会变的不可控。但如果将`build()`方法放在State中的话，构建过程不仅可以直接访问状态，而且也无需公开私有状态，这会非常方便。\n\n- 继承`StatefulWidget`不便\n\n  例如，Flutter中有一个动画widget的基类`AnimatedWidget`，它继承自`StatefulWidget`类。`AnimatedWidget`中引入了一个抽象方法`build(BuildContext context)`，继承自`AnimatedWidget`的动画widget都要实现这个`build`方法。现在设想一下，如果`StatefulWidget` 类中已经有了一个`build`方法，正如上面所述，此时`build`方法需要接收一个state对象，这就意味着`AnimatedWidget`必须将自己的State对象(记为_animatedWidgetState)提供给其子类，因为子类需要在其`build`方法中调用父类的`build`方法，代码可能如下：\n\n  ```dart\n  class MyAnimationWidget extends AnimatedWidget{\n      @override\n      Widget build(BuildContext context, State state){\n        //由于子类要用到AnimatedWidget的状态对象_animatedWidgetState，\n        //所以AnimatedWidget必须通过某种方式将其状态对象_animatedWidgetState\n        //暴露给其子类   \n        super.build(context, _animatedWidgetState)\n      }\n  }\n  ```\n\n  这样很显然是不合理的，因为\n\n  1. `AnimatedWidget`的状态对象是`AnimatedWidget`内部实现细节，不应该暴露给外部。\n  2. 如果要将父类状态暴露给子类，那么必须得有一种传递机制，而做这一套传递机制是无意义的，因为父子类之间状态的传递和子类本身逻辑是无关的。\n\n综上所述，可以发现，对于`StatefulWidget`，将`build`方法放在State中，可以给开发带来很大的灵活性。\n\n"
  },
  {
    "path": "src/v2/chapter2/flutter_app_debug.md",
    "content": "\n# 2.5 调试Flutter应用\n\n有各种各样的工具和功能来帮助调试Flutter应用程序。\n\n### Dart 分析器\n\n在运行应用程序前，请运行`flutter analyze`测试你的代码。这个工具是一个静态代码检查工具，它是`dartanalyzer`工具的一个包装，主要用于分析代码并帮助开发者发现可能的错误，比如，Dart分析器大量使用了代码中的类型注释来帮助追踪问题，避免`var`、无类型的参数、无类型的列表文字等。\n\n 如果你使用IntelliJ的Flutter插件，那么分析器在打开IDE时就已经自动启用了，如果读者使用的是其它IDE，强烈建议读者启用Dart 分析器，因为在大多数时候，Dart 分析器可以在代码运行前发现大多数问题。\n\n### Dart Observatory (语句级的单步调试和分析器)\n\n如果我们使用`flutter run`启动应用程序，那么当它运行时，我们可以打开Observatory工具的Web页面，例如Observatory默认监听[http://127.0.0.1:8100/](http://127.0.0.1:8100/)，可以在浏览器中直接打开该链接。直接使用语句级单步调试器连接到您的应用程序。如果您使用的是IntelliJ，则还可以使用其内置的调试器来调试您的应用程序。\n\nObservatory 同时支持分析、检查堆等。有关Observatory的更多信息请参考[Observatory 文档](https://dart-lang.github.io/observatory/)。\n\n如果您使用Observatory进行分析，请确保通过`--profile`选项来运行`flutter run`命令来运行应用程序。 否则，配置文件中将出现的主要问题将是调试断言，以验证框架的各种不变量（请参阅下面的“调试模式断言”）。\n\n### `debugger()` 声明\n\n当使用Dart Observatory（或另一个Dart调试器，例如IntelliJ IDE中的调试器）时，可以使用该`debugger()`语句插入编程式断点。要使用这个，你必须添加`import 'dart:developer';`到相关文件顶部。\n\n`debugger()`语句采用一个可选`when`参数，您可以指定该参数仅在特定条件为真时中断，如下所示：\n\n```dart\nvoid someFunction(double offset) {\n  debugger(when: offset > 30.0);\n  // ...\n}\n```\n\n### `print`、`debugPrint`、`flutter logs`\n\nDart `print()`功能将输出到系统控制台，您可以使用`flutter logs`来查看它（基本上是一个包装`adb logcat`）。\n\n如果你一次输出太多，那么Android有时会丢弃一些日志行。为了避免这种情况，您可以使用Flutter的`foundation`库中的[`debugPrint()`](https://docs.flutter.io/flutter/foundation/debugPrint.html)。 这是一个封装print，它将输出限制在一个级别，避免被Android内核丢弃。\n\nFlutter框架中的许多类都有`toString`实现。按照惯例，这些输出通常包括对象的`runtimeType`单行输出，通常在表单中ClassName(more information about this instance…)。 树中使用的一些类也具有`toStringDeep`，从该点返回整个子树的多行描述。已一些具有详细信息`toString`的类会实现一个`toStringShort`，它只返回对象的类型或其他非常简短的（一个或两个单词）描述。\n\n### 调试模式断言\n\n在Flutter应用调试过程中，Dart `assert`语句被启用，并且Flutter框架使用它来执行许多运行时检查来验证是否违反一些不可变的规则。\n\n当一个不可变的规则被违反时，它被报告给控制台，并带有一些上下文信息来帮助追踪问题的根源。\n\n要关闭调试模式并使用发布模式，请使用`flutter run --release`运行您的应用程序。 这也关闭了Observatory调试器。一个中间模式可以关闭除Observatory之外所有调试辅助工具的，称为“profile mode”，用`--profile`替代`--release`即可。\n\n### 调试应用程序层\n\nFlutter框架的每一层都提供了将其当前状态或事件转储(dump)到控制台（使用`debugPrint`）的功能。\n\n#### Widget 树\n\n要转储Widgets树的状态，请调用[`debugDumpApp()`](https://docs.flutter.io/flutter/widgets/debugDumpApp.html)。 只要应用程序已经构建了至少一次（即在调用`build()`之后的任何时间），您可以在应用程序未处于构建阶段（即，不在`build()`方法内调用 ）的任何时间调用此方法（在调用`runApp()`之后）。\n\n如, 这个应用程序:\n\n```dart\nimport 'package:flutter/material.dart';\n\nvoid main() {\n  runApp(\n    new MaterialApp(\n      home: new AppHome(),\n    ),\n  );\n}\n\nclass AppHome extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return new Material(\n      child: new Center(\n        child: new FlatButton(\n          onPressed: () {\n            debugDumpApp();\n          },\n          child: new Text('Dump App'),\n        ),\n      ),\n    );\n  }\n}\n```\n\n…会输出这样的内容（精确的细节会根据框架的版本、设备的大小等等而变化）：\n\n```shell\nI/flutter ( 6559): WidgetsFlutterBinding - CHECKED MODE\nI/flutter ( 6559): RenderObjectToWidgetAdapter<RenderBox>([GlobalObjectKey RenderView(497039273)]; renderObject: RenderView)\nI/flutter ( 6559): └MaterialApp(state: _MaterialAppState(1009803148))\nI/flutter ( 6559):  └ScrollConfiguration()\nI/flutter ( 6559):   └AnimatedTheme(duration: 200ms; state: _AnimatedThemeState(543295893; ticker inactive; ThemeDataTween(ThemeData(Brightness.light Color(0xff2196f3) etc...) → null)))\nI/flutter ( 6559):    └Theme(ThemeData(Brightness.light Color(0xff2196f3) etc...))\nI/flutter ( 6559):     └WidgetsApp([GlobalObjectKey _MaterialAppState(1009803148)]; state: _WidgetsAppState(552902158))\nI/flutter ( 6559):      └CheckedModeBanner()\nI/flutter ( 6559):       └Banner()\nI/flutter ( 6559):        └CustomPaint(renderObject: RenderCustomPaint)\nI/flutter ( 6559):         └DefaultTextStyle(inherit: true; color: Color(0xd0ff0000); family: \"monospace\"; size: 48.0; weight: 900; decoration: double Color(0xffffff00) TextDecoration.underline)\nI/flutter ( 6559):          └MediaQuery(MediaQueryData(size: Size(411.4, 683.4), devicePixelRatio: 2.625, textScaleFactor: 1.0, padding: EdgeInsets(0.0, 24.0, 0.0, 0.0)))\nI/flutter ( 6559):           └LocaleQuery(null)\nI/flutter ( 6559):            └Title(color: Color(0xff2196f3))\n... #省略剩余内容\n```\n\n这是一个“扁平化”的树，显示了通过各种构建函数投影的所有widget（如果你在widget树的根中调用`toStringDeepwidget`，这是你获得的树）。 你会看到很多在你的应用源代码中没有出现的widget，因为它们是被框架中widget的`build()`函数插入的。例如，[`InkFeature`](https://docs.flutter.io/flutter/material/InkFeature-class.html)是Material widget的一个实现细节 。\n\n当按钮从被按下变为被释放时debugDumpApp()被调用，FlatButton对象同时调用`setState()`，并将自己标记为\"dirty\"。 这就是为什么如果你看转储，你会看到特定的对象标记为“dirty”。您还可以查看已注册了哪些手势监听器; 在这种情况下，一个单一的GestureDetector被列出，并且监听“tap”手势（“tap”是`TapGestureDetector`的`toStringShort`函数输出的）\n\n如果您编写自己的widget，则可以通过覆盖[`debugFillProperties()`](https://docs.flutter.io/flutter/widgets/Widget/debugFillProperties.html)来添加信息。 将[DiagnosticsProperty](https://docs.flutter.io/flutter/foundation/DiagnosticsProperty-class.html)对象作为方法参数，并调用父类方法。 该函数是该`toString`方法用来填充小部件描述信息的。\n\n#### 渲染树\n\n如果您尝试调试布局问题，那么Widget树可能不够详细。在这种情况下，您可以通过调用`debugDumpRenderTree()`转储渲染树。 正如`debugDumpApp()`，除布局或绘制阶段外，您可以随时调用此函数。作为一般规则，从[frame 回调](https://docs.flutter.io/flutter/scheduler/SchedulerBinding/addPersistentFrameCallback.html) 或事件处理器中调用它是最佳解决方案。\n\n要调用`debugDumpRenderTree()`，您需要添加`import'package:flutter/rendering.dart';`到您的源文件。\n\n上面这个小例子的输出结果如下所示：\n\n```shell\nI/flutter ( 6559): RenderView\nI/flutter ( 6559):  │ debug mode enabled - android\nI/flutter ( 6559):  │ window size: Size(1080.0, 1794.0) (in physical pixels)\nI/flutter ( 6559):  │ device pixel ratio: 2.625 (physical pixels per logical pixel)\nI/flutter ( 6559):  │ configuration: Size(411.4, 683.4) at 2.625x (in logical pixels)\nI/flutter ( 6559):  │\nI/flutter ( 6559):  └─child: RenderCustomPaint\nI/flutter ( 6559):    │ creator: CustomPaint ← Banner ← CheckedModeBanner ←\nI/flutter ( 6559):    │   WidgetsApp-[GlobalObjectKey _MaterialAppState(1009803148)] ←\nI/flutter ( 6559):    │   Theme ← AnimatedTheme ← ScrollConfiguration ← MaterialApp ←\nI/flutter ( 6559):    │   [root]\nI/flutter ( 6559):    │ parentData: <none>\nI/flutter ( 6559):    │ constraints: BoxConstraints(w=411.4, h=683.4)\nI/flutter ( 6559):    │ size: Size(411.4, 683.4)\n... # 省略\n```\n\n这是根`RenderObject`对象的`toStringDeep`函数的输出。\n\n当调试布局问题时，关键要看的是`size`和`constraints`字段。约束沿着树向下传递，尺寸向上传递。\n\n如果您编写自己的渲染对象，则可以通过覆盖[`debugFillProperties()`](https://docs.flutter.io/flutter/rendering/Layer/debugFillProperties.html)将信息添加到转储。 将[DiagnosticsProperty](https://docs.flutter.io/flutter/foundation/DiagnosticsProperty-class.html)对象作为方法的参数，并调用父类方法。\n\n#### Layer树\n\n读者可以理解为渲染树是可以分层的，而最终绘制需要将不同的层合成起来，而Layer则是绘制时需要合成的层，如果您尝试调试合成问题，则可以使用[`debugDumpLayerTree()`](https://docs.flutter.io/flutter/rendering/debugDumpLayerTree.html)。对于上面的例子，它会输出：\n\n```\nI/flutter : TransformLayer\nI/flutter :  │ creator: [root]\nI/flutter :  │ offset: Offset(0.0, 0.0)\nI/flutter :  │ transform:\nI/flutter :  │   [0] 3.5,0.0,0.0,0.0\nI/flutter :  │   [1] 0.0,3.5,0.0,0.0\nI/flutter :  │   [2] 0.0,0.0,1.0,0.0\nI/flutter :  │   [3] 0.0,0.0,0.0,1.0\nI/flutter :  │\nI/flutter :  ├─child 1: OffsetLayer\nI/flutter :  │ │ creator: RepaintBoundary ← _FocusScope ← Semantics ← Focus-[GlobalObjectKey MaterialPageRoute(560156430)] ← _ModalScope-[GlobalKey 328026813] ← _OverlayEntry-[GlobalKey 388965355] ← Stack ← Overlay-[GlobalKey 625702218] ← Navigator-[GlobalObjectKey _MaterialAppState(859106034)] ← Title ← ⋯\nI/flutter :  │ │ offset: Offset(0.0, 0.0)\nI/flutter :  │ │\nI/flutter :  │ └─child 1: PictureLayer\nI/flutter :  │\nI/flutter :  └─child 2: PictureLayer\n```\n\n这是根`Layer`的`toStringDeep`输出的。\n\n根部的变换是应用设备像素比的变换; 在这种情况下，每个逻辑像素代表3.5个设备像素。\n\n`RepaintBoundary` widget在渲染树的层中创建了一个`RenderRepaintBoundary`。这用于减少需要重绘的需求量。\n\n### 语义\n\n您还可以调用[`debugDumpSemanticsTree()`](https://docs.flutter.io/flutter/rendering/debugDumpSemanticsTree.html)获取语义树（呈现给系统可访问性API的树）的转储。 要使用此功能，必须首先启用辅助功能，例如启用系统辅助工具或`SemanticsDebugger` （下面讨论）。\n\n对于上面的例子，它会输出:\n\n```\nI/flutter : SemanticsNode(0; Rect.fromLTRB(0.0, 0.0, 411.4, 683.4))\nI/flutter :  ├SemanticsNode(1; Rect.fromLTRB(0.0, 0.0, 411.4, 683.4))\nI/flutter :  │ └SemanticsNode(2; Rect.fromLTRB(0.0, 0.0, 411.4, 683.4); canBeTapped)\nI/flutter :  └SemanticsNode(3; Rect.fromLTRB(0.0, 0.0, 411.4, 683.4))\nI/flutter :    └SemanticsNode(4; Rect.fromLTRB(0.0, 0.0, 82.0, 36.0); canBeTapped; \"Dump App\")\n```\n\n### 调度\n\n要找出相对于帧的开始/结束事件发生的位置，可以切换[`debugPrintBeginFrameBanner`](https://docs.flutter.io/flutter/scheduler/debugPrintBeginFrameBanner.html)和[`debugPrintEndFrameBanner`](https://docs.flutter.io/flutter/scheduler/debugPrintEndFrameBanner.html)布尔值以将帧的开始和结束打印到控制台。\n\n例如:\n\n```\nI/flutter : ▄▄▄▄▄▄▄▄ Frame 12         30s 437.086ms ▄▄▄▄▄▄▄▄\nI/flutter : Debug print: Am I performing this work more than once per frame?\nI/flutter : Debug print: Am I performing this work more than once per frame?\nI/flutter : ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀\n```\n\n[`debugPrintScheduleFrameStacks`](https://docs.flutter.io/flutter/scheduler/debugPrintScheduleFrameStacks.html)还可以用来打印导致当前帧被调度的调用堆栈。\n\n### 可视化调试\n\n您也可以通过设置`debugPaintSizeEnabled`为`true`以可视方式调试布局问题。 这是来自`rendering`库的布尔值。它可以在任何时候启用，并在为true时影响绘制。 设置它的最简单方法是在`void main()`的顶部设置。\n\n当它被启用时，所有的盒子都会得到一个明亮的深青色边框，padding（来自widget如Padding）显示为浅蓝色，子widget周围有一个深蓝色框， 对齐方式（来自widget如Center和Align）显示为黄色箭头. 空白（如没有任何子节点的Container）以灰色显示。\n\n[`debugPaintBaselinesEnabled`](https://docs.flutter.io/flutter/rendering/debugPaintBaselinesEnabled.html)做了类似的事情，但对于具有基线的对象，文字基线以绿色显示，表意(ideographic)基线以橙色显示。\n\n[`debugPaintPointersEnabled`](https://docs.flutter.io/flutter/rendering/debugPaintPointersEnabled.html)标志打开一个特殊模式，任何正在点击的对象都会以深青色突出显示。 这可以帮助您确定某个对象是否以某种不正确的方式进行hit测试（Flutter检测点击的位置是否有能响应用户操作的widget）,例如，如果它实际上超出了其父项的范围，首先不会考虑通过hit测试。\n\n如果您尝试调试合成图层，例如以确定是否以及在何处添加`RepaintBoundary` widget，则可以使用[`debugPaintLayerBordersEnabled`](https://docs.flutter.io/flutter/rendering/debugPaintLayerBordersEnabled.html) 标志， 该标志用橙色或轮廓线标出每个层的边界，或者使用[`debugRepaintRainbowEnabled`](https://docs.flutter.io/flutter/rendering/debugRepaintRainbowEnabled.html)标志， 只要他们重绘时，这会使该层被一组旋转色所覆盖。\n\n所有这些标志只能在调试模式下工作。通常，Flutter框架中以“`debug...`” 开头的任何内容都只能在调试模式下工作。\n\n### 调试动画\n\n调试动画最简单的方法是减慢它们的速度。为此，请将[`timeDilation`](https://docs.flutter.io/flutter/scheduler/timeDilation.html)变量（在scheduler库中）设置为大于1.0的数字，例如50.0。 最好在应用程序启动时只设置一次。如果您在运行中更改它，尤其是在动画运行时将其值改小，则在观察时可能会出现倒退，这可能会导致断言命中，并且这通常会干扰我们的开发工作。\n\n### 调试性能问题\n\n要了解您的应用程序导致重新布局或重新绘制的原因，您可以分别设置[`debugPrintMarkNeedsLayoutStacks`](https://docs.flutter.io/flutter/rendering/debugPrintMarkNeedsLayoutStacks.html)和 [`debugPrintMarkNeedsPaintStacks`](https://docs.flutter.io/flutter/rendering/debugPrintMarkNeedsPaintStacks.html)标志。 每当渲染盒被要求重新布局和重新绘制时，这些都会将堆栈跟踪记录到控制台。如果这种方法对您有用，您可以使用`services`库中的`debugPrintStack()`方法按需打印堆栈痕迹。\n\n### 统计应用启动时间\n\n要收集有关Flutter应用程序启动所需时间的详细信息，可以在运行`flutter run`时使用`trace-startup`和`profile`选项。\n\n```shell\n$ flutter run --trace-startup --profile\n```\n\n跟踪输出保存为`start_up_info.json`，在Flutter工程目录在build目录下。输出列出了从应用程序启动到这些跟踪事件（以微秒捕获）所用的时间：\n\n- 进入Flutter引擎时.\n- 展示应用第一帧时.\n- 初始化Flutter框架时.\n- 完成Flutter框架初始化时.\n\n如 :\n\n```json\n{\n  \"engineEnterTimestampMicros\": 96025565262,\n  \"timeToFirstFrameMicros\": 2171978,\n  \"timeToFrameworkInitMicros\": 514585,\n  \"timeAfterFrameworkInitMicros\": 1657393\n}\n```\n\n### 跟踪Dart代码性能\n\n要执行自定义性能跟踪和测量Dart任意代码段的wall/CPU时间（类似于在Android上使用[systrace](https://developer.android.com/studio/profile/systrace.html)）。 使用`dart:developer`的[Timeline](https://api.dartlang.org/stable/dart-developer/Timeline-class.html)工具来包含你想测试的代码块，例如：\n\n```dart\nTimeline.startSync('interesting function');\n// iWonderHowLongThisTakes();\nTimeline.finishSync();\n```\n\n然后打开你应用程序的Observatory timeline页面，在“Recorded Streams”中选择‘Dart’复选框，并执行你想测量的功能。\n\n刷新页面将在Chrome的[跟踪工具](https://www.chromium.org/developers/how-tos/trace-event-profiling-tool)中显示应用按时间顺序排列的timeline记录。\n\n请确保运行`flutter run`时带有`--profile`标志，以确保运行时性能特征与您的最终产品差异最小。\n\n\n"
  },
  {
    "path": "src/v2/chapter2/flutter_assets_mgr.md",
    "content": "# 2.4 资源管理\n\nFlutter APP安装包中会包含代码和 assets（资源）两部分。Assets是会打包到程序安装包中的，可在运行时访问。常见类型的assets包括静态数据（例如JSON文件）、配置文件、图标和图片（JPEG，WebP，GIF，动画WebP / GIF，PNG，BMP和WBMP）等。\n\n## 指定 assets\n\n和包管理一样，Flutter也使用[`pubspec.yaml`](https://www.dartlang.org/tools/pub/pubspec)文件来管理应用程序所需的资源，举个例子:\n\n```yaml\nflutter:\n  assets:\n    - assets/my_icon.png\n    - assets/background.png\n```\n\n`assets`指定应包含在应用程序中的文件， 每个asset都通过相对于`pubspec.yaml`文件所在的文件系统路径来标识自身的路径。asset的声明顺序是无关紧要的，asset的实际目录可以是任意文件夹（在本示例中是assets文件夹）。\n\n在构建期间，Flutter将asset放置到称为 *asset bundle* 的特殊存档中，应用程序可以在运行时读取它们（但不能修改）。\n\n## Asset 变体（variant）\n\n构建过程支持“asset变体”的概念：不同版本的asset可能会显示在不同的上下文中。 在`pubspec.yaml`的assets部分中指定asset路径时，构建过程中，会在相邻子目录中查找具有相同名称的任何文件。这些文件随后会与指定的asset一起被包含在asset bundle中。\n\n例如，如果应用程序目录中有以下文件:\n\n- …/pubspec.yaml\n- …/graphics/my_icon.png\n- …/graphics/background.png\n- …/graphics/dark/background.png\n- …etc.\n\n然后`pubspec.yaml`文件中只需包含:\n\n```\nflutter:\n  assets:\n    - graphics/background.png\n```\n\n那么这两个`graphics/background.png`和`graphics/dark/background.png` 都将包含在您的asset bundle中。前者被认为是_main asset_ （主资源），后者被认为是一种变体（variant）。\n\n在选择匹配当前设备分辨率的图片时，Flutter会使用到asset变体（见下文），将来，Flutter可能会将这种机制扩展到本地化、阅读提示等方面。\n\n## 加载 assets\n\n您的应用可以通过[`AssetBundle`](https://docs.flutter.io/flutter/services/AssetBundle-class.html)对象访问其asset 。有两种主要方法允许从Asset bundle中加载字符串或图片（二进制）文件。\n\n### 加载文本assets\n\n- 通过[`rootBundle`](https://docs.flutter.io/flutter/services/rootBundle.html) 对象加载：每个Flutter应用程序都有一个[`rootBundle`](https://docs.flutter.io/flutter/services/rootBundle.html)对象， 通过它可以轻松访问主资源包，直接使用`package:flutter/services.dart`中全局静态的`rootBundle`对象来加载asset即可。\n- 通过 [`DefaultAssetBundle`](https://docs.flutter.io/flutter/widgets/DefaultAssetBundle-class.html) 加载：建议使用 [`DefaultAssetBundle`](https://docs.flutter.io/flutter/widgets/DefaultAssetBundle-class.html) 来获取当前BuildContext的AssetBundle。 这种方法不是使用应用程序构建的默认asset bundle，而是使父级widget在运行时动态替换的不同的AssetBundle，这对于本地化或测试场景很有用。\n\n通常，可以使用`DefaultAssetBundle.of()`在应用运行时来间接加载asset（例如JSON文件），而在widget上下文之外，或其它`AssetBundle`句柄不可用时，可以使用`rootBundle`直接加载这些asset，例如：\n\n```dart\nimport 'dart:async' show Future;\nimport 'package:flutter/services.dart' show rootBundle;\n\nFuture<String> loadAsset() async {\n  return await rootBundle.loadString('assets/config.json');\n}\n```\n\n### 加载图片\n\n类似于原生开发，Flutter也可以为当前设备加载适合其分辨率的图像。\n\n#### 声明分辨率相关的图片 assets\n\n[`AssetImage`](https://docs.flutter.io/flutter/painting/AssetImage-class.html) 可以将asset的请求逻辑映射到最接近当前设备像素比例（dpi）的asset。为了使这种映射起作用，必须根据特定的目录结构来保存asset：\n\n- …/image.png\n- …/**M**x/image.png\n- …/**N**x/image.png\n- …etc.\n\n其中M和N是数字标识符，对应于其中包含的图像的分辨率，也就是说，它们指定不同设备像素比例的图片。\n\n主资源默认对应于1.0倍的分辨率图片。看一个例子：\n\n- …/my_icon.png\n- …/2.0x/my_icon.png\n- …/3.0x/my_icon.png\n\n在设备像素比率为1.8的设备上，`.../2.0x/my_icon.png` 将被选择。对于2.7的设备像素比率，`.../3.0x/my_icon.png`将被选择。\n\n如果未在`Image` widget上指定渲染图像的宽度和高度，那么`Image` widget将占用与主资源相同的屏幕空间大小。 也就是说，如果`.../my_icon.png`是72px乘72px，那么`.../3.0x/my_icon.png`应该是216px乘216px; 但如果未指定宽度和高度，它们都将渲染为72像素×72像素（以逻辑像素为单位）。\n\n`pubspec.yaml`中asset部分中的每一项都应与实际文件相对应，但主资源项除外。当主资源缺少某个资源时，会按分辨率从低到高的顺序去选择 ，也就是说1x中没有的话会在2x中找，2x中还没有的话就在3x中找。\n\n#### 加载图片\n\n要加载图片，可以使用 [`AssetImage`](https://docs.flutter.io/flutter/painting/AssetImage-class.html)类。例如，我们可以从上面的asset声明中加载背景图片：\n\n```dart\nWidget build(BuildContext context) {\n  return new DecoratedBox(\n    decoration: new BoxDecoration(\n      image: new DecorationImage(\n        image: new AssetImage('graphics/background.png'),\n      ),\n    ),\n  );\n}\n```\n\n注意，`AssetImage` 并非是一个widget， 它实际上是一个`ImageProvider`，有些时候你可能期望直接得到一个显示图片的widget，那么你可以使用`Image.asset()`方法，如：\n\n```dart\nWidget build(BuildContext context) {\n  return Image.asset('graphics/background.png');\n}\n```\n\n使用默认的 asset bundle 加载资源时，内部会自动处理分辨率等，这些处理对开发者来说是无感知的。 (如果使用一些更低级别的类，如 [`ImageStream`](https://docs.flutter.io/flutter/painting/ImageStream-class.html)或 [`ImageCache`](https://docs.flutter.io/flutter/painting/ImageCache-class.html) 时你会注意到有与缩放相关的参数)\n\n#### 依赖包中的资源图片\n\n要加载依赖包中的图像，必须给`AssetImage`提供`package`参数。\n\n例如，假设您的应用程序依赖于一个名为“my_icons”的包，它具有如下目录结构：\n\n- …/pubspec.yaml\n- …/icons/heart.png\n- …/icons/1.5x/heart.png\n- …/icons/2.0x/heart.png\n- …etc.\n\n然后加载图像，使用:\n\n```dart\n new AssetImage('icons/heart.png', package: 'my_icons')\n```\n\n或\n\n```dart\nnew Image.asset('icons/heart.png', package: 'my_icons')\n```\n\n**注意：包在使用本身的资源时也应该加上`package`参数来获取**。\n\n\n\n##### 打包包中的 assets\n\n如果在`pubspec.yaml`文件中声明了期望的资源，它将会打包到相应的package中。特别是，包本身使用的资源必须在`pubspec.yaml`中指定。\n\n包也可以选择在其`lib/`文件夹中包含未在其`pubspec.yaml`文件中声明的资源。在这种情况下，对于要打包的图片，应用程序必须在`pubspec.yaml`中指定包含哪些图像。 例如，一个名为“fancy_backgrounds”的包，可能包含以下文件：\n\n- …/lib/backgrounds/background1.png\n- …/lib/backgrounds/background2.png\n- …/lib/backgrounds/background3.png\n\n要包含第一张图像，必须在`pubspec.yaml`的assets部分中声明它：\n\n```\nflutter:\n  assets:\n    - packages/fancy_backgrounds/backgrounds/background1.png\n```\n\n`lib/`是隐含的，所以它不应该包含在资产路径中。\n\n### 特定平台 assets\n\n上面的资源都是flutter应用中的，这些资源只有在Flutter框架运行之后才能使用，如果要给我们的应用设置APP图标或者添加启动图，那我们必须使用特定平台的assets。\n\n#### 设置APP图标\n\n更新Flutter应用程序启动图标的方式与在本机Android或iOS应用程序中更新启动图标的方式相同。\n\n- Android\n\n  在Flutter项目的根目录中，导航到`.../android/app/src/main/res`目录，里面包含了各种资源文件夹（如`mipmap-hdpi`已包含占位符图像“ic_launcher.png”，见图2-8）。 只需按照[Android开发人员指南](https://developer.android.com/guide/practices/ui_guidelines/icon_design_launcher.html#size)中的说明， 将其替换为所需的资源，并遵守每种屏幕密度（dpi）的建议图标大小标准。\n\n  ![图2-8](../imgs/2-8.png)\n\n  > **注意:** 如果您重命名.png文件，则还必须在您`AndroidManifest.xml`的`<application>`标签的`android:icon`属性中更新名称。\n\n- iOS\n\n  在Flutter项目的根目录中，导航到`.../ios/Runner`。该目录中`Assets.xcassets/AppIcon.appiconset`已经包含占位符图片（见图2-9）， 只需将它们替换为适当大小的图片，保留原始文件名称。\n  \n  ![图2-9](../imgs/2-9.png)\n\n   \n\n#### 更新启动页\n\n![图2-10](../imgs/2-10.png)\n\n在Flutter框架加载时，Flutter会使用本地平台机制绘制启动页。此启动页将持续到Flutter渲染应用程序的第一帧时。\n\n> **注意:** 这意味着如果您不在应用程序的`main()`方法中调用[runApp](https://docs.flutter.io/flutter/widgets/runApp.html) 函数 （或者更具体地说，如果您不调用[`window.render`](https://docs.flutter.io/flutter/dart-ui/Window/render.html)去响应[`window.onDrawFrame`](https://docs.flutter.io/flutter/dart-ui/Window/onDrawFrame.html)）的话， 启动屏幕将永远持续显示。\n\n##### Android\n\n要将启动屏幕（splash screen）添加到您的Flutter应用程序， 请导航至`.../android/app/src/main`。在`res/drawable/launch_background.xml`，通过自定义drawable来实现自定义启动界面（你也可以直接换一张图片）。\n\n##### iOS\n\n要将图片添加到启动屏幕（splash screen）的中心，请导航至`.../ios/Runner`。在`Assets.xcassets/LaunchImage.imageset`， 拖入图片，并命名为`LaunchImage.png`、`LaunchImage@2x.png`、`LaunchImage@3x.png`。 如果你使用不同的文件名，那您还必须更新同一目录中的`Contents.json`文件，图片的具体尺寸可以查看苹果官方的标准。\n\n您也可以通过打开Xcode完全自定义storyboard。在Project Navigator中导航到`Runner/Runner`然后通过打开`Assets.xcassets`拖入图片，或者通过在LaunchScreen.storyboard中使用Interface Builder进行自定义，如图2-11所示。\n\n![图2-11](../imgs/2-11.png)\n\n\n"
  },
  {
    "path": "src/v2/chapter2/flutter_package_mgr.md",
    "content": "# 2.3 包管理\n\n在软件开发中，很多时候有一些公共的库或SDK可能会被很多项目用到，因此，将这些代码单独抽到一个独立模块，然后哪个项目需要使用时再直接集成这个模块，便可大大提高开发效率。很多编程语言或开发工具都支持这种“模块共享”机制，如Java语言中这种独立模块会被打成一个jar包，Android中的aar包，Web开发中的npm包等。为了方便表述，我们将这种可共享的独立模块统一称为“包”（ Package）。\n\n一个APP在实际开发中往往会依赖很多包，而这些包通常都有交叉依赖关系、版本依赖等，如果由开发者手动来管理应用中的依赖包将会非常麻烦。因此，各种开发生态或编程语言官方通常都会提供一些包管理工具，比如在Android提供了Gradle来管理依赖，iOS用Cocoapods或Carthage来管理依赖，Node中通过npm等。而在Flutter开发中也有自己的包管理工具。本节我们主要介绍一下flutter如何使用配置文件`pubspec.yaml`（位于项目根目录）来管理第三方依赖包。\n\nYAML是一种直观、可读性高并且容易被人类阅读的文件格式，它和xml或Json相比，它语法简单并非常容易解析，所以YAML常用于配置文件，Flutter也是用yaml文件作为其配置文件。Flutter项目默认的配置文件是`pubspec.yaml`，我们看一个简单的示例：\n\n```yaml\nname: flutter_in_action\ndescription: First Flutter application.\n\nversion: 1.0.0+1\n\ndependencies:\n  flutter:\n    sdk: flutter\n  cupertino_icons: ^0.1.2\n\ndev_dependencies:\n  flutter_test:\n    sdk: flutter\n    \nflutter:\n  uses-material-design: true\n```\n\n下面，我们逐一解释一下各个字段的意义：\n\n- `name`：应用或包名称。\n- `description`: 应用或包的描述、简介。\n- `version`：应用或包的版本号。\n- `dependencies`：应用或包依赖的其它包或插件。\n- `dev_dependencies`：开发环境依赖的工具包（而不是flutter应用本身依赖的包）。\n- `flutter`：flutter相关的配置选项。\n\n如果我们的Flutter应用本身依赖某个包，我们需要将所依赖的包添加到`dependencies` 下，接下来我们通过一个例子来演示一下如何添加、下载并使用第三方包。\n\n## Pub仓库\n\nPub（https://pub.dev/ ）是Google官方的Dart Packages仓库，类似于node中的npm仓库，android中的jcenter。我们可以在Pub上面查找我们需要的包和插件，也可以向Pub发布我们的包和插件。我们将在后面的章节中介绍如何向Pub发布我们的包和插件。\n\n## 示例\n\n接下来，我们实现一个显示随机字符串的widget。有一个名为“english_words”的开源软件包，其中包含数千个常用的英文单词以及一些实用功能。我们首先在pub上找到english_words这个包（如图2-5所示），确定其最新的版本号和是否支持Flutter。\n\n![图2-5](../imgs/2-5.png)\n\n我们看到“english_words”包最新的版本是3.1.3，并且支持flutter，接下来：\n\n1. 将“english_words”（3.1.3版本）添加到依赖项列表，如下：\n\n   ```yaml\n   dependencies:\n     flutter:\n       sdk: flutter\n   \n     cupertino_icons: ^0.1.0\n     # 新添加的依赖\n     english_words: ^3.1.3\n   ```\n\n2. 下载包。在Android Studio的编辑器视图中查看pubspec.yaml时（图2-6），单击右上角的 **Packages get** 。\n\n   ![图2-6](../imgs/2-6.png)\n\n   这会将依赖包安装到您的项目。我们可以在控制台中看到以下内容：\n   \n   ```shell\n   flutter packages get\n   Running \"flutter packages get\" in flutter_in_action...\n   Process finished with exit code 0\n   ```\n   \n   我们也可以在控制台，定位到当前工程目录，然后手动运行`flutter packages get` 命令来下载依赖包。另外，需要注意`dependencies`和`dev_dependencies`的区别，前者的依赖包将作为APP的源码的一部分参与编译，生成最终的安装包。而后者的依赖包只是作为开发阶段的一些工具包，主要是用于帮助我们提高开发、测试效率，比如flutter的自动化测试包等。\n\n3. 引入`english_words`包。\n\n   ```dart\n   import 'package:english_words/english_words.dart';\n   ```\n\n   在输入时，Android Studio会自动提供有关库导入的建议选项。导入后该行代码将会显示为灰色，表示导入的库尚未使用。\n\n4. 使用`english_words`包来生成随机字符串。\n\n   ```dart\n   class RandomWordsWidget extends StatelessWidget {\n     @override\n     Widget build(BuildContext context) {\n      // 生成随机字符串\n       final wordPair = new WordPair.random();\n       return Padding(\n         padding: const EdgeInsets.all(8.0),\n         child: new Text(wordPair.toString()),\n       );\n     }\n   }\n   ```\n\n   我们将`RandomWordsWidget` 添加到 `_MyHomePageState.build` 的`Column`的子widget中。\n\n   ```dart\n   Column(\n     mainAxisAlignment: MainAxisAlignment.center,\n     children: <Widget>[\n       ... //省略无关代码\n       RandomWordsWidget(),\n     ],\n   )\n   ```\n\n5. 如果应用程序正在运行，请使用热重载按钮（⚡️图标） 更新正在运行的应用程序。每次单击热重载或保存项目时，都会在正在运行的应用程序中随机选择不同的单词对。 这是因为单词对是在 `build` 方法内部生成的。每次热更新时，`build`方法都会被执行，运行效果如图2-7所示。\n\n   ![图2-7](../imgs/2-7.png)\n\n\n## 其它依赖方式\n\n上文所述的依赖方式是依赖Pub仓库的。但我们还可以依赖本地包和git仓库。\n\n- 依赖本地包\n\n  如果我们正在本地开发一个包，包名为pkg1，我们可以通过下面方式依赖：\n\n  ```yaml\n  dependencies:\n  \tpkg1:\n          path: ../../code/pkg1\n  ```\n\n  路径可以是相对的，也可以是绝对的。\n\n- 依赖Git：你也可以依赖存储在Git仓库中的包。如果软件包位于仓库的根目录中，请使用以下语法\n\n  ```yaml\n  dependencies:\n    pkg1:\n      git:\n        url: git://github.com/xxx/pkg1.git\n  ```\n\n  上面假定包位于Git存储库的根目录中。如果不是这种情况，可以使用path参数指定相对位置，例如：\n\n  ```yaml\n  dependencies:\n    package1:\n      git:\n        url: git://github.com/flutter/packages.git\n        path: packages/package1        \n  ```\n\n上面介绍的这些依赖方式是Flutter开发中常用的，但还有一些其它依赖方式，完整的内容读者可以自行查看：https://www.dartlang.org/tools/pub/dependencies 。\n\n## 总结\n\n本节介绍了Flutter中包管理、引用、下载的整体流程，我们将在后面的章节中介绍如何开发并发布我们自己的包。\n\n\n"
  },
  {
    "path": "src/v2/chapter2/flutter_router.md",
    "content": "# 2.2 路由管理\n\n路由(Route)在移动开发中通常指页面（Page），这跟web开发中单页应用的Route概念意义是相同的，Route在Android中通常指一个Activity，在iOS中指一个ViewController。所谓路由管理，就是管理页面之间如何跳转，通常也可被称为导航管理。Flutter中的路由管理和原生开发类似，无论是Android还是iOS，导航管理都会维护一个路由栈，路由入栈(push)操作对应打开一个新页面，路由出栈(pop)操作对应页面关闭操作，而路由管理主要是指如何来管理路由栈。\n\n## 2.2.1 一个简单示例\n\n我们在上一节“计数器”示例的基础上，做如下修改：\n\n1. 创建一个新路由，命名“NewRoute”\n\n   ```dart\n   class NewRoute extends StatelessWidget {\n     @override\n     Widget build(BuildContext context) {\n       return Scaffold(\n         appBar: AppBar(\n           title: Text(\"New route\"),\n         ),\n         body: Center(\n           child: Text(\"This is new route\"),\n         ),\n       );\n     }\n   }\n   ```\n\n   新路由继承自`StatelessWidget`，界面很简单，在页面中间显示一句\"This is new route\"。\n\n2. 在`_MyHomePageState.build`方法中的`Column`的子widget中添加一个按钮（`FlatButton`） :\n\n   ```dart\n   Column(\n         mainAxisAlignment: MainAxisAlignment.center,\n         children: <Widget>[\n         ... //省略无关代码\n         FlatButton(\n            child: Text(\"open new route\"),\n            textColor: Colors.blue,\n            onPressed: () {\n             //导航到新路由   \n             Navigator.push( context,\n              MaterialPageRoute(builder: (context) {\n                 return NewRoute();\n              }));\n             },\n            ),\n          ],\n    )\n   ```\n\n   我们添加了一个打开新路由的按钮，并将按钮文字颜色设置为蓝色，点击该按钮后就会打开新的路由页面，效果如图2-2和2-3所示。\n\n   ![图2-2](../imgs/2-2.png) ![图2-3](../imgs/2-3.png)\n\n\n\n\n\n## 2.2.2 MaterialPageRoute\n\n`MaterialPageRoute`继承自`PageRoute`类，`PageRoute`类是一个抽象类，表示占有整个屏幕空间的一个模态路由页面，它还定义了路由构建及切换时过渡动画的相关接口及属性。`MaterialPageRoute` 是Material组件库提供的组件，它可以针对不同平台，实现与平台页面切换动画风格一致的路由切换动画：\n\n- 对于Android，当打开新页面时，新的页面会从屏幕底部滑动到屏幕顶部；当关闭页面时，当前页面会从屏幕顶部滑动到屏幕底部后消失，同时上一个页面会显示到屏幕上。\n- 对于iOS，当打开页面时，新的页面会从屏幕右侧边缘一致滑动到屏幕左边，直到新页面全部显示到屏幕上，而上一个页面则会从当前屏幕滑动到屏幕左侧而消失；当关闭页面时，正好相反，当前页面会从屏幕右侧滑出，同时上一个页面会从屏幕左侧滑入。\n\n下面我们介绍一下`MaterialPageRoute` 构造函数的各个参数的意义：\n\n```dart\n  MaterialPageRoute({\n    WidgetBuilder builder,\n    RouteSettings settings,\n    bool maintainState = true,\n    bool fullscreenDialog = false,\n  })\n```\n\n- `builder` 是一个WidgetBuilder类型的回调函数，它的作用是构建路由页面的具体内容，返回值是一个widget。我们通常要实现此回调，返回新路由的实例。\n- `settings` 包含路由的配置信息，如路由名称、是否初始路由（首页）。\n- `maintainState`：默认情况下，当入栈一个新路由时，原来的路由仍然会被保存在内存中，如果想在路由没用的时候释放其所占用的所有资源，可以设置`maintainState`为false。\n- `fullscreenDialog`表示新的路由页面是否是一个全屏的模态对话框，在iOS中，如果`fullscreenDialog`为`true`，新页面将会从屏幕底部滑入（而不是水平方向）。\n\n> 如果想自定义路由切换动画，可以自己继承PageRoute来实现，我们将在后面介绍动画时，实现一个自定义的路由组件。\n\n\n\n## 2.2.3 Navigator\n\n`Navigator`是一个路由管理的组件，它提供了打开和退出路由页方法。`Navigator`通过一个栈来管理活动路由集合。通常当前屏幕显示的页面就是栈顶的路由。`Navigator`提供了一系列方法来管理路由栈，在此我们只介绍其最常用的两个方法：\n\n### Future  push(BuildContext context, Route route)\n\n将给定的路由入栈（即打开新的页面），返回值是一个`Future`对象，用以接收新路由出栈（即关闭）时的返回数据。\n\n### bool  pop(BuildContext context, [ result ])\n\n将栈顶路由出栈，`result`为页面关闭时返回给上一个页面的数据。\n\n`Navigator` 还有很多其它方法，如`Navigator.replace`、`Navigator.popUntil`等，详情请参考API文档或SDK源码注释，在此不再赘述。下面我们还需要介绍一下路由相关的另一个概念“命名路由”。\n\n### 实例方法\n\nNavigator类中第一个参数为context的**静态方法**都对应一个Navigator的**实例方法**， 比如`Navigator.push(BuildContext context, Route route) `等价于`Navigator.of(context).push(Route route)` ，下面命名路由相关的方法也是一样的。\n\n\n\n## 2.2.4 路由传值\n\n很多时候，在路由跳转时我们需要带一些参数，比如打开商品详情页时，我们需要带一个商品id，这样商品详情页才知道展示哪个商品信息；又比如我们在填写订单时需要选择收货地址，打开地址选择页并选择地址后，可以将用户选择的地址返回到订单页等等。下面我们通过一个简单的示例来演示新旧路由如何传参。\n\n### 示例\n\n我们创建一个`TipRoute`路由，它接受一个提示文本参数，负责将传入它的文本显示在页面上，另外`TipRoute`中我们添加一个“返回”按钮，点击后在返回上一个路由的同时会带上一个返回参数，下面我们看一下实现代码。\n\n`TipRoute`实现代码：\n\n```dart\nclass TipRoute extends StatelessWidget {\n  TipRoute({\n    Key key,\n    @required this.text,  // 接收一个text参数\n  }) : super(key: key);\n  final String text;\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        title: Text(\"提示\"),\n      ),\n      body: Padding(\n        padding: EdgeInsets.all(18),\n        child: Center(\n          child: Column(\n            children: <Widget>[\n              Text(text),\n              RaisedButton(\n                onPressed: () => Navigator.pop(context, \"我是返回值\"),\n                child: Text(\"返回\"),\n              )\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n```\n\n下面是打开新路由`TipRoute`的代码：\n\n```dart\nclass RouterTestRoute extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return Center(\n      child: RaisedButton(\n        onPressed: () async {\n          // 打开`TipRoute`，并等待返回结果\n          var result = await Navigator.push(\n            context,\n            MaterialPageRoute(\n              builder: (context) {\n                return TipRoute(\n                  // 路由参数\n                  text: \"我是提示xxxx\",\n                );\n              },\n            ),\n          );\n          //输出`TipRoute`路由返回结果\n          print(\"路由返回值: $result\");\n        },\n        child: Text(\"打开提示页\"),\n      ),\n    );\n  }\n}\n```\n\n运行上面代码，点击`RouterTestRoute`页的“打开提示页”按钮，会打开`TipRoute`页，运行效果如图2-4所示下：\n\n![图2-4](../imgs/2-4.png)\n\n需要说明：\n\n1. 提示文案“我是提示xxxx”是通过`TipRoute`的`text`参数传递给新路由页的。我们可以通过等待`Navigator.push(…)`返回的`Future`来获取新路由的返回数据。\n\n2. 在`TipRoute`页中有两种方式可以返回到上一页；第一种方式时直接点击导航栏返回箭头，第二种方式是点击页面中的“返回”按钮。这两种返回方式的区别是前者不会返回数据给上一个路由，而后者会。下面是分别点击页面中的返回按钮和导航栏返回箭头后，`RouterTestRoute`页中`print`方法在控制台输出的内容：\n\n   ```\n   I/flutter (27896): 路由返回值: 我是返回值\n   I/flutter (27896): 路由返回值: null\n   ```\n\n上面介绍的是非命名路由的传值方式，命名路由的传值方式会有所不同，我们会在下面介绍命名路由时介绍。\n\n## 2.2.5 命名路由\n\n所谓“命名路由”（Named Route）即有名字的路由，我们可以先给路由起一个名字，然后就可以通过路由名字直接打开新的路由了，这为路由管理带来了一种直观、简单的方式。\n\n### 路由表\n\n要想使用命名路由，我们必须先提供并注册一个路由表（routing table），这样应用程序才知道哪个名字与哪个路由组件相对应。其实注册路由表就是给路由起名字，路由表的定义如下：\n\n```dart\nMap<String, WidgetBuilder> routes;\n```\n\n它是一个`Map`，key为路由的名字，是个字符串；value是个`builder`回调函数，用于生成相应的路由widget。我们在通过路由名字打开新路由时，应用会根据路由名字在路由表中查找到对应的`WidgetBuilder`回调函数，然后调用该回调函数生成路由widget并返回。\n\n### 注册路由表\n\n路由表的注册方式很简单，我们回到之前“计数器”的示例，然后在`MyApp`类的`build`方法中找到`MaterialApp`，添加`routes`属性，代码如下：\n\n```dart\nMaterialApp(\n  title: 'Flutter Demo',\n  theme: ThemeData(\n    primarySwatch: Colors.blue,\n  ),\n  //注册路由表\n  routes:{\n   \"new_page\":(context) => NewRoute(),\n    ... // 省略其它路由注册信息\n  } ,\n  home: MyHomePage(title: 'Flutter Demo Home Page'),\n);\n```\n\n现在我们就完成了路由表的注册。上面的代码中`home`路由并没有使用命名路由，如果我们也想将`home`注册为命名路由应该怎么做呢？其实很简单，直接看代码：\n\n```dart\nMaterialApp(\n  title: 'Flutter Demo',\n  initialRoute:\"/\", //名为\"/\"的路由作为应用的home(首页)\n  theme: ThemeData(\n    primarySwatch: Colors.blue,\n  ),\n  //注册路由表\n  routes:{\n   \"new_page\":(context) => NewRoute(),\n   \"/\":(context) => MyHomePage(title: 'Flutter Demo Home Page'), //注册首页路由\n  } \n);\n```\n\n可以看到，我们只需在路由表中注册一下`MyHomePage`路由，然后将其名字作为`MaterialApp`的`initialRoute`属性值即可，该属性决定应用的初始路由页是哪一个命名路由。\n\n### 通过路由名打开新路由页\n\n要通过路由名称来打开新路由，可以使用`Navigator` 的`pushNamed`方法：\n\n```dart\nFuture pushNamed(BuildContext context, String routeName,{Object arguments})\n```\n\n`Navigator` 除了`pushNamed`方法，还有`pushReplacementNamed`等其他管理命名路由的方法，读者可以自行查看API文档。接下来我们通过路由名来打开新的路由页，修改`FlatButton`的`onPressed`回调代码，改为：\n\n```dart\nonPressed: () {\n  Navigator.pushNamed(context, \"new_page\");\n  //Navigator.push(context,\n  //  MaterialPageRoute(builder: (context) {\n  //  return NewRoute();\n  //}));  \n},\n```\n\n热重载应用，再次点击“open new route”按钮，依然可以打开新的路由页。\n\n### 命名路由参数传递\n\n在Flutter最初的版本中，命名路由是不能传递参数的，后来才支持了参数；下面展示命名路由如何传递并获取路由参数：\n\n我们先注册一个路由：\n\n```dart\n routes:{\n   \"new_page\":(context) => EchoRoute(),\n  } ,\n```\n\n在路由页通过`RouteSetting`对象获取路由参数：\n\n```dart\nclass EchoRoute extends StatelessWidget {\n\n  @override\n  Widget build(BuildContext context) {\n    //获取路由参数  \n    var args=ModalRoute.of(context).settings.arguments;\n    //...省略无关代码\n  }\n}\n```\n\n在打开路由时传递参数\n\n```dart\nNavigator.of(context).pushNamed(\"new_page\", arguments: \"hi\");\n```\n\n### 适配\n\n假设我们也想将上面路由传参示例中的`TipRoute`路由页注册到路由表中，以便也可以通过路由名来打开它。但是，由于`TipRoute`接受一个`text` 参数，我们如何在不改变`TipRoute`源码的前提下适配这种情况？其实很简单：\n\n```dart\nMaterialApp(\n  ... //省略无关代码\n  routes: {\n   \"tip2\": (context){\n     return TipRoute(text: ModalRoute.of(context).settings.arguments);\n   },\n }, \n);\n```\n\n## 2.2.6 路由生成钩子\n\n假设我们要开发一个电商APP，当用户没有登录时可以看店铺、商品等信息，但交易记录、购物车、用户个人信息等页面需要登录后才能看。为了实现上述功能，我们需要在打开每一个路由页前判断用户登录状态！如果每次打开路由前我们都需要去判断一下将会非常麻烦，那有什么更好的办法吗？答案是有！\n\n`MaterialApp`有一个`onGenerateRoute`属性，它在打开命名路由时可能会被调用，之所以说可能，是因为当调用`Navigator.pushNamed(...)`打开命名路由时，如果指定的路由名在路由表中已注册，则会调用路由表中的`builder`函数来生成路由组件；如果路由表中没有注册，才会调用`onGenerateRoute`来生成路由。`onGenerateRoute`回调签名如下：\n\n```dart\nRoute<dynamic> Function(RouteSettings settings)\n```\n\n有了`onGenerateRoute`回调，要实现上面控制页面权限的功能就非常容易：我们放弃使用路由表，取而代之的是提供一个`onGenerateRoute`回调，然后在该回调中进行统一的权限控制，如：\n\n```dart\nMaterialApp(\n  ... //省略无关代码\n  onGenerateRoute:(RouteSettings settings){\n\t  return MaterialPageRoute(builder: (context){\n\t\t   String routeName = settings.name;\n       // 如果访问的路由页需要登录，但当前未登录，则直接返回登录页路由，\n       // 引导用户登录；其它情况则正常打开路由。\n     }\n   );\n  }\n);\n```\n\n> 注意，`onGenerateRoute`只会对命名路由生效。\n\n## 2.2.7 总结\n\n本章先介绍了Flutter中路由管理、传参的方式，然后又着重介绍了命名路由相关内容。在此需要说明一点，由于命名路由只是一种可选的路由管理方式，在实际开发中，读者可能心中会犹豫到底使用哪种路由管理方式。在此，根据笔者经验，建议读者最好统一使用命名路由的管理方式，这将会带来如下好处：\n\n1. 语义化更明确。\n2. 代码更好维护；如果使用匿名路由，则必须在调用`Navigator.push`的地方创建新路由页，这样不仅需要import新路由页的dart文件，而且这样的代码将会非常分散。\n3. 可以通过`onGenerateRoute`做一些全局的路由跳转前置处理逻辑。\n\n综上所述，笔者比较建议使用命名路由，当然这并不是什么金科玉律，读者可以根据自己偏好或实际情况来决定。\n\n另外，还有一些关于路由管理的内容我们没有介绍，比如路由MaterialApp中还有`navigatorObservers`和`onUnknownRoute`两个回调属性，前者可以监听所有路由跳转动作，后者在打开一个不存在的命名路由时会被调用，由于这些功能并不常用，而且也比较简单，我们便不再花费篇幅来介绍了，读者可以自行查看API文档。"
  },
  {
    "path": "src/v2/chapter2/index.md",
    "content": "## 简介\n\n本章将通过一些简单的示例来一步步介绍Flutter的开发流程.\n\n## 本章目录\n\n* [2.1：计数器示例](first_flutter_app.md)\n* [2.2：路由管理](flutter_router.md)  \n* [2.3：包管理](flutter_package_mgr.md)        \n* [2.4：资源管理](flutter_assets_mgr.md)    \n* [2.5：调试Flutter APP](flutter_app_debug.md)\n* [2.6：Dart线程模型及异常捕获](thread_model_and_error_report.md)\n"
  },
  {
    "path": "src/v2/chapter2/thread_model_and_error_report.md",
    "content": "# 2.6 Flutter异常捕获\n\n在介绍Flutter异常捕获之前必须先了解一下Dart单线程模型，只有了解了Dart的代码执行流程，我们才能知道该在什么地方去捕获异常。\n\n## 2.6.1 Dart单线程模型\n\n在Java和Objective-C（以下简称“OC”）中，如果程序发生异常且没有被捕获，那么程序将会终止，但是这在Dart或JavaScript中则不会！究其原因，这和它们的运行机制有关系。Java和OC都是多线程模型的编程语言，任意一个线程触发异常且该异常未被捕获时，就会导致整个进程退出。但Dart和JavaScript不会，它们都是单线程模型，运行机制很相似(但有区别)，下面我们通过Dart官方提供的一张图来看看Dart大致运行原理：\n\n\n\n![图2-12](../imgs/2-12.png)\n\nDart 在单线程中是以消息循环机制来运行的，其中包含两个任务队列，一个是“微任务队列”  **microtask queue**，另一个叫做“事件队列” **event queue**。从图中可以发现，微任务队列的执行优先级高于事件队列。\n\n现在我们来介绍一下Dart线程运行过程，如上图中所示，入口函数 main() 执行完后，消息循环机制便启动了。首先会按照先进先出的顺序逐个执行微任务队列中的任务，事件任务执行完毕后程序便会退出，但是，在事件任务执行的过程中也可以插入新的微任务和事件任务，在这种情况下，整个线程的执行过程便是一直在循环，不会退出，而Flutter中，主线程的执行过程正是如此，永不终止。\n\n在Dart中，所有的外部事件任务都在事件队列中，如IO、计时器、点击、以及绘制事件等，而微任务通常来源于Dart内部，并且微任务非常少，之所以如此，是因为微任务队列优先级高，如果微任务太多，执行时间总和就越久，事件队列任务的延迟也就越久，对于GUI应用来说最直观的表现就是比较卡，所以必须得保证微任务队列不会太长。值得注意的是，我们可以通过`Future.microtask(…)`方法向微任务队列插入一个任务。\n\n在事件循环中，当某个任务发生异常并没有被捕获时，程序并不会退出，而直接导致的结果是**当前任务**的后续代码就不会被执行了，也就是说一个任务中的异常是不会影响其它任务执行的。\n\n\n\n## 2.6.2 Flutter异常捕获\n\nDart中可以通过`try/catch/finally`来捕获代码块异常，这个和其它编程语言类似，如果读者不清楚，可以查看Dart语言文档，不再赘述，下面我们看看Flutter中的异常捕获。\n\n### Flutter框架异常捕获\n\nFlutter 框架为我们在很多关键的方法进行了异常捕获。这里举一个例子，当我们布局发生越界或不合规范时，Flutter就会自动弹出一个错误界面，这是因为Flutter已经在执行build方法时添加了异常捕获，最终的源码如下：\n\n```dart\n@override\nvoid performRebuild() {\n ...\n  try {\n    //执行build方法  \n    built = build();\n  } catch (e, stack) {\n    // 有异常时则弹出错误提示  \n    built = ErrorWidget.builder(_debugReportException('building $this', e, stack));\n  } \n  ...\n}      \n```\n\n可以看到，在发生异常时，Flutter默认的处理方式是弹一个ErrorWidget，但如果我们想自己捕获异常并上报到报警平台的话应该怎么做？我们进入`_debugReportException()`方法看看：\n\n```dart\nFlutterErrorDetails _debugReportException(\n  String context,\n  dynamic exception,\n  StackTrace stack, {\n  InformationCollector informationCollector\n}) {\n  //构建错误详情对象  \n  final FlutterErrorDetails details = FlutterErrorDetails(\n    exception: exception,\n    stack: stack,\n    library: 'widgets library',\n    context: context,\n    informationCollector: informationCollector,\n  );\n  //报告错误 \n  FlutterError.reportError(details);\n  return details;\n}\n```\n\n我们发现，错误是通过`FlutterError.reportError`方法上报的，继续跟踪：\n\n```dart\n\nstatic void reportError(FlutterErrorDetails details) {\n  ...\n  if (onError != null)\n    onError(details); //调用了onError回调\n}\n```\n\n我们发现`onError`是`FlutterError`的一个静态属性，它有一个默认的处理方法 `dumpErrorToConsole`，到这里就清晰了，如果我们想自己上报异常，只需要提供一个自定义的错误处理回调即可，如：\n\n```dart\nvoid main() {\n  FlutterError.onError = (FlutterErrorDetails details) {\n    reportError(details);\n  };\n ...\n}\n```\n\n这样我们就可以处理那些Flutter为我们捕获的异常了，接下来我们看看如何捕获其它异常。\n\n### 其它异常捕获与日志收集\n\n在Flutter中，还有一些Flutter没有为我们捕获的异常，如调用空对象方法异常、Future中的异常。在Dart中，异常分两类：同步异常和异步异常，同步异常可以通过`try/catch`捕获，而异步异常则比较麻烦，如下面的代码是捕获不了`Future`的异常的：\n\n```dart\ntry{\n    Future.delayed(Duration(seconds: 1)).then((e) => Future.error(\"xxx\"));\n}catch (e){\n    print(e)\n}\n```\n\nDart中有一个`runZoned(...)` 方法，可以给执行对象指定一个Zone。Zone表示一个代码执行的环境范围，为了方便理解，读者可以将Zone类比为一个代码执行沙箱，不同沙箱的之间是隔离的，沙箱可以捕获、拦截或修改一些代码行为，如Zone中可以捕获日志输出、Timer创建、微任务调度的行为，同时Zone也可以捕获所有未处理的异常。下面我们看看`runZoned(...)`方法定义：\n\n```dart\nR runZoned<R>(R body(), {\n    Map zoneValues, \n    ZoneSpecification zoneSpecification,\n    Function onError,\n}) \n```\n\n- `zoneValues`: Zone 的私有数据，可以通过实例`zone[key]`获取，可以理解为每个“沙箱”的私有数据。\n\n- `zoneSpecification`：Zone的一些配置，可以自定义一些代码行为，比如拦截日志输出行为等，举个例子：\n\n  下面是拦截应用中所有调用`print`输出日志的行为。\n\n  ```dart\n  main() {\n    runZoned(() => runApp(MyApp()), zoneSpecification: new ZoneSpecification(\n        print: (Zone self, ZoneDelegate parent, Zone zone, String line) {\n          parent.print(zone, \"Intercepted: $line\");\n        }),\n    );\n  }\n  ```\n\n  这样一来，我们APP中所有调用`print`方法输出日志的行为都会被拦截，通过这种方式，我们也可以在应用中记录日志，等到应用触发未捕获的异常时，将异常信息和日志统一上报。ZoneSpecification还可以自定义一些其他行为，读者可以查看API文档。\n\n- `onError`：Zone中未捕获异常处理回调，如果开发者提供了onError回调或者通过`ZoneSpecification.handleUncaughtError`指定了错误处理回调，那么这个zone将会变成一个error-zone，该error-zone中发生未捕获异常(无论同步还是异步)时都会调用开发者提供的回调，如：\n\n  ```dart\n  runZoned(() {\n      runApp(MyApp());\n  }, onError: (Object obj, StackTrace stack) {\n      var details=makeDetails(obj,stack);\n      reportError(details);\n  });\n  ```\n\n  这样一来，结合上面的`FlutterError.onError`我们就可以捕获我们Flutter应用中全部错误了！需要注意的是，error-zone内部发生的错误是不会跨越当前error-zone的边界的，如果想跨越error-zone边界去捕获异常，可以通过共同的“源”zone来捕获，如：\n\n  ```dart\n  var future = new Future.value(499);\n  runZoned(() {\n  \tvar future2 = future.then((_) { throw \"error in first error-zone\"; });\n  \trunZoned(() {\n  \t\tvar future3 = future2.catchError((e) { print(\"Never reached!\"); });\n  \t}, onError: (e) { print(\"unused error handler\"); });\n  }, onError: (e) { print(\"catches error of first error-zone.\"); });\n  \n  ```\n\n### 总结\n我们最终的异常捕获和上报代码大致如下：\n\n```dart\nvoid collectLog(String line){\n    ... //收集日志\n}\nvoid reportErrorAndLog(FlutterErrorDetails details){\n    ... //上报错误和日志逻辑\n}\n\nFlutterErrorDetails makeDetails(Object obj, StackTrace stack){\n    ...// 构建错误信息\n}\n\nvoid main() {\n  FlutterError.onError = (FlutterErrorDetails details) {\n    reportErrorAndLog(details);\n  };\n  runZoned(\n    () => runApp(MyApp()),\n    zoneSpecification: ZoneSpecification(\n      print: (Zone self, ZoneDelegate parent, Zone zone, String line) {\n        collectLog(line); // 收集日志\n      },\n    ),\n    onError: (Object obj, StackTrace stack) {\n      var details = makeDetails(obj, stack);\n      reportErrorAndLog(details);\n    },\n  );\n}\n```\n\n\n\n\n\n"
  },
  {
    "path": "src/v2/chapter3/buttons.md",
    "content": "# 3.4 按钮\n\n## 3.4.1 Material组件库中的按钮\n\nMaterial 组件库中提供了多种按钮组件如`RaisedButton`、`FlatButton`、`OutlineButton`等，它们都是直接或间接对`RawMaterialButton`组件的包装定制，所以他们大多数属性都和`RawMaterialButton`一样。在介绍各个按钮时我们先介绍其默认外观，而按钮的外观大都可以通过属性来自定义，我们在后面统一介绍这些属性。另外，所有Material 库中的按钮都有如下相同点：\n\n1. 按下时都会有“水波动画”（又称“涟漪动画”，就是点击时按钮上会出现水波荡漾的动画）。\n2. 有一个`onPressed`属性来设置点击回调，当按钮按下时会执行该回调，如果不提供该回调则按钮会处于禁用状态，禁用状态不响应用户点击。\n\n### RaisedButton\n\n`RaisedButton` 即\"漂浮\"按钮，它默认带有阴影和灰色背景。按下后，阴影会变大，如图3-10所示：\n\n![图3-10](../imgs/3-10.png)\n\n使用`RaisedButton`非常简单，如：\n\n```dart\nRaisedButton(\n  child: Text(\"normal\"),\n  onPressed: () {},\n);\n```\n\n### FlatButton\n\n`FlatButton`即扁平按钮，默认背景透明并不带阴影。按下后，会有背景色，如图3-11所示：\n\n![图3-11](../imgs/3-11.png)\n\n使用FlatButton也很简单，代码如下：\n\n```dart\nFlatButton(\n  child: Text(\"normal\"),\n  onPressed: () {},\n)\n```\n\n### OutlineButton\n\n`OutlineButton`默认有一个边框，不带阴影且背景透明。按下后，边框颜色会变亮、同时出现背景和阴影(较弱)，如图3-12所示：\n\n![图3-12](../imgs/3-12.png)\n\n使用`OutlineButton`也很简单，代码如下：\n\n```dart\nOutlineButton(\n  child: Text(\"normal\"),\n  onPressed: () {},\n)\n```\n\n### IconButton\n\n`IconButton`是一个可点击的Icon，不包括文字，默认没有背景，点击后会出现背景，如图3-13所示：\n\n![图3-13](../imgs/3-13.png)\n\n代码如下：\n\n```dart\nIconButton(\n  icon: Icon(Icons.thumb_up),\n  onPressed: () {},\n)\n```\n\n\n\n### 带图标的按钮\n\n`RaisedButton`、`FlatButton`、`OutlineButton`都有一个`icon` 构造函数，通过它可以轻松创建带图标的按钮，如图3-14所示：\n\n![图3-14](../imgs/3-14.png)\n\n代码如下：\n\n```dart\nRaisedButton.icon(\n  icon: Icon(Icons.send),\n  label: Text(\"发送\"),\n  onPressed: _onPressed,\n),\nOutlineButton.icon(\n  icon: Icon(Icons.add),\n  label: Text(\"添加\"),\n  onPressed: _onPressed,\n),\nFlatButton.icon(\n  icon: Icon(Icons.info),\n  label: Text(\"详情\"),\n  onPressed: _onPressed,\n),\n```\n\n\n\n## 3.4.2 自定义按钮外观\n\n按钮外观可以通过其属性来定义，不同按钮属性大同小异，我们以FlatButton为例，介绍一下常见的按钮属性，详细的信息可以查看API文档。\n\n```dart\nconst FlatButton({\n  ...  \n  @required this.onPressed, //按钮点击回调\n  this.textColor, //按钮文字颜色\n  this.disabledTextColor, //按钮禁用时的文字颜色\n  this.color, //按钮背景颜色\n  this.disabledColor,//按钮禁用时的背景颜色\n  this.highlightColor, //按钮按下时的背景颜色\n  this.splashColor, //点击时，水波动画中水波的颜色\n  this.colorBrightness,//按钮主题，默认是浅色主题 \n  this.padding, //按钮的填充\n  this.shape, //外形\n  @required this.child, //按钮的内容\n})\n```\n\n其中大多数属性名都是自解释的，我们不赘述。下面我们通过一个示例来看看如何自定义按钮。\n\n#### 示例\n\n定义一个背景蓝色，两边圆角的按钮。效果如图3-15所示：\n\n![图3-15](../imgs/3-15.png)\n\n代码如下：\n\n```dart\nFlatButton(\n  color: Colors.blue,\n  highlightColor: Colors.blue[700],\n  colorBrightness: Brightness.dark,\n  splashColor: Colors.grey,\n  child: Text(\"Submit\"),\n  shape:RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)),\n  onPressed: () {},\n)\n```\n\n很简单吧，在上面的代码中，我们主要通过`shape`来指定其外形为一个圆角矩形。因为按钮背景是蓝色(深色)，我们需要指定按钮主题`colorBrightness`为`Brightness.dark`，这是为了保证按钮文字颜色为浅色。\n\nFlutter 中没有提供去除背景的设置，假若我们需要去除背景，则可以通过将背景颜色设置为全透明来实现。对应上面的代码，便是将 `color: Colors.blue` 替换为 `color: Color(0x000000)`。\n\n细心的读者可能会发现这个按钮没有阴影(点击之后也没有)，这样会显得没有质感。其实这也很容易，将上面的`FlatButton`换成`RaisedButton`就行，其它代码不用改（这里 color 也不做更改），换了之后的效果如图3-16所示：\n\n![图3-16](../imgs/3-16.png)\n\n是不是有质感了！之所以会这样，是因为`RaisedButton`默认有配置阴影：\n\n```dart\nconst RaisedButton({\n  ...\n  this.elevation = 2.0, //正常状态下的阴影\n  this.highlightElevation = 8.0,//按下时的阴影\n  this.disabledElevation = 0.0,// 禁用时的阴影\n  ...\n}\n```\n\n值得注意的是，在Material 组件库中，我们会在很多组件中见到elevation相关的属性，它们都是用来控制阴影的，这是因为阴影在Material设计风格中是一种很重要的表现形式，以后在介绍其它组件时，便不再赘述。\n\n如果我们想实现一个背景渐变的圆角按钮，按钮有没有相应的属性呢？答案是否定的，但是，我们可以通过其它方式来实现，我们将在后面\"自定义组件\"一章中实现。\n\n\n"
  },
  {
    "path": "src/v2/chapter3/flutter_widget_intro.md",
    "content": "# 3.1 Widget简介\n\n## 3.1.1 概念\n\n在前面的介绍中，我们知道在Flutter中几乎所有的对象都是一个Widget。与原生开发中“控件”不同的是，Flutter中的Widget的概念更广泛，它不仅可以表示UI元素，也可以表示一些功能性的组件如：用于手势检测的 `GestureDetector` widget、用于APP主题数据传递的`Theme`等等，而原生开发中的控件通常只是指UI元素。在后面的内容中，我们在描述UI元素时可能会用到“控件”、“组件”这样的概念，读者心里需要知道他们就是widget，只是在不同场景的不同表述而已。由于Flutter主要就是用于构建用户界面的，所以，在大多数时候，读者可以认为widget就是一个控件，不必纠结于概念。\n\n## 3.1.2 Widget与Element\n\n在Flutter中，Widget的功能是“描述一个UI元素的配置数据”，它就是说，Widget其实并不是表示最终绘制在设备屏幕上的显示元素，而它只是描述显示元素的一个配置数据。\n\n实际上，Flutter中真正代表屏幕上显示元素的类是`Element`，也就是说Widget只是描述`Element`的配置数据！有关`Element`的详细介绍我们将在本书后面的高级部分深入介绍，现在，读者只需要知道：**Widget只是UI元素的一个配置数据，并且一个Widget可以对应多个`Element`**。这是因为同一个Widget对象可以被添加到UI树的不同部分，而真正渲染时，UI树的每一个`Element`节点都会对应一个Widget对象。总结一下：\n\n- Widget实际上就是`Element`的配置数据，Widget树实际上是一个配置树，而真正的UI渲染树是由`Element`构成；不过，由于`Element`是通过Widget生成的，所以它们之间有对应关系，在大多数场景，我们可以宽泛地认为Widget树就是指UI控件树或UI渲染树。\n- 一个Widget对象可以对应多个`Element`对象。这很好理解，根据同一份配置（Widget），可以创建多个实例（Element）。\n\n读者应该将这两点牢记在心中。\n\n## 3.1.3 Widget主要接口\n\n\n我们先来看一下Widget类的声明：\n\n```dart\n@immutable\nabstract class Widget extends DiagnosticableTree {\n  const Widget({ this.key });\n  final Key key;\n    \n  @protected\n  Element createElement();\n\n  @override\n  String toStringShort() {\n    return key == null ? '$runtimeType' : '$runtimeType-$key';\n  }\n\n  @override\n  void debugFillProperties(DiagnosticPropertiesBuilder properties) {\n    super.debugFillProperties(properties);\n    properties.defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.dense;\n  }\n  \n  static bool canUpdate(Widget oldWidget, Widget newWidget) {\n    return oldWidget.runtimeType == newWidget.runtimeType\n        && oldWidget.key == newWidget.key;\n  }\n}\n```\n\n- `Widget`类继承自`DiagnosticableTree`，`DiagnosticableTree`即“诊断树”，主要作用是提供调试信息。\n- `Key`: 这个`key`属性类似于React/Vue中的`key`，主要的作用是决定是否在下一次`build`时复用旧的widget，决定的条件在`canUpdate()`方法中。\n- `createElement()`：正如前文所述“一个Widget可以对应多个`Element`”；Flutter Framework在构建UI树时，会先调用此方法生成对应节点的`Element`对象。此方法是Flutter Framework隐式调用的，在我们开发过程中基本不会调用到。\n- `debugFillProperties(...)` 复写父类的方法，主要是设置诊断树的一些特性。\n- `canUpdate(...)`是一个静态方法，它主要用于在Widget树重新`build`时复用旧的widget，其实具体来说，应该是：是否用新的Widget对象去更新旧UI树上所对应的`Element`对象的配置；通过其源码我们可以看到，只要`newWidget`与`oldWidget`的`runtimeType`和`key`同时相等时就会用`newWidget`去更新`Element`对象的配置，否则就会创建新的`Element`。\n\n有关Key和Widget复用的细节将会在本书后面高级部分深入讨论，读者现在只需知道，为Widget显式添加key的话可能（但不一定）会使UI在重新构建时变的高效，读者目前可以先忽略此参数。本书后面的示例中，只会在构建列表项UI时会显式指定Key。\n\n另外`Widget`类本身是一个抽象类，其中最核心的就是定义了`createElement()`接口，在Flutter开发中，我们一般都不用直接继承`Widget`类来实现一个新组件，相反，我们通常会通过继承`StatelessWidget`或`StatefulWidget`来间接继承`Widget`类来实现。`StatelessWidget`和`StatefulWidget`都是直接继承自`Widget`类，而这两个类也正是Flutter中非常重要的两个抽象类，它们引入了两种Widget模型，接下来我们将重点介绍一下这两个类。\n\n## 3.1.4 StatelessWidget\n\n在之前的章节中，我们已经简单介绍过`StatelessWidget`，`StatelessWidget`相对比较简单，它继承自`Widget`类，重写了`createElement() `方法：\n\n```dart\n@override\nStatelessElement createElement() => new StatelessElement(this);\n```\n\n`StatelessElement` 间接继承自`Element`类，与`StatelessWidget`相对应（作为其配置数据）。\n\n`StatelessWidget`用于不需要维护状态的场景，它通常在`build`方法中通过嵌套其它Widget来构建UI，在构建过程中会递归的构建其嵌套的Widget。我们看一个简单的例子：\n\n```dart\nclass Echo extends StatelessWidget {\n  const Echo({\n    Key key,  \n    @required this.text,\n    this.backgroundColor:Colors.grey,\n  }):super(key:key);\n    \n  final String text;\n  final Color backgroundColor;\n\n  @override\n  Widget build(BuildContext context) {\n    return Center(\n      child: Container(\n        color: backgroundColor,\n        child: Text(text),\n      ),\n    );\n  }\n}\n```\n\n上面的代码，实现了一个回显字符串的`Echo` widget。\n\n> 按照惯例，`widget`的构造函数参数应使用命名参数，命名参数中的必要参数要添加`@required`标注，这样有利于静态代码分析器进行检查。另外，在继承`widget`时，第一个参数通常应该是`Key`，另外，如果Widget需要接收子Widget，那么`child`或`children`参数通常应被放在参数列表的最后。同样是按照惯例，Widget的属性应尽可能的被声明为`final`，防止被意外改变。\n\n然后我们可以通过如下方式使用它：\n\n```dart\nWidget build(BuildContext context) {\n  return Echo(text: \"hello world\");\n}\n```\n\n运行后效果如图3-1所示：\n\n![图3-1](../imgs/3-1.png)\n\n### Context\n\n`build`方法有一个`context`参数，它是`BuildContext`类的一个实例，表示当前widget在widget树中的上下文，每一个widget都会对应一个context对象（因为每一个widget都是widget树上的一个节点）。实际上，`context`是当前widget在widget树中位置中执行”相关操作“的一个句柄，比如它提供了从当前widget开始向上遍历widget树以及按照widget类型查找父级widget的方法。下面是在子树中获取父级widget的一个示例：\n\n```dart\nclass ContextRoute extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        title: Text(\"Context测试\"),\n      ),\n      body: Container(\n        child: Builder(builder: (context) {\n          // 在Widget树中向上查找最近的父级`Scaffold` widget\n          Scaffold scaffold = context.findAncestorWidgetOfExactType<Scaffold>();\n          // 直接返回 AppBar的title， 此处实际上是Text(\"Context测试\")\n          return (scaffold.appBar as AppBar).title;\n        }),\n      ),\n    );\n  }\n}\n```\n\n运行后效果如图3-1-1所示：\n\n![图3-1-1](../imgs/3-1-1.png)\n\n>**注意**：对于`BuildContext`读者现在可以先作了解，随着本书后面内容的展开，也会用到Context的一些方法，读者可以通过具体的场景对其有个直观的认识。关于`BuildContext`更多的内容，我们也将在后面高级部分再深入介绍。\n\n## 3.1.5 StatefulWidget\n\n和`StatelessWidget`一样，`StatefulWidget`也是继承自`Widget`类，并重写了`createElement() `方法，不同的是返回的`Element` 对象并不相同；另外`StatefulWidget`类中添加了一个新的接口`createState()`。\n\n下面我们看看`StatefulWidget`的类定义：\n\n```dart\nabstract class StatefulWidget extends Widget {\n  const StatefulWidget({ Key key }) : super(key: key);\n    \n  @override\n  StatefulElement createElement() => new StatefulElement(this);\n    \n  @protected\n  State createState();\n}\n```\n\n- `StatefulElement ` 间接继承自`Element`类，与StatefulWidget相对应（作为其配置数据）。`StatefulElement `中可能会多次调用`createState()`来创建状态(State)对象。\n\n- `createState()` 用于创建和Stateful widget相关的状态，它在Stateful widget的生命周期中可能会被多次调用。例如，当一个Stateful widget同时插入到widget树的多个位置时，Flutter framework就会调用该方法为每一个位置生成一个独立的State实例，其实，本质上就是一个`StatefulElement`对应一个State实例。\n\n  > 在本书中经常会出现“树”的概念，在不同的场景可能指不同的意思，在说“widget树”时它可以指widget结构树，但由于widget与Element有对应关系（一可能对多），在有些场景（Flutter的SDK文档中）也代指“UI树”的意思。而在stateful widget中，State对象也和`StatefulElement`具有对应关系（一对一），所以在Flutter的SDK文档中，可以经常看到“从树中移除State对象”或“插入State对象到树中”这样的描述。其实，无论哪种描述，其意思都是在描述“一棵构成用户界面的节点元素的树”，读者不必纠结于这些概念，还是那句话“得其神，忘其形”，因此，本书中出现的各种“树”，如果没有特别说明，读者都可抽象的认为它是“一棵构成用户界面的节点元素的树”。\n\n## 3.1.6 State\n\n一个StatefulWidget类会对应一个State类，State表示与其对应的StatefulWidget要维护的状态，State中的保存的状态信息可以：\n\n1. 在widget 构建时可以被同步读取。\n2. 在widget生命周期中可以被改变，当State被改变时，可以手动调用其`setState()`方法通知Flutter framework状态发生改变，Flutter framework在收到消息后，会重新调用其`build`方法重新构建widget树，从而达到更新UI的目的。\n\nState中有两个常用属性：\n\n1. `widget`，它表示与该State实例关联的widget实例，由Flutter framework动态设置。注意，这种关联并非永久的，因为在应用生命周期中，UI树上的某一个节点的widget实例在重新构建时可能会变化，但State实例只会在第一次插入到树中时被创建，当在重新构建时，如果widget被修改了，Flutter framework会动态设置State.widget为新的widget实例。\n\n2. `context`。StatefulWidget对应的BuildContext，作用同StatelessWidget的BuildContext。\n\n#### State生命周期\n\n理解State的生命周期对flutter开发非常重要，为了加深读者印象，本节我们通过一个实例来演示一下State的生命周期。在接下来的示例中，我们实现一个计数器widget，点击它可以使计数器加1，由于要保存计数器的数值状态，所以我们应继承StatefulWidget，代码如下：\n\n```dart\nclass CounterWidget extends StatefulWidget {\n  const CounterWidget({\n    Key key,\n    this.initValue: 0\n  });\n\n  final int initValue;\n\n  @override\n  _CounterWidgetState createState() => new _CounterWidgetState();\n}\n```\n\n`CounterWidget`接收一个`initValue`整型参数，它表示计数器的初始值。下面我们看一下State的代码：\n\n```dart\nclass _CounterWidgetState extends State<CounterWidget> {  \n  int _counter;\n\n  @override\n  void initState() {\n    super.initState();\n    //初始化状态  \n    _counter=widget.initValue;\n    print(\"initState\");\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    print(\"build\");\n    return Scaffold(\n      body: Center(\n        child: FlatButton(\n          child: Text('$_counter'),\n          //点击后计数器自增\n          onPressed:()=>setState(()=> ++_counter,\n          ),\n        ),\n      ),\n    );\n  }\n\n  @override\n  void didUpdateWidget(CounterWidget oldWidget) {\n    super.didUpdateWidget(oldWidget);\n    print(\"didUpdateWidget\");\n  }\n\n  @override\n  void deactivate() {\n    super.deactivate();\n    print(\"deactive\");\n  }\n\n  @override\n  void dispose() {\n    super.dispose();\n    print(\"dispose\");\n  }\n\n  @override\n  void reassemble() {\n    super.reassemble();\n    print(\"reassemble\");\n  }\n\n  @override\n  void didChangeDependencies() {\n    super.didChangeDependencies();\n    print(\"didChangeDependencies\");\n  }\n\n}\n```\n\n接下来，我们创建一个新路由，在新路由中，我们只显示一个`CounterWidget`：\n\n```dart\nWidget build(BuildContext context) {\n  return CounterWidget();\n}\n```\n\n我们运行应用并打开该路由页面，在新路由页打开后，屏幕中央就会出现一个数字0，然后控制台日志输出：\n\n```\nI/flutter ( 5436): initState\nI/flutter ( 5436): didChangeDependencies\nI/flutter ( 5436): build\n```\n\n可以看到，在StatefulWidget插入到Widget树时首先`initState`方法会被调用。\n\n然后我们点击⚡️按钮热重载，控制台输出日志如下：\n\n```\nI/flutter ( 5436): reassemble\nI/flutter ( 5436): didUpdateWidget\nI/flutter ( 5436): build\n```\n\n可以看到此时` initState` 和`didChangeDependencies`都没有被调用，而此时`didUpdateWidget`被调用。\n\n接下来，我们在widget树中移除`CounterWidget`，将路由`build`方法改为：\n\n```dart\nWidget build(BuildContext context) {\n  //移除计数器 \n  //return CounterWidget();\n  //随便返回一个Text()\n  return Text(\"xxx\");\n}\n```\n\n然后热重载，日志如下：\n\n```\nI/flutter ( 5436): reassemble\nI/flutter ( 5436): deactive\nI/flutter ( 5436): dispose\n```\n\n我们可以看到，在`CounterWidget`从widget树中移除时，`deactive`和`dispose`会依次被调用。\n\n下面我们来看看各个回调函数：\n\n- `initState`：当Widget第一次插入到Widget树时会被调用，对于每一个State对象，Flutter framework只会调用一次该回调，所以，通常在该回调中做一些一次性的操作，如状态初始化、订阅子树的事件通知等。不能在该回调中调用`BuildContext.dependOnInheritedWidgetOfExactType`（该方法用于在Widget树上获取离当前widget最近的一个父级`InheritFromWidget`，关于`InheritedWidget`我们将在后面章节介绍），原因是在初始化完成后，Widget树中的`InheritFromWidget`也可能会发生变化，所以正确的做法应该在在`build（）`方法或`didChangeDependencies()`中调用它。\n- `didChangeDependencies()`：当State对象的依赖发生变化时会被调用；例如：在之前`build()` 中包含了一个`InheritedWidget`，然后在之后的`build()` 中`InheritedWidget`发生了变化，那么此时`InheritedWidget`的子widget的`didChangeDependencies()`回调都会被调用。典型的场景是当系统语言Locale或应用主题改变时，Flutter framework会通知widget调用此回调。\n- `build()`：此回调读者现在应该已经相当熟悉了，它主要是用于构建Widget子树的，会在如下场景被调用：\n\n  1. 在调用`initState()`之后。\n  2. 在调用`didUpdateWidget()`之后。\n  3. 在调用`setState()`之后。\n  4. 在调用`didChangeDependencies()`之后。\n  5. 在State对象从树中一个位置移除后（会调用deactivate）又重新插入到树的其它位置之后。\n- `reassemble()`：此回调是专门为了开发调试而提供的，在热重载(hot reload)时会被调用，此回调在Release模式下永远不会被调用。\n- `didUpdateWidget()`：在widget重新构建时，Flutter framework会调用`Widget.canUpdate`来检测Widget树中同一位置的新旧节点，然后决定是否需要更新，如果`Widget.canUpdate`返回`true`则会调用此回调。正如之前所述，`Widget.canUpdate`会在新旧widget的key和runtimeType同时相等时会返回true，也就是说在在新旧widget的key和runtimeType同时相等时`didUpdateWidget()`就会被调用。\n- `deactivate()`：当State对象从树中被移除时，会调用此回调。在一些场景下，Flutter framework会将State对象重新插到树中，如包含此State对象的子树在树的一个位置移动到另一个位置时（可以通过GlobalKey来实现）。如果移除后没有重新插入到树中则紧接着会调用`dispose()`方法。\n- `dispose()`：当State对象从树中被永久移除时调用；通常在此回调中释放资源。\n\nStatefulWidget生命周期如图3-2所示：\n\n![图3-2](../imgs/3-2.jpg)\n\n\n\n> **注意**：在继承`StatefulWidget`重写其方法时，对于包含`@mustCallSuper`标注的父类方法，都要在子类方法中先调用父类方法。\n\n\n### 为什么要将build方法放在State中，而不是放在StatefulWidget中？\n\n现在，我们回答之前提出的问题，为什么`build()`方法放在State（而不是`StatefulWidget`）中 ？这主要是为了提高开发的灵活性。如果将`build()`方法在`StatefulWidget`中则会有两个问题：\n\n- 状态访问不便。\n\n  试想一下，如果我们的`StatefulWidget`有很多状态，而每次状态改变都要调用`build`方法，由于状态是保存在State中的，如果`build`方法在`StatefulWidget`中，那么`build`方法和状态分别在两个类中，那么构建时读取状态将会很不方便！试想一下，如果真的将`build`方法放在StatefulWidget中的话，由于构建用户界面过程需要依赖State，所以`build`方法将必须加一个`State`参数，大概是下面这样：\n\n  ```dart\n    Widget build(BuildContext context, State state){\n        //state.counter\n        ...\n    }\n  ```\n\n  这样的话就只能将State的所有状态声明为公开的状态，这样才能在State类外部访问状态！但是，将状态设置为公开后，状态将不再具有私密性，这就会导致对状态的修改将会变的不可控。但如果将`build()`方法放在State中的话，构建过程不仅可以直接访问状态，而且也无需公开私有状态，这会非常方便。\n\n- 继承`StatefulWidget`不便。\n\n  例如，Flutter中有一个动画widget的基类`AnimatedWidget`，它继承自`StatefulWidget`类。`AnimatedWidget`中引入了一个抽象方法`build(BuildContext context)`，继承自`AnimatedWidget`的动画widget都要实现这个`build`方法。现在设想一下，如果`StatefulWidget` 类中已经有了一个`build`方法，正如上面所述，此时`build`方法需要接收一个state对象，这就意味着`AnimatedWidget`必须将自己的State对象(记为_animatedWidgetState)提供给其子类，因为子类需要在其`build`方法中调用父类的`build`方法，代码可能如下：\n\n  ```dart\n  class MyAnimationWidget extends AnimatedWidget{\n      @override\n      Widget build(BuildContext context, State state){\n        //由于子类要用到AnimatedWidget的状态对象_animatedWidgetState，\n        //所以AnimatedWidget必须通过某种方式将其状态对象_animatedWidgetState\n        //暴露给其子类   \n        super.build(context, _animatedWidgetState)\n      }\n  }\n  ```\n\n  这样很显然是不合理的，因为\n\n  1. `AnimatedWidget`的状态对象是`AnimatedWidget`内部实现细节，不应该暴露给外部。\n  2. 如果要将父类状态暴露给子类，那么必须得有一种传递机制，而做这一套传递机制是无意义的，因为父子类之间状态的传递和子类本身逻辑是无关的。\n\n综上所述，可以发现，对于`StatefulWidget`，将`build`方法放在State中，可以给开发带来很大的灵活性。\n\n\n\n## 3.1.7 在Widget树中获取State对象\n\n由于StatefulWidget的的具体逻辑都在其State中，所以很多时候，我们需要获取StatefulWidget对应的State对象来调用一些方法，比如`Scaffold`组件对应的状态类`ScaffoldState`中就定义了打开SnackBar(路由页底部提示条)的方法。我们有两种方法在子widget树中获取父级StatefulWidget的State对象。\n\n### 通过Context获取\n\n`context`对象有一个`findAncestorStateOfType()`方法，该方法可以从当前节点沿着widget树向上查找指定类型的StatefulWidget对应的State对象。下面是实现打开SnackBar的示例：\n\n```dart\nScaffold(\n  appBar: AppBar(\n    title: Text(\"子树中获取State对象\"),\n  ),\n  body: Center(\n    child: Builder(builder: (context) {\n      return RaisedButton(\n        onPressed: () {\n          // 查找父级最近的Scaffold对应的ScaffoldState对象\n          ScaffoldState _state = context.findAncestorStateOfType<ScaffoldState>();\n          //调用ScaffoldState的showSnackBar来弹出SnackBar\n          _state.showSnackBar(\n            SnackBar(\n              content: Text(\"我是SnackBar\"),\n            ),\n          );\n        },\n        child: Text(\"显示SnackBar\"),\n      );\n    }),\n  ),\n);\n```\n\n上面示例运行后，点击”显示SnackBar“，效果如图3-1-2所示：\n\n![图3-1-2](../imgs/3-1-2.png)\n\n一般来说，如果StatefulWidget的状态是私有的（不应该向外部暴露），那么我们代码中就不应该去直接获取其State对象；如果StatefulWidget的状态是希望暴露出的（通常还有一些组件的操作方法），我们则可以去直接获取其State对象。但是通过`context.findAncestorStateOfType`获取StatefulWidget的状态的方法是通用的，我们并不能在语法层面指定StatefulWidget的状态是否私有，所以在Flutter开发中便有了一个默认的约定：如果StatefulWidget的状态是希望暴露出的，应当在StatefulWidget中提供一个`of`静态方法来获取其State对象，开发者便可直接通过该方法来获取；如果State不希望暴露，则不提供`of`方法。这个约定在Flutter SDK里随处可见。所以，上面示例中的`Scaffold`也提供了一个`of`方法，我们其实是可以直接调用它的：\n\n```dart\n...//省略无关代码\n// 直接通过of静态方法来获取ScaffoldState \nScaffoldState _state=Scaffold.of(context); \n_state.showSnackBar(\n  SnackBar(\n    content: Text(\"我是SnackBar\"),\n  ),\n);\n```\n\n### 通过GlobalKey\n\nFlutter还有一种通用的获取`State`对象的方法——通过GlobalKey来获取！ 步骤分两步：\n\n1. 给目标`StatefulWidget`添加`GlobalKey`。\n\n   ```dart\n   //定义一个globalKey, 由于GlobalKey要保持全局唯一性，我们使用静态变量存储\n   static GlobalKey<ScaffoldState> _globalKey= GlobalKey();\n   ...\n   Scaffold(\n       key: _globalKey , //设置key\n       ...  \n   )\n   ```\n\n2. 通过`GlobalKey`来获取`State`对象\n\n   ```dart\n   _globalKey.currentState.openDrawer()\n   ```\n\nGlobalKey是Flutter提供的一种在整个APP中引用element的机制。如果一个widget设置了`GlobalKey`，那么我们便可以通过`globalKey.currentWidget`获得该widget对象、`globalKey.currentElement`来获得widget对应的element对象，如果当前widget是`StatefulWidget`，则可以通过`globalKey.currentState`来获得该widget对应的state对象。\n\n> 注意：使用GlobalKey开销较大，如果有其他可选方案，应尽量避免使用它。另外同一个GlobalKey在整个widget树中必须是唯一的，不能重复。\n\n## 3.1.8 Flutter SDK内置组件库介绍\n\nFlutter提供了一套丰富、强大的基础组件，在基础组件库之上Flutter又提供了一套Material风格（Android默认的视觉风格）和一套Cupertino风格（iOS视觉风格）的组件库。要使用基础组件库，需要先导入：\n\n```dart\nimport 'package:flutter/widgets.dart';\n```\n\n下面我们介绍一下常用的组件。\n\n#### 基础组件\n\n- [`Text`](https://docs.flutter.io/flutter/widgets/Text-class.html)：该组件可让您创建一个带格式的文本。\n- [`Row`](https://docs.flutter.io/flutter/widgets/Row-class.html)、 [`Column`](https://docs.flutter.io/flutter/widgets/Column-class.html)： 这些具有弹性空间的布局类Widget可让您在水平（Row）和垂直（Column）方向上创建灵活的布局。其设计是基于Web开发中的Flexbox布局模型。\n- [`Stack`](https://docs.flutter.io/flutter/widgets/Stack-class.html)： 取代线性布局 (译者语：和Android中的`FrameLayout`相似)，[`Stack`](https://docs.flutter.io/flutter/widgets/Stack-class.html)允许子 widget 堆叠， 你可以使用 [`Positioned`](https://docs.flutter.io/flutter/widgets/Positioned-class.html) 来定位他们相对于`Stack`的上下左右四条边的位置。Stacks是基于Web开发中的绝对定位（absolute positioning )布局模型设计的。\n- [`Container`](https://docs.flutter.io/flutter/widgets/Container-class.html)： [`Container`](https://docs.flutter.io/flutter/widgets/Container-class.html) 可让您创建矩形视觉元素。container 可以装饰一个[`BoxDecoration`](https://docs.flutter.io/flutter/painting/BoxDecoration-class.html), 如 background、一个边框、或者一个阴影。 [`Container`](https://docs.flutter.io/flutter/widgets/Container-class.html) 也可以具有边距（margins）、填充(padding)和应用于其大小的约束(constraints)。另外， [`Container`](https://docs.flutter.io/flutter/widgets/Container-class.html)可以使用矩阵在三维空间中对其进行变换。\n\n\n#### Material组件\n\nFlutter提供了一套丰富的Material组件，它可以帮助我们构建遵循Material Design设计规范的应用程序。Material应用程序以[`MaterialApp`](https://docs.flutter.io/flutter/material/MaterialApp-class.html) 组件开始， 该组件在应用程序的根部创建了一些必要的组件，比如`Theme`组件，它用于配置应用的主题。 是否使用[`MaterialApp`](https://docs.flutter.io/flutter/material/MaterialApp-class.html)完全是可选的，但是使用它是一个很好的做法。在之前的示例中，我们已经使用过多个Material 组件了，如：`Scaffold`、`AppBar`、`FlatButton`等。要使用Material 组件，需要先引入它：\n\n```dart\nimport 'package:flutter/material.dart';\n```\n\n#### Cupertino组件\n\nFlutter也提供了一套丰富的Cupertino风格的组件，尽管目前还没有Material 组件那么丰富，但是它仍在不断的完善中。值得一提的是在Material 组件库中有一些组件可以根据实际运行平台来切换表现风格，比如`MaterialPageRoute`，在路由切换时，如果是Android系统，它将会使用Android系统默认的页面切换动画(从底向上)；如果是iOS系统，它会使用iOS系统默认的页面切换动画（从右向左）。由于在前面的示例中还没有Cupertino组件的示例，下面我们实现一个简单的Cupertino组件风格的页面：\n\n```dart\n//导入cupertino widget库\nimport 'package:flutter/cupertino.dart';\n\nclass CupertinoTestRoute extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return CupertinoPageScaffold(\n      navigationBar: CupertinoNavigationBar(\n        middle: Text(\"Cupertino Demo\"),\n      ),\n      child: Center(\n        child: CupertinoButton(\n            color: CupertinoColors.activeBlue,\n            child: Text(\"Press\"),\n            onPressed: () {}\n        ),\n      ),\n    );\n  }\n}\n```\n\n下面（图3-3）是在iPhoneX上页面效果截图：\n\n![图3-3](../imgs/3-3.png)\n\n\n\n### 关于示例\n\n本章后面章节的示例中会使用一些布局类组件，如`Scaffold`、`Row`、`Column`等，这些组件将在后面“布局类组件”一章中详细介绍，读者可以先不用关注。\n\n### 总结\n\nFlutter提供了丰富的组件，在实际的开发中你可以根据需要随意使用它们，而不必担心引入过多组件库会让你的应用安装包变大，这不是web开发，dart在编译时只会编译你使用了的代码。由于Material和Cupertino都是在基础组件库之上的，所以如果我们的应用中引入了这两者之一，则不需要再引入`flutter/widgets.dart`了，因为它们内部已经引入过了。\n\n\n\n\n\n"
  },
  {
    "path": "src/v2/chapter3/img_and_icon.md",
    "content": "## 3.5 图片及ICON\n\n## 3.5.1 图片\n\nFlutter中，我们可以通过`Image`组件来加载并显示图片，`Image`的数据源可以是asset、文件、内存以及网络。\n\n### ImageProvider\n\n`ImageProvider` 是一个抽象类，主要定义了图片数据获取的接口`load()`，从不同的数据源获取图片需要实现不同的`ImageProvider` ，如`AssetImage`是实现了从Asset中加载图片的ImageProvider，而`NetworkImage`实现了从网络加载图片的ImageProvider。\n\n### Image\n\n`Image` widget有一个必选的`image`参数，它对应一个ImageProvider。下面我们分别演示一下如何从asset和网络加载图片。\n\n#### 从asset中加载图片\n\n1. 在工程根目录下创建一个`images目录`，并将图片avatar.png拷贝到该目录。\n\n2. 在`pubspec.yaml`中的`flutter`部分添加如下内容：\n\n   ```yaml\n     assets:\n       - images/avatar.png\n   ```\n  > 注意: 由于 yaml 文件对缩进严格，所以必须严格按照每一层两个空格的方式进行缩进，此处assets前面应有两个空格。\n\n3. 加载该图片\n\n   ```dart\n   Image(\n     image: AssetImage(\"images/avatar.png\"),\n     width: 100.0\n   );\n   ```\n   Image也提供了一个快捷的构造函数`Image.asset`用于从asset中加载、显示图片：\n\n   ```dart\n   Image.asset(\"images/avatar.png\",\n     width: 100.0,\n   )\n   ```\n\n\n\n#### 从网络加载图片\n\n```dart\nImage(\n  image: NetworkImage(\n      \"https://avatars2.githubusercontent.com/u/20411648?s=460&v=4\"),\n  width: 100.0,\n)\n```\n\nImage也提供了一个快捷的构造函数`Image.network`用于从网络加载、显示图片：\n\n```dart\nImage.network(\n  \"https://avatars2.githubusercontent.com/u/20411648?s=460&v=4\",\n  width: 100.0,\n)\n```\n\n运行上面两个示例，图片加载成功后如图3-17所示：\n\n![图3-17](../imgs/3-17.png)\n\n\n\n#### 参数\n\n`Image`在显示图片时定义了一系列参数，通过这些参数我们可以控制图片的显示外观、大小、混合效果等。我们看一下Image的主要参数：\n\n```dart\nconst Image({\n  ...\n  this.width, //图片的宽\n  this.height, //图片高度\n  this.color, //图片的混合色值\n  this.colorBlendMode, //混合模式\n  this.fit,//缩放模式\n  this.alignment = Alignment.center, //对齐方式\n  this.repeat = ImageRepeat.noRepeat, //重复方式\n  ...\n})\n```\n\n- `width`、`height`：用于设置图片的宽、高，当不指定宽高时，图片会根据当前父容器的限制，尽可能的显示其原始大小，如果只设置`width`、`height`的其中一个，那么另一个属性默认会按比例缩放，但可以通过下面介绍的`fit`属性来指定适应规则。\n\n- `fit`：该属性用于在图片的显示空间和图片本身大小不同时指定图片的适应模式。适应模式是在`BoxFit`中定义，它是一个枚举类型，有如下值：\n\n  - `fill`：会拉伸填充满显示空间，图片本身长宽比会发生变化，图片会变形。\n  - `cover`：会按图片的长宽比放大后居中填满显示空间，图片不会变形，超出显示空间部分会被剪裁。\n  - `contain`：这是图片的默认适应规则，图片会在保证图片本身长宽比不变的情况下缩放以适应当前显示空间，图片不会变形。\n  - `fitWidth`：图片的宽度会缩放到显示空间的宽度，高度会按比例缩放，然后居中显示，图片不会变形，超出显示空间部分会被剪裁。\n  - `fitHeight`：图片的高度会缩放到显示空间的高度，宽度会按比例缩放，然后居中显示，图片不会变形，超出显示空间部分会被剪裁。\n  - `none`：图片没有适应策略，会在显示空间内显示图片，如果图片比显示空间大，则显示空间只会显示图片中间部分。\n\n  一图胜万言！ 我们对一个宽高相同的头像图片应用不同的`fit`值，效果如图3-18所示：\n\n  ![图3-18](../imgs/3-18.png)\n  \n\n\n- `color`和 `colorBlendMode`：在图片绘制时可以对每一个像素进行颜色混合处理，`color`指定混合色，而`colorBlendMode`指定混合模式，下面是一个简单的示例：\n\n  ```dart\n  Image(\n    image: AssetImage(\"images/avatar.png\"),\n    width: 100.0,\n    color: Colors.blue,\n    colorBlendMode: BlendMode.difference,\n  );\n  ```\n\n运行效果如图3-19所示（彩色）:\n\n![图3-19](../imgs/3-19.png)\n\n- `repeat`：当图片本身大小小于显示空间时，指定图片的重复规则。简单示例如下：\n\n  ```dart\n  Image(\n    image: AssetImage(\"images/avatar.png\"),\n    width: 100.0,\n    height: 200.0,\n    repeat: ImageRepeat.repeatY ,\n  )\n  ```\n  运行后效果如图3-20所示：\n\n  ![图3-20](../imgs/3-20.png)\n\n完整的示例代码如下：\n\n```dart\nimport 'package:flutter/material.dart';\n\nclass ImageAndIconRoute extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    var img=AssetImage(\"imgs/avatar.png\");\n    return SingleChildScrollView(\n      child: Column(\n        children: <Image>[\n          Image(\n            image: img,\n            height: 50.0,\n            width: 100.0,\n            fit: BoxFit.fill,\n          ),\n          Image(\n            image: img,\n            height: 50,\n            width: 50.0,\n            fit: BoxFit.contain,\n          ),\n          Image(\n            image: img,\n            width: 100.0,\n            height: 50.0,\n            fit: BoxFit.cover,\n          ),\n          Image(\n            image: img,\n            width: 100.0,\n            height: 50.0,\n            fit: BoxFit.fitWidth,\n          ),\n          Image(\n            image: img,\n            width: 100.0,\n            height: 50.0,\n            fit: BoxFit.fitHeight,\n          ),\n          Image(\n            image: img,\n            width: 100.0,\n            height: 50.0,\n            fit: BoxFit.scaleDown,\n          ),\n          Image(\n            image: img,\n            height: 50.0,\n            width: 100.0,\n            fit: BoxFit.none,\n          ),\n          Image(\n            image: img,\n            width: 100.0,\n            color: Colors.blue,\n            colorBlendMode: BlendMode.difference,\n            fit: BoxFit.fill,\n          ),\n          Image(\n            image: img,\n            width: 100.0,\n            height: 200.0,\n            repeat: ImageRepeat.repeatY ,\n          )\n        ].map((e){\n          return Row(\n            children: <Widget>[\n              Padding(\n                padding: EdgeInsets.all(16.0),\n                child: SizedBox(\n                  width: 100,\n                  child: e,\n                ),\n              ),\n              Text(e.fit.toString())\n            ],\n          );\n        }).toList()\n      ),\n    );\n  }\n}\n```\n\n### Image缓存\n\nFlutter框架对加载过的图片是有缓存的（内存），默认最大缓存数量是1000，最大缓存空间为100M。关于Image的详细内容及原理我们将会在后面进阶部分深入介绍。\n\n## 3.5.2 ICON\n\nFlutter中，可以像Web开发一样使用iconfont，iconfont即“字体图标”，它是将图标做成字体文件，然后通过指定不同的字符而显示不同的图片。\n\n> 在字体文件中，每一个字符都对应一个位码，而每一个位码对应一个显示字形，不同的字体就是指字形不同，即字符对应的字形是不同的。而在iconfont中，只是将位码对应的字形做成了图标，所以不同的字符最终就会渲染成不同的图标。\n\n在Flutter开发中，iconfont和图片相比有如下优势：\n\n1. 体积小：可以减小安装包大小。\n2. 矢量的：iconfont都是矢量图标，放大不会影响其清晰度。\n3. 可以应用文本样式：可以像文本一样改变字体图标的颜色、大小对齐等。\n4. 可以通过TextSpan和文本混用。\n\n##### 使用Material Design字体图标\n\nFlutter默认包含了一套Material Design的字体图标，在`pubspec.yaml`文件中的配置如下\n\n```yaml\nflutter:\n  uses-material-design: true\n```\n\nMaterial Design所有图标可以在其官网查看：https://material.io/tools/icons/\n\n我们看一个简单的例子：\n\n```dart\nString icons = \"\";\n// accessible: &#xE914; or 0xE914 or E914\nicons += \"\\uE914\";\n// error: &#xE000; or 0xE000 or E000\nicons += \" \\uE000\";\n// fingerprint: &#xE90D; or 0xE90D or E90D\nicons += \" \\uE90D\";\n\nText(icons,\n  style: TextStyle(\n      fontFamily: \"MaterialIcons\",\n      fontSize: 24.0,\n      color: Colors.green\n  ),\n);\n```\n\n运行效果如图3-21所示：\n\n![图3-21](../imgs/3-21.png)\n\n通过这个示例可以看到，使用图标就像使用文本一样，但是这种方式需要我们提供每个图标的码点，这并对开发者不友好，所以，Flutter封装了`IconData`和`Icon`来专门显示字体图标，上面的例子也可以用如下方式实现：\n\n```dart\nRow(\n  mainAxisAlignment: MainAxisAlignment.center,\n  children: <Widget>[\n    Icon(Icons.accessible,color: Colors.green,),\n    Icon(Icons.error,color: Colors.green,),\n    Icon(Icons.fingerprint,color: Colors.green,),\n  ],\n)\n```\n\n`Icons`类中包含了所有Material Design图标的`IconData`静态变量定义。\n\n\n\n#### 使用自定义字体图标\n\n我们也可以使用自定义字体图标。iconfont.cn上有很多字体图标素材，我们可以选择自己需要的图标打包下载后，会生成一些不同格式的字体文件，在Flutter中，我们使用ttf格式即可。\n\n假设我们项目中需要使用一个书籍图标和微信图标，我们打包下载后导入：\n\n1. 导入字体图标文件；这一步和导入字体文件相同，假设我们的字体图标文件保存在项目根目录下，路径为\"fonts/iconfont.ttf\"：\n\n   ```yaml\n   fonts:\n     - family: myIcon  #指定一个字体名\n       fonts:\n         - asset: fonts/iconfont.ttf\n   ```\n\n2. 为了使用方便，我们定义一个`MyIcons`类，功能和`Icons`类一样：将字体文件中的所有图标都定义成静态变量：\n\n   ```dart\n   class MyIcons{\n     // book 图标\n     static const IconData book = const IconData(\n         0xe614, \n         fontFamily: 'myIcon', \n         matchTextDirection: true\n     );\n     // 微信图标\n     static const IconData wechat = const IconData(\n         0xec7d,  \n         fontFamily: 'myIcon', \n         matchTextDirection: true\n     );\n   }\n   ```\n\n3. 使用\n\n   ```dart\n   Row(\n     mainAxisAlignment: MainAxisAlignment.center,\n     children: <Widget>[\n       Icon(MyIcons.book,color: Colors.purple,),\n       Icon(MyIcons.wechat,color: Colors.green,),\n     ],\n   )\n   ```\n\n   运行后效果如图3-22所示：\n\n   ![图3-22](../imgs/3-22.png)\n\n"
  },
  {
    "path": "src/v2/chapter3/index.md",
    "content": "# 基础Widget\n\n本节介绍一下Flutter中常用的一些基础widget，由于大多数widget的属性都比较多，我们在介绍widget时会着重介绍常用的属性，而不会像API文档一样所有属性都介绍，关于属性详细的信息请参考Flutter SDK文档。\n\n## 本章目录\n\n* [3.1：Widget简介](flutter_widget_intro.md)\n* [3.2：状态管理](state_manage.md)\n* [3.3：文本、字体样式](text.md)\n* [3.4：按钮](buttons.md)      \n* [3.5：图片和Icon](img_and_icon.md)   \n* [3.6：单选框和复选框](radio_and_checkbox.md)   \n* [3.7：输入框和表单](input_and_form.md) \n* [3.8：进度指示器](progress.md)  \n"
  },
  {
    "path": "src/v2/chapter3/input_and_form.md",
    "content": "\n# 3.7 输入框及表单\n\nMaterial组件库中提供了输入框组件`TextField`和表单组件`Form`。下面我们分别介绍一下。\n\n## 3.7.1 TextField\n\n`TextField`用于文本输入，它提供了很多属性，我们先简单介绍一下主要属性的作用，然后通过几个示例来演示一下关键属性的用法。\n\n```dart\nconst TextField({\n  ...\n  TextEditingController controller, \n  FocusNode focusNode,\n  InputDecoration decoration = const InputDecoration(),\n  TextInputType keyboardType,\n  TextInputAction textInputAction,\n  TextStyle style,\n  TextAlign textAlign = TextAlign.start,\n  bool autofocus = false,\n  bool obscureText = false,\n  int maxLines = 1,\n  int maxLength,\n  bool maxLengthEnforced = true,\n  ValueChanged<String> onChanged,\n  VoidCallback onEditingComplete,\n  ValueChanged<String> onSubmitted,\n  List<TextInputFormatter> inputFormatters,\n  bool enabled,\n  this.cursorWidth = 2.0,\n  this.cursorRadius,\n  this.cursorColor,\n  ...\n})\n```\n\n\n\n- `controller`：编辑框的控制器，通过它可以设置/获取编辑框的内容、选择编辑内容、监听编辑文本改变事件。大多数情况下我们都需要显式提供一个`controller`来与文本框交互。如果没有提供`controller`，则`TextField`内部会自动创建一个。\n\n- `focusNode`：用于控制`TextField`是否占有当前键盘的输入焦点。它是我们和键盘交互的一个句柄（handle）。\n\n- `InputDecoration`：用于控制`TextField`的外观显示，如提示文本、背景颜色、边框等。\n\n- `keyboardType`：用于设置该输入框默认的键盘输入类型，取值如下：\n\n  \n\n  | TextInputType枚举值 | 含义                                                |\n  | ------------------- | --------------------------------------------------- |\n  | text                | 文本输入键盘                                        |\n  | multiline           | 多行文本，需和maxLines配合使用(设为null或大于1)     |\n  | number              | 数字；会弹出数字键盘                                |\n  | phone               | 优化后的电话号码输入键盘；会弹出数字键盘并显示“* #” |\n  | datetime            | 优化后的日期输入键盘；Android上会显示“: -”          |\n  | emailAddress        | 优化后的电子邮件地址；会显示“@ .”                   |\n  | url                 | 优化后的url输入键盘； 会显示“/ .”                   |\n\n  \n\n- `textInputAction`：键盘动作按钮图标(即回车键位图标)，它是一个枚举值，有多个可选值，全部的取值列表读者可以查看API文档，下面是当值为`TextInputAction.search`时，原生Android系统下键盘样式如图3-24所示：\n\n  ![图3-24](../imgs/3-24.png)\n\n- `style`：正在编辑的文本样式。\n\n- `textAlign`: 输入框内编辑文本在水平方向的对齐方式。\n\n- `autofocus`: 是否自动获取焦点。\n\n- `obscureText`：是否隐藏正在编辑的文本，如用于输入密码的场景等，文本内容会用“•”替换。\n\n- `maxLines`：输入框的最大行数，默认为1；如果为`null`，则无行数限制。\n\n- `maxLength`和`maxLengthEnforced` ：`maxLength`代表输入框文本的最大长度，设置后输入框右下角会显示输入的文本计数。`maxLengthEnforced`决定当输入文本长度超过`maxLength`时是否阻止输入，为`true`时会阻止输入，为`false`时不会阻止输入但输入框会变红。\n\n- `onChange`：输入框内容改变时的回调函数；注：内容改变事件也可以通过`controller`来监听。\n\n- `onEditingComplete`和`onSubmitted`：这两个回调都是在输入框输入完成时触发，比如按了键盘的完成键（对号图标）或搜索键（🔍图标）。不同的是两个回调签名不同，`onSubmitted`回调是`ValueChanged<String>`类型，它接收当前输入内容做为参数，而`onEditingComplete`不接收参数。\n\n- `inputFormatters`：用于指定输入格式；当用户输入内容改变时，会根据指定的格式来校验。\n\n- `enable`：如果为`false`，则输入框会被禁用，禁用状态不接收输入和事件，同时显示禁用态样式（在其`decoration`中定义）。\n\n- `cursorWidth`、`cursorRadius`和`cursorColor`：这三个属性是用于自定义输入框光标宽度、圆角和颜色的。\n\n#### 示例：登录输入框\n\n##### 布局\n\n```dart\nColumn(\n        children: <Widget>[\n          TextField(\n            autofocus: true,\n            decoration: InputDecoration(\n                labelText: \"用户名\",\n                hintText: \"用户名或邮箱\",\n                prefixIcon: Icon(Icons.person)\n            ),\n          ),\n          TextField(\n            decoration: InputDecoration(\n                labelText: \"密码\",\n                hintText: \"您的登录密码\",\n                prefixIcon: Icon(Icons.lock)\n            ),\n            obscureText: true,\n          ),\n        ],\n);\n```\n\n运行后，效果如图3-25所示：\n\n![图3-25](../imgs/3-25.png)\n\n##### 获取输入内容\n\n获取输入内容有两种方式：\n\n1. 定义两个变量，用于保存用户名和密码，然后在`onChange`触发时，各自保存一下输入内容。\n2. 通过`controller`直接获取。\n\n第一种方式比较简单，不在举例，我们来重点看一下第二种方式，我们以用户名输入框举例：\n\n定义一个`controller`：\n\n```dart\n//定义一个controller\nTextEditingController _unameController = TextEditingController();\n```\n\n然后设置输入框controller：\n\n```dart\nTextField(\n    autofocus: true,\n    controller: _unameController, //设置controller\n    ...\n)\n```\n\n通过controller获取输入框内容\n\n```dart\nprint(_unameController.text)\n```\n\n##### 监听文本变化\n\n监听文本变化也有两种方式：\n\n1. 设置`onChange`回调，如：\n\n   ```dart\n   TextField(\n       autofocus: true,\n       onChanged: (v) {\n         print(\"onChange: $v\");\n       }\n   )\n   ```\n\n2. 通过`controller`监听，如：\n\n   ```dart\n   @override\n   void initState() {\n     //监听输入改变  \n     _unameController.addListener((){\n       print(_unameController.text);\n     });\n   }\n   ```\n\n两种方式相比，`onChanged`是专门用于监听文本变化，而`controller`的功能却多一些，除了能监听文本变化外，它还可以设置默认值、选择文本，下面我们看一个例子：\n\n创建一个`controller`:\n\n```dart\nTextEditingController _selectionController =  TextEditingController();\n```\n\n设置默认值，并从第三个字符开始选中后面的字符\n\n```dart\n_selectionController.text=\"hello world!\";\n_selectionController.selection=TextSelection(\n    baseOffset: 2,\n    extentOffset: _selectionController.text.length\n);\n```\n\n设置`controlle`r:\n\n```dart\nTextField(\n  controller: _selectionController,\n)\n```\n\n运行效果如图3-26所示：\n\n![图3-26](../imgs/3-26.png)\n\n##### 控制焦点\n\n焦点可以通过`FocusNode`和`FocusScopeNode`来控制，默认情况下，焦点由`FocusScope`来管理，它代表焦点控制范围，可以在这个范围内可以通过`FocusScopeNode`在输入框之间移动焦点、设置默认焦点等。我们可以通过`FocusScope.of(context)` 来获取Widget树中默认的`FocusScopeNode`。下面看一个示例，在此示例中创建两个`TextField`，第一个自动获取焦点，然后创建两个按钮：\n\n- 点击第一个按钮可以将焦点从第一个`TextField`挪到第二个`TextField`。\n- 点击第二个按钮可以关闭键盘。\n\n我们要实现的效果如图3-27所示：\n\n![图3-27](../imgs/3-27.png)\n\n代码如下：\n\n```dart\nclass FocusTestRoute extends StatefulWidget {\n  @override\n  _FocusTestRouteState createState() => new _FocusTestRouteState();\n}\n\nclass _FocusTestRouteState extends State<FocusTestRoute> {\n  FocusNode focusNode1 = new FocusNode();\n  FocusNode focusNode2 = new FocusNode();\n  FocusScopeNode focusScopeNode;\n\n  @override\n  Widget build(BuildContext context) {\n    return Padding(\n      padding: EdgeInsets.all(16.0),\n      child: Column(\n        children: <Widget>[\n          TextField(\n            autofocus: true, \n            focusNode: focusNode1,//关联focusNode1\n            decoration: InputDecoration(\n                labelText: \"input1\"\n            ),\n          ),\n          TextField(\n            focusNode: focusNode2,//关联focusNode2\n            decoration: InputDecoration(\n                labelText: \"input2\"\n            ),\n          ),\n          Builder(builder: (ctx) {\n            return Column(\n              children: <Widget>[\n                RaisedButton(\n                  child: Text(\"移动焦点\"),\n                  onPressed: () {\n                    //将焦点从第一个TextField移到第二个TextField\n                    // 这是一种写法 FocusScope.of(context).requestFocus(focusNode2);\n                    // 这是第二种写法\n                    if(null == focusScopeNode){\n                      focusScopeNode = FocusScope.of(context);\n                    }\n                    focusScopeNode.requestFocus(focusNode2);\n                  },\n                ),\n                RaisedButton(\n                  child: Text(\"隐藏键盘\"),\n                  onPressed: () {\n                    // 当所有编辑框都失去焦点时键盘就会收起  \n                    focusNode1.unfocus();\n                    focusNode2.unfocus();\n                  },\n                ),\n              ],\n            );\n          },\n          ),\n        ],\n      ),\n    );\n  }\n\n}\n```\n\n`FocusNode`和`FocusScopeNode`还有一些其它的方法，详情可以查看API文档。\n\n##### 监听焦点状态改变事件\n\n`FocusNode`继承自`ChangeNotifier`，通过`FocusNode`可以监听焦点的改变事件，如：\n\n```dart\n...\n// 创建 focusNode   \nFocusNode focusNode = new FocusNode();\n...\n// focusNode绑定输入框   \nTextField(focusNode: focusNode);\n...\n// 监听焦点变化    \nfocusNode.addListener((){\n   print(focusNode.hasFocus);\n});\n```\n\n获得焦点时`focusNode.hasFocus`值为`true`，失去焦点时为`false`。\n\n##### 自定义样式\n\n虽然我们可以通过`decoration`属性来定义输入框样式，下面以自定义输入框下划线颜色为例来介绍一下：\n\n```dart\nTextField(\n  decoration: InputDecoration(\n    labelText: \"请输入用户名\",\n    prefixIcon: Icon(Icons.person),\n    // 未获得焦点下划线设为灰色\n    enabledBorder: UnderlineInputBorder(\n      borderSide: BorderSide(color: Colors.grey),\n    ),\n    //获得焦点下划线设为蓝色\n    focusedBorder: UnderlineInputBorder(\n      borderSide: BorderSide(color: Colors.blue),\n    ),\n  ),\n),\n```\n\n上面代码我们直接通过InputDecoration的enabledBorder和focusedBorder来分别设置了输入框在未获取焦点和获得焦点后的下划线颜色。另外，我们也可以通过主题来自定义输入框的样式，下面我们探索一下如何在不使用enabledBorder和focusedBorder的情况下来自定义下滑线颜色。\n\n由于`TextField`在绘制下划线时使用的颜色是主题色里面的`hintColor`，但提示文本颜色也是用的`hintColor`， 如果我们直接修改`hintColor`，那么下划线和提示文本的颜色都会变。值得高兴的是`decoration`中可以设置`hintStyle`，它可以覆盖`hintColor`，并且主题中可以通过`inputDecorationTheme`来设置输入框默认的`decoration`。所以我们可以通过主题来自定义，代码如下：\n\n```dart\nTheme(\n  data: Theme.of(context).copyWith(\n      hintColor: Colors.grey[200], //定义下划线颜色\n      inputDecorationTheme: InputDecorationTheme(\n          labelStyle: TextStyle(color: Colors.grey),//定义label字体样式\n          hintStyle: TextStyle(color: Colors.grey, fontSize: 14.0)//定义提示文本样式\n      )\n  ),\n  child: Column(\n    children: <Widget>[\n      TextField(\n        decoration: InputDecoration(\n            labelText: \"用户名\",\n            hintText: \"用户名或邮箱\",\n            prefixIcon: Icon(Icons.person)\n        ),\n      ),\n      TextField(\n        decoration: InputDecoration(\n            prefixIcon: Icon(Icons.lock),\n            labelText: \"密码\",\n            hintText: \"您的登录密码\",\n            hintStyle: TextStyle(color: Colors.grey, fontSize: 13.0)\n        ),\n        obscureText: true,\n      )\n    ],\n  )\n)\n```\n\n运行效果如图3-28所示：\n\n![图3-28](../imgs/3-28.png)\n\n我们成功的自定义了下划线颜色和提问文字样式，细心的读者可能已经发现，通过这种方式自定义后，输入框在获取焦点时，`labelText`不会高亮显示了，正如上图中的\"用户名\"本应该显示蓝色，但现在却显示为灰色，并且我们还是无法定义下划线宽度。另一种灵活的方式是直接隐藏掉`TextField`本身的下划线，然后通过`Container`去嵌套定义样式，如:\n\n```dart\nContainer(\n  child: TextField(\n    keyboardType: TextInputType.emailAddress,\n    decoration: InputDecoration(\n        labelText: \"Email\",\n        hintText: \"电子邮件地址\",\n        prefixIcon: Icon(Icons.email),\n        border: InputBorder.none //隐藏下划线\n    )\n  ),\n  decoration: BoxDecoration(\n      // 下滑线浅灰色，宽度1像素\n      border: Border(bottom: BorderSide(color: Colors.grey[200], width: 1.0))\n  ),\n)\n```\n\n运行效果：\n\n![image-20180904150511545](https://cdn.jsdelivr.net/gh/flutterchina/flutter-in-action@1.0/docs/imgs/image-20180904150511545.png)\n\n通过这种组件组合的方式，也可以定义背景圆角等。一般来说，优先通过`decoration`来自定义样式，如果`decoration`实现不了，再用widget组合的方式。\n\n> 思考题：在这个示例中，下划线颜色是固定的，所以获得焦点后颜色仍然为灰色，如何实现点击后下滑线也变色呢？\n\n## 3.7.2 表单Form\n\n实际业务中，在正式向服务器提交数据前，都会对各个输入框数据进行合法性校验，但是对每一个`TextField`都分别进行校验将会是一件很麻烦的事。还有，如果用户想清除一组`TextField`的内容，除了一个一个清除有没有什么更好的办法呢？为此，Flutter提供了一个`Form` 组件，它可以对输入框进行分组，然后进行一些统一操作，如输入内容校验、输入框重置以及输入内容保存。\n\n#### Form\n\n`Form`继承自`StatefulWidget`对象，它对应的状态类为`FormState`。我们先看看`Form`类的定义：\n\n```dart\nForm({\n  @required Widget child,\n  bool autovalidate = false,\n  WillPopCallback onWillPop,\n  VoidCallback onChanged,\n})\n```\n\n- `autovalidate`：是否自动校验输入内容；当为`true`时，每一个子FormField内容发生变化时都会自动校验合法性，并直接显示错误信息。否则，需要通过调用`FormState.validate()`来手动校验。\n- `onWillPop`：决定`Form`所在的路由是否可以直接返回（如点击返回按钮），该回调返回一个`Future`对象，如果Future的最终结果是`false`，则当前路由不会返回；如果为`true`，则会返回到上一个路由。此属性通常用于拦截返回按钮。\n- `onChanged`：`Form`的任意一个子`FormField`内容发生变化时会触发此回调。\n\n\n\n#### FormField\n\n`Form`的子孙元素必须是`FormField`类型，`FormField`是一个抽象类，定义几个属性，`FormState`内部通过它们来完成操作，`FormField`部分定义如下：\n\n```dart\nconst FormField({\n  ...\n  FormFieldSetter<T> onSaved, //保存回调\n  FormFieldValidator<T>  validator, //验证回调\n  T initialValue, //初始值\n  bool autovalidate = false, //是否自动校验。\n})\n```\n\n为了方便使用，Flutter提供了一个`TextFormField`组件，它继承自`FormField`类，也是`TextField`的一个包装类，所以除了`FormField`定义的属性之外，它还包括`TextField`的属性。\n\n#### FormState \n\n`FormState`为`Form`的`State`类，可以通过`Form.of()`或`GlobalKey`获得。我们可以通过它来对`Form`的子孙`FormField`进行统一操作。我们看看其常用的三个方法：\n\n- `FormState.validate()`：调用此方法后，会调用`Form`子孙`FormField的validate`回调，如果有一个校验失败，则返回false，所有校验失败项都会返回用户返回的错误提示。\n- `FormState.save()`：调用此方法后，会调用`Form`子孙`FormField`的`save`回调，用于保存表单内容\n- `FormState.reset()`：调用此方法后，会将子孙`FormField`的内容清空。\n\n#### 示例\n\n我们修改一下上面用户登录的示例，在提交之前校验：\n\n1. 用户名不能为空，如果为空则提示“用户名不能为空”。\n2. 密码不能小于6位，如果小于6为则提示“密码不能少于6位”。\n\n完整代码：\n\n```dart\nclass FormTestRoute extends StatefulWidget {\n  @override\n  _FormTestRouteState createState() => new _FormTestRouteState();\n}\n\nclass _FormTestRouteState extends State<FormTestRoute> {\n  TextEditingController _unameController = new TextEditingController();\n  TextEditingController _pwdController = new TextEditingController();\n  GlobalKey _formKey= new GlobalKey<FormState>();\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        title:Text(\"Form Test\"),\n      ),\n      body: Padding(\n        padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 24.0),\n        child: Form(\n          key: _formKey, //设置globalKey，用于后面获取FormState\n          autovalidate: true, //开启自动校验\n          child: Column(\n            children: <Widget>[\n              TextFormField(\n                  autofocus: true,\n                  controller: _unameController,\n                  decoration: InputDecoration(\n                      labelText: \"用户名\",\n                      hintText: \"用户名或邮箱\",\n                      icon: Icon(Icons.person)\n                  ),\n                  // 校验用户名\n                  validator: (v) {\n                    return v\n                        .trim()\n                        .length > 0 ? null : \"用户名不能为空\";\n                  }\n\n              ),\n              TextFormField(\n                  controller: _pwdController,\n                  decoration: InputDecoration(\n                      labelText: \"密码\",\n                      hintText: \"您的登录密码\",\n                      icon: Icon(Icons.lock)\n                  ),\n                  obscureText: true,\n                  //校验密码\n                  validator: (v) {\n                    return v\n                        .trim()\n                        .length > 5 ? null : \"密码不能少于6位\";\n                  }\n              ),\n              // 登录按钮\n              Padding(\n                padding: const EdgeInsets.only(top: 28.0),\n                child: Row(\n                  children: <Widget>[\n                    Expanded(\n                      child: RaisedButton(\n                        padding: EdgeInsets.all(15.0),\n                        child: Text(\"登录\"),\n                        color: Theme\n                            .of(context)\n                            .primaryColor,\n                        textColor: Colors.white,\n                        onPressed: () {\n                          //在这里不能通过此方式获取FormState，context不对\n                          //print(Form.of(context));\n                            \n                          // 通过_formKey.currentState 获取FormState后，\n                          // 调用validate()方法校验用户名密码是否合法，校验\n                          // 通过后再提交数据。 \n                          if((_formKey.currentState as FormState).validate()){\n                            //验证通过提交数据\n                          }\n                        },\n                      ),\n                    ),\n                  ],\n                ),\n              )\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n```\n\n\n\n运行后效果如图3-29所示：\n\n![图3-29](../imgs/3-29.png)\n\n\n\n注意，登录按钮的`onPressed`方法中不能通过`Form.of(context)`来获取，原因是，此处的`context`为`FormTestRoute`的context，而`Form.of(context)`是根据所指定`context`向根去查找，而`FormState`是在`FormTestRoute`的子树中，所以不行。正确的做法是通过`Builder`来构建登录按钮，`Builder`会将`widget`节点的`context`作为回调参数：\n\n```dart\nExpanded(\n // 通过Builder来获取RaisedButton所在widget树的真正context(Element) \n  child:Builder(builder: (context){\n    return RaisedButton(\n      ...\n      onPressed: () {\n        //由于本widget也是Form的子代widget，所以可以通过下面方式获取FormState  \n        if(Form.of(context).validate()){\n          //验证通过提交数据\n        }\n      },\n    );\n  })\n)\n```\n\n其实`context`正是操作Widget所对应的`Element`的一个接口，由于Widget树对应的`Element`都是不同的，所以`context`也都是不同的，有关`context`的更多内容会在后面高级部分详细讨论。Flutter中有很多“of(context)”这种方法，读者在使用时一定要注意`context`是否正确。\n\n\n\n\n\n\n"
  },
  {
    "path": "src/v2/chapter3/progress.md",
    "content": "# 3.8 进度指示器\n\nMaterial 组件库中提供了两种进度指示器：`LinearProgressIndicator`和`CircularProgressIndicator`，它们都可以同时用于精确的进度指示和模糊的进度指示。精确进度通常用于任务进度可以计算和预估的情况，比如文件下载；而模糊进度则用户任务进度无法准确获得的情况，如下拉刷新，数据提交等。\n\n### LinearProgressIndicator\n\n`LinearProgressIndicator`是一个线性、条状的进度条，定义如下：\n\n```dart\nLinearProgressIndicator({\n  double value,\n  Color backgroundColor,\n  Animation<Color> valueColor,\n  ...\n})\n```\n\n- `value`：`value`表示当前的进度，取值范围为[0,1]；如果`value`为`null`时则指示器会执行一个循环动画（模糊进度）；当`value`不为`null`时，指示器为一个具体进度的进度条。\n- `backgroundColor`：指示器的背景色。\n- `valueColor`: 指示器的进度条颜色；值得注意的是，该值类型是`Animation<Color> `，这允许我们对进度条的颜色也可以指定动画。如果我们不需要对进度条颜色执行动画，换言之，我们想对进度条应用一种固定的颜色，此时我们可以通过`AlwaysStoppedAnimation`来指定。\n\n### 示例\n\n```dart\n// 模糊进度条(会执行一个动画)\nLinearProgressIndicator(\n  backgroundColor: Colors.grey[200],\n  valueColor: AlwaysStoppedAnimation(Colors.blue),\n),\n//进度条显示50%\nLinearProgressIndicator(\n  backgroundColor: Colors.grey[200],\n  valueColor: AlwaysStoppedAnimation(Colors.blue),\n  value: .5, \n)\n```\n\n运行效果如图3-30所示：\n\n![图3-30](../imgs/3-30.png)\n\n第一个进度条在执行循环动画：蓝色条一直在移动，而第二个进度条是静止的，停在50%的位置。\n\n### CircularProgressIndicator\n\n`CircularProgressIndicator`是一个圆形进度条，定义如下：\n\n```dart\n CircularProgressIndicator({\n  double value,\n  Color backgroundColor,\n  Animation<Color> valueColor,\n  this.strokeWidth = 4.0,\n  ...   \n}) \n```\n\n前三个参数和`LinearProgressIndicator`相同，不再赘述。`strokeWidth` 表示圆形进度条的粗细。示例如下：\n\n```dart\n// 模糊进度条(会执行一个旋转动画)\nCircularProgressIndicator(\n  backgroundColor: Colors.grey[200],\n  valueColor: AlwaysStoppedAnimation(Colors.blue),\n),\n//进度条显示50%，会显示一个半圆\nCircularProgressIndicator(\n  backgroundColor: Colors.grey[200],\n  valueColor: AlwaysStoppedAnimation(Colors.blue),\n  value: .5,\n),\n```\n\n运行效果如图3-31所示：\n\n![图3-31](../imgs/3-31.png)\n\n第一个进度条会执行旋转动画，而第二个进度条是静止的，它停在50%的位置。\n\n### 自定义尺寸\n\n我们可以发现`LinearProgressIndicator`和`CircularProgressIndicator`，并没有提供设置圆形进度条尺寸的参数；如果我们希望`LinearProgressIndicator`的线细一些，或者希望`CircularProgressIndicator`的圆大一些该怎么做？\n\n其实`LinearProgressIndicator`和`CircularProgressIndicator`都是取父容器的尺寸作为绘制的边界的。知道了这点，我们便可以通过尺寸限制类Widget，如`ConstrainedBox`、`SizedBox` （我们将在后面容器类组件一章中介绍）来指定尺寸，如：\n\n```dart\n// 线性进度条高度指定为3\nSizedBox(\n  height: 3,\n  child: LinearProgressIndicator(\n    backgroundColor: Colors.grey[200],\n    valueColor: AlwaysStoppedAnimation(Colors.blue),\n    value: .5,\n  ),\n),\n// 圆形进度条直径指定为100\nSizedBox(\n  height: 100,\n  width: 100,\n  child: CircularProgressIndicator(\n    backgroundColor: Colors.grey[200],\n    valueColor: AlwaysStoppedAnimation(Colors.blue),\n    value: .7,\n  ),\n),\n```\n\n运行效果如图3-32所示：\n\n![图3-32](../imgs/3-32.png)\n\n注意，如果`CircularProgressIndicator`显示空间的宽高不同，则会显示为椭圆。如：\n\n```dart\n// 宽高不等\nSizedBox(\n  height: 100,\n  width: 130,\n  child: CircularProgressIndicator(\n    backgroundColor: Colors.grey[200],\n    valueColor: AlwaysStoppedAnimation(Colors.blue),\n    value: .7,\n  ),\n),\n```\n\n运行效果如图3-33所示：\n\n![progress_oval](../imgs/progress_oval.png)\n\n### 进度色动画\n\n前面说过可以通过`valueColor`对进度条颜色做动画，关于动画我们将在后面专门的章节详细介绍，这里先给出一个例子，读者在了解了Flutter动画一章后再回过头来看。\n\n我们实现一个进度条在3秒内从灰色变成蓝色的动画：\n\n```dart\nimport 'package:flutter/material.dart';\n\nclass ProgressRoute extends StatefulWidget {\n  @override\n  _ProgressRouteState createState() => _ProgressRouteState();\n}\n\nclass _ProgressRouteState extends State<ProgressRoute>\n    with SingleTickerProviderStateMixin {\n  AnimationController _animationController;\n\n  @override\n  void initState() {\n    //动画执行时间3秒  \n    _animationController =\n        new AnimationController(vsync: this, duration: Duration(seconds: 3));\n    _animationController.forward();\n    _animationController.addListener(() => setState(() => {}));\n    super.initState();\n  }\n\n  @override\n  void dispose() {\n    _animationController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return SingleChildScrollView(\n      child: Column(\n        children: <Widget>[\n            Padding(\n            padding: EdgeInsets.all(16),\n            child: LinearProgressIndicator(\n              backgroundColor: Colors.grey[200],\n              valueColor: ColorTween(begin: Colors.grey, end: Colors.blue)\n                .animate(_animationController), // 从灰色变成蓝色\n              value: _animationController.value,\n            ),\n          );\n        ],\n      ),\n    );\n  }\n}\n```\n\n### 自定义进度指示器样式\n\n定制进度指示器风格样式，可以通过`CustomPainter` Widget 来自定义绘制逻辑，实际上`LinearProgressIndicator`和`CircularProgressIndicator`也正是通过`CustomPainter`来实现外观绘制的。关于`CustomPainter`，我们将在后面“自定义Widget”一章中详细介绍。\n\n> [flutter_spinkit](https://pub.flutter-io.cn/packages/flutter_spinkit) 包提供了多种风格的模糊进度指示器，读者若是感兴趣，可以参考。\n"
  },
  {
    "path": "src/v2/chapter3/radio_and_checkbox.md",
    "content": "# 3.6 单选开关和复选框\n\nMaterial 组件库中提供了Material风格的单选开关`Switch`和复选框`Checkbox`，虽然它们都是继承自`StatefulWidget`，但它们本身不会保存当前选中状态，选中状态都是由父组件来管理的。当`Switch`或`Checkbox`被点击时，会触发它们的`onChanged`回调，我们可以在此回调中处理选中状态改变逻辑。下面看一个简单的例子：\n\n```dart\nclass SwitchAndCheckBoxTestRoute extends StatefulWidget {\n  @override\n  _SwitchAndCheckBoxTestRouteState createState() => new _SwitchAndCheckBoxTestRouteState();\n}\n\nclass _SwitchAndCheckBoxTestRouteState extends State<SwitchAndCheckBoxTestRoute> {\n  bool _switchSelected=true; //维护单选开关状态\n  bool _checkboxSelected=true;//维护复选框状态\n  @override\n  Widget build(BuildContext context) {\n    return Column(\n      children: <Widget>[\n        Switch(\n          value: _switchSelected,//当前状态\n          onChanged:(value){\n            //重新构建页面  \n            setState(() {\n              _switchSelected=value;\n            });\n          },\n        ),\n        Checkbox(\n          value: _checkboxSelected,\n          activeColor: Colors.red, //选中时的颜色\n          onChanged:(value){\n            setState(() {\n              _checkboxSelected=value;\n            });\n          } ,\n        )\n      ],\n    );\n  }\n}\n```\n\n\n\n上面代码中，由于需要维护`Switch`和`Checkbox`的选中状态，所以`SwitchAndCheckBoxTestRoute`继承自`StatefulWidget` 。在其`build`方法中分别构建了一个`Switch`和`Checkbox`，初始状态都为选中状态，当用户点击时，会将状态置反，然后回调用`setState()`通知Flutter framework重新构建UI。\n\n![图3-23](../imgs/3-23.png)\n\n\n\n### 属性及外观\n\n`Switch`和`Checkbox`属性比较简单，读者可以查看API文档，它们都有一个`activeColor`属性，用于设置激活态的颜色。至于大小，到目前为止，`Checkbox`的大小是固定的，无法自定义，而`Switch`只能定义宽度，高度也是固定的。值得一提的是`Checkbox`有一个属性`tristate` ，表示是否为三态，其默认值为`false` ，这时Checkbox有两种状态即“选中”和“不选中”，对应的value值为`true`和`false` 。如果`tristate`值为`true`时，value的值会增加一个状态`null`，读者可以自行了解。\n\n\n\n### 总结\n\n通过`Switch`和`Checkbox`我们可以看到，虽然它们本身是与状态（是否选中）关联的，但它们却不是自己来维护状态，而是需要父组件来管理状态，然后当用户点击时，再通过事件通知给父组件，这样是合理的，因为`Switch`和`Checkbox`是否选中本就和用户数据关联，而这些用户数据也不可能是它们的私有状态。我们在自定义组件时也应该思考一下哪种状态的管理方式最为合理。\n\n"
  },
  {
    "path": "src/v2/chapter3/state_manage.md",
    "content": "# 3.2 状态管理\n\n响应式的编程框架中都会有一个永恒的主题——“状态(State)管理”，无论是在React/Vue（两者都是支持响应式编程的Web开发框架）还是Flutter中，他们讨论的问题和解决的思想都是一致的。所以，如果你对React/Vue的状态管理有了解，可以跳过本节。言归正传，我们想一个问题，`StatefulWidget`的状态应该被谁管理？Widget本身？父Widget？都会？还是另一个对象？答案是取决于实际情况！以下是管理状态的最常见的方法：\n\n- Widget管理自己的状态。\n- Widget管理子Widget状态。\n- 混合管理（父Widget和子Widget都管理状态）。\n\n如何决定使用哪种管理方法？下面是官方给出的一些原则可以帮助你做决定：\n\n- 如果状态是用户数据，如复选框的选中状态、滑块的位置，则该状态最好由父Widget管理。\n- 如果状态是有关界面外观效果的，例如颜色、动画，那么状态最好由Widget本身来管理。\n- 如果某一个状态是不同Widget共享的则最好由它们共同的父Widget管理。\n\n在Widget内部管理状态封装性会好一些，而在父Widget中管理会比较灵活。有些时候，如果不确定到底该怎么管理状态，那么推荐的首选是在父widget中管理（灵活会显得更重要一些）。\n\n接下来，我们将通过创建三个简单示例TapboxA、TapboxB和TapboxC来说明管理状态的不同方式。 这些例子功能是相似的 ——创建一个盒子，当点击它时，盒子背景会在绿色与灰色之间切换。状态 `_active`确定颜色：绿色为`true` ，灰色为`false`，如图3-4所示。![a large grey box with the text, 'Inactive'](../imgs/3-4.png)\n\n\n\n下面的例子将使用`GestureDetector`来识别点击事件，关于该`GestureDetector`的详细内容我们将在后面“事件处理”一章中介绍。\n\n### 3.2.1 Widget管理自身状态\n\n_TapboxAState 类:\n\n- 管理TapboxA的状态。\n- 定义`_active`：确定盒子的当前颜色的布尔值。\n- 定义`_handleTap()`函数，该函数在点击该盒子时更新`_active`，并调用`setState()`更新UI。\n- 实现widget的所有交互式行为。\n\n```dart\n// TapboxA 管理自身状态.\n\n//------------------------- TapboxA ----------------------------------\n\nclass TapboxA extends StatefulWidget {\n  TapboxA({Key key}) : super(key: key);\n\n  @override\n  _TapboxAState createState() => new _TapboxAState();\n}\n\nclass _TapboxAState extends State<TapboxA> {\n  bool _active = false;\n\n  void _handleTap() {\n    setState(() {\n      _active = !_active;\n    });\n  }\n\n  Widget build(BuildContext context) {\n    return new GestureDetector(\n      onTap: _handleTap,\n      child: new Container(\n        child: new Center(\n          child: new Text(\n            _active ? 'Active' : 'Inactive',\n            style: new TextStyle(fontSize: 32.0, color: Colors.white),\n          ),\n        ),\n        width: 200.0,\n        height: 200.0,\n        decoration: new BoxDecoration(\n          color: _active ? Colors.lightGreen[700] : Colors.grey[600],\n        ),\n      ),\n    );\n  }\n}\n\n```\n\n\n\n### 3.2.2 父Widget管理子Widget的状态\n\n对于父Widget来说，管理状态并告诉其子Widget何时更新通常是比较好的方式。 例如，`IconButton`是一个图标按钮，但它是一个无状态的Widget，因为我们认为父Widget需要知道该按钮是否被点击来采取相应的处理。\n\n在以下示例中，TapboxB通过回调将其状态导出到其父组件，状态由父组件管理，因此它的父组件为`StatefulWidget`。但是由于TapboxB不管理任何状态，所以`TapboxB`为`StatelessWidget`。\n\n`ParentWidgetState` 类:\n\n- 为TapboxB 管理`_active`状态。\n- 实现`_handleTapboxChanged()`，当盒子被点击时调用的方法。\n- 当状态改变时，调用`setState()`更新UI。\n\nTapboxB 类:\n\n- 继承`StatelessWidget`类，因为所有状态都由其父组件处理。\n- 当检测到点击时，它会通知父组件。\n\n```dart\n// ParentWidget 为 TapboxB 管理状态.\n\n//------------------------ ParentWidget --------------------------------\n\nclass ParentWidget extends StatefulWidget {\n  @override\n  _ParentWidgetState createState() => new _ParentWidgetState();\n}\n\nclass _ParentWidgetState extends State<ParentWidget> {\n  bool _active = false;\n\n  void _handleTapboxChanged(bool newValue) {\n    setState(() {\n      _active = newValue;\n    });\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return new Container(\n      child: new TapboxB(\n        active: _active,\n        onChanged: _handleTapboxChanged,\n      ),\n    );\n  }\n}\n\n//------------------------- TapboxB ----------------------------------\n\nclass TapboxB extends StatelessWidget {\n  TapboxB({Key key, this.active: false, @required this.onChanged})\n      : super(key: key);\n\n  final bool active;\n  final ValueChanged<bool> onChanged;\n\n  void _handleTap() {\n    onChanged(!active);\n  }\n\n  Widget build(BuildContext context) {\n    return new GestureDetector(\n      onTap: _handleTap,\n      child: new Container(\n        child: new Center(\n          child: new Text(\n            active ? 'Active' : 'Inactive',\n            style: new TextStyle(fontSize: 32.0, color: Colors.white),\n          ),\n        ),\n        width: 200.0,\n        height: 200.0,\n        decoration: new BoxDecoration(\n          color: active ? Colors.lightGreen[700] : Colors.grey[600],\n        ),\n      ),\n    );\n  }\n}\n```\n\n \n\n### 3.2.3 混合状态管理\n\n对于一些组件来说，混合管理的方式会非常有用。在这种情况下，组件自身管理一些内部状态，而父组件管理一些其他外部状态。\n\n在下面TapboxC示例中，手指按下时，盒子的周围会出现一个深绿色的边框，抬起时，边框消失。点击完成后，盒子的颜色改变。 TapboxC将其`_active`状态导出到其父组件中，但在内部管理其`_highlight`状态。这个例子有两个状态对象`_ParentWidgetState`和`_TapboxCState`。\n\n`_ParentWidgetStateC `类:\n\n- 管理`_active` 状态。\n- 实现 `_handleTapboxChanged()` ，当盒子被点击时调用。\n- 当点击盒子并且`_active`状态改变时调用`setState()`更新UI。\n\n`_TapboxCState` 对象:\n\n- 管理`_highlight` 状态。\n- `GestureDetector`监听所有tap事件。当用户点下时，它添加高亮（深绿色边框）；当用户释放时，会移除高亮。\n- 当按下、抬起、或者取消点击时更新`_highlight`状态，调用`setState()`更新UI。\n- 当点击时，将状态的改变传递给父组件。\n\n```dart\n//---------------------------- ParentWidget ----------------------------\n\nclass ParentWidgetC extends StatefulWidget {\n  @override\n  _ParentWidgetCState createState() => new _ParentWidgetCState();\n}\n\nclass _ParentWidgetCState extends State<ParentWidgetC> {\n  bool _active = false;\n\n  void _handleTapboxChanged(bool newValue) {\n    setState(() {\n      _active = newValue;\n    });\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return new Container(\n      child: new TapboxC(\n        active: _active,\n        onChanged: _handleTapboxChanged,\n      ),\n    );\n  }\n}\n\n//----------------------------- TapboxC ------------------------------\n\nclass TapboxC extends StatefulWidget {\n  TapboxC({Key key, this.active: false, @required this.onChanged})\n      : super(key: key);\n\n  final bool active;\n  final ValueChanged<bool> onChanged;\n  \n  @override\n  _TapboxCState createState() => new _TapboxCState();\n}\n\nclass _TapboxCState extends State<TapboxC> {\n  bool _highlight = false;\n\n  void _handleTapDown(TapDownDetails details) {\n    setState(() {\n      _highlight = true;\n    });\n  }\n\n  void _handleTapUp(TapUpDetails details) {\n    setState(() {\n      _highlight = false;\n    });\n  }\n\n  void _handleTapCancel() {\n    setState(() {\n      _highlight = false;\n    });\n  }\n\n  void _handleTap() {\n    widget.onChanged(!widget.active);\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    // 在按下时添加绿色边框，当抬起时，取消高亮  \n    return new GestureDetector(\n      onTapDown: _handleTapDown, // 处理按下事件\n      onTapUp: _handleTapUp, // 处理抬起事件\n      onTap: _handleTap,\n      onTapCancel: _handleTapCancel,\n      child: new Container(\n        child: new Center(\n          child: new Text(widget.active ? 'Active' : 'Inactive',\n              style: new TextStyle(fontSize: 32.0, color: Colors.white)),\n        ),\n        width: 200.0,\n        height: 200.0,\n        decoration: new BoxDecoration(\n          color: widget.active ? Colors.lightGreen[700] : Colors.grey[600],\n          border: _highlight\n              ? new Border.all(\n                  color: Colors.teal[700],\n                  width: 10.0,\n                )\n              : null,\n        ),\n      ),\n    );\n  }\n}\n```\n\n另一种实现可能会将高亮状态导出到父组件，但同时保持`_active`状态为内部状态，但如果你要将该TapBox给其它人使用，可能没有什么意义。 开发人员只会关心该框是否处于Active状态，而不在乎高亮显示是如何管理的，所以应该让TapBox内部处理这些细节。\n\n### 3.2.4 全局状态管理\n\n当应用中需要一些跨组件（包括跨路由）的状态需要同步时，上面介绍的方法便很难胜任了。比如，我们有一个设置页，里面可以设置应用的语言，我们为了让设置实时生效，我们期望在语言状态发生改变时，APP中依赖应用语言的组件能够重新build一下，但这些依赖应用语言的组件和设置页并不在一起，所以这种情况用上面的方法很难管理。这时，正确的做法是通过一个全局状态管理器来处理这种相距较远的组件之间的通信。目前主要有两种办法：\n\n1. 实现一个全局的事件总线，将语言状态改变对应为一个事件，然后在APP中依赖应用语言的组件的`initState` 方法中订阅语言改变的事件。当用户在设置页切换语言后，我们发布语言改变事件，而订阅了此事件的组件就会收到通知，收到通知后调用`setState(...)`方法重新`build`一下自身即可。\n2. 使用一些专门用于状态管理的包，如Provider、Redux，读者可以在pub上查看其详细信息。\n\n本书将在\"功能型组件\"一章中介绍Provider包的实现原理及用法，同时也将会在\"事件处理与通知\"一章中实现一个全局事件总线，读者有需要可以直接翻看。\n\n"
  },
  {
    "path": "src/v2/chapter3/text.md",
    "content": "## 3.3 文本及样式\n\n### 3.3.1 Text\n\n`Text`用于显示简单样式文本，它包含一些控制文本显示样式的一些属性，一个简单的例子如下：\n\n```dart\nText(\"Hello world\",\n  textAlign: TextAlign.left,\n);\n\nText(\"Hello world! I'm Jack. \"*4,\n  maxLines: 1,\n  overflow: TextOverflow.ellipsis,\n);\n\nText(\"Hello world\",\n  textScaleFactor: 1.5,\n);\n```\n运行效果如图3-5所示：\n\n![image-20180829103242552](../imgs/3-5.png)\n\n- `textAlign`：文本的对齐方式；可以选择左对齐、右对齐还是居中。注意，对齐的参考系是Text widget本身。本例中虽然是指定了居中对齐，但因为Text文本内容宽度不足一行，Text的宽度和文本内容长度相等，那么这时指定对齐方式是没有意义的，只有Text宽度大于文本内容长度时指定此属性才有意义。下面我们指定一个较长的字符串：\n\n  ```dart\n  Text(\"Hello world \"*6,  //字符串重复六次\n    textAlign: TextAlign.center,\n  )；\n  ```\n\n  运行效果如图3-6所示：\n\n  ![image-20180829104807535](../imgs/3-6.png)\n\n​      字符串内容超过一行，Text宽度等于屏幕宽度，第二行文本便会居中显示。\n\n- `maxLines`、`overflow`：指定文本显示的最大行数，默认情况下，文本是自动折行的，如果指定此参数，则文本最多不会超过指定的行。如果有多余的文本，可以通过`overflow`来指定截断方式，默认是直接截断，本例中指定的截断方式`TextOverflow.ellipsis`，它会将多余文本截断后以省略符“...”表示；TextOverflow的其它截断方式请参考SDK文档。\n- `textScaleFactor`：代表文本相对于当前字体大小的缩放因子，相对于去设置文本的样式`style`属性的`fontSize`，它是调整字体大小的一个快捷方式。该属性的默认值可以通过`MediaQueryData.textScaleFactor`获得，如果没有`MediaQuery`，那么会默认值将为1.0。\n\n### 3.3.2 TextStyle\n\n`TextStyle`用于指定文本显示的样式如颜色、字体、粗细、背景等。我们看一个示例：\n\n```dart\nText(\"Hello world\",\n  style: TextStyle(\n    color: Colors.blue,\n    fontSize: 18.0,\n    height: 1.2,  \n    fontFamily: \"Courier\",\n    background: new Paint()..color=Colors.yellow,\n    decoration:TextDecoration.underline,\n    decorationStyle: TextDecorationStyle.dashed\n  ),\n);\n```\n\n效果如图3-7所示：\n\n![3-7](../imgs/3-7.png)\n\n此示例只展示了TextStyle的部分属性，它还有一些其它属性，属性名基本都是自解释的，在此不再赘述，读者可以查阅SDK文档。值得注意的是：\n\n- `height`：该属性用于指定行高，但它并不是一个绝对值，而是一个因子，具体的行高等于`fontSize`*`height`。\n\n- `fontFamily` ：由于不同平台默认支持的字体集不同，所以在手动指定字体时一定要先在不同平台测试一下。\n\n- `fontSize`：该属性和Text的`textScaleFactor`都用于控制字体大小。但是有两个主要区别：\n\n  - `fontSize`可以精确指定字体大小，而`textScaleFactor`只能通过缩放比例来控制。\n  - `textScaleFactor`主要是用于系统字体大小设置改变时对Flutter应用字体进行全局调整，而`fontSize`通常用于单个文本，字体大小不会跟随系统字体大小变化。\n\n### 3.3.3 TextSpan\n\n在上面的例子中，Text的所有文本内容只能按同一种样式，如果我们需要对一个Text内容的不同部分按照不同的样式显示，这时就可以使用`TextSpan`，它代表文本的一个“片段”。我们看看TextSpan的定义:\n\n```dart\nconst TextSpan({\n  TextStyle style, \n  Sting text,\n  List<TextSpan> children,\n  GestureRecognizer recognizer,\n});\n```\n\n其中`style` 和 `text`属性代表该文本片段的样式和内容。  `children`是一个`TextSpan`的数组，也就是说`TextSpan`可以包括其他`TextSpan`。而`recognizer`用于对该文本片段上用于手势进行识别处理。下面我们看一个效果（图3-8），然后用`TextSpan`实现它。\n\n![3-8](../imgs/3-8.png)\n\n源码：\n\n```dart\nText.rich(TextSpan(\n    children: [\n     TextSpan(\n       text: \"Home: \"\n     ),\n     TextSpan(\n       text: \"https://flutterchina.club\",\n       style: TextStyle(\n         color: Colors.blue\n       ),  \n       recognizer: _tapRecognizer\n     ),\n    ]\n))\n```\n\n- 上面代码中，我们通过TextSpan实现了一个基础文本片段和一个链接片段，然后通过`Text.rich ` 方法将`TextSpan` 添加到Text中，之所以可以这样做，是因为Text其实就是RichText的一个包装，而RichText是可以显示多种样式(富文本)的widget。\n- `_tapRecognizer`，它是点击链接后的一个处理器（代码已省略），关于手势识别的更多内容我们将在后面单独介绍。\n\n### 3.3.4 DefaultTextStyle\n\n在Widget树中，文本的样式默认是可以被继承的（子类文本类组件未指定具体样式时可以使用Widget树中父级设置的默认样式），因此，如果在Widget树的某一个节点处设置一个默认的文本样式，那么该节点的子树中所有文本都会默认使用这个样式，而`DefaultTextStyle`正是用于设置默认文本样式的。下面我们看一个例子：\n\n```dart\nDefaultTextStyle(\n  //1.设置文本默认样式  \n  style: TextStyle(\n    color:Colors.red,\n    fontSize: 20.0,\n  ),\n  textAlign: TextAlign.start,\n  child: Column(\n    crossAxisAlignment: CrossAxisAlignment.start,\n    children: <Widget>[\n      Text(\"hello world\"),\n      Text(\"I am Jack\"),\n      Text(\"I am Jack\",\n        style: TextStyle(\n          inherit: false, //2.不继承默认样式\n          color: Colors.grey\n        ),\n      ),\n    ],\n  ),\n);\n```\n\n上面代码中，我们首先设置了一个默认的文本样式，即字体为20像素(逻辑像素)、颜色为红色。然后通过`DefaultTextStyle` 设置给了子树Column节点处，这样一来Column的所有子孙Text默认都会继承该样式，除非Text显示指定不继承样式，如代码中注释2。示例运行效果如图3-9：\n\n![3-9](../imgs/3-9.png)\n\n### 3.3.5 字体\n\n可以在Flutter应用程序中使用不同的字体。例如，我们可能会使用设计人员创建的自定义字体，或者其它第三方的字体，如[Google Fonts](https://fonts.google.com/)中的字体。本节将介绍如何为Flutter应用配置字体，并在渲染文本时使用它们。\n\n在Flutter中使用字体分两步完成。首先在`pubspec.yaml`中声明它们，以确保它们会打包到应用程序中。然后通过[`TextStyle`](https://docs.flutter.io/flutter/painting/TextStyle-class.html)属性使用字体。\n\n#### 在asset中声明\n\n要将字体文件打包到应用中，和使用其它资源一样，要先在`pubspec.yaml`中声明它。然后将字体文件复制到在`pubspec.yaml`中指定的位置。如：\n\n```yaml\nflutter:\n  fonts:\n    - family: Raleway\n      fonts:\n        - asset: assets/fonts/Raleway-Regular.ttf\n        - asset: assets/fonts/Raleway-Medium.ttf\n          weight: 500\n        - asset: assets/fonts/Raleway-SemiBold.ttf\n          weight: 600\n    - family: AbrilFatface\n      fonts:\n        - asset: assets/fonts/abrilfatface/AbrilFatface-Regular.ttf\n```\n\n#### 使用字体\n\n```dart\n// 声明文本样式\nconst textStyle = const TextStyle(\n  fontFamily: 'Raleway',\n);\n\n// 使用文本样式\nvar buttonText = const Text(\n  \"Use the font for this text\",\n  style: textStyle,\n);\n```\n\n#### Package中的字体\n\n要使用Package中定义的字体，**必须提供`package`参数**。例如，假设上面的字体声明位于`my_package`包中。然后创建TextStyle的过程如下：\n\n```dart\nconst textStyle = const TextStyle(\n  fontFamily: 'Raleway',\n  package: 'my_package', //指定包名\n);\n```\n\n如果在package包内部使用它自己定义的字体，也应该在创建文本样式时指定`package`参数，如上例所示。\n\n一个包也可以只提供字体文件而不需要在pubspec.yaml中声明。 这些文件应该存放在包的`lib/`文件夹中。字体文件不会自动绑定到应用程序中，应用程序可以在声明字体时有选择地使用这些字体。假设一个名为my_package的包中有一个字体文件：\n\n```\nlib/fonts/Raleway-Medium.ttf\n```\n\n然后，应用程序可以声明一个字体，如下面的示例所示：\n\n```yaml\n flutter:\n   fonts:\n     - family: Raleway\n       fonts:\n         - asset: assets/fonts/Raleway-Regular.ttf\n         - asset: packages/my_package/fonts/Raleway-Medium.ttf\n           weight: 500\n```\n\n`lib/`是隐含的，所以它不应该包含在asset路径中。\n\n在这种情况下，由于应用程序本地定义了字体，所以在创建TextStyle时可以不指定`package`参数：\n\n```dart\nconst textStyle = const TextStyle(\n  fontFamily: 'Raleway',\n);\n```\n\n"
  },
  {
    "path": "src/v2/chapter4/alignment.md",
    "content": "# 4.6 对齐与相对定位（Align）\n\n在上一节中我们讲过通过`Stack`和`Positioned`，我们可以指定一个或多个子元素相对于父元素各个边的精确偏移，并且可以重叠。但如果我们只想简单的调整**一个**子元素在父元素中的位置的话，使用`Align`组件会更简单一些。\n\n## 4.6.1 Align\n\n`Align` 组件可以调整子组件的位置，并且可以根据子组件的宽高来确定自身的的宽高，定义如下：\n\n```dart\nAlign({\n  Key key,\n  this.alignment = Alignment.center,\n  this.widthFactor,\n  this.heightFactor,\n  Widget child,\n})\n```\n\n- `alignment` : 需要一个`AlignmentGeometry`类型的值，表示子组件在父组件中的起始位置。`AlignmentGeometry` 是一个抽象类，它有两个常用的子类：`Alignment`和 `FractionalOffset`，我们将在下面的示例中详细介绍。\n- `widthFactor`和`heightFactor`是用于确定`Align` 组件本身宽高的属性；它们是两个缩放因子，会分别乘以子元素的宽、高，最终的结果就是`Align` 组件的宽高。如果值为`null`，则组件的宽高将会占用尽可能多的空间。\n\n### 示例\n\n我们先来看一个简单的例子：\n\n```dart\nContainer(\n  height: 120.0,\n  width: 120.0,\n  color: Colors.blue[50],\n  child: Align(\n    alignment: Alignment.topRight,\n    child: FlutterLogo(\n      size: 60,\n    ),\n  ),\n)\n```\n\n运行效果如图4-11所示：\n\n![图4-11](../imgs/4-11.png)\n\n`FlutterLogo` 是Flutter SDK提供的一个组件，内容就是Flutter的商标。在上面的例子中，我们显式指定了`Container`的宽、高都为120。如果我们不显式指定宽高，而通过同时指定`widthFactor`和`heightFactor` 为2也是可以达到同样的效果：\n\n```dart\nAlign(\n  widthFactor: 2,\n  heightFactor: 2,\n  alignment: Alignment.topRight,\n  child: FlutterLogo(\n    size: 60,\n  ),\n),\n```\n\n因为`FlutterLogo`的宽高为60，则`Align`的最终宽高都为`2*60=120`。\n\n另外，我们通过`Alignment.topRight`将`FlutterLogo`定位在`Container`的右上角。那`Alignment.topRight`是什么呢？通过源码我们可以看到其定义如下：\n\n```dart\n//右上角\nstatic const Alignment topRight = Alignment(1.0, -1.0);\n```\n\n可以看到它只是`Alignment`的一个实例，下面我们介绍一下`Alignment`。\n\n### Alignment\n\n`Alignment`继承自`AlignmentGeometry`，表示矩形内的一个点，他有两个属性`x`、`y`，分别表示在水平和垂直方向的偏移，`Alignment`定义如下：\n\n```dart\nAlignment(this.x, this.y)\n```\n\n`Alignment` Widget会以**矩形的中心点作为坐标原点**，即`Alignment(0.0, 0.0)` 。`x`、`y`的值从-1到1分别代表矩形左边到右边的距离和顶部到底边的距离，因此2个水平（或垂直）单位则等于矩形的宽（或高），如`Alignment(-1.0, -1.0)` 代表矩形的左侧顶点，而`Alignment(1.0, 1.0)`代表右侧底部终点，而`Alignment(1.0, -1.0)` 则正是右侧顶点，即`Alignment.topRight`。为了使用方便，矩形的原点、四个顶点，以及四条边的终点在`Alignment`类中都已经定义为了静态常量。\n\n`Alignment`可以通过其**坐标转换公式**将其坐标转为子元素的具体偏移坐标：\n\n```\n(Alignment.x*childWidth/2+childWidth/2, Alignment.y*childHeight/2+childHeight/2)\n```\n\n其中`childWidth`为子元素的宽度，`childHeight`为子元素高度。\n\n现在我们再看看上面的示例，我们将`Alignment(1.0, -1.0)`带入上面公式，可得`FlutterLogo`的实际偏移坐标正是（60，0）。下面再看一个例子：\n\n```dart\n Align(\n  widthFactor: 2,\n  heightFactor: 2,\n  alignment: Alignment(2,0.0),\n  child: FlutterLogo(\n    size: 60,\n  ),\n)\n```\n\n我们可以先想象一下运行效果：将`Alignment(2,0.0)`带入上述坐标转换公式，可以得到`FlutterLogo`的实际偏移坐标为（90，30）。实际运行如图4-12所示：\n\n![图4-12](../imgs/4-12.png)\n\n### FractionalOffset\n\n`FractionalOffset` 继承自 `Alignment `，它和 `Alignment `唯一的区别就是坐标原点不同！`FractionalOffset` 的坐标原点为矩形的左侧顶点，这和布局系统的一致，所以理解起来会比较容易。`FractionalOffset`的坐标转换公式为：\n\n```\n实际偏移 = (FractionalOffse.x * childWidth, FractionalOffse.y * childHeight)\n```\n\n下面看一个例子：\n\n```dart\nContainer(\n  height: 120.0,\n  width: 120.0,\n  color: Colors.blue[50],\n  child: Align(\n    alignment: FractionalOffset(0.2, 0.6),\n    child: FlutterLogo(\n      size: 60,\n    ),\n  ),\n)\n```\n\n实际运行效果如图4-13所示下：\n\n![图4-13](../imgs/4-13.png)\n\n我们将`FractionalOffset(0.2, 0.6)`带入坐标转换公式得`FlutterLogo`实际偏移为（12，36），和实际运行效果吻合。\n\n## 4.6.2 Align和Stack对比\n\n可以看到，`Align`和`Stack`/`Positioned`都可以用于指定子元素相对于父元素的偏移，但它们还是有两个主要区别：\n\n1. 定位参考系统不同；`Stack`/`Positioned`定位的的参考系可以是父容器矩形的四个顶点；而`Align`则需要先通过`alignment` 参数来确定坐标原点，不同的`alignment`会对应不同原点，最终的偏移是需要通过`alignment`的转换公式来计算出。\n2. `Stack`可以有多个子元素，并且子元素可以堆叠，而`Align`只能有一个子元素，不存在堆叠。\n\n\n\n## 4.6.3 Center组件\n\n我们在前面章节的例子中已经使用过`Center`组件来居中子元素了，现在我们正式来介绍一下它。通过查找SDK源码，我们看到`Center`组件定义如下：\n\n```dart\nclass Center extends Align {\n  const Center({ Key key, double widthFactor, double heightFactor, Widget child })\n    : super(key: key, widthFactor: widthFactor, heightFactor: heightFactor, child: child);\n}\n```\n\n可以看到`Center`继承自`Align`，它比`Align`只少了一个`alignment` 参数；由于`Align`的构造函数中`alignment` 值为`Alignment.center`，所以，我们可以认为`Center`组件其实是对齐方式确定（`Alignment.center`）了的`Align`。\n\n上面我们讲过当`widthFactor`或`heightFactor`为`null`时组件的宽高将会占用尽可能多的空间，这一点需要特别注意，我们通过一个示例说明：\n\n```dart\n...//省略无关代码\nDecoratedBox(\n  decoration: BoxDecoration(color: Colors.red),\n  child: Center(\n    child: Text(\"xxx\"),\n  ),\n),\nDecoratedBox(\n  decoration: BoxDecoration(color: Colors.red),\n  child: Center(\n    widthFactor: 1,\n    heightFactor: 1,\n    child: Text(\"xxx\"),\n  ),\n)\n```\n\n运行效果如图4-14所示：\n\n![图4-14](../imgs/4-14.png)\n\n## 总结\n\n本节重点介绍了`Align`组件及两种偏移类`Alignment` 和`FractionalOffset`，读者需要理解这两种偏移类的区别及各自的坐标转化公式。另外，在此建议读者在需要制定一些精确的偏移时应优先使用`FractionalOffset`，因为它的坐标原点和布局系统相同，能更容易算出实际偏移。\n\n在后面，我们又介绍了`Align`组件和`Stack`/`Positioned`、`Center`的关系，读者可以对比理解。\n\n还有，熟悉Web开发的同学可能会发现`Align`组件的特性和Web开发中相对定位（`position: relative`）非常像，是的！在大多数时候，我们可以直接使用`Align`组件来实现Web中相对定位的效果，读者可以类比记忆。\n"
  },
  {
    "path": "src/v2/chapter4/flex.md",
    "content": "\n\n# 4.3 弹性布局（Flex）\n\n弹性布局允许子组件按照一定比例来分配父容器空间。弹性布局的概念在其它UI系统中也都存在，如H5中的弹性盒子布局，Android中的`FlexboxLayout`等。Flutter中的弹性布局主要通过`Flex`和`Expanded`来配合实现。\n\n### Flex\n\n`Flex`组件可以沿着水平或垂直方向排列子组件，如果你知道主轴方向，使用`Row`或`Column`会方便一些，因为`Row`和`Column`都继承自`Flex`，参数基本相同，所以能使用Flex的地方基本上都可以使用`Row`或`Column`。`Flex`本身功能是很强大的，它也可以和`Expanded`组件配合实现弹性布局。接下来我们只讨论`Flex`和弹性布局相关的属性(其它属性已经在介绍`Row`和`Column`时介绍过了)。\n\n```dart\nFlex({\n  ...\n  @required this.direction, //弹性布局的方向, Row默认为水平方向，Column默认为垂直方向\n  List<Widget> children = const <Widget>[],\n})\n```\n\n`Flex`继承自`MultiChildRenderObjectWidget`，对应的`RenderObject`为`RenderFlex`，`RenderFlex`中实现了其布局算法。\n\n### Expanded\n\n可以按比例“扩伸” `Row`、`Column`和`Flex`子组件所占用的空间。\n\n```dart\nconst Expanded({\n  int flex = 1, \n  @required Widget child,\n})\n```\n\n`flex`参数为弹性系数，如果为0或`null`，则`child`是没有弹性的，即不会被扩伸占用的空间。如果大于0，所有的`Expanded`按照其flex的比例来分割主轴的全部空闲空间。下面我们看一个例子：\n\n```dart\nclass FlexLayoutTestRoute extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return Column(\n      children: <Widget>[\n        //Flex的两个子widget按1：2来占据水平空间  \n        Flex(\n          direction: Axis.horizontal,\n          children: <Widget>[\n            Expanded(\n              flex: 1,\n              child: Container(\n                height: 30.0,\n                color: Colors.red,\n              ),\n            ),\n            Expanded(\n              flex: 2,\n              child: Container(\n                height: 30.0,\n                color: Colors.green,\n              ),\n            ),\n          ],\n        ),\n        Padding(\n          padding: const EdgeInsets.only(top: 20.0),\n          child: SizedBox(\n            height: 100.0,\n            //Flex的三个子widget，在垂直方向按2：1：1来占用100像素的空间  \n            child: Flex(\n              direction: Axis.vertical,\n              children: <Widget>[\n                Expanded(\n                  flex: 2,\n                  child: Container(\n                    height: 30.0,\n                    color: Colors.red,\n                  ),\n                ),\n                Spacer(\n                  flex: 1,\n                ),\n                Expanded(\n                  flex: 1,\n                  child: Container(\n                    height: 30.0,\n                    color: Colors.green,\n                  ),\n                ),\n              ],\n            ),\n          ),\n        ),\n      ],\n    );\n  }\n}\n```\n\n运行效果如图4-5所示：\n\n![弹性布局](../imgs/4-5.png)\n\n示例中的`Spacer`的功能是占用指定比例的空间，实际上它只是`Expanded`的一个包装类，`Spacer`的源码如下：\n\n```dart\nclass Spacer extends StatelessWidget {\n  const Spacer({Key key, this.flex = 1})\n    : assert(flex != null),\n      assert(flex > 0),\n      super(key: key);\n  \n  final int flex;\n\n  @override\n  Widget build(BuildContext context) {\n    return Expanded(\n      flex: flex,\n      child: const SizedBox.shrink(),\n    );\n  }\n}\n```\n\n### 小结\n\n弹性布局比较简单，唯一需要注意的就是`Row`、`Column`以及`Flex`的关系。"
  },
  {
    "path": "src/v2/chapter4/index.md",
    "content": "# 本章目录\n\n* [4.1：布局类组件简介](intro.md)\n* [4.2：线性布局（Row、Column）](row_and_column.md)\n* [4.3：弹性布局（Flex）](flex.md)\n* [4.4：流式布局（Wrap、Flow）](wrap_and_flow.md)      \n* [4.5：层叠布局（Stack、Positioned）](stack.md)\n* [4.6：对齐与相对定位（Align）](alignment.md)  \n\n"
  },
  {
    "path": "src/v2/chapter4/intro.md",
    "content": "# 4.1 布局类组件简介\n\n布局类组件都会包含一个或多个子组件，不同的布局类组件对子组件排版(layout)方式不同。我们在前面说过`Element`树才是最终的绘制树，`Element`树是通过Widget树来创建的（通过`Widget.createElement()`），Widget其实就是Element的配置数据。在Flutter中，根据Widget是否需要包含子节点将Widget分为了三类，分别对应三种Element，如下表：\n\n| Widget                        | 对应的Element                  | 用途                                                         |\n| ----------------------------- | ------------------------------ | ------------------------------------------------------------ |\n| LeafRenderObjectWidget        | LeafRenderObjectElement        | Widget树的叶子节点，用于没有子节点的widget，通常基础组件都属于这一类，如Image。 |\n| SingleChildRenderObjectWidget | SingleChildRenderObjectElement | 包含一个子Widget，如：ConstrainedBox、DecoratedBox等         |\n| MultiChildRenderObjectWidget  | MultiChildRenderObjectElement  | 包含多个子Widget，一般都有一个children参数，接受一个Widget数组。如Row、Column、Stack等 |\n\n> 注意，Flutter中的很多Widget是直接继承自StatelessWidget或StatefulWidget，然后在`build()`方法中构建真正的RenderObjectWidget，如Text，它其实是继承自StatelessWidget，然后在`build()`方法中通过RichText来构建其子树，而RichText才是继承自MultiChildRenderObjectWidget。所以为了方便叙述，我们也可以直接说Text属于MultiChildRenderObjectWidget（其它widget也可以这么描述），这才是本质。读到这里我们也会发现，其实**StatelessWidget和StatefulWidget就是两个用于组合Widget的基类，它们本身并不关联最终的渲染对象（RenderObjectWidget）**。\n\n布局类组件就是指直接或间接继承(包含)`MultiChildRenderObjectWidget`的Widget，它们一般都会有一个`children`属性用于接收子Widget。我们看一下继承关系 Widget > RenderObjectWidget > (Leaf/SingleChild/MultiChild)RenderObjectWidget 。\n\n`RenderObjectWidget`类中定义了创建、更新`RenderObject`的方法，子类必须实现他们，关于`RenderObject`我们现在只需要知道它是最终布局、渲染UI界面的对象即可，也就是说，对于布局类组件来说，其布局算法都是通过对应的`RenderObject`对象来实现的，所以读者如果对接下来介绍的某个布局类组件的原理感兴趣，可以查看其对应的`RenderObject`的实现，比如`Stack`（层叠布局）对应的`RenderObject`对象就是`RenderStack`，而层叠布局的实现就在`RenderStack`中。\n\n在本章中，为了让读者对布局类Widget有个快速的认识，所以我们并不会深入到`RenderObject`的细节中去。在学习本章时，读者的重点是掌握不同布局组件的布局特点，具体原理和细节等我们对Flutter整体入门后，感兴趣的话再去研究。\n\n"
  },
  {
    "path": "src/v2/chapter4/row_and_column.md",
    "content": "\n\n# 4.2 线性布局（Row和Column）\n\n所谓线性布局，即指沿水平或垂直方向排布子组件。Flutter中通过`Row`和`Column`来实现线性布局，类似于Android中的`LinearLayout`控件。`Row`和`Column`都继承自`Flex`，我们将在弹性布局一节中详细介绍`Flex`。\n\n### 主轴和纵轴\n\n对于线性布局，有主轴和纵轴之分，如果布局是沿水平方向，那么主轴就是指水平方向，而纵轴即垂直方向；如果布局沿垂直方向，那么主轴就是指垂直方向，而纵轴就是水平方向。在线性布局中，有两个定义对齐方式的枚举类`MainAxisAlignment`和`CrossAxisAlignment`，分别代表主轴对齐和纵轴对齐。\n\n### Row\n\nRow可以在水平方向排列其子widget。定义如下：\n\n```dart\nRow({\n  ...  \n  TextDirection textDirection,    \n  MainAxisSize mainAxisSize = MainAxisSize.max,    \n  MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,\n  VerticalDirection verticalDirection = VerticalDirection.down,  \n  CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,\n  List<Widget> children = const <Widget>[],\n})\n```\n\n\n- `textDirection`：表示水平方向子组件的布局顺序(是从左往右还是从右往左)，默认为系统当前Locale环境的文本方向(如中文、英语都是从左往右，而阿拉伯语是从右往左)。\n- `mainAxisSize`：表示`Row`在主轴(水平)方向占用的空间，默认是`MainAxisSize.max`，表示尽可能多的占用水平方向的空间，此时无论子widgets实际占用多少水平空间，`Row`的宽度始终等于水平方向的最大宽度；而`MainAxisSize.min`表示尽可能少的占用水平空间，当子组件没有占满水平剩余空间，则`Row`的实际宽度等于所有子组件占用的的水平空间；\n- `mainAxisAlignment`：表示子组件在`Row`所占用的水平空间内对齐方式，如果`mainAxisSize`值为`MainAxisSize.min`，则此属性无意义，因为子组件的宽度等于`Row`的宽度。只有当`mainAxisSize`的值为`MainAxisSize.max`时，此属性才有意义，`MainAxisAlignment.start`表示沿`textDirection`的初始方向对齐，如`textDirection`取值为`TextDirection.ltr`时，则`MainAxisAlignment.start`表示左对齐，`textDirection`取值为`TextDirection.rtl`时表示从右对齐。而`MainAxisAlignment.end`和`MainAxisAlignment.start`正好相反；`MainAxisAlignment.center`表示居中对齐。读者可以这么理解：`textDirection`是`mainAxisAlignment`的参考系。\n- `verticalDirection`：表示`Row`纵轴（垂直）的对齐方向，默认是`VerticalDirection.down`，表示从上到下。\n- `crossAxisAlignment`：表示子组件在纵轴方向的对齐方式，`Row`的高度等于子组件中最高的子元素高度，它的取值和`MainAxisAlignment`一样(包含`start`、`end`、 `center`三个值)，不同的是`crossAxisAlignment`的参考系是`verticalDirection`，即`verticalDirection`值为`VerticalDirection.down`时`crossAxisAlignment.start`指顶部对齐，`verticalDirection`值为`VerticalDirection.up`时，`crossAxisAlignment.start`指底部对齐；而`crossAxisAlignment.end`和`crossAxisAlignment.start`正好相反；\n- `children` ：子组件数组。\n\n### 示例\n\n请阅读下面代码，先想象一下运行的结果：\n\n```dart\nColumn(\n  //测试Row对齐方式，排除Column默认居中对齐的干扰\n  crossAxisAlignment: CrossAxisAlignment.start,\n  children: <Widget>[\n    Row(\n      mainAxisAlignment: MainAxisAlignment.center,\n      children: <Widget>[\n        Text(\" hello world \"),\n        Text(\" I am Jack \"),\n      ],\n    ),\n    Row(\n      mainAxisSize: MainAxisSize.min,\n      mainAxisAlignment: MainAxisAlignment.center,\n      children: <Widget>[\n        Text(\" hello world \"),\n        Text(\" I am Jack \"),\n      ],\n    ),\n    Row(\n      mainAxisAlignment: MainAxisAlignment.end,\n      textDirection: TextDirection.rtl,\n      children: <Widget>[\n        Text(\" hello world \"),\n        Text(\" I am Jack \"),\n      ],\n    ),\n    Row(\n      crossAxisAlignment: CrossAxisAlignment.start,  \n      verticalDirection: VerticalDirection.up,\n      children: <Widget>[\n        Text(\" hello world \", style: TextStyle(fontSize: 30.0),),\n        Text(\" I am Jack \"),\n      ],\n    ),\n  ],\n);\n```\n\n实际运行结果如图4-1所示：\n\n![图4-1](../imgs/4-1.png)\n\n解释：第一个`Row`很简单，默认为居中对齐；第二个`Row`，由于`mainAxisSize`值为`MainAxisSize.min`，`Row`的宽度等于两个`Text`的宽度和，所以对齐是无意义的，所以会从左往右显示；第三个`Row`设置`textDirection`值为`TextDirection.rtl`，所以子组件会从右向左的顺序排列，而此时`MainAxisAlignment.end`表示左对齐，所以最终显示结果就是图中第三行的样子；第四个Row测试的是纵轴的对齐方式，由于两个子Text字体不一样，所以其高度也不同，我们指定了`verticalDirection`值为`VerticalDirection.up`，即从低向顶排列，而此时`crossAxisAlignment`值为`CrossAxisAlignment.start`表示底对齐。\n\n### Column\n\n`Column`可以在垂直方向排列其子组件。参数和`Row`一样，不同的是布局方向为垂直，主轴纵轴正好相反，读者可类比`Row`来理解，下面看一个例子：\n\n```dart\nimport 'package:flutter/material.dart';\n\nclass CenterColumnRoute extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return Column(\n      crossAxisAlignment: CrossAxisAlignment.center,\n      children: <Widget>[\n        Text(\"hi\"),\n        Text(\"world\"),\n      ],\n    );\n  }\n}\n```\n\n运行效果如图4-2所示：\n\n![图4-2示例](../imgs/4-2.png)\n\n\n\n解释：\n\n- 由于我们没有指定`Column`的`mainAxisSize`，所以使用默认值`MainAxisSize.max`，则`Column`会在垂直方向占用尽可能多的空间，此例中为屏幕高度。\n- 由于我们指定了 `crossAxisAlignment` 属性为`CrossAxisAlignment.center`，那么子项在`Column`纵轴方向（此时为水平方向）会居中对齐。注意，在水平方向对齐是有边界的，总宽度为`Column`占用空间的实际宽度，而实际的宽度取决于子项中宽度最大的Widget。在本例中，`Column`有两个子Widget，而显示“world”的`Text`宽度最大，所以`Column`的实际宽度则为`Text(\"world\")` 的宽度，所以居中对齐后`Text(\"hi\")`会显示在`Text(\"world\")`的中间部分。\n\n**实际上，`Row`和`Column`都只会在主轴方向占用尽可能大的空间，而纵轴的长度则取决于他们最大子元素的长度**。如果我们想让本例中的两个文本控件在整个手机屏幕中间对齐，我们有两种方法：\n\n- 将`Column`的宽度指定为屏幕宽度；这很简单，我们可以通过`ConstrainedBox`或`SizedBox`（我们将在后面章节中专门介绍这两个Widget）来强制更改宽度限制，例如：\n\n  ```dart\n  ConstrainedBox(\n    constraints: BoxConstraints(minWidth: double.infinity), \n    child: Column(\n      crossAxisAlignment: CrossAxisAlignment.center,\n      children: <Widget>[\n        Text(\"hi\"),\n        Text(\"world\"),\n      ],\n    ),\n  );\n  ```\n\n  将`minWidth`设为`double.infinity`，可以使宽度占用尽可能多的空间。\n\n- 使用`Center` Widget；我们将在后面章节中介绍。\n\n\n\n### 特殊情况\n\n如果`Row`里面嵌套`Row`，或者`Column`里面再嵌套`Column`，那么只有最外面的`Row`或`Column`会占用尽可能大的空间，里面`Row`或`Column`所占用的空间为实际大小，下面以`Column`为例说明：\n\n```dart\nContainer(\n  color: Colors.green,\n  child: Padding(\n    padding: const EdgeInsets.all(16.0),\n    child: Column(\n      crossAxisAlignment: CrossAxisAlignment.start,\n      mainAxisSize: MainAxisSize.max, //有效，外层Colum高度为整个屏幕\n      children: <Widget>[\n        Container(\n          color: Colors.red,\n          child: Column(\n            mainAxisSize: MainAxisSize.max,//无效，内层Colum高度为实际高度  \n            children: <Widget>[\n              Text(\"hello world \"),\n              Text(\"I am Jack \"),\n            ],\n          ),\n        )\n      ],\n    ),\n  ),\n);\n```\n\n运行效果如图4-3所示：\n\n![图4-3](../imgs/4-3.png)\n\n如果要让里面的`Column`占满外部`Column`，可以使用`Expanded` 组件：\n\n```dart\nExpanded( \n  child: Container(\n    color: Colors.red,\n    child: Column(\n      mainAxisAlignment: MainAxisAlignment.center, //垂直方向居中对齐\n      children: <Widget>[\n        Text(\"hello world \"),\n        Text(\"I am Jack \"),\n      ],\n    ),\n  ),\n)\n```\n\n运行效果如图4-4所示：\n\n![图4-4](../imgs/4-4.png)\n\n我们将在介绍弹性布局时详细介绍Expanded。\n\n"
  },
  {
    "path": "src/v2/chapter4/stack.md",
    "content": "# 4.5 层叠布局 Stack、Positioned\n\n层叠布局和Web中的绝对定位、Android中的Frame布局是相似的，子组件可以根据距父容器四个角的位置来确定自身的位置。绝对定位允许子组件堆叠起来（按照代码中声明的顺序）。Flutter中使用`Stack`和`Positioned`这两个组件来配合实现绝对定位。`Stack`允许子组件堆叠，而`Positioned`用于根据`Stack`的四个角来确定子组件的位置。\n\n### Stack\n\n```dart\nStack({\n  this.alignment = AlignmentDirectional.topStart,\n  this.textDirection,\n  this.fit = StackFit.loose,\n  this.overflow = Overflow.clip,\n  List<Widget> children = const <Widget>[],\n})\n```\n\n- `alignment`：此参数决定如何去对齐没有定位（没有使用`Positioned`）或部分定位的子组件。所谓部分定位，在这里**特指没有在某一个轴上定位：**`left`、`right`为横轴，`top`、`bottom`为纵轴，只要包含某个轴上的一个定位属性就算在该轴上有定位。\n- `textDirection`：和`Row`、`Wrap`的`textDirection`功能一样，都用于确定`alignment`对齐的参考系，即：`textDirection`的值为`TextDirection.ltr`，则`alignment`的`start`代表左，`end`代表右，即`从左往右`的顺序；`textDirection`的值为`TextDirection.rtl`，则alignment的`start`代表右，`end`代表左，即`从右往左`的顺序。\n- `fit`：此参数用于确定**没有定位**的子组件如何去适应`Stack`的大小。`StackFit.loose`表示使用子组件的大小，`StackFit.expand`表示扩伸到`Stack`的大小。\n- `overflow`：此属性决定如何显示超出`Stack`显示空间的子组件；值为`Overflow.clip`时，超出部分会被剪裁（隐藏），值为`Overflow.visible` 时则不会。\n\n### Positioned\n\n```dart\nconst Positioned({\n  Key key,\n  this.left, \n  this.top,\n  this.right,\n  this.bottom,\n  this.width,\n  this.height,\n  @required Widget child,\n})\n```\n\n`left`、`top` 、`right`、 `bottom`分别代表离`Stack`左、上、右、底四边的距离。`width`和`height`用于指定需要定位元素的宽度和高度。注意，`Positioned`的`width`、`height` 和其它地方的意义稍微有点区别，此处用于配合`left`、`top` 、`right`、 `bottom`来定位组件，举个例子，在水平方向时，你只能指定`left`、`right`、`width`三个属性中的两个，如指定`left`和`width`后，`right`会自动算出(`left`+`width`)，如果同时指定三个属性则会报错，垂直方向同理。\n\n### 示例\n\n在下面的例子中，我们通过对几个`Text`组件的定位来演示`Stack`和`Positioned`的特性：\n\n```dart\n//通过ConstrainedBox来确保Stack占满屏幕\nConstrainedBox(\n  constraints: BoxConstraints.expand(),\n  child: Stack(\n    alignment:Alignment.center , //指定未定位或部分定位widget的对齐方式\n    children: <Widget>[\n      Container(child: Text(\"Hello world\",style: TextStyle(color: Colors.white)),\n        color: Colors.red,\n      ),\n      Positioned(\n        left: 18.0,\n        child: Text(\"I am Jack\"),\n      ),\n      Positioned(\n        top: 18.0,\n        child: Text(\"Your friend\"),\n      )        \n    ],\n  ),\n);\n```\n\n运行效果见图4-9：\n\n![图4-9](../imgs/4-9.png)\n\n\n\n由于第一个子文本组件`Text(\"Hello world\")`没有指定定位，并且`alignment`值为`Alignment.center`，所以它会居中显示。第二个子文本组件`Text(\"I am Jack\")`只指定了水平方向的定位(`left`)，所以属于部分定位，即垂直方向上没有定位，那么它在垂直方向的对齐方式则会按照`alignment`指定的对齐方式对齐，即垂直方向居中。对于第三个子文本组件`Text(\"Your friend\")`，和第二个`Text`原理一样，只不过是水平方向没有定位，则水平方向居中。\n\n我们给上例中的`Stack`指定一个`fit`属性，然后将三个子文本组件的顺序调整一下：\n\n```dart\nStack(\n  alignment:Alignment.center ,\n  fit: StackFit.expand, //未定位widget占满Stack整个空间\n  children: <Widget>[\n    Positioned(\n      left: 18.0,\n      child: Text(\"I am Jack\"),\n    ),\n    Container(child: Text(\"Hello world\",style: TextStyle(color: Colors.white)),\n      color: Colors.red,\n    ),\n    Positioned(\n      top: 18.0,\n      child: Text(\"Your friend\"),\n    )\n  ],\n),\n```\n\n显示效果如图4-10所示：\n\n![图4-10](../imgs/4-10.png)\n\n可以看到，由于第二个子文本组件没有定位，所以`fit`属性会对它起作用，就会占满`Stack`。由于`Stack`子元素是堆叠的，所以第一个子文本组件被第二个遮住了，而第三个在最上层，所以可以正常显示。\n\n"
  },
  {
    "path": "src/v2/chapter4/wrap_and_flow.md",
    "content": "# 4.4 流式布局\n\n在介绍Row和Colum时，如果子widget超出屏幕范围，则会报溢出错误，如：\n\n```dart\nRow(\n  children: <Widget>[\n    Text(\"xxx\"*100)\n  ],\n);\n```\n\n运行效果如图4-6所示：\n\n![图4-6](../imgs/4-6.png)\n\n可以看到，右边溢出部分报错。这是因为Row默认只有一行，如果超出屏幕不会折行。我们把超出屏幕显示范围会自动折行的布局称为流式布局。Flutter中通过`Wrap`和`Flow`来支持流式布局，将上例中的Row换成Wrap后溢出部分则会自动折行，下面我们分别介绍`Wrap`和`Flow`.\n\n## 4.4.1 Wrap\n\n下面是Wrap的定义:\n\n```dart\nWrap({\n  ...\n  this.direction = Axis.horizontal,\n  this.alignment = WrapAlignment.start,\n  this.spacing = 0.0,\n  this.runAlignment = WrapAlignment.start,\n  this.runSpacing = 0.0,\n  this.crossAxisAlignment = WrapCrossAlignment.start,\n  this.textDirection,\n  this.verticalDirection = VerticalDirection.down,\n  List<Widget> children = const <Widget>[],\n})\n```\n\n我们可以看到Wrap的很多属性在`Row`（包括`Flex`和`Column`）中也有，如`direction`、`crossAxisAlignment`、`textDirection`、`verticalDirection`等，这些参数意义是相同的，我们不再重复介绍，读者可以查阅前面介绍`Row`的部分。读者可以认为`Wrap`和`Flex`（包括`Row`和`Column`）除了超出显示范围后`Wrap`会折行外，其它行为基本相同。下面我们看一下`Wrap`特有的几个属性：\n\n- `spacing`：主轴方向子widget的间距\n- `runSpacing`：纵轴方向的间距\n- `runAlignment`：纵轴方向的对齐方式\n\n下面看一个示例子：\n\n```dart\nWrap(\n  spacing: 8.0, // 主轴(水平)方向间距\n  runSpacing: 4.0, // 纵轴（垂直）方向间距\n  alignment: WrapAlignment.center, //沿主轴方向居中\n  children: <Widget>[\n    new Chip(\n      avatar: new CircleAvatar(backgroundColor: Colors.blue, child: Text('A')),\n      label: new Text('Hamilton'),\n    ),\n    new Chip(\n      avatar: new CircleAvatar(backgroundColor: Colors.blue, child: Text('M')),\n      label: new Text('Lafayette'),\n    ),\n    new Chip(\n      avatar: new CircleAvatar(backgroundColor: Colors.blue, child: Text('H')),\n      label: new Text('Mulligan'),\n    ),\n    new Chip(\n      avatar: new CircleAvatar(backgroundColor: Colors.blue, child: Text('J')),\n      label: new Text('Laurens'),\n    ),\n  ],\n)\n```\n\n运行效果如图4-7所示：\n\n![图4-7](../imgs/4-7.png)\n\n## 4.4.2 Flow\n\n我们一般很少会使用`Flow`，因为其过于复杂，需要自己实现子widget的位置转换，在很多场景下首先要考虑的是`Wrap`是否满足需求。`Flow`主要用于一些需要自定义布局策略或性能要求较高(如动画中)的场景。`Flow`有如下优点：\n\n- 性能好；`Flow`是一个对子组件尺寸以及位置调整非常高效的控件，`Flow`用转换矩阵在对子组件进行位置调整的时候进行了优化：在`Flow`定位过后，如果子组件的尺寸或者位置发生了变化，在`FlowDelegate`中的`paintChildren()`方法中调用`context.paintChild` 进行重绘，而`context.paintChild`在重绘时使用了转换矩阵，并没有实际调整组件位置。\n- 灵活；由于我们需要自己实现`FlowDelegate`的`paintChildren()`方法，所以我们需要自己计算每一个组件的位置，因此，可以自定义布局策略。\n\n缺点：\n\n- 使用复杂。\n- 不能自适应子组件大小，必须通过指定父容器大小或实现`TestFlowDelegate`的`getSize`返回固定大小。\n\n示例：\n\n我们对六个色块进行自定义流式布局：\n\n```dart\nFlow(\n  delegate: TestFlowDelegate(margin: EdgeInsets.all(10.0)),\n  children: <Widget>[\n    new Container(width: 80.0, height:80.0, color: Colors.red,),\n    new Container(width: 80.0, height:80.0, color: Colors.green,),\n    new Container(width: 80.0, height:80.0, color: Colors.blue,),\n    new Container(width: 80.0, height:80.0,  color: Colors.yellow,),\n    new Container(width: 80.0, height:80.0, color: Colors.brown,),\n    new Container(width: 80.0, height:80.0,  color: Colors.purple,),\n  ],\n)\n```\n\n实现TestFlowDelegate:\n\n```dart\nclass TestFlowDelegate extends FlowDelegate {\n  EdgeInsets margin = EdgeInsets.zero;\n  TestFlowDelegate({this.margin});\n  @override\n  void paintChildren(FlowPaintingContext context) {\n    var x = margin.left;\n    var y = margin.top;\n    //计算每一个子widget的位置  \n    for (int i = 0; i < context.childCount; i++) {\n      var w = context.getChildSize(i).width + x + margin.right;\n      if (w < context.size.width) {\n        context.paintChild(i,\n            transform: new Matrix4.translationValues(\n                x, y, 0.0));\n        x = w + margin.left;\n      } else {\n        x = margin.left;\n        y += context.getChildSize(i).height + margin.top + margin.bottom;\n        //绘制子widget(有优化)  \n        context.paintChild(i,\n            transform: new Matrix4.translationValues(\n                x, y, 0.0));\n         x += context.getChildSize(i).width + margin.left + margin.right;\n      }\n    }\n  }\n\n  @override\n  getSize(BoxConstraints constraints){\n    //指定Flow的大小  \n    return Size(double.infinity,200.0);\n  }\n\n  @override\n  bool shouldRepaint(FlowDelegate oldDelegate) {\n    return oldDelegate != this;\n  }\n}\n```\n\n运行效果见图4-8：\n\n![图4-8](../imgs/4-8.png)\n\n可以看到我们主要的任务就是实现`paintChildren`，它的主要任务是确定每个子widget位置。由于Flow不能自适应子widget的大小，我们通过在`getSize`返回一个固定大小来指定Flow的大小。\n\n"
  },
  {
    "path": "src/v2/chapter5/clip.md",
    "content": "# 5.7 剪裁（Clip）\n\nFlutter中提供了一些剪裁函数，用于对组件进行剪裁。\n\n| 剪裁Widget | 作用                                                     |\n| ---------- | -------------------------------------------------------- |\n| ClipOval   | 子组件为正方形时剪裁为内贴圆形，为矩形时，剪裁为内贴椭圆 |\n| ClipRRect  | 将子组件剪裁为圆角矩形                                   |\n| ClipRect   | 剪裁子组件到实际占用的矩形大小（溢出部分剪裁）           |\n\n下面看一个例子：\n\n```dart\nimport 'package:flutter/material.dart';\n\nclass ClipTestRoute extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    // 头像  \n    Widget avatar = Image.asset(\"imgs/avatar.png\", width: 60.0);\n    return Center(\n      child: Column(\n        children: <Widget>[\n          avatar, //不剪裁\n          ClipOval(child: avatar), //剪裁为圆形\n          ClipRRect( //剪裁为圆角矩形\n            borderRadius: BorderRadius.circular(5.0),\n            child: avatar,\n          ), \n          Row(\n            mainAxisAlignment: MainAxisAlignment.center,\n            children: <Widget>[\n              Align(\n                alignment: Alignment.topLeft,\n                widthFactor: .5,//宽度设为原来宽度一半，另一半会溢出\n                child: avatar,\n              ),\n              Text(\"你好世界\", style: TextStyle(color: Colors.green),)\n            ],\n          ),\n          Row(\n            mainAxisAlignment: MainAxisAlignment.center,\n            children: <Widget>[\n              ClipRect(//将溢出部分剪裁\n                child: Align(\n                  alignment: Alignment.topLeft,\n                  widthFactor: .5,//宽度设为原来宽度一半\n                  child: avatar,\n                ),\n              ),\n              Text(\"你好世界\",style: TextStyle(color: Colors.green))\n            ],\n          ),\n        ],\n      ),\n    );\n  }\n}\n```\n\n\n\n运行效果如图5-24所示：\n\n![图5-24](../imgs/5-24.png)\n\n\n\n上面示例代码注释比较详细，在此不再赘述。但值得一提的是最后的两个`Row`！它们通过`Align`设置`widthFactor`为0.5后，图片的实际宽度等于60×0.5，即原宽度一半，但此时图片溢出部分依然会显示，所以第一个“你好世界”会和图片的另一部分重合，为了剪裁掉溢出部分，我们在第二个`Row`中通过`ClipRect`将溢出部分剪裁掉了。\n\n### CustomClipper\n\n如果我们想剪裁子组件的特定区域，比如，在上面示例的图片中，如果我们只想截取图片中部40×30像素的范围应该怎么做？这时我们可以使用`CustomClipper`来自定义剪裁区域，实现代码如下：\n\n首先，自定义一个`CustomClipper`：\n\n```dart\nclass MyClipper extends CustomClipper<Rect> {\n  @override\n  Rect getClip(Size size) => Rect.fromLTWH(10.0, 15.0, 40.0, 30.0);\n\n  @override\n  bool shouldReclip(CustomClipper<Rect> oldClipper) => false;\n}\n```\n\n- `getClip()`是用于获取剪裁区域的接口，由于图片大小是60×60，我们返回剪裁区域为`Rect.fromLTWH(10.0, 15.0, 40.0, 30.0)`，即图片中部40×30像素的范围。\n- `shouldReclip()` 接口决定是否重新剪裁。如果在应用中，剪裁区域始终不会发生变化时应该返回`false`，这样就不会触发重新剪裁，避免不必要的性能开销。如果剪裁区域会发生变化（比如在对剪裁区域执行一个动画），那么变化后应该返回`true`来重新执行剪裁。\n\n然后，我们通过`ClipRect`来执行剪裁，为了看清图片实际所占用的位置，我们设置一个红色背景：\n\n```dart\nDecoratedBox(\n  decoration: BoxDecoration(\n    color: Colors.red\n  ),\n  child: ClipRect(\n      clipper: MyClipper(), //使用自定义的clipper\n      child: avatar\n  ),\n)\n```\n\n运行效果如图5-25所示：\n\n![图5-25](../imgs/5-25.png)\n\n可以看到我们的剪裁成功了，但是图片所占用的空间大小仍然是60×60（红色区域），这是因为剪裁是在layout完成后的绘制阶段进行的，所以不会影响组件的大小，这和`Transform`原理是相似的。\n\n\n\n\n\n"
  },
  {
    "path": "src/v2/chapter5/constrainedbox_and_sizebox.md",
    "content": "# 5.2 尺寸限制类容器\n\n尺寸限制类容器用于限制容器大小，Flutter中提供了多种这样的容器，如`ConstrainedBox`、`SizedBox`、`UnconstrainedBox`、`AspectRatio`等，本节将介绍一些常用的。\n\n## 5.2.1 ConstrainedBox\n\n`ConstrainedBox`用于对子组件添加额外的约束。例如，如果你想让子组件的最小高度是80像素，你可以使用`const BoxConstraints(minHeight: 80.0)`作为子组件的约束。\n\n#### 示例\n\n我们先定义一个`redBox`，它是一个背景颜色为红色的盒子，不指定它的宽度和高度：\n\n```dart\nWidget redBox=DecoratedBox(\n  decoration: BoxDecoration(color: Colors.red),\n);\n```\n\n我们实现一个最小高度为50，宽度尽可能大的红色容器。\n\n```dart\nConstrainedBox(\n  constraints: BoxConstraints(\n    minWidth: double.infinity, //宽度尽可能大\n    minHeight: 50.0 //最小高度为50像素\n  ),\n  child: Container(\n      height: 5.0, \n      child: redBox \n  ),\n)\n```\n\n运行效果如图5-2所示：\n\n![图5-2](../imgs/5-2.png)\n\n可以看到，我们虽然将Container的高度设置为5像素，但是最终却是50像素，这正是ConstrainedBox的最小高度限制生效了。如果将Container的高度设置为80像素，那么最终红色区域的高度也会是80像素，因为在此示例中，ConstrainedBox只限制了最小高度，并未限制最大高度。\n\n#### BoxConstraints\n\nBoxConstraints用于设置限制条件，它的定义如下：\n\n```dart\nconst BoxConstraints({\n  this.minWidth = 0.0, //最小宽度\n  this.maxWidth = double.infinity, //最大宽度\n  this.minHeight = 0.0, //最小高度\n  this.maxHeight = double.infinity //最大高度\n})\n```\n\nBoxConstraints还定义了一些便捷的构造函数，用于快速生成特定限制规则的BoxConstraints，如`BoxConstraints.tight(Size size)`，它可以生成给定大小的限制；`const BoxConstraints.expand()`可以生成一个尽可能大的用以填充另一个容器的BoxConstraints。除此之外还有一些其它的便捷函数，读者可以查看[API文档](https://docs.flutter.io/flutter/rendering/BoxConstraints-class.html)。\n\n## 5.2.2 SizedBox\n\n`SizedBox`用于给子元素指定固定的宽高，如：\n\n```dart\nSizedBox(\n  width: 80.0,\n  height: 80.0,\n  child: redBox\n)\n```\n运行效果如图5-3所示：\n\n![图5-3](../imgs/5-3.png)\n\n实际上`SizedBox`只是`ConstrainedBox`的一个定制，上面代码等价于：\n\n\n```dart\nConstrainedBox(\n  constraints: BoxConstraints.tightFor(width: 80.0,height: 80.0),\n  child: redBox, \n)\n```\n\n而`BoxConstraints.tightFor(width: 80.0,height: 80.0)`等价于：\n\n```dart\nBoxConstraints(minHeight: 80.0,maxHeight: 80.0,minWidth: 80.0,maxWidth: 80.0)\n```\n\n而实际上`ConstrainedBox`和`SizedBox`都是通过`RenderConstrainedBox`来渲染的，我们可以看到`ConstrainedBox`和`SizedBox`的`createRenderObject()`方法都返回的是一个`RenderConstrainedBox`对象：\n\n```dart\n@override\nRenderConstrainedBox createRenderObject(BuildContext context) {\n  return new RenderConstrainedBox(\n    additionalConstraints: ...,\n  );\n}\n```\n\n\n\n## 5.2.3 多重限制\n\n如果某一个组件有多个父级`ConstrainedBox`限制，那么最终会是哪个生效？我们看一个例子：\n\n```dart\nConstrainedBox(\n    constraints: BoxConstraints(minWidth: 60.0, minHeight: 60.0), //父\n    child: ConstrainedBox(\n      constraints: BoxConstraints(minWidth: 90.0, minHeight: 20.0),//子\n      child: redBox,\n    )\n)\n```\n\n上面我们有父子两个`ConstrainedBox`，他们的限制条件不同，运行后效果如图5-4所示：\n\n![图5-4](../imgs/5-4.png)\n\n最终显示效果是宽90，高60，也就是说是子`ConstrainedBox`的`minWidth`生效，而`minHeight`是父`ConstrainedBox`生效。单凭这个例子，我们还总结不出什么规律，我们将上例中父子限制条件换一下：\n\n```dart\nConstrainedBox(\n    constraints: BoxConstraints(minWidth: 90.0, minHeight: 20.0),\n    child: ConstrainedBox(\n      constraints: BoxConstraints(minWidth: 60.0, minHeight: 60.0),\n      child: redBox,\n    )\n)\n```\n\n运行效果如图5-5所示：\n\n![图5-5](../imgs/5-5.png)\n\n最终的显示效果仍然是90，高60，效果相同，但意义不同，因为此时`minWidth`生效的是父`ConstrainedBox`，而`minHeight`是子`ConstrainedBox`生效。\n\n通过上面示例，我们发现有多重限制时，对于`minWidth`和`minHeight`来说，是取父子中相应数值较大的。实际上，只有这样才能保证父限制与子限制不冲突。\n\n> 思考题：对于`maxWidth`和`maxHeight`，多重限制的策略是什么样的呢？\n\n\n\n## 5.2.4 UnconstrainedBox\n\n`UnconstrainedBox`不会对子组件产生任何限制，它允许其子组件按照其本身大小绘制。一般情况下，我们会很少直接使用此组件，但在\"去除\"多重限制的时候也许会有帮助，我们看下下面的代码：\n\n```dart\nConstrainedBox(\n    constraints: BoxConstraints(minWidth: 60.0, minHeight: 100.0),  //父\n    child: UnconstrainedBox( //“去除”父级限制\n      child: ConstrainedBox(\n        constraints: BoxConstraints(minWidth: 90.0, minHeight: 20.0),//子\n        child: redBox,\n      ),\n    )\n)\n```\n\n上面代码中，如果没有中间的`UnconstrainedBox`，那么根据上面所述的多重限制规则，那么最终将显示一个90×100的红色框。但是由于` UnconstrainedBox` “去除”了父`ConstrainedBox`的限制，则最终会按照子`ConstrainedBox`的限制来绘制`redBox`，即90×20：\n\n![图5-6](../imgs/5-6.png)\n\n但是，读者请注意，`UnconstrainedBox`对父组件限制的“去除”并非是真正的去除：上面例子中虽然红色区域大小是90×20，但上方仍然有80的空白空间。也就是说父限制的`minHeight`(100.0)仍然是生效的，只不过它不影响最终子元素`redBox`的大小，但仍然还是占有相应的空间，可以认为此时的父`ConstrainedBox`是作用于子`UnconstrainedBox`上，而`redBox`只受子`ConstrainedBox`限制，这一点请读者务必注意。\n\n那么有什么方法可以彻底去除父`ConstrainedBox`的限制吗？答案是否定的！所以在此提示读者，在定义一个通用的组件时，如果要对子组件指定限制，那么一定要注意，因为一旦指定限制条件，子组件如果要进行相关自定义大小时将可能非常困难，因为子组件在不更改父组件的代码的情况下无法彻底去除其限制条件。\n\n在实际开发中，当我们发现已经使用`SizedBox`或`ConstrainedBox`给子元素指定了宽高，但是仍然没有效果时，几乎可以断定：已经有父元素已经设置了限制！举个例子，如Material组件库中的`AppBar`（导航栏）的右侧菜单中，我们使用`SizedBox`指定了loading按钮的大小，代码如下：\n\n```dart\n AppBar(\n   title: Text(title),\n   actions: <Widget>[\n         SizedBox(\n             width: 20, \n             height: 20,\n             child: CircularProgressIndicator(\n                 strokeWidth: 3,\n                 valueColor: AlwaysStoppedAnimation(Colors.white70),\n             ),\n         )\n   ],\n)\n```\n\n上面代码运行后，效果如图5-7所示：\n\n![图5-6](../imgs/5-7.png)\n\n我们会发现右侧loading按钮大小并没有发生变化！这正是因为`AppBar`中已经指定了`actions`按钮的限制条件，所以我们要自定义loading按钮大小，就必须通过`UnconstrainedBox`来“去除”父元素的限制，代码如下：\n\n```dart\nAppBar(\n  title: Text(title),\n  actions: <Widget>[\n      UnconstrainedBox(\n            child: SizedBox(\n              width: 20,\n              height: 20,\n              child: CircularProgressIndicator(\n                strokeWidth: 3,\n                valueColor: AlwaysStoppedAnimation(Colors.white70),\n              ),\n          ),\n      )\n  ],\n)\n```\n\n运行后效果如图5-8所示：\n\n![图5-8](../imgs/5-8.png)\n\n生效了！\n\n## 5.2.4 其它尺寸限制类容器\n\n除了上面介绍的这些常用的尺寸限制类容器外，还有一些其他的尺寸限制类容器，比如`AspectRatio`，它可以指定子组件的长宽比、`LimitedBox` 用于指定最大宽高、`FractionallySizedBox` 可以根据父容器宽高的百分比来设置子组件宽高等，由于这些容器使用起来都比较简单，我们便不再赘述，读者可以自行了解。"
  },
  {
    "path": "src/v2/chapter5/container.md",
    "content": "# 5.5 Container\n\n我们在前面的章节示例中多次用到过`Container`组件，本节我们就详细介绍一下`Container`组件。`Container`是一个组合类容器，它本身不对应具体的`RenderObject`，它是`DecoratedBox`、`ConstrainedBox、Transform`、`Padding`、`Align`等组件组合的一个多功能容器，所以我们只需通过一个`Container`组件可以实现同时需要装饰、变换、限制的场景。下面是`Container`的定义：\n\n```dart\nContainer({\n  this.alignment,\n  this.padding, //容器内补白，属于decoration的装饰范围\n  Color color, // 背景色\n  Decoration decoration, // 背景装饰\n  Decoration foregroundDecoration, //前景装饰\n  double width,//容器的宽度\n  double height, //容器的高度\n  BoxConstraints constraints, //容器大小的限制条件\n  this.margin,//容器外补白，不属于decoration的装饰范围\n  this.transform, //变换\n  this.child,\n})\n```\n\n`Container`的大多数属性在介绍其它容器时都已经介绍过了，不再赘述，但有两点需要说明：\n\n- 容器的大小可以通过`width`、`height`属性来指定，也可以通过`constraints`来指定；如果它们同时存在时，`width`、`height`优先。实际上Container内部会根据`width`、`height`来生成一个`constraints`。\n- `color`和`decoration`是互斥的，如果同时设置它们则会报错！实际上，当指定`color`时，`Container`内会自动创建一个`decoration`。\n\n### 实例\n\n我们通过`Container`来实现如图5-16所示的卡片：\n\n![图5-16](../imgs/5-16.png)\n\n\n\n实现代码如下：\n\n```dart\nContainer(\n  margin: EdgeInsets.only(top: 50.0, left: 120.0), //容器外填充\n  constraints: BoxConstraints.tightFor(width: 200.0, height: 150.0), //卡片大小\n  decoration: BoxDecoration(//背景装饰\n      gradient: RadialGradient( //背景径向渐变\n          colors: [Colors.red, Colors.orange],\n          center: Alignment.topLeft,\n          radius: .98\n      ),\n      boxShadow: [ //卡片阴影\n        BoxShadow(\n            color: Colors.black54,\n            offset: Offset(2.0, 2.0),\n            blurRadius: 4.0\n        )\n      ]\n  ),\n  transform: Matrix4.rotationZ(.2), //卡片倾斜变换\n  alignment: Alignment.center, //卡片内文字居中\n  child: Text( //卡片文字\n    \"5.20\", style: TextStyle(color: Colors.white, fontSize: 40.0),\n  ),\n);\n```\n\n\n\n可以看到`Container`具备多种组件的功能，通过查看`Container`源码，我们会很容易发现它正是前面我们介绍过的多种组件组合而成。在Flutter中，`Container`组件也正是组合优先于继承的实例。\n\n### Padding和Margin\n\n接下来我们来研究一下`Container`组件`margin`和`padding`属性的区别:\n\n```dart\n...\nContainer(\n  margin: EdgeInsets.all(20.0), //容器外补白\n  color: Colors.orange,\n  child: Text(\"Hello world!\"),\n),\nContainer(\n  padding: EdgeInsets.all(20.0), //容器内补白\n  color: Colors.orange,\n  child: Text(\"Hello world!\"),\n),\n...\n```\n\n![图5-17](../imgs/5-17.png)\n\n可以发现，直观的感觉就是`margin`的留白是在容器外部，而`padding`的留白是在容器内部，读者需要记住这个差异。事实上，`Container`内`margin`和`padding`都是通过`Padding` 组件来实现的，上面的示例代码实际上等价于：\n\n```dart\n...\nPadding(\n  padding: EdgeInsets.all(20.0),\n  child: DecoratedBox(\n    decoration: BoxDecoration(color: Colors.orange),\n    child: Text(\"Hello world!\"),\n  ),\n),\nDecoratedBox(\n  decoration: BoxDecoration(color: Colors.orange),\n  child: Padding(\n    padding: const EdgeInsets.all(20.0),\n    child: Text(\"Hello world!\"),\n  ),\n),\n...    \n```\n\n"
  },
  {
    "path": "src/v2/chapter5/decoratedbox.md",
    "content": "# 5.3 装饰容器DecoratedBox\n\n`DecoratedBox`可以在其子组件绘制前(或后)绘制一些装饰（Decoration），如背景、边框、渐变等。`DecoratedBox`定义如下：\n\n```dart\nconst DecoratedBox({\n  Decoration decoration,\n  DecorationPosition position = DecorationPosition.background,\n  Widget child\n})\n```\n\n- `decoration`：代表将要绘制的装饰，它的类型为`Decoration`。`Decoration`是一个抽象类，它定义了一个接口 `createBoxPainter()`，子类的主要职责是需要通过实现它来创建一个画笔，该画笔用于绘制装饰。\n- `position`：此属性决定在哪里绘制`Decoration`，它接收`DecorationPosition`的枚举类型，该枚举类有两个值：\n  - `background`：在子组件之后绘制，即背景装饰。\n  - `foreground`：在子组件之上绘制，即前景。\n\n#### BoxDecoration\n\n我们通常会直接使用`BoxDecoration`类，它是一个Decoration的子类，实现了常用的装饰元素的绘制。\n\n```dart\nBoxDecoration({\n  Color color, //颜色\n  DecorationImage image,//图片\n  BoxBorder border, //边框\n  BorderRadiusGeometry borderRadius, //圆角\n  List<BoxShadow> boxShadow, //阴影,可以指定多个\n  Gradient gradient, //渐变\n  BlendMode backgroundBlendMode, //背景混合模式\n  BoxShape shape = BoxShape.rectangle, //形状\n})\n```\n\n各个属性名都是自解释的，详情读者可以查看API文档。下面我们实现一个带阴影的背景色渐变的按钮：\n\n```dart\n DecoratedBox(\n    decoration: BoxDecoration(\n      gradient: LinearGradient(colors:[Colors.red,Colors.orange[700]]), //背景渐变\n      borderRadius: BorderRadius.circular(3.0), //3像素圆角\n      boxShadow: [ //阴影\n        BoxShadow(\n            color:Colors.black54,\n            offset: Offset(2.0,2.0),\n            blurRadius: 4.0\n        )\n      ]\n    ),\n  child: Padding(padding: EdgeInsets.symmetric(horizontal: 80.0, vertical: 18.0),\n    child: Text(\"Login\", style: TextStyle(color: Colors.white),),\n  )\n)\n```\n\n运行后效果如图5-9所示：\n\n![图5-9](../imgs/5-9.png)\n\n怎么样，通过`BoxDecoration`我们实现了一个渐变按钮的外观，但此示例还不是一个标准的按钮，因为它还不能响应点击事件，我们将在后面“自定义组件”一章中实现一个完整功能的`GradientButton`。另外，上面的例子中使用了`LinearGradient`类，它用于定义线性渐变的类，Flutter中还提供了其它渐变配置类，如`RadialGradient`、`SweepGradient`，读者若有需要可以自行查看API文档。\n"
  },
  {
    "path": "src/v2/chapter5/index.md",
    "content": "# 容器类Widget\n\n容器类Widget和布局类Widget都作用于其子Widget，不同的是：\n\n- 布局类Widget一般都需要接收一个widget数组（children），他们直接或间接继承自（或包含）MultiChildRenderObjectWidget ；而容器类Widget一般只需要接收一个子Widget（child），他们直接或间接继承自（或包含）SingleChildRenderObjectWidget。\n- 布局类Widget是按照一定的排列方式来对其子Widget进行排列；而容器类Widget一般只是包装其子Widget，对其添加一些修饰（补白或背景色等）、变换(旋转或剪裁等)、或限制(大小等)。\n\n注意，Flutter官方并没有对Widget进行官方分类，我们对其分类主要是为了方便讨论和对Widget功能区分的记忆。\n\n## 本章目录\n\n* [5.1：填充（Padding）](padding.md)\n* [5.2：尺寸限制类容器（ConstrainedBox等）](constrainedbox_and_sizebox.md)\n* [5.3：装饰容器（DecoratedBox）](decoratedbox.md)      \n* [5.4：变换（Transform）](transform.md) \n* [5.5：Container容器](container.md) \n* [5.6：Scaffold、TabBar、底部导航](material_scaffold.md) \n* [5.7：剪裁（Clip）](clip.md) \n"
  },
  {
    "path": "src/v2/chapter5/material_scaffold.md",
    "content": "# 5.6 Scaffold、TabBar、底部导航\n\nMaterial组件库提供了丰富多样的组件，本节介绍一些常用的组件，其余的读者可以自行查看文档或Flutter Gallery中Material组件部分的示例。\n\n> Flutter Gallery是Flutter官方提供的Flutter Demo，源码位于flutter源码中的examples目录下，笔者强烈建议用户将Flutter Gallery示例跑起来，它是一个很全面的Flutter示例应用，是非常好的参考Demo，也是笔者学习Flutter的第一手资料。\n\n## 5.6.1 Scaffold\n\n一个完整的路由页可能会包含导航栏、抽屉菜单(Drawer)以及底部Tab导航菜单等。如果每个路由页面都需要开发者自己手动去实现这些，这会是一件非常麻烦且无聊的事。幸运的是，Flutter Material组件库提供了一些现成的组件来减少我们的开发任务。`Scaffold`是一个路由页的骨架，我们使用它可以很容易地拼装出一个完整的页面。\n\n### 示例\n\n我们实现一个页面，它包含：\n\n1. 一个导航栏\n2. 导航栏右边有一个分享按钮\n3. 有一个抽屉菜单\n4. 有一个底部导航\n5. 右下角有一个悬浮的动作按钮\n\n最终效果如图5-18、图5-19所示：\n\n![图5-18](../imgs/5-18.png) ![图5-19](../imgs/5-19.png)\n\n\n\n实现代码如下：\n\n```dart\nclass ScaffoldRoute extends StatefulWidget {\n  @override\n  _ScaffoldRouteState createState() => _ScaffoldRouteState();\n}\n\nclass _ScaffoldRouteState extends State<ScaffoldRoute> {\n  int _selectedIndex = 1;\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar( //导航栏\n        title: Text(\"App Name\"), \n        actions: <Widget>[ //导航栏右侧菜单\n          IconButton(icon: Icon(Icons.share), onPressed: () {}),\n        ],\n      ),\n      drawer: new MyDrawer(), //抽屉\n      bottomNavigationBar: BottomNavigationBar( // 底部导航\n        items: <BottomNavigationBarItem>[\n          BottomNavigationBarItem(icon: Icon(Icons.home), title: Text('Home')),\n          BottomNavigationBarItem(icon: Icon(Icons.business), title: Text('Business')),\n          BottomNavigationBarItem(icon: Icon(Icons.school), title: Text('School')),\n        ],\n        currentIndex: _selectedIndex,\n        fixedColor: Colors.blue,\n        onTap: _onItemTapped,\n      ),\n      floatingActionButton: FloatingActionButton( //悬浮按钮\n          child: Icon(Icons.add),\n          onPressed:_onAdd\n      ),\n    );\n  }\n  void _onItemTapped(int index) {\n    setState(() {\n      _selectedIndex = index;\n    });\n  }\n  void _onAdd(){\n  }\n}\n```\n\n上面代码中我们用到了如下组件：\n\n| 组件名称             | 解释           |\n| -------------------- | -------------- |\n| AppBar               | 一个导航栏骨架 |\n| MyDrawer             | 抽屉菜单       |\n| BottomNavigationBar  | 底部导航栏     |\n| FloatingActionButton | 漂浮按钮       |\n\n下面我们来分别介绍一下它们。\n\n## 5.6.2 AppBar\n\n`AppBar`是一个Material风格的导航栏，通过它可以设置导航栏标题、导航栏菜单、导航栏底部的Tab标题等。下面我们看看AppBar的定义：\n\n```dart\nAppBar({\n  Key key,\n  this.leading, //导航栏最左侧Widget，常见为抽屉菜单按钮或返回按钮。\n  this.automaticallyImplyLeading = true, //如果leading为null，是否自动实现默认的leading按钮\n  this.title,// 页面标题\n  this.actions, // 导航栏右侧菜单\n  this.bottom, // 导航栏底部菜单，通常为Tab按钮组\n  this.elevation = 4.0, // 导航栏阴影\n  this.centerTitle, //标题是否居中 \n  this.backgroundColor,\n  ...   //其它属性见源码注释\n})\n```\n\n如果给`Scaffold`添加了抽屉菜单，默认情况下`Scaffold`会自动将`AppBar`的`leading`设置为菜单按钮（如上面截图所示），点击它便可打开抽屉菜单。如果我们想自定义菜单图标，可以手动来设置`leading`，如：\n\n```dart\nScaffold(\n  appBar: AppBar(\n    title: Text(\"App Name\"),\n    leading: Builder(builder: (context) {\n      return IconButton(\n        icon: Icon(Icons.dashboard, color: Colors.white), //自定义图标\n        onPressed: () {\n          // 打开抽屉菜单  \n          Scaffold.of(context).openDrawer(); \n        },\n      );\n    }),\n    ...  \n  )  \n```\n\n代码运行效果如图5-20所示：\n\n![图5-20](../imgs/5-20.png)\n\n可以看到左侧菜单已经替换成功。\n\n代码中打开抽屉菜单的方法在`ScaffoldState`中，通过`Scaffold.of(context)`可以获取父级最近的`Scaffold` 组件的`State`对象。\n\n### TabBar\n\n下面我们通过“bottom”属性来添加一个导航栏底部Tab按钮组，将要实现的效果如图5-21所示：\n\n![图5-21](../imgs/5-21.png)\n\nMaterial组件库中提供了一个`TabBar`组件，它可以快速生成`Tab`菜单，下面是上图对应的源码：\n\n```dart\nclass _ScaffoldRouteState extends State<ScaffoldRoute>\n    with SingleTickerProviderStateMixin {\n\n  TabController _tabController; //需要定义一个Controller\n  List tabs = [\"新闻\", \"历史\", \"图片\"];\n\n  @override\n  void initState() {\n    super.initState();\n    // 创建Controller  \n    _tabController = TabController(length: tabs.length, vsync: this);\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        ... //省略无关代码\n        bottom: TabBar(   //生成Tab菜单\n          controller: _tabController,\n          tabs: tabs.map((e) => Tab(text: e)).toList()\n        ),\n      ),\n      ... //省略无关代码\n\n  }\n```\n\n上面代码首先创建了一个`TabController` ，它是用于控制/监听`Tab`菜单切换的。接下来通过TabBar生成了一个底部菜单栏，`TabBar`的`tabs`属性接受一个Widget数组，表示每一个Tab子菜单，我们可以自定义，也可以像示例中一样直接使用`Tab` 组件，它是Material组件库提供的Material风格的Tab菜单。\n\n`Tab`组件有三个可选参数，除了可以指定文字外，还可以指定Tab菜单图标，或者直接自定义组件样式。`Tab`组件定义如下：\n\n```dart\nTab({\n  Key key,\n  this.text, // 菜单文本\n  this.icon, // 菜单图标\n  this.child, // 自定义组件样式\n})\n```\n\n开发者可以根据实际需求来定制。\n\n### TabBarView\n\n通过`TabBar`我们只能生成一个静态的菜单，真正的Tab页还没有实现。由于`Tab`菜单和Tab页的切换需要同步，我们需要通过`TabController`去监听Tab菜单的切换去切换Tab页，代码如：\n\n```dart\n_tabController.addListener((){  \n  switch(_tabController.index){\n    case 1: ...;\n    case 2: ... ;   \n  }\n});\n```\n\n如果我们Tab页可以滑动切换的话，还需要在滑动过程中更新TabBar指示器的偏移！显然，要手动处理这些是很麻烦的，为此，Material库提供了一个`TabBarView`组件，通过它不仅可以轻松的实现Tab页，而且可以非常容易的配合TabBar来实现同步切换和滑动状态同步，示例如下：\n\n```dart\nScaffold(\n  appBar: AppBar(\n    ... //省略无关代码\n    bottom: TabBar(\n      controller: _tabController,\n      tabs: tabs.map((e) => Tab(text: e)).toList()),\n  ),\n  drawer: new MyDrawer(),\n  body: TabBarView(\n    controller: _tabController,\n    children: tabs.map((e) { //创建3个Tab页\n      return Container(\n        alignment: Alignment.center,\n        child: Text(e, textScaleFactor: 5),\n      );\n    }).toList(),\n  ),\n  ... // 省略无关代码  \n)    \n```\n\n运行后效果如图5-22所示：\n\n![图5-22](../imgs/5-22.png)\n\n现在，无论是点击导航栏Tab菜单还是在页面上左右滑动，Tab页面都会切换，并且Tab菜单的状态和Tab页面始终保持同步！那它们是如何实现同步的呢？细心的读者可能已经发现，上例中`TabBar`和`TabBarView`的`controller`是同一个！正是如此，`TabBar`和`TabBarView`正是通过同一个`controller`来实现菜单切换和滑动状态同步的，有关`TabController`的详细信息，我们不在本书做过多介绍，使用时读者直接查看SDK即可。\n\n另外，Material组件库也提供了一个`PageView` 组件，它和`TabBarView`功能相似，读者可以自行了解一下。\n\n\n\n## 5.6.3 抽屉菜单Drawer\n\n`Scaffold`的`drawer`和`endDrawer`属性可以分别接受一个Widget来作为页面的左、右抽屉菜单。如果开发者提供了抽屉菜单，那么当用户手指从屏幕左（或右）侧向里滑动时便可打开抽屉菜单。本节开始部分的示例中实现了一个左抽屉菜单`MyDrawer`，它的源码如下：\n\n```dart\nclass MyDrawer extends StatelessWidget {\n  const MyDrawer({\n    Key key,\n  }) : super(key: key);\n\n  @override\n  Widget build(BuildContext context) {\n    return Drawer(\n      child: MediaQuery.removePadding(\n        context: context,\n        //移除抽屉菜单顶部默认留白\n        removeTop: true,\n        child: Column(\n          crossAxisAlignment: CrossAxisAlignment.start,\n          children: <Widget>[\n            Padding(\n              padding: const EdgeInsets.only(top: 38.0),\n              child: Row(\n                children: <Widget>[\n                  Padding(\n                    padding: const EdgeInsets.symmetric(horizontal: 16.0),\n                    child: ClipOval(\n                      child: Image.asset(\n                        \"imgs/avatar.png\",\n                        width: 80,\n                      ),\n                    ),\n                  ),\n                  Text(\n                    \"Wendux\",\n                    style: TextStyle(fontWeight: FontWeight.bold),\n                  )\n                ],\n              ),\n            ),\n            Expanded(\n              child: ListView(\n                children: <Widget>[\n                  ListTile(\n                    leading: const Icon(Icons.add),\n                    title: const Text('Add account'),\n                  ),\n                  ListTile(\n                    leading: const Icon(Icons.settings),\n                    title: const Text('Manage accounts'),\n                  ),\n                ],\n              ),\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}\n```\n\n抽屉菜单通常将`Drawer`组件作为根节点，它实现了Material风格的菜单面板，`MediaQuery.removePadding`可以移除Drawer默认的一些留白（比如Drawer默认顶部会留和手机状态栏等高的留白），读者可以尝试传递不同的参数来看看实际效果。抽屉菜单页由顶部和底部组成，顶部由用户头像和昵称组成，底部是一个菜单列表，用ListView实现，关于ListView我们将在后面“可滚动组件”一节详细介绍。\n\n## 5.6.4 FloatingActionButton\n\n`FloatingActionButton`是Material设计规范中的一种特殊Button，通常悬浮在页面的某一个位置作为某种常用动作的快捷入口，如本节示例中页面右下角的\"➕\"号按钮。我们可以通过`Scaffold`的`floatingActionButton`属性来设置一个`FloatingActionButton`，同时通过`floatingActionButtonLocation`属性来指定其在页面中悬浮的位置，这个比较简单，不再赘述。\n\n## 5.6.5  底部Tab导航栏\n\n我们可以通过`Scaffold`的`bottomNavigationBar`属性来设置底部导航，如本节开始示例所示，我们通过Material组件库提供的`BottomNavigationBar`和`BottomNavigationBarItem`两种组件来实现Material风格的底部导航栏。可以看到上面的实现代码非常简单，所以不再赘述，但是如果我们想实现如图5-23所示效果的底部导航栏应该怎么做呢？\n\n![图5-23](../imgs/5-23.png)\n\nMaterial组件库中提供了一个`BottomAppBar` 组件，它可以和`FloatingActionButton`配合实现这种“打洞”效果，源码如下：\n\n```dart\nbottomNavigationBar: BottomAppBar(\n  color: Colors.white,\n  shape: CircularNotchedRectangle(), // 底部导航栏打一个圆形的洞\n  child: Row(\n    children: [\n      IconButton(icon: Icon(Icons.home)),\n      SizedBox(), //中间位置空出\n      IconButton(icon: Icon(Icons.business)),\n    ],\n    mainAxisAlignment: MainAxisAlignment.spaceAround, //均分底部导航栏横向空间\n  ),\n)\n```\n\n可以看到，上面代码中没有控制打洞位置的属性，实际上，打洞的位置取决于`FloatingActionButton`的位置，上面`FloatingActionButton`的位置为：\n\n```dart\nfloatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,\n```\n所以打洞位置在底部导航栏的正中间。\n\n`BottomAppBar`的`shape`属性决定洞的外形，`CircularNotchedRectangle`实现了一个圆形的外形，我们也可以自定义外形，比如，Flutter Gallery示例中就有一个“钻石”形状的示例，读者感兴趣可以自行查看。\n"
  },
  {
    "path": "src/v2/chapter5/padding.md",
    "content": "\n\n## 5.1 填充（Padding）\n\n`Padding`可以给其子节点添加填充（留白），和边距效果类似。我们在前面很多示例中都已经使用过它了，现在来看看它的定义：\n\n```dart\nPadding({\n  ...\n  EdgeInsetsGeometry padding,\n  Widget child,\n})\n```\n\n`EdgeInsetsGeometry`是一个抽象类，开发中，我们一般都使用`EdgeInsets`类，它是`EdgeInsetsGeometry`的一个子类，定义了一些设置填充的便捷方法。\n\n### EdgeInsets\n\n我们看看`EdgeInsets`提供的便捷方法：\n\n- `fromLTRB(double left, double top, double right, double bottom) `：分别指定四个方向的填充。\n- `all(double value)` : 所有方向均使用相同数值的填充。\n- `only({left, top, right ,bottom })`：可以设置具体某个方向的填充(可以同时指定多个方向)。\n- `symmetric({  vertical, horizontal })`：用于设置对称方向的填充，`vertical`指`top`和`bottom`，`horizontal`指`left`和`right`。\n\n### 示例\n\n下面的示例主要展示了`EdgeInsets`的不同用法，比较简单，源码如下：\n\n```dart\nclass PaddingTestRoute extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return Padding(\n      //上下左右各添加16像素补白\n      padding: EdgeInsets.all(16.0),\n      child: Column(\n        //显式指定对齐方式为左对齐，排除对齐干扰\n        crossAxisAlignment: CrossAxisAlignment.start,\n        children: <Widget>[\n          Padding(\n            //左边添加8像素补白\n            padding: const EdgeInsets.only(left: 8.0),\n            child: Text(\"Hello world\"),\n          ),\n          Padding(\n            //上下各添加8像素补白\n            padding: const EdgeInsets.symmetric(vertical: 8.0),\n            child: Text(\"I am Jack\"),\n          ),\n          Padding(\n            // 分别指定四个方向的补白\n            padding: const EdgeInsets.fromLTRB(20.0,.0,20.0,20.0),\n            child: Text(\"Your friend\"),\n          )\n        ],\n      ),\n    );\n  }\n}\n```\n\n运行效果如图5-1所示：\n\n![图5-1](../imgs/5-1.png)"
  },
  {
    "path": "src/v2/chapter5/transform.md",
    "content": "# 5.4 变换（Transform）\n\n`Transform`可以在其子组件绘制时对其应用一些矩阵变换来实现一些特效。`Matrix4`是一个4D矩阵，通过它我们可以实现各种矩阵操作，下面是一个例子：\n\n```dart\nContainer(\n  color: Colors.black,\n  child: new Transform(\n    alignment: Alignment.topRight, //相对于坐标系原点的对齐方式\n    transform: new Matrix4.skewY(0.3), //沿Y轴倾斜0.3弧度\n    child: new Container(\n      padding: const EdgeInsets.all(8.0),\n      color: Colors.deepOrange,\n      child: const Text('Apartment for rent!'),\n    ),\n  ),\n);\n```\n\n运行效果如图5-10所示：\n\n![图5-10](../imgs/5-10.png)\n\n> 关于矩阵变换的相关内容属于线性代数范畴，本书不做讨论，读者有兴趣可以自行了解。本书中，我们把焦点放在Flutter中一些常见的变换效果上。另外，由于矩阵变化时发生在绘制时，而无需重新布局和构建等过程，所以性能很好。\n>\n\n### 平移\n\n`Transform.translate`接收一个`offset`参数，可以在绘制时沿`x`、`y`轴对子组件平移指定的距离。\n\n```dart\nDecoratedBox(\n  decoration:BoxDecoration(color: Colors.red),\n  //默认原点为左上角，左移20像素，向上平移5像素  \n  child: Transform.translate(\n    offset: Offset(-20.0, -5.0),\n    child: Text(\"Hello world\"),\n  ),\n)\n```\n\n效果如图5-11所示：\n\n![图5-11](../imgs/5-11.png)\n\n### 旋转\n\n`Transform.rotate`可以对子组件进行旋转变换，如：\n\n```dart\nDecoratedBox(\n  decoration:BoxDecoration(color: Colors.red),\n  child: Transform.rotate(\n    //旋转90度\n    angle:math.pi/2 ,\n    child: Text(\"Hello world\"),\n  ),\n)；\n```\n> 注意：要使用`math.pi`需先进行如下导包。  \n```dart  \nimport 'dart:math' as math;  \n```\n\n效果如图5-12所示：\n\n![图5-12](../imgs/5-12.png)\n\n### 缩放\n\n`Transform.scale`可以对子组件进行缩小或放大，如：\n\n```dart\nDecoratedBox(\n  decoration:BoxDecoration(color: Colors.red),\n  child: Transform.scale(\n      scale: 1.5, //放大到1.5倍\n      child: Text(\"Hello world\")\n  )\n);\n```\n\n效果如图5-13所示：\n\n![图5-13](../imgs/5-13.png)\n\n### 注意\n\n- `Transform`的变换是应用在绘制阶段，而并不是应用在布局(layout)阶段，所以无论对子组件应用何种变化，其占用空间的大小和在屏幕上的位置都是固定不变的，因为这些是在布局阶段就确定的。下面我们具体说明：\n\n  ```dart\n   Row(\n    mainAxisAlignment: MainAxisAlignment.center,\n    children: <Widget>[\n      DecoratedBox(\n        decoration:BoxDecoration(color: Colors.red),\n        child: Transform.scale(scale: 1.5,\n            child: Text(\"Hello world\")\n        )\n      ),\n      Text(\"你好\", style: TextStyle(color: Colors.green, fontSize: 18.0),)\n    ],\n  )\n  ```\n\n  运行效果如图5-14所示：\n\n  ![图5-14](../imgs/5-14.png)\n\n  由于第一个`Text`应用变换(放大)后，其在绘制时会放大，但其占用的空间依然为红色部分，所以第二个`Text`会紧挨着红色部分，最终就会出现文字重合。\n\n- 由于矩阵变化只会作用在绘制阶段，所以在某些场景下，在UI需要变化时，可以直接通过矩阵变化来达到视觉上的UI改变，而不需要去重新触发build流程，这样会节省layout的开销，所以性能会比较好。如之前介绍的`Flow`组件，它内部就是用矩阵变换来更新UI，除此之外，Flutter的动画组件中也大量使用了`Transform`以提高性能。\n\n> 思考题：使用`Transform`对其子组件先进行平移然后再旋转和先旋转再平移，两者最终的效果一样吗？为什么？\n\n### RotatedBox\n\n`RotatedBox`和`Transform.rotate`功能相似，它们都可以对子组件进行旋转变换，但是有一点不同：`RotatedBox`的变换是在layout阶段，会影响在子组件的位置和大小。我们将上面介绍`Transform.rotate`时的示例改一下：\n\n```dart\nRow(\n  mainAxisAlignment: MainAxisAlignment.center,\n  children: <Widget>[\n    DecoratedBox(\n      decoration: BoxDecoration(color: Colors.red),\n      //将Transform.rotate换成RotatedBox  \n      child: RotatedBox(\n        quarterTurns: 1, //旋转90度(1/4圈)\n        child: Text(\"Hello world\"),\n      ),\n    ),\n    Text(\"你好\", style: TextStyle(color: Colors.green, fontSize: 18.0),)\n  ],\n),\n```\n\n效果如图5-15所示：\n\n![图5-15](../imgs/5-15.png)\n\n\n\n由于`RotatedBox`是作用于layout阶段，所以子组件会旋转90度（而不只是绘制的内容），`decoration`会作用到子组件所占用的实际空间上，所以最终就是上图的效果，读者可以和前面`Transform.rotate`示例对比理解。\n\n"
  },
  {
    "path": "src/v2/chapter6/custom_scrollview.md",
    "content": "# 6.5 CustomScrollView\n\n`CustomScrollView`是可以使用Sliver来自定义滚动模型（效果）的组件。它可以包含多种滚动模型，举个例子，假设有一个页面，顶部需要一个`GridView`，底部需要一个`ListView`，而要求整个页面的滑动效果是统一的，即它们看起来是一个整体。如果使用`GridView`+`ListView`来实现的话，就不能保证一致的滑动效果，因为它们的滚动效果是分离的，所以这时就需要一个\"胶水\"，把这些彼此独立的可滚动组件\"粘\"起来，而`CustomScrollView`的功能就相当于“胶水”。\n\n### 可滚动组件的Sliver版\n\nSliver在前面讲过，有细片、薄片之意，在Flutter中，Sliver通常指可滚动组件子元素（就像一个个薄片一样）。但是在`CustomScrollView`中，需要粘起来的可滚动组件就是`CustomScrollView`的Sliver了，如果直接将`ListView`、`GridView`作为`CustomScrollView`是不行的，因为它们本身是可滚动组件而并不是Sliver！因此，为了能让可滚动组件能和`CustomScrollView`配合使用，Flutter提供了一些可滚动组件的Sliver版，如SliverList、SliverGrid等。实际上Sliver版的可滚动组件和非Sliver版的可滚动组件最大的区别就是**前者不包含滚动模型（自身不能再滚动），而后者包含滚动模型** ，也正因如此，`CustomScrollView`才可以将多个Sliver\"粘\"在一起，这些Sliver共用`CustomScrollView`的`Scrollable`，所以最终才实现了统一的滑动效果。\n\n> Sliver系列Widget比较多，我们不会一一介绍，读者只需记住它的特点，需要时再去查看文档即可。上面之所以说“大多数”Sliver都和可滚动组件对应，是由于还有一些如SliverPadding、SliverAppBar等是和可滚动组件无关的，它们主要是为了结合CustomScrollView一起使用，这是因为**CustomScrollView的子组件必须都是Sliver**。\n\n### 示例\n\n```dart\nimport 'package:flutter/material.dart';\n\nclass CustomScrollViewTestRoute extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    //因为本路由没有使用Scaffold，为了让子级Widget(如Text)使用\n    //Material Design 默认的样式风格,我们使用Material作为本路由的根。\n    return Material(\n      child: CustomScrollView(\n        slivers: <Widget>[\n          //AppBar，包含一个导航栏\n          SliverAppBar(\n            pinned: true,\n            expandedHeight: 250.0,\n            flexibleSpace: FlexibleSpaceBar(\n              title: const Text('Demo'),\n              background: Image.asset(\n                \"./images/avatar.png\", fit: BoxFit.cover,),\n            ),\n          ),\n\n          SliverPadding(\n            padding: const EdgeInsets.all(8.0),\n            sliver: new SliverGrid( //Grid\n              gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount(\n                crossAxisCount: 2, //Grid按两列显示\n                mainAxisSpacing: 10.0,\n                crossAxisSpacing: 10.0,\n                childAspectRatio: 4.0,\n              ),\n              delegate: new SliverChildBuilderDelegate(\n                    (BuildContext context, int index) {\n                  //创建子widget      \n                  return new Container(\n                    alignment: Alignment.center,\n                    color: Colors.cyan[100 * (index % 9)],\n                    child: new Text('grid item $index'),\n                  );\n                },\n                childCount: 20,\n              ),\n            ),\n          ),\n          //List\n          new SliverFixedExtentList(\n            itemExtent: 50.0,\n            delegate: new SliverChildBuilderDelegate(\n                    (BuildContext context, int index) {\n                  //创建列表项      \n                  return new Container(\n                    alignment: Alignment.center,\n                    color: Colors.lightBlue[100 * (index % 9)],\n                    child: new Text('list item $index'),\n                  );\n                },\n                childCount: 50 //50个列表项\n            ),\n          ),\n        ],\n      ),\n    );\n  }\n}\n```\n\n\n\n代码分为三部分：\n\n- 头部`SliverAppBar`：`SliverAppBar`对应`AppBar`，两者不同之处在于`SliverAppBar`可以集成到`CustomScrollView`。`SliverAppBar`可以结合`FlexibleSpaceBar`实现Material Design中头部伸缩的模型，具体效果，读者可以运行该示例查看。\n- 中间的`SliverGrid`：它用`SliverPadding`包裹以给`SliverGrid`添加补白。`SliverGrid`是一个两列，宽高比为4的网格，它有20个子组件。\n- 底部`SliverFixedExtentList`：它是一个所有子元素高度都为50像素的列表。\n\n运行效果如图：\n\n![图6-12](../imgs/6-12.png)![图6-13](../imgs/6-13.png)\n\n"
  },
  {
    "path": "src/v2/chapter6/gridview.md",
    "content": "# 6.4 GridView\n\n`GridView`可以构建一个二维网格列表，其默认构造函数定义如下：\n\n```dart\nGridView({\n  Axis scrollDirection = Axis.vertical,\n  bool reverse = false,\n  ScrollController controller,\n  bool primary,\n  ScrollPhysics physics,\n  bool shrinkWrap = false,\n  EdgeInsetsGeometry padding,\n  @required SliverGridDelegate gridDelegate, //控制子widget layout的委托\n  bool addAutomaticKeepAlives = true,\n  bool addRepaintBoundaries = true,\n  double cacheExtent,\n  List<Widget> children = const <Widget>[],\n})\n```\n\n我们可以看到，`GridView`和`ListView`的大多数参数都是相同的，它们的含义也都相同的，如有疑惑读者可以翻阅ListView一节，在此不再赘述。我们唯一需要关注的是`gridDelegate`参数，类型是`SliverGridDelegate`，它的作用是控制`GridView`子组件如何排列(layout)。\n\n`SliverGridDelegate`是一个抽象类，定义了`GridView` Layout相关接口，子类需要通过实现它们来实现具体的布局算法。Flutter中提供了两个`SliverGridDelegate`的子类`SliverGridDelegateWithFixedCrossAxisCount`和`SliverGridDelegateWithMaxCrossAxisExtent`，我们可以直接使用，下面我们分别来介绍一下它们。\n\n### SliverGridDelegateWithFixedCrossAxisCount\n\n该子类实现了一个横轴为固定数量子元素的layout算法，其构造函数为：\n\n```dart\nSliverGridDelegateWithFixedCrossAxisCount({\n  @required double crossAxisCount, \n  double mainAxisSpacing = 0.0,\n  double crossAxisSpacing = 0.0,\n  double childAspectRatio = 1.0,\n})\n```\n\n- `crossAxisCount`：横轴子元素的数量。此属性值确定后子元素在横轴的长度就确定了，即ViewPort横轴长度除以`crossAxisCount`的商。\n- `mainAxisSpacing`：主轴方向的间距。\n- `crossAxisSpacing`：横轴方向子元素的间距。\n- `childAspectRatio`：子元素在横轴长度和主轴长度的比例。由于`crossAxisCount`指定后，子元素横轴长度就确定了，然后通过此参数值就可以确定子元素在主轴的长度。\n\n可以发现，子元素的大小是通过`crossAxisCount`和`childAspectRatio`两个参数共同决定的。注意，这里的子元素指的是子组件的最大显示空间，注意确保子组件的实际大小不要超出子元素的空间。\n\n下面看一个例子：\n\n```dart\nGridView(\n  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(\n      crossAxisCount: 3, //横轴三个子widget\n      childAspectRatio: 1.0 //宽高比为1时，子widget\n  ),\n  children:<Widget>[\n    Icon(Icons.ac_unit),\n    Icon(Icons.airport_shuttle),\n    Icon(Icons.all_inclusive),\n    Icon(Icons.beach_access),\n    Icon(Icons.cake),\n    Icon(Icons.free_breakfast)\n  ]\n);\n```\n\n![图6-9](../imgs/6-9.png)\n\n\n\n#### GridView.count\n\n`GridView.count`构造函数内部使用了`SliverGridDelegateWithFixedCrossAxisCount`，我们通过它可以快速的创建横轴固定数量子元素的`GridView`，上面的示例代码等价于：\n\n```dart\nGridView.count( \n  crossAxisCount: 3,\n  childAspectRatio: 1.0,\n  children: <Widget>[\n    Icon(Icons.ac_unit),\n    Icon(Icons.airport_shuttle),\n    Icon(Icons.all_inclusive),\n    Icon(Icons.beach_access),\n    Icon(Icons.cake),\n    Icon(Icons.free_breakfast),\n  ],\n);\n```\n\n\n\n### SliverGridDelegateWithMaxCrossAxisExtent\n\n该子类实现了一个横轴子元素为固定最大长度的layout算法，其构造函数为：\n\n```dart\nSliverGridDelegateWithMaxCrossAxisExtent({\n  double maxCrossAxisExtent,\n  double mainAxisSpacing = 0.0,\n  double crossAxisSpacing = 0.0,\n  double childAspectRatio = 1.0,\n})\n```\n\n`maxCrossAxisExtent`为子元素在横轴上的最大长度，之所以是“最大”长度，是**因为横轴方向每个子元素的长度仍然是等分的**，举个例子，如果ViewPort的横轴长度是450，那么当`maxCrossAxisExtent`的值在区间[450/4，450/3)内的话，子元素最终实际长度都为112.5，而`childAspectRatio`所指的子元素横轴和主轴的长度比为**最终的长度比**。其它参数和`SliverGridDelegateWithFixedCrossAxisCount`相同。\n\n下面我们看一个例子：\n\n```dart\nGridView(\n  padding: EdgeInsets.zero,\n  gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(\n      maxCrossAxisExtent: 120.0,\n      childAspectRatio: 2.0 //宽高比为2\n  ),\n  children: <Widget>[\n    Icon(Icons.ac_unit),\n    Icon(Icons.airport_shuttle),\n    Icon(Icons.all_inclusive),\n    Icon(Icons.beach_access),\n    Icon(Icons.cake),\n    Icon(Icons.free_breakfast),\n  ],\n);\n```\n\n![图6-10](../imgs/6-10.png)\n\n#### GridView.extent\n\nGridView.extent构造函数内部使用了SliverGridDelegateWithMaxCrossAxisExtent，我们通过它可以快速的创建纵轴子元素为固定最大长度的的GridView，上面的示例代码等价于：\n\n```dart\nGridView.extent(\n   maxCrossAxisExtent: 120.0,\n   childAspectRatio: 2.0,\n   children: <Widget>[\n     Icon(Icons.ac_unit),\n     Icon(Icons.airport_shuttle),\n     Icon(Icons.all_inclusive),\n     Icon(Icons.beach_access),\n     Icon(Icons.cake),\n     Icon(Icons.free_breakfast),\n   ],\n );\n```\n\n\n\n### GridView.builder\n\n上面我们介绍的GridView都需要一个widget数组作为其子元素，这些方式都会提前将所有子widget都构建好，所以只适用于子widget数量比较少时，当子widget比较多时，我们可以通过`GridView.builder`来动态创建子widget。`GridView.builder` 必须指定的参数有两个：\n\n```dart\nGridView.builder(\n ...\n @required SliverGridDelegate gridDelegate, \n @required IndexedWidgetBuilder itemBuilder,\n)\n```\n\n其中`itemBuilder`为子widget构建器。\n\n#### 示例\n\n假设我们需要从一个异步数据源（如网络）分批获取一些`Icon`，然后用`GridView`来展示：\n\n```dart\nclass InfiniteGridView extends StatefulWidget {\n  @override\n  _InfiniteGridViewState createState() => new _InfiniteGridViewState();\n}\n\nclass _InfiniteGridViewState extends State<InfiniteGridView> {\n\n  List<IconData> _icons = []; //保存Icon数据\n\n  @override\n  void initState() {\n    // 初始化数据  \n    _retrieveIcons();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return GridView.builder(\n        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(\n            crossAxisCount: 3, //每行三列\n            childAspectRatio: 1.0 //显示区域宽高相等\n        ),\n        itemCount: _icons.length,\n        itemBuilder: (context, index) {\n          //如果显示到最后一个并且Icon总数小于200时继续获取数据\n          if (index == _icons.length - 1 && _icons.length < 200) {\n            _retrieveIcons();\n          }\n          return Icon(_icons[index]);\n        }\n    );\n  }\n\n  //模拟异步获取数据\n  void _retrieveIcons() {\n    Future.delayed(Duration(milliseconds: 200)).then((e) {\n      setState(() {\n        _icons.addAll([\n          Icons.ac_unit,\n          Icons.airport_shuttle,\n          Icons.all_inclusive,\n          Icons.beach_access, Icons.cake,\n          Icons.free_breakfast\n        ]);\n      });\n    });\n  }\n}\n```\n\n\n\n- `_retrieveIcons()`：在此方法中我们通过`Future.delayed`来模拟从异步数据源获取数据，每次获取数据需要200毫秒，获取成功后将新数据添加到_icons，然后调用setState重新构建。\n- 在itemBuilder中，如果显示到最后一个时，判断是否需要继续获取数据，然后返回一个Icon。\n\n### 更多\n\nFlutter的`GridView`默认子元素显示空间是相等的，但在实际开发中，你可能会遇到子元素大小不等的情况，如下面这样的布局：\n\n![图6-11](../imgs/6-11.png)\n\nPub上有一个包“flutter_staggered_grid_view” ，它实现了一个交错GridView的布局模型，可以很轻松的实现这种布局，详情读者可以自行了解。\n\n"
  },
  {
    "path": "src/v2/chapter6/index.md",
    "content": "# 本章目录\n\n* [6.1：可滚动组件简介](intro.md)\n* [6.2：SingleChildScrollView](single_child_scrollview.md)\n* [6.3：ListView](listview.md)\n* [6.4：GridView](gridview.md)      \n* [6.5：CustomScrollView](custom_scrollview.md) \n* [6.6：滚动监听及控制（ScrollController）](scroll_controller.md) \n"
  },
  {
    "path": "src/v2/chapter6/intro.md",
    "content": "# 6.1 可滚动组件简介\n\n当组件内容超过当前显示视口(ViewPort)时，如果没有特殊处理，Flutter则会提示Overflow错误。为此，Flutter提供了多种可滚动组件（Scrollable Widget）用于显示列表和长布局。在本章中，我们先介绍一下常用的可滚动组件（如`ListView`、`GridView`等），然后介绍一下`ScrollController`。可滚动组件都直接或间接包含一个`Scrollable`组件，因此它们包括一些共同的属性，为了避免重复介绍，我们在此统一介绍一下：\n\n```dart\nScrollable({\n  ...\n  this.axisDirection = AxisDirection.down,\n  this.controller,\n  this.physics,\n  @required this.viewportBuilder, //后面介绍\n})\n```\n\n- `axisDirection`滚动方向。\n- `physics`：此属性接受一个`ScrollPhysics`类型的对象，它决定可滚动组件如何响应用户操作，比如用户滑动完抬起手指后，继续执行动画；或者滑动到边界时，如何显示。默认情况下，Flutter会根据具体平台分别使用不同的`ScrollPhysics`对象，应用不同的显示效果，如当滑动到边界时，继续拖动的话，在iOS上会出现弹性效果，而在Android上会出现微光效果。如果你想在所有平台下使用同一种效果，可以显式指定一个固定的`ScrollPhysics`，Flutter SDK中包含了两个`ScrollPhysics`的子类，他们可以直接使用：\n  - `ClampingScrollPhysics`：Android下微光效果。\n  - `BouncingScrollPhysics`：iOS下弹性效果。\n- `controller`：此属性接受一个`ScrollController`对象。`ScrollController`的主要作用是控制滚动位置和监听滚动事件。默认情况下，Widget树中会有一个默认的`PrimaryScrollController`，如果子树中的可滚动组件没有显式的指定`controller`，并且`primary`属性值为`true`时（默认就为`true`），可滚动组件会使用这个默认的`PrimaryScrollController`。这种机制带来的好处是父组件可以控制子树中可滚动组件的滚动行为，例如，`Scaffold`正是使用这种机制在iOS中实现了点击导航栏回到顶部的功能。我们将在本章后面“滚动控制”一节详细介绍`ScrollController`。\n\n### Scrollbar\n\n`Scrollbar`是一个Material风格的滚动指示器（滚动条），如果要给可滚动组件添加滚动条，只需将`Scrollbar`作为可滚动组件的任意一个父级组件即可，如：\n\n```dart\nScrollbar(\n  child: SingleChildScrollView(\n    ...\n  ),\n);\n```\n\n`Scrollbar`和`CupertinoScrollbar`都是通过监听滚动通知来确定滚动条位置的。关于的滚动通知的详细内容我们将在本章最后一节中专门介绍。\n\n#### CupertinoScrollbar\n\n`CupertinoScrollbar`是iOS风格的滚动条，如果你使用的是`Scrollbar`，那么在iOS平台它会自动切换为`CupertinoScrollbar`。\n\n### ViewPort视口\n\n在很多布局系统中都有ViewPort的概念，在Flutter中，术语ViewPort（视口），如无特别说明，则是指一个Widget的实际显示区域。例如，一个`ListView`的显示区域高度是800像素，虽然其列表项总高度可能远远超过800像素，但是其ViewPort仍然是800像素。\n\n### 基于Sliver的延迟构建\n\n通常可滚动组件的子组件可能会非常多、占用的总高度也会非常大；如果要一次性将子组件全部构建出将会非常昂贵！为此，Flutter中提出一个Sliver（中文为“薄片”的意思）概念，如果一个可滚动组件支持Sliver模型，那么该滚动可以将子组件分成好多个“薄片”（Sliver），只有当Sliver出现在视口中时才会去构建它，这种模型也称为“基于Sliver的延迟构建模型”。可滚动组件中有很多都支持基于Sliver的延迟构建模型，如`ListView`、`GridView`，但是也有不支持该模型的，如`SingleChildScrollView`。\n\n### 主轴和纵轴\n\n在可滚动组件的坐标描述中，通常将滚动方向称为主轴，非滚动方向称为纵轴。由于可滚动组件的默认方向一般都是沿垂直方向，所以默认情况下主轴就是指垂直方向，水平方向同理。\n\n"
  },
  {
    "path": "src/v2/chapter6/listview.md",
    "content": "# 6.3 ListView\n\n`ListView`是最常用的可滚动组件之一，它可以沿一个方向线性排布所有子组件，并且它也支持基于Sliver的延迟构建模型。我们看看ListView的默认构造函数定义：\n\n```dart\nListView({\n  ...  \n  //可滚动widget公共参数\n  Axis scrollDirection = Axis.vertical,\n  bool reverse = false,\n  ScrollController controller,\n  bool primary,\n  ScrollPhysics physics,\n  EdgeInsetsGeometry padding,\n  \n  //ListView各个构造函数的共同参数  \n  double itemExtent,\n  bool shrinkWrap = false,\n  bool addAutomaticKeepAlives = true,\n  bool addRepaintBoundaries = true,\n  double cacheExtent,\n    \n  //子widget列表\n  List<Widget> children = const <Widget>[],\n})\n```\n\n上面参数分为两组：第一组是可滚动组件的公共参数，本章第一节中已经介绍过，不再赘述；第二组是`ListView`各个构造函数（`ListView`有多个构造函数）的共同参数，我们重点来看看这些参数，：\n\n- `itemExtent`：该参数如果不为`null`，则会强制`children`的“长度”为`itemExtent`的值；这里的“长度”是指滚动方向上子组件的长度，也就是说如果滚动方向是垂直方向，则`itemExtent`代表子组件的高度；如果滚动方向为水平方向，则`itemExtent`就代表子组件的宽度。在`ListView`中，指定`itemExtent`比让子组件自己决定自身长度会更高效，这是因为指定`itemExtent`后，滚动系统可以提前知道列表的长度，而无需每次构建子组件时都去再计算一下，尤其是在滚动位置频繁变化时（滚动系统需要频繁去计算列表高度）。\n- `shrinkWrap`：该属性表示是否根据子组件的总长度来设置`ListView`的长度，默认值为`false` 。默认情况下，`ListView`的会在滚动方向尽可能多的占用空间。当`ListView`在一个无边界(滚动方向上)的容器中时，`shrinkWrap`必须为`true`。\n- `addAutomaticKeepAlives`：该属性表示是否将列表项（子组件）包裹在`AutomaticKeepAlive` 组件中；典型地，在一个懒加载列表中，如果将列表项包裹在`AutomaticKeepAlive`中，在该列表项滑出视口时它也不会被GC（垃圾回收），它会使用`KeepAliveNotification`来保存其状态。如果列表项自己维护其`KeepAlive`状态，那么此参数必须置为`false`。\n- `addRepaintBoundaries`：该属性表示是否将列表项（子组件）包裹在`RepaintBoundary`组件中。当可滚动组件滚动时，将列表项包裹在`RepaintBoundary`中可以避免列表项重绘，但是当列表项重绘的开销非常小（如一个颜色块，或者一个较短的文本）时，不添加`RepaintBoundary`反而会更高效。和`addAutomaticKeepAlive`一样，如果列表项自己维护其`KeepAlive`状态，那么此参数必须置为`false`。\n\n> 注意：上面这些参数并非`ListView`特有，在本章后面介绍的其它可滚动组件也可能会拥有这些参数，它们的含义是相同的。\n\n### 默认构造函数\n\n默认构造函数有一个`children`参数，它接受一个Widget列表（List<Widget>）。这种方式适合只有少量的子组件的情况，因为这种方式需要将所有`children`都提前创建好（这需要做大量工作），而不是等到子widget真正显示的时候再创建，也就是说通过默认构造函数构建的ListView没有应用基于Sliver的懒加载模型。实际上通过此方式创建的`ListView`和使用`SingleChildScrollView`+`Column`的方式没有本质的区别。下面是一个例子：\n\n```dart\nListView(\n  shrinkWrap: true, \n  padding: const EdgeInsets.all(20.0),\n  children: <Widget>[\n    const Text('I\\'m dedicating every day to you'),\n    const Text('Domestic life was never quite my style'),\n    const Text('When you smile, you knock me out, I fall apart'),\n    const Text('And I thought I was so smart'),\n  ],\n);\n```\n\n> 再次强调，可滚动组件通过一个List<Widget>来作为其children属性时，只适用于子组件较少的情况，这是一个通用规律，并非`ListView`自己的特性，像`GridView`也是如此。\n\n### ListView.builder\n\n`ListView.builder`适合列表项比较多（或者无限）的情况，因为只有当子组件真正显示的时候才会被创建，也就说通过该构造函数创建的`ListView`是支持基于Sliver的懒加载模型的。下面看一下`ListView.builder`的核心参数列表：\n\n```dart\nListView.builder({\n  // ListView公共参数已省略  \n  ...\n  @required IndexedWidgetBuilder itemBuilder,\n  int itemCount,\n  ...\n})\n```\n\n- `itemBuilder`：它是列表项的构建器，类型为`IndexedWidgetBuilder`，返回值为一个widget。当列表滚动到具体的`index`位置时，会调用该构建器构建列表项。\n- `itemCount`：列表项的数量，如果为`null`，则为无限列表。\n\n> 可滚动组件的构造函数如果需要一个列表项Builder，那么通过该构造函数构建的可滚动组件通常就是支持基于Sliver的懒加载模型的，反之则不支持，这是个一般规律。我们在后面在介绍可滚动组件的构造函数时将不再专门说明其是否支持基于Sliver的懒加载模型了。\n\n下面看一个例子：\n\n```dart\nListView.builder(\n    itemCount: 100,\n    itemExtent: 50.0, //强制高度为50.0\n    itemBuilder: (BuildContext context, int index) {\n      return ListTile(title: Text(\"$index\"));\n    }\n);\n```\n\n运行效果如图6-2所示：\n\n![图6-2](../imgs/6-2.png)\n\n\n\n### ListView.separated\n\n`ListView.separated`可以在生成的列表项之间添加一个分割组件，它比`ListView.builder`多了一个`separatorBuilder`参数，该参数是一个分割组件生成器。\n\n下面我们看一个例子：奇数行添加一条蓝色下划线，偶数行添加一条绿色下划线。\n\n```dart\nclass ListView3 extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    //下划线widget预定义以供复用。  \n    Widget divider1=Divider(color: Colors.blue,);\n    Widget divider2=Divider(color: Colors.green);\n    return ListView.separated(\n        itemCount: 100,\n        //列表项构造器\n        itemBuilder: (BuildContext context, int index) {\n          return ListTile(title: Text(\"$index\"));\n        },\n        //分割器构造器\n        separatorBuilder: (BuildContext context, int index) {\n          return index%2==0?divider1:divider2;\n        },\n    );\n  }\n}\n```\n\n![图6-3](../imgs/6-3.png)\n\n### 实例：无限加载列表\n\n假设我们要从数据源异步分批拉取一些数据，然后用`ListView`展示，当我们滑动到列表末尾时，判断是否需要再去拉取数据，如果是，则去拉取，拉取过程中在表尾显示一个loading，拉取成功后将数据插入列表；如果不需要再去拉取，则在表尾提示\"没有更多\"。代码如下：\n\n```dart\nclass InfiniteListView extends StatefulWidget {\n  @override\n  _InfiniteListViewState createState() => new _InfiniteListViewState();\n}\n\nclass _InfiniteListViewState extends State<InfiniteListView> {\n  static const loadingTag = \"##loading##\"; //表尾标记\n  var _words = <String>[loadingTag];\n\n  @override\n  void initState() {\n    super.initState();\n    _retrieveData();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return ListView.separated(\n      itemCount: _words.length,\n      itemBuilder: (context, index) {\n        //如果到了表尾\n        if (_words[index] == loadingTag) {\n          //不足100条，继续获取数据\n          if (_words.length - 1 < 100) {\n            //获取数据\n            _retrieveData();\n            //加载时显示loading\n            return Container(\n              padding: const EdgeInsets.all(16.0),\n              alignment: Alignment.center,\n              child: SizedBox(\n                  width: 24.0,\n                  height: 24.0,\n                  child: CircularProgressIndicator(strokeWidth: 2.0)\n              ),\n            );\n          } else {\n            //已经加载了100条数据，不再获取数据。\n            return Container(\n                alignment: Alignment.center,\n                padding: EdgeInsets.all(16.0),\n                child: Text(\"没有更多了\", style: TextStyle(color: Colors.grey),)\n            );\n          }\n        }\n        //显示单词列表项\n        return ListTile(title: Text(_words[index]));\n      },\n      separatorBuilder: (context, index) => Divider(height: .0),\n    );\n  }\n\n  void _retrieveData() {\n    Future.delayed(Duration(seconds: 2)).then((e) {\n      setState(() {\n        //重新构建列表\n\t\t_words.insertAll(_words.length - 1,\n          //每次生成20个单词\n          generateWordPairs().take(20).map((e) => e.asPascalCase).toList()\n      \t);\n      });\n    });\n  }\n\n}\n```\n\n运行后效果如图6-4、6-5所示：\n\n![图6-4](../imgs/6-4.png)![图6-5](../imgs/6-5.png)\n\n代码比较简单，读者可以参照代码中的注释理解，故不再赘述。需要说明的是，`_retrieveData()`的功能是模拟从数据源异步获取数据，我们使用english_words包的`generateWordPairs()`方法每次生成20个单词。\n\n### 添加固定列表头\n\n很多时候我们需要给列表添加一个固定表头，比如我们想实现一个商品列表，需要在列表顶部添加一个“商品列表”标题，期望的效果如图6-6所示：\n\n![图6-6](../imgs/6-6.png)\n\n我们按照之前经验，写出如下代码：\n\n```dart\n@override\nWidget build(BuildContext context) {\n  return Column(children: <Widget>[\n    ListTile(title:Text(\"商品列表\")),\n    ListView.builder(itemBuilder: (BuildContext context, int index) {\n        return ListTile(title: Text(\"$index\"));\n    }),\n  ]);\n}\n```\n\n然后运行，发现并没有出现我们期望的效果，相反触发了一个异常；\n\n```\nError caught by rendering library, thrown during performResize()。\nVertical viewport was given unbounded height ...\n```\n\n从异常信息中我们可以看到是因为`ListView`高度边界无法确定引起，所以解决的办法也很明显，我们需要给`ListView`指定边界，我们通过`SizedBox`指定一个列表高度看看是否生效：\n\n```dart\n... //省略无关代码\nSizedBox(\n    height: 400, //指定列表高度为400\n    child: ListView.builder(itemBuilder: (BuildContext context, int index) {\n        return ListTile(title: Text(\"$index\"));\n    }),\n),\n...\n```\n\n运行效果如图6-7所示：\n\n![图6-7](../imgs/6-7.png)\n\n可以看到，现在没有触发异常并且列表已经显示出来了，但是我们的手机屏幕高度要大于400，所以底部会有一些空白。那如果我们要实现列表铺满除表头以外的屏幕空间应该怎么做？直观的方法是我们去动态计算，用屏幕高度减去状态栏、导航栏、表头的高度即为剩余屏幕高度，代码如下：\n\n```dart\n... //省略无关代码\nSizedBox(\n  //Material设计规范中状态栏、导航栏、ListTile高度分别为24、56、56 \n  height: MediaQuery.of(context).size.height-24-56-56,\n  child: ListView.builder(itemBuilder: (BuildContext context, int index) {\n    return ListTile(title: Text(\"$index\"));\n  }),\n)\n...    \n```\n\n运行效果如下图6-8所示：\n\n![图6-8](../imgs/6-8.png)\n\n可以看到，我们期望的效果实现了，但是这种方法并不优雅，如果页面布局发生变化，比如表头布局调整导致表头高度改变，那么剩余空间的高度就得重新计算。那么有什么方法可以自动拉伸`ListView`以填充屏幕剩余空间的方法吗？当然有！答案就是`Flex`。前面已经介绍过在弹性布局中，可以使用`Expanded`自动拉伸组件大小，并且我们也说过`Column`是继承自`Flex`的，所以我们可以直接使用`Column`+`Expanded`来实现，代码如下：\n\n```dart\n@override\nWidget build(BuildContext context) {\n  return Column(children: <Widget>[\n    ListTile(title:Text(\"商品列表\")),\n    Expanded(\n      child: ListView.builder(itemBuilder: (BuildContext context, int index) {\n        return ListTile(title: Text(\"$index\"));\n      }),\n    ),\n  ]);\n}\n```\n\n运行后，和上图一样，完美实现了！\n\n### 总结\n\n本节主要介绍了`ListView`的一些公共参数以及常用的构造函数。不同的构造函数对应了不同的列表项生成模型，如果需要自定义列表项生成模型，可以通过`ListView.custom`来自定义，它需要实现一个`SliverChildDelegate`用来给ListView生成列表项组件，更多详情请参考API文档。\n\n"
  },
  {
    "path": "src/v2/chapter6/scroll_controller.md",
    "content": "\n# 6.6 滚动监听及控制\n\n在前几节中，我们介绍了Flutter中常用的可滚动组件，也说过可以用`ScrollController`来控制可滚动组件的滚动位置，本节先介绍一下`ScrollController`，然后以`ListView`为例，展示一下`ScrollController`的具体用法。最后，再介绍一下路由切换时如何来保存滚动位置。\n\n## 6.6.1 ScrollController\n\n`ScrollController`构造函数如下：\n\n```dart\nScrollController({\n  double initialScrollOffset = 0.0, //初始滚动位置\n  this.keepScrollOffset = true,//是否保存滚动位置\n  ...\n})\n```\n\n我们介绍一下`ScrollController`常用的属性和方法：\n\n- `offset`：可滚动组件当前的滚动位置。\n- `jumpTo(double offset)`、`animateTo(double offset,...)`：这两个方法用于跳转到指定的位置，它们不同之处在于，后者在跳转时会执行一个动画，而前者不会。\n\n`ScrollController`还有一些属性和方法，我们将在后面原理部分解释。\n\n#### 滚动监听\n\n`ScrollController`间接继承自`Listenable`，我们可以根据`ScrollController`来监听滚动事件，如：\n\n```dart\ncontroller.addListener(()=>print(controller.offset))\n```\n\n### 示例\n\n我们创建一个`ListView`，当滚动位置发生变化时，我们先打印出当前滚动位置，然后判断当前位置是否超过1000像素，如果超过则在屏幕右下角显示一个“返回顶部”的按钮，该按钮点击后可以使ListView恢复到初始位置；如果没有超过1000像素，则隐藏“返回顶部”按钮。代码如下：\n\n```dart\n\nclass ScrollControllerTestRoute extends StatefulWidget {\n  @override\n  ScrollControllerTestRouteState createState() {\n    return new ScrollControllerTestRouteState();\n  }\n}\n\nclass ScrollControllerTestRouteState extends State<ScrollControllerTestRoute> {\n  ScrollController _controller = new ScrollController();\n  bool showToTopBtn = false; //是否显示“返回到顶部”按钮\n\n  @override\n  void initState() {\n    super.initState();\n    //监听滚动事件，打印滚动位置\n    _controller.addListener(() {\n      print(_controller.offset); //打印滚动位置\n      if (_controller.offset < 1000 && showToTopBtn) {\n        setState(() {\n          showToTopBtn = false;\n        });\n      } else if (_controller.offset >= 1000 && showToTopBtn == false) {\n        setState(() {\n          showToTopBtn = true;\n        });\n      }\n    });\n  }\n\n  @override\n  void dispose() {\n    //为了避免内存泄露，需要调用_controller.dispose\n    _controller.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(title: Text(\"滚动控制\")),\n      body: Scrollbar(\n        child: ListView.builder(\n            itemCount: 100,\n            itemExtent: 50.0, //列表项高度固定时，显式指定高度是一个好习惯(性能消耗小)\n            controller: _controller,\n            itemBuilder: (context, index) {\n              return ListTile(title: Text(\"$index\"),);\n            }\n        ),\n      ),\n      floatingActionButton: !showToTopBtn ? null : FloatingActionButton(\n          child: Icon(Icons.arrow_upward),\n          onPressed: () {\n            //返回到顶部时执行动画\n            _controller.animateTo(.0,\n                duration: Duration(milliseconds: 200),\n                curve: Curves.ease\n            );\n          }\n      ),\n    );\n  }\n}\n```\n\n代码说明已经包含在注释里，下面我们看看运行效果：\n\n![图6-14](../imgs/6-14.png)![图6-15](../imgs/6-15.png)\n\n由于列表项高度为50像素，当滑动到第20个列表项后，右下角“返回顶部”按钮会显示，点击该按钮，ListView会在返回顶部的过程中执行一个滚动动画，动画时间是200毫秒，动画曲线是`Curves.ease`，关于动画的详细内容我们将在后面“动画”一章中详细介绍。\n\n### 滚动位置恢复\n\n`PageStorage`是一个用于保存页面(路由)相关数据的组件，它并不会影响子树的UI外观，其实，`PageStorage`是一个功能型组件，它拥有一个存储桶（bucket），子树中的Widget可以通过指定不同的`PageStorageKey`来存储各自的数据或状态。\n\n每次滚动结束，可滚动组件都会将滚动位置`offset`存储到`PageStorage`中，当可滚动组件重新创建时再恢复。如果`ScrollController.keepScrollOffset`为`false`，则滚动位置将不会被存储，可滚动组件重新创建时会使用`ScrollController.initialScrollOffset`；`ScrollController.keepScrollOffset`为`true`时，可滚动组件在**第一次**创建时，会滚动到`initialScrollOffset`处，因为这时还没有存储过滚动位置。在接下来的滚动中就会存储、恢复滚动位置，而`initialScrollOffset`会被忽略。\n\n当一个路由中包含多个可滚动组件时，如果你发现在进行一些跳转或切换操作后，滚动位置不能正确恢复，这时你可以通过显式指定`PageStorageKey`来分别跟踪不同的可滚动组件的位置，如：\n\n```dart\nListView(key: PageStorageKey(1), ... );\n...\nListView(key: PageStorageKey(2), ... );\n```\n\n不同的`PageStorageKey`，需要不同的值，这样才可以为不同可滚动组件保存其滚动位置。\n\n> 注意：一个路由中包含多个可滚动组件时，如果要分别跟踪它们的滚动位置，并非一定就得给他们分别提供`PageStorageKey`。这是因为Scrollable本身是一个StatefulWidget，它的状态中也会保存当前滚动位置，所以，只要可滚动组件本身没有被从树上detach掉，那么其State就不会销毁(dispose)，滚动位置就不会丢失。只有当Widget发生结构变化，导致可滚动组件的State销毁或重新构建时才会丢失状态，这种情况就需要显式指定`PageStorageKey`，通过`PageStorage`来存储滚动位置，一个典型的场景是在使用`TabBarView`时，在Tab发生切换时，Tab页中的可滚动组件的State就会销毁，这时如果想恢复滚动位置就需要指定`PageStorageKey`。\n\n\n\n### ScrollPosition\n\nScrollPosition是用来保存可滚动组件的滚动位置的。一个`ScrollController`对象可以同时被多个可滚动组件使用，`ScrollController`会为每一个可滚动组件创建一个`ScrollPosition`对象，这些`ScrollPosition`保存在`ScrollController`的`positions`属性中（`List<ScrollPosition>`）。`ScrollPosition`是真正保存滑动位置信息的对象，`offset`只是一个便捷属性：\n\n```dart\ndouble get offset => position.pixels;\n```\n\n一个`ScrollController`虽然可以对应多个可滚动组件，但是有一些操作，如读取滚动位置`offset`，则需要一对一！但是我们仍然可以在一对多的情况下，通过其它方法读取滚动位置，举个例子，假设一个`ScrollController`同时被两个可滚动组件使用，那么我们可以通过如下方式分别读取他们的滚动位置：\n\n```dart\n...\ncontroller.positions.elementAt(0).pixels\ncontroller.positions.elementAt(1).pixels\n...    \n```\n\n我们可以通过`controller.positions.length`来确定`controller`被几个可滚动组件使用。\n\n#### ScrollPosition的方法\n\n`ScrollPosition`有两个常用方法：`animateTo()` 和 `jumpTo()`，它们是真正来控制跳转滚动位置的方法，`ScrollController`的这两个同名方法，内部最终都会调用`ScrollPosition`的。\n\n### ScrollController控制原理\n\n我们来介绍一下`ScrollController`的另外三个方法：\n\n```dart\nScrollPosition createScrollPosition(\n    ScrollPhysics physics,\n    ScrollContext context,\n    ScrollPosition oldPosition);\nvoid attach(ScrollPosition position) ;\nvoid detach(ScrollPosition position) ;\n```\n\n当`ScrollController`和可滚动组件关联时，可滚动组件首先会调用`ScrollController`的`createScrollPosition()`方法来创建一个`ScrollPosition`来存储滚动位置信息，接着，可滚动组件会调用`attach()`方法，将创建的`ScrollPosition`添加到`ScrollController`的`positions`属性中，这一步称为“注册位置”，只有注册后`animateTo()` 和 `jumpTo()`才可以被调用。\n\n当可滚动组件销毁时，会调用`ScrollController`的`detach()`方法，将其`ScrollPosition`对象从`ScrollController`的`positions`属性中移除，这一步称为“注销位置”，注销后`animateTo()` 和 `jumpTo()` 将不能再被调用。\n\n需要注意的是，`ScrollController`的`animateTo()` 和 `jumpTo()`内部会调用所有`ScrollPosition`的`animateTo()` 和 `jumpTo()`，以实现所有和该`ScrollController`关联的可滚动组件都滚动到指定的位置。\n\n\n\n## 6.6.2 滚动监听\n\nFlutter Widget树中子Widget可以通过发送通知（Notification）与父(包括祖先)Widget通信。父级组件可以通过`NotificationListener`组件来监听自己关注的通知，这种通信方式类似于Web开发中浏览器的事件冒泡，我们在Flutter中沿用“冒泡”这个术语，关于通知冒泡我们将在后面“事件处理与通知”一章中详细介绍。\n\n可滚动组件在滚动时会发送`ScrollNotification`类型的通知，`ScrollBar`正是通过监听滚动通知来实现的。通过`NotificationListener`监听滚动事件和通过`ScrollController`有两个主要的不同：\n\n1. 通过NotificationListener可以在从可滚动组件到widget树根之间任意位置都能监听。而`ScrollController`只能和具体的可滚动组件关联后才可以。\n2. 收到滚动事件后获得的信息不同；`NotificationListener`在收到滚动事件时，通知中会携带当前滚动位置和ViewPort的一些信息，而`ScrollController`只能获取当前滚动位置。\n\n### 示例\n\n下面，我们监听`ListView`的滚动通知，然后显示当前滚动进度百分比：\n\n```dart\nimport 'package:flutter/material.dart';\n\nclass ScrollNotificationTestRoute extends StatefulWidget {\n  @override\n  _ScrollNotificationTestRouteState createState() =>\n      new _ScrollNotificationTestRouteState();\n}\n\nclass _ScrollNotificationTestRouteState\n    extends State<ScrollNotificationTestRoute> {\n  String _progress = \"0%\"; //保存进度百分比\n\n  @override\n  Widget build(BuildContext context) {\n    return Scrollbar( //进度条\n      // 监听滚动通知\n      child: NotificationListener<ScrollNotification>(\n        onNotification: (ScrollNotification notification) {\n          double progress = notification.metrics.pixels /\n              notification.metrics.maxScrollExtent;\n          //重新构建\n          setState(() {\n            _progress = \"${(progress * 100).toInt()}%\";\n          });\n          print(\"BottomEdge: ${notification.metrics.extentAfter == 0}\");\n          //return true; //放开此行注释后，进度条将失效\n        },\n        child: Stack(\n          alignment: Alignment.center,\n          children: <Widget>[\n            ListView.builder(\n                itemCount: 100,\n                itemExtent: 50.0,\n                itemBuilder: (context, index) {\n                  return ListTile(title: Text(\"$index\"));\n                }\n            ),\n            CircleAvatar(  //显示进度百分比\n              radius: 30.0,\n              child: Text(_progress),\n              backgroundColor: Colors.black54,\n            )\n          ],\n        ),\n      ),\n    );\n  }\n}\n```\n\n运行结果如图6-16所示：\n\n![图6-16](../imgs/6-16.png)\n\n在接收到滚动事件时，参数类型为`ScrollNotification`，它包括一个`metrics`属性，它的类型是`ScrollMetrics`，该属性包含当前ViewPort及滚动位置等信息：\n\n- `pixels`：当前滚动位置。\n- `maxScrollExtent`：最大可滚动长度。\n- `extentBefore`：滑出ViewPort顶部的长度；此示例中相当于顶部滑出屏幕上方的列表长度。\n- `extentInside`：ViewPort内部长度；此示例中屏幕显示的列表部分的长度。\n- `extentAfter`：列表中未滑入ViewPort部分的长度；此示例中列表底部未显示到屏幕范围部分的长度。\n- `atEdge`：是否滑到了可滚动组件的边界（此示例中相当于列表顶或底部）。\n\nScrollMetrics还有一些其它属性，读者可以自行查阅API文档。\n\n"
  },
  {
    "path": "src/v2/chapter6/single_child_scrollview.md",
    "content": "\n# 6.2 SingleChildScrollView\n\n`SingleChildScrollView`类似于Android中的`ScrollView`，它只能接收一个子组件。定义如下：\n\n```dart\nSingleChildScrollView({\n  this.scrollDirection = Axis.vertical, //滚动方向，默认是垂直方向\n  this.reverse = false, \n  this.padding, \n  bool primary, \n  this.physics, \n  this.controller,\n  this.child,\n})\n```\n\n除了上一节我们介绍过的可滚动组件的通用属性外，我们重点看一下`reverse`和`primary`两个属性：\n\n- `reverse`：该属性API文档解释是：是否按照阅读方向相反的方向滑动，如：`scrollDirection`值为`Axis.horizontal`，如果阅读方向是从左到右(取决于语言环境，阿拉伯语就是从右到左)。`reverse`为`true`时，那么滑动方向就是从右往左。其实此属性本质上是决定可滚动组件的初始滚动位置是在“头”还是“尾”，取`false`时，初始滚动位置在“头”，反之则在“尾”，读者可以自己试验。\n- `primary`：指是否使用widget树中默认的`PrimaryScrollController`；当滑动方向为垂直方向（`scrollDirection`值为`Axis.vertical`）并且没有指定`controller`时，`primary`默认为`true`.\n\n需要注意的是，通常`SingleChildScrollView`只应在期望的内容不会超过屏幕太多时使用，这是因为`SingleChildScrollView`不支持基于Sliver的延迟实例化模型，所以如果预计视口可能包含超出屏幕尺寸太多的内容时，那么使用`SingleChildScrollView`将会非常昂贵（性能差），此时应该使用一些支持Sliver延迟加载的可滚动组件，如`ListView`。\n\n### 示例\n\n下面是一个将大写字母A-Z沿垂直方向显示的例子，由于垂直方向空间会超过屏幕视口高度，所以我们使用`SingleChildScrollView`：\n\n```dart\nclass SingleChildScrollViewTestRoute extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    String str = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\";\n    return Scrollbar( // 显示进度条\n      child: SingleChildScrollView(\n        padding: EdgeInsets.all(16.0),\n        child: Center(\n          child: Column( \n            //动态创建一个List<Widget>  \n            children: str.split(\"\") \n                //每一个字母都用一个Text显示,字体为原来的两倍\n                .map((c) => Text(c, textScaleFactor: 2.0,)) \n                .toList(),\n          ),\n        ),\n      ),\n    );\n  }\n}\n```\n\n\n\n运行效果如图6-1所示：\n\n![图6-1](../imgs/6-1.png)\n\n"
  },
  {
    "path": "src/v2/chapter7/dailog.md",
    "content": "# 7.6 对话框详解\n\n本节将详细介绍一下Flutter中对话框的使用方式、实现原理、样式定制及状态管理。\n\n## 7.6.1 使用对话框\n\n对话框本质上也是UI布局，通常一个对话框会包含标题、内容，以及一些操作按钮，为此，Material库中提供了一些现成的对话框组件来用于快速的构建出一个完整的对话框。\n\n### AlertDialog\n\n下面我们主要介绍一下Material库中的`AlertDialog`组件，它的构造函数定义如下：\n\n```dart\nconst AlertDialog({\n  Key key,\n  this.title, //对话框标题组件\n  this.titlePadding, // 标题填充\n  this.titleTextStyle, //标题文本样式\n  this.content, // 对话框内容组件\n  this.contentPadding = const EdgeInsets.fromLTRB(24.0, 20.0, 24.0, 24.0), //内容的填充\n  this.contentTextStyle,// 内容文本样式\n  this.actions, // 对话框操作按钮组\n  this.backgroundColor, // 对话框背景色\n  this.elevation,// 对话框的阴影\n  this.semanticLabel, //对话框语义化标签(用于读屏软件)\n  this.shape, // 对话框外形\n})\n```\n\n参数都比较简单，不在赘述。下面我们看一个例子，假如我们要在删除文件时弹出一个确认对话框，该对话框如图7-10所示：\n\n![图7-10](../imgs/7-10.png)\n\n该对话框样式代码如下：\n\n```dart\nAlertDialog(\n  title: Text(\"提示\"),\n  content: Text(\"您确定要删除当前文件吗?\"),\n  actions: <Widget>[\n    FlatButton(\n      child: Text(\"取消\"),\n      onPressed: () => Navigator.of(context).pop(), //关闭对话框\n    ),\n    FlatButton(\n      child: Text(\"删除\"),\n      onPressed: () {\n        // ... 执行删除操作\n        Navigator.of(context).pop(true); //关闭对话框\n      },\n    ),\n  ],\n);\n```\n\n实现代码很简单，不在赘述。唯一需要注意的是我们是通过`Navigator.of(context).pop(…)`方法来关闭对话框的，这和路由返回的方式是一致的，并且都可以返回一个结果数据。现在，对话框我们已经构建好了，那么如何将它弹出来呢？还有对话框返回的数据应如何被接收呢？这些问题的答案都在`showDialog()`方法中。\n\n`showDialog()`是Material组件库提供的一个用于弹出Material风格对话框的方法，签名如下：\n\n```dart\nFuture<T> showDialog<T>({\n  @required BuildContext context,\n  bool barrierDismissible = true, //点击对话框barrier(遮罩)时是否关闭它\n  WidgetBuilder builder, // 对话框UI的builder\n})\n```\n\n该方法只有两个参数，含义见注释。该方法返回一个`Future`，它正是用于接收对话框的返回值：如果我们是通过点击对话框遮罩关闭的，则`Future`的值为`null`，否则为我们通过`Navigator.of(context).pop(result)`返回的result值，下面我们看一下整个示例：\n\n```dart\n//点击该按钮后弹出对话框\nRaisedButton(\n  child: Text(\"对话框1\"),\n  onPressed: () async {\n    //弹出对话框并等待其关闭\n    bool delete = await showDeleteConfirmDialog1();\n    if (delete == null) {\n      print(\"取消删除\");\n    } else {\n      print(\"已确认删除\");\n      //... 删除文件\n    }\n  },\n),\n\n// 弹出对话框\nFuture<bool> showDeleteConfirmDialog1() {\n  return showDialog<bool>(\n    context: context,\n    builder: (context) {\n      return AlertDialog(\n        title: Text(\"提示\"),\n        content: Text(\"您确定要删除当前文件吗?\"),\n        actions: <Widget>[\n          FlatButton(\n            child: Text(\"取消\"),\n            onPressed: () => Navigator.of(context).pop(), // 关闭对话框\n          ),\n          FlatButton(\n            child: Text(\"删除\"),\n            onPressed: () {\n              //关闭对话框并返回true\n              Navigator.of(context).pop(true);\n            },\n          ),\n        ],\n      );\n    },\n  );\n}\n```\n\n示例运行后，我们点击对话框“取消”按钮或遮罩，控制台就会输出\"取消删除\"，如果点击“删除”按钮，控制台就会输出\"已确认删除\"。\n\n> 注意：如果`AlertDialog`的内容过长，内容将会溢出，这在很多时候可能不是我们期望的，所以如果对话框内容过长时，可以用`SingleChildScrollView`将内容包裹起来。\n\n### SimpleDialog\n\n`SimpleDialog`也是Material组件库提供的对话框，它会展示一个列表，用于列表选择的场景。下面是一个选择APP语言的示例，运行结果如图7-11。\n\n![图7-11](../imgs/7-11.png)\n\n实现代码如下：\n\n```dart\nFuture<void> changeLanguage() async {\n  int i = await showDialog<int>(\n      context: context,\n      builder: (BuildContext context) {\n        return SimpleDialog(\n          title: const Text('请选择语言'),\n          children: <Widget>[\n            SimpleDialogOption(\n              onPressed: () {\n                // 返回1\n                Navigator.pop(context, 1);\n              },\n              child: Padding(\n                padding: const EdgeInsets.symmetric(vertical: 6),\n                child: const Text('中文简体'),\n              ),\n            ),\n            SimpleDialogOption(\n              onPressed: () {\n                // 返回2\n                Navigator.pop(context, 2);\n              },\n              child: Padding(\n                padding: const EdgeInsets.symmetric(vertical: 6),\n                child: const Text('美国英语'),\n              ),\n            ),\n          ],\n        );\n      });\n\n  if (i != null) {\n    print(\"选择了：${i == 1 ? \"中文简体\" : \"美国英语\"}\");\n  }\n}\n```\n\n列表项组件我们使用了`SimpleDialogOption`组件来包装了一下，它相当于一个FlatButton，只不过按钮文案是左对齐的，并且padding较小。上面示例运行后，用户选择一种语言后，控制台就会打印出它。\n\n### Dialog\n\n实际上`AlertDialog`和`SimpleDialog`都使用了`Dialog`类。由于`AlertDialog`和`SimpleDialog`中使用了`IntrinsicWidth`来尝试通过子组件的实际尺寸来调整自身尺寸，这就导致他们的子组件不能是延迟加载模型的组件（如`ListView`、`GridView` 、 `CustomScrollView`等），如下面的代码运行后会报错。\n\n```dart\nAlertDialog(\n  content: ListView(\n    children: ...//省略\n  ),\n);\n```\n\n如果我们就是需要嵌套一个`ListView`应该怎么做？这时，我们可以直接使用`Dialog`类，如：\n\n```dart\nDialog(\n  child: ListView(\n    children: ...//省略\n  ),\n);\n```\n\n下面我们看一个弹出一个有30个列表项的对话框示例，运行效果如图7-12所示：\n\n![图7-12](../imgs/7-12.png)\n\n实现代码如下：\n\n```dart\nFuture<void> showListDialog() async {\n  int index = await showDialog<int>(\n    context: context,\n    builder: (BuildContext context) {\n      var child = Column(\n        children: <Widget>[\n          ListTile(title: Text(\"请选择\")),\n          Expanded(\n              child: ListView.builder(\n            itemCount: 30,\n            itemBuilder: (BuildContext context, int index) {\n              return ListTile(\n                title: Text(\"$index\"),\n                onTap: () => Navigator.of(context).pop(index),\n              );\n            },\n          )),\n        ],\n      );\n      //使用AlertDialog会报错\n      //return AlertDialog(content: child);\n      return Dialog(child: child);\n    },\n  );\n  if (index != null) {\n    print(\"点击了：$index\");\n  }\n}\n```\n\n现在，我们己经介绍完了`AlertDialog`、`SimpleDialog`以及`Dialog`。上面的示例中，我们在调用`showDialog`时，在`builder`中都是构建了这三个对话框组件的一种，可能有些读者会惯性的以为在`builder`中只能返回这三者之一，其实这不是必须的！就拿`Dialog`的示例来举例，我们完全可以用下面的代码来替代`Dialog`：\n\n```dart\n// return Dialog(child: child) \nreturn UnconstrainedBox(\n  constrainedAxis: Axis.vertical,\n  child: ConstrainedBox(\n    constraints: BoxConstraints(maxWidth: 280),\n    child: Material(\n      child: child,\n      type: MaterialType.card,\n    ),\n  ),\n);\n```\n\n上面代码运行后可以实现一样的效果。现在我们总结一下：`AlertDialog`、`SimpleDialog`以及`Dialog`是Material组件库提供的三种对话框，旨在帮助开发者快速构建出符合Material设计规范的对话框，但读者完全可以自定义对话框样式，因此，我们仍然可以实现各种样式的对话框，这样即带来了易用性，又有很强的扩展性。\n\n## 7.6.2 对话框打开动画及遮罩\n\n我们可以把对话框分为内部样式和外部样式两部分。内部样式指对话框中显示的具体内容，这部分内容我们已经在上面介绍过了；外部样式包含对话框遮罩样式、打开动画等，本节主要介绍如何自定义这些外部样式。\n\n> 关于动画相关内容我们将在本书后面章节介绍，下面内容读者可以先了解一下（不必深究），读者可以在学习完动画相关内容后再回头来看。\n\n我们已经介绍过了`showDialog`方法，它是Material组件库中提供的一个打开Material风格对话框的方法。那如何打开一个普通风格的对话框呢（非Material风格）？ Flutter 提供了一个`showGeneralDialog`方法，签名如下：\n\n```dart\nFuture<T> showGeneralDialog<T>({\n  @required BuildContext context,\n  @required RoutePageBuilder pageBuilder, //构建对话框内部UI\n  bool barrierDismissible, //点击遮罩是否关闭对话框\n  String barrierLabel, // 语义化标签(用于读屏软件)\n  Color barrierColor, // 遮罩颜色\n  Duration transitionDuration, // 对话框打开/关闭的动画时长\n  RouteTransitionsBuilder transitionBuilder, // 对话框打开/关闭的动画\n})\n```\n\n实际上，`showDialog`方法正是`showGeneralDialog`的一个封装，定制了Material风格对话框的遮罩颜色和动画。Material风格对话框打开/关闭动画是一个Fade（渐隐渐显）动画，如果我们想使用一个缩放动画就可以通过`transitionBuilder`来自定义。下面我们自己封装一个`showCustomDialog`方法，它定制的对话框动画为缩放动画，并同时制定遮罩颜色为`Colors.black87`：\n\n```dart\nFuture<T> showCustomDialog<T>({\n  @required BuildContext context,\n  bool barrierDismissible = true,\n  WidgetBuilder builder,\n}) {\n  final ThemeData theme = Theme.of(context, shadowThemeOnly: true);\n  return showGeneralDialog(\n    context: context,\n    pageBuilder: (BuildContext buildContext, Animation<double> animation,\n        Animation<double> secondaryAnimation) {\n      final Widget pageChild = Builder(builder: builder);\n      return SafeArea(\n        child: Builder(builder: (BuildContext context) {\n          return theme != null\n              ? Theme(data: theme, child: pageChild)\n              : pageChild;\n        }),\n      );\n    },\n    barrierDismissible: barrierDismissible,\n    barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,\n    barrierColor: Colors.black87, // 自定义遮罩颜色\n    transitionDuration: const Duration(milliseconds: 150),\n    transitionBuilder: _buildMaterialDialogTransitions,\n  );\n}\n\nWidget _buildMaterialDialogTransitions(\n    BuildContext context,\n    Animation<double> animation,\n    Animation<double> secondaryAnimation,\n    Widget child) {\n  // 使用缩放动画\n  return ScaleTransition(\n    scale: CurvedAnimation(\n      parent: animation,\n      curve: Curves.easeOut,\n    ),\n    child: child,\n  );\n}\n```\n\n现在，我们使用`showCustomDialog`打开文件删除确认对话框，代码如下：\n\n```dart\n... //省略无关代码\nshowCustomDialog<bool>(\n  context: context,\n  builder: (context) {\n    return AlertDialog(\n      title: Text(\"提示\"),\n      content: Text(\"您确定要删除当前文件吗?\"),\n      actions: <Widget>[\n        FlatButton(\n          child: Text(\"取消\"),\n          onPressed: () => Navigator.of(context).pop(),\n        ),\n        FlatButton(\n          child: Text(\"删除\"),\n          onPressed: () {\n            // 执行删除操作\n            Navigator.of(context).pop(true);\n          },\n        ),\n      ],\n    );\n  },\n);\n```\n\n运行效果如图7-13所示：\n\n![图7-13](../imgs/7-13.png)\n\n可以发现，遮罩颜色比通过`showDialog`方法打开的对话框更深。另外对话框打开/关闭的动画已变为缩放动画了，读者可以亲自运行示例查看效果。\n\n## 7.6.3 对话框实现原理\n\n我们已经知道对话框最终都是由`showGeneralDialog`方法打开的，我们来看看它的具体实现：\n\n```dart\nFuture<T> showGeneralDialog<T>({\n  @required BuildContext context,\n  @required RoutePageBuilder pageBuilder,\n  bool barrierDismissible,\n  String barrierLabel,\n  Color barrierColor,\n  Duration transitionDuration,\n  RouteTransitionsBuilder transitionBuilder,\n}) {\n  return Navigator.of(context, rootNavigator: true).push<T>(_DialogRoute<T>(\n    pageBuilder: pageBuilder,\n    barrierDismissible: barrierDismissible,\n    barrierLabel: barrierLabel,\n    barrierColor: barrierColor,\n    transitionDuration: transitionDuration,\n    transitionBuilder: transitionBuilder,\n  ));\n}\n```\n\n实现很简单，直接调用`Navigator`的`push`方法打开了一个新的对话框路由`_DialogRoute`，然后返回了`push`的返回值。可见对话框实际上正是通过路由的形式实现的，这也是为什么我们可以使用`Navigator`的`pop` 方法来退出对话框的原因。关于对话框的样式定制在`_DialogRoute`中，没有什么新的东西，读者可以自行查看。\n\n## 7.6.4 对话框状态管理\n\n我们在用户选择删除一个文件时，会询问是否删除此文件；在用户选择一个文件夹是，应该再让用户确认是否删除子文件夹。为了在用户选择了文件夹时避免二次弹窗确认是否删除子目录，我们在确认对话框底部添加一个“同时删除子目录？”的复选框，如图7-14所示：\n\n![图7-14](../imgs/7-14.png)\n\n\n\n现在就有一个问题：如何管理复选框的选中状态？习惯上，我们会在路由页的State中来管理选中状态，我们可能会写出如下这样的代码：\n\n```dart\nclass _DialogRouteState extends State<DialogRoute> {\n  bool withTree = false; // 复选框选中状态\n\n  @override\n  Widget build(BuildContext context) {\n    return Column(\n      children: <Widget>[\n        RaisedButton(\n          child: Text(\"对话框2\"),\n          onPressed: () async {\n            bool delete = await showDeleteConfirmDialog2();\n            if (delete == null) {\n              print(\"取消删除\");\n            } else {\n              print(\"同时删除子目录: $delete\");\n            }\n          },\n        ),\n      ],\n    );\n  }\n\n  Future<bool> showDeleteConfirmDialog2() {\n    withTree = false; // 默认复选框不选中\n    return showDialog<bool>(\n      context: context,\n      builder: (context) {\n        return AlertDialog(\n          title: Text(\"提示\"),\n          content: Column(\n            crossAxisAlignment: CrossAxisAlignment.start,\n            mainAxisSize: MainAxisSize.min,\n            children: <Widget>[\n              Text(\"您确定要删除当前文件吗?\"),\n              Row(\n                children: <Widget>[\n                  Text(\"同时删除子目录？\"),\n                  Checkbox(\n                    value: withTree,\n                    onChanged: (bool value) {\n                      //复选框选中状态发生变化时重新构建UI\n                      setState(() {\n                        //更新复选框状态\n                        withTree = !withTree;\n                      });\n                    },\n                  ),\n                ],\n              ),\n            ],\n          ),\n          actions: <Widget>[\n            FlatButton(\n              child: Text(\"取消\"),\n              onPressed: () => Navigator.of(context).pop(),\n            ),\n            FlatButton(\n              child: Text(\"删除\"),\n              onPressed: () {\n                //执行删除操作\n                Navigator.of(context).pop(withTree);\n              },\n            ),\n          ],\n        );\n      },\n    );\n  }\n}\n```\n\n然后，当我们运行上面的代码时我们会发现复选框根本选不中！为什么会这样呢？其实原因很简单，我们知道`setState`方法只会针对当前context的子树重新build，但是我们的对话框并不是在`_DialogRouteState`的`build` 方法中构建的，而是通过`showDialog`单独构建的，所以在`_DialogRouteState`的context中调用`setState`是无法影响通过`showDialog`构建的UI的。另外，我们可以从另外一个角度来理解这个现象，前面说过对话框也是通过路由的方式来实现的，那么上面的代码实际上就等同于企图在父路由中调用`setState`来让子路由更新，这显然是不行的！简尔言之，根本原因就是context不对。那如何让复选框可点击呢？通常有如下三种方法：\n\n### 单独抽离出StatefulWidget\n\n既然是context不对，那么直接的思路就是将复选框的选中逻辑单独封装成一个`StatefulWidget`，然后在其内部管理复选状态。我们先来看看这种方法，下面是实现代码：\n\n```dart\n// 单独封装一个内部管理选中状态的复选框组件\nclass DialogCheckbox extends StatefulWidget {\n  DialogCheckbox({\n    Key key,\n    this.value,\n    @required this.onChanged,\n  });\n\n  final ValueChanged<bool> onChanged;\n  final bool value;\n\n  @override\n  _DialogCheckboxState createState() => _DialogCheckboxState();\n}\n\nclass _DialogCheckboxState extends State<DialogCheckbox> {\n  bool value;\n\n  @override\n  void initState() {\n    value = widget.value;\n    super.initState();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Checkbox(\n      value: value,\n      onChanged: (v) {\n        //将选中状态通过事件的形式抛出\n        widget.onChanged(v);\n        setState(() {\n          //更新自身选中状态\n          value = v;\n        });\n      },\n    );\n  }\n}\n```\n\n下面是弹出对话框的代码：\n\n```dart\nFuture<bool> showDeleteConfirmDialog3() {\n  bool _withTree = false; //记录复选框是否选中\n  return showDialog<bool>(\n    context: context,\n    builder: (context) {\n      return AlertDialog(\n        title: Text(\"提示\"),\n        content: Column(\n          crossAxisAlignment: CrossAxisAlignment.start,\n          mainAxisSize: MainAxisSize.min,\n          children: <Widget>[\n            Text(\"您确定要删除当前文件吗?\"),\n            Row(\n              children: <Widget>[\n                Text(\"同时删除子目录？\"),\n                DialogCheckbox(\n                  value: _withTree, //默认不选中\n                  onChanged: (bool value) {\n                    //更新选中状态\n                    _withTree = !_withTree;\n                  },\n                ),\n              ],\n            ),\n          ],\n        ),\n        actions: <Widget>[\n          FlatButton(\n            child: Text(\"取消\"),\n            onPressed: () => Navigator.of(context).pop(),\n          ),\n          FlatButton(\n            child: Text(\"删除\"),\n            onPressed: () {\n              // 将选中状态返回\n              Navigator.of(context).pop(_withTree);\n            },\n          ),\n        ],\n      );\n    },\n  );\n}\n```\n\n最后，就是使用：\n\n```dart\nRaisedButton(\n  child: Text(\"话框3（复选框可点击）\"),\n  onPressed: () async {\n    //弹出删除确认对话框，等待用户确认\n    bool deleteTree = await showDeleteConfirmDialog3();\n    if (deleteTree == null) {\n      print(\"取消删除\");\n    } else {\n      print(\"同时删除子目录: $deleteTree\");\n    }\n  },\n),\n```\n\n运行后效果如图7-15所示：\n\n![图7-15](../imgs/7-15.png)\n\n可见复选框能选中了，点击“取消”或“删除”后，控制台就会打印出最终的确认状态。\n\n### 使用StatefulBuilder方法\n\n上面的方法虽然能解决对话框状态更新的问题，但是有一个明显的缺点——对话框上所有可能会改变状态的组件都得单独封装在一个在内部管理状态的`StatefulWidget`中，这样不仅麻烦，而且复用性不大。因此，我们来想想能不能找到一种更简单的方法？上面的方法本质上就是将对话框的状态置于一个`StatefulWidget`的上下文中，由`StatefulWidget`在内部管理，那么我们有没有办法在不需要单独抽离组件的情况下创建一个`StatefulWidget`的上下文呢？想到这里，我们可以从`Builder`组件的实现获得灵感。在前面介绍过`Builder`组件可以获得组件所在位置的真正的Context，那它是怎么实现的呢，我们看看它的源码：\n\n```dart\nclass Builder extends StatelessWidget {\n  const Builder({\n    Key key,\n    @required this.builder,\n  }) : assert(builder != null),\n       super(key: key);\n  final WidgetBuilder builder;\n\n  @override\n  Widget build(BuildContext context) => builder(context);\n}\n```\n\n可以看到，`Builder`实际上只是继承了`StatelessWidget`，然后在`build`方法中获取当前context后将构建方法代理到了`builder`回调，可见，`Builder`实际上是获取了`StatelessWidget` 的上下文（context）。那么我们能否用相同的方法获取`StatefulWidget` 的上下文，并代理其`build`方法呢？下面我们照猫画虎，来封装一个`StatefulBuilder`方法：\n\n```dart\nclass StatefulBuilder extends StatefulWidget {\n  const StatefulBuilder({\n    Key key,\n    @required this.builder,\n  }) : assert(builder != null),\n       super(key: key);\n\n  final StatefulWidgetBuilder builder;\n\n  @override\n  _StatefulBuilderState createState() => _StatefulBuilderState();\n}\n\nclass _StatefulBuilderState extends State<StatefulBuilder> {\n  @override\n  Widget build(BuildContext context) => widget.builder(context, setState);\n}\n```\n\n代码很简单，`StatefulBuilder`获取了`StatefulWidget`的上下文，并代理了其构建过程。下面我们就可以通过`StatefulBuilder`来重构上面的代码了（变动只在`DialogCheckbox`部分）：\n\n```dart\n... //省略无关代码\nRow(\n  children: <Widget>[\n    Text(\"同时删除子目录？\"),\n    //使用StatefulBuilder来构建StatefulWidget上下文\n    StatefulBuilder(\n      builder: (context, _setState) {\n        return Checkbox(\n          value: _withTree, //默认不选中\n          onChanged: (bool value) {\n            //_setState方法实际就是该StatefulWidget的setState方法，\n            //调用后builder方法会重新被调用\n            _setState(() {\n              //更新选中状态\n              _withTree = !_withTree;\n            });\n          },\n        );\n      },\n    ),\n  ],\n),\n```\n\n实际上，这种方法本质上就是子组件通知父组件（StatefulWidget）重新build子组件本身来实现UI更新的，读者可以对比代码理解。实际上`StatefulBuilder`正是Flutter SDK中提供的一个类，它和`Builder`的原理是一样的，在此，提醒读者一定要将`StatefulBuilder`和`Builder`理解透彻，因为它们在Flutter中是非常实用的。\n\n### 精妙的解法\n\n是否还有更简单的解决方案呢？要确认这个问题，我们就得先搞清楚UI是怎么更新的，我们知道在调用`setState`方法后`StatefulWidget`就会重新build，那`setState`方法做了什么呢？我们能不能从中找到方法？顺着这个思路，我们就得看一下`setState`的核心源码：\n\n```dart\nvoid setState(VoidCallback fn) {\n  ... //省略无关代码\n  _element.markNeedsBuild();\n}\n```\n\n可以发现，`setState`中调用了`Element`的`markNeedsBuild()`方法，我们前面说过，Flutter是一个响应式框架，要更新UI只需改变状态后通知框架页面需要重构即可，而`Element`的`markNeedsBuild()`方法正是来实现这个功能的！`markNeedsBuild()`方法会将当前的`Element`对象标记为“dirty”（脏的），在每一个Frame，Flutter都会重新构建被标记为“dirty”`Element`对象。既然如此，我们有没有办法获取到对话框内部UI的`Element`对象，然后将其标示为为“dirty”呢？答案是肯定的！我们可以通过Context来得到`Element`对象，至于`Element`与`Context`的关系我们将会在后面“Flutter核心原理”一章中再深入介绍，现在只需要简单的认为：在组件树中，`context`实际上就是`Element`对象的引用。知道这个后，那么解决的方案就呼之欲出了，我们可以通过如下方式来让复选框可以更新：\n\n```dart\nFuture<bool> showDeleteConfirmDialog4() {\n  bool _withTree = false;\n  return showDialog<bool>(\n    context: context,\n    builder: (context) {\n      return AlertDialog(\n        title: Text(\"提示\"),\n        content: Column(\n          crossAxisAlignment: CrossAxisAlignment.start,\n          mainAxisSize: MainAxisSize.min,\n          children: <Widget>[\n            Text(\"您确定要删除当前文件吗?\"),\n            Row(\n              children: <Widget>[\n                Text(\"同时删除子目录？\"),\n                Checkbox( // 依然使用Checkbox组件\n                  value: _withTree,\n                  onChanged: (bool value) {\n                    // 此时context为对话框UI的根Element，我们 \n                    // 直接将对话框UI对应的Element标记为dirty\n                    (context as Element).markNeedsBuild();\n                    _withTree = !_withTree;\n                  },\n                ),\n              ],\n            ),\n          ],\n        ),\n        actions: <Widget>[\n          FlatButton(\n            child: Text(\"取消\"),\n            onPressed: () => Navigator.of(context).pop(),\n          ),\n          FlatButton(\n            child: Text(\"删除\"),\n            onPressed: () {\n              // 执行删除操作\n              Navigator.of(context).pop(_withTree);\n            },\n          ),\n        ],\n      );\n    },\n  );\n}\n```\n\n上面的代码运行后复选框也可以正常选中。可以看到，我们只用了一行代码便解决了这个问题！当然上面的代码并不是最优，因为我们只需要更新复选框的状态，而此时的`context`我们用的是对话框的根`context`，所以会导致整个对话框UI组件全部rebuild，因此最好的做法是将`context`的“范围”缩小，也就是说只将`Checkbox`的Element标记为`dirty`，优化后的代码为：\n\n```dart\n... //省略无关代码\nRow(\n  children: <Widget>[\n    Text(\"同时删除子目录？\"),\n    // 通过Builder来获得构建Checkbox的`context`，\n    // 这是一种常用的缩小`context`范围的方式\n    Builder(\n      builder: (BuildContext context) {\n        return Checkbox(\n          value: _withTree,\n          onChanged: (bool value) {\n            (context as Element).markNeedsBuild();\n            _withTree = !_withTree;\n          },\n        );\n      },\n    ),\n  ],\n),\n```\n\n## 7.6.5 其它类型的对话框\n\n### 底部菜单列表\n\n`showModalBottomSheet`方法可以弹出一个Material风格的底部菜单列表模态对话框，示例如下：\n\n```dart\n// 弹出底部菜单列表模态对话框\nFuture<int> _showModalBottomSheet() {\n  return showModalBottomSheet<int>(\n    context: context,\n    builder: (BuildContext context) {\n      return ListView.builder(\n        itemCount: 30,\n        itemBuilder: (BuildContext context, int index) {\n          return ListTile(\n            title: Text(\"$index\"),\n            onTap: () => Navigator.of(context).pop(index),\n          );\n        },\n      );\n    },\n  );\n}\n```\n\n点击按钮，弹出该对话框：\n\n```dart\nRaisedButton(\n  child: Text(\"显示底部菜单列表\"),\n  onPressed: () async {\n    int type = await _showModalBottomSheet();\n    print(type);\n  },\n),\n```\n\n运行后效果如图7-16所示：\n\n![图7-16](../imgs/7-16.png)\n\n`showModalBottomSheet`的实现原理和`showGeneralDialog`实现原理相同，都是通过路由的方式来实现的，读者可以查看源码对比。但值得一提的是还有一个`showBottomSheet`方法，该方法会从设备底部向上弹出一个全屏的菜单列表，示例如下：\n\n```dart\n// 返回的是一个controller\nPersistentBottomSheetController<int> _showBottomSheet() {\n  return showBottomSheet<int>(\n    context: context,\n    builder: (BuildContext context) {\n      return ListView.builder(\n        itemCount: 30,\n        itemBuilder: (BuildContext context, int index) {\n          return ListTile(\n            title: Text(\"$index\"),\n            onTap: (){\n              // do something\n              print(\"$index\");\n              Navigator.of(context).pop();\n            },\n          );\n        },\n      );\n    },\n  );\n}\n```\n\n运行效果如图7-17所示：\n\n![图7-17](../imgs/7-17.png)\n\n`PersistentBottomSheetController`中包含了一些控制对话框的方法比如`close`方法可以关闭该对话框，功能比较简单，读者可以自行查看源码。唯一需要注意的是，`showBottomSheet`和我们上面介绍的弹出对话框的方法原理不同：`showBottomSheet`是调用widget树顶部的`Scaffold`组件的`ScaffoldState `的`showBottomSheet`同名方法实现，也就是说要调用`showBottomSheet`方法就必须得保证父级组件中有`Scaffold`。\n\n### Loading框\n\n其实Loading框可以直接通过`showDialog`+`AlertDialog`来自定义：\n\n```dart\nshowLoadingDialog() {\n  showDialog(\n    context: context,\n    barrierDismissible: false, //点击遮罩不关闭对话框\n    builder: (context) {\n      return AlertDialog(\n        content: Column(\n          mainAxisSize: MainAxisSize.min,\n          children: <Widget>[\n            CircularProgressIndicator(),\n            Padding(\n              padding: const EdgeInsets.only(top: 26.0),\n              child: Text(\"正在加载，请稍后...\"),\n            )\n          ],\n        ),\n      );\n    },\n  );\n}\n```\n\n显示效果如图7-18所示：\n\n![图7-18](../imgs/7-18.png)\n\n如果我们嫌Loading框太宽，想自定义对话框宽度，这时只使用`SizedBox`或`ConstrainedBox`是不行的，原因是`showDialog`中已经给对话框设置了宽度限制，根据我们在第五章“尺寸限制类容器”一节中所述，我们可以使用`UnconstrainedBox`先抵消`showDialog`对宽度的限制，然后再使用`SizedBox`指定宽度，代码如下：\n\n```dart\n... //省略无关代码\nUnconstrainedBox(\n  constrainedAxis: Axis.vertical,\n  child: SizedBox(\n    width: 280,\n    child: AlertDialog(\n      content: Column(\n        mainAxisSize: MainAxisSize.min,\n        children: <Widget>[\n          CircularProgressIndicator(value: .8,),\n          Padding(\n            padding: const EdgeInsets.only(top: 26.0),\n            child: Text(\"正在加载，请稍后...\"),\n          )\n        ],\n      ),\n    ),\n  ),\n);\n```\n\n代码运行后，效果如图7-19所示：\n\n![图7-19](../imgs/7-19.png)\n\n### 日历选择\n\n我们先看一下Material风格的日历选择器，如图7-20所示：\n\n![图7-20](../imgs/7-20.png)\n\n实现代码：\n\n```dart\nFuture<DateTime> _showDatePicker1() {\n  var date = DateTime.now();\n  return showDatePicker(\n    context: context,\n    initialDate: date,\n    firstDate: date,\n    lastDate: date.add( //未来30天可选\n      Duration(days: 30),\n    ),\n  );\n}\n```\n\niOS风格的日历选择器需要使用`showCupertinoModalPopup`方法和`CupertinoDatePicker`组件来实现：\n\n```dart\nFuture<DateTime> _showDatePicker2() {\n  var date = DateTime.now();\n  return showCupertinoModalPopup(\n    context: context,\n    builder: (ctx) {\n      return SizedBox(\n        height: 200,\n        child: CupertinoDatePicker(\n          mode: CupertinoDatePickerMode.dateAndTime,\n          minimumDate: date,\n          maximumDate: date.add(\n            Duration(days: 30),\n          ),\n          maximumYear: date.year + 1,\n          onDateTimeChanged: (DateTime value) {\n            print(value);\n          },\n        ),\n      );\n    },\n  );\n}\n```\n\n运行效果如图7-21所示：\n\n![图7-21](../imgs/7-21.png)\n"
  },
  {
    "path": "src/v2/chapter7/futurebuilder_and_streambuilder.md",
    "content": "# 7.5 异步UI更新（FutureBuilder、StreamBuilder）\n\n很多时候我们会依赖一些异步数据来动态更新UI，比如在打开一个页面时我们需要先从互联网上获取数据，在获取数据的过程中我们显示一个加载框，等获取到数据时我们再渲染页面；又比如我们想展示Stream（比如文件流、互联网数据接收流）的进度。当然，通过StatefulWidget我们完全可以实现上述这些功能。但由于在实际开发中依赖异步数据更新UI的这种场景非常常见，因此Flutter专门提供了`FutureBuilder`和`StreamBuilder`两个组件来快速实现这种功能。\n\n## 7.5.1 FutureBuilder\n\n`FutureBuilder`会依赖一个`Future`，它会根据所依赖的`Future`的状态来动态构建自身。我们看一下`FutureBuilder`构造函数：\n\n```dart\nFutureBuilder({\n  this.future,\n  this.initialData,\n  @required this.builder,\n})\n```\n\n- `future`：`FutureBuilder`依赖的`Future`，通常是一个异步耗时任务。\n\n- `initialData`：初始数据，用户设置默认数据。\n\n- `builder`：Widget构建器；该构建器会在`Future`执行的不同阶段被多次调用，构建器签名如下：\n\n  ```dart\n  Function (BuildContext context, AsyncSnapshot snapshot) \n  ```\n\n  `snapshot`会包含当前异步任务的状态信息及结果信息 ，比如我们可以通过`snapshot.connectionState`获取异步任务的状态信息、通过`snapshot.hasError`判断异步任务是否有错误等等，完整的定义读者可以查看`AsyncSnapshot`类定义。\n  \n  另外，`FutureBuilder`的`builder`函数签名和`StreamBuilder`的`builder`是相同的。\n\n### 示例\n\n我们实现一个路由，当该路由打开时我们从网上获取数据，获取数据时弹一个加载框；获取结束时，如果成功则显示获取到的数据，如果失败则显示错误。由于我们还没有介绍在flutter中如何发起网络请求，所以在这里我们不真正去网络请求数据，而是模拟一下这个过程，隔3秒后返回一个字符串：\n\n```dart\nFuture<String> mockNetworkData() async {\n  return Future.delayed(Duration(seconds: 2), () => \"我是从互联网上获取的数据\");\n}\n```\n\n`FutureBuilder`使用代码如下：\n\n```dart\n...\nWidget build(BuildContext context) {\n  return Center(\n    child: FutureBuilder<String>(\n      future: mockNetworkData(),\n      builder: (BuildContext context, AsyncSnapshot snapshot) {\n        // 请求已结束\n        if (snapshot.connectionState == ConnectionState.done) {\n          if (snapshot.hasError) {\n            // 请求失败，显示错误\n            return Text(\"Error: ${snapshot.error}\");\n          } else {\n            // 请求成功，显示数据\n            return Text(\"Contents: ${snapshot.data}\");\n          }\n        } else {\n          // 请求未结束，显示loading\n          return CircularProgressIndicator();\n        }\n      },\n    ),\n  );\n}\n```\n\n运行结果如图7-8、7-9所示：\n\n![图7-8](../imgs/7-8.png)![图7-9](../imgs/7-9.png)\n\n上面代码中我们在`builder`中根据当前异步任务状态`ConnectionState`来返回不同的widget。`ConnectionState`是一个枚举类，定义如下：\n\n```dart\nenum ConnectionState {\n  /// 当前没有异步任务，比如[FutureBuilder]的[future]为null时\n  none,\n\n  /// 异步任务处于等待状态\n  waiting,\n\n  /// Stream处于激活状态（流上已经有数据传递了），对于FutureBuilder没有该状态。\n  active,\n\n  /// 异步任务已经终止.\n  done,\n}\n```\n\n注意，`ConnectionState.active`只在`StreamBuilder`中才会出现。\n\n\n\n## 7.5.2 StreamBuilder\n\n我们知道，在Dart中`Stream` 也是用于接收异步事件数据，和`Future` 不同的是，它可以接收多个异步操作的结果，它常用于会多次读取数据的异步任务场景，如网络内容下载、文件读写等。`StreamBuilder`正是用于配合`Stream`来展示流上事件（数据）变化的UI组件。下面看一下`StreamBuilder`的默认构造函数：\n\n```dart\nStreamBuilder({\n  Key key,\n  this.initialData,\n  Stream<T> stream,\n  @required this.builder,\n}) \n```\n\n可以看到和`FutureBuilder`的构造函数只有一点不同：前者需要一个`future`，而后者需要一个`stream`。\n\n### 示例\n\n我们创建一个计时器的示例：每隔1秒，计数加1。这里，我们使用`Stream`来实现每隔一秒生成一个数字:\n\n```dart\nStream<int> counter() {\n  return Stream.periodic(Duration(seconds: 1), (i) {\n    return i;\n  });\n}\n```\n\n`StreamBuilder`使用代码如下：\n\n```dart\n  \n Widget build(BuildContext context) {\n    return StreamBuilder<int>(\n      stream: counter(), //\n      //initialData: ,// a Stream<int> or null\n      builder: (BuildContext context, AsyncSnapshot<int> snapshot) {\n        if (snapshot.hasError)\n          return Text('Error: ${snapshot.error}');\n        switch (snapshot.connectionState) {\n          case ConnectionState.none:\n            return Text('没有Stream');\n          case ConnectionState.waiting:\n            return Text('等待数据...');\n          case ConnectionState.active:\n            return Text('active: ${snapshot.data}');\n          case ConnectionState.done:\n            return Text('Stream已关闭');\n        }\n        return null; // unreachable\n      },\n    );\n }\n```\n\n读者可以自己运行本示例查看运行结果。注意，本示例只是为了演示`StreamBuilder`的使用，在实战中，凡是UI会依赖多个异步数据而发生变化的场景都可以使用`StreamBuilder`。\n"
  },
  {
    "path": "src/v2/chapter7/index.md",
    "content": "## 功能型Widget简介\n\n功能型Widget指的是不会影响UI布局及外观的Widget，它们通常具有一定的功能，如事件监听、数据存储等，我们之前介绍过的FocusScope（焦点控制）、PageStorage（数据存储）、NotificationListener（事件监听）都属于功能型Widget。由于Widget是Flutter的一等公民，功能型Widget非常多，我们不会去一一介绍，本章中主要介绍几种常用的功能型Widget。\n\n## 本章目录\n\n* [7.1：导航返回拦截（WillPopScope）](willpopscope.md)\n* [7.2：数据共享（InheritedWidget）](inherited_widget.md)\n* [7.3： 跨组件状态共享（Provider）](provider.md)\n* [7.4：颜色和主题（Theme）](theme.md) \n* [7.5：异步UI更新（FutureBuilder、StreamBuilder）](futurebuilder_and_streambuilder.md)\n* [7.6：对话框详解](dailog.md)  \n"
  },
  {
    "path": "src/v2/chapter7/inherited_widget.md",
    "content": "\n\n# 7.2 数据共享（InheritedWidget）\n\n`InheritedWidget`是Flutter中非常重要的一个功能型组件，它提供了一种数据在widget树中从上到下传递、共享的方式，比如我们在应用的根widget中通过`InheritedWidget`共享了一个数据，那么我们便可以在任意子widget中来获取该共享的数据！这个特性在一些需要在widget树中共享数据的场景中非常方便！如Flutter SDK中正是通过InheritedWidget来共享应用主题（`Theme`）和Locale (当前语言环境)信息的。\n\n> `InheritedWidget`和React中的context功能类似，和逐级传递数据相比，它们能实现组件跨级传递数据。`InheritedWidget`的在widget树中数据传递方向是从上到下的，这和通知`Notification`（将在下一章中介绍）的传递方向正好相反。\n\n### didChangeDependencies\n\n在之前介绍`StatefulWidget`时，我们提到`State`对象有一个`didChangeDependencies`回调，它会在“依赖”发生变化时被Flutter Framework调用。而这个“依赖”指的就是子widget是否使用了父widget中`InheritedWidget`的数据！如果使用了，则代表子widget依赖有依赖`InheritedWidget`；如果没有使用则代表没有依赖。这种机制可以使子组件在所依赖的`InheritedWidget`变化时来更新自身！比如当主题、locale(语言)等发生变化时，依赖其的子widget的`didChangeDependencies`方法将会被调用。\n\n下面我们看一下之前“计数器”示例应用程序的`InheritedWidget`版本。需要说明的是，本示例主要是为了演示`InheritedWidget`的功能特性，并不是计数器的推荐实现方式。\n\n首先，我们通过继承`InheritedWidget`，将当前计数器点击次数保存在`ShareDataWidget`的`data`属性中：\n\n```dart\nclass ShareDataWidget extends InheritedWidget {\n  ShareDataWidget({\n    @required this.data,\n    Widget child\n  }) :super(child: child);\n    \n  final int data; //需要在子树中共享的数据，保存点击次数\n    \n  //定义一个便捷方法，方便子树中的widget获取共享数据  \n  static ShareDataWidget of(BuildContext context) {\n    return context.dependOnInheritedWidgetOfExactType<ShareDataWidget>();\n  }\n\n  //该回调决定当data发生变化时，是否通知子树中依赖data的Widget  \n  @override\n  bool updateShouldNotify(ShareDataWidget old) {\n    //如果返回true，则子树中依赖(build函数中有调用)本widget\n    //的子widget的`state.didChangeDependencies`会被调用\n    return old.data != data;\n  }\n}\n```\n\n然后我们实现一个子组件` _TestWidget`，在其`build`方法中引用`ShareDataWidget`中的数据。同时，在其`didChangeDependencies()` 回调中打印日志：\n\n```dart\nclass _TestWidget extends StatefulWidget {\n  @override\n  __TestWidgetState createState() => new __TestWidgetState();\n}\n\nclass __TestWidgetState extends State<_TestWidget> {\n  @override\n  Widget build(BuildContext context) {\n    //使用InheritedWidget中的共享数据\n    return Text(ShareDataWidget\n        .of(context)\n        .data\n        .toString());\n  }\n\n  @override\n  void didChangeDependencies() {\n    super.didChangeDependencies();\n    //父或祖先widget中的InheritedWidget改变(updateShouldNotify返回true)时会被调用。\n    //如果build中没有依赖InheritedWidget，则此回调不会被调用。\n    print(\"Dependencies change\");\n  }\n}\n```\n\n最后，我们创建一个按钮，每点击一次，就将`ShareDataWidget`的值自增：\n\n```dart\nclass InheritedWidgetTestRoute extends StatefulWidget {\n  @override\n  _InheritedWidgetTestRouteState createState() => new _InheritedWidgetTestRouteState();\n}\n\nclass _InheritedWidgetTestRouteState extends State<InheritedWidgetTestRoute> {\n  int count = 0;\n\n  @override\n  Widget build(BuildContext context) {\n    return  Center(\n      child: ShareDataWidget( //使用ShareDataWidget\n        data: count,\n        child: Column(\n          mainAxisAlignment: MainAxisAlignment.center,\n          children: <Widget>[\n            Padding(\n              padding: const EdgeInsets.only(bottom: 20.0),\n              child: _TestWidget(),//子widget中依赖ShareDataWidget\n            ),\n            RaisedButton(\n              child: Text(\"Increment\"),\n              //每点击一次，将count自增，然后重新build,ShareDataWidget的data将被更新  \n              onPressed: () => setState(() => ++count),\n            )\n          ],\n        ),\n      ),\n    );\n  }\n}\n```\n\n运行后界面如图7-1所示：\n\n![图7-1](../imgs/7-1.png)\n\n每点击一次按钮，计数器就会自增，控制台就会打印一句日志：\n\n```\nI/flutter ( 8513): Dependencies change\n```\n\n可见依赖发生变化后，其`didChangeDependencies()`会被调用。但是读者要注意，**如果_TestWidget的build方法中没有使用ShareDataWidget的数据，那么它的`didChangeDependencies()`将不会被调用，因为它并没有依赖ShareDataWidget**。例如，我们将`__TestWidgetState`代码改为下面这样，`didChangeDependencies()`将不会被调用:\n\n```dart\nclass __TestWidgetState extends State<_TestWidget> {\n  @override\n  Widget build(BuildContext context) {\n    // 使用InheritedWidget中的共享数据\n    //    return Text(ShareDataWidget\n    //        .of(context)\n    //        .data\n    //        .toString());\n     return Text(\"text\");\n  }\n\n  @override\n  void didChangeDependencies() {\n    super.didChangeDependencies();\n    // build方法中没有依赖InheritedWidget，此回调不会被调用。\n    print(\"Dependencies change\");\n  }\n}\n```\n\n上面的代码中，我们将`build()`方法中依赖`ShareDataWidget`的代码注释掉了，然后返回一个固定`Text`，这样一来，当点击Increment按钮后，`ShareDataWidget`的`data`虽然发生变化，但由于`__TestWidgetState`并未依赖`ShareDataWidget`，所以`__TestWidgetState`的`didChangeDependencies`方法不会被调用。其实，这个机制很好理解，因为在数据发生变化时只对使用该数据的Widget更新是合理并且性能友好的。\n\n> 思考题：Flutter framework是怎么知道子widget有没有依赖InheritedWidget的？\n\n#### 应该在didChangeDependencies()中做什么？\n\n一般来说，子widget很少会重写此方法，因为在依赖改变后framework也都会调用`build()`方法。但是，如果你需要在依赖改变后执行一些昂贵的操作，比如网络请求，这时最好的方式就是在此方法中执行，这样可以避免每次`build()`都执行这些昂贵操作。\n\n### 深入了解InheritedWidget\n\n现在来思考一下，如果我们只想在`__TestWidgetState`中引用`ShareDataWidget`数据，但却不希望在`ShareDataWidget`发生变化时调用`__TestWidgetState`的`didChangeDependencies()`方法应该怎么办？其实答案很简单，我们只需要将`ShareDataWidget.of()`的实现改一下即可：\n\n```dart\n//定义一个便捷方法，方便子树中的widget获取共享数据\nstatic ShareDataWidget of(BuildContext context) {\n  //return context.dependOnInheritedWidgetOfExactType<ShareDataWidget>();\n  return context.getElementForInheritedWidgetOfExactType<ShareDataWidget>().widget;\n}\n```\n\n唯一的改动就是获取`ShareDataWidget`对象的方式，把`dependOnInheritedWidgetOfExactType()`方法换成了`context.getElementForInheritedWidgetOfExactType<ShareDataWidget>().widget`，那么他们到底有什么区别呢，我们看一下这两个方法的源码（实现代码在`Element`类中，`Context`和`Element`的关系我们将在后面专门介绍）：\n\n```dart \n@override\nInheritedElement getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() {\n  assert(_debugCheckStateIsActiveForAncestorLookup());\n  final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];\n  return ancestor;\n}\n@override\nInheritedWidget dependOnInheritedWidgetOfExactType({ Object aspect }) {\n  assert(_debugCheckStateIsActiveForAncestorLookup());\n  final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];\n  //多出的部分\n  if (ancestor != null) {\n    assert(ancestor is InheritedElement);\n    return dependOnInheritedElement(ancestor, aspect: aspect) as T;\n  }\n  _hadUnsatisfiedDependencies = true;\n  return null;\n}\n```\n\n我们可以看到，`dependOnInheritedWidgetOfExactType()` 比 `getElementForInheritedWidgetOfExactType()`多调了`dependOnInheritedElement`方法，`dependOnInheritedElement`源码如下：\n\n```dart\n  @override\n  InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect }) {\n    assert(ancestor != null);\n    _dependencies ??= HashSet<InheritedElement>();\n    _dependencies.add(ancestor);\n    ancestor.updateDependencies(this, aspect);\n    return ancestor.widget;\n  }\n```\n\n可以看到`dependOnInheritedElement`方法中主要是注册了依赖关系！看到这里也就清晰了，**调用`dependOnInheritedWidgetOfExactType()` 和 `getElementForInheritedWidgetOfExactType()`的区别就是前者会注册依赖关系，而后者不会**，所以在调用`dependOnInheritedWidgetOfExactType()`时，`InheritedWidget`和依赖它的子孙组件关系便完成了注册，之后当`InheritedWidget`发生变化时，就会更新依赖它的子孙组件，也就是会调这些子孙组件的`didChangeDependencies()`方法和`build()`方法。而当调用的是 `getElementForInheritedWidgetOfExactType()`时，由于没有注册依赖关系，所以之后当`InheritedWidget`发生变化时，就不会更新相应的子孙Widget。\n\n注意，如果将上面示例中`ShareDataWidget.of()`方法实现改成调用`getElementForInheritedWidgetOfExactType()`，运行示例后，点击\"Increment\"按钮，会发现`__TestWidgetState `的`didChangeDependencies()`方法确实不会再被调用，但是其`build()`仍然会被调用！造成这个的原因其实是，点击\"Increment\"按钮后，会调用`_InheritedWidgetTestRouteState`的`setState()`方法，此时会重新构建整个页面，由于示例中，`__TestWidget` 并没有任何缓存，所以它也都会被重新构建，所以也会调用`build()`方法。\n\n那么，现在就带来了一个问题：实际上，我们只想更新子树中依赖了`ShareDataWidget`的组件，而现在只要调用`_InheritedWidgetTestRouteState`的`setState()`方法，所有子节点都会被重新build，这很没必要，那么有什么办法可以避免呢？答案是缓存！一个简单的做法就是通过封装一个`StatefulWidget`，将子Widget树缓存起来，具体做法下一节我们将通过实现一个`Provider` Widget 来演示如何缓存，以及如何利用`InheritedWidget` 来实现Flutter全局状态共享。\n\n\n\n\n\n"
  },
  {
    "path": "src/v2/chapter7/provider.md",
    "content": "# 7.3 跨组件状态共享（Provider）\n\n在Flutter开发中，状态管理是一个永恒的话题。一般的原则是：如果状态是组件私有的，则应该由组件自己管理；如果状态要跨组件共享，则该状态应该由各个组件共同的父元素来管理。对于组件私有的状态管理很好理解，但对于跨组件共享的状态，管理的方式就比较多了，如使用全局事件总线EventBus（将在下一章中介绍），它是一个观察者模式的实现，通过它就可以实现跨组件状态同步：状态持有方（发布者）负责更新、发布状态，状态使用方（观察者）监听状态改变事件来执行一些操作。下面我们看一个登陆状态同步的简单示例：\n\n定义事件：\n\n```dart\nenum Event{\n  login,\n  ... //省略其它事件\n}\n```\n\n登录页代码大致如下：\n\n```dart\n// 登录状态改变后发布状态改变事件\nbus.emit(Event.login);\n```\n\n依赖登录状态的页面：\n\n```dart\nvoid onLoginChanged(e){\n  //登录状态变化处理逻辑\n}\n\n@override\nvoid initState() {\n  //订阅登录状态改变事件\n  bus.on(Event.login,onLogin);\n  super.initState();\n}\n\n@override\nvoid dispose() {\n  //取消订阅\n  bus.off(Event.login,onLogin);\n  super.dispose();\n}\n```\n\n我们可以发现，通过观察者模式来实现跨组件状态共享有一些明显的缺点：\n\n1. 必须显式定义各种事件，不好管理\n2. 订阅者必须需显式注册状态改变回调，也必须在组件销毁时手动去解绑回调以避免内存泄露。\n\n在Flutter当中有没有更好的跨组件状态管理方式了呢？答案是肯定的，那怎么做的？我们想想前面介绍的`InheritedWidget`，它的天生特性就是能绑定`InheritedWidget`与依赖它的子孙组件的依赖关系，并且当`InheritedWidget`数据发生变化时，可以自动更新依赖的子孙组件！利用这个特性，我们可以将需要跨组件共享的状态保存在`InheritedWidget`中，然后在子组件中引用`InheritedWidget`即可，Flutter社区著名的Provider包正是基于这个思想实现的一套跨组件状态共享解决方案，接下来我们便详细介绍一下Provider的用法及原理。\n\n## Provider\n\n为了加强读者的理解，我们不直接去看Provider包的源代码，相反，我会带着你根据上面描述的通过`InheritedWidget`实现的思路来一步一步地实现一个最小功能的Provider。\n\n首先，我们需要一个保存需要共享的数据`InheritedWidget`，由于具体业务数据类型不可预期，为了通用性，我们使用泛型，定义一个通用的`InheritedProvider`类，它继承自`InheritedWidget`：\n\n```dart\n// 一个通用的InheritedWidget，保存任需要跨组件共享的状态\nclass InheritedProvider<T> extends InheritedWidget {\n  InheritedProvider({@required this.data, Widget child}) : super(child: child);\n  \n  //共享状态使用泛型\n  final T data;\n  \n  @override\n  bool updateShouldNotify(InheritedProvider<T> old) {\n    //在此简单返回true，则每次更新都会调用依赖其的子孙节点的`didChangeDependencies`。\n    return true;\n  }\n}\n```\n\n数据保存的地方有了，那么接下来我们需要做的就是在数据发生变化的时候来重新构建`InheritedProvider`，那么现在就面临两个问题：\n\n1. 数据发生变化怎么通知？\n2. 谁来重新构建`InheritedProvider`？\n\n第一个问题其实很好解决，我们当然可以使用之前介绍的eventBus来进行事件通知，但是为了更贴近Flutter开发，我们使用Flutter SDK中提供的`ChangeNotifier`类 ，它继承自`Listenable`，也实现了一个Flutter风格的发布者-订阅者模式，`ChangeNotifier`定义大致如下：\n\n```dart\nclass ChangeNotifier implements Listenable {\n  List listeners=[];\n  @override\n  void addListener(VoidCallback listener) {\n     //添加监听器\n     listeners.add(listener);\n  }\n  @override\n  void removeListener(VoidCallback listener) {\n    //移除监听器\n    listeners.remove(listener);\n  }\n  \n  void notifyListeners() {\n    //通知所有监听器，触发监听器回调 \n    listeners.forEach((item)=>item());\n  }\n   \n  ... //省略无关代码\n}\n\n```\n\n我们可以通过调用`addListener()`和`removeListener()`来添加、移除监听器（订阅者）；通过调用`notifyListeners()` 可以触发所有监听器回调。\n\n现在，我们将要共享的状态放到一个Model类中，然后让它继承自`ChangeNotifier`，这样当共享的状态改变时，我们只需要调用`notifyListeners()` 来通知订阅者，然后由订阅者来重新构建`InheritedProvider`，这也是第二个问题的答案！接下来我们便实现这个订阅者类：\n\n```dart\n\nclass ChangeNotifierProvider<T extends ChangeNotifier> extends StatefulWidget {\n  ChangeNotifierProvider({\n    Key key,\n    this.data,\n    this.child,\n  });\n\n  final Widget child;\n  final T data;\n\n  //定义一个便捷方法，方便子树中的widget获取共享数据\n  static T of<T>(BuildContext context) {\n    final type = _typeOf<InheritedProvider<T>>();\n    final provider =  context.dependOnInheritedWidgetOfExactType<InheritedProvider<T>>();\n    return provider.data;\n  }\n\n  @override\n  _ChangeNotifierProviderState<T> createState() => _ChangeNotifierProviderState<T>();\n}\n```\n\n该类继承`StatefulWidget`，然后定义了一个`of()`静态方法供子类方便获取Widget树中的`InheritedProvider`中保存的共享状态(model)，下面我们实现该类对应的`_ChangeNotifierProviderState`类：\n\n```dart\nclass _ChangeNotifierProviderState<T extends ChangeNotifier> extends State<ChangeNotifierProvider<T>> {\n  void update() {\n    //如果数据发生变化（model类调用了notifyListeners），重新构建InheritedProvider\n    setState(() => {});\n  }\n\n  @override\n  void didUpdateWidget(ChangeNotifierProvider<T> oldWidget) {\n    //当Provider更新时，如果新旧数据不\"==\"，则解绑旧数据监听，同时添加新数据监听\n    if (widget.data != oldWidget.data) {\n      oldWidget.data.removeListener(update);\n      widget.data.addListener(update);\n    }\n    super.didUpdateWidget(oldWidget);\n  }\n\n  @override\n  void initState() {\n    // 给model添加监听器\n    widget.data.addListener(update);\n    super.initState();\n  }\n\n  @override\n  void dispose() {\n    // 移除model的监听器\n    widget.data.removeListener(update);\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return InheritedProvider<T>(\n      data: widget.data,\n      child: widget.child,\n    );\n  }\n}\n```\n\n可以看到`_ChangeNotifierProviderState`类的主要作用就是监听到共享状态（model）改变时重新构建Widget树。注意，在`_ChangeNotifierProviderState`类中调用`setState()`方法，`widget.child`始终是同一个，所以执行build时，`InheritedProvider`的child引用的始终是同一个子widget，所以`widget.child`并不会重新`build`，这也就相当于对`child`进行了缓存！当然如果`ChangeNotifierProvider`父级Widget重新build时，则其传入的`child`便有可能会发生变化。\n\n现在我们所需要的各个工具类都已完成，下面我们通过一个购物车的例子来看看怎么使用上面的这些类。\n\n### 购物车示例\n\n我们需要实现一个显示购物车中所有商品总价的功能：\n\n1. 向购物车中添加新商品时总价更新\n\n定义一个`Item`类，用于表示商品信息：\n\n```dart\nclass Item {\n  Item(this.price, this.count);\n  double price; //商品单价\n  int count; // 商品份数\n  //... 省略其它属性\n}\n```\n\n定义一个保存购物车内商品数据的`CartModel`类:\n\n```dart\nclass CartModel extends ChangeNotifier {\n  // 用于保存购物车中商品列表\n  final List<Item> _items = [];\n\n  // 禁止改变购物车里的商品信息\n  UnmodifiableListView<Item> get items => UnmodifiableListView(_items);\n\n  // 购物车中商品的总价\n  double get totalPrice =>\n      _items.fold(0, (value, item) => value + item.count * item.price);\n\n  // 将 [item] 添加到购物车。这是唯一一种能从外部改变购物车的方法。\n  void add(Item item) {\n    _items.add(item);\n    // 通知监听器（订阅者），重新构建InheritedProvider， 更新状态。\n    notifyListeners();\n  }\n}\n```\n\n `CartModel `即要跨组件共享的model类。最后我们构建示例页面：\n\n```dart\nclass ProviderRoute extends StatefulWidget {\n  @override\n  _ProviderRouteState createState() => _ProviderRouteState();\n}\n\nclass _ProviderRouteState extends State<ProviderRoute> {\n  @override\n  Widget build(BuildContext context) {\n    return Center(\n      child: ChangeNotifierProvider<CartModel>(\n        data: CartModel(),\n        child: Builder(builder: (context) {\n          return Column(\n            children: <Widget>[\n              Builder(builder: (context){\n                var cart=ChangeNotifierProvider.of<CartModel>(context);\n                return Text(\"总价: ${cart.totalPrice}\");\n              }),\n              Builder(builder: (context){\n                print(\"RaisedButton build\"); //在后面优化部分会用到\n                return RaisedButton(\n                  child: Text(\"添加商品\"),\n                  onPressed: () {\n                    //给购物车中添加商品，添加后总价会更新\n                    ChangeNotifierProvider.of<CartModel>(context).add(Item(20.0, 1));\n                  },\n                );\n              }),\n            ],\n          );\n        }),\n      ),\n    );\n  }\n}\n```\n\n运行示例后效果如图7-2所示：\n\n![provider](../imgs/7-2.png)\n\n每次点击”添加商品“按钮，总价就会增加20，我们期望的功能实现了！可能有些读者会疑惑，我们饶了一大圈实现这么简单的功能有意义么？其实，就这个例子来看，只是更新同一个路由页中的一个状态，我们使用`ChangeNotifierProvider`的优势并不明显，但是如果我们是做一个购物APP呢？由于购物车数据是通常是会在整个APP中共享的，比如会跨路由共享。如果我们将`ChangeNotifierProvider`放在整个应用的Widget树的根上，那么整个APP就可以共享购物车的数据了，这时`ChangeNotifierProvider`的优势将会非常明显。\n\n虽然上面的例子比较简单，但它却将Provider的原理和流程体现的很清楚，图7-3是Provider的原理图：\n\n![图7-3](../imgs/7-3.png)\n\nModel变化后会自动通知`ChangeNotifierProvider`（订阅者），`ChangeNotifierProvider`内部会重新构建`InheritedWidget`，而依赖该`InheritedWidget`的子孙Widget就会更新。\n\n我们可以发现使用Provider，将会带来如下收益：\n\n1. 我们的业务代码更关注数据了，只要更新Model，则UI会自动更新，而不用在状态改变后再去手动调用`setState()`来显式更新页面。\n2. 数据改变的消息传递被屏蔽了，我们无需手动去处理状态改变事件的发布和订阅了，这一切都被封装在Provider中了。这真的很棒，帮我们省掉了大量的工作！\n3. 在大型复杂应用中，尤其是需要全局共享的状态非常多时，使用Provider将会大大简化我们的代码逻辑，降低出错的概率，提高开发效率。 \n\n### 优化\n\n我们上面实现的`ChangeNotifierProvider`是有两个明显缺点：代码组织问题和性能问题，下面我们一一讨论。\n\n#### 代码组织问题\n\n我们先看一下构建显示总价Text的代码：\n\n```dart\nBuilder(builder: (context){\n  var cart=ChangeNotifierProvider.of<CartModel>(context);\n  return Text(\"总价: ${cart.totalPrice}\");\n})\n```\n\n这段代码有两点可以优化：\n\n1. 需要显式调用`ChangeNotifierProvider.of`，当APP内部依赖`CartModel`很多时，这样的代码将很冗余。\n2. 语义不明确；由于`ChangeNotifierProvider`是订阅者，那么依赖`CartModel`的Widget自然就是订阅者，其实也就是状态的消费者，如果我们用`Builder` 来构建，语义就不是很明确；如果我们能使用一个具有明确语义的Widget，比如就叫`Consumer`，这样最终的代码语义将会很明确，只要看到`Consumer`，我们就知道它是依赖某个跨组件或全局的状态。\n\n为了优化这两个问题，我们可以封装一个`Consumer` Widget，实现如下：\n\n```dart\n// 这是一个便捷类，会获得当前context和指定数据类型的Provider\nclass Consumer<T> extends StatelessWidget {\n  Consumer({\n    Key key,\n    @required this.builder,\n    this.child,\n  })  : assert(builder != null),\n        super(key: key);\n\n  final Widget child;\n\n  final Widget Function(BuildContext context, T value) builder;\n\n  @override\n  Widget build(BuildContext context) {\n    return builder(\n      context,\n      ChangeNotifierProvider.of<T>(context), //自动获取Model\n    );\n  }\n}\n```\n\n`Consumer`实现非常简单，它通过指定模板参数，然后再内部自动调用`ChangeNotifierProvider.of`获取相应的Model，并且`Consumer`这个名字本身也是具有确切语义（消费者）。现在上面的代码块可以优化为如下这样：\n\n```dart\nConsumer<CartModel>(\n  builder: (context, cart)=> Text(\"总价: ${cart.totalPrice}\");\n)\n```\n\n是不是很优雅！\n\n#### 性能问题\n\n上面的代码还有一个性能问题，就在构建”添加按钮“的代码处：\n\n```dart\nBuilder(builder: (context) {\n  print(\"RaisedButton build\"); // 构建时输出日志\n  return RaisedButton(\n    child: Text(\"添加商品\"),\n    onPressed: () {\n      ChangeNotifierProvider.of<CartModel>(context).add(Item(20.0, 1));\n    },\n  );\n}\n```\n\n我们点击”添加商品“按钮后，由于购物车商品总价会变化，所以显示总价的Text更新是符合预期的，但是”添加商品“按钮本身没有变化，是不应该被重新build的。但是我们运行示例，每次点击”添加商品“按钮，控制台都会输出\"RaisedButton build\"日志，也就是说”添加商品“按钮在每次点击时其自身都会重新build！这是为什么呢？如果你已经理解了`InheritedWidget`的更新机制，那么答案一眼就能看出：这是因为构建`RaisedButton`的`Builder`中调用了`ChangeNotifierProvider.of`，也就是说依赖了Widget树上面的`InheritedWidget`（即`InheritedProvider` ）Widget，所以当添加完商品后，`CartModel`发生变化，会通知`ChangeNotifierProvider`, 而`ChangeNotifierProvider`则会重新构建子树，所以`InheritedProvider`将会更新，此时依赖它的子孙Widget就会被重新构建。\n\n问题的原因搞清楚了，那么我们如何避免这不必要重构呢？既然按钮重新被build是因为按钮和`InheritedWidget`建立了依赖关系，那么我们只要打破或解除这种依赖关系就可以了。那么如何解除按钮和`InheritedWidget`的依赖关系呢？我们上一节介绍`InheritedWidget`时已经讲过了：调用`dependOnInheritedWidgetOfExactType()` 和 `getElementForInheritedWidgetOfExactType()`的区别就是前者会注册依赖关系，而后者不会。所以我们只需要将`ChangeNotifierProvider.of`的实现改为下面这样即可：\n\n```dart\n //添加一个listen参数，表示是否建立依赖关系\n  static T of<T>(BuildContext context, {bool listen = true}) {\n    final type = _typeOf<InheritedProvider<T>>();\n    final provider = listen\n        ? context.dependOnInheritedWidgetOfExactType<InheritedProvider<T>>()\n        : context.getElementForInheritedWidgetOfExactType<InheritedProvider<T>>()?.widget\n            as InheritedProvider<T>;\n    return provider.data;\n  }\n```\n\n然后我们将调用部分代码改为：\n\n```dart\nColumn(\n    children: <Widget>[\n      Consumer<CartModel>(\n        builder: (BuildContext context, cart) =>Text(\"总价: ${cart.totalPrice}\"),\n      ),\n      Builder(builder: (context) {\n        print(\"RaisedButton build\");\n        return RaisedButton(\n          child: Text(\"添加商品\"),\n          onPressed: () {\n            // listen 设为false，不建立依赖关系\n            ChangeNotifierProvider.of<CartModel>(context, listen: false)\n                .add(Item(20.0, 1));\n          },\n        );\n      })\n    ],\n  )\n```\n\n修改后再次运行上面的示例，我们会发现点击”添加商品“按钮后，控制台不会再输出\"RaisedButton build\"了，即按钮不会被重新构建了。而总价仍然会更新，这是因为`Consumer`中调用`ChangeNotifierProvider.of`时`listen`值为默认值true，所以还是会建立依赖关系。\n\n至此我们便实现了一个迷你的Provider，它具备Pub上Provider Package中的核心功能；但是我们的迷你版功能并不全面，如只实现了一个可监听的ChangeNotifierProvider，并没有实现只用于数据共享的Provider；另外，我们的实现有些边界也没有考虑的到，比如如何保证在Widget树重新build时Model始终是单例等。所以建议读者在实战中还是使用Provider Package，而本节实现这个迷你Provider的主要目的主要是为了帮助读者了解Provider Package底层的原理。\n\n### 其它状态管理包\n\n现在Flutter社区已经有很多专门用于状态管理的包了，在此我们列出几个相对评分比较高的：\n\n| 包名                                                         | 介绍                                          |\n| ------------------------------------------------------------ | --------------------------------------------- |\n| [Provider](https://pub.flutter-io.cn/packages/provider) & [Scoped Model](https://pub.flutter-io.cn/packages/scoped_model) | 这两个包都是基于`InheritedWidget`的，原理相似 |\n| [Redux](https://pub.flutter-io.cn/packages/flutter_redux)    | 是Web开发中React生态链中Redux包的Flutter实现  |\n| [MobX](https://pub.dev/packages/flutter_mobx)                | 是Web开发中React生态链中MobX包的Flutter实现   |\n| [BLoC](https://pub.dev/packages/flutter_bloc)                | 是BLoC模式的Flutter实现                       |\n\n在此笔者不对这些包做推荐，读者有兴趣都可以研究一下，了解它们各自的思想。\n\n### 总结\n\n本节通过介绍事件总线在跨组件共享中的一些缺点引出了通过`InheritedWidget`来实现状态的共享的思想，然后基于该思想实现了一个简单的Provider，在实现的过程中也更深入的探索了`InheritedWidget`与其依赖项的注册机制和更新机制。通过本节的学习，读者应该达到两个目标，首先是对`InheritedWidget`彻底吃透，其次是Provider的设计思想。\n\n`InheritedWidget`是Flutter中非常重要的一个Widget，像国际化、主题等都是通过它来实现，所以我们也不惜篇幅，通过好几节来介绍它的，在下一节中，我们将介绍另一个基于`InheritedWidget`的组件Theme(主题)。\n\n\n\n\n\n\n\n"
  },
  {
    "path": "src/v2/chapter7/theme.md",
    "content": "\n\n# 7.4 颜色和主题\n\n## 7.4.1 颜色\n\n在介绍主题前我们先了解一些Flutter中的Color类。Color类中颜色以一个int值保存，我们知道显示器颜色是由红、绿、蓝三基色组成，每种颜色占8比特，存储结构如下：\n\n| Bit（位） | 颜色             |\n| --------- | ---------------- |\n| 0-7       | 蓝色             |\n| 8-15      | 绿色             |\n| 16-23     | 红色             |\n| 24-31     | Alpha (不透明度) |\n\n上面表格中的的字段在Color类中都有对应的属性，而Color中的众多方法也就是操作这些属性的，由于大多比较简单，读者可以查看类定义了解。在此我们主要讨论两点：色值转换和亮度。\n\n### **如何将颜色字符串转成Color对象**\n\n如Web开发中的色值通常是一个字符串如\"#dc380d\"，它是一个RGB值，我们可以通过下面这些方法将其转为Color类：\n\n```dart\nColor(0xffdc380d); //如果颜色固定可以直接使用整数值\n//颜色是一个字符串变量\nvar c = \"dc380d\";\nColor(int.parse(c,radix:16)|0xFF000000) //通过位运算符将Alpha设置为FF\nColor(int.parse(c,radix:16)).withAlpha(255)  //通过方法将Alpha设置为FF\n```\n\n### 颜色亮度\n\n假如，我们要实现一个背景颜色和Title可以自定义的导航栏，并且背景色为深色时我们应该让Title显示为浅色；背景色为浅色时，Title显示为深色。要实现这个功能，我们就需要来计算背景色的亮度，然后动态来确定Title的颜色。Color类中提供了一个`computeLuminance()`方法，它可以返回一个[0-1]的一个值，数字越大颜色就越浅，我们可以根据它来动态确定Title的颜色，下面是导航栏NavBar的简单实现：\n\n```dart\nclass NavBar extends StatelessWidget {\n  final String title;\n  final Color color; //背景颜色\n\n  NavBar({\n    Key key,\n    this.color,\n    this.title,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return Container(\n      constraints: BoxConstraints(\n        minHeight: 52,\n        minWidth: double.infinity,\n      ),\n      decoration: BoxDecoration(\n        color: color,\n        boxShadow: [\n          //阴影\n          BoxShadow(\n            color: Colors.black26,\n            offset: Offset(0, 3),\n            blurRadius: 3,\n          ),\n        ],\n      ),\n      child: Text(\n        title,\n        style: TextStyle(\n          fontWeight: FontWeight.bold,\n          //根据背景色亮度来确定Title颜色\n          color: color.computeLuminance() < 0.5 ? Colors.white : Colors.black,\n        ),\n      ),\n      alignment: Alignment.center,\n    );\n  }\n}\n```\n\n测试代码如下：\n\n```dart\nColumn(\n  children: <Widget>[\n    //背景为蓝色，则title自动为白色\n    NavBar(color: Colors.blue, title: \"标题\"), \n    //背景为白色，则title自动为黑色\n    NavBar(color: Colors.white, title: \"标题\"),\n  ]\n)\n```\n\n运行效果如图7-4所示：\n\n![NavBar](../imgs/7-4.png)\n\n### MaterialColor\n\n`MaterialColor`是实现Material Design中的颜色的类，它包含一种颜色的10个级别的渐变色。`MaterialColor`通过\"[]\"运算符的索引值来代表颜色的深度，有效的索引有：50，100，200，…，900，数字越大，颜色越深。`MaterialColor`的默认值为索引等于500的颜色。举个例子，`Colors.blue`是预定义的一个`MaterialColor`类对象，定义如下：\n\n```dart\nstatic const MaterialColor blue = MaterialColor(\n  _bluePrimaryValue,\n  <int, Color>{\n     50: Color(0xFFE3F2FD),\n    100: Color(0xFFBBDEFB),\n    200: Color(0xFF90CAF9),\n    300: Color(0xFF64B5F6),\n    400: Color(0xFF42A5F5),\n    500: Color(_bluePrimaryValue),\n    600: Color(0xFF1E88E5),\n    700: Color(0xFF1976D2),\n    800: Color(0xFF1565C0),\n    900: Color(0xFF0D47A1),\n  },\n);\nstatic const int _bluePrimaryValue = 0xFF2196F3;\n```\n\n`Colors.blue[50]`到`Colors.blue[900]`的色值从浅蓝到深蓝渐变，效果如图7-5所示：\n\n![NavBar](../imgs/7-5.jpeg)\n\n\n\n## 7.4.2 Theme\n\n`Theme`组件可以为Material APP定义主题数据（ThemeData）。Material组件库里很多组件都使用了主题数据，如导航栏颜色、标题字体、Icon样式等。`Theme`内会使用`InheritedWidget`来为其子树共享样式数据。\n\n### ThemeData\n\n`ThemeData`用于保存是Material 组件库的主题数据，Material组件需要遵守相应的设计规范，而这些规范可自定义部分都定义在ThemeData中了，所以我们可以通过ThemeData来自定义应用主题。在子组件中，我们可以通过`Theme.of`方法来获取当前的`ThemeData`。\n\n> 注意：Material Design 设计规范中有些是不能自定义的，如导航栏高度，ThemeData只包含了可自定义部分。\n\n我们看看`ThemeData`部分数据定义：\n\n```dart\nThemeData({\n  Brightness brightness, //深色还是浅色\n  MaterialColor primarySwatch, //主题颜色样本，见下面介绍\n  Color primaryColor, //主色，决定导航栏颜色\n  Color accentColor, //次级色，决定大多数Widget的颜色，如进度条、开关等。\n  Color cardColor, //卡片颜色\n  Color dividerColor, //分割线颜色\n  ButtonThemeData buttonTheme, //按钮主题\n  Color cursorColor, //输入框光标颜色\n  Color dialogBackgroundColor,//对话框背景颜色\n  String fontFamily, //文字字体\n  TextTheme textTheme,// 字体主题，包括标题、body等文字样式\n  IconThemeData iconTheme, // Icon的默认样式\n  TargetPlatform platform, //指定平台，应用特定平台控件风格\n  ...\n})\n```\n\n上面只是`ThemeData`的一小部分属性，完整的数据定义读者可以查看SDK。上面属性中需要说明的是`primarySwatch`，它是主题颜色的一个\"样本色\"，通过这个样本色可以在一些条件下生成一些其它的属性，例如，如果没有指定`primaryColor`，并且当前主题不是深色主题，那么`primaryColor`就会默认为`primarySwatch`指定的颜色，还有一些相似的属性如`accentColor` 、`indicatorColor`等也会受`primarySwatch`影响。\n\n### 示例\n\n我们实现一个路由换肤功能：\n\n```dart\n\nclass ThemeTestRoute extends StatefulWidget {\n  @override\n  _ThemeTestRouteState createState() => new _ThemeTestRouteState();\n}\n\nclass _ThemeTestRouteState extends State<ThemeTestRoute> {\n  Color _themeColor = Colors.teal; //当前路由主题色\n\n  @override\n  Widget build(BuildContext context) {\n    ThemeData themeData = Theme.of(context);\n    return Theme(\n      data: ThemeData(\n          primarySwatch: _themeColor, //用于导航栏、FloatingActionButton的背景色等\n          iconTheme: IconThemeData(color: _themeColor) //用于Icon颜色\n      ),\n      child: Scaffold(\n        appBar: AppBar(title: Text(\"主题测试\")),\n        body: Column(\n          mainAxisAlignment: MainAxisAlignment.center,\n          children: <Widget>[\n            //第一行Icon使用主题中的iconTheme\n            Row(\n                mainAxisAlignment: MainAxisAlignment.center,\n                children: <Widget>[\n                  Icon(Icons.favorite),\n                  Icon(Icons.airport_shuttle),\n                  Text(\"  颜色跟随主题\")\n                ]\n            ),\n            //为第二行Icon自定义颜色（固定为黑色)\n            Theme(\n              data: themeData.copyWith(\n                iconTheme: themeData.iconTheme.copyWith(\n                    color: Colors.black\n                ),\n              ),\n              child: Row(\n                  mainAxisAlignment: MainAxisAlignment.center,\n                  children: <Widget>[\n                    Icon(Icons.favorite),\n                    Icon(Icons.airport_shuttle),\n                    Text(\"  颜色固定黑色\")\n                  ]\n              ),\n            ),\n          ],\n        ),\n        floatingActionButton: FloatingActionButton(\n            onPressed: () =>  //切换主题\n                setState(() =>\n                _themeColor =\n                _themeColor == Colors.teal ? Colors.blue : Colors.teal\n                ),\n            child: Icon(Icons.palette)\n        ),\n      ),\n    );\n  }\n}\n```\n\n运行后点击右下角悬浮按钮则可以切换主题，如图7-6、7-7所示：\n\n![图7-6](../imgs/7-6.png)![图7-7](../imgs/7-7.png)\n\n\n\n需要注意的有三点：\n\n- 可以通过局部主题覆盖全局主题，正如代码中通过Theme为第二行图标指定固定颜色（黑色）一样，这是一种常用的技巧，Flutter中会经常使用这种方法来自定义子树主题。那么为什么局部主题可以覆盖全局主题？这主要是因为widget中使用主题样式时是通过`Theme.of(BuildContext context)`来获取的，我们看看其简化后的代码：\n\n- ```dart \n  static ThemeData of(BuildContext context, { bool shadowThemeOnly = false }) {\n     // 简化代码，并非源码  \n     return context.dependOnInheritedWidgetOfExactType<_InheritedTheme>().theme.data\n  }\n  ```\n\n  `context.dependOnInheritedWidgetOfExactType` 会在widget树中从当前位置向上查找第一个类型为`_InheritedTheme`的widget。所以当局部指定`Theme`后，其子树中通过`Theme.of()`向上查找到的第一个`_InheritedTheme`便是我们指定的`Theme`。\n\n- 本示例是对单个路由换肤，如果想要对整个应用换肤，则可以去修改`MaterialApp`的`theme`属性。\n"
  },
  {
    "path": "src/v2/chapter7/willpopscope.md",
    "content": "# 7.1 导航返回拦截（WillPopScope）\n\n为了避免用户误触返回按钮而导致APP退出，在很多APP中都拦截了用户点击返回键的按钮，然后进行一些防误触判断，比如当用户在某一个时间段内点击两次时，才会认为用户是要退出（而非误触）。Flutter中可以通过`WillPopScope`来实现返回按钮拦截，我们看看`WillPopScope`的默认构造函数：\n\n```dart\nconst WillPopScope({\n  ...\n  @required WillPopCallback onWillPop,\n  @required Widget child\n})\n```\n\n`onWillPop`是一个回调函数，当用户点击返回按钮时被调用（包括导航返回按钮及Android物理返回按钮）。该回调需要返回一个`Future`对象，如果返回的`Future`最终值为`false`时，则当前路由不出栈(不会返回)；最终值为`true`时，当前路由出栈退出。我们需要提供这个回调来决定是否退出。\n\n### 示例\n\n为了防止用户误触返回键退出，我们拦截返回事件。当用户在1秒内点击两次返回按钮时，则退出；如果间隔超过1秒则不退出，并重新记时。代码如下：\n\n```dart\nimport 'package:flutter/material.dart';\n\nclass WillPopScopeTestRoute extends StatefulWidget {\n  @override\n  WillPopScopeTestRouteState createState() {\n    return new WillPopScopeTestRouteState();\n  }\n}\n\nclass WillPopScopeTestRouteState extends State<WillPopScopeTestRoute> {\n  DateTime _lastPressedAt; //上次点击时间\n\n  @override\n  Widget build(BuildContext context) {\n    return new WillPopScope(\n        onWillPop: () async {\n          if (_lastPressedAt == null ||\n              DateTime.now().difference(_lastPressedAt) > Duration(seconds: 1)) {\n            //两次点击间隔超过1秒则重新计时\n            _lastPressedAt = DateTime.now();\n            return false;\n          }\n          return true;\n        },\n        child: Container(\n          alignment: Alignment.center,\n          child: Text(\"1秒内连续按两次返回键退出\"),\n        )\n    );\n  }\n}\n```\n\n读者可以运行示例看看效果。\n"
  },
  {
    "path": "src/v2/chapter8/eventbus.md",
    "content": "\n\n# 8.3 事件总线\n\n在APP中，我们经常会需要一个广播机制，用以跨页面事件通知，比如一个需要登录的APP中，页面会关注用户登录或注销事件，来进行一些状态更新。这时候，一个事件总线便会非常有用，事件总线通常实现了订阅者模式，订阅者模式包含发布者和订阅者两种角色，可以通过事件总线来触发事件和监听事件，本节我们实现一个简单的全局事件总线，我们使用单例模式，代码如下：\n\n```dart\n//订阅者回调签名\ntypedef void EventCallback(arg);\n\nclass EventBus {\n  //私有构造函数\n  EventBus._internal();\n\n  //保存单例\n  static EventBus _singleton = new EventBus._internal();\n\n  //工厂构造函数\n  factory EventBus()=> _singleton;\n\n  //保存事件订阅者队列，key:事件名(id)，value: 对应事件的订阅者队列\n  var _emap = new Map<Object, List<EventCallback>>();\n\n  //添加订阅者\n  void on(eventName, EventCallback f) {\n    if (eventName == null || f == null) return;\n    _emap[eventName] ??= new List<EventCallback>();\n    _emap[eventName].add(f);\n  }\n\n  //移除订阅者\n  void off(eventName, [EventCallback f]) {\n    var list = _emap[eventName];\n    if (eventName == null || list == null) return;\n    if (f == null) {\n      _emap[eventName] = null;\n    } else {\n      list.remove(f);\n    }\n  }\n\n  //触发事件，事件触发后该事件所有订阅者会被调用\n  void emit(eventName, [arg]) {\n    var list = _emap[eventName];\n    if (list == null) return;\n    int len = list.length - 1;\n    //反向遍历，防止订阅者在回调中移除自身带来的下标错位 \n    for (var i = len; i > -1; --i) {\n      list[i](arg);\n    }\n  }\n}\n\n//定义一个top-level（全局）变量，页面引入该文件后可以直接使用bus\nvar bus = new EventBus();\n```\n\n使用示例：\n\n```dart\n//页面A中\n...\n //监听登录事件\nbus.on(\"login\", (arg) {\n  // do something\n});\n\n//登录页B中\n...\n//登录成功后触发登录事件，页面A中订阅者会被调用\nbus.emit(\"login\", userInfo);\n\n```\n\n\n> 注意：Dart中实现单例模式的标准做法就是使用static变量+工厂构造函数的方式，这样就可以保证`new EventBus()`始终返回都是同一个实例，读者应该理解并掌握这种方法。\n\n事件总线通常用于组件之间状态共享，但关于组件之间状态共享也有一些专门的包如redux、以及前面介绍过的Provider。对于一些简单的应用，事件总线是足以满足业务需求的，如果你决定使用状态管理包的话，一定要想清楚您的APP是否真的有必要使用它，防止“化简为繁”、过度设计。\n"
  },
  {
    "path": "src/v2/chapter8/gesture.md",
    "content": "# 8.2 手势识别 \n\n本节先介绍一些Flutter中用于处理手势的`GestureDetector`和`GestureRecognizer`，然后再仔细讨论一下手势竞争与冲突问题。\n\n## 8.2.1 GestureDetector\n\n`GestureDetector`是一个用于手势识别的功能性组件，我们通过它可以来识别各种手势。`GestureDetector`实际上是指针事件的语义化封装，接下来我们详细介绍一下各种手势识别。\n\n### 点击、双击、长按\n\n我们通过`GestureDetector`对`Container`进行手势识别，触发相应事件后，在`Container`上显示事件名，为了增大点击区域，将`Container`设置为200×100，代码如下：\n\n```dart\n\nclass GestureDetectorTestRoute extends StatefulWidget {\n  @override\n  _GestureDetectorTestRouteState createState() =>\n      new _GestureDetectorTestRouteState();\n}\n\nclass _GestureDetectorTestRouteState extends State<GestureDetectorTestRoute> {\n  String _operation = \"No Gesture detected!\"; //保存事件名\n  @override\n  Widget build(BuildContext context) {\n    return Center(\n      child: GestureDetector(\n        child: Container(\n          alignment: Alignment.center,\n          color: Colors.blue,\n          width: 200.0, \n          height: 100.0,\n          child: Text(_operation,\n            style: TextStyle(color: Colors.white),\n          ),\n        ),\n        onTap: () => updateText(\"Tap\"),//点击\n        onDoubleTap: () => updateText(\"DoubleTap\"), //双击\n        onLongPress: () => updateText(\"LongPress\"), //长按\n      ),\n    );\n  }\n\n  void updateText(String text) {\n    //更新显示的事件名\n    setState(() {\n      _operation = text;\n    });\n  }\n}\n```\n\n运行效果如图8-2所示：\n\n![图8-2](../imgs/8-2.png)\n\n\n\n> **注意**： 当同时监听`onTap`和`onDoubleTap`事件时，当用户触发tap事件时，会有200毫秒左右的延时，这是因为当用户点击完之后很可能会再次点击以触发双击事件，所以`GestureDetector`会等一段时间来确定是否为双击事件。如果用户只监听了`onTap`（没有监听`onDoubleTap`）事件时，则没有延时。\n\n\n\n### 拖动、滑动\n\n一次完整的手势过程是指用户手指按下到抬起的整个过程，期间，用户按下手指后可能会移动，也可能不会移动。`GestureDetector`对于拖动和滑动事件是没有区分的，他们本质上是一样的。`GestureDetector`会将要监听的组件的原点（左上角）作为本次手势的原点，当用户在监听的组件上按下手指时，手势识别就会开始。下面我们看一个拖动圆形字母A的示例：\n\n```dart\nclass _Drag extends StatefulWidget {\n  @override\n  _DragState createState() => new _DragState();\n}\n\nclass _DragState extends State<_Drag> with SingleTickerProviderStateMixin {\n  double _top = 0.0; //距顶部的偏移\n  double _left = 0.0;//距左边的偏移\n\n  @override\n  Widget build(BuildContext context) {\n    return Stack(\n      children: <Widget>[\n        Positioned(\n          top: _top,\n          left: _left,\n          child: GestureDetector(\n            child: CircleAvatar(child: Text(\"A\")),\n            //手指按下时会触发此回调\n            onPanDown: (DragDownDetails e) {\n              //打印手指按下的位置(相对于屏幕)\n              print(\"用户手指按下：${e.globalPosition}\");\n            },\n            //手指滑动时会触发此回调\n            onPanUpdate: (DragUpdateDetails e) {\n              //用户手指滑动时，更新偏移，重新构建\n              setState(() {\n                _left += e.delta.dx;\n                _top += e.delta.dy;\n              });\n            },\n            onPanEnd: (DragEndDetails e){\n              //打印滑动结束时在x、y轴上的速度\n              print(e.velocity);\n            },\n          ),\n        )\n      ],\n    );\n  }\n}\n```\n\n运行后，就可以在任意方向拖动了，运行效果如图8-3所示：\n\n![图8-3](../imgs/8-3.png)\n\n\n\n日志：\n\n```\nI/flutter ( 8513): 用户手指按下：Offset(26.3, 101.8)\nI/flutter ( 8513): Velocity(235.5, 125.8)\n```\n\n\n\n代码解释：\n\n- `DragDownDetails.globalPosition`：当用户按下时，此属性为用户按下的位置相对于**屏幕**（而非父组件）原点(左上角)的偏移。\n- `DragUpdateDetails.delta`：当用户在屏幕上滑动时，会触发多次Update事件，`delta`指一次Update事件的滑动的偏移量。\n- `DragEndDetails.velocity`：该属性代表用户抬起手指时的滑动速度(包含x、y两个轴的），示例中并没有处理手指抬起时的速度，常见的效果是根据用户抬起手指时的速度做一个减速动画。\n\n### 单一方向拖动\n\n在本示例中，是可以朝任意方向拖动的，但是在很多场景，我们只需要沿一个方向来拖动，如一个垂直方向的列表，`GestureDetector`可以只识别特定方向的手势事件，我们将上面的例子改为只能沿垂直方向拖动：\n\n```dart\nclass _DragVertical extends StatefulWidget {\n  @override\n  _DragVerticalState createState() => new _DragVerticalState();\n}\n\nclass _DragVerticalState extends State<_DragVertical> {\n  double _top = 0.0;\n\n  @override\n  Widget build(BuildContext context) {\n    return Stack(\n      children: <Widget>[\n        Positioned(\n          top: _top,\n          child: GestureDetector(\n            child: CircleAvatar(child: Text(\"A\")),\n            //垂直方向拖动事件\n            onVerticalDragUpdate: (DragUpdateDetails details) {\n              setState(() {\n                _top += details.delta.dy;\n              });\n            }\n          ),\n        )\n      ],\n    );\n  }\n}\n```\n\n这样就只能在垂直方向拖动了，如果只想在水平方向滑动同理。\n\n### 缩放\n\n`GestureDetector`可以监听缩放事件，下面示例演示了一个简单的图片缩放效果：\n\n```dart\nclass _ScaleTestRouteState extends State<_ScaleTestRoute> {\n  double _width = 200.0; //通过修改图片宽度来达到缩放效果\n\n  @override\n  Widget build(BuildContext context) {\n   return Center(\n     child: GestureDetector(\n        //指定宽度，高度自适应\n        child: Image.asset(\"./images/sea.png\", width: _width),\n        onScaleUpdate: (ScaleUpdateDetails details) {\n          setState(() {\n            //缩放倍数在0.8到10倍之间\n            _width=200*details.scale.clamp(.8, 10.0);\n          });\n        },\n      ),\n   );\n  }\n}\n```\n\n运行效果如图8-4所示：\n\n![图8-4](../imgs/8-4.png)\n\n现在在图片上双指张开、收缩就可以放大、缩小图片。本示例比较简单，实际中我们通常还需要一些其它功能，如双击放大或缩小一定倍数、双指张开离开屏幕时执行一个减速放大动画等，读者可以在学习完后面“动画”一章中的内容后自己来尝试实现一下。\n\n## 8.2.2 GestureRecognizer\n\n`GestureDetector`内部是使用一个或多个`GestureRecognizer`来识别各种手势的，而`GestureRecognizer`的作用就是通过`Listener`来将原始指针事件转换为语义手势，`GestureDetector`直接可以接收一个子widget。`GestureRecognizer`是一个抽象类，一种手势的识别器对应一个`GestureRecognizer`的子类，Flutter实现了丰富的手势识别器，我们可以直接使用。\n\n#### 示例\n\n假设我们要给一段富文本（`RichText`）的不同部分分别添加点击事件处理器，但是`TextSpan`并不是一个widget，这时我们不能用`GestureDetector`，但`TextSpan`有一个`recognizer`属性，它可以接收一个`GestureRecognizer`。\n\n假设我们需要在点击时给文本变色:\n\n```dart\nimport 'package:flutter/gestures.dart';\n\nclass _GestureRecognizerTestRouteState\n    extends State<_GestureRecognizerTestRoute> {\n  TapGestureRecognizer _tapGestureRecognizer = new TapGestureRecognizer();\n  bool _toggle = false; //变色开关\n\n  @override\n  void dispose() {\n     //用到GestureRecognizer的话一定要调用其dispose方法释放资源\n    _tapGestureRecognizer.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Center(\n      child: Text.rich(\n          TextSpan(\n              children: [\n                TextSpan(text: \"你好世界\"),\n                TextSpan(\n                  text: \"点我变色\",\n                  style: TextStyle(\n                      fontSize: 30.0,\n                      color: _toggle ? Colors.blue : Colors.red\n                  ),\n                  recognizer: _tapGestureRecognizer\n                    ..onTap = () {\n                      setState(() {\n                        _toggle = !_toggle;\n                      });\n                    },\n                ),\n                TextSpan(text: \"你好世界\"),\n              ]\n          )\n      ),\n    );\n  }\n}\n```\n\n运行效果：\n\n![图8-5](../imgs/8-5.png)\n\n\n\n> 注意：使用`GestureRecognizer`后一定要调用其`dispose()`方法来释放资源（主要是取消内部的计时器）。\n>\n\n\n\n## 8.2.3 手势竞争与冲突\n\n### 竞争\n\n如果在上例中我们同时监听水平和垂直方向的拖动事件，那么我们斜着拖动时哪个方向会生效？实际上取决于第一次移动时两个轴上的位移分量，哪个轴的大，哪个轴在本次滑动事件竞争中就胜出。实际上Flutter中的手势识别引入了一个Arena的概念，Arena直译为“竞技场”的意思，每一个手势识别器（`GestureRecognizer`）都是一个“竞争者”（`GestureArenaMember`），当发生滑动事件时，他们都要在“竞技场”去竞争本次事件的处理权，而最终只有一个“竞争者”会胜出(win)。例如，假设有一个`ListView`，它的第一个子组件也是`ListView`，如果现在滑动这个子`ListView`，父`ListView`会动吗？答案是否定的，这时只有子`ListView`会动，因为这时子`ListView`会胜出而获得滑动事件的处理权。\n\n### **示例**\n\n我们以拖动手势为例，同时识别水平和垂直方向的拖动手势，当用户按下手指时就会触发竞争（水平方向和垂直方向），一旦某个方向“获胜”，则直到当次拖动手势结束都会沿着该方向移动。代码如下：\n\n```dart\nimport 'package:flutter/material.dart';\n\nclass BothDirectionTestRoute extends StatefulWidget {\n  @override\n  BothDirectionTestRouteState createState() =>\n      new BothDirectionTestRouteState();\n}\n\nclass BothDirectionTestRouteState extends State<BothDirectionTestRoute> {\n  double _top = 0.0;\n  double _left = 0.0;\n\n  @override\n  Widget build(BuildContext context) {\n    return Stack(\n      children: <Widget>[\n        Positioned(\n          top: _top,\n          left: _left,\n          child: GestureDetector(\n            child: CircleAvatar(child: Text(\"A\")),\n            //垂直方向拖动事件\n            onVerticalDragUpdate: (DragUpdateDetails details) {\n              setState(() {\n                _top += details.delta.dy;\n              });\n            },\n            onHorizontalDragUpdate: (DragUpdateDetails details) {\n              setState(() {\n                _left += details.delta.dx;\n              });\n            },\n          ),\n        )\n      ],\n    );\n  }\n}\n```\n\n此示例运行后，每次拖动只会沿一个方向移动（水平或垂直），而竞争发生在手指按下后首次移动（move）时，此例中具体的“获胜”条件是：首次移动时的位移在水平和垂直方向上的分量大的一个获胜。\n\n### 手势冲突\n\n由于手势竞争最终只有一个胜出者，所以，当有多个手势识别器时，可能会产生冲突。假设有一个widget，它可以左右拖动，现在我们也想检测在它上面手指按下和抬起的事件，代码如下：\n\n```dart\nclass GestureConflictTestRouteState extends State<GestureConflictTestRoute> {\n  double _left = 0.0;\n  @override\n  Widget build(BuildContext context) {\n    return Stack(\n      children: <Widget>[\n        Positioned(\n          left: _left,\n          child: GestureDetector(\n              child: CircleAvatar(child: Text(\"A\")), //要拖动和点击的widget\n              onHorizontalDragUpdate: (DragUpdateDetails details) {\n                setState(() {\n                  _left += details.delta.dx;\n                });\n              },\n              onHorizontalDragEnd: (details){\n                print(\"onHorizontalDragEnd\");\n              },\n              onTapDown: (details){\n                print(\"down\");\n              },\n              onTapUp: (details){\n                print(\"up\");\n              },\n          ),\n        )\n      ],\n    );\n  }\n}\n```\n\n现在我们按住圆形“A”拖动然后抬起手指，控制台日志如下:\n\n```\nI/flutter (17539): down\nI/flutter (17539): onHorizontalDragEnd\n```\n\n我们发现没有打印\"up\"，这是因为在拖动时，刚开始按下手指时在没有移动时，拖动手势还没有完整的语义，此时TapDown手势胜出(win)，此时打印\"down\"，而拖动时，拖动手势会胜出，当手指抬起时，`onHorizontalDragEnd` 和 `onTapUp`发生了冲突，但是因为是在拖动的语义中，所以`onHorizontalDragEnd`胜出，所以就会打印 “onHorizontalDragEnd”。如果我们的代码逻辑中，对于手指按下和抬起是强依赖的，比如在一个轮播图组件中，我们希望手指按下时，暂停轮播，而抬起时恢复轮播，但是由于轮播图组件中本身可能已经处理了拖动手势（支持手动滑动切换），甚至可能也支持了缩放手势，这时我们如果在外部再用`onTapDown`、`onTapUp`来监听的话是不行的。这时我们应该怎么做？其实很简单，通过Listener监听原始指针事件就行：\n\n```dart\nPositioned(\n  top:80.0,\n  left: _leftB,\n  child: Listener(\n    onPointerDown: (details) {\n      print(\"down\");\n    },\n    onPointerUp: (details) {\n      //会触发\n      print(\"up\");\n    },\n    child: GestureDetector(\n      child: CircleAvatar(child: Text(\"B\")),\n      onHorizontalDragUpdate: (DragUpdateDetails details) {\n        setState(() {\n          _leftB += details.delta.dx;\n        });\n      },\n      onHorizontalDragEnd: (details) {\n        print(\"onHorizontalDragEnd\");\n      },\n    ),\n  ),\n)\n```\n\n手势冲突只是手势级别的，而手势是对原始指针的语义化的识别，所以在遇到复杂的冲突场景时，都可以通过`Listener`直接识别原始指针事件来解决冲突。\n\n"
  },
  {
    "path": "src/v2/chapter8/index.md",
    "content": "## 事件处理与通知\n\nFlutter中的手势系统有两个独立的层。第一层为原始指针(pointer)事件，它描述了屏幕上指针（例如，触摸、鼠标和触控笔）的位置和移动。 第二层为手势，描述由一个或多个指针移动组成的语义动作，如拖动、缩放、双击等。本章将先分别介绍如何处理这两种事件，最后再介绍一下Flutter中重要的Notification机制。\n\n## 本章目录\n\n* [原始指针事件处理](listener.md)\n* [手势识别](gesture.md)\n* [全局事件总线](eventbus.md) \n* [通知Notification](notification.md) \n"
  },
  {
    "path": "src/v2/chapter8/listener.md",
    "content": "# 8.1 原始指针事件处理\n\n本节先来介绍一下原始指针事件(Pointer Event，在移动设备上通常为触摸事件)，下一节再介绍手势处理。\n\n在移动端，各个平台或UI系统的原始指针事件模型基本都是一致，即：一次完整的事件分为三个阶段：手指按下、手指移动、和手指抬起，而更高级别的手势（如点击、双击、拖动等）都是基于这些原始事件的。\n\n当指针按下时，Flutter会对应用程序执行**命中测试(Hit Test)**，以确定指针与屏幕接触的位置存在哪些组件（widget）， 指针按下事件（以及该指针的后续事件）然后被分发到由命中测试发现的最内部的组件，然后从那里开始，事件会在组件树中向上冒泡，这些事件会从最内部的组件被分发到组件树根的路径上的所有组件，这和Web开发中浏览器的**事件冒泡**机制相似， 但是Flutter中没有机制取消或停止“冒泡”过程，而浏览器的冒泡是可以停止的。注意，只有通过命中测试的组件才能触发事件。\n\nFlutter中可以使用`Listener`来监听原始触摸事件，按照本书对组件的分类，则`Listener`也是一个功能性组件。下面是`Listener`的构造函数定义：\n\n```dart\nListener({\n  Key key,\n  this.onPointerDown, //手指按下回调\n  this.onPointerMove, //手指移动回调\n  this.onPointerUp,//手指抬起回调\n  this.onPointerCancel,//触摸事件取消回调\n  this.behavior = HitTestBehavior.deferToChild, //在命中测试期间如何表现\n  Widget child\n})\n```\n\n我们先看一个示例，后面再单独讨论一下`behavior`属性。\n\n```dart\n...\n//定义一个状态，保存当前指针位置\nPointerEvent _event;\n...\nListener(\n  child: Container(\n    alignment: Alignment.center,\n    color: Colors.blue,\n    width: 300.0,\n    height: 150.0,\n    child: Text(_event?.toString()??\"\",style: TextStyle(color: Colors.white)),\n  ),\n  onPointerDown: (PointerDownEvent event) => setState(()=>_event=event),\n  onPointerMove: (PointerMoveEvent event) => setState(()=>_event=event),\n  onPointerUp: (PointerUpEvent event) => setState(()=>_event=event),\n),\n```\n\n运行后效果如图8-1所示：\n\n![图8-1](../imgs/8-1.png)\n\n手指在蓝色矩形区域内移动即可看到当前指针偏移，当触发指针事件时，参数`PointerDownEvent`、`PointerMoveEvent`、`PointerUpEvent`都是`PointerEvent`的一个子类，`PointerEvent`类中包括当前指针的一些信息，如：\n\n- `position`：它是鼠标相对于当对于全局坐标的偏移。\n- `delta`：两次指针移动事件（`PointerMoveEvent`）的距离。\n- `pressure`：按压力度，如果手机屏幕支持压力传感器(如iPhone的3D Touch)，此属性会更有意义，如果手机不支持，则始终为1。\n- `orientation`：指针移动方向，是一个角度值。\n\n上面只是`PointerEvent`一些常用属性，除了这些它还有很多属性，读者可以查看API文档。\n\n现在，我们重点来介绍一下`behavior`属性，它决定子组件如何响应命中测试，它的值类型为`HitTestBehavior`，这是一个枚举类，有三个枚举值：\n\n- `deferToChild`：子组件会一个接一个的进行命中测试，如果子组件中有测试通过的，则当前组件通过，这就意味着，如果指针事件作用于子组件上时，其父级组件也肯定可以收到该事件。\n\n- `opaque`：在命中测试时，将当前组件当成不透明处理(即使本身是透明的)，最终的效果相当于当前Widget的整个区域都是点击区域。举个例子：\n\n  ```dart\n  Listener(\n      child: ConstrainedBox(\n          constraints: BoxConstraints.tight(Size(300.0, 150.0)),\n          child: Center(child: Text(\"Box A\")),\n      ),\n      //behavior: HitTestBehavior.opaque,\n      onPointerDown: (event) => print(\"down A\")\n  ),\n  ```\n\n  上例中，只有点击文本内容区域才会触发点击事件，因为 `deferToChild` 会去子组件判断是否命中测试，而该例中子组件就是 `Text(\"Box A\")` 。\n  如果我们想让整个300×150的矩形区域都能点击我们可以将`behavior`设为`HitTestBehavior.opaque`。注意，该属性并不能用于在组件树中拦截（忽略）事件，它只是决定命中测试时的组件大小。\n\n- `translucent`：当点击组件透明区域时，可以对自身边界内及底部可视区域都进行命中测试，这意味着点击顶部组件透明区域时，顶部组件和底部组件都可以接收到事件，例如：\n\n  ```dart\n  Stack(\n    children: <Widget>[\n      Listener(\n        child: ConstrainedBox(\n          constraints: BoxConstraints.tight(Size(300.0, 200.0)),\n          child: DecoratedBox(\n              decoration: BoxDecoration(color: Colors.blue)),\n        ),\n        onPointerDown: (event) => print(\"down0\"),\n      ),\n      Listener(\n        child: ConstrainedBox(\n          constraints: BoxConstraints.tight(Size(200.0, 100.0)),\n          child: Center(child: Text(\"左上角200*100范围内非文本区域点击\")),\n        ),\n        onPointerDown: (event) => print(\"down1\"),\n        //behavior: HitTestBehavior.translucent, //放开此行注释后可以\"点透\"\n      )\n    ],\n  )\n  ```\n\n  上例中，当注释掉最后一行代码后，在左上角200*100范围内非文本区域点击时（顶部组件透明区域），控制台只会打印“down0”，也就是说顶部组件没有接收到事件，而只有底部接收到了。当放开注释后，再点击时顶部和底部都会接收到事件，此时会打印：\n\n  ```\n  I/flutter ( 3039): down1\n  I/flutter ( 3039): down0\n  ```\n  如果`behavior`值改为`HitTestBehavior.opaque`，则只会打印\"down1\"。\n\n### 忽略PointerEvent\n\n假如我们不想让某个子树响应`PointerEvent`的话，我们可以使用`IgnorePointer`和`AbsorbPointer`，这两个组件都能阻止子树接收指针事件，不同之处在于`AbsorbPointer`本身会参与命中测试，而`IgnorePointer`本身不会参与，这就意味着`AbsorbPointer`本身是可以接收指针事件的(但其子树不行)，而`IgnorePointer`不可以。一个简单的例子如下：\n\n```dart\nListener(\n  child: AbsorbPointer(\n    child: Listener(\n      child: Container(\n        color: Colors.red,\n        width: 200.0,\n        height: 100.0,\n      ),\n      onPointerDown: (event)=>print(\"in\"),\n    ),\n  ),\n  onPointerDown: (event)=>print(\"up\"),\n)\n```\n\n点击`Container`时，由于它在`AbsorbPointer`的子树上，所以不会响应指针事件，所以日志不会输出\"in\"，但`AbsorbPointer`本身是可以接收指针事件的，所以会输出\"up\"。如果将`AbsorbPointer`换成`IgnorePointer`，那么两个都不会输出。\n\n\n"
  },
  {
    "path": "src/v2/chapter8/notification.md",
    "content": "# 8.4 Notification\n\n通知（Notification）是Flutter中一个重要的机制，在widget树中，每一个节点都可以分发通知，通知会沿着当前节点向上传递，所有父节点都可以通过`NotificationListener`来监听通知。Flutter中将这种由子向父的传递通知的机制称为**通知冒泡**（Notification Bubbling）。通知冒泡和用户触摸事件冒泡是相似的，但有一点不同：通知冒泡可以中止，但用户触摸事件不行。\n\n> 通知冒泡和Web开发中浏览器事件冒泡原理是相似的，都是事件从出发源逐层向上传递，我们可以在上层节点任意位置来监听通知/事件，也可以终止冒泡过程，终止冒泡后，通知将不会再向上传递。\n\nFlutter中很多地方使用了通知，如可滚动组件（Scrollable Widget）滑动时就会分发**滚动通知**（ScrollNotification），而Scrollbar正是通过监听ScrollNotification来确定滚动条位置的。\n\n下面是一个监听可滚动组件滚动通知的例子：\n\n```dart\nNotificationListener(\n  onNotification: (notification){\n    switch (notification.runtimeType){\n      case ScrollStartNotification: print(\"开始滚动\"); break;\n      case ScrollUpdateNotification: print(\"正在滚动\"); break;\n      case ScrollEndNotification: print(\"滚动停止\"); break;\n      case OverscrollNotification: print(\"滚动到边界\"); break;\n    }\n  },\n  child: ListView.builder(\n      itemCount: 100,\n      itemBuilder: (context, index) {\n        return ListTile(title: Text(\"$index\"),);\n      }\n  ),\n);\n```\n\n上例中的滚动通知如`ScrollStartNotification`、`ScrollUpdateNotification`等都是继承自`ScrollNotification`类，不同类型的通知子类会包含不同的信息，比如`ScrollUpdateNotification`有一个`scrollDelta`属性，它记录了移动的位移，其它通知属性读者可以自己查看SDK文档。\n\n上例中，我们通过`NotificationListener`来监听子`ListView`的滚动通知的，`NotificationListener`定义如下：\n\n```dart\nclass NotificationListener<T extends Notification> extends StatelessWidget {\n  const NotificationListener({\n    Key key,\n    @required this.child,\n    this.onNotification,\n  }) : super(key: key);\n ...//省略无关代码 \n}  \n```\n\n我们可以看到：\n\n1. `NotificationListener` 继承自`StatelessWidget `类，所以它可以直接嵌套到Widget树中。\n\n2. `NotificationListener` 可以指定一个模板参数，该模板参数类型必须是继承自`Notification`；当显式指定模板参数时，`NotificationListener` 便只会接收该参数类型的通知。举个例子，如果我们将上例子代码改为：\n\n   ```dart\n   //指定监听通知的类型为滚动结束通知(ScrollEndNotification)\n   NotificationListener<ScrollEndNotification>(\n     onNotification: (notification){\n       //只会在滚动结束时才会触发此回调\n       print(notification);\n     },\n     child: ListView.builder(\n         itemCount: 100,\n         itemBuilder: (context, index) {\n           return ListTile(title: Text(\"$index\"),);\n         }\n     ),\n   );\n   ```\n\n   上面代码运行后便只会在滚动结束时在控制台打印出通知的信息。\n\n3. `onNotification`回调为通知处理回调，其函数签名如下：\n\n   ```dart\n   typedef NotificationListenerCallback<T extends Notification> = bool Function(T notification);\n   ```\n\n   它的返回值类型为布尔值，当返回值为`true`时，阻止冒泡，其父级Widget将再也收不到该通知；当返回值为`false` 时继续向上冒泡通知。\n\nFlutter的UI框架实现中，除了在可滚动组件在滚动过程中会发出`ScrollNotification`之外，还有一些其它的通知，如`SizeChangedLayoutNotification`、`KeepAliveNotification` 、`LayoutChangedNotification`等，Flutter正是通过这种通知机制来使父元素可以在一些特定时机来做一些事情。\n\n#### 自定义通知\n\n除了Flutter内部通知，我们也可以自定义通知，下面我们看看如何实现自定义通知：\n\n1. 定义一个通知类，要继承自Notification类；\n\n   ```dart\n   class MyNotification extends Notification {\n     MyNotification(this.msg);\n     final String msg;\n   }\n   ```\n\n2. 分发通知。\n\n   `Notification`有一个`dispatch(context)`方法，它是用于分发通知的，我们说过`context`实际上就是操作`Element`的一个接口，它与`Element`树上的节点是对应的，通知会从`context`对应的`Element`节点向上冒泡。\n\n下面我们看一个完整的例子：\n\n```dart\nclass NotificationRoute extends StatefulWidget {\n  @override\n  NotificationRouteState createState() {\n    return new NotificationRouteState();\n  }\n}\n\nclass NotificationRouteState extends State<NotificationRoute> {\n  String _msg=\"\";\n  @override\n  Widget build(BuildContext context) {\n    //监听通知  \n    return NotificationListener<MyNotification>(\n      onNotification: (notification) {\n        setState(() {\n          _msg+=notification.msg+\"  \";\n        });\n       return true;\n      },\n      child: Center(\n        child: Column(\n          mainAxisSize: MainAxisSize.min,\n          children: <Widget>[\n//          RaisedButton(\n//           onPressed: () => MyNotification(\"Hi\").dispatch(context),\n//           child: Text(\"Send Notification\"),\n//          ),  \n            Builder(\n              builder: (context) {\n                return RaisedButton(\n                  //按钮点击时分发通知  \n                  onPressed: () => MyNotification(\"Hi\").dispatch(context),\n                  child: Text(\"Send Notification\"),\n                );\n              },\n            ),\n            Text(_msg)\n          ],\n        ),\n      ),\n    );\n  }\n}\n\nclass MyNotification extends Notification {\n  MyNotification(this.msg);\n  final String msg;\n}\n```\n\n上面代码中，我们每点一次按钮就会分发一个`MyNotification`类型的通知，我们在Widget根上监听通知，收到通知后我们将通知通过Text显示在屏幕上。\n\n> 注意：代码中注释的部分是不能正常工作的，因为这个`context`是根Context，而NotificationListener是监听的子树，所以我们通过`Builder`来构建RaisedButton，来获得按钮位置的context。\n\n运行效果如图8-6所示：\n\n![图8-6](../imgs/8-6.png)\n\n### 阻止冒泡\n\n我们将上面的例子改为：\n\n```dart\nclass NotificationRouteState extends State<NotificationRoute> {\n  String _msg=\"\";\n  @override\n  Widget build(BuildContext context) {\n    //监听通知\n    return NotificationListener<MyNotification>(\n      onNotification: (notification){\n        print(notification.msg); //打印通知\n        return false;\n      },\n      child: NotificationListener<MyNotification>(\n        onNotification: (notification) {\n          setState(() {\n            _msg+=notification.msg+\"  \";\n          });\n          return false; \n        },\n        child: ...//省略重复代码\n      ),\n    );\n  }\n}\n```\n\n上列中两个`NotificationListener`进行了嵌套，子`NotificationListener`的`onNotification`回调返回了`false`，表示不阻止冒泡，所以父`NotificationListener`仍然会受到通知，所以控制台会打印出通知信息；如果将子`NotificationListener`的`onNotification`回调的返回值改为`true`，则父`NotificationListener`便不会再打印通知了，因为子`NotificationListener`已经终止通知冒泡了。\n\n### 通知冒泡原理\n\n我们在上面介绍了通知冒泡的现象及使用，现在我们更深入一些，介绍一下Flutter框架中是如何实现通知冒泡的。为了搞清楚这个问题，就必须看一下源码，我们从通知分发的的源头出发，然后再顺藤摸瓜。由于通知是通过`Notification`的`dispatch(context)`方法发出的，那我们先看看`dispatch(context)`方法中做了什么，下面是相关源码：\n\n```dart\nvoid dispatch(BuildContext target) {\n  target?.visitAncestorElements(visitAncestor);\n}\n```\n\n`dispatch(context)`中调用了当前context的`visitAncestorElements`方法，该方法会从当前Element开始向上遍历父级元素；`visitAncestorElements`有一个遍历回调参数，在遍历过程中对遍历到的父级元素都会执行该回调。遍历的终止条件是：已经遍历到根Element或某个遍历回调返回`false`。源码中传给`visitAncestorElements`方法的遍历回调为`visitAncestor`方法，我们看看`visitAncestor`方法的实现：\n\n```dart\n//遍历回调，会对每一个父级Element执行此回调\nbool visitAncestor(Element element) {\n  //判断当前element对应的Widget是否是NotificationListener。\n  \n  //由于NotificationListener是继承自StatelessWidget，\n  //故先判断是否是StatelessElement\n  if (element is StatelessElement) {\n    //是StatelessElement，则获取element对应的Widget，判断\n    //是否是NotificationListener 。\n    final StatelessWidget widget = element.widget;\n    if (widget is NotificationListener<Notification>) {\n      //是NotificationListener，则调用该NotificationListener的_dispatch方法\n      if (widget._dispatch(this, element)) \n        return false;\n    }\n  }\n  return true;\n}\n```\n\n`visitAncestor `会判断每一个遍历到的父级Widget是否是`NotificationListener`，如果不是，则返回`true`继续向上遍历，如果是，则调用`NotificationListener`的`_dispatch`方法，我们看看`_dispatch`方法的源码：\n\n```dart\n  bool _dispatch(Notification notification, Element element) {\n    // 如果通知监听器不为空，并且当前通知类型是该NotificationListener\n    // 监听的通知类型，则调用当前NotificationListener的onNotification\n    if (onNotification != null && notification is T) {\n      final bool result = onNotification(notification);\n      // 返回值决定是否继续向上遍历\n      return result == true; \n    }\n    return false;\n  }\n```\n\n我们可以看到`NotificationListener`的`onNotification`回调最终是在`_dispatch`方法中执行的，然后会根据返回值来确定是否继续向上冒泡。上面的源码实现其实并不复杂，通过阅读这些源码，一些额外的点读者可以注意一下：\n\n1. `Context`上也提供了遍历Element树的方法。\n2. 我们可以通过`Element.widget`得到`element`节点对应的widget；我们已经反复讲过Widget和Element的对应关系，读者通过这些源码来加深理解。\n\n### 总结\n\nFlutter中通过通知冒泡实现了一套自低向上的消息传递机制，这个和Web开发中浏览器的事件冒泡原理类似，Web开发者可以类比学习。另外我们通过源码了解了Flutter 通知冒泡的流程和原理，便于读者加深理解和学习Flutter的框架设计思想，在此，再次建议读者在平时学习中能多看看源码，定会受益匪浅。\n"
  },
  {
    "path": "src/v2/chapter9/animated_switcher.md",
    "content": "# 9.6 通用“动画切换”组件（AnimatedSwitcher）\n\n实际开发中，我们经常会遇到切换UI元素的场景，比如Tab切换、路由切换。为了增强用户体验，通常在切换时都会指定一个动画，以使切换过程显得平滑。Flutter SDK组件库中已经提供了一些常用的切换组件，如`PageView`、`TabView`等，但是，这些组件并不能覆盖全部的需求场景，为此，Flutter SDK中提供了一个`AnimatedSwitcher`组件，它定义了一种通用的UI切换抽象。\n\n## 9.6.1 AnimatedSwitcher\n\n`AnimatedSwitcher` 可以同时对其新、旧子元素添加显示、隐藏动画。也就是说在`AnimatedSwitcher`的子元素发生变化时，会对其旧元素和新元素，我们先看看`AnimatedSwitcher` 的定义：\n\n```dart\nconst AnimatedSwitcher({\n  Key key,\n  this.child,\n  @required this.duration, // 新child显示动画时长\n  this.reverseDuration,// 旧child隐藏的动画时长\n  this.switchInCurve = Curves.linear, // 新child显示的动画曲线\n  this.switchOutCurve = Curves.linear,// 旧child隐藏的动画曲线\n  this.transitionBuilder = AnimatedSwitcher.defaultTransitionBuilder, // 动画构建器\n  this.layoutBuilder = AnimatedSwitcher.defaultLayoutBuilder, //布局构建器\n})\n```\n\n当`AnimatedSwitcher`的child发生变化时（类型或Key不同），旧child会执行隐藏动画，新child会执行执行显示动画。究竟执行何种动画效果则由`transitionBuilder `参数决定，该参数接受一个`AnimatedSwitcherTransitionBuilder `类型的builder，定义如下：\n\n```dart\ntypedef AnimatedSwitcherTransitionBuilder =\n  Widget Function(Widget child, Animation<double> animation);\n```\n\n该`builder`在`AnimatedSwitcher`的child切换时会分别对新、旧child绑定动画：\n\n1. 对旧child，绑定的动画会反向执行（reverse）\n2. 对新child，绑定的动画会正向指向（forward）\n\n这样一下，便实现了对新、旧child的动画绑定。`AnimatedSwitcher`的默认值是`AnimatedSwitcher.defaultTransitionBuilder` ：\n\n```dart\nWidget defaultTransitionBuilder(Widget child, Animation<double> animation) {\n  return FadeTransition(\n    opacity: animation,\n    child: child,\n  );\n}\n```\n\n可以看到，返回了`FadeTransition`对象，也就是说默认情况，`AnimatedSwitcher`会对新旧child执行“渐隐”和“渐显”动画。\n\n### 例子\n\n下面我们看一个列子：实现一个计数器，然后再每一次自增的过程中，旧数字执行缩小动画隐藏，新数字执行放大动画显示，代码如下：\n\n```dart\nimport 'package:flutter/material.dart';\n\nclass AnimatedSwitcherCounterRoute extends StatefulWidget {\n   const AnimatedSwitcherCounterRoute({Key key}) : super(key: key);\n\n   @override\n   _AnimatedSwitcherCounterRouteState createState() => _AnimatedSwitcherCounterRouteState();\n }\n\n class _AnimatedSwitcherCounterRouteState extends State<AnimatedSwitcherCounterRoute> {\n   int _count = 0;\n\n   @override\n   Widget build(BuildContext context) {\n     return Center(\n       child: Column(\n         mainAxisAlignment: MainAxisAlignment.center,\n         children: <Widget>[\n           AnimatedSwitcher(\n             duration: const Duration(milliseconds: 500),\n             transitionBuilder: (Widget child, Animation<double> animation) {\n               //执行缩放动画\n               return ScaleTransition(child: child, scale: animation);\n             },\n             child: Text(\n               '$_count',\n               //显示指定key，不同的key会被认为是不同的Text，这样才能执行动画\n               key: ValueKey<int>(_count),\n               style: Theme.of(context).textTheme.headline4,\n             ),\n           ),\n           RaisedButton(\n             child: const Text('+1',),\n             onPressed: () {\n               setState(() {\n                 _count += 1;\n               });\n             },\n           ),\n         ],\n       ),\n     );\n   }\n }\n```\n\n运行示例代码，当点击“+1”按钮时，原先的数字会逐渐缩小直至隐藏，而新数字会逐渐放大，我截取了动画执行过程的一帧，如图9-5所示：\n\n![图9-5](../imgs/9-5.png)\n\n上图是第一次点击“+1”按钮后切换动画的一帧，此时“0”正在逐渐缩小，而“1”正在“0”的中间，正在逐渐放大。\n\n> 注意：AnimatedSwitcher的新旧child，如果类型相同，则Key必须不相等。\n\n### AnimatedSwitcher实现原理\n\n实际上，`AnimatedSwitcher`的实现原理是比较简单的，我们根据`AnimatedSwitcher`的使用方式也可以猜个大概。要想实现新旧child切换动画，只需要明确两个问题：动画执行的时机是和如何对新旧child执行动画。从`AnimatedSwitcher`的使用方式我们可以看到，当child发生变化时（子widget的key和类型**不**同时相等则认为发生变化），则重新会重新执行`build`，然后动画开始执行。我们可以通过继承StatefulWidget来实现`AnimatedSwitcher`，具体做法是在`didUpdateWidget` 回调中判断其新旧child是否发生变化，如果发生变化，则对旧child执行反向退场（reverse）动画，对新child执行正向（forward）入场动画即可。下面是`AnimatedSwitcher`实现的部分核心伪代码：\n\n```dart\nWidget _widget; //\nvoid didUpdateWidget(AnimatedSwitcher oldWidget) {\n  super.didUpdateWidget(oldWidget);\n  // 检查新旧child是否发生变化(key和类型同时相等则返回true，认为没变化)\n  if (Widget.canUpdate(widget.child, oldWidget.child)) {\n    // child没变化，...\n  } else {\n    //child发生了变化，构建一个Stack来分别给新旧child执行动画\n   _widget= Stack(\n      alignment: Alignment.center,\n      children:[\n        //旧child应用FadeTransition\n        FadeTransition(\n         opacity: _controllerOldAnimation,\n         child : oldWidget.child,\n        ),\n        //新child应用FadeTransition\n        FadeTransition(\n         opacity: _controllerNewAnimation,\n         child : widget.child,\n        ),\n      ]\n    );\n    // 给旧child执行反向退场动画\n    _controllerOldAnimation.reverse();\n    //给新child执行正向入场动画\n    _controllerNewAnimation.forward();\n  }\n}\n\n//build方法\nWidget build(BuildContext context){\n  return _widget;\n}\n```\n\n上面伪代码展示了`AnimatedSwitcher`实现的核心逻辑，当然`AnimatedSwitcher`真正的实现比这个复杂，它可以自定义进退场过渡动画以及执行动画时的布局等。在此，我们删繁就简，通过伪代码形式让读者能够清楚看到主要的实现思路，具体的实现读者可以参考`AnimatedSwitcher`源码。\n\n另外，Flutter SDK中还提供了一个`AnimatedCrossFade`组件，它也可以切换两个子元素，切换过程执行渐隐渐显的动画，和`AnimatedSwitcher`不同的是`AnimatedCrossFade`是针对两个子元素，而`AnimatedSwitcher`是在一个子元素的新旧值之间切换。`AnimatedCrossFade`实现原理比较简单，也有和`AnimatedSwitcher`类似的地方，因此不再赘述，读者有兴趣可以查看其源码。\n\n## 9.6.2 AnimatedSwitcher高级用法\n\n假设现在我们想实现一个类似路由平移切换的动画：旧页面屏幕中向左侧平移退出，新页面重屏幕右侧平移进入。如果要用AnimatedSwitcher的话，我们很快就会发现一个问题：做不到！我们可能会写出下面的代码：\n\n```dart\nAnimatedSwitcher(\n  duration: Duration(milliseconds: 200),\n  transitionBuilder: (Widget child, Animation<double> animation) {\n    var tween=Tween<Offset>(begin: Offset(1, 0), end: Offset(0, 0))\n     return SlideTransition(\n       child: child,\n       position: tween.animate(animation),\n    );\n  },\n  ...//省略\n)\n```\n\n上面的代码有什么问题呢？我们前面说过在`AnimatedSwitcher`的child切换时会分别对新child执行正向动画（forward），而对旧child执行反向动画（reverse），所以真正的效果便是：新child确实从屏幕右侧平移进入了，但旧child却会从屏幕**右侧**（而不是左侧）退出。其实也很容易理解，因为在没有特殊处理的情况下，同一个动画的正向和逆向正好是相反（对称）的。\n\n那么问题来了，难道就不能使用`AnimatedSwitcher`了？答案当然是否定的！仔细想想这个问题，究其原因，就是因为同一个`Animation`正向（forward）和反向（reverse）是对称的。所以如果我们可以打破这种对称性，那么便可以实现这个功能了，下面我们来封装一个`MySlideTransition`，它与`SlideTransition`唯一的不同就是对动画的反向执行进行了定制（从左边滑出隐藏），代码如下：\n\n```dart\nclass MySlideTransition extends AnimatedWidget {\n  MySlideTransition({\n    Key key,\n    @required Animation<Offset> position,\n    this.transformHitTests = true,\n    this.child,\n  })\n      : assert(position != null),\n        super(key: key, listenable: position) ;\n\n  Animation<Offset> get position => listenable;\n  final bool transformHitTests;\n  final Widget child;\n\n  @override\n  Widget build(BuildContext context) {\n    Offset offset=position.value;\n    //动画反向执行时，调整x偏移，实现“从左边滑出隐藏”\n    if (position.status == AnimationStatus.reverse) {\n         offset = Offset(-offset.dx, offset.dy);\n    }\n    return FractionalTranslation(\n      translation: offset,\n      transformHitTests: transformHitTests,\n      child: child,\n    );\n  }\n}\n```\n\n调用时，将`SlideTransition`替换成`MySlideTransition `即可：\n\n```dart\nAnimatedSwitcher(\n  duration: Duration(milliseconds: 200),\n  transitionBuilder: (Widget child, Animation<double> animation) {\n    var tween=Tween<Offset>(begin: Offset(1, 0), end: Offset(0, 0))\n     return MySlideTransition(\n              child: child,\n              position: tween.animate(animation),\n    \t      );\n  },\n  ...//省略\n)\n```\n\n运行后，我截取动画执行过程中的一帧，如图9-6所示：\n\n![图9-6](../imgs/9-6.png)\n\n上图中“0”从左侧滑出，而“1”从右侧滑入。可以看到，我们通过这种巧妙的方式实现了类似路由进场切换的动画，实际上Flutter路由切换也正是通过`AnimatedSwitcher`来实现的。\n\n### SlideTransitionX\n\n上面的示例我们实现了“左出右入”的动画，那如果要实现“右入左出”、“上入下出”或者 “下入上出”怎么办？当然，我们可以分别修改上面的代码，但是这样每种动画都得单独定义一个“Transition”，这很麻烦。本节将封装一个通用的`SlideTransitionX` 来实现这种“出入滑动动画”，代码如下：\n\n```dart\nclass SlideTransitionX extends AnimatedWidget {\n  SlideTransitionX({\n    Key key,\n    @required Animation<double> position,\n    this.transformHitTests = true,\n    this.direction = AxisDirection.down,\n    this.child,\n  })\n      : assert(position != null),\n        super(key: key, listenable: position) {\n    // 偏移在内部处理      \n    switch (direction) {\n      case AxisDirection.up:\n        _tween = Tween(begin: Offset(0, 1), end: Offset(0, 0));\n        break;\n      case AxisDirection.right:\n        _tween = Tween(begin: Offset(-1, 0), end: Offset(0, 0));\n        break;\n      case AxisDirection.down:\n        _tween = Tween(begin: Offset(0, -1), end: Offset(0, 0));\n        break;\n      case AxisDirection.left:\n        _tween = Tween(begin: Offset(1, 0), end: Offset(0, 0));\n        break;\n    }\n  }\n\n\n  Animation<double> get position => listenable;\n\n  final bool transformHitTests;\n\n  final Widget child;\n\n  //退场（出）方向\n  final AxisDirection direction;\n\n  Tween<Offset> _tween;\n\n  @override\n  Widget build(BuildContext context) {\n    Offset offset = _tween.evaluate(position);\n    if (position.status == AnimationStatus.reverse) {\n      switch (direction) {\n        case AxisDirection.up:\n          offset = Offset(offset.dx, -offset.dy);\n          break;\n        case AxisDirection.right:\n          offset = Offset(-offset.dx, offset.dy);\n          break;\n        case AxisDirection.down:\n          offset = Offset(offset.dx, -offset.dy);\n          break;\n        case AxisDirection.left:\n          offset = Offset(-offset.dx, offset.dy);\n          break;\n      }\n    }\n    return FractionalTranslation(\n      translation: offset,\n      transformHitTests: transformHitTests,\n      child: child,\n    );\n  }\n}\n```\n\n现在如果我们想实现各种“滑动出入动画”便非常容易，只需给`direction `传递不同的方向值即可，比如要实现“上入下出”，则：\n\n```dart\nAnimatedSwitcher(\n  duration: Duration(milliseconds: 200),\n  transitionBuilder: (Widget child, Animation<double> animation) {\n    var tween=Tween<Offset>(begin: Offset(1, 0), end: Offset(0, 0))\n     return SlideTransitionX(\n              child: child,\n     \t\t\t\t  direction: AxisDirection.down, //上入下出\n              position: animation,\n    \t      );\n  },\n  ...//省略其余代码\n)\n```\n\n运行后，我截取动画执行过程中的一帧，如图9-7所示：\n\n![图9-7](../imgs/9-7.png)\n\n上图中“1”从底部滑出，而“2”从顶部滑入。读者可以尝试给`SlideTransitionX`的`direction`取不同的值来查看运行效果。\n\n## 总结\n\n本节我们学习了`AnimatedSwitcher`的详细用法，同时也介绍了打破`AnimatedSwitcher`动画对称性的方法。我们可以发现：在需要切换新旧UI元素的场景，`AnimatedSwitcher`将十分实用。\n\n"
  },
  {
    "path": "src/v2/chapter9/animated_widgets.md",
    "content": "# 9.7 动画过渡组件\n\n为了表述方便，本书约定，将在Widget属性发生变化时会执行过渡动画的组件统称为”动画过渡组件“，而动画过渡组件最明显的一个特征就是它会在内部自管理`AnimationController`。我们知道，为了方便使用者可以自定义动画的曲线、执行时长、方向等，在前面介绍过的动画封装方法中，通常都需要使用者自己提供一个`AnimationController`对象来自定义这些属性值。但是，如此一来，使用者就必须得手动管理`AnimationController`，这又会增加使用的复杂性。因此，如果也能将`AnimationController`进行封装，则会大大提高动画组件的易用性。\n\n## 9.7.1 自定义动画过渡组件\n\n我们要实现一个`AnimatedDecoratedBox`，它可以在`decoration`属性发生变化时，从旧状态变成新状态的过程可以执行一个过渡动画。根据前面所学的知识，我们实现了一个`AnimatedDecoratedBox1`组件：\n\n```dart\nclass AnimatedDecoratedBox1 extends StatefulWidget {\n  AnimatedDecoratedBox1({\n    Key key,\n    @required this.decoration,\n    this.child,\n    this.curve = Curves.linear,\n    @required this.duration,\n    this.reverseDuration,\n  });\n\n  final BoxDecoration decoration;\n  final Widget child;\n  final Duration duration;\n  final Curve curve;\n  final Duration reverseDuration;\n\n  @override\n  _AnimatedDecoratedBox1State createState() => _AnimatedDecoratedBox1State();\n}\n\nclass _AnimatedDecoratedBox1State extends State<AnimatedDecoratedBox1>\n    with SingleTickerProviderStateMixin {\n  @protected\n  AnimationController get controller => _controller;\n  AnimationController _controller;\n\n  Animation<double> get animation => _animation;\n  Animation<double> _animation;\n\n  DecorationTween _tween;\n\n  @override\n  Widget build(BuildContext context) {\n    return AnimatedBuilder(\n      animation: _animation,\n      builder: (context, child){\n        return DecoratedBox(\n          decoration: _tween.animate(_animation).value,\n          child: child,\n        );\n      },\n      child: widget.child,\n    );\n  }\n\n  @override\n  void initState() {\n    super.initState();\n    _controller = AnimationController(\n      duration: widget.duration,\n      reverseDuration: widget.reverseDuration,\n      vsync: this,\n    );\n    _tween = DecorationTween(begin: widget.decoration);\n    _updateCurve();\n  }\n\n  void _updateCurve() {\n    if (widget.curve != null)\n      _animation = CurvedAnimation(parent: _controller, curve: widget.curve);\n    else\n      _animation = _controller;\n  }\n\n\n  @override\n  void didUpdateWidget(AnimatedDecoratedBox1 oldWidget) {\n    super.didUpdateWidget(oldWidget);\n    if (widget.curve != oldWidget.curve)\n      _updateCurve();\n    _controller.duration = widget.duration;\n    _controller.reverseDuration = widget.reverseDuration;\n    if(widget.decoration!= (_tween.end ?? _tween.begin)){\n      _tween\n        ..begin = _tween.evaluate(_animation)\n        ..end = widget.decoration;\n      _controller\n        ..value = 0.0\n        ..forward();\n    }\n  }\n\n  @override\n  void dispose() {\n    _controller.dispose();\n    super.dispose();\n  }\n}\n```\n\n下面我们来使用`AnimatedDecoratedBox1`来实现按钮点击后背景色从蓝色过渡到红色的效果：\n\n```dart\nColor _decorationColor = Colors.blue;\nvar duration = Duration(seconds: 1);\n...//省略无关代码\nAnimatedDecoratedBox(\n  duration: duration,\n  decoration: BoxDecoration(color: _decorationColor),\n  child: FlatButton(\n    onPressed: () {\n      setState(() {\n        _decorationColor = Colors.red;\n      });\n    },\n    child: Text(\n      \"AnimatedDecoratedBox\",\n      style: TextStyle(color: Colors.white),\n    ),\n  ),\n)\n```\n\n点击前效果如图9-8所示，点击后截取了过渡过程的一帧如图9-9所示： ![img](../imgs/9-8.png?lastModify=1565699462)![img](../imgs/9-9.png?lastModify=1565699462)\n\n点击后，按钮背景色会从蓝色向红色过渡，图9-9是过渡过程中的一帧，有点偏紫色，整个过渡动画结束后背景会变为红色。\n\n上面的代码虽然实现了我们期望的功能，但是代码却比较复杂。稍加思考后，我们就可以发现，`AnimationController`的管理以及Tween更新部分的代码都是可以抽象出来的，如果我们这些通用逻辑封装成基类，那么要实现动画过渡组件只需要继承这些基类，然后定制自身不同的代码（比如动画每一帧的构建方法）即可，这样将会简化代码。\n\n为了方便开发者来实现动画过渡组件的封装，Flutter提供了一个`ImplicitlyAnimatedWidget`抽象类，它继承自StatefulWidget，同时提供了一个对应的`ImplicitlyAnimatedWidgetState`类，`AnimationController`的管理就在`ImplicitlyAnimatedWidgetState`类中。开发者如果要封装动画，只需要分别继承`ImplicitlyAnimatedWidget`和`ImplicitlyAnimatedWidgetState`类即可，下面我们演示一下具体如何实现。\n\n我们需要分两步实现：\n\n1. 继承`ImplicitlyAnimatedWidget`类。\n\n   ```dart\n   class AnimatedDecoratedBox extends ImplicitlyAnimatedWidget {\n     AnimatedDecoratedBox({\n       Key key,\n       @required this.decoration,\n       this.child,\n       Curve curve = Curves.linear, //动画曲线\n       @required Duration duration, // 正向动画执行时长\n       Duration reverseDuration, // 反向动画执行时长\n     }) : super(\n             key: key,\n             curve: curve,\n             duration: duration,\n             reverseDuration: reverseDuration,\n           );\n     final BoxDecoration decoration;\n     final Widget child;\n   \n     @override\n     _AnimatedDecoratedBoxState createState() {\n       return _AnimatedDecoratedBoxState();\n     }\n   }\n   ```\n\n   其中`curve`、`duration`、`reverseDuration`三个属性在`ImplicitlyAnimatedWidget `中已定义。 可以看到`AnimatedDecoratedBox`类和普通继承自`StatefulWidget`的类没有什么不同。\n\n2. State类继承自`AnimatedWidgetBaseState`（该类继承自`ImplicitlyAnimatedWidgetState`类）。\n\n   ```dart\n   class _AnimatedDecoratedBoxState\n       extends AnimatedWidgetBaseState<AnimatedDecoratedBox> {\n     DecorationTween _decoration; //定义一个Tween\n   \n     @override\n     Widget build(BuildContext context) {\n       return DecoratedBox(\n         decoration: _decoration.evaluate(animation),\n         child: widget.child,\n       );\n     }\n   \n     @override\n     void forEachTween(visitor) {\n       // 在需要更新Tween时，基类会调用此方法\n       _decoration = visitor(_decoration, widget.decoration,\n           (value) => DecorationTween(begin: value));\n     }\n   }\n   ```\n\n   可以看到我们实现了` build`和`forEachTween`两个方法。在动画执行过程中，每一帧都会调用`build`方法（调用逻辑在`ImplicitlyAnimatedWidgetState`中），所以在`build`方法中我们需要构建每一帧的`DecoratedBox`状态，因此得算出每一帧的`decoration` 状态，这个我们可以通过` _decoration.evaluate(animation)` 来算出，其中`animation`是`ImplicitlyAnimatedWidgetState`基类中定义的对象，`_decoration`是我们自定义的一个`DecorationTween`类型的对象，那么现在的问题就是它是在什么时候被赋值的呢？要回答这个问题，我们就得搞清楚什么时候需要对`_decoration`赋值。我们知道`_decoration`是一个Tween，而Tween的主要职责就是定义动画的起始状态（begin）和终止状态(end)。对于`AnimatedDecoratedBox`来说，`decoration`的终止状态就是用户传给它的值，而起始状态是不确定的，有以下两种情况：\n\n   1. `AnimatedDecoratedBox`首次build，此时直接将其`decoration`值置为起始状态，即`_decoration`值为`DecorationTween(begin: decoration)` 。\n   2. `AnimatedDecoratedBox`的`decoration`更新时，则起始状态为`_decoration.animate(animation)`，即`_decoration`值为`DecorationTween(begin: _decoration.animate(animation)，end:decoration)`。\n   \n\n现在`forEachTween`的作用就很明显了，它正是用于来更新Tween的初始值的，在上述两种情况下会被调用，而开发者只需重写此方法，并在此方法中更新Tween的起始状态值即可。而一些更新的逻辑被屏蔽在了`visitor`回调，我们只需要调用它并给它传递正确的参数即可，`visitor`方法签名如下：\n\n```dart\n   Tween visitor(\n     Tween<dynamic> tween, //当前的tween，第一次调用为null\n     dynamic targetValue, // 终止状态\n     TweenConstructor<dynamic> constructor，//Tween构造器，在上述三种情况下会被调用以更新tween\n   );\n```\n\n可以看到，通过继承`ImplicitlyAnimatedWidget`和`ImplicitlyAnimatedWidgetState`类可以快速的实现动画过渡组件的封装，这和我们纯手工实现相比，代码简化了很多。\n\n> 如果读者还有疑惑，建议查看`ImplicitlyAnimatedWidgetState`的源码并结合本示例代码对比理解。\n\n### 动画过渡组件的反向动画\n\n在使用动画过渡组件，我们只需要在改变一些属性值后重新build组件即可，所以要实现状态反向过渡，只需要将前后状态值互换即可实现，这本来是不需要再浪费笔墨的。但是`ImplicitlyAnimatedWidget`构造函数中却有一个`reverseDuration`属性用于设置反向动画的执行时长，这貌似在告诉读者`ImplicitlyAnimatedWidget`本身也提供了执行反向动画的接口，于是笔者查看了`ImplicitlyAnimatedWidgetState`源码并未发现有执行反向动画的接口，唯一有用的是它暴露了控制动画的`controller`。所以如果要让`reverseDuration`生效，我们只能先获取`controller`，然后再通过`controller.reverse()`来启动反向动画，比如我们在上面示例的基础上实现一个循环的点击背景颜色变换效果，要求从蓝色变为红色时动画执行时间为400ms，从红变蓝为2s，如果要使`reverseDuration`生效，我们需要这么做：\n\n```dart\nAnimatedDecoratedBox(\n  duration: Duration( milliseconds: 400),\n  decoration: BoxDecoration(color: _decorationColor),\n  reverseDuration: Duration(seconds: 2),\n  child: Builder(builder: (context) {\n    return FlatButton(\n      onPressed: () {\n        if (_decorationColor == Colors.red) {\n          ImplicitlyAnimatedWidgetState _state =\n              context.findAncestorStateOfType<ImplicitlyAnimatedWidgetState>();\n           // 通过controller来启动反向动画\n          _state.controller.reverse().then((e) {\n            // 经验证必须调用setState来触发rebuild，否则状态同步会有问题\n            setState(() {\n              _decorationColor = Colors.blue;\n            });\n          });\n        } else {\n          setState(() {\n            _decorationColor = Colors.red;\n          });\n        }\n      },\n      child: Text(\n        \"AnimatedDecoratedBox toggle\",\n        style: TextStyle(color: Colors.white),\n      ),\n    );\n  }),\n)\n```\n\n上面的代码实际上是非常糟糕且没必要的，它需要我们了解`ImplicitlyAnimatedWidgetState `内部实现，并且要手动去启动反向动画。我们完全可以通过如下代码实现相同的效果：\n\n```dart\nAnimatedDecoratedBox(\n  duration: Duration(\n      milliseconds: _decorationColor == Colors.red ? 400 : 2000),\n  decoration: BoxDecoration(color: _decorationColor),\n  child: Builder(builder: (context) {\n    return FlatButton(\n      onPressed: () {\n        setState(() {\n          _decorationColor = _decorationColor == Colors.blue\n              ? Colors.red\n              : Colors.blue;\n        });\n      },\n      child: Text(\n        \"AnimatedDecoratedBox toggle\",\n        style: TextStyle(color: Colors.white),\n      ),\n    );\n  }),\n)\n```\n\n这样的代码是不是优雅的多！那么现在问题来了，为什么`ImplicitlyAnimatedWidgetState `要提供一个`reverseDuration`参数呢？笔者仔细研究了`ImplicitlyAnimatedWidgetState `的实现，发现唯一的解释就是该参数并非是给`ImplicitlyAnimatedWidgetState `用的，而是给子类用的！原因正如我们前面说的，要使`reverseDuration` 有用就必须得获取`controller ` 属性来手动启动反向动画，`ImplicitlyAnimatedWidgetState `中的`controller ` 属性是一个保护属性，定义如下：\n\n```dart\n @protected\n  AnimationController get controller => _controller;\n```\n\n而保护属性原则上只应该在子类中使用，而不应该像上面示例代码一样在外部使用。综上，我们可以得出两条结论：\n\n1. 使用动画过渡组件时如果需要执行反向动画的场景，应尽量使用状态互换的方法，而不应该通过获取`ImplicitlyAnimatedWidgetState `中`controller`的方式。\n\n2. 如果我们自定义的动画过渡组件用不到`reverseDuration` ，那么最好就不要暴露此参数，比如我们上面自定义的`AnimatedDecoratedBox`定义中就可以去除`reverseDuration` 可选参数，如：\n\n   ```dart\n   class AnimatedDecoratedBox extends ImplicitlyAnimatedWidget {\n     AnimatedDecoratedBox({\n       Key key,\n       @required this.decoration,\n       this.child,\n       Curve curve = Curves.linear,\n       @required Duration duration,\n     }) : super(\n             key: key,\n             curve: curve,\n             duration: duration,\n           );\n   ```\n\n## 9.7.2 Flutter预置的动画过渡组件\n\nFlutter SDK中也预置了很多动画过渡组件，实现方式和大都和`AnimatedDecoratedBox`差不多，如表9-1所示：\n\n| 组件名                   | 功能                                                         |\n| ------------------------ | ------------------------------------------------------------ |\n| AnimatedPadding          | 在padding发生变化时会执行过渡动画到新状态                    |\n| AnimatedPositioned       | 配合Stack一起使用，当定位状态发生变化时会执行过渡动画到新的状态。 |\n| AnimatedOpacity          | 在透明度opacity发生变化时执行过渡动画到新状态                |\n| AnimatedAlign            | 当`alignment`发生变化时会执行过渡动画到新的状态。            |\n| AnimatedContainer        | 当Container属性发生变化时会执行过渡动画到新的状态。          |\n| AnimatedDefaultTextStyle | 当字体样式发生变化时，子组件中继承了该样式的文本组件会动态过渡到新样式。 |\n\n<center>表9-1：Flutter预置的动画过渡组件</center>\n下面我们通过一个示例来感受一下这些预置的动画过渡组件效果：\n\n```dart\nimport 'package:flutter/material.dart';\n\nclass AnimatedWidgetsTest extends StatefulWidget {\n  @override\n  _AnimatedWidgetsTestState createState() => _AnimatedWidgetsTestState();\n}\n\nclass _AnimatedWidgetsTestState extends State<AnimatedWidgetsTest> {\n  double _padding = 10;\n  var _align = Alignment.topRight;\n  double _height = 100;\n  double _left = 0;\n  Color _color = Colors.red;\n  TextStyle _style = TextStyle(color: Colors.black);\n  Color _decorationColor = Colors.blue;\n\n  @override\n  Widget build(BuildContext context) {\n    var duration = Duration(seconds: 5);\n    return SingleChildScrollView(\n      child: Column(\n        children: <Widget>[\n          RaisedButton(\n            onPressed: () {\n              setState(() {\n                _padding = 20;\n              });\n            },\n            child: AnimatedPadding(\n              duration: duration,\n              padding: EdgeInsets.all(_padding),\n              child: Text(\"AnimatedPadding\"),\n            ),\n          ),\n          SizedBox(\n            height: 50,\n            child: Stack(\n              children: <Widget>[\n                AnimatedPositioned(\n                  duration: duration,\n                  left: _left,\n                  child: RaisedButton(\n                    onPressed: () {\n                      setState(() {\n                        _left = 100;\n                      });\n                    },\n                    child: Text(\"AnimatedPositioned\"),\n                  ),\n                )\n              ],\n            ),\n          ),\n          Container(\n            height: 100,\n            color: Colors.grey,\n            child: AnimatedAlign(\n              duration: duration,\n              alignment: _align,\n              child: RaisedButton(\n                onPressed: () {\n                  setState(() {\n                    _align = Alignment.center;\n                  });\n                },\n                child: Text(\"AnimatedAlign\"),\n              ),\n            ),\n          ),\n          AnimatedContainer(\n            duration: duration,\n            height: _height,\n            color: _color,\n            child: FlatButton(\n              onPressed: () {\n                setState(() {\n                  _height = 150;\n                  _color = Colors.blue;\n                });\n              },\n              child: Text(\n                \"AnimatedContainer\",\n                style: TextStyle(color: Colors.white),\n              ),\n            ),\n          ),\n          AnimatedDefaultTextStyle(\n            child: GestureDetector(\n              child: Text(\"hello world\"),\n              onTap: () {\n                setState(() {\n                  _style = TextStyle(\n                    color: Colors.blue,\n                    decorationStyle: TextDecorationStyle.solid,\n                    decorationColor: Colors.blue,\n                  );\n                });\n              },\n            ),\n            style: _style,\n            duration: duration,\n          ),\n          AnimatedDecoratedBox(\n            duration: duration,\n            decoration: BoxDecoration(color: _decorationColor),\n            child: FlatButton(\n              onPressed: () {\n                setState(() {\n                  _decorationColor = Colors.red;\n                });\n              },\n              child: Text(\n                \"AnimatedDecoratedBox\",\n                style: TextStyle(color: Colors.white),\n              ),\n            ),\n          )\n        ].map((e) {\n          return Padding(\n            padding: EdgeInsets.symmetric(vertical: 16),\n            child: e,\n          );\n        }).toList(),\n      ),\n    );\n  }\n}\n```\n\n运行后效果如图9-10所示：\n\n![图9-10](../imgs/9-10.png)\n\n读者可以点击一下相应组件来查看一下实际的运行效果。\n"
  },
  {
    "path": "src/v2/chapter9/animation_structure.md",
    "content": "# 9.2 动画基本结构及状态监听\n\n## 9.2.1 动画基本结构\n\n在Flutter中我们可以通过多种方式来实现动画，下面通过一个图片逐渐放大示例的不同实现来演示Flutter中动画的不同实现方式的区别。\n\n### 基础版本\n\n下面我们演示一下最基础的动画实现方式：\n\n```dart\nclass ScaleAnimationRoute extends StatefulWidget {\n  @override\n  _ScaleAnimationRouteState createState() => new _ScaleAnimationRouteState();\n}\n\n//需要继承TickerProvider，如果有多个AnimationController，则应该使用TickerProviderStateMixin。\nclass _ScaleAnimationRouteState extends State<ScaleAnimationRoute>  with SingleTickerProviderStateMixin{ \n    \n  Animation<double> animation;\n  AnimationController controller;\n    \n  initState() {\n    super.initState();\n    controller = new AnimationController(\n        duration: const Duration(seconds: 3), vsync: this);\n    //图片宽高从0变到300\n    animation = new Tween(begin: 0.0, end: 300.0).animate(controller)\n      ..addListener(() {\n        setState(()=>{});\n      });\n    //启动动画(正向执行)\n    controller.forward();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return new Center(\n       child: Image.asset(\"imgs/avatar.png\",\n          width: animation.value,\n          height: animation.value\n      ),\n    );\n  }\n\n  dispose() {\n    //路由销毁时需要释放动画资源\n    controller.dispose();\n    super.dispose();\n  }\n}\n```\n\n上面代码中`addListener()`函数调用了`setState()`，所以每次动画生成一个新的数字时，当前帧被标记为脏(dirty)，这会导致widget的`build()`方法再次被调用，而在`build()`中，改变Image的宽高，因为它的高度和宽度现在使用的是`animation.value` ，所以就会逐渐放大。值得注意的是动画完成时要释放控制器(调用`dispose()`方法)以防止内存泄漏。\n\n上面的例子中并没有指定Curve，所以放大的过程是线性的（匀速），下面我们指定一个Curve，来实现一个类似于弹簧效果的动画过程，我们只需要将`initState`中的代码改为下面这样即可：\n\n```dart\n  initState() {\n    super.initState();\n    controller = new AnimationController(\n        duration: const Duration(seconds: 3), vsync: this);\n    //使用弹性曲线\n    animation=CurvedAnimation(parent: controller, curve: Curves.bounceIn);\n    //图片宽高从0变到300\n    animation = new Tween(begin: 0.0, end: 300.0).animate(animation)\n      ..addListener(() {\n        setState(() {\n        });\n      });\n    //启动动画\n    controller.forward();\n  }\n```\n\n上面代码执行后截取了其中的两帧，效果如图9-1、9-2所示：\n\n![图9-1](../imgs/9-1.png)![图9-2](../imgs/9-2.png)\n\n### 使用AnimatedWidget简化\n\n细心的读者可能已经发现上面示例中通过`addListener()`和`setState()` 来更新UI这一步其实是通用的，如果每个动画中都加这么一句是比较繁琐的。`AnimatedWidget`类封装了调用`setState()`的细节，并允许我们将widget分离出来，重构后的代码如下：\n\n```dart\nclass AnimatedImage extends AnimatedWidget {\n  AnimatedImage({Key key, Animation<double> animation})\n      : super(key: key, listenable: animation);\n\n  Widget build(BuildContext context) {\n    final Animation<double> animation = listenable;\n    return new Center(\n      child: Image.asset(\"imgs/avatar.png\",\n          width: animation.value,\n          height: animation.value\n      ),\n    );\n  }\n}\n\n\nclass ScaleAnimationRoute1 extends StatefulWidget {\n  @override\n  _ScaleAnimationRouteState createState() => new _ScaleAnimationRouteState();\n}\n\nclass _ScaleAnimationRouteState extends State<ScaleAnimationRoute1>\n    with SingleTickerProviderStateMixin {\n\n  Animation<double> animation;\n  AnimationController controller;\n\n  initState() {\n    super.initState();\n    controller = new AnimationController(\n        duration: const Duration(seconds: 3), vsync: this);\n    //图片宽高从0变到300\n    animation = new Tween(begin: 0.0, end: 300.0).animate(controller);\n    //启动动画\n    controller.forward();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return AnimatedImage(animation: animation,);\n  }\n\n  dispose() {\n    //路由销毁时需要释放动画资源\n    controller.dispose();\n    super.dispose();\n  }\n}\n```\n\n\n\n### 用AnimatedBuilder重构\n\n用AnimatedWidget可以从动画中分离出widget，而动画的渲染过程（即设置宽高）仍然在AnimatedWidget中，假设如果我们再添加一个widget透明度变化的动画，那么我们需要再实现一个AnimatedWidget，这样不是很优雅，如果我们能把渲染过程也抽象出来，那就会好很多，而AnimatedBuilder正是将渲染逻辑分离出来, 上面的build方法中的代码可以改为：\n\n```dart\n@override\nWidget build(BuildContext context) {\n  //return AnimatedImage(animation: animation,);\n    return AnimatedBuilder(\n      animation: animation,\n      child: Image.asset(\"images/avatar.png\"),\n      builder: (BuildContext ctx, Widget child) {\n        return new Center(\n          child: Container(\n              height: animation.value, \n              width: animation.value, \n              child: child,\n          ),\n        );\n      },\n    );\n}\n```\n\n上面的代码中有一个迷惑的问题是，`child`看起来像被指定了两次。但实际发生的事情是：将外部引用`child`传递给`AnimatedBuilder`后`AnimatedBuilder`再将其传递给匿名构造器， 然后将该对象用作其子对象。最终的结果是`AnimatedBuilder`返回的对象插入到widget树中。\n\n也许你会说这和我们刚开始的示例差不了多少，其实它会带来三个好处：\n\n1. 不用显式的去添加帧监听器，然后再调用`setState()` 了，这个好处和`AnimatedWidget`是一样的。\n\n2. 动画构建的范围缩小了，如果没有`builder`，`setState()`将会在父组件上下文中调用，这将会导致父组件的`build`方法重新调用；而有了`builder`之后，只会导致动画widget自身的`build`重新调用，避免不必要的rebuild。\n\n3. 通过`AnimatedBuilder`可以封装常见的过渡效果来复用动画。下面我们通过封装一个`GrowTransition`来说明，它可以对子widget实现放大动画：\n\n   ```dart\n   class GrowTransition extends StatelessWidget {\n     GrowTransition({this.child, this.animation});\n   \n     final Widget child;\n     final Animation<double> animation;\n       \n     Widget build(BuildContext context) {\n       return new Center(\n         child: new AnimatedBuilder(\n             animation: animation,\n             builder: (BuildContext context, Widget child) {\n               return new Container(\n                   height: animation.value, \n                   width: animation.value, \n                   child: child\n               );\n             },\n             child: child\n         ),\n       );\n     }\n   }\n   ```\n\n   这样，最初的示例就可以改为：\n\n   ```dart\n   ...\n   Widget build(BuildContext context) {\n       return GrowTransition(\n       child: Image.asset(\"images/avatar.png\"), \n       animation: animation,\n       );\n   }\n   ```\n\n   **Flutter中正是通过这种方式封装了很多动画，如：FadeTransition、ScaleTransition、SizeTransition等，很多时候都可以复用这些预置的过渡类。**\n\n## 9.2.2 动画状态监听\n\n上面说过，我们可以通过`Animation`的`addStatusListener()`方法来添加动画状态改变监听器。Flutter中，有四种动画状态，在`AnimationStatus`枚举类中定义，下面我们逐个说明：\n\n| 枚举值      | 含义             |\n| ----------- | ---------------- |\n| `dismissed` | 动画在起始点停止 |\n| `forward`   | 动画正在正向执行 |\n| `reverse`   | 动画正在反向执行 |\n| `completed` | 动画在终点停止   |\n\n#### 示例\n\n我们将上面图片放大的示例改为先放大再缩小再放大……这样的循环动画。要实现这种效果，我们只需要监听动画状态的改变即可，即：在动画正向执行结束时反转动画，在动画反向执行结束时再正向执行动画。代码如下：\n\n```dart\n  initState() {\n    super.initState();\n    controller = new AnimationController(\n        duration: const Duration(seconds: 1), vsync: this);\n    //图片宽高从0变到300\n    animation = new Tween(begin: 0.0, end: 300.0).animate(controller);\n    animation.addStatusListener((status) {\n      if (status == AnimationStatus.completed) {\n        //动画执行结束时反向执行动画\n        controller.reverse();\n      } else if (status == AnimationStatus.dismissed) {\n        //动画恢复到初始状态时执行动画（正向）\n        controller.forward();\n      }\n    });\n\n    //启动动画（正向）\n    controller.forward();\n  }\n```\n\n"
  },
  {
    "path": "src/v2/chapter9/hero.md",
    "content": "# 9.4 Hero动画\n\nHero指的是可以在路由(页面)之间“飞行”的widget，简单来说Hero动画就是在路由切换时，有一个共享的widget可以在新旧路由间切换。由于共享的widget在新旧路由页面上的位置、外观可能有所差异，所以在路由切换时会从旧路逐渐过渡到新路由中的指定位置，这样就会产生一个Hero动画。\n\n你可能多次看到过 hero 动画。例如，一个路由中显示待售商品的缩略图列表，选择一个条目会将其跳转到一个新路由，新路由中包含该商品的详细信息和“购买”按钮。 在Flutter中将图片从一个路由“飞”到另一个路由称为**hero动画**，尽管相同的动作有时也称为 **共享元素转换**。下面我们通过一个示例来体验一下hero 动画。\n\n> 为什么要将这种可飞行的共享组件称为hero（英雄），有一种说法是说美国文化中的超人是可以飞的，那是美国人心中的大英雄，还有漫威中的超级英雄基本上都是会飞的，所以Flutter开发人员就对这种“会飞的widget”就起了一个富有浪漫主义的名字hero。当然这种说法并非官方解释，但却很有意思。\n\n#### 示例\n\n假设有两个路由A和B，他们的内容交互如下：\n\nA：包含一个用户头像，圆形，点击后跳到B路由，可以查看大图。\n\nB：显示用户头像原图，矩形；\n\n在AB两个路由之间跳转的时候，用户头像会逐渐过渡到目标路由页的头像上，接下来我们先看看代码，然后再解析：\n\n```dart\n// 路由A\nclass HeroAnimationRoute extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return Container(\n      alignment: Alignment.topCenter,\n      child: InkWell(\n        child: Hero(\n          tag: \"avatar\", //唯一标记，前后两个路由页Hero的tag必须相同\n          child: ClipOval(\n            child: Image.asset(\"images/avatar.png\",\n              width: 50.0,\n            ),\n          ),\n        ),\n        onTap: () {\n          //打开B路由  \n          Navigator.push(context, PageRouteBuilder(\n              pageBuilder: (BuildContext context, Animation animation,\n                  Animation secondaryAnimation) {\n                return new FadeTransition(\n                  opacity: animation,\n                  child: Scaffold(\n                    appBar: AppBar(\n                      title: Text(\"原图\"),\n                    ),\n                    body: HeroAnimationRouteB(),\n                  ),\n                );\n              })\n          );\n        },\n      ),\n    );\n  }\n}\n```\n\n路由B:\n\n```dart\nclass HeroAnimationRouteB extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return Center(\n      child: Hero(\n          tag: \"avatar\", //唯一标记，前后两个路由页Hero的tag必须相同\n          child: Image.asset(\"images/avatar.png\"),\n      ),\n    );\n  }\n}\n```\n\n我们可以看到，实现Hero动画只需要用`Hero`组件将要共享的widget包装起来，并提供一个相同的tag即可，中间的过渡帧都是Flutter Framework自动完成的。必须要注意， 前后路由页的共享`Hero`的tag必须是相同的，Flutter Framework内部正是通过tag来确定新旧路由页widget的对应关系的。\n\nHero动画的原理比较简单，Flutter Framework知道新旧路由页中共享元素的位置和大小，所以根据这两个端点，在动画执行过程中求出过渡时的插值（中间态）即可，而感到幸运的是，这些事情不需要我们自己动手，Flutter已经帮我们做了！\n\n"
  },
  {
    "path": "src/v2/chapter9/index.md",
    "content": "## 简介\n\n精心设计的动画会让用户界面感觉更直观、流畅，能改善用户体验。 Flutter可以轻松实现各种动画类型，对于许多widget，特别是[Material Design widgets](https://flutter.io/docs/reference/widgets/material)，都带有在其设计规范中定义的标准动画效果(但也可以自定义这些效果)。本章将详细介绍Flutter的动画系统，并会通过几个小实例来演示，以帮助开发者迅速理解并掌握动画的开发流程与原理。\n\n## 本章目录\n\n* [9.1：Flutter动画简介](intro.md)\n* [9.2：动画结构](animation_structure.md)\n* [9.3：自定义路由过渡动画](route_transition.md) \n* [9.4：Hero动画](hero.md) \n* [9.5：交织动画](stagger_animation.md) \n* [9.6：通用“动画切换”组件（AnimatedSwitcher）](animated_switcher.md) \n* [9.7：动画过渡组件](animated_widgets.md) \n"
  },
  {
    "path": "src/v2/chapter9/intro.md",
    "content": "# 9.1 Flutter动画简介\n\n\n\n在任何系统的UI框架中，动画实现的原理都是相同的，即：在一段时间内，快速地多次改变UI外观；由于人眼会产生视觉暂留，所以最终看到的就是一个“连续”的动画，这和电影的原理是一样的。我们将UI的一次改变称为一个动画帧，对应一次屏幕刷新，而决定动画流畅度的一个重要指标就是帧率FPS（Frame Per Second），即每秒的动画帧数。很明显，帧率越高则动画就会越流畅！一般情况下，对于人眼来说，动画帧率超过16FPS，就比较流畅了，超过32FPS就会非常的细腻平滑，而超过32FPS，人眼基本上就感受不到差别了。由于动画的每一帧都是要改变UI输出，所以在一个时间段内连续的改变UI输出是比较耗资源的，对设备的软硬件系统要求都较高，所以在UI系统中，动画的平均帧率是重要的性能指标，而在Flutter中，理想情况下是可以实现60FPS的，这和原生应用能达到的帧率是基本是持平的。\n\n### Flutter中动画抽象\n\n为了方便开发者创建动画，不同的UI系统对动画都进行了一些抽象，比如在Android中可以通过XML来描述一个动画然后设置给View。Flutter中也对动画进行了抽象，主要涉及Animation、Curve、Controller、Tween这四个角色，它们一起配合来完成一个完整动画，下面我们一一来介绍它们。\n\n### Animation\n\n`Animation`是一个抽象类，它本身和UI渲染没有任何关系，而它主要的功能是保存动画的插值和状态；其中一个比较常用的`Animation`类是`Animation<double>`。`Animation`对象是一个在一段时间内依次生成一个区间(Tween)之间值的类。`Animation`对象在整个动画执行过程中输出的值可以是线性的、曲线的、一个步进函数或者任何其他曲线函数等等，这由`Curve`来决定。 根据`Animation`对象的控制方式，动画可以正向运行（从起始状态开始，到终止状态结束），也可以反向运行，甚至可以在中间切换方向。`Animation`还可以生成除`double`之外的其他类型值，如：`Animation<Color>` 或` Animation<Size>`。在动画的每一帧中，我们可以通过`Animation`对象的`value`属性获取动画的当前状态值。\n\n#### 动画通知\n\n我们可以通过`Animation`来监听动画每一帧以及执行状态的变化，`Animation`有如下两个方法：\n\n1. `addListener()`；它可以用于给`Animation`添加帧监听器，在每一帧都会被调用。帧监听器中最常见的行为是改变状态后调用`setState()`来触发UI重建。\n2. `addStatusListener()`；它可以给`Animation`添加“动画状态改变”监听器；动画开始、结束、正向或反向（见`AnimationStatus`定义）时会调用状态改变的监听器。\n\n读者在此只需要知道帧监听器和状态监听器的区别，在后面的章节中我们将会举例说明。\n\n### Curve\n\n动画过程可以是匀速的、匀加速的或者先加速后减速等。Flutter中通过`Curve`（曲线）来描述动画过程，我们把匀速动画称为线性的(Curves.linear)，而非匀速动画称为非线性的。\n\n我们可以通过`CurvedAnimation`来指定动画的曲线，如：\n\n```dart\nfinal CurvedAnimation curve =\n    new CurvedAnimation(parent: controller, curve: Curves.easeIn);\n```\n\n`CurvedAnimation`和`AnimationController`（下面介绍）都是`Animation<double>`类型。`CurvedAnimation`可以通过包装`AnimationController`和`Curve`生成一个新的动画对象 ，我们正是通过这种方式来将动画和动画执行的曲线关联起来的。我们指定动画的曲线为` Curves.easeIn`，它表示动画开始时比较慢，结束时比较快。 [Curves](https://docs.flutter.io/flutter/animation/Curves-class.html) 类是一个预置的枚举类，定义了许多常用的曲线，下面列几种常用的：\n\n| Curves曲线 | 动画过程                     |\n| ---------- | ---------------------------- |\n| linear     | 匀速的                       |\n| decelerate | 匀减速                       |\n| ease       | 开始加速，后面减速           |\n| easeIn     | 开始慢，后面快               |\n| easeOut    | 开始快，后面慢               |\n| easeInOut  | 开始慢，然后加速，最后再减速 |\n\n除了上面列举的， [Curves](https://docs.flutter.io/flutter/animation/Curves-class.html) 类中还定义了许多其它的曲线，在此便不一一介绍，读者可以自行查看Curves类定义。\n\n当然我们也可以创建自己Curve，例如我们定义一个正弦曲线：\n\n```dart\nclass ShakeCurve extends Curve {\n  @override\n  double transform(double t) {\n    return math.sin(t * math.PI * 2);\n  }\n}\n```\n\n\n\n### AnimationController\n\n`AnimationController`用于控制动画，它包含动画的启动`forward()`、停止`stop()` 、反向播放 `reverse()`等方法。`AnimationController`会在动画的每一帧，就会生成一个新的值。默认情况下，`AnimationController`在给定的时间段内线性的生成从0.0到1.0（默认区间）的数字。 例如，下面代码创建一个`Animation`对象（但不会启动动画）：\n\n```dart\nfinal AnimationController controller = new AnimationController(\n    duration: const Duration(milliseconds: 2000), vsync: this);\n```\n\n`AnimationController`生成数字的区间可以通过`lowerBound`和`upperBound`来指定，如：\n\n```dart\nfinal AnimationController controller = new AnimationController( \n duration: const Duration(milliseconds: 2000), \n lowerBound: 10.0,\n upperBound: 20.0,\n vsync: this\n);\n```\n\n`AnimationController`派生自`Animation<double>`，因此可以在需要`Animation`对象的任何地方使用。 但是，`AnimationController`具有控制动画的其他方法，例如`forward()`方法可以启动正向动画，`reverse()`可以启动反向动画。在动画开始执行后开始生成动画帧，屏幕每刷新一次就是一个动画帧，在动画的每一帧，会随着根据动画的曲线来生成当前的动画值（`Animation.value`），然后根据当前的动画值去构建UI，当所有动画帧依次触发时，动画值会依次改变，所以构建的UI也会依次变化，所以最终我们可以看到一个完成的动画。 另外在动画的每一帧，`Animation`对象会调用其帧监听器，等动画状态发生改变时（如动画结束）会调用状态改变监听器。\n\n`duration`表示动画执行的时长，通过它我们可以控制动画的速度。\n\n> **注意**： 在某些情况下，动画值可能会超出`AnimationController`的[0.0，1.0]的范围，这取决于具体的曲线。例如，`fling()`函数可以根据我们手指滑动（甩出）的速度(velocity)、力量(force)等来模拟一个手指甩出动画，因此它的动画值可以在[0.0，1.0]范围之外 。也就是说，根据选择的曲线，`CurvedAnimation`的输出可以具有比输入更大的范围。例如，Curves.elasticIn等弹性曲线会生成大于或小于默认范围的值。\n\n#### Ticker\n\n当创建一个`AnimationController`时，需要传递一个`vsync`参数，它接收一个`TickerProvider`类型的对象，它的主要职责是创建`Ticker`，定义如下：\n\n```dart\nabstract class TickerProvider {\n  //通过一个回调创建一个Ticker\n  Ticker createTicker(TickerCallback onTick);\n}\n```\n\nFlutter应用在启动时都会绑定一个`SchedulerBinding`，通过`SchedulerBinding`可以给每一次屏幕刷新添加回调，而`Ticker`就是通过`SchedulerBinding`来添加屏幕刷新回调，这样一来，每次屏幕刷新都会调用`TickerCallback`。使用`Ticker`(而不是`Timer`)来驱动动画会防止屏幕外动画（动画的UI不在当前屏幕时，如锁屏时）消耗不必要的资源，因为Flutter中屏幕刷新时会通知到绑定的`SchedulerBinding`，而`Ticker`是受`SchedulerBinding`驱动的，由于锁屏后屏幕会停止刷新，所以`Ticker`就不会再触发。\n\n通常我们会将`SingleTickerProviderStateMixin`添加到`State`的定义中，然后将State对象作为`vsync`的值，这在后面的例子中可以见到。\n\n### Tween\n\n默认情况下，`AnimationController`对象值的范围是[0.0，1.0]。如果我们需要构建UI的动画值在不同的范围或不同的数据类型，则可以使用`Tween`来添加映射以生成不同的范围或数据类型的值。例如，像下面示例，`Tween`生成[-200.0，0.0]的值：\n\n```dart\nfinal Tween doubleTween = new Tween<double>(begin: -200.0, end: 0.0);\n```\n\n`Tween`构造函数需要`begin`和`end`两个参数。`Tween`的唯一职责就是定义从输入范围到输出范围的映射。输入范围通常为[0.0，1.0]，但这不是必须的，我们可以自定义需要的范围。\n\n`Tween`继承自`Animatable<T>`，而不是继承自`Animation<T>`，`Animatable`中主要定义动画值的映射规则。\n\n下面我们看一个ColorTween将动画输入范围映射为两种颜色值之间过渡输出的例子：\n\n```dart\nfinal Tween colorTween =\n    new ColorTween(begin: Colors.transparent, end: Colors.black54);\n```\n\n\n\n`Tween`对象不存储任何状态，相反，它提供了`evaluate(Animation<double> animation)`方法，它可以获取动画当前映射值。 `Animation`对象的当前值可以通过`value()`方法取到。`evaluate`函数还执行一些其它处理，例如分别确保在动画值为0.0和1.0时返回开始和结束状态。\n\n#### Tween.animate\n\n要使用Tween对象，需要调用其`animate()`方法，然后传入一个控制器对象。例如，以下代码在500毫秒内生成从0到255的整数值。\n\n```dart\nfinal AnimationController controller = new AnimationController(\n    duration: const Duration(milliseconds: 500), vsync: this);\nAnimation<int> alpha = new IntTween(begin: 0, end: 255).animate(controller);\n```\n\n注意`animate()`返回的是一个`Animation`，而不是一个`Animatable`。\n\n以下示例构建了一个控制器、一条曲线和一个Tween：\n\n```dart\nfinal AnimationController controller = new AnimationController(\n    duration: const Duration(milliseconds: 500), vsync: this);\nfinal Animation curve =\n    new CurvedAnimation(parent: controller, curve: Curves.easeOut);\nAnimation<int> alpha = new IntTween(begin: 0, end: 255).animate(curve);\n```\n\n"
  },
  {
    "path": "src/v2/chapter9/route_transition.md",
    "content": "\n# 9.3 自定义路由切换动画\n\n我们在第二章“路由管理”一节中讲过：Material组件库中提供了一个`MaterialPageRoute`组件，它可以使用和平台风格一致的路由切换动画，如在iOS上会左右滑动切换，而在Android上会上下滑动切换。现在，我们如果在Android上也想使用左右切换风格，该怎么做？一个简单的作法是可以直接使用`CupertinoPageRoute`，如：\n\n```dart\n Navigator.push(context, CupertinoPageRoute(  \n   builder: (context)=>PageB(),\n ));\n```\n\n`CupertinoPageRoute`是Cupertino组件库提供的iOS风格的路由切换组件，它实现的就是左右滑动切换。那么我们如何来自定义路由切换动画呢？答案就是`PageRouteBuilder`。下面我们来看看如何使用`PageRouteBuilder`来自定义路由切换动画。例如我们想以渐隐渐入动画来实现路由过渡，实现代码如下：\n\n```dart\nNavigator.push(\n  context,\n  PageRouteBuilder(\n    transitionDuration: Duration(milliseconds: 500), //动画时间为500毫秒\n    pageBuilder: (BuildContext context, Animation animation,\n        Animation secondaryAnimation) {\n      return new FadeTransition(\n        //使用渐隐渐入过渡,\n        opacity: animation,\n        child: PageB(), //路由B\n      );\n    },\n  ),\n);\n```\n\n我们可以看到` pageBuilder` 有一个`animation`参数，这是Flutter路由管理器提供的，在路由切换时` pageBuilder`在每个动画帧都会被回调，因此我们可以通过`animation`对象来自定义过渡动画。\n\n无论是`MaterialPageRoute`、`CupertinoPageRoute`，还是`PageRouteBuilder`，它们都继承自PageRoute类，而`PageRouteBuilder`其实只是`PageRoute`的一个包装，我们可以直接继承`PageRoute`类来实现自定义路由，上面的例子可以通过如下方式实现：\n\n1. 定义一个路由类`FadeRoute`\n\n   ```dart\n   class FadeRoute extends PageRoute {\n     FadeRoute({\n       @required this.builder,\n       this.transitionDuration = const Duration(milliseconds: 300),\n       this.opaque = true,\n       this.barrierDismissible = false,\n       this.barrierColor,\n       this.barrierLabel,\n       this.maintainState = true,\n     });\n   \n     final WidgetBuilder builder;\n   \n     @override\n     final Duration transitionDuration;\n   \n     @override\n     final bool opaque;\n   \n     @override\n     final bool barrierDismissible;\n   \n     @override\n     final Color barrierColor;\n   \n     @override\n     final String barrierLabel;\n   \n     @override\n     final bool maintainState;\n   \n     @override\n     Widget buildPage(BuildContext context, Animation<double> animation,\n         Animation<double> secondaryAnimation) => builder(context);\n   \n     @override\n     Widget buildTransitions(BuildContext context, Animation<double> animation,\n         Animation<double> secondaryAnimation, Widget child) {\n        return FadeTransition( \n          opacity: animation,\n          child: builder(context),\n        );\n     }\n   }\n   ```\n\n2. 使用`FadeRoute`\n\n   ```dart\n   Navigator.push(context, FadeRoute(builder: (context) {\n     return PageB();\n   }));\n   ```\n\n虽然上面的两种方法都可以实现自定义切换动画，但实际使用时应优先考虑使用PageRouteBuilder，这样无需定义一个新的路由类，使用起来会比较方便。但是有些时候`PageRouteBuilder`是不能满足需求的，例如在应用过渡动画时我们需要读取当前路由的一些属性，这时就只能通过继承`PageRoute`的方式了，举个例子，假如我们只想在打开新路由时应用动画，而在返回时不使用动画，那么我们在构建过渡动画时就必须判断当前路由`isActive`属性是否为`true`，代码如下：\n\n```dart\n@override\nWidget buildTransitions(BuildContext context, Animation<double> animation,\n    Animation<double> secondaryAnimation, Widget child) {\n //当前路由被激活，是打开新路由\n if(isActive) {\n   return FadeTransition(\n     opacity: animation,\n     child: builder(context),\n   );\n }else{\n   //是返回，则不应用过渡动画\n   return Padding(padding: EdgeInsets.zero);\n }\n}\n```\n\n关于路由参数的详细信息读者可以自行查阅API文档，比较简单，不再赘述。\n"
  },
  {
    "path": "src/v2/chapter9/stagger_animation.md",
    "content": "\n# 9.5 交织动画\n\n有些时候我们可能会需要一些复杂的动画，这些动画可能由一个动画序列或重叠的动画组成，比如：有一个柱状图，需要在高度增长的同时改变颜色，等到增长到最大高度后，我们需要在X轴上平移一段距离。可以发现上述场景在不同阶段包含了多种动画，要实现这种效果，使用交织动画（Stagger Animation）会非常简单。交织动画需要注意以下几点：\n\n1. 要创建交织动画，需要使用多个动画对象（`Animation`）。\n2. 一个`AnimationController`控制所有的动画对象。\n3. 给每一个动画对象指定时间间隔（Interval）\n\n所有动画都由同一个[AnimationController](https://docs.flutter.io/flutter/animation/AnimationController-class.html)驱动，无论动画需要持续多长时间，控制器的值必须在0.0到1.0之间，而每个动画的间隔（Interval）也必须介于0.0和1.0之间。对于在间隔中设置动画的每个属性，需要分别创建一个[Tween](https://docs.flutter.io/flutter/animation/Tween-class.html) 用于指定该属性的开始值和结束值。也就是说0.0到1.0代表整个动画过程，我们可以给不同动画指定不同的起始点和终止点来决定它们的开始时间和终止时间。\n\n### 示例\n\n下面我们看一个例子，实现一个柱状图增长的动画：\n\n1. 开始时高度从0增长到300像素，同时颜色由绿色渐变为红色；这个过程占据整个动画时间的60%。\n2. 高度增长到300后，开始沿X轴向右平移100像素；这个过程占用整个动画时间的40%。\n\n我们将执行动画的Widget分离出来：\n\n```dart\nclass StaggerAnimation extends StatelessWidget {\n  StaggerAnimation({ Key key, this.controller }): super(key: key){\n    //高度动画\n    height = Tween<double>(\n      begin:.0 ,\n      end: 300.0,\n    ).animate(\n      CurvedAnimation(\n        parent: controller,\n        curve: Interval(\n          0.0, 0.6, //间隔，前60%的动画时间\n          curve: Curves.ease,\n        ),\n      ),\n    );\n\n    color = ColorTween(\n      begin:Colors.green ,\n      end:Colors.red,\n    ).animate(\n      CurvedAnimation(\n        parent: controller,\n        curve: Interval(\n          0.0, 0.6,//间隔，前60%的动画时间\n          curve: Curves.ease,\n        ),\n      ),\n    );\n\n    padding = Tween<EdgeInsets>(\n      begin:EdgeInsets.only(left: .0),\n      end:EdgeInsets.only(left: 100.0),\n    ).animate(\n      CurvedAnimation(\n        parent: controller,\n        curve: Interval(\n          0.6, 1.0, //间隔，后40%的动画时间\n          curve: Curves.ease,\n        ),\n      ),\n    );\n  }\n\n\n  final Animation<double> controller;\n  Animation<double> height;\n  Animation<EdgeInsets> padding;\n  Animation<Color> color;\n\n  Widget _buildAnimation(BuildContext context, Widget child) {\n    return Container(\n      alignment: Alignment.bottomCenter,\n      padding:padding.value ,\n      child: Container(\n        color: color.value,\n        width: 50.0,\n        height: height.value,\n      ),\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return AnimatedBuilder(\n      builder: _buildAnimation,\n      animation: controller,\n    );\n  }\n}\n```\n\n`StaggerAnimation`中定义了三个动画，分别是对`Container`的`height`、`color`、`padding`属性设置的动画，然后通过`Interval`来为每个动画指定在整个动画过程中的起始点和终点。下面我们来实现启动动画的路由：\n\n```dart\nclass StaggerRoute extends StatefulWidget {\n  @override\n  _StaggerRouteState createState() => _StaggerRouteState();\n}\n\nclass _StaggerRouteState extends State<StaggerRoute> with TickerProviderStateMixin {\n  AnimationController _controller;\n\n  @override\n  void initState() {\n    super.initState();\n\n    _controller = AnimationController(\n        duration: const Duration(milliseconds: 2000),\n        vsync: this\n    );\n  }\n\n\n  Future<Null> _playAnimation() async {\n    try {\n      //先正向执行动画\n      await _controller.forward().orCancel;\n      //再反向执行动画\n      await _controller.reverse().orCancel;\n    } on TickerCanceled {\n      // the animation got canceled, probably because we were disposed\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return  GestureDetector(\n      behavior: HitTestBehavior.opaque,\n      onTap: () {\n        _playAnimation();\n      },\n      child: Center(\n        child: Container(\n          width: 300.0,\n          height: 300.0,\n          decoration: BoxDecoration(\n            color: Colors.black.withOpacity(0.1),\n            border: Border.all(\n              color:  Colors.black.withOpacity(0.5),\n            ),\n          ),\n          //调用我们定义的交织动画Widget\n          child: StaggerAnimation(\n              controller: _controller\n          ),\n        ),\n      ),\n    );\n  }\n}\n```\n执行效果如图，点击图9-3灰色矩形，就可以看到整个动画效果，图9-4是动画执行过程中的一帧。\n\n![图9-3](../imgs/9-3.png)![图9-4](../imgs/9-4.png)\n\n"
  },
  {
    "path": "src/v2/gitbook/pub.js",
    "content": "!function(t,n){for(var r in n)t[r]=n[r]}(window,function(t){function n(o){if(r[o])return r[o].exports;var e=r[o]={i:o,l:!1,exports:{}};return t[o].call(e.exports,e,e.exports,n),e.l=!0,e.exports}var r={};return n.m=t,n.c=r,n.i=function(t){return t},n.d=function(t,r,o){n.o(t,r)||Object.defineProperty(t,r,{configurable:!1,enumerable:!0,get:o})},n.n=function(t){var r=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(r,\"a\",r),r},n.o=function(t,n){return Object.prototype.hasOwnProperty.call(t,n)},n.p=\"\",n(n.s=2)}([function(t,n,r){\"use strict\";function o(t,n){var r={};for(var o in t)r[o]=t[o];return r.target=r.currentTarget=n,r}function e(t){function n(n){return function(){var r=this.hasOwnProperty(n+\"_\")?this[n+\"_\"]:this.xhr[n],o=(t[n]||{}).getter;return o&&o(r,this)||r}}function r(n){return function(r){var e=this.xhr,i=this,u=t[n];if(\"on\"===n.substring(0,2))i[n+\"_\"]=r,e[n]=function(u){u=o(u,i),t[n]&&t[n].call(i,e,u)||r.call(i,u)};else{var c=(u||{}).setter;r=c&&c(r,i)||r,this[n+\"_\"]=r;try{e[n]=r}catch(t){}}}}function e(n){return function(){var r=[].slice.call(arguments);if(t[n]){var o=t[n].call(this,r,this.xhr);if(o)return o}return this.xhr[n].apply(this.xhr,r)}}return window[c]=window[c]||XMLHttpRequest,XMLHttpRequest=function(){var t=new window[c];for(var o in t){var i=\"\";try{i=u(t[o])}catch(t){}\"function\"===i?this[o]=e(o):Object.defineProperty(this,o,{get:n(o),set:r(o),enumerable:!0})}var f=this;t.getProxy=function(){return f},this.xhr=t},window[c]}function i(){window[c]&&(XMLHttpRequest=window[c]),window[c]=void 0}Object.defineProperty(n,\"__esModule\",{value:!0});var u=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&\"function\"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?\"symbol\":typeof t};n.configEvent=o,n.hook=e,n.unHook=i;var c=\"_rxhr\"},,function(t,n,r){\"use strict\";Object.defineProperty(n,\"__esModule\",{value:!0}),n.ah=void 0;var o=r(0);n.ah={hook:o.hook,unHook:o.unHook}}]));\n\nvar _hmt = _hmt || [];\n$(\"#bd\").remove();\n(function () {\n    var hm = document.createElement(\"script\");\n    hm.src = \"https://hm.baidu.com/hm.js?170231fea4f81697eb046edc1a91fe5b\";\n    var s = document.getElementsByTagName(\"script\")[0];\n    hm.id = \"bd\"\n    s.parentNode.insertBefore(hm, s);\n})();\nvar timer;\nfunction init() {\n    var p = location.pathname;\n    if (p[p.length - 1] === '/') {\n        p += \"index.md\"\n    } else {\n        p = p.split(\".\")[0] + \".md\";\n    }\n    p = \"https://github.com/flutterchina/flutter-in-action/blob/master/docs\" + p;\n    $(\".pull-right .fa-edit\").parent(\"a\").attr(\"href\", p);\n    $(\"table\").wrap(\"<div style='overflow: auto'></div>\");\n    //百度统计\n    var e = /([http|https]:\\/\\/[a-zA-Z0-9\\_\\.]+\\.baidu\\.com)/gi, r = window.location.href,\n        t = document.referrer;\n    if (!e.test(r)) {\n        var o = \"https://sp0.baidu.com/9_Q4simg2RQJ8t7jm9iCKT-xh_/s.gif\";\n        t ? (o += \"?r=\" + encodeURIComponent(document.referrer), r && (o += \"&l=\" + r)) : r && (o += \"?l=\" + r);\n        var i = new Image;\n        i.src = o\n    }\n    $(\".copyright,.maoyun\").remove();\n    $(\"<div class='copyright'> 版权所有，禁止私自转发、克隆网站。</div><div style='text-align: center' class='f-links'><a onclick='buy(\\\"link\\\")' href='https://item.jd.com/12816296.html' title='点击购买' target='_blank' > 购买实体书 </a> | <a  href='https://flutterchina.club/docs'> Flutter中文网 </a></div>\").appendTo(\".page-inner\");\n    $(\"<div style='text-align: center' class='maoyun'> <span style='position: relative; top: -3px; left: -4px'>感谢</span><a href='https://www.maoyuncloud.com/' target='_blank'><img src=//pcdn.flutterchina.club/imgs/maoyun.png height='20'></a></div>\").appendTo(\".page-inner\");\n}\n\nfunction _track(p,url) {\n    _hmt.push(['_trackEvent', 'ad', 'click', p]);\n    setTimeout(function () {\n        location.href = url\n    }, 100);\n}\n\nfunction buy(p){\n    _hmt.push(['_trackEvent', 'buy', 'click', p]);\n}\n\ninit();\n\nah.hook({\n    open: function (arg, xhr) {\n        if (location.hostname !== 'localhost') {\n            if (arg[1][0] === '.') arg[1] = arg[1].slice(1);\n            arg[1] = \"https://pcdn.flutterchina.club\" + arg[1].replace(\".html\", \".1\")\n        }\n    },\n    setRequestHeader: function (arg) {\n        if (arg[0] !== 'Accept') return true;\n    },\n    onload:function(xhr){\n        setTimeout(function () {\n            if ( $(\"#book-search-results .ad\").length === 0) {\n                $(\".ad\").clone().show().prependTo(\"#book-search-results\")\n            }\n            var extension=xhr.responseURL.split(\".\").pop()\n            if(extension!=='json'){\n                console.log(\"jump:\"+location.href)\n                _hmt.push(['_trackPageview', location.pathname]);\n                init()\n            }\n        });\n    }\n})\n"
  },
  {
    "path": "src/v2/img_des.txt",
    "content": "1-1  Flutter框架图\n1-2  Flutter安装包下载\n1-3  Android Studio工具栏\n1-4  应用首页\n1-5  登录Xcode\n1-6  添加信任\n1-7  验证bundle id是否唯一\n1-8  缺少依赖报错\n1-9  安装依赖\n1-10 下载依赖失败\n\n2-1  计数器示例\n2-2  添加打开新路由按钮\n2-3  新路由页\n2-4  路由传参示例\n2-5  Pub上的包信息\n2-6  在.yaml中添加包依赖\n2-7  热重载\n2-8  设置APP图标-Android\n2-9  设置APP图标-iOS\n2-10 应用启动页\n2-11 Xcode中设置启动页\n2-12 Dart单线程模型\n\n3-1  StatelessWidget示例\n3-1-1  通过Context查找父Widget\n3-1-2  显示显示SnackBar\n3-2  StatefulWidget生命周期图\n3-3  Cupertino组件示例\n3-4  状态管理示例\n3-5  Text示例\n3-6  Text居中对齐示例\n3-7  TextStyle示例\n3-8  TextSpan示例\n3-9  DefaultTextStyle示例\n3-10 RaisedButton示例\n3-11 FlatButton示例\n3-12 OutlineButton示例\n3-13 IconButton示例\n3-14 带图标的按钮\n3-15 两边圆角的按钮\n3-16 两边圆角、带阴影的按钮\n3-17 Image示例\n3-18 Image各种不同的fit效果示例\n3-19 Image colorBlendMode效果示例\n3-20 Image repeat效果示例\n3-21 字体图标示例\n3-22 自定义字体图标示例\n3-23 单选、复选框示例\n3-24 Android键盘搜索模式\n3-25 登录输入框示例\n3-26 输入框内容选中示例\n3-27 输入框焦点控制示例\n3-28 自定义输入框样式示例\n3-29 表单预验证示例\n3-30 LinearProgressIndicator示例\n3-31 CircularProgressIndicator示例\n3-32 自定义进度指示器尺寸示例\n3-33 椭圆形进度指示器示例\n\n\n4-1  线性布局示例\n4-2  Column示例\n4-3  Column嵌套示例\n4-4  Column和Expanded组件示例\n4-5  弹性布局示例\n4-6  溢出示例\n4-7  Wrap示例\n4-8  Flow示例\n4-9  Stack、Positioned示例（一）\n4-10 Stack、Positioned示例（二）\n4-11 Align示例\n4-12 Alignment效果示例\n4-13 FractionalOffset效果示例\n4-14 缩放因子效果对比\n\n\n5-1  Padding示例\n5-2  ConstrainedBox示例\n5-3  SizedBox示例\n5-4  多重限制示例（一）\n5-5  多重限制示例（二）\n5-6  UnconstrainedBox示例\n5-7  导航栏自定义Loading大小（一）\n5-8  导航栏自定义Loading大小（二）\n5-9  DecoratedBox示例\n5-10 Transform倾斜变换示例\n5-11 Transform平移变换示例\n5-12 Transform旋转变换示例\n5-13 Transform缩放变换示例\n5-14 Transform变换不影响组件位置\n5-15 RotatedBox示例\n5-16 Container示例\n5-17 Padding和Margin对比示例\n5-18 包含顶部和底部导航的主页\n5-19 抽屉菜单\n5-20 自定义菜单图标\n5-21 TabBar示例\n5-22 完整Tab示例\n5-23 \"打洞\"效果的底部导航栏示例\n5-24 剪裁效果示例\n5-25 自定义剪裁区域示例\n\n\n6-1  SingleChildScrollView示例\n6-2  ListView.builder示例\n6-3  ListView.separated示例\n6-4  加载更多\n6-5  没有更多\n6-6  添加列表头\n6-7  指定列表高度\n6-8  动态计算列表高度\n6-9  GridView示例一\n6-10 GridView示例二\n6-11 StaggeredGridView\n6-12 CustomScrollView示例\n6-13 CustomScrollView示例\n6-14 未显示回到顶部按钮\n6-15 显示返回顶部按钮\n6-16 监听滚动通知\n\n7-1  InheritedWidget版的计数器示例\n7-2  Provider示例\n7-3  Provider原理图\n7-4  前景色自适应的NavBar\n7-5  MaterialColor示例\n7-6  青色主题\n7-7  蓝色主题\n7-8  加载中\n7-9  加载成功\n7-10 删除确认对话框\n7-11 SimpleDialog示例\n7-12 Dialog示例\n7-13 自定义对话框样式\n7-14 带复选框的对话框\n7-15 复选框可选中\n7-16 Material风格的底部菜单列表模态对话框\n7-17 showBottomSheet示例\n7-18 Loading框\n7-19 Loading框(自定义宽度)\n7-20 Material风格的日历选择框\n7-21 iOS风格的日历选择框\n\n8-1  Listener示例\n8-2  手势检测（点击、双击、长按）示例\n8-3  拖动（任意方向）示例\n8-4  缩放示例\n8-5  GestureRecognizer示例\n8-6  Notification示例\n\n9-1  放大动画（一）\n9-2  放大动画（二）\n9-3  交织动画（一）\n9-4  交织动画的一帧\n9-5  左出右进\n9-6  左出右进\n9-7  上入下出\n9-8  AnimatedDecoratedBox点击前\n9-9  AnimatedDecoratedBox过渡中的一帧\n9-10 动画过渡组件示例\n\n\n10-1  渐变按钮示例\n10-2  TurnBox示例\n10-3  五子棋盘示例\n\n11-1  请求百度首页\n11-2  Websocket示例\n11-3  Socket示例\n11-4  IDE错误提示\n\n12-1  创建Package工程\n12-2  Package目录结构\n12-3  MessageChannel\n12-4  相机示例\n12-5  Platform View 示例\n\n13-1  Android系统语言设置\n\n14-0  Flutter中的三棵树\n14-1  自定义UI框架（一）\n14-2  自定义UI框架（二）\n14-3  布局溢出提示\n14-4  MyImage示例\n\n15-1 主页（未登录）\n15-2 主页（已登录）\n15-3 抽屉菜单（未登录）\n15-4 抽屉菜单（已登录）\n15-5 登录页\n15-6 语言选择页（中文简体）\n15-7 语言选择页（英文）\n15-8 主题切换页\n\n"
  },
  {
    "path": "src/v2/imgs/index.md",
    "content": "\n# 前言\n\n本书是第一本系统介绍Flutter技术的中文书籍，它是Flutter中文社区发起的开源项目之一，旨在帮助开发者入门，系统地、循序渐进的了解Flutter。\n\n> 本书官网访问量较大，由于服务器配置有限，所以某些时段访问本站可能略有延迟，如果延迟较久，读者可以直接在[Github上阅读本书](https://github.com/flutterchina/flutter-in-action/blob/master/docs/SUMMARY.md)。\n\n\n## 缘起\n\n在全球，随着Flutter被越来越多的知名公司应用在自己的商业APP中，Flutter这门新技术也逐渐进入了移动开发者的视野，尤其是当Google在2018年IO大会上发布了第一个Preview版本后，国内刮起来一股学习Flutter的热潮。\n\n在Flutter发布之初，当时，我在看完Flutter原理介绍后，就对它产生了浓厚的兴趣。当时笔者身边也一些人比较关注Flutter，我也被经常问到关于Flutter的一些问题，比如Flutter怎么样？和RN有什么区别？Flutter为什么要用Dart？当时也听到了一些批评的声音，比如有些人说Flutter只是重复造轮子，没啥亮点、Flutter最大的缺点就是使用了Dart语言等。在听到这些问题及论调后，我深知这是对Flutter的不了解而造成的，这和当时国内缺乏Flutter中文文档和教程有直接关系，很多人对Flutter的了解都只停留在Google的发布会介绍（有中文翻译）。\n\n在笔者深入的了解Flutter后，深知Flutter必将是一个会改变移动开发格局的里程碑级作品，它从设计之初就对性能和开发效率兼顾，并且借鉴了React（一个Web开发框架）的响应式的UI框架设计思想等，总之，很难用一两句话说完Flutter的优点，同时我也很快成为了Flutter的路转粉。\n\n为了更好的帮助中国开发者了解这门新技术，我在2018年初开始翻译Flutter官网文档，同年4月份上线了[Flutter中文网](https://flutterchina.club/)，上线后反响很强烈，Flutter中文网也很快被传播开，百度搜索排名迅速蹿升到前三，截止目前，Flutter中文官网日PV在7万左右，每日独立访问人数近一万多。\n\n虽然Flutter中文网给中国开发者提供了很好的第一手了解Flutter的资料，但是笔者还会经常遇到一些对Flutter技术处于围观而不愿尝试的开发者。这主要是因为当时Flutter在国内没有成功案例，再加上新技术都有学习成本，所以即使有文档，也会有一些开发者犹豫是否来学习。为了解决这部分开发者的疑虑，我就想如果能用Flutter开发一个完整的APP发布到应用商店，这样开发者就可以在犹豫的时候可以先实际感受一下Flutter应用，这样有个直观的了解后，就会容易做出判断，为此，我开发了[Gitme](https://flutterchina.club/app/gm.html)，它是一个Github客户端，它支持了源码浏览、Issue、Label等Github的大多数功能，到目前为止，通过Gitme登录过Github账号的用户有8000多人，日活用户有1000人。更重要的是，有很多人正是用了Gitme后，才来学Flutter的。\n\n无论是做Flutter中文网，还是写Gitme，主要目标都是帮助开发者学习Flutter，同时消除围观开发者疑虑。但当开发者们真正开始动手时，Flutter的生态问题就变得尤为突出。由于在2018年初Flutter刚起步时，很多基础的包和库都是空白，少数已有的一些库也大都是预览版（未到1.0），存在很多bug。这个状况不是一两个人花一两天能搞定的，这是需要聚整个Flutter开发者社群之力，耗费数年时间才可能有所改善。因此，在2018年4月份，我以Flutter中文网名义发起了Flutter开源计划，该计划主要是开发一些常用的包来丰富Flutter生态，帮助开发者提高开发效率。自在github建立[Flutter中文开发者社区账号](https://github.com/flutterchina)以来，前后开源了dio、cookieJar、flukit等多个项目，而dio在开源两周后，就迅速成为Flutter第三方包中Star排名第一的开源库。\n\n虽然做的事情已经够多了，但是仍有一些很有必要去做的事情，由于时间原因，一直被搁置。\n\n随着学习Flutter的人越来越多，一部分开发者通过查看官网的文档就能入门，但也有很多开发者感觉学习时仍然有些吃力，主要原因有两个，首先官网的文档主要是为了引导开发者快速上手的，讲的并不是很细；其次是我们虽然翻译了官方文档，但是对于Flutter SDK文档并没有翻译，而在开发中遇到的一些具体问题通常都得去查看SDK文档。所以，要解决这两个问题，必须得有一个系统化的Flutter教程，它不仅可以快速引导开发者入门，而且也能触及到一些细节和原理，最好也能提供一些学习和研究Flutter的方法。因此，如果能有一本能系统地介绍Flutter的书籍便是便是非常棒的！但是，当时没有一本关于Flutter的中文书籍，因此，我便计划写一本能由浅入深、系统介绍Flutter的书。2018年12月，《Flutter实战》完成初稿，并在Github上开源，同时上线了[《Flutter实战》电子书官网](https://book.flutterchina.club/) ，至今每天有3000多人在线浏览本书。那为什么不直接出版？如果直接出版，不仅有稿费，而且也能保护知识产权，而直接开源，不就只能当雷锋了？其实，无论是做中文网、写Gitme、做Flutter开源项目，我的出发点都是为了能帮助中国开发者了解、学习Flutter，而这是一件非常有意义的事，只要坚持做对别人有价值的事，那么上帝迟早会奖赏你；当然我们也在网站中留了打赏的按钮，如果读者觉得有帮助，可以扫码打赏，请笔者喝一杯咖啡。另外由于成书比较仓促，当时书中也有很多错误，开源后，有很多读者提PR来纠正书中的错别字，时至今日，有78名开发者为本书提过PR，我在此，衷心的感谢你们。\n\n起初，我没有出版实体书的打算，当时我以为开发者直接通过在线访问本书官网即方便又不用付费，何乐而不为。但在本书上线后，很多读者来添加微信好友，表示非常有收益，很期待出版纸质书，甚至有比较热心的读者想提前付定金预定！我理解，这是大家对我所做之事的认可和鼓励。考虑到，确实有一部分读者，尤其是还没毕业的同学，可能更喜欢通过书籍去学习，为此，我已经和机械工业出版社合作，目前本书纸质版正在出版中，敬请关注。\n\n### 本书组织结构\n\n本书采用由浅入深的方式介绍Flutter技术原理，分为三篇，总计15章，各篇的主要内容如下。\n\n- 第一篇，入门篇（第1章~第7章），包括Flutter技术的出现背景和简介、Flutter的各种类型的Widget以及如何构建UI。通过学习本章，读者可以掌握如何使用Flutter来构建UI界面。\n- 第二篇，进阶篇（第8章~第14章），包括Flutter中的事件机制、动画、自定义组件、文件和网络、插件、国际化以及Flutter核心原理等。通过本章内容，读者可以对Flutter整体构建及原理有一个深入的认识。\n- 第三篇，实例篇（第15章），本章主要通过一个简版的Github APP来将前面介绍的内容串起来，让开发者对一个完整的Flutter APP开发流有个了解。\n\n## 本书特色\n\n笔者在大学时读过候捷（真名侯俊杰）写的一些C++相关书籍，在他的《深入浅出MFC》一书中，有一句话我印象像非常深 “唯有深入，方能浅出”。我非常认同这句话，对于一门技术，只有了解的深入，才能用最浅显、通俗的话语描述出。我在写作本书时，深入浅出就是一个主要目标。所以，本书的目标不仅是想告诉读者如何使用Flutter，而且也非常关注各个知识点的底层实现以及设计思想。从本书章节划分上来看，入门篇为“浅出”，进阶篇则是“深入”。另外由于PC客户端开发、移动开发、Web开发这些经验我都有，而Flutter本质上是一个UI系统，而UI系统的设计和实现在”大前端“下有很多相通之处，所以在本书中的一些知识点我也会对比一些其他UI系统（主要是Android或Web）相应的实现，便于有相关开发经验的读者对比理解。\n\n## 本书读者对象\n\n- 读者至少熟悉一种编程语言。\n- 读者最好接触过PC客户端、移动开发或Web前端开发中的一种。\n- 本书不适合做为编程的入门读物。\n\n## 关于随书源码\n\n由于篇幅所限，本书中大多数示例代码都只是部分核心代码，读者可以去[这里]( https://github.com/wendux/flutter_in_action_source_code )查看下载。\n\n## 致谢\n\n感谢一直以来支持Flutter中文网、Flutter开源项目的人以及所有对本书提过PR的人，正是因为有你们，才有这本书。另外尤其感谢给本书打赏过的同学，你们的支持给了我很大的鼓励。\n\n\n## 权益\n\n最后，知识是应该付费的，创作不易，开源不等于免费，如果您是本书读者并手头宽裕，可以点击下面打赏按钮打赏；当然，如果您囊中羞涩，您也可以阅读本书，但我对您有个小小的要求，希望您在阅读的过程中能积极参与到本书的纠错以及未完成内容的创作上来，也算是有所付出。\n\n近来在网上发现很多**原封不动复制本书**的镜像网站和大量复制或**引用了本书但未注明出处**的博客、文章甚至书籍；对此，笔者在此声明，本书著作权归wendux所有，任何组织或个人在未经授权的情况下复制、拷贝、抄袭本书用于商业目的，笔者保留追究其法律责任的权利。如果是非商业目的的转载和引用，请注明出处并附上本书网址。另外如有出版机构愿意为本书出版实体书或者想转载本书内容，亦或是想合作，请加微信Demons-du.\n\n### 勘误\n\n由于Flutter SDK在不断更新，本书中的部分内容（如类的继承关系、参数等）可能会和新版本的Flutter不一致，读者以最新的Flutter SDK为准。另外，由于时间仓促，书中难免有错误之处，如果您发现本书中的错误，欢迎点击右上角的”编辑按钮“，提PR。如果您想一起参与本书创作，可以参考[《Flutter实战》贡献指南](https://github.com/flutterchina/flutter-in-action#%E8%B4%A1%E7%8C%AE%E9%A1%BB%E7%9F%A5)。\n\n\n\n\n\n"
  },
  {
    "path": "src/v2/index.md",
    "content": "\n# 前言\n\n本书是第一本系统介绍Flutter技术的中文书籍，它是Flutter中文社区发起的开源项目之一，旨在帮助开发者入门，系统地、循序渐进的了解Flutter。\n\n> 本书官网访问量较大，由于服务器配置有限，所以某些时段访问本站可能略有延迟，如果延迟较久，读者可以直接在[Github上阅读本书](https://github.com/flutterchina/flutter-in-action/blob/master/docs/SUMMARY.md)。\n\n\n## 缘起\n\n在全球，随着Flutter被越来越多的知名公司应用在自己的商业APP中，Flutter这门新技术也逐渐进入了移动开发者的视野，尤其是当Google在2018年IO大会上发布了第一个Preview版本后，国内刮起来一股学习Flutter的热潮。\n\n在Flutter发布之初，当时，我在看完Flutter原理介绍后，就对它产生了浓厚的兴趣。当时笔者身边也一些人比较关注Flutter，我也被经常问到关于Flutter的一些问题，比如Flutter怎么样？和RN有什么区别？Flutter为什么要用Dart？当时也听到了一些批评的声音，比如有些人说Flutter只是重复造轮子，没啥亮点、Flutter最大的缺点就是使用了Dart语言等。在听到这些问题及论调后，我深知这是对Flutter的不了解而造成的，这和当时国内缺乏Flutter中文文档和教程有直接关系，很多人对Flutter的了解都只停留在Google的发布会介绍（有中文翻译）。\n\n在笔者深入的了解Flutter后，深知Flutter必将是一个会改变移动开发格局的里程碑级作品，它从设计之初就对性能和开发效率兼顾，并且借鉴了React（一个Web开发框架）的响应式的UI框架设计思想等，总之，很难用一两句话说完Flutter的优点，同时我也很快成为了Flutter的路转粉。\n\n为了更好的帮助中国开发者了解这门新技术，我在2018年初开始翻译Flutter官网文档，同年4月份上线了[Flutter中文网](https://flutterchina.club/)，上线后反响很强烈，Flutter中文网也很快被传播开，百度搜索排名迅速蹿升到前三，截止目前，Flutter中文官网日PV在7万左右，每日独立访问人数近一万多。\n\n虽然Flutter中文网给中国开发者提供了很好的第一手了解Flutter的资料，但是笔者还会经常遇到一些对Flutter技术处于围观而不愿尝试的开发者。这主要是因为当时Flutter在国内没有成功案例，再加上新技术都有学习成本，所以即使有文档，也会有一些开发者犹豫是否来学习。为了解决这部分开发者的疑虑，我就想如果能用Flutter开发一个完整的APP发布到应用商店，这样开发者就可以在犹豫的时候可以先实际感受一下Flutter应用，这样有个直观的了解后，就会容易做出判断，为此，我开发了[Gitme](https://flutterchina.club/app/gm.html)，它是一个Github客户端，它支持了源码浏览、Issue、Label等Github的大多数功能，到目前为止，通过Gitme登录过Github账号的用户有8000多人，日活用户有1000人。更重要的是，有很多人正是用了Gitme后，才来学Flutter的。\n\n无论是做Flutter中文网，还是写Gitme，主要目标都是帮助开发者学习Flutter，同时消除围观开发者疑虑。但当开发者们真正开始动手时，Flutter的生态问题就变得尤为突出。由于在2018年初Flutter刚起步时，很多基础的包和库都是空白，少数已有的一些库也大都是预览版（未到1.0），存在很多bug。这个状况不是一两个人花一两天能搞定的，这是需要聚整个Flutter开发者社群之力，耗费数年时间才可能有所改善。因此，在2018年4月份，我以Flutter中文网名义发起了Flutter开源计划，该计划主要是开发一些常用的包来丰富Flutter生态，帮助开发者提高开发效率。自在github建立[Flutter中文开发者社区账号](https://github.com/flutterchina)以来，前后开源了dio、cookieJar、flukit等多个项目，而dio在开源两周后，就迅速成为Flutter第三方包中Star排名第一的开源库。\n\n虽然做的事情已经够多了，但是仍有一些很有必要去做的事情，由于时间原因，一直被搁置。\n\n随着学习Flutter的人越来越多，一部分开发者通过查看官网的文档就能入门，但也有很多开发者感觉学习时仍然有些吃力，主要原因有两个，首先官网的文档主要是为了引导开发者快速上手的，讲的并不是很细；其次是我们虽然翻译了官方文档，但是对于Flutter SDK文档并没有翻译，而在开发中遇到的一些具体问题通常都得去查看SDK文档。所以，要解决这两个问题，必须得有一个系统化的Flutter教程，它不仅可以快速引导开发者入门，而且也能触及到一些细节和原理，最好也能提供一些学习和研究Flutter的方法。因此，如果能有一本能系统地介绍Flutter的书籍便是便是非常棒的！但是，当时没有一本关于Flutter的中文书籍，因此，我便计划写一本能由浅入深、系统介绍Flutter的书。2018年12月，《Flutter实战》完成初稿，并在Github上开源，同时上线了[《Flutter实战》电子书官网](https://book.flutterchina.club/) ，至今每天有3000多人在线浏览本书。那为什么不直接出版？如果直接出版，不仅有稿费，而且也能保护知识产权，而直接开源，不就只能当雷锋了？其实，无论是做中文网、写Gitme、做Flutter开源项目，我的出发点都是为了能帮助中国开发者了解、学习Flutter，而这是一件非常有意义的事，只要坚持做对别人有价值的事，那么上帝迟早会奖赏你；当然我们也在网站中留了打赏的按钮，如果读者觉得有帮助，可以扫码打赏，请笔者喝一杯咖啡。另外由于成书比较仓促，当时书中也有很多错误，开源后，有很多读者提PR来纠正书中的错别字，时至今日，有78名开发者为本书提过PR，我在此，衷心的感谢你们。\n\n起初，我没有出版实体书的打算，当时我以为开发者直接通过在线访问本书官网即方便又不用付费，何乐而不为。但在本书上线后，很多读者来添加微信好友，表示非常有收益，很期待出版纸质书，甚至有比较热心的读者想提前付定金预定！我理解，这是大家对我所做之事的认可和鼓励。考虑到，确实有一部分读者，尤其是还没毕业的同学，可能更喜欢通过书籍去学习，为此，我已经和机械工业出版社合作，目前本书纸质版正在出版中，敬请关注。\n\n### 本书组织结构\n\n本书采用由浅入深的方式介绍Flutter技术原理，分为三篇，总计15章，各篇的主要内容如下。\n\n- 第一篇，入门篇（第1章~第7章），包括Flutter技术的出现背景和简介、Flutter的各种类型的Widget以及如何构建UI。通过学习本章，读者可以掌握如何使用Flutter来构建UI界面。\n- 第二篇，进阶篇（第8章~第14章），包括Flutter中的事件机制、动画、自定义组件、文件和网络、插件、国际化以及Flutter核心原理等。通过本章内容，读者可以对Flutter整体构建及原理有一个深入的认识。\n- 第三篇，实例篇（第15章），本章主要通过一个简版的Github APP来将前面介绍的内容串起来，让开发者对一个完整的Flutter APP开发流有个了解。\n\n## 本书特色\n\n笔者在大学时读过候捷（真名侯俊杰）写的一些C++相关书籍，在他的《深入浅出MFC》一书中，有一句话我印象像非常深 “唯有深入，方能浅出”。我非常认同这句话，对于一门技术，只有了解的深入，才能用最浅显、通俗的话语描述出。我在写作本书时，深入浅出就是一个主要目标。所以，本书的目标不仅是想告诉读者如何使用Flutter，而且也非常关注各个知识点的底层实现以及设计思想。从本书章节划分上来看，入门篇为“浅出”，进阶篇则是“深入”。另外由于PC客户端开发、移动开发、Web开发这些经验我都有，而Flutter本质上是一个UI系统，而UI系统的设计和实现在”大前端“下有很多相通之处，所以在本书中的一些知识点我也会对比一些其他UI系统（主要是Android或Web）相应的实现，便于有相关开发经验的读者对比理解。\n\n## 本书读者对象\n\n- 读者至少熟悉一种编程语言。\n- 读者最好接触过PC客户端、移动开发或Web前端开发中的一种。\n- 本书不适合做为编程的入门读物。\n\n## 关于随书源码\n\n由于篇幅所限，本书中大多数示例代码都只是部分核心代码，读者可以去[这里]( https://github.com/wendux/flutter_in_action_source_code )查看下载。\n\n## 致谢\n\n感谢一直以来支持Flutter中文网、Flutter开源项目的人以及所有对本书提过PR的人，正是因为有你们，才有这本书。另外尤其感谢给本书打赏过的同学，你们的支持给了我很大的鼓励。\n\n\n## 权益\n\n最后，知识是应该付费的，创作不易，开源不等于免费，如果您是本书读者并手头宽裕，可以点击下面打赏按钮打赏；当然，如果您囊中羞涩，您也可以阅读本书，但我对您有个小小的要求，希望您在阅读的过程中能积极参与到本书的纠错以及未完成内容的创作上来，也算是有所付出。\n\n近来在网上发现很多**原封不动复制本书**的镜像网站和大量复制或**引用了本书但未注明出处**的博客、文章甚至书籍；对此，笔者在此声明，本书著作权归wendux所有，任何组织或个人在未经授权的情况下复制、拷贝、抄袭本书用于商业目的，笔者保留追究其法律责任的权利。如果是非商业目的的转载和引用，请注明出处并附上本书网址。另外如有出版机构愿意为本书出版实体书或者想转载本书内容，亦或是想合作，请加微信Demons-du.\n\n### 勘误\n\n由于Flutter SDK在不断更新，本书中的部分内容（如类的继承关系、参数等）可能会和新版本的Flutter不一致，读者以最新的Flutter SDK为准。另外，由于时间仓促，书中难免有错误之处，如果您发现本书中的错误，欢迎点击右上角的”编辑按钮“，提PR。如果您想一起参与本书创作，可以参考[《Flutter实战》贡献指南](https://github.com/flutterchina/flutter-in-action#%E8%B4%A1%E7%8C%AE%E9%A1%BB%E7%9F%A5)。\n\n\n\n\n\n"
  },
  {
    "path": "src/v2/intro.md",
    "content": "\n# 前言\n\n本书是第一本系统介绍Flutter技术的中文书籍，它是Flutter中文社区发起的开源项目之一，旨在帮助开发者入门，系统地、循序渐进的了解Flutter。\n\n> 本书官网访问量较大，由于服务器配置有限，所以某些时段访问本站可能略有延迟，如果延迟较久，读者可以直接在[Github上阅读本书](https://github.com/flutterchina/flutter-in-action/blob/master/docs/SUMMARY.md)。\n\n\n## 缘起\n\n在全球，随着Flutter被越来越多的知名公司应用在自己的商业APP中，Flutter这门新技术也逐渐进入了移动开发者的视野，尤其是当Google在2018年IO大会上发布了第一个Preview版本后，国内刮起来一股学习Flutter的热潮。\n\n在Flutter发布之初，当时，我在看完Flutter原理介绍后，就对它产生了浓厚的兴趣。当时笔者身边也一些人比较关注Flutter，我也被经常问到关于Flutter的一些问题，比如Flutter怎么样？和RN有什么区别？Flutter为什么要用Dart？当时也听到了一些批评的声音，比如有些人说Flutter只是重复造轮子，没啥亮点、Flutter最大的缺点就是使用了Dart语言等。在听到这些问题及论调后，我深知这是对Flutter的不了解而造成的，这和当时国内缺乏Flutter中文文档和教程有直接关系，很多人对Flutter的了解都只停留在Google的发布会介绍（有中文翻译）。\n\n在笔者深入的了解Flutter后，深知Flutter必将是一个会改变移动开发格局的里程碑级作品，它从设计之初就对性能和开发效率兼顾，并且借鉴了React（一个Web开发框架）的响应式的UI框架设计思想等，总之，很难用一两句话说完Flutter的优点，同时我也很快成为了Flutter的路转粉。\n\n为了更好的帮助中国开发者了解这门新技术，我在2018年初开始翻译Flutter官网文档，同年4月份上线了[Flutter中文网](https://flutterchina.club/)，上线后反响很强烈，Flutter中文网也很快被传播开，百度搜索排名迅速蹿升到前三，截止目前，Flutter中文官网日PV在7万左右，每日独立访问人数近一万多。\n\n虽然Flutter中文网给中国开发者提供了很好的第一手了解Flutter的资料，但是笔者还会经常遇到一些对Flutter技术处于围观而不愿尝试的开发者。这主要是因为当时Flutter在国内没有成功案例，再加上新技术都有学习成本，所以即使有文档，也会有一些开发者犹豫是否来学习。为了解决这部分开发者的疑虑，我就想如果能用Flutter开发一个完整的APP发布到应用商店，这样开发者就可以在犹豫的时候可以先实际感受一下Flutter应用，这样有个直观的了解后，就会容易做出判断，为此，我开发了[Gitme](https://flutterchina.club/app/gm.html)，它是一个Github客户端，它支持了源码浏览、Issue、Label等Github的大多数功能，到目前为止，通过Gitme登录过Github账号的用户有8000多人，日活用户有1000人。更重要的是，有很多人正是用了Gitme后，才来学Flutter的。\n\n无论是做Flutter中文网，还是写Gitme，主要目标都是帮助开发者学习Flutter，同时消除围观开发者疑虑。但当开发者们真正开始动手时，Flutter的生态问题就变得尤为突出。由于在2018年初Flutter刚起步时，很多基础的包和库都是空白，少数已有的一些库也大都是预览版（未到1.0），存在很多bug。这个状况不是一两个人花一两天能搞定的，这是需要聚整个Flutter开发者社群之力，耗费数年时间才可能有所改善。因此，在2018年4月份，我以Flutter中文网名义发起了Flutter开源计划，该计划主要是开发一些常用的包来丰富Flutter生态，帮助开发者提高开发效率。自在github建立[Flutter中文开发者社区账号](https://github.com/flutterchina)以来，前后开源了dio、cookieJar、flukit等多个项目，而dio在开源两周后，就迅速成为Flutter第三方包中Star排名第一的开源库。\n\n虽然做的事情已经够多了，但是仍有一些很有必要去做的事情，由于时间原因，一直被搁置。\n\n随着学习Flutter的人越来越多，一部分开发者通过查看官网的文档就能入门，但也有很多开发者感觉学习时仍然有些吃力，主要原因有两个，首先官网的文档主要是为了引导开发者快速上手的，讲的并不是很细；其次是我们虽然翻译了官方文档，但是对于Flutter SDK文档并没有翻译，而在开发中遇到的一些具体问题通常都得去查看SDK文档。所以，要解决这两个问题，必须得有一个系统化的Flutter教程，它不仅可以快速引导开发者入门，而且也能触及到一些细节和原理，最好也能提供一些学习和研究Flutter的方法。因此，如果能有一本能系统地介绍Flutter的书籍便是便是非常棒的！但是，当时没有一本关于Flutter的中文书籍，因此，我便计划写一本能由浅入深、系统介绍Flutter的书。2018年12月，《Flutter实战》完成初稿，并在Github上开源，同时上线了[《Flutter实战》电子书官网](https://book.flutterchina.club/) ，至今每天有3000多人在线浏览本书。那为什么不直接出版？如果直接出版，不仅有稿费，而且也能保护知识产权，而直接开源，不就只能当雷锋了？其实，无论是做中文网、写Gitme、做Flutter开源项目，我的出发点都是为了能帮助中国开发者了解、学习Flutter，而这是一件非常有意义的事，只要坚持做对别人有价值的事，那么上帝迟早会奖赏你；当然我们也在网站中留了打赏的按钮，如果读者觉得有帮助，可以扫码打赏，请笔者喝一杯咖啡。另外由于成书比较仓促，当时书中也有很多错误，开源后，有很多读者提PR来纠正书中的错别字，时至今日，有78名开发者为本书提过PR，我在此，衷心的感谢你们。\n\n起初，我没有出版实体书的打算，当时我以为开发者直接通过在线访问本书官网即方便又不用付费，何乐而不为。但在本书上线后，很多读者来添加微信好友，表示非常有收益，很期待出版纸质书，甚至有比较热心的读者想提前付定金预定！我理解，这是大家对我所做之事的认可和鼓励。考虑到，确实有一部分读者，尤其是还没毕业的同学，可能更喜欢通过书籍去学习，为此，我已经和机械工业出版社合作，目前本书纸质版正在出版中，敬请关注。\n\n### 本书组织结构\n\n本书采用由浅入深的方式介绍Flutter技术原理，分为三篇，总计15章，各篇的主要内容如下。\n\n- 第一篇，入门篇（第1章~第7章），包括Flutter技术的出现背景和简介、Flutter的各种类型的Widget以及如何构建UI。通过学习本章，读者可以掌握如何使用Flutter来构建UI界面。\n- 第二篇，进阶篇（第8章~第14章），包括Flutter中的事件机制、动画、自定义组件、文件和网络、插件、国际化以及Flutter核心原理等。通过本章内容，读者可以对Flutter整体构建及原理有一个深入的认识。\n- 第三篇，实例篇（第15章），本章主要通过一个简版的Github APP来将前面介绍的内容串起来，让开发者对一个完整的Flutter APP开发流有个了解。\n\n## 本书特色\n\n笔者在大学时读过候捷（真名侯俊杰）写的一些C++相关书籍，在他的《深入浅出MFC》一书中，有一句话我印象像非常深 “唯有深入，方能浅出”。我非常认同这句话，对于一门技术，只有了解的深入，才能用最浅显、通俗的话语描述出。我在写作本书时，深入浅出就是一个主要目标。所以，本书的目标不仅是想告诉读者如何使用Flutter，而且也非常关注各个知识点的底层实现以及设计思想。从本书章节划分上来看，入门篇为“浅出”，进阶篇则是“深入”。另外由于PC客户端开发、移动开发、Web开发这些经验我都有，而Flutter本质上是一个UI系统，而UI系统的设计和实现在”大前端“下有很多相通之处，所以在本书中的一些知识点我也会对比一些其他UI系统（主要是Android或Web）相应的实现，便于有相关开发经验的读者对比理解。\n\n## 本书读者对象\n\n- 读者至少熟悉一种编程语言。\n- 读者最好接触过PC客户端、移动开发或Web前端开发中的一种。\n- 本书不适合做为编程的入门读物。\n\n## 关于随书源码\n\n由于篇幅所限，本书中大多数示例代码都只是部分核心代码，读者可以去[这里]( https://github.com/wendux/flutter_in_action_source_code )查看下载。\n\n## 致谢\n\n感谢一直以来支持Flutter中文网、Flutter开源项目的人以及所有对本书提过PR的人，正是因为有你们，才有这本书。另外尤其感谢给本书打赏过的同学，你们的支持给了我很大的鼓励。\n\n\n## 权益\n\n最后，知识是应该付费的，创作不易，开源不等于免费，如果您是本书读者并手头宽裕，可以点击下面打赏按钮打赏；当然，如果您囊中羞涩，您也可以阅读本书，但我对您有个小小的要求，希望您在阅读的过程中能积极参与到本书的纠错以及未完成内容的创作上来，也算是有所付出。\n\n近来在网上发现很多**原封不动复制本书**的镜像网站和大量复制或**引用了本书但未注明出处**的博客、文章甚至书籍；对此，笔者在此声明，本书著作权归wendux所有，任何组织或个人在未经授权的情况下复制、拷贝、抄袭本书用于商业目的，笔者保留追究其法律责任的权利。如果是非商业目的的转载和引用，请注明出处并附上本书网址。另外如有出版机构愿意为本书出版实体书或者想转载本书内容，亦或是想合作，请加微信Demons-du.\n\n### 勘误\n\n由于Flutter SDK在不断更新，本书中的部分内容（如类的继承关系、参数等）可能会和新版本的Flutter不一致，读者以最新的Flutter SDK为准。另外，由于时间仓促，书中难免有错误之处，如果您发现本书中的错误，欢迎点击右上角的”编辑按钮“，提PR。如果您想一起参与本书创作，可以参考[《Flutter实战》贡献指南](https://github.com/flutterchina/flutter-in-action#%E8%B4%A1%E7%8C%AE%E9%A1%BB%E7%9F%A5)。\n\n\n\n\n\n"
  },
  {
    "path": "src/v2/join_us.md",
    "content": "---\nsidebar: auto\n---\n\n# 字节跳动-内推\n\n欢迎来字节跳动，和作者（wendux） 一起做同事！我们有大量HC，社招、校招、实习，前端、客户端、后端都有，欢迎对技术有热情的同学来投递！风里雨里，我在字节跳动等你~\n\n> 投递方式：加微信（Demons-du），好友申请时请按 \"姓名+学校+职位+来自flutter社区\" 备注信息，微信请求通过后，字节跳动VIP通道就建立了（后续发简历、进度跟进、问题咨询都可以直接微信联系哦）。\n\n## 前端团队介绍\n\n我们是字节跳动-幸福里FE团队，诞生于2018年9月，从最初的4人组成长到今天的30多人，成员年龄跨度从90后到00后。技术栈覆盖当下前端主流全方向（Vue/React/Typescript/nodejs/webgl/flutter/Taro)，团队内大牛多多，技术氛围浓厚，有VR专家老吴、3D渲染一哥博哥、《Flutter实战》作者wendux、深谙 Web 框架及工程化的董老师、以及技能树满点的杰哥等等，还有，团队经常组织线下学习及娱乐活动，是一个开心且战斗力极强的team。只要你觉得自己够出色，或想让自己变得更出色，还等什么，放肆地加入我们吧。 \n\n### 业务线介绍\n\n幸福里是字节跳动旗下集内容、社区、工具于一体的房产信息、服务、交易平台。产品基于个性化推荐引擎向用户推荐优质的房产内容和全面、真实的房源信息，致力于为用户提供全面、专业、可靠的购房决策支持。\n\n幸福里始于2018年8月，是国内发展最快的，集内容、社区、工具于一体的房产信息与服务平台，业务覆盖一二线共23城，现累积注册用户千万，目前进入高速增长期。\n\n### 团队福利\n\n五险一金、补充医疗保险、定期体检、年终奖、股票期权、带薪年假、员工旅游、交通补助、包吃、节日福利、住房补贴，不限量零食下午茶、弹性工作制\n\n#### 实习生\n\n1. 团队为每一位实习生提供专职mentor，手把手带入工作业务。\n2. 团队为没有基础的实习生提供“筑基计划”的课程学习，轻松进阶前端技能。\n\n## 职位介绍\n\n### 职位—：前端开发工程师（校招/社招）急\n\n**职位描述:**\n\n1. 负责移动端 /PC 端业务系统、小程序、跨端页面开发；\n\n2. 负责推动与优化业务线中前端基础架构、组件抽象、技术调研；\n\n4. 积极推动改进产品，包括技术、用户体验、产品等各个维度的改进。\n\n**职位要求:**\n\n1. 本科及以上学历，计算机、通信等相关专业；\n\n2. 熟练掌握 EcmaScript，CSS，HTML，DOM、绘图、动画、协议、安全、网络、性能优化等前端技术，对主流前端框架（ React \\ Vue 等）至少一种有深入应用并深入理解其设计原理；\n\n3. 有安卓、iOS 开发经验或跨端技术flutter者优先；\n\n4. 对用户体验、交互操作流程，及用户需求有一定了解；\n\n5. 积极乐观，责任心强，工作认真细致，具备良好的服务意识，具有良好的团队沟通与协作能力；\n\n6. 热爱前端技术，有较强的学习能力，有强烈的求知欲、好奇心和进取心 ，能及时关注和学习业界最新的前端技术。\n\n### 职位二：校招前端开发实习生\n\n**职位描述：**\n\n1、负责字节跳动-幸福里业务h5、小程序、中台系统的维护与开发；\n\n2、负责根据已有前端项目的基础架构进行合理的技术优化；\n\n3、积极推动改进产品，包括技术、用户体验、产品等各个维度。\n\n**职位要求：**\n\n1、计算机基础扎实：数据结构、算法、操作系统；\n\n2、熟悉掌握javascript、ES6 语言特性，熟练掌握css中常见布局方式，以及CSS3动画技术；\n\n3、熟练掌握VUE或React技术（非必须）；\n\n4、有ACM竞赛且获奖者优先；\n\n5、具较强的学习能力、主动、自驱、有责任心。\n\n**实习生培养计划**：\n\n我们会为每一位实习生配备一名mentor进行日常决疑解惑和指导，同时我们针对不太熟悉前端的同学进行一个专门的【筑基培训】，旨在帮助快速补齐前端基础，以及明确后续学习和成长路线，有老司机带，不迷路！\n\n### 职位三：web3D开发工程师（急）\n\n**职位描述：**\n\n1. 负责 VR 看房相关的业务开发，包括渲染SDK、标注平台等。\n2. 负责 VR 数据平台相关开发：包括数据预处理\n\n**职位要求：**\n\n1. 熟练掌握 JavaScript，WebGL；\n\n2. 熟悉计算机图形学，渲染管线/线性代数； \n\n3. 熟悉常用 Shader 原理及编写；\n\n4. 熟悉至少一款 H5 渲染引擎，如ThreeJS，Babylon等； \n\n6. 热爱钻研新技术，有强烈的好奇心和求知欲，有良好的编码规范；\n7. 加分项：\n\n-  熟悉VR看房相关业务 \n- 熟悉后端开发（Node）、对服务稳定性、并发了解同学优先（VR数据平台）。\n- 熟悉 ThreeJS，Babylon, Unity3D 有相关 3D 作品或 DEMO。\n-  各大前端技术社区活跃者、有自己的开源项目；\n\n## 其它职位（实习/校招/社招均可）\n\n### Android开发工程师\n\n职位描述\n\n1、负责公司移动产品的研发, 编写高质量的代码；\n\n2、和产品经理配合, 深度参与手机产品需求讨论, 功能定义等； \n\n3、设计良好的代码结构, 不断迭代重构 。\n\n职位要求\n\n1、智能手机爱好者和使用者, 追求良好的用户体验；\n\n2、热爱移动产品研发, 愿意在移动开发领域深入钻研, 并成为专家；\n\n3、熟练掌握JAVA, 熟悉Android SDK；\n\n4、一年以上Android开发经验, 能独立开发Android App； 5、对软件产品有强烈的责任心, 具备良好的沟通能力和优秀的团队协作能力。\n\n5、有flutter开发经验者加分。\n\n\n\n### iOS开发工程师\n\n职位描述\n\n1、负责公司移动产品的研发，编写高质量的代码；\n\n2、和产品经理配合，深度参与手机产品需求讨论，功能定义等； \n\n3、设计良好的代码结构，不断迭代重构 ；\n\n4、导并带领初级工程师共同完成研发任务。\n\n职位要求\n\n1、有强烈的求知欲和进取心；\n\n2、具有扎实的编程工底，良好的设计能力和编程习惯；\n\n3、至少精通一门编程语言 ，熟练掌握Objective-C，熟悉Swift的优先 ；\n\n4、一年以上iOS开发经验，能独立开发iPhoneApp者先。\n\n5、有flutter开发经验者加分。\n\n### 后端开发工程师\n\n**职位描述**\n\n1、主导或参与系统设计、研发、部署等相关工作；\n\n2、研发基础服务组件，解决共性需求，减少重复开发与运维；\n\n3、有较强的系统问题分析经验和能力，能够解决复杂的系统问题； \n\n4、参与部分生产系统维护工作，解决生产系统问题及进行系统调优。\n\n**职位要求**\n\n1、本科及以上学历，计算机相关专业；\n\n2、热爱计算机科学和互联网技术，精通至少一门编程语言，包括但不仅限于：Java、C、C++、PHP、 Python、Go； \n\n3、掌握扎实的计算机基础知识，深入理解数据结构、算法和操作系统知识； \n\n4、有优秀的逻辑分析能力，能够对业务逻辑进行合理的抽象和拆分； \n\n5、有强烈的求知欲，优秀的学习和沟通能力。\n\n## [返回书籍菜单列表](/index)\n\n\n\n\n\n"
  },
  {
    "path": "src/v2/next.md",
    "content": "# 下一步\n\n### 其它平台\n\n本书主要讲的是Flutter在移动端开发\n\n- "
  },
  {
    "path": "src/v2/preface.md",
    "content": "# 前言\n\n### 缘起\n\n在全球，随着Flutter被越来越多的知名公司应用在自己的商业APP中，Flutter这门新技术也逐渐进入了移动开发者的视野，尤其是当Google在2018年IO大会上发布了第一个Preview版本后，国内刮起来一股学习Flutter的热潮。\n\n在Flutter发布之初，当时，我在看完Flutter原理介绍后，就对它产生了浓厚的兴趣。当时笔者身边也一些人比较关注Flutter，我也被经常问到关于Flutter的一些问题，比如Flutter怎么样？和RN有什么区别？Flutter为什么要用Dart？当时也听到了一些批评的声音，比如有些人说Flutter只是重复造轮子，没啥亮点、Flutter最大的缺点就是使用了Dart语言等。在听到这些问题及论调后，我深知这是对Flutter的不了解而造成的，这和当时国内缺乏Flutter中文文档和教程有直接关系，很多人对Flutter的了解都只停留在Google的发布会介绍（有中文翻译）。\n\n在笔者深入的了解Flutter后，深知Flutter必将是一个会改变移动开发格局的里程碑级作品，它从设计之初就对性能和开发效率兼顾，并且借鉴了React（一个Web开发框架）的响应式的UI框架设计思想等，总之，很难用一两句话说完Flutter的优点，同时我也很快成为了Flutter的路转粉。\n\n为了更好的帮助中国开发者了解这门新技术，我在2018年初开始翻译Flutter官网文档，同年4月份上线了[Flutter中文网](https://flutterchina.club/)，上线后反响很强烈，Flutter中文网也很快被传播开，百度搜索排名迅速蹿升到前三，截止目前，Flutter中文官网日PV在7万左右，每日独立访问人数近一万多。\n\n虽然Flutter中文网给中国开发者提供了很好的第一手了解Flutter的资料，但是笔者还会经常遇到一些对Flutter技术处于围观而不愿尝试的开发者。这主要是因为当时Flutter在国内没有成功案例，再加上新技术都有学习成本，所以即使有文档，也会有一些开发者犹豫是否来学习。为了解决这部分开发者的疑虑，我就想如果能用Flutter开发一个完整的APP发布到应用商店，这样开发者就可以在犹豫的时候可以先实际感受一下Flutter应用，这样有个直观的了解后，就会容易做出判断，为此，我开发了[Gitme](https://flutterchina.club/app/gm.html)，它是一个Github客户端，它支持了源码浏览、Issue、Label等Github的大多数功能，到目前为止，通过Gitme登录过Github账号的用户有8000多人，日活用户有1000人。更重要的是，有很多人正是用了Gitme后，才来学Flutter的。\n\n无论是做Flutter中文网，还是写Gitme，主要目标都是帮助开发者学习Flutter，同时消除围观开发者疑虑。但当开发者们真正开始动手时，Flutter的生态问题就变得尤为突出。由于在2018年初Flutter刚起步时，很多基础的包和库都是空白，少数已有的一些库也大都是预览版（未到1.0），存在很多bug。这个状况不是一两个人花一两天能搞定的，这是需要聚整个Flutter开发者社群之力，耗费数年时间才可能有所改善。因此，在2018年4月份，我以Flutter中文网名义发起了Flutter开源计划，该计划主要是开发一些常用的包来丰富Flutter生态，帮助开发者提高开发效率。自在github建立[Flutter中文开发者社区官方账号](https://github.com/flutterchina)以来，前后开源了dio、cookieJar、flukit等多个项目，而dio在开源两周后，就迅速成为Flutter第三方包中Star排名第一的开源库。\n\n虽然做的事情已经够多了，但是仍有一些很有必要去做的事情，由于时间原因，一直被搁置。\n\n随着学习Flutter的人越来越多，一部分开发者通过查看官网的文档就能入门，但也有很多开发者感觉学习时仍然有些吃力，主要原因有两个，首先官网的文档主要是为了引导开发者快速上手的，讲的并不是很细；其次是我们虽然翻译了官方文档，但是对于Flutter SDK文档并没有翻译，而在开发中遇到的一些具体问题通常都得去查看SDK文档。所以，要解决这两个问题，必须得有一个系统化的Flutter教程，它不仅可以快速引导开发者入门，而且也能触及到一些细节和原理，最好也能提供一些学习和研究Flutter的方法。因此，如果能有一本能系统地介绍Flutter的书籍便是便是非常棒的！但是，当时没有一本关于Flutter的中文书籍，因此，我便计划写一本能由浅入深、系统介绍Flutter的书。2018年12月，《Flutter实战》完成初稿，并在Github上开源，同时上线了《Flutter实战》电子书官网（https://book.flutterchina.club/） ，至今每天有3000多人在线浏览本书。那为什么不直接出版？如果直接出版，不仅有稿费，而且也能保护知识产权，而直接开源，不就就只能当雷锋了？其实，无论是做中文网、写Gitme、做Flutter开源项目，我的出发点都是为了能帮助中国开发者了解、学习Flutter，而这是一件非常有意义的事，只要坚持做对别人有价值的事，那么上帝迟早会奖赏你；当然我们也在网站中留了打赏的按钮，如果读者觉得有帮助，可以扫码打赏，请笔者喝一杯咖啡。另外由于成书比较仓促，当时书中也有很多错误，开源后，有很多读者提PR来纠正书中的错别字，时至今日，有78名开发者为本书提过PR，我在此，衷心的感谢你们。\n\n起初，我没有出版实体书的打算，当时我以为开发者直接通过在线访问本书官网即方便又不用付费，何乐而不为。但在本书上线后，很多读者来添加微信好友，表示非常有收益，很期待出版纸质书，甚至有比较热心的读者想提前付定金预定！我理解，这是大家对我所做之事的认可和鼓励。考虑到，确实有一部分读者，尤其是还没毕业的同学，可能更喜欢通过书籍去学习，为此，我已经和机械工业出版社合作，目前本书纸质版正在出版中，敬请关注。\n\n### 本书组织结构\n\n本书采用由浅入深的方式介绍Flutter技术原理，分为三篇，总计15章，各篇的主要内容如下。\n\n- 第一篇，入门篇（第1章~第7章），包括Flutter技术的出现背景和简介、Flutter的各种类型的Widget以及如何构建UI。通过学习本章，读者可以掌握如何使用Flutter来构建UI界面。\n- 第二篇，进阶篇（第8章~第14章），包括Flutter中的事件机制、动画、自定义组件、文件和网络、插件、国际化以及Flutter核心原理等。通过本章内容，读者可以对Flutter整体构建及原理有一个深入的认识。\n- 第三篇，实例篇（第15张），本章主要通过一个简版的Github APP来将前面介绍的内容串起来，让开发者对一个完整的Flutter APP开发流有个了解。\n\n由于Flutter的很多知识点是相互交织的，很难将它们彻底划分开，所以本书中也难免会出现一些在前面章节会使用在后面章节的场景，比如我们在入门篇介绍进度指示器时会用到在进阶篇中才介绍的动画相关知识。本书中对于这种情况会在相应的章节进行说明。读者可以直接跳到后面相应知识点章节阅读后再返回，也可以先有个印象，待学习到后面相关章节后再回头来看。\n\n### 本书特色\n\n笔者在大学时读过候捷（真名侯俊杰）写的一些C++相关书籍，在他的《深入浅出MFC》一书中，有一句话我印象非常深 “唯有深入，方能浅出”。我非常认同这句话，对于一门技术，只有了解的深入，才能用最浅显、通俗的话语描述出。我在写作本书时，深入浅出就是一个主要目标。所以，本书的目标不仅是想告诉读者如何使用Flutter，而且也非常关注各个知识点的底层实现以及设计思想。从本书章节划分上来看，入门篇为“浅出”，进阶篇则是“深入”。另外由于PC客户端开发、移动开发、Web开发这些经验我都有，而Flutter本质上是一个UI系统，而UI系统的设计和实现在”大前端“下有很多相通之处，所以在本书中的一些知识点我也会对比一些其他UI系统（主要是Android或Web）相应的实现，便于有相关开发经验的读者对比理解。\n\n### 本书读者对象\n\n- 读者至少熟悉一种编程语言。\n- 读者最好接触过PC客户端、移动开发或Web前端开发中的一种。\n- 本书不适合做为编程的入门读物。\n\n### 关于随书源码\n\n由于篇幅所限，本书中大多数示例代码都只是部分核心代码，读者可以去https://github.com/wendux/flutter_in_action_source_code 查看\n\n### 勘误\n\n由于Flutter SDK在不断更新，本书中的部分内容（如类的继承关系、参数等）可能会和新版本的Flutter不一致，读者以最新的Flutter SDK为准。另外，由于时间仓促，书中难免有错误之处，如果发现错误，可以在本书Github项目issue列表中去反馈，地址是https://github.com/flutterchina/flutter-in-action/issues 。\n\n### 致谢\n\n感谢一直以来支持Flutter中文网、Flutter开源项目的人以及所有对本书提过PR的人，正是因为有你们，才有这本书。另外尤其感谢给本书打赏过的同学，你们的支持给了我很大的鼓励。\n\n"
  },
  {
    "path": "src/v2/reference.md",
    "content": "# 参考文献\n\n- React Native官网：https://facebook.github.io/react-native/\n\n- Weex：https://weex.apache.org/zh/guide/introduction.html\n\n- 快应用：https://www.quickapp.cn/\n\n- QT for mobile：https://www.qt.io/mobile-app-development/\n\n- Flutter官网：https://flutter.dev/\n\n- Flutter中文网社区：https://flutterchina.club/docs/\n\n- Dart Packages官网：https://pub.dev/\n\n- Flutter中文开发者社区开源项目：https://github.com/flutterchina\n\n- Material Design：https://material.io/\n\n- Github 开发者中心官网：https://developer.github.com/v3/\n\n- Android开发者中心官网：https://developer.android.google.cn/\n\n- Apple开发者中心官网：https://developer.apple.com/\n\n  "
  },
  {
    "path": "src/v2/summary.md",
    "content": "﻿# Summary\n\n* [简介](README.md)\n* [前言](intro.md)\n\n## 入门篇\n\n* [第一章：起步](chapter1/index.md)\n    * [1.1：移动开发技术简介](chapter1/mobile_development_intro.md)\n    * [1.2：初识Flutter](chapter1/flutter_intro.md)  \n    * [1.3：搭建Flutter开发环境](chapter1/install_flutter.md)      \n    * [1.4：Dart语言简介](chapter1/dart.md)    \n    \n* [第二章：第一个Flutter应用](chapter2/index.md)\n    * [2.1：计数器示例](chapter2/first_flutter_app.md)\n    * [2.2：路由管理](chapter2/flutter_router.md)  \n    * [2.3：包管理](chapter2/flutter_package_mgr.md)        \n    * [2.4：资源管理](chapter2/flutter_assets_mgr.md)    \n    * [2.5：调试Flutter APP](chapter2/flutter_app_debug.md)\n    * [2.6：Dart线程模型及异常捕获](chapter2/thread_model_and_error_report.md)\n    \n* [第三章：基础组件](chapter3/index.md)\n    * [3.1：Widget简介](chapter3/flutter_widget_intro.md)\n    * [3.2：状态管理](chapter3/state_manage.md)\n    * [3.3：文本、字体样式](chapter3/text.md)\n    * [3.4：按钮](chapter3/buttons.md)      \n    * [3.5：图片和Icon](chapter3/img_and_icon.md)   \n    * [3.6：单选框和复选框](chapter3/radio_and_checkbox.md)   \n    * [3.7：输入框和表单](chapter3/input_and_form.md) \n    * [3.8：进度指示器](chapter3/progress.md)     \n    \n* [第四章：布局类组件](chapter4/index.md)\n    * [4.1：布局类组件简介](chapter4/intro.md)\n    * [4.2：线性布局（Row、Column）](chapter4/row_and_column.md)\n    * [4.3：弹性布局（Flex）](chapter4/flex.md)\n    * [4.4：流式布局（Wrap、Flow）](chapter4/wrap_and_flow.md)      \n    * [4.5：层叠布局（Stack、Positioned）](chapter4/stack.md)\n    * [4.6：对齐与相对定位（Align）](chapter4/alignment.md)    \n\n* [第五章：容器类组件](chapter5/index.md)\n    * [5.1：填充（Padding）](chapter5/padding.md)\n    * [5.2：尺寸限制类容器（ConstrainedBox等）](chapter5/constrainedbox_and_sizebox.md)\n    * [5.3：装饰容器（DecoratedBox）](chapter5/decoratedbox.md)      \n    * [5.4：变换（Transform）](chapter5/transform.md) \n    * [5.5：Container容器](chapter5/container.md) \n    * [5.6：Scaffold、TabBar、底部导航](chapter5/material_scaffold.md) \n    * [5.7：剪裁（Clip）](chapter5/clip.md) \n    \n* [第六章：可滚动组件](chapter6/index.md)\n    * [6.1：可滚动组件简介](chapter6/intro.md)\n    * [6.2：SingleChildScrollView](chapter6/single_child_scrollview.md)\n    * [6.3：ListView](chapter6/listview.md)\n    * [6.4：GridView](chapter6/gridview.md)      \n    * [6.5：CustomScrollView](chapter6/custom_scrollview.md) \n    * [6.6：滚动监听及控制（ScrollController）](chapter6/scroll_controller.md) \n    \n* [第七章：功能型组件](chapter7/index.md)\n    * [7.1：导航返回拦截（WillPopScope）](chapter7/willpopscope.md)\n    * [7.2：数据共享（InheritedWidget）](chapter7/inherited_widget.md)\n    * [7.3： 跨组件状态共享（Provider）](chapter7/provider.md)\n    * [7.4：颜色和主题（Theme）](chapter7/theme.md) \n    * [7.5：异步UI更新（FutureBuilder、StreamBuilder）](chapter7/futurebuilder_and_streambuilder.md)\n    * [7.6：对话框详解](chapter7/dailog.md)  \n     \n## 进阶篇\n\n* [第八章：事件处理与通知](chapter8/index.md)\n    * [8.1：原始指针事件处理](chapter8/listener.md)\n    * [8.2：手势识别](chapter8/gesture.md)\n    * [8.3：全局事件总线](chapter8/eventbus.md) \n    * [8.4：通知(Notification)](chapter8/notification.md)     \n    \n* [第九章：动画](chapter9/index.md)\n    * [9.1：Flutter动画简介](chapter9/intro.md)\n    * [9.2：动画结构](chapter9/animation_structure.md)\n    * [9.3：自定义路由过渡动画](chapter9/route_transition.md) \n    * [9.4：Hero动画](chapter9/hero.md) \n    * [9.5：交织动画](chapter9/stagger_animation.md) \n    * [9.6：通用“动画切换”组件（AnimatedSwitcher）](chapter9/animated_switcher.md) \n    * [9.7：动画过渡组件](chapter9/animated_widgets.md)     \n* [第十章：自定义组件](chapter10/index.md)\n    * [10.1：自定义组件方法简介](chapter10/intro.md)\n    * [10.2：组合现有组件](chapter10/combine.md)\n    * [10.3：组合实例：TurnBox](chapter10/turn_box.md)\n    * [10.4：自绘组件（CustomPaint与Canvas）](chapter10/custom_paint.md) \n    * [10.5：自绘实例：圆形渐变进度条(自绘)](chapter10/gradient_circular_progress_demo.md) \n    \n* [第十一章：文件操作与网络请求](chapter11/index.md)\n    * [11.1：文件操作](chapter11/file_operation.md)\n    * [11.2：Http请求-HttpClient](chapter11/http.md)\n    * [11.3：Http请求-Dio package](chapter11/dio.md) \n    * [11.4：实例：Http分块下载](chapter11/download_with_chunks.md) \n    * [11.5：WebSocket](chapter11/websocket.md) \n    * [11.6：使用Socket API](chapter11/socket.md) \n    * [11.7：Json转Dart Model类](chapter11/json_model.md) \n    \n* [第十二章：包与插件](chapter12/index.md)\n    * [12.1：开发package](chapter12/develop_package.md)\n    * [12.2：平台通道简介](chapter12/platform-channel.md)\n    * [12.3：开发Flutter插件](chapter12/develop_plugin.md)\n    * [12.4：插件开发：实现Android端API](chapter12/android_implement.md)\n    * [12.5：插件开发：实现IOS端API](chapter12/ios_implement.md)\n    * [12.6：Texture和PlatformView](chapter12/texture_platformview.md) \n    \n* [第十三章：国际化](chapter13/index.md)\n    * [13.1：让App支持多语言](chapter13/multi_languages_support.md)\n    * [13.2：实现Localizations](chapter13/locallization_implement.md) \n    * [13.3：使用Intl包](chapter13/intl.md) \n    * [13.4：国际化常见问题](chapter13/faq.md) \n    \n* [第十四章：Flutter核心原理](chapter14/index.md)\n    * [14.1：Flutter UI系统](chapter14/flutter_ui_system.md)\n    * [14.2：Element和BuildContext](chapter14/element_buildcontext.md)\n    * [14.3：RenderObject与RenderBox](chapter14/render_object.md)\n    * [14.4：Flutter从启动到显示](chapter14/flutter_app_startup.md)\n    * [14.5：Flutter图片加载与缓存](chapter14/image_and_cache.md)\n \n## 实例篇\n\n* [第十五章：一个完整的Flutter应用](chapter15/intro.md)\n    * [15.1：应用简介](chapter15/intro.md)    \n    * [15.2：APP代码结构](chapter15/code_structure.md) \n    * [15.3：Model类定义](chapter15/models.md) \n    * [15.4：全局变量及共享状态](chapter15/globals.md)  \n    * [15.5：网络请求封装](chapter15/network.md) \n    * [15.6：App入口及首页](chapter15/entry.md) \n    * [15.7：登录页](chapter15/login_page.md) \n    * [15.8：多语言和多主题](chapter15/language_and_theme_setting.md)      \n\n\n"
  }
]